http.c 8.86 KB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
1 2
/*
 * HTTP protocol for ffmpeg client
3
 * Copyright (c) 2000, 2001 Fabrice Bellard.
Fabrice Bellard's avatar
Fabrice Bellard committed
4
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
Fabrice Bellard's avatar
Fabrice Bellard committed
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
Fabrice Bellard's avatar
Fabrice Bellard committed
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
Fabrice Bellard's avatar
Fabrice Bellard committed
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Fabrice Bellard's avatar
Fabrice Bellard committed
20
 */
Fabrice Bellard's avatar
Fabrice Bellard committed
21
#include "avformat.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
22 23
#include <unistd.h>
#include <sys/types.h>
24
#include <sys/socket.h>
Fabrice Bellard's avatar
Fabrice Bellard committed
25
#include <netinet/in.h>
26 27 28 29 30
#ifndef __BEOS__
# include <arpa/inet.h>
#else
# include "barpainet.h"
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
31 32
#include <netdb.h>

33
#include "base64.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
34 35 36 37 38 39 40 41 42

/* XXX: POST protocol is not completly implemented because ffmpeg use
   only a subset of it */

//#define DEBUG

/* used for protocol handling */
#define BUFFER_SIZE 1024
#define URL_SIZE    4096
43
#define MAX_REDIRECTS 8
Fabrice Bellard's avatar
Fabrice Bellard committed
44 45

typedef struct {
46
    URLContext *hd;
Fabrice Bellard's avatar
Fabrice Bellard committed
47 48 49
    unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
    int line_count;
    int http_code;
50
    offset_t off, filesize;
Fabrice Bellard's avatar
Fabrice Bellard committed
51 52 53
    char location[URL_SIZE];
} HTTPContext;

54
static int http_connect(URLContext *h, const char *path, const char *hoststr,
55
                        const char *auth, int *new_location);
56
static int http_write(URLContext *h, uint8_t *buf, int size);
Fabrice Bellard's avatar
Fabrice Bellard committed
57

58

Fabrice Bellard's avatar
Fabrice Bellard committed
59
/* return non zero if error */
60
static int http_open_cnx(URLContext *h)
Fabrice Bellard's avatar
Fabrice Bellard committed
61
{
62 63
    const char *path, *proxy_path;
    char hostname[1024], hoststr[1024];
64
    char auth[1024];
65 66
    char path1[1024];
    char buf[1024];
67 68
    int port, use_proxy, err, location_changed = 0, redirects = 0;
    HTTPContext *s = h->priv_data;
69
    URLContext *hd = NULL;
Fabrice Bellard's avatar
Fabrice Bellard committed
70 71

    proxy_path = getenv("http_proxy");
72
    use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
73
        strstart(proxy_path, "http://", NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
74 75 76

    /* fill the dest addr */
 redo:
77
    /* needed in any case to build the host string */
78
    url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
79
              path1, sizeof(path1), s->location);
80 81
    if (port > 0) {
        snprintf(hoststr, sizeof(hoststr), "%s:%d", hostname, port);
Fabrice Bellard's avatar
Fabrice Bellard committed
82
    } else {
83
        pstrcpy(hoststr, sizeof(hoststr), hostname);
Fabrice Bellard's avatar
Fabrice Bellard committed
84
    }
85

Fabrice Bellard's avatar
Fabrice Bellard committed
86
    if (use_proxy) {
87
        url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
88
                  NULL, 0, proxy_path);
89
        path = s->location;
Fabrice Bellard's avatar
Fabrice Bellard committed
90
    } else {
91
        if (path1[0] == '\0')
Fabrice Bellard's avatar
Fabrice Bellard committed
92 93
            path = "/";
        else
94
            path = path1;
Fabrice Bellard's avatar
Fabrice Bellard committed
95
    }
96 97
    if (port < 0)
        port = 80;
Fabrice Bellard's avatar
Fabrice Bellard committed
98

99 100 101
    snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
    err = url_open(&hd, buf, URL_RDWR);
    if (err < 0)
Fabrice Bellard's avatar
Fabrice Bellard committed
102 103
        goto fail;

104
    s->hd = hd;
105
    if (http_connect(h, path, hoststr, auth, &location_changed) < 0)
Fabrice Bellard's avatar
Fabrice Bellard committed
106
        goto fail;
