00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "libavutil/avstring.h"
00023 #include "avformat.h"
00024 #include "internal.h"
00025 #include "network.h"
00026 #include "http.h"
00027 #include "os_support.h"
00028 #include "httpauth.h"
00029 #include "url.h"
00030 #include "libavutil/opt.h"
00031
00032
00033
00034
00035
00036 #define BUFFER_SIZE 4096
00037 #define MAX_REDIRECTS 8
00038
00039 typedef struct {
00040 const AVClass *class;
00041 URLContext *hd;
00042 unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
00043 int line_count;
00044 int http_code;
00045 int64_t chunksize;
00046 char *user_agent;
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 int end_chunked_post;
00055 int end_header;
00056 int multiple_requests;
00057 uint8_t *post_data;
00058 int post_datalen;
00059 int is_akamai;
00060 } HTTPContext;
00061
00062 #define OFFSET(x) offsetof(HTTPContext, x)
00063 #define D AV_OPT_FLAG_DECODING_PARAM
00064 #define E AV_OPT_FLAG_ENCODING_PARAM
00065 #define DEC AV_OPT_FLAG_DECODING_PARAM
00066 static const AVOption options[] = {
00067 {"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
00068 {"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
00069 {"user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC},
00070 {"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
00071 {"post_data", "custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E },
00072 {NULL}
00073 };
00074 #define HTTP_CLASS(flavor)\
00075 static const AVClass flavor ## _context_class = {\
00076 .class_name = #flavor,\
00077 .item_name = av_default_item_name,\
00078 .option = options,\
00079 .version = LIBAVUTIL_VERSION_INT,\
00080 }
00081
00082 HTTP_CLASS(http);
00083 HTTP_CLASS(https);
00084
00085 static int http_connect(URLContext *h, const char *path, const char *local_path,
00086 const char *hoststr, const char *auth,
00087 const char *proxyauth, int *new_location);
00088
00089 void ff_http_init_auth_state(URLContext *dest, const URLContext *src)
00090 {
00091 memcpy(&((HTTPContext*)dest->priv_data)->auth_state,
00092 &((HTTPContext*)src->priv_data)->auth_state, sizeof(HTTPAuthState));
00093 memcpy(&((HTTPContext*)dest->priv_data)->proxy_auth_state,
00094 &((HTTPContext*)src->priv_data)->proxy_auth_state,
00095 sizeof(HTTPAuthState));
00096 }
00097
00098
00099 static int http_open_cnx(URLContext *h)
00100 {
00101 const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
00102 char hostname[1024], hoststr[1024], proto[10];
00103 char auth[1024], proxyauth[1024] = "";
00104 char path1[1024];
00105 char buf[1024], urlbuf[1024];
00106 int port, use_proxy, err, location_changed = 0, redirects = 0, attempts = 0;
00107 HTTPAuthType cur_auth_type, cur_proxy_auth_type;
00108 HTTPContext *s = h->priv_data;
00109
00110 proxy_path = getenv("http_proxy");
00111 use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
00112 av_strstart(proxy_path, "http://", NULL);
00113
00114
00115 redo:
00116
00117 av_url_split(proto, sizeof(proto), auth, sizeof(auth),
00118 hostname, sizeof(hostname), &port,
00119 path1, sizeof(path1), s->location);
00120 ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
00121
00122 if (!strcmp(proto, "https")) {
00123 lower_proto = "tls";
00124 use_proxy = 0;
00125 if (port < 0)
00126 port = 443;
00127 }
00128 if (port < 0)
00129 port = 80;
00130
00131 if (path1[0] == '\0')
00132 path = "/";
00133 else
00134 path = path1;
00135 local_path = path;
00136 if (use_proxy) {
00137
00138
00139 ff_url_join(urlbuf, sizeof(urlbuf), proto, NULL, hostname, port, "%s",
00140 path1);
00141 path = urlbuf;
00142 av_url_split(NULL, 0, proxyauth, sizeof(proxyauth),
00143 hostname, sizeof(hostname), &port, NULL, 0, proxy_path);
00144 }
00145
00146 ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
00147
00148 if (!s->hd) {
00149 err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE,
00150 &h->interrupt_callback, NULL);
00151 if (err < 0)
00152 goto fail;
00153 }
00154
00155 cur_auth_type = s->auth_state.auth_type;
00156 cur_proxy_auth_type = s->auth_state.auth_type;
00157 if (http_connect(h, path, local_path, hoststr, auth, proxyauth, &location_changed) < 0)
00158 goto fail;
00159 attempts++;
00160 if (s->http_code == 401) {
00161 if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) &&
00162 s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
00163 ffurl_closep(&s->hd);
00164 goto redo;
00165 } else
00166 goto fail;
00167 }
00168 if (s->http_code == 407) {
00169 if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
00170 s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
00171 ffurl_closep(&s->hd);
00172 goto redo;
00173 } else
00174 goto fail;
00175 }
00176 if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307)
00177 && location_changed == 1) {
00178
00179 ffurl_closep(&s->hd);
00180 if (redirects++ >= MAX_REDIRECTS)
00181 return AVERROR(EIO);
00182
00183
00184 memset(&s->auth_state, 0, sizeof(s->auth_state));
00185 attempts = 0;
00186 location_changed = 0;
00187 goto redo;
00188 }
00189 return 0;
00190 fail:
00191 if (s->hd)
00192 ffurl_closep(&s->hd);
00193 return AVERROR(EIO);
00194 }
00195
00196 int ff_http_do_new_request(URLContext *h, const char *uri)
00197 {
00198 HTTPContext *s = h->priv_data;
00199
00200 s->off = 0;
00201 av_strlcpy(s->location, uri, sizeof(s->location));
00202
00203 return http_open_cnx(h);
00204 }
00205
00206 static int http_open(URLContext *h, const char *uri, int flags)
00207 {
00208 HTTPContext *s = h->priv_data;
00209
00210 h->is_streamed = 1;
00211
00212 s->filesize = -1;
00213 av_strlcpy(s->location, uri, sizeof(s->location));
00214
00215 if (s->headers) {
00216 int len = strlen(s->headers);
00217 if (len < 2 || strcmp("\r\n", s->headers + len - 2))
00218 av_log(h, AV_LOG_WARNING, "No trailing CRLF found in HTTP header.\n");
00219 }
00220
00221 return http_open_cnx(h);
00222 }
00223 static int http_getc(HTTPContext *s)
00224 {
00225 int len;
00226 if (s->buf_ptr >= s->buf_end) {
00227 len = ffurl_read(s->hd, s->buffer, BUFFER_SIZE);
00228 if (len < 0) {
00229 return len;
00230 } else if (len == 0) {
00231 return -1;
00232 } else {
00233 s->buf_ptr = s->buffer;
00234 s->buf_end = s->buffer + len;
00235 }
00236 }
00237 return *s->buf_ptr++;
00238 }
00239
00240 static int http_get_line(HTTPContext *s, char *line, int line_size)
00241 {
00242 int ch;
00243 char *q;
00244
00245 q = line;
00246 for(;;) {
00247 ch = http_getc(s);
00248 if (ch < 0)
00249 return ch;
00250 if (ch == '\n') {
00251
00252 if (q > line && q[-1] == '\r')
00253 q--;
00254 *q = '\0';
00255
00256 return 0;
00257 } else {
00258 if ((q - line) < line_size - 1)
00259 *q++ = ch;
00260 }
00261 }
00262 }
00263
00264 static int process_line(URLContext *h, char *line, int line_count,
00265 int *new_location)
00266 {
00267 HTTPContext *s = h->priv_data;
00268 char *tag, *p, *end;
00269
00270
00271 if (line[0] == '\0') {
00272 s->end_header = 1;
00273 return 0;
00274 }
00275
00276 p = line;
00277 if (line_count == 0) {
00278 while (!isspace(*p) && *p != '\0')
00279 p++;
00280 while (isspace(*p))
00281 p++;
00282 s->http_code = strtol(p, &end, 10);
00283
00284 av_dlog(NULL, "http_code=%d\n", s->http_code);
00285
00286
00287
00288 if (s->http_code >= 400 && s->http_code < 600 && (s->http_code != 401
00289 || s->auth_state.auth_type != HTTP_AUTH_NONE) &&
00290 (s->http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) {
00291 end += strspn(end, SPACE_CHARS);
00292 av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n",
00293 s->http_code, end);
00294 return -1;
00295 }
00296 } else {
00297 while (*p != '\0' && *p != ':')
00298 p++;
00299 if (*p != ':')
00300 return 1;
00301
00302 *p = '\0';
00303 tag = line;
00304 p++;
00305 while (isspace(*p))
00306 p++;
00307 if (!av_strcasecmp(tag, "Location")) {
00308 strcpy(s->location, p);
00309 *new_location = 1;
00310 } else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) {
00311 s->filesize = strtoll(p, NULL, 10);
00312 } else if (!av_strcasecmp (tag, "Content-Range")) {
00313
00314 const char *slash;
00315 if (!strncmp (p, "bytes ", 6)) {
00316 p += 6;
00317 s->off = strtoll(p, NULL, 10);
00318 if ((slash = strchr(p, '/')) && strlen(slash) > 0)
00319 s->filesize = strtoll(slash+1, NULL, 10);
00320 }
00321 if (!s->is_akamai || s->filesize != 2147483647)
00322 h->is_streamed = 0;
00323 } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) {
00324 h->is_streamed = 0;
00325 } else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) {
00326 s->filesize = -1;
00327 s->chunksize = 0;
00328 } else if (!av_strcasecmp (tag, "WWW-Authenticate")) {
00329 ff_http_auth_handle_header(&s->auth_state, tag, p);
00330 } else if (!av_strcasecmp (tag, "Authentication-Info")) {
00331 ff_http_auth_handle_header(&s->auth_state, tag, p);
00332 } else if (!av_strcasecmp (tag, "Proxy-Authenticate")) {
00333 ff_http_auth_handle_header(&s->proxy_auth_state, tag, p);
00334 } else if (!av_strcasecmp (tag, "Connection")) {
00335 if (!strcmp(p, "close"))
00336 s->willclose = 1;
00337 } else if (!av_strcasecmp (tag, "Server") && !av_strcasecmp (p, "AkamaiGHost")) {
00338 s->is_akamai = 1;
00339 }
00340 }
00341 return 1;
00342 }
00343
00344 static inline int has_header(const char *str, const char *header)
00345 {
00346
00347 if (!str)
00348 return 0;
00349 return av_stristart(str, header + 2, NULL) || av_stristr(str, header);
00350 }
00351
00352 static int http_read_header(URLContext *h, int *new_location)
00353 {
00354 HTTPContext *s = h->priv_data;
00355 char line[1024];
00356 int err = 0;
00357
00358 s->chunksize = -1;
00359
00360 for (;;) {
00361 if ((err = http_get_line(s, line, sizeof(line))) < 0)
00362 return err;
00363
00364 av_dlog(NULL, "header='%s'\n", line);
00365
00366 err = process_line(h, line, s->line_count, new_location);
00367 if (err < 0)
00368 return err;
00369 if (err == 0)
00370 break;
00371 s->line_count++;
00372 }
00373
00374 return err;
00375 }
00376
00377 static int http_connect(URLContext *h, const char *path, const char *local_path,
00378 const char *hoststr, const char *auth,
00379 const char *proxyauth, int *new_location)
00380 {
00381 HTTPContext *s = h->priv_data;
00382 int post, err;
00383 char headers[4096] = "";
00384 char *authstr = NULL, *proxyauthstr = NULL;
00385 int64_t off = s->off;
00386 int len = 0;
00387 const char *method;
00388
00389
00390
00391 post = h->flags & AVIO_FLAG_WRITE;
00392
00393 if (s->post_data) {
00394
00395
00396 post = 1;
00397 s->chunked_post = 0;
00398 }
00399
00400 method = post ? "POST" : "GET";
00401 authstr = ff_http_auth_create_response(&s->auth_state, auth, local_path,
00402 method);
00403 proxyauthstr = ff_http_auth_create_response(&s->proxy_auth_state, proxyauth,
00404 local_path, method);
00405
00406
00407 if (!has_header(s->headers, "\r\nUser-Agent: "))
00408 len += av_strlcatf(headers + len, sizeof(headers) - len,
00409 "User-Agent: %s\r\n",
00410 s->user_agent ? s->user_agent : LIBAVFORMAT_IDENT);
00411 if (!has_header(s->headers, "\r\nAccept: "))
00412 len += av_strlcpy(headers + len, "Accept: */*\r\n",
00413 sizeof(headers) - len);
00414
00415
00416
00417 if (!has_header(s->headers, "\r\nRange: ") && !post)
00418 len += av_strlcatf(headers + len, sizeof(headers) - len,
00419 "Range: bytes=%"PRId64"-\r\n", s->off);
00420
00421 if (!has_header(s->headers, "\r\nConnection: ")) {
00422 if (s->multiple_requests) {
00423 len += av_strlcpy(headers + len, "Connection: keep-alive\r\n",
00424 sizeof(headers) - len);
00425 } else {
00426 len += av_strlcpy(headers + len, "Connection: close\r\n",
00427 sizeof(headers) - len);
00428 }
00429 }
00430
00431 if (!has_header(s->headers, "\r\nHost: "))
00432 len += av_strlcatf(headers + len, sizeof(headers) - len,
00433 "Host: %s\r\n", hoststr);
00434 if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data)
00435 len += av_strlcatf(headers + len, sizeof(headers) - len,
00436 "Content-Length: %d\r\n", s->post_datalen);
00437
00438
00439 if (s->headers)
00440 av_strlcpy(headers + len, s->headers, sizeof(headers) - len);
00441
00442 snprintf(s->buffer, sizeof(s->buffer),
00443 "%s %s HTTP/1.1\r\n"
00444 "%s"
00445 "%s"
00446 "%s"
00447 "%s%s"
00448 "\r\n",
00449 method,
00450 path,
00451 post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "",
00452 headers,
00453 authstr ? authstr : "",
00454 proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
00455
00456 av_freep(&authstr);
00457 av_freep(&proxyauthstr);
00458 if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
00459 return err;
00460
00461 if (s->post_data)
00462 if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0)
00463 return err;
00464
00465
00466 s->buf_ptr = s->buffer;
00467 s->buf_end = s->buffer;
00468 s->line_count = 0;
00469 s->off = 0;
00470 s->filesize = -1;
00471 s->willclose = 0;
00472 s->end_chunked_post = 0;
00473 s->end_header = 0;
00474 if (post && !s->post_data) {
00475
00476
00477
00478 s->http_code = 200;
00479 return 0;
00480 }
00481
00482
00483 err = http_read_header(h, new_location);
00484 if (err < 0)
00485 return err;
00486
00487 return (off == s->off) ? 0 : -1;
00488 }
00489
00490
00491 static int http_buf_read(URLContext *h, uint8_t *buf, int size)
00492 {
00493 HTTPContext *s = h->priv_data;
00494 int len;
00495
00496 len = s->buf_end - s->buf_ptr;
00497 if (len > 0) {
00498 if (len > size)
00499 len = size;
00500 memcpy(buf, s->buf_ptr, len);
00501 s->buf_ptr += len;
00502 } else {
00503 if (!s->willclose && s->filesize >= 0 && s->off >= s->filesize)
00504 return AVERROR_EOF;
00505 len = ffurl_read(s->hd, buf, size);
00506 }
00507 if (len > 0) {
00508 s->off += len;
00509 if (s->chunksize > 0)
00510 s->chunksize -= len;
00511 }
00512 return len;
00513 }
00514
00515 static int http_read(URLContext *h, uint8_t *buf, int size)
00516 {
00517 HTTPContext *s = h->priv_data;
00518 int err, new_location;
00519
00520 if (!s->hd)
00521 return AVERROR_EOF;
00522
00523 if (s->end_chunked_post && !s->end_header) {
00524 err = http_read_header(h, &new_location);
00525 if (err < 0)
00526 return err;
00527 }
00528
00529 if (s->chunksize >= 0) {
00530 if (!s->chunksize) {
00531 char line[32];
00532
00533 for(;;) {
00534 do {
00535 if ((err = http_get_line(s, line, sizeof(line))) < 0)
00536 return err;
00537 } while (!*line);
00538
00539 s->chunksize = strtoll(line, NULL, 16);
00540
00541 av_dlog(NULL, "Chunked encoding data size: %"PRId64"'\n", s->chunksize);
00542
00543 if (!s->chunksize)
00544 return 0;
00545 break;
00546 }
00547 }
00548 size = FFMIN(size, s->chunksize);
00549 }
00550 return http_buf_read(h, buf, size);
00551 }
00552
00553
00554 static int http_write(URLContext *h, const uint8_t *buf, int size)
00555 {
00556 char temp[11] = "";
00557 int ret;
00558 char crlf[] = "\r\n";
00559 HTTPContext *s = h->priv_data;
00560
00561 if (!s->chunked_post) {
00562
00563 return ffurl_write(s->hd, buf, size);
00564 }
00565
00566
00567
00568 if (size > 0) {
00569
00570 snprintf(temp, sizeof(temp), "%x\r\n", size);
00571
00572 if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
00573 (ret = ffurl_write(s->hd, buf, size)) < 0 ||
00574 (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
00575 return ret;
00576 }
00577 return size;
00578 }
00579
00580 static int http_shutdown(URLContext *h, int flags)
00581 {
00582 int ret = 0;
00583 char footer[] = "0\r\n\r\n";
00584 HTTPContext *s = h->priv_data;
00585
00586
00587 if ((flags & AVIO_FLAG_WRITE) && s->chunked_post) {
00588 ret = ffurl_write(s->hd, footer, sizeof(footer) - 1);
00589 ret = ret > 0 ? 0 : ret;
00590 s->end_chunked_post = 1;
00591 }
00592
00593 return ret;
00594 }
00595
00596 static int http_close(URLContext *h)
00597 {
00598 int ret = 0;
00599 HTTPContext *s = h->priv_data;
00600
00601 if (!s->end_chunked_post) {
00602
00603 ret = http_shutdown(h, h->flags);
00604 }
00605
00606 if (s->hd)
00607 ffurl_closep(&s->hd);
00608 return ret;
00609 }
00610
00611 static int64_t http_seek(URLContext *h, int64_t off, int whence)
00612 {
00613 HTTPContext *s = h->priv_data;
00614 URLContext *old_hd = s->hd;
00615 int64_t old_off = s->off;
00616 uint8_t old_buf[BUFFER_SIZE];
00617 int old_buf_size;
00618
00619 if (whence == AVSEEK_SIZE)
00620 return s->filesize;
00621 else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed)
00622 return -1;
00623
00624
00625 old_buf_size = s->buf_end - s->buf_ptr;
00626 memcpy(old_buf, s->buf_ptr, old_buf_size);
00627 s->hd = NULL;
00628 if (whence == SEEK_CUR)
00629 off += s->off;
00630 else if (whence == SEEK_END)
00631 off += s->filesize;
00632 s->off = off;
00633
00634
00635 if (http_open_cnx(h) < 0) {
00636 memcpy(s->buffer, old_buf, old_buf_size);
00637 s->buf_ptr = s->buffer;
00638 s->buf_end = s->buffer + old_buf_size;
00639 s->hd = old_hd;
00640 s->off = old_off;
00641 return -1;
00642 }
00643 ffurl_close(old_hd);
00644 return off;
00645 }
00646
00647 static int
00648 http_get_file_handle(URLContext *h)
00649 {
00650 HTTPContext *s = h->priv_data;
00651 return ffurl_get_file_handle(s->hd);
00652 }
00653
00654 #if CONFIG_HTTP_PROTOCOL
00655 URLProtocol ff_http_protocol = {
00656 .name = "http",
00657 .url_open = http_open,
00658 .url_read = http_read,
00659 .url_write = http_write,
00660 .url_seek = http_seek,
00661 .url_close = http_close,
00662 .url_get_file_handle = http_get_file_handle,
00663 .url_shutdown = http_shutdown,
00664 .priv_data_size = sizeof(HTTPContext),
00665 .priv_data_class = &http_context_class,
00666 .flags = URL_PROTOCOL_FLAG_NETWORK,
00667 };
00668 #endif
00669 #if CONFIG_HTTPS_PROTOCOL
00670 URLProtocol ff_https_protocol = {
00671 .name = "https",
00672 .url_open = http_open,
00673 .url_read = http_read,
00674 .url_write = http_write,
00675 .url_seek = http_seek,
00676 .url_close = http_close,
00677 .url_get_file_handle = http_get_file_handle,
00678 .url_shutdown = http_shutdown,
00679 .priv_data_size = sizeof(HTTPContext),
00680 .priv_data_class = &https_context_class,
00681 .flags = URL_PROTOCOL_FLAG_NETWORK,
00682 };
00683 #endif
00684
00685 #if CONFIG_HTTPPROXY_PROTOCOL
00686 static int http_proxy_close(URLContext *h)
00687 {
00688 HTTPContext *s = h->priv_data;
00689 if (s->hd)
00690 ffurl_closep(&s->hd);
00691 return 0;
00692 }
00693
00694 static int http_proxy_open(URLContext *h, const char *uri, int flags)
00695 {
00696 HTTPContext *s = h->priv_data;
00697 char hostname[1024], hoststr[1024];
00698 char auth[1024], pathbuf[1024], *path;
00699 char lower_url[100];
00700 int port, ret = 0, attempts = 0;
00701 HTTPAuthType cur_auth_type;
00702 char *authstr;
00703 int new_loc;
00704
00705 h->is_streamed = 1;
00706
00707 av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
00708 pathbuf, sizeof(pathbuf), uri);
00709 ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
00710 path = pathbuf;
00711 if (*path == '/')
00712 path++;
00713
00714 ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port,
00715 NULL);
00716 redo:
00717 ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
00718 &h->interrupt_callback, NULL);
00719 if (ret < 0)
00720 return ret;
00721
00722 authstr = ff_http_auth_create_response(&s->proxy_auth_state, auth,
00723 path, "CONNECT");
00724 snprintf(s->buffer, sizeof(s->buffer),
00725 "CONNECT %s HTTP/1.1\r\n"
00726 "Host: %s\r\n"
00727 "Connection: close\r\n"
00728 "%s%s"
00729 "\r\n",
00730 path,
00731 hoststr,
00732 authstr ? "Proxy-" : "", authstr ? authstr : "");
00733 av_freep(&authstr);
00734
00735 if ((ret = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
00736 goto fail;
00737
00738 s->buf_ptr = s->buffer;
00739 s->buf_end = s->buffer;
00740 s->line_count = 0;
00741 s->filesize = -1;
00742 cur_auth_type = s->proxy_auth_state.auth_type;
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753 ret = http_read_header(h, &new_loc);
00754 if (ret < 0)
00755 goto fail;
00756
00757 attempts++;
00758 if (s->http_code == 407 &&
00759 (cur_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
00760 s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 2) {
00761 ffurl_closep(&s->hd);
00762 goto redo;
00763 }
00764
00765 if (s->http_code < 400)
00766 return 0;
00767 ret = AVERROR(EIO);
00768
00769 fail:
00770 http_proxy_close(h);
00771 return ret;
00772 }
00773
00774 static int http_proxy_write(URLContext *h, const uint8_t *buf, int size)
00775 {
00776 HTTPContext *s = h->priv_data;
00777 return ffurl_write(s->hd, buf, size);
00778 }
00779
00780 URLProtocol ff_httpproxy_protocol = {
00781 .name = "httpproxy",
00782 .url_open = http_proxy_open,
00783 .url_read = http_buf_read,
00784 .url_write = http_proxy_write,
00785 .url_close = http_proxy_close,
00786 .url_get_file_handle = http_get_file_handle,
00787 .priv_data_size = sizeof(HTTPContext),
00788 .flags = URL_PROTOCOL_FLAG_NETWORK,
00789 };
00790 #endif