[FFmpeg-devel] [GSoC] Proof-of-concept HTTP Server

Nicolas George george at nsup.org
Sun Mar 22 10:33:43 CET 2015


Le primidi 1er germinal, an CCXXIII, Stephan Holljes a écrit :
> Please comment.

As Michael pointed out, the patch was mangled at sending. That happens with
mail user agent that rely on "rich" text editor and do not let users control
the formatting finely. Using git send-email or attaching the file will solve
the problem. They also include authorship and date information.

> 
> Regards,
> Stephan Holljes
> 
> ---
>  libavformat/http.c |  113
> ++++++++++++++++++++++++++++++++++++++--------------
>  1 file changed, 83 insertions(+), 30 deletions(-)
> 
> diff --git a/libavformat/http.c b/libavformat/http.c
> index da3c9be..d61e4e2 100644
> --- a/libavformat/http.c
> +++ b/libavformat/http.c
> @@ -96,8 +96,12 @@ typedef struct HTTPContext {
>      int send_expect_100;
>      char *method;
>      int reconnect;
> +    int listen;
> +    int fd;
> +    int header_sent;
>  } HTTPContext;
> 
> +
>  #define OFFSET(x) offsetof(HTTPContext, x)
>  #define D AV_OPT_FLAG_DECODING_PARAM
>  #define E AV_OPT_FLAG_ENCODING_PARAM
> @@ -127,6 +131,7 @@ static const AVOption options[] = {
>      { "end_offset", "try to limit the request to bytes preceding this
> offset", OFFSET(end_off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D
> },
>      { "method", "Override the HTTP method", OFFSET(method),
> AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
>      { "reconnect", "auto reconnect after disconnect before EOF",
> OFFSET(reconnect), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D },
> +    { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64
> = 0 }, 0, 1, D },
>      { NULL }
>  };
> 
> @@ -299,8 +304,10 @@ int ff_http_averror(int status_code, int
> default_averror)
>  static int http_open(URLContext *h, const char *uri, int flags,
>                       AVDictionary **options)
>  {
> +    struct addrinfo hints = { 0 }, *ai;
>      HTTPContext *s = h->priv_data;
> -    int ret;
> +    int ret = -1, fd;
> +    char portstr[] = "8080"; // allow non-root users for now
> 
>      if( s->seekable == 1 )
>          h->is_streamed = 0;
> @@ -320,11 +327,39 @@ static int http_open(URLContext *h, const char *uri,
> int flags,
>              av_log(h, AV_LOG_WARNING,
>                     "No trailing CRLF found in HTTP header.\n");
>      }

> +    if (s->listen) {
> +        hints.ai_family = AF_UNSPEC;
> +        hints.ai_socktype = SOCK_STREAM;
> +        hints.ai_flags |= AI_PASSIVE;
> +        ret = getaddrinfo(NULL, portstr, &hints, &ai);
> +        if (ret) {
> +            av_log(h, AV_LOG_ERROR, "borked");
> +            return AVERROR(EIO);
> +        }
> +        fd = ff_socket(ai->ai_family,
> +                       ai->ai_socktype,
> +                       ai->ai_protocol);
> +        if (fd < 0) {
> +            ret = ff_neterrno();
> +            freeaddrinfo(ai);
> +            return -1;
> +        }

This part looks copied from tcp.c. It would be much better to use the API
provided by TCP. In other words, just like the client part does:

        err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE,
                         &h->interrupt_callback, options);

you should do the same thing for creating the listening socket, just adding
the listen option. There are several reasons for doing that.

First, you avoid reimplementing all the interrupt_callback logic and cie,
which would be necessary for a complete implementation.

Second, this code with getaddrinfo is slightly wrong (wrt multi-protocol
hosts), so when it gets fixed in tcp.c, it would benefit to all the code.

> 
> -    ret = http_open_cnx(h, options);
> -    if (ret < 0)
> -        av_dict_free(&s->chained_options);
> -    return ret;
> +        fd = ff_listen_bind(fd, ai->ai_addr, ai->ai_addrlen, -1, h);
> +        if (fd < 0) {
> +            freeaddrinfo(ai);
> +            return fd;
> +        }
> +        h->is_streamed = 1;
> +        s->fd = fd;
> +        freeaddrinfo(ai);
> +        return 0;
> +    } else {
> +        ret = http_open_cnx(h, options);
> +        if (ret < 0)
> +            av_dict_free(&s->chained_options);
> +        return ret;
> +    }
>  }
> 
>  static int http_getc(HTTPContext *s)
> @@ -1102,25 +1137,40 @@ static int http_write(URLContext *h, const uint8_t
> *buf, int size)
>      char temp[11] = "";  /* 32-bit hex + CRLF + nul */
>      int ret;
>      char crlf[] = "\r\n";
> +    char header[] = "HTTP 200 OK\r\n\r\n";
>      HTTPContext *s = h->priv_data;
> +    if (!s->listen) {
> +        if (!s->chunked_post) {
> +            /* non-chunked data is sent without any special encoding */
> +            return ffurl_write(s->hd, buf, size);
> +        }
> 
> -    if (!s->chunked_post) {
> -        /* non-chunked data is sent without any special encoding */
> -        return ffurl_write(s->hd, buf, size);
> -    }
> -
> -    /* silently ignore zero-size data since chunk encoding that would
> -     * signal EOF */
> -    if (size > 0) {
> -        /* upload data using chunked encoding */
> -        snprintf(temp, sizeof(temp), "%x\r\n", size);
> +        /* silently ignore zero-size data since chunk encoding that would
> +         * signal EOF */
> +        if (size > 0) {
> +            /* upload data using chunked encoding */
> +            snprintf(temp, sizeof(temp), "%x\r\n", size);
> 
> -        if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
> -            (ret = ffurl_write(s->hd, buf, size)) < 0          ||
> -            (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
> -            return ret;
> +            if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
> +                (ret = ffurl_write(s->hd, buf, size)) < 0          ||
> +                (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
> +                return ret;
> +        }
> +        return size;
> +    } else {
> +        if (!s->header_sent) {
> +            ret = send(s->fd, header, sizeof(header), MSG_NOSIGNAL);
> +            s->header_sent = 1;
> +            return ret < 0 ? ff_neterrno() : ret;
> +        }
> +        if (size > 0) {
> +            ret = send(s->fd, buf, size, MSG_NOSIGNAL);
> +            return ret < 0 ? ff_neterrno() : ret;
> +        } else {
> +            ret = -1;
> +        }
> +        return ret;

When you add a test or a loop around a large portion of the code, do not
reindent the existing code immediately, leave that for a second commit when
the patch is finished. That makes it simpler to see what was actually
changed.

You can also consider using a more separate approach:

    if (old_case) {
    old_code
    } else {
        new_code
    }

can also be written:

    if (new_case)
        return do_new_code();

>      }
> -    return size;
>  }
> 
>  static int http_shutdown(URLContext *h, int flags)
> @@ -1143,20 +1193,23 @@ static int http_close(URLContext *h)
>  {
>      int ret = 0;
>      HTTPContext *s = h->priv_data;
> -
> +    if (!s->listen) {
>  #if CONFIG_ZLIB
> -    inflateEnd(&s->inflate_stream);
> -    av_freep(&s->inflate_buffer);
> +        inflateEnd(&s->inflate_stream);
> +        av_freep(&s->inflate_buffer);
>  #endif /* CONFIG_ZLIB */
> 
> -    if (!s->end_chunked_post)
> -        /* Close the write direction by sending the end of chunked
> encoding. */
> -        ret = http_shutdown(h, h->flags);
> +        if (!s->end_chunked_post)
> +            /* Close the write direction by sending the end of chunked
> encoding. */
> +            ret = http_shutdown(h, h->flags);
> 
> -    if (s->hd)
> -        ffurl_closep(&s->hd);
> -    av_dict_free(&s->chained_options);
> -    return ret;
> +        if (s->hd)
> +            ffurl_closep(&s->hd);
> +        av_dict_free(&s->chained_options);
> +        return ret;
> +    } else {
> +        return shutdown(s->fd, SHUT_RDWR);
> +    }
>  }
> 
>  static int64_t http_seek_internal(URLContext *h, int64_t off, int whence,
> int force_reconnect)

Regards,

-- 
  Nicolas George
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20150322/9f4b41b8/attachment.asc>


More information about the ffmpeg-devel mailing list