107
    if (s->http_code == 303 && location_changed == 1) {
Fabrice Bellard's avatar
Fabrice Bellard committed
108
        /* url moved, get next */
109
        url_close(hd);
110 111 112
        if (redirects++ >= MAX_REDIRECTS)
            return AVERROR_IO;
        location_changed = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
113 114 115 116
        goto redo;
    }
    return 0;
 fail:
117 118
    if (hd)
        url_close(hd);
119
    return AVERROR_IO;
Fabrice Bellard's avatar
Fabrice Bellard committed
120 121
}

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
static int http_open(URLContext *h, const char *uri, int flags)
{
    HTTPContext *s;
    int ret;

    h->is_streamed = 1;

    s = av_malloc(sizeof(HTTPContext));
    if (!s) {
        return -ENOMEM;
    }
    h->priv_data = s;
    s->filesize = -1;
    s->off = 0;
    pstrcpy (s->location, URL_SIZE, uri);

    ret = http_open_cnx(h);
    if (ret != 0)
        av_free (s);
    return ret;
}
Fabrice Bellard's avatar
Fabrice Bellard committed
143 144 145 146
static int http_getc(HTTPContext *s)
{
    int len;
    if (s->buf_ptr >= s->buf_end) {
147
        len = url_read(s->hd, s->buffer, BUFFER_SIZE);
Fabrice Bellard's avatar
Fabrice Bellard committed
148
        if (len < 0) {
149
            return AVERROR_IO;
Fabrice Bellard's avatar
Fabrice Bellard committed
150 151 152 153 154 155 156 157 158 159
        } else if (len == 0) {
            return -1;
        } else {
            s->buf_ptr = s->buffer;
            s->buf_end = s->buffer + len;
        }
    }
    return *s->buf_ptr++;
}

160 161
static int process_line(URLContext *h, char *line, int line_count,
                        int *new_location)
Fabrice Bellard's avatar
Fabrice Bellard committed
162
{
163
    HTTPContext *s = h->priv_data;
Fabrice Bellard's avatar
Fabrice Bellard committed
164
    char *tag, *p;
165

Fabrice Bellard's avatar
Fabrice Bellard committed
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    /* end of header */
    if (line[0] == '\0')
        return 0;

    p = line;
    if (line_count == 0) {
        while (!isspace(*p) && *p != '\0')
            p++;
        while (isspace(*p))
            p++;
        s->http_code = strtol(p, NULL, 10);
#ifdef DEBUG
        printf("http_code=%d\n", s->http_code);
#endif
    } else {
        while (*p != '\0' && *p != ':')
            p++;
183
        if (*p != ':')
Fabrice Bellard's avatar
Fabrice Bellard committed
184
            return 1;
185

Fabrice Bellard's avatar
Fabrice Bellard committed
186 187 188 189 190 191 192
        *p = '\0';
        tag = line;
        p++;
        while (isspace(*p))
            p++;
        if (!strcmp(tag, "Location")) {
            strcpy(s->location, p);
193 194 195 196 197 198 199 200 201 202 203 204 205
            *new_location = 1;
        } else if (!strcmp (tag, "Content-Length") && s->filesize == -1) {
            s->filesize = atoll(p);
        } else if (!strcmp (tag, "Content-Range")) {
            /* "bytes $from-$to/$document_size" */
            const char *slash;
            if (!strncmp (p, "bytes ", 6)) {
                p += 6;
                s->off = atoll(p);
                if ((slash = strchr(p, '/')) && strlen(slash) > 0)
                    s->filesize = atoll(slash+1);
            }
            h->is_streamed = 0; /* we _can_ in fact seek */
Fabrice Bellard's avatar
Fabrice Bellard committed
206 207 208 209 210
        }
    }
    return 1;
}

211
static int http_connect(URLContext *h, const char *path, const char *hoststr,
212
                        const char *auth, int *new_location)
