int64_t off, filesize;
char location[MAX_URL_SIZE];
HTTPAuthState auth_state;
+ HTTPAuthState proxy_auth_state;
char *headers;
int willclose; /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
int chunked_post;
.item_name = av_default_item_name,\
.option = options,\
.version = LIBAVUTIL_VERSION_INT,\
-};
+}
HTTP_CLASS(http);
HTTP_CLASS(https);
-static int http_connect(URLContext *h, const char *path, const char *hoststr,
- const char *auth, int *new_location);
+static int http_connect(URLContext *h, const char *path, const char *local_path,
+ const char *hoststr, const char *auth,
+ const char *proxyauth, int *new_location);
void ff_http_init_auth_state(URLContext *dest, const URLContext *src)
{
memcpy(&((HTTPContext*)dest->priv_data)->auth_state,
&((HTTPContext*)src->priv_data)->auth_state, sizeof(HTTPAuthState));
+ memcpy(&((HTTPContext*)dest->priv_data)->proxy_auth_state,
+ &((HTTPContext*)src->priv_data)->proxy_auth_state,
+ sizeof(HTTPAuthState));
}
/* return non zero if error */
static int http_open_cnx(URLContext *h)
{
- const char *path, *proxy_path, *lower_proto = "tcp";
+ const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
char hostname[1024], hoststr[1024], proto[10];
- char auth[1024];
+ char auth[1024], proxyauth[1024] = "";
char path1[1024];
- char buf[1024];
+ char buf[1024], urlbuf[1024];
int port, use_proxy, err, location_changed = 0, redirects = 0;
- HTTPAuthType cur_auth_type;
+ HTTPAuthType cur_auth_type, cur_proxy_auth_type;
HTTPContext *s = h->priv_data;
URLContext *hd = NULL;
path1, sizeof(path1), s->location);
ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
- if (use_proxy) {
- av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
- NULL, 0, proxy_path);
- path = s->location;
- } else {
- if (path1[0] == '\0')
- path = "/";
- else
- path = path1;
- }
if (!strcmp(proto, "https")) {
lower_proto = "tls";
+ use_proxy = 0;
if (port < 0)
port = 443;
}
if (port < 0)
port = 80;
+ if (path1[0] == '\0')
+ path = "/";
+ else
+ path = path1;
+ local_path = path;
+ if (use_proxy) {
+ /* Reassemble the request URL without auth string - we don't
+ * want to leak the auth to the proxy. */
+ ff_url_join(urlbuf, sizeof(urlbuf), proto, NULL, hostname, port, "%s",
+ path1);
+ path = urlbuf;
+ av_url_split(NULL, 0, proxyauth, sizeof(proxyauth),
+ hostname, sizeof(hostname), &port, NULL, 0, proxy_path);
+ }
+
ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
- err = ffurl_open(&hd, buf, AVIO_FLAG_READ_WRITE);
+ err = ffurl_open(&hd, buf, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, NULL);
if (err < 0)
goto fail;
s->hd = hd;
cur_auth_type = s->auth_state.auth_type;
- if (http_connect(h, path, hoststr, auth, &location_changed) < 0)
+ cur_proxy_auth_type = s->auth_state.auth_type;
+ if (http_connect(h, path, local_path, hoststr, auth, proxyauth, &location_changed) < 0)
goto fail;
if (s->http_code == 401) {
if (cur_auth_type == HTTP_AUTH_NONE && s->auth_state.auth_type != HTTP_AUTH_NONE) {
} else
goto fail;
}
+ if (s->http_code == 407) {
+ if (cur_proxy_auth_type == HTTP_AUTH_NONE &&
+ s->proxy_auth_state.auth_type != HTTP_AUTH_NONE) {
+ ffurl_close(hd);
+ goto redo;
+ } else
+ goto fail;
+ }
if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307)
&& location_changed == 1) {
/* url moved, get next */
/* error codes are 4xx and 5xx, but regard 401 as a success, so we
* don't abort until all headers have been parsed. */
- if (s->http_code >= 400 && s->http_code < 600 && s->http_code != 401) {
+ if (s->http_code >= 400 && s->http_code < 600 && (s->http_code != 401
+ || s->auth_state.auth_type != HTTP_AUTH_NONE) &&
+ (s->http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) {
end += strspn(end, SPACE_CHARS);
av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n",
s->http_code, end);
ff_http_auth_handle_header(&s->auth_state, tag, p);
} else if (!av_strcasecmp (tag, "Authentication-Info")) {
ff_http_auth_handle_header(&s->auth_state, tag, p);
+ } else if (!av_strcasecmp (tag, "Proxy-Authenticate")) {
+ ff_http_auth_handle_header(&s->proxy_auth_state, tag, p);
} else if (!av_strcasecmp (tag, "Connection")) {
if (!strcmp(p, "close"))
s->willclose = 1;
return av_stristart(str, header + 2, NULL) || av_stristr(str, header);
}
-static int http_connect(URLContext *h, const char *path, const char *hoststr,
- const char *auth, int *new_location)
+static int http_connect(URLContext *h, const char *path, const char *local_path,
+ const char *hoststr, const char *auth,
+ const char *proxyauth, int *new_location)
{
HTTPContext *s = h->priv_data;
int post, err;
char line[1024];
char headers[1024] = "";
- char *authstr = NULL;
+ char *authstr = NULL, *proxyauthstr = NULL;
int64_t off = s->off;
int len = 0;
+ const char *method;
/* send http header */
post = h->flags & AVIO_FLAG_WRITE;
- authstr = ff_http_auth_create_response(&s->auth_state, auth, path,
- post ? "POST" : "GET");
+ method = post ? "POST" : "GET";
+ authstr = ff_http_auth_create_response(&s->auth_state, auth, local_path,
+ method);
+ proxyauthstr = ff_http_auth_create_response(&s->proxy_auth_state, proxyauth,
+ local_path, method);
/* set default headers if needed */
if (!has_header(s->headers, "\r\nUser-Agent: "))
"%s"
"%s"
"%s"
+ "%s%s"
"\r\n",
- post ? "POST" : "GET",
+ method,
path,
post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "",
headers,
- authstr ? authstr : "");
+ authstr ? authstr : "",
+ proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
av_freep(&authstr);
+ av_freep(&proxyauthstr);
if (ffurl_write(s->hd, s->buffer, strlen(s->buffer)) < 0)
return AVERROR(EIO);
}
-static int http_read(URLContext *h, uint8_t *buf, int size)
+static int http_buf_read(URLContext *h, uint8_t *buf, int size)
{
HTTPContext *s = h->priv_data;
int len;
+ /* 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 {
+ if (!s->willclose && s->filesize >= 0 && s->off >= s->filesize)
+ return AVERROR_EOF;
+ len = ffurl_read(s->hd, buf, size);
+ }
+ if (len > 0) {
+ s->off += len;
+ if (s->chunksize > 0)
+ s->chunksize -= len;
+ }
+ return len;
+}
+
+static int http_read(URLContext *h, uint8_t *buf, int size)
+{
+ HTTPContext *s = h->priv_data;
if (s->chunksize >= 0) {
if (!s->chunksize) {
}
size = FFMIN(size, s->chunksize);
}
- /* 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 {
- if (!s->willclose && s->filesize >= 0 && s->off >= s->filesize)
- return AVERROR_EOF;
- len = ffurl_read(s->hd, buf, size);
- }
- if (len > 0) {
- s->off += len;
- if (s->chunksize > 0)
- s->chunksize -= len;
- }
- return len;
+ return http_buf_read(h, buf, size);
}
/* used only when posting data */
.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