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


/* 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

typedef struct {
42
    URLContext *hd;
Fabrice Bellard's avatar
Fabrice Bellard committed
43 44 45 46 47 48
    unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
    int line_count;
    int http_code;
    char location[URL_SIZE];
} HTTPContext;

49 50
static int http_connect(URLContext *h, const char *path, const char *hoststr,
                        const char *auth);
51
static int http_write(URLContext *h, uint8_t *buf, int size);
52
static char *b64_encode(const unsigned char *src );
Fabrice Bellard's avatar
Fabrice Bellard committed
53

54

Fabrice Bellard's avatar
Fabrice Bellard committed
55 56 57
/* return non zero if error */
static int http_open(URLContext *h, const char *uri, int flags)
{
58 59
    const char *path, *proxy_path;
    char hostname[1024], hoststr[1024];
60
    char auth[1024];
61 62 63
    char path1[1024];
    char buf[1024];
    int port, use_proxy, err;
Fabrice Bellard's avatar
Fabrice Bellard committed
64
    HTTPContext *s;
65
    URLContext *hd = NULL;
Fabrice Bellard's avatar
Fabrice Bellard committed
66 67 68

    h->is_streamed = 1;

69
    s = av_malloc(sizeof(HTTPContext));
Fabrice Bellard's avatar
Fabrice Bellard committed
70 71 72 73 74 75
    if (!s) {
        return -ENOMEM;
    }
    h->priv_data = s;

    proxy_path = getenv("http_proxy");
76
    use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
77
        strstart(proxy_path, "http://", NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
78 79 80

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

Fabrice Bellard's avatar
Fabrice Bellard committed
90
    if (use_proxy) {
91
        url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
92
                  NULL, 0, proxy_path);
Fabrice Bellard's avatar
Fabrice Bellard committed
93 94
        path = uri;
    } else {
95
        if (path1[0] == '\0')
Fabrice Bellard's avatar
Fabrice Bellard committed
96 97
            path = "/";
        else
98
            path = path1;
Fabrice Bellard's avatar
Fabrice Bellard committed
99
    }
100 101
    if (port < 0)
        port = 80;
Fabrice Bellard's avatar
Fabrice Bellard committed
102

103 104 105
    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
106 107
        goto fail;

108
    s->hd = hd;
109
    if (http_connect(h, path, hoststr, auth) < 0)
Fabrice Bellard's avatar
Fabrice Bellard committed
110 111 112 113
        goto fail;
    if (s->http_code == 303 && s->location[0] != '\0') {
        /* url moved, get next */
        uri = s->location;
114
        url_close(hd);
Fabrice Bellard's avatar
Fabrice Bellard committed
115 116 117 118
        goto redo;
    }
    return 0;
 fail:
119 120
    if (hd)
        url_close(hd);
121
    av_free(s);
122
    return AVERROR_IO;
Fabrice Bellard's avatar
Fabrice Bellard committed
123 124 125 126 127 128
}

static int http_getc(HTTPContext *s)
{
    int len;
    if (s->buf_ptr >= s->buf_end) {
129
        len = url_read(s->hd, s->buffer, BUFFER_SIZE);
Fabrice Bellard's avatar
Fabrice Bellard committed
130
        if (len < 0) {
131
            return AVERROR_IO;
Fabrice Bellard's avatar
Fabrice Bellard committed
132 133 134 135 136 137 138 139 140 141 142 143 144
        } else if (len == 0) {
            return -1;
        } else {
            s->buf_ptr = s->buffer;
            s->buf_end = s->buffer + len;
        }
    }
    return *s->buf_ptr++;
}

static int process_line(HTTPContext *s, char *line, int line_count)
{
    char *tag, *p;
145

Fabrice Bellard's avatar
Fabrice Bellard committed
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    /* 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++;
163
        if (*p != ':')
Fabrice Bellard's avatar
Fabrice Bellard committed
164
            return 1;
165

Fabrice Bellard's avatar
Fabrice Bellard committed
166 167 168 169 170 171 172 173 174 175 176 177
        *p = '\0';
        tag = line;
        p++;
        while (isspace(*p))
            p++;
        if (!strcmp(tag, "Location")) {
            strcpy(s->location, p);
        }
    }
    return 1;
}

178 179
static int http_connect(URLContext *h, const char *path, const char *hoststr,
                        const char *auth)
Fabrice Bellard's avatar
Fabrice Bellard committed
180 181 182 183
{
    HTTPContext *s = h->priv_data;
    int post, err, ch;
    char line[1024], *q;
184
    char *auth_b64;
Fabrice Bellard's avatar
Fabrice Bellard committed
185 186 187 188 189


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

190
    auth_b64 = b64_encode(auth);
Fabrice Bellard's avatar
Fabrice Bellard committed
191
    snprintf(s->buffer, sizeof(s->buffer),
192 193 194 195
             "%s %s HTTP/1.0\r\n"
             "User-Agent: %s\r\n"
             "Accept: */*\r\n"
             "Host: %s\r\n"
196
             "Authorization: Basic %s\r\n"
197
             "\r\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
198 199
             post ? "POST" : "GET",
             path,
200
             LIBAVFORMAT_IDENT,
201
             hoststr,
202
             auth_b64);
203

204
    av_freep(&auth_b64);
Fabrice Bellard's avatar
Fabrice Bellard committed
205
    if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
206
        return AVERROR_IO;
207

Fabrice Bellard's avatar
Fabrice Bellard committed
208 209 210 211 212
    /* init input buffer */
    s->buf_ptr = s->buffer;
    s->buf_end = s->buffer;
    s->line_count = 0;
    s->location[0] = '\0';
213 214
    if (post) {
        sleep(1);
Fabrice Bellard's avatar
Fabrice Bellard committed
215
        return 0;
216
    }
217

Fabrice Bellard's avatar
Fabrice Bellard committed
218 219 220 221 222
    /* wait for header */
    q = line;
    for(;;) {
        ch = http_getc(s);
        if (ch < 0)
223
            return AVERROR_IO;
Fabrice Bellard's avatar
Fabrice Bellard committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
        if (ch == '\n') {
            /* process line */
            if (q > line && q[-1] == '\r')
                q--;
            *q = '\0';
#ifdef DEBUG
            printf("header='%s'\n", line);
#endif
            err = process_line(s, line, s->line_count);
            if (err < 0)
                return err;
            if (err == 0)
                return 0;
            s->line_count++;
            q = line;
        } else {
            if ((q - line) < sizeof(line) - 1)
                *q++ = ch;
        }
    }
}


247
static int http_read(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
248 249
{
    HTTPContext *s = h->priv_data;
250
    int len;
Fabrice Bellard's avatar
Fabrice Bellard committed
251

252 253 254 255 256 257 258 259 260
    /* 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
261
    }
262
    return len;
Fabrice Bellard's avatar
Fabrice Bellard committed
263 264 265
}

/* used only when posting data */
266
static int http_write(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
267 268
{
    HTTPContext *s = h->priv_data;
269
    return url_write(s->hd, buf, size);
Fabrice Bellard's avatar
Fabrice Bellard committed
270 271 272 273 274
}

static int http_close(URLContext *h)
{
    HTTPContext *s = h->priv_data;
275 276
    url_close(s->hd);
    av_free(s);
Fabrice Bellard's avatar
Fabrice Bellard committed
277 278 279 280 281 282 283 284 285 286 287
    return 0;
}

URLProtocol http_protocol = {
    "http",
    http_open,
    http_read,
    http_write,
    NULL, /* seek */
    http_close,
};
288

289 290
/*****************************************************************************
 * b64_encode: stolen from VLC's http.c
291
 * simplified by michael
292
 *****************************************************************************/
293

294
static char *b64_encode( const unsigned char *src )
295 296
{
    static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
297 298
    unsigned int len= strlen(src);
    char *ret, *dst;
299 300
    unsigned i_bits = 0;
    unsigned i_shift = 0;
301 302 303 304 305 306

    if(len < UINT_MAX/4){
        ret=dst= av_malloc( len * 4 / 3 + 12 );
    }else
        return NULL;

307 308 309
    while(*src){
        i_bits = (i_bits << 8) + *src++;
        i_shift += 8;
310

311 312
        do{
            *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
313
            i_shift -= 6;
314
        }while( i_shift > 6 || (*src == 0 && i_shift>0));
315
    }
316 317
    *dst++ = '=';
    *dst   = '\0';
318

319 320 321
    return ret;
}