[FFmpeg-devel] [PATCH] Fix HTTP authentication problem for POST actions.

Jakob van Bethlehem jakob at jet-stream.nl
Wed Sep 11 15:21:27 CEST 2013


Dear developers

Hopefully I did everything allright, this being my very first experience with providing patches through git.

One issue remains: the problem solved by this fix should have been discussed (shortly) on one of the ffmpeg mailing lists recently. I have been unable however to find the thread. A colleague (who unfortunately turned ill, so I can't ask him) of mine sent a whole bunch of logs, and someone promised that he/she would take a look at it some time. In the meantime, given that this problem was a big concern to us, we decided we really also should invest the time in providing the fix, resulting in the current patch. So hopefully this person, unfortunately unknown to me, will notice that the mentioned issue has been solved in the meantime

Sincerely,
Jakob van Bethlehem

On 11 sep. 2013, at 15:05, Jakob van Bethlehem <jakob at jet-stream.nl> wrote:

> 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)
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel



More information about the ffmpeg-devel mailing list