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