diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 00924c8a11..bee2f5f11f 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -242,6 +242,7 @@ void av_register_all(void) REGISTER_PROTOCOL (FILE, file); REGISTER_PROTOCOL (GOPHER, gopher); REGISTER_PROTOCOL (HTTP, http); + REGISTER_PROTOCOL (HTTPPROXY, httpproxy); REGISTER_PROTOCOL (HTTPS, https); REGISTER_PROTOCOL (MMSH, mmsh); REGISTER_PROTOCOL (MMST, mmst); diff --git a/libavformat/http.c b/libavformat/http.c index d5bf96bf7a..5941925118 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -578,3 +578,118 @@ URLProtocol ff_https_protocol = { .priv_data_class = &https_context_class, }; #endif + +#if CONFIG_HTTPPROXY_PROTOCOL +static int http_proxy_close(URLContext *h) +{ + HTTPContext *s = h->priv_data; + if (s->hd) + ffurl_close(s->hd); + return 0; +} + +static int http_proxy_open(URLContext *h, const char *uri, int flags) +{ + HTTPContext *s = h->priv_data; + char hostname[1024], hoststr[1024]; + char auth[1024], pathbuf[1024], *path; + char line[1024], lower_url[100]; + int port, ret = 0; + HTTPAuthType cur_auth_type; + char *authstr; + + h->is_streamed = 1; + + av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port, + pathbuf, sizeof(pathbuf), uri); + ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL); + path = pathbuf; + if (*path == '/') + path++; + + ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port, + NULL); +redo: + ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE, + &h->interrupt_callback, NULL); + if (ret < 0) + return ret; + + authstr = ff_http_auth_create_response(&s->proxy_auth_state, auth, + path, "CONNECT"); + snprintf(s->buffer, sizeof(s->buffer), + "CONNECT %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Connection: close\r\n" + "%s%s" + "\r\n", + path, + hoststr, + authstr ? "Proxy-" : "", authstr ? authstr : ""); + av_freep(&authstr); + + if ((ret = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0) + goto fail; + + s->buf_ptr = s->buffer; + s->buf_end = s->buffer; + s->line_count = 0; + s->filesize = -1; + cur_auth_type = s->proxy_auth_state.auth_type; + + for (;;) { + int new_loc; + // Note: This uses buffering, potentially reading more than the + // HTTP header. If tunneling a protocol where the server starts + // the conversation, we might buffer part of that here, too. + // Reading that requires using the proper ffurl_read() function + // on this URLContext, not using the fd directly (as the tls + // protocol does). This shouldn't be an issue for tls though, + // since the client starts the conversation there, so there + // is no extra data that we might buffer up here. + if (http_get_line(s, line, sizeof(line)) < 0) { + ret = AVERROR(EIO); + goto fail; + } + + av_dlog(h, "header='%s'\n", line); + + ret = process_line(h, line, s->line_count, &new_loc); + if (ret < 0) + goto fail; + if (ret == 0) + break; + s->line_count++; + } + if (s->http_code == 407 && cur_auth_type == HTTP_AUTH_NONE && + s->proxy_auth_state.auth_type != HTTP_AUTH_NONE) { + ffurl_close(s->hd); + s->hd = NULL; + goto redo; + } + + if (s->http_code < 400) + return 0; + ret = AVERROR(EIO); + +fail: + http_proxy_close(h); + return ret; +} + +static int http_proxy_write(URLContext *h, const uint8_t *buf, int size) +{ + HTTPContext *s = h->priv_data; + return ffurl_write(s->hd, buf, size); +} + +URLProtocol ff_httpproxy_protocol = { + .name = "httpproxy", + .url_open = http_proxy_open, + .url_read = http_buf_read, + .url_write = http_proxy_write, + .url_close = http_proxy_close, + .url_get_file_handle = http_get_file_handle, + .priv_data_size = sizeof(HTTPContext), +}; +#endif diff --git a/libavformat/version.h b/libavformat/version.h index ec5d1fda03..6c1db42d21 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -24,7 +24,7 @@ #include "libavutil/avutil.h" #define LIBAVFORMAT_VERSION_MAJOR 53 -#define LIBAVFORMAT_VERSION_MINOR 14 +#define LIBAVFORMAT_VERSION_MINOR 15 #define LIBAVFORMAT_VERSION_MICRO 0 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \