[FFmpeg-devel] [PATCH] Fix HTTP authentication problem for POST actions.
Jakob van Bethlehem
jakob at jet-stream.nl
Wed Sep 11 15:05:48 CEST 2013
From: "J. van Bethlehem" <jakob at jet-stream.nl>
Upon executing HTTP POST requests, ffmpeg started the POST action
immediately after sending initial headers, therefore making it
impossible to properly deal with a 401 Authentication challenge from
the target server. This fix forces an initial empty POST request,
postponing POSTing data until after the proper HTTP authentication
header has been constructed
Signed-off-by: J. van Bethlehem <jakob at jet-stream.nl>
---
libavformat/http.c | 82 +++++++++++++++++++++++++++++++-----------------------
1 file changed, 47 insertions(+), 35 deletions(-)
diff --git a/libavformat/http.c b/libavformat/http.c
index 3edddbf..9887490 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -194,6 +194,7 @@ static int http_open_cnx(URLContext *h)
if (http_connect(h, path, local_path, hoststr, auth, proxyauth, &location_changed) < 0)
goto fail;
attempts++;
+
if (s->http_code == 401) {
if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) &&
s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
@@ -553,7 +554,6 @@ static int http_read_header(URLContext *h, int *new_location)
return err;
av_dlog(NULL, "header='%s'\n", line);
-
err = process_line(h, line, s->line_count, new_location);
if (err < 0)
return err;
@@ -570,31 +570,37 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
const char *proxyauth, int *new_location)
{
HTTPContext *s = h->priv_data;
- int post, err;
+ int post, err, auth_phase;
char headers[4096] = "";
char *authstr = NULL, *proxyauthstr = NULL;
int64_t off = s->off;
int len = 0;
const char *method;
-
-
- /* send http header */
+
+ // Determine HTTP method
post = h->flags & AVIO_FLAG_WRITE;
-
if (s->post_data) {
/* force POST method and disable chunked encoding when
* custom HTTP post data is set */
post = 1;
s->chunked_post = 0;
}
-
method = post ? "POST" : "GET";
+
+ /* If we expect to need authorization (ie: auth is non-NULL), but
+ * the current authorization state is still HTTP_AUTH_NONE, assume
+ * this is the first call to a server in order to get a 401
+ * with the authentication scheme to use. This modus has an
+ * impact in particular for POST-actions, for which the actual
+ * posting needs to be postponed untill after the first pass
+ */
+ auth_phase = auth && s->auth_state.auth_type == HTTP_AUTH_NONE && s->http_code != 401;
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 */
+ /* Set default headers if needed */
if (!has_header(s->headers, "\r\nUser-Agent: "))
len += av_strlcatf(headers + len, sizeof(headers) - len,
"User-Agent: %s\r\n", s->user_agent);
@@ -607,7 +613,9 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->seekable == -1))
len += av_strlcatf(headers + len, sizeof(headers) - len,
"Range: bytes=%"PRId64"-\r\n", s->off);
-
+ if (!has_header(s->headers, "\r\nHost: "))
+ len += av_strlcatf(headers + len, sizeof(headers) - len,
+ "Host: %s\r\n", hoststr);
if (!has_header(s->headers, "\r\nConnection: ")) {
if (s->multiple_requests) {
len += av_strlcpy(headers + len, "Connection: keep-alive\r\n",
@@ -617,16 +625,6 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
sizeof(headers) - len);
}
}
-
- if (!has_header(s->headers, "\r\nHost: "))
- len += av_strlcatf(headers + len, sizeof(headers) - len,
- "Host: %s\r\n", hoststr);
- if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data)
- len += av_strlcatf(headers + len, sizeof(headers) - len,
- "Content-Length: %d\r\n", s->post_datalen);
- if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type)
- len += av_strlcatf(headers + len, sizeof(headers) - len,
- "Content-Type: %s\r\n", s->content_type);
if (!has_header(s->headers, "\r\nCookie: ") && s->cookies) {
char *cookies = NULL;
if (!get_cookies(s, &cookies, path, hoststr)) {
@@ -640,33 +638,54 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
"Icy-MetaData: %d\r\n", 1);
}
- /* now add in custom headers */
+ /* The next set of headers should only be added when this is not
+ * the initial (POST) authentication pass */
+ if (!auth_phase) {
+ if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data)
+ len += av_strlcatf(headers + len, sizeof(headers) - len,
+ "Content-Length: %d\r\n", s->post_datalen);
+ if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type)
+ len += av_strlcatf(headers + len, sizeof(headers) - len,
+ "Content-Type: %s\r\n", s->content_type);
+ if (!has_header(s->headers, "\r\nTransfer-Encoding") && post && s->chunked_post)
+ len += av_strlcatf(headers + len, sizeof(headers) - len,
+ "Transfer-Encoding: %s\r\n", "chunked");
+ }
+
+ /* Add any custom headers */
if (s->headers)
av_strlcpy(headers + len, s->headers, sizeof(headers) - len);
+ /* Format and send headers */
snprintf(s->buffer, sizeof(s->buffer),
"%s %s HTTP/1.1\r\n"
"%s"
"%s"
- "%s"
"%s%s"
"\r\n",
method,
path,
- post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "",
headers,
authstr ? authstr : "",
- proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
+ proxyauthstr ? "Proxy-" : "",
+ proxyauthstr ? proxyauthstr : "");
av_freep(&authstr);
av_freep(&proxyauthstr);
if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
return err;
- if (s->post_data)
- if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0)
- return err;
-
+ /* Post data if passed initial authentication pass */
+ if (!auth_phase && post) {
+ // either post given data
+ if (s->post_data)
+ if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0)
+ return err;
+ // or assume everything is oke, and post by calling code proceed
+ s->http_code=200;
+ return 0;
+ }
+
/* init input buffer */
s->buf_ptr = s->buffer;
s->buf_end = s->buffer;
@@ -677,15 +696,8 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
s->willclose = 0;
s->end_chunked_post = 0;
s->end_header = 0;
- if (post && !s->post_data) {
- /* Pretend that it did work. We didn't read any header yet, since
- * we've still to send the POST data, but the code calling this
- * function will check http_code after we return. */
- s->http_code = 200;
- return 0;
- }
- /* wait for header */
+ /* process headers */
err = http_read_header(h, new_location);
if (err < 0)
return err;
--
1.7.12.4 (Apple Git-37)
More information about the ffmpeg-devel
mailing list