• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

libavformat/http.c

Go to the documentation of this file.
00001 /*
00002  * HTTP protocol for avconv client
00003  * Copyright (c) 2000, 2001 Fabrice Bellard
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * Libav is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00022 #include "libavutil/avstring.h"
00023 #include "avformat.h"
00024 #include <unistd.h>
00025 #include "internal.h"
00026 #include "network.h"
00027 #include "http.h"
00028 #include "os_support.h"
00029 #include "httpauth.h"
00030 #include "url.h"
00031 #include "libavutil/opt.h"
00032 
00033 /* XXX: POST protocol is not completely implemented because avconv uses
00034    only a subset of it. */
00035 
00036 /* used for protocol handling */
00037 #define BUFFER_SIZE 1024
00038 #define MAX_REDIRECTS 8
00039 
00040 typedef struct {
00041     const AVClass *class;
00042     URLContext *hd;
00043     unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
00044     int line_count;
00045     int http_code;
00046     int64_t chunksize;      
00047     int64_t off, filesize;
00048     char location[MAX_URL_SIZE];
00049     HTTPAuthState auth_state;
00050     HTTPAuthState proxy_auth_state;
00051     char *headers;
00052     int willclose;          
00053     int chunked_post;
00054 } HTTPContext;
00055 
00056 #define OFFSET(x) offsetof(HTTPContext, x)
00057 #define D AV_OPT_FLAG_DECODING_PARAM
00058 #define E AV_OPT_FLAG_ENCODING_PARAM
00059 static const AVOption options[] = {
00060 {"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.dbl = 1}, 0, 1, E },
00061 {"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
00062 {NULL}
00063 };
00064 #define HTTP_CLASS(flavor)\
00065 static const AVClass flavor ## _context_class = {\
00066     .class_name     = #flavor,\
00067     .item_name      = av_default_item_name,\
00068     .option         = options,\
00069     .version        = LIBAVUTIL_VERSION_INT,\
00070 }
00071 
00072 HTTP_CLASS(http);
00073 HTTP_CLASS(https);
00074 
00075 static int http_connect(URLContext *h, const char *path, const char *local_path,
00076                         const char *hoststr, const char *auth,
00077                         const char *proxyauth, int *new_location);
00078 
00079 void ff_http_init_auth_state(URLContext *dest, const URLContext *src)
00080 {
00081     memcpy(&((HTTPContext*)dest->priv_data)->auth_state,
00082            &((HTTPContext*)src->priv_data)->auth_state, sizeof(HTTPAuthState));
00083     memcpy(&((HTTPContext*)dest->priv_data)->proxy_auth_state,
00084            &((HTTPContext*)src->priv_data)->proxy_auth_state,
00085            sizeof(HTTPAuthState));
00086 }
00087 
00088 /* return non zero if error */
00089 static int http_open_cnx(URLContext *h)
00090 {
00091     const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
00092     char hostname[1024], hoststr[1024], proto[10];
00093     char auth[1024], proxyauth[1024] = "";
00094     char path1[1024];
00095     char buf[1024], urlbuf[1024];
00096     int port, use_proxy, err, location_changed = 0, redirects = 0;
00097     HTTPAuthType cur_auth_type, cur_proxy_auth_type;
00098     HTTPContext *s = h->priv_data;
00099     URLContext *hd = NULL;
00100 
00101     proxy_path = getenv("http_proxy");
00102     use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
00103         av_strstart(proxy_path, "http://", NULL);
00104 
00105     /* fill the dest addr */
00106  redo:
00107     /* needed in any case to build the host string */
00108     av_url_split(proto, sizeof(proto), auth, sizeof(auth),
00109                  hostname, sizeof(hostname), &port,
00110                  path1, sizeof(path1), s->location);
00111     ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
00112 
00113     if (!strcmp(proto, "https")) {
00114         lower_proto = "tls";
00115         use_proxy = 0;
00116         if (port < 0)
00117             port = 443;
00118     }
00119     if (port < 0)
00120         port = 80;
00121 
00122     if (path1[0] == '\0')
00123         path = "/";
00124     else
00125         path = path1;
00126     local_path = path;
00127     if (use_proxy) {
00128         /* Reassemble the request URL without auth string - we don't
00129          * want to leak the auth to the proxy. */
00130         ff_url_join(urlbuf, sizeof(urlbuf), proto, NULL, hostname, port, "%s",
00131                     path1);
00132         path = urlbuf;
00133         av_url_split(NULL, 0, proxyauth, sizeof(proxyauth),
00134                      hostname, sizeof(hostname), &port, NULL, 0, proxy_path);
00135     }
00136 
00137     ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
00138     err = ffurl_open(&hd, buf, AVIO_FLAG_READ_WRITE,
00139                      &h->interrupt_callback, NULL);
00140     if (err < 0)
00141         goto fail;
00142 
00143     s->hd = hd;
00144     cur_auth_type = s->auth_state.auth_type;
00145     cur_proxy_auth_type = s->auth_state.auth_type;
00146     if (http_connect(h, path, local_path, hoststr, auth, proxyauth, &location_changed) < 0)
00147         goto fail;
00148     if (s->http_code == 401) {
00149         if (cur_auth_type == HTTP_AUTH_NONE && s->auth_state.auth_type != HTTP_AUTH_NONE) {
00150             ffurl_close(hd);
00151             goto redo;
00152         } else
00153             goto fail;
00154     }
00155     if (s->http_code == 407) {
00156         if (cur_proxy_auth_type == HTTP_AUTH_NONE &&
00157             s->proxy_auth_state.auth_type != HTTP_AUTH_NONE) {
00158             ffurl_close(hd);
00159             goto redo;
00160         } else
00161             goto fail;
00162     }
00163     if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307)
00164         && location_changed == 1) {
00165         /* url moved, get next */
00166         ffurl_close(hd);
00167         if (redirects++ >= MAX_REDIRECTS)
00168             return AVERROR(EIO);
00169         location_changed = 0;
00170         goto redo;
00171     }
00172     return 0;
00173  fail:
00174     if (hd)
00175         ffurl_close(hd);
00176     s->hd = NULL;
00177     return AVERROR(EIO);
00178 }
00179 
00180 static int http_open(URLContext *h, const char *uri, int flags)
00181 {
00182     HTTPContext *s = h->priv_data;
00183 
00184     h->is_streamed = 1;
00185 
00186     s->filesize = -1;
00187     av_strlcpy(s->location, uri, sizeof(s->location));
00188 
00189     if (s->headers) {
00190         int len = strlen(s->headers);
00191         if (len < 2 || strcmp("\r\n", s->headers + len - 2))
00192             av_log(h, AV_LOG_WARNING, "No trailing CRLF found in HTTP header.\n");
00193     }
00194 
00195     return http_open_cnx(h);
00196 }
00197 static int http_getc(HTTPContext *s)
00198 {
00199     int len;
00200     if (s->buf_ptr >= s->buf_end) {
00201         len = ffurl_read(s->hd, s->buffer, BUFFER_SIZE);
00202         if (len < 0) {
00203             return AVERROR(EIO);
00204         } else if (len == 0) {
00205             return -1;
00206         } else {
00207             s->buf_ptr = s->buffer;
00208             s->buf_end = s->buffer + len;
00209         }
00210     }
00211     return *s->buf_ptr++;
00212 }
00213 
00214 static int http_get_line(HTTPContext *s, char *line, int line_size)
00215 {
00216     int ch;
00217     char *q;
00218 
00219     q = line;
00220     for(;;) {
00221         ch = http_getc(s);
00222         if (ch < 0)
00223             return AVERROR(EIO);
00224         if (ch == '\n') {
00225             /* process line */
00226             if (q > line && q[-1] == '\r')
00227                 q--;
00228             *q = '\0';
00229 
00230             return 0;
00231         } else {
00232             if ((q - line) < line_size - 1)
00233                 *q++ = ch;
00234         }
00235     }
00236 }
00237 
00238 static int process_line(URLContext *h, char *line, int line_count,
00239                         int *new_location)
00240 {
00241     HTTPContext *s = h->priv_data;
00242     char *tag, *p, *end;
00243 
00244     /* end of header */
00245     if (line[0] == '\0')
00246         return 0;
00247 
00248     p = line;
00249     if (line_count == 0) {
00250         while (!isspace(*p) && *p != '\0')
00251             p++;
00252         while (isspace(*p))
00253             p++;
00254         s->http_code = strtol(p, &end, 10);
00255 
00256         av_dlog(NULL, "http_code=%d\n", s->http_code);
00257 
00258         /* error codes are 4xx and 5xx, but regard 401 as a success, so we
00259          * don't abort until all headers have been parsed. */
00260         if (s->http_code >= 400 && s->http_code < 600 && (s->http_code != 401
00261             || s->auth_state.auth_type != HTTP_AUTH_NONE) &&
00262             (s->http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) {
00263             end += strspn(end, SPACE_CHARS);
00264             av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n",
00265                    s->http_code, end);
00266             return -1;
00267         }
00268     } else {
00269         while (*p != '\0' && *p != ':')
00270             p++;
00271         if (*p != ':')
00272             return 1;
00273 
00274         *p = '\0';
00275         tag = line;
00276         p++;
00277         while (isspace(*p))
00278             p++;
00279         if (!av_strcasecmp(tag, "Location")) {
00280             strcpy(s->location, p);
00281             *new_location = 1;
00282         } else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) {
00283             s->filesize = atoll(p);
00284         } else if (!av_strcasecmp (tag, "Content-Range")) {
00285             /* "bytes $from-$to/$document_size" */
00286             const char *slash;
00287             if (!strncmp (p, "bytes ", 6)) {
00288                 p += 6;
00289                 s->off = atoll(p);
00290                 if ((slash = strchr(p, '/')) && strlen(slash) > 0)
00291                     s->filesize = atoll(slash+1);
00292             }
00293             h->is_streamed = 0; /* we _can_ in fact seek */
00294         } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) {
00295             h->is_streamed = 0;
00296         } else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) {
00297             s->filesize = -1;
00298             s->chunksize = 0;
00299         } else if (!av_strcasecmp (tag, "WWW-Authenticate")) {
00300             ff_http_auth_handle_header(&s->auth_state, tag, p);
00301         } else if (!av_strcasecmp (tag, "Authentication-Info")) {
00302             ff_http_auth_handle_header(&s->auth_state, tag, p);
00303         } else if (!av_strcasecmp (tag, "Proxy-Authenticate")) {
00304             ff_http_auth_handle_header(&s->proxy_auth_state, tag, p);
00305         } else if (!av_strcasecmp (tag, "Connection")) {
00306             if (!strcmp(p, "close"))
00307                 s->willclose = 1;
00308         }
00309     }
00310     return 1;
00311 }
00312 
00313 static inline int has_header(const char *str, const char *header)
00314 {
00315     /* header + 2 to skip over CRLF prefix. (make sure you have one!) */
00316     if (!str)
00317         return 0;
00318     return av_stristart(str, header + 2, NULL) || av_stristr(str, header);
00319 }
00320 
00321 static int http_connect(URLContext *h, const char *path, const char *local_path,
00322                         const char *hoststr, const char *auth,
00323                         const char *proxyauth, int *new_location)
00324 {
00325     HTTPContext *s = h->priv_data;
00326     int post, err;
00327     char line[1024];
00328     char headers[1024] = "";
00329     char *authstr = NULL, *proxyauthstr = NULL;
00330     int64_t off = s->off;
00331     int len = 0;
00332     const char *method;
00333 
00334 
00335     /* send http header */
00336     post = h->flags & AVIO_FLAG_WRITE;
00337     method = post ? "POST" : "GET";
00338     authstr = ff_http_auth_create_response(&s->auth_state, auth, local_path,
00339                                            method);
00340     proxyauthstr = ff_http_auth_create_response(&s->proxy_auth_state, proxyauth,
00341                                                 local_path, method);
00342 
00343     /* set default headers if needed */
00344     if (!has_header(s->headers, "\r\nUser-Agent: "))
00345        len += av_strlcatf(headers + len, sizeof(headers) - len,
00346                           "User-Agent: %s\r\n", LIBAVFORMAT_IDENT);
00347     if (!has_header(s->headers, "\r\nAccept: "))
00348         len += av_strlcpy(headers + len, "Accept: */*\r\n",
00349                           sizeof(headers) - len);
00350     if (!has_header(s->headers, "\r\nRange: ") && !post)
00351         len += av_strlcatf(headers + len, sizeof(headers) - len,
00352                            "Range: bytes=%"PRId64"-\r\n", s->off);
00353     if (!has_header(s->headers, "\r\nConnection: "))
00354         len += av_strlcpy(headers + len, "Connection: close\r\n",
00355                           sizeof(headers)-len);
00356     if (!has_header(s->headers, "\r\nHost: "))
00357         len += av_strlcatf(headers + len, sizeof(headers) - len,
00358                            "Host: %s\r\n", hoststr);
00359 
00360     /* now add in custom headers */
00361     if (s->headers)
00362         av_strlcpy(headers + len, s->headers, sizeof(headers) - len);
00363 
00364     snprintf(s->buffer, sizeof(s->buffer),
00365              "%s %s HTTP/1.1\r\n"
00366              "%s"
00367              "%s"
00368              "%s"
00369              "%s%s"
00370              "\r\n",
00371              method,
00372              path,
00373              post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "",
00374              headers,
00375              authstr ? authstr : "",
00376              proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
00377 
00378     av_freep(&authstr);
00379     av_freep(&proxyauthstr);
00380     if (ffurl_write(s->hd, s->buffer, strlen(s->buffer)) < 0)
00381         return AVERROR(EIO);
00382 
00383     /* init input buffer */
00384     s->buf_ptr = s->buffer;
00385     s->buf_end = s->buffer;
00386     s->line_count = 0;
00387     s->off = 0;
00388     s->filesize = -1;
00389     s->willclose = 0;
00390     if (post) {
00391         /* Pretend that it did work. We didn't read any header yet, since
00392          * we've still to send the POST data, but the code calling this
00393          * function will check http_code after we return. */
00394         s->http_code = 200;
00395         return 0;
00396     }
00397     s->chunksize = -1;
00398 
00399     /* wait for header */
00400     for(;;) {
00401         if (http_get_line(s, line, sizeof(line)) < 0)
00402             return AVERROR(EIO);
00403 
00404         av_dlog(NULL, "header='%s'\n", line);
00405 
00406         err = process_line(h, line, s->line_count, new_location);
00407         if (err < 0)
00408             return err;
00409         if (err == 0)
00410             break;
00411         s->line_count++;
00412     }
00413 
00414     return (off == s->off) ? 0 : -1;
00415 }
00416 
00417 
00418 static int http_buf_read(URLContext *h, uint8_t *buf, int size)
00419 {
00420     HTTPContext *s = h->priv_data;
00421     int len;
00422     /* read bytes from input buffer first */
00423     len = s->buf_end - s->buf_ptr;
00424     if (len > 0) {
00425         if (len > size)
00426             len = size;
00427         memcpy(buf, s->buf_ptr, len);
00428         s->buf_ptr += len;
00429     } else {
00430         if (!s->willclose && s->filesize >= 0 && s->off >= s->filesize)
00431             return AVERROR_EOF;
00432         len = ffurl_read(s->hd, buf, size);
00433     }
00434     if (len > 0) {
00435         s->off += len;
00436         if (s->chunksize > 0)
00437             s->chunksize -= len;
00438     }
00439     return len;
00440 }
00441 
00442 static int http_read(URLContext *h, uint8_t *buf, int size)
00443 {
00444     HTTPContext *s = h->priv_data;
00445 
00446     if (s->chunksize >= 0) {
00447         if (!s->chunksize) {
00448             char line[32];
00449 
00450             for(;;) {
00451                 do {
00452                     if (http_get_line(s, line, sizeof(line)) < 0)
00453                         return AVERROR(EIO);
00454                 } while (!*line);    /* skip CR LF from last chunk */
00455 
00456                 s->chunksize = strtoll(line, NULL, 16);
00457 
00458                 av_dlog(NULL, "Chunked encoding data size: %"PRId64"'\n", s->chunksize);
00459 
00460                 if (!s->chunksize)
00461                     return 0;
00462                 break;
00463             }
00464         }
00465         size = FFMIN(size, s->chunksize);
00466     }
00467     return http_buf_read(h, buf, size);
00468 }
00469 
00470 /* used only when posting data */
00471 static int http_write(URLContext *h, const uint8_t *buf, int size)
00472 {
00473     char temp[11] = "";  /* 32-bit hex + CRLF + nul */
00474     int ret;
00475     char crlf[] = "\r\n";
00476     HTTPContext *s = h->priv_data;
00477 
00478     if (!s->chunked_post) {
00479         /* non-chunked data is sent without any special encoding */
00480         return ffurl_write(s->hd, buf, size);
00481     }
00482 
00483     /* silently ignore zero-size data since chunk encoding that would
00484      * signal EOF */
00485     if (size > 0) {
00486         /* upload data using chunked encoding */
00487         snprintf(temp, sizeof(temp), "%x\r\n", size);
00488 
00489         if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
00490             (ret = ffurl_write(s->hd, buf, size)) < 0 ||
00491             (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
00492             return ret;
00493     }
00494     return size;
00495 }
00496 
00497 static int http_close(URLContext *h)
00498 {
00499     int ret = 0;
00500     char footer[] = "0\r\n\r\n";
00501     HTTPContext *s = h->priv_data;
00502 
00503     /* signal end of chunked encoding if used */
00504     if ((h->flags & AVIO_FLAG_WRITE) && s->chunked_post) {
00505         ret = ffurl_write(s->hd, footer, sizeof(footer) - 1);
00506         ret = ret > 0 ? 0 : ret;
00507     }
00508 
00509     if (s->hd)
00510         ffurl_close(s->hd);
00511     return ret;
00512 }
00513 
00514 static int64_t http_seek(URLContext *h, int64_t off, int whence)
00515 {
00516     HTTPContext *s = h->priv_data;
00517     URLContext *old_hd = s->hd;
00518     int64_t old_off = s->off;
00519     uint8_t old_buf[BUFFER_SIZE];
00520     int old_buf_size;
00521 
00522     if (whence == AVSEEK_SIZE)
00523         return s->filesize;
00524     else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed)
00525         return -1;
00526 
00527     /* we save the old context in case the seek fails */
00528     old_buf_size = s->buf_end - s->buf_ptr;
00529     memcpy(old_buf, s->buf_ptr, old_buf_size);
00530     s->hd = NULL;
00531     if (whence == SEEK_CUR)
00532         off += s->off;
00533     else if (whence == SEEK_END)
00534         off += s->filesize;
00535     s->off = off;
00536 
00537     /* if it fails, continue on old connection */
00538     if (http_open_cnx(h) < 0) {
00539         memcpy(s->buffer, old_buf, old_buf_size);
00540         s->buf_ptr = s->buffer;
00541         s->buf_end = s->buffer + old_buf_size;
00542         s->hd = old_hd;
00543         s->off = old_off;
00544         return -1;
00545     }
00546     ffurl_close(old_hd);
00547     return off;
00548 }
00549 
00550 static int
00551 http_get_file_handle(URLContext *h)
00552 {
00553     HTTPContext *s = h->priv_data;
00554     return ffurl_get_file_handle(s->hd);
00555 }
00556 
00557 #if CONFIG_HTTP_PROTOCOL
00558 URLProtocol ff_http_protocol = {
00559     .name                = "http",
00560     .url_open            = http_open,
00561     .url_read            = http_read,
00562     .url_write           = http_write,
00563     .url_seek            = http_seek,
00564     .url_close           = http_close,
00565     .url_get_file_handle = http_get_file_handle,
00566     .priv_data_size      = sizeof(HTTPContext),
00567     .priv_data_class     = &http_context_class,
00568     .flags               = URL_PROTOCOL_FLAG_NETWORK,
00569 };
00570 #endif
00571 #if CONFIG_HTTPS_PROTOCOL
00572 URLProtocol ff_https_protocol = {
00573     .name                = "https",
00574     .url_open            = http_open,
00575     .url_read            = http_read,
00576     .url_write           = http_write,
00577     .url_seek            = http_seek,
00578     .url_close           = http_close,
00579     .url_get_file_handle = http_get_file_handle,
00580     .priv_data_size      = sizeof(HTTPContext),
00581     .priv_data_class     = &https_context_class,
00582     .flags               = URL_PROTOCOL_FLAG_NETWORK,
00583 };
00584 #endif
00585 
00586 #if CONFIG_HTTPPROXY_PROTOCOL
00587 static int http_proxy_close(URLContext *h)
00588 {
00589     HTTPContext *s = h->priv_data;
00590     if (s->hd)
00591         ffurl_close(s->hd);
00592     return 0;
00593 }
00594 
00595 static int http_proxy_open(URLContext *h, const char *uri, int flags)
00596 {
00597     HTTPContext *s = h->priv_data;
00598     char hostname[1024], hoststr[1024];
00599     char auth[1024], pathbuf[1024], *path;
00600     char line[1024], lower_url[100];
00601     int port, ret = 0;
00602     HTTPAuthType cur_auth_type;
00603     char *authstr;
00604 
00605     h->is_streamed = 1;
00606 
00607     av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
00608                  pathbuf, sizeof(pathbuf), uri);
00609     ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
00610     path = pathbuf;
00611     if (*path == '/')
00612         path++;
00613 
00614     ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port,
00615                 NULL);
00616 redo:
00617     ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
00618                      &h->interrupt_callback, NULL);
00619     if (ret < 0)
00620         return ret;
00621 
00622     authstr = ff_http_auth_create_response(&s->proxy_auth_state, auth,
00623                                            path, "CONNECT");
00624     snprintf(s->buffer, sizeof(s->buffer),
00625              "CONNECT %s HTTP/1.1\r\n"
00626              "Host: %s\r\n"
00627              "Connection: close\r\n"
00628              "%s%s"
00629              "\r\n",
00630              path,
00631              hoststr,
00632              authstr ? "Proxy-" : "", authstr ? authstr : "");
00633     av_freep(&authstr);
00634 
00635     if ((ret = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
00636         goto fail;
00637 
00638     s->buf_ptr = s->buffer;
00639     s->buf_end = s->buffer;
00640     s->line_count = 0;
00641     s->filesize = -1;
00642     cur_auth_type = s->proxy_auth_state.auth_type;
00643 
00644     for (;;) {
00645         int new_loc;
00646         // Note: This uses buffering, potentially reading more than the
00647         // HTTP header. If tunneling a protocol where the server starts
00648         // the conversation, we might buffer part of that here, too.
00649         // Reading that requires using the proper ffurl_read() function
00650         // on this URLContext, not using the fd directly (as the tls
00651         // protocol does). This shouldn't be an issue for tls though,
00652         // since the client starts the conversation there, so there
00653         // is no extra data that we might buffer up here.
00654         if (http_get_line(s, line, sizeof(line)) < 0) {
00655             ret = AVERROR(EIO);
00656             goto fail;
00657         }
00658 
00659         av_dlog(h, "header='%s'\n", line);
00660 
00661         ret = process_line(h, line, s->line_count, &new_loc);
00662         if (ret < 0)
00663             goto fail;
00664         if (ret == 0)
00665             break;
00666         s->line_count++;
00667     }
00668     if (s->http_code == 407 && cur_auth_type == HTTP_AUTH_NONE &&
00669         s->proxy_auth_state.auth_type != HTTP_AUTH_NONE) {
00670         ffurl_close(s->hd);
00671         s->hd = NULL;
00672         goto redo;
00673     }
00674 
00675     if (s->http_code < 400)
00676         return 0;
00677     ret = AVERROR(EIO);
00678 
00679 fail:
00680     http_proxy_close(h);
00681     return ret;
00682 }
00683 
00684 static int http_proxy_write(URLContext *h, const uint8_t *buf, int size)
00685 {
00686     HTTPContext *s = h->priv_data;
00687     return ffurl_write(s->hd, buf, size);
00688 }
00689 
00690 URLProtocol ff_httpproxy_protocol = {
00691     .name                = "httpproxy",
00692     .url_open            = http_proxy_open,
00693     .url_read            = http_buf_read,
00694     .url_write           = http_proxy_write,
00695     .url_close           = http_proxy_close,
00696     .url_get_file_handle = http_get_file_handle,
00697     .priv_data_size      = sizeof(HTTPContext),
00698     .flags               = URL_PROTOCOL_FLAG_NETWORK,
00699 };
00700 #endif
Generated on Thu Jan 24 2013 17:08:55 for Libav by doxygen 1.7.1