Fabrice Bellard's avatar
Fabrice Bellard committed
213 214 215 216
{
    HTTPContext *s = h->priv_data;
    int post, err, ch;
    char line[1024], *q;
217
    char *auth_b64;
218
    offset_t off = s->off;
Fabrice Bellard's avatar
Fabrice Bellard committed
219 220 221 222 223


    /* send http header */
    post = h->flags & URL_WRONLY;

224
    auth_b64 = av_base64_encode((uint8_t *)auth, strlen(auth));
Fabrice Bellard's avatar
Fabrice Bellard committed
225
    snprintf(s->buffer, sizeof(s->buffer),
226
             "%s %s HTTP/1.1\r\n"
227 228
             "User-Agent: %s\r\n"
             "Accept: */*\r\n"
229
             "Range: bytes=%llu-\r\n"
230
             "Host: %s\r\n"
231
             "Authorization: Basic %s\r\n"
232
             "\r\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
233 234
             post ? "POST" : "GET",
             path,
235
             LIBAVFORMAT_IDENT,
236
             s->off,
237
             hoststr,
238
             auth_b64);
239

240
    av_freep(&auth_b64);
Fabrice Bellard's avatar
Fabrice Bellard committed
241
    if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
242
        return AVERROR_IO;
243

Fabrice Bellard's avatar
Fabrice Bellard committed
244 245 246 247
    /* init input buffer */
    s->buf_ptr = s->buffer;
    s->buf_end = s->buffer;
    s->line_count = 0;
248
    s->off = 0;
249 250
    if (post) {
        sleep(1);
Fabrice Bellard's avatar
Fabrice Bellard committed
251
        return 0;
252
    }
253

Fabrice Bellard's avatar
Fabrice Bellard committed
254 255 256 257 258
    /* wait for header */
    q = line;
    for(;;) {
        ch = http_getc(s);
        if (ch < 0)
259
            return AVERROR_IO;
Fabrice Bellard's avatar
Fabrice Bellard committed
260 261 262 263 264 265 266 267
        if (ch == '\n') {
            /* process line */
            if (q > line && q[-1] == '\r')
                q--;
            *q = '\0';
#ifdef DEBUG
            printf("header='%s'\n", line);
#endif
268
            err = process_line(h, line, s->line_count, new_location);
Fabrice Bellard's avatar
Fabrice Bellard committed
269 270 271
            if (err < 0)
                return err;
            if (err == 0)
272
                break;
Fabrice Bellard's avatar
Fabrice Bellard committed
273 274 275 276 277 278 279
            s->line_count++;
            q = line;
        } else {
            if ((q - line) < sizeof(line) - 1)
                *q++ = ch;
        }
    }
280 281

    return (off == s->off) ? 0 : -1;
Fabrice Bellard's avatar
Fabrice Bellard committed
282 283 284
}


285
static int http_read(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
286 287
{
    HTTPContext *s = h->priv_data;
288
    int len;
Fabrice Bellard's avatar
Fabrice Bellard committed
289

290 291 292 293 294 295 296 297 298
    /* read bytes from input buffer first */
    len = s->buf_end - s->buf_ptr;
    if (len > 0) {
        if (len > size)
            len = size;
        memcpy(buf, s->buf_ptr, len);
        s->buf_ptr += len;
    } else {
        len = url_read(s->hd, buf, size);
Fabrice Bellard's avatar
Fabrice Bellard committed
299
    }
300 301
    if (len > 0)
        s->off += len;
302
    return len;
Fabrice Bellard's avatar
Fabrice Bellard committed
303 304 305
}

/* used only when posting data */
306
static int http_write(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
307 308
{
    HTTPContext *s = h->priv_data;
309
    return url_write(s->hd, buf, size);
Fabrice Bellard's avatar
Fabrice Bellard committed
310 311 312 313 314
}

static int http_close(URLContext *h)
{
    HTTPContext *s = h->priv_data;
315 316
    url_close(s->hd);
    av_free(s);
Fabrice Bellard's avatar
Fabrice Bellard committed
317 318 319
    return 0;
}

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
static offset_t http_seek(URLContext *h, offset_t off, int whence)
{
    HTTPContext *s = h->priv_data;
    URLContext *old_hd = s->hd;
    offset_t old_off = s->off;

    if (whence == AVSEEK_SIZE)
        return s->filesize;
    else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed)
        return -1;

    /* we save the old context in case the seek fails */
    s->hd = NULL;
    if (whence == SEEK_CUR)
        off += s->off;
    else if (whence == SEEK_END)
        off += s->filesize;
    s->off = off;

    /* if it fails, continue on old connection */
    if (http_open_cnx(h) < 0) {
        s->hd = old_hd;
        s->off = old_off;
        return -1;
    }
    url_close(old_hd);
    return off;
}

Fabrice Bellard's avatar
Fabrice Bellard committed
349 350 351 352 353
URLProtocol http_protocol = {
    "http",
    http_open,
    http_read,
    http_write,
354
    http_seek,
Fabrice Bellard's avatar
Fabrice Bellard committed
355 356
    http_close,
};