http.c 8.19 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 17
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 77
    use_proxy = (proxy_path != NULL) && !getenv("no_proxy") && 
        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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
        } 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;
    
    /* 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++;
        if (*p != ':') 
            return 1;
        
        *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 184 185 186 187 188 189
{
    HTTPContext *s = h->priv_data;
    int post, err, ch;
    char line[1024], *q;


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

    snprintf(s->buffer, sizeof(s->buffer),
190 191 192 193
             "%s %s HTTP/1.0\r\n"
             "User-Agent: %s\r\n"
             "Accept: */*\r\n"
             "Host: %s\r\n"
194
             "Authorization: Basic %s\r\n"
195
             "\r\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
196 197
             post ? "POST" : "GET",
             path,
198
             LIBAVFORMAT_IDENT,
199 200
             hoststr,
             b64_encode(auth));
201
    
Fabrice Bellard's avatar
Fabrice Bellard committed
202
    if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
203
        return AVERROR_IO;
Fabrice Bellard's avatar
Fabrice Bellard committed
204 205 206 207 208 209
        
    /* init input buffer */
    s->buf_ptr = s->buffer;
    s->buf_end = s->buffer;
    s->line_count = 0;
    s->location[0] = '\0';
210 211
    if (post) {
        sleep(1);
Fabrice Bellard's avatar
Fabrice Bellard committed
212
        return 0;
213
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
214 215 216 217 218 219
    
    /* wait for header */
    q = line;
    for(;;) {
        ch = http_getc(s);
        if (ch < 0)
220
            return AVERROR_IO;
Fabrice Bellard's avatar
Fabrice Bellard committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
        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;
        }
    }
}


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

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

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

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

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

286 287 288 289
/*****************************************************************************
 * b64_encode: stolen from VLC's http.c
 *****************************************************************************/
                                                                                
290
static char *b64_encode( const unsigned char *src )
291 292
{
    static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
293 294
    unsigned int len= strlen(src);
    char *ret, *dst;
295 296
    unsigned i_bits = 0;
    unsigned i_shift = 0;
297 298 299 300 301 302

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

303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    for( ;; )
    {
        if( *src )
        {
            i_bits = ( i_bits << 8 )|( *src++ );
            i_shift += 8;
        }
        else if( i_shift > 0 )
        {
           i_bits <<= 6 - i_shift;
           i_shift = 6;
        }
        else
        {
            *dst++ = '=';
            break;
        }
                                                                                
        while( i_shift >= 6 )
        {
            i_shift -= 6;
            *dst++ = b64[(i_bits >> i_shift)&0x3f];
        }
    }
                                                                                
    *dst++ = '\0';
                                                                                
    return ret;
}