[FFmpeg-devel] [GSoC] Proof-of-concept HTTP Server
Stephan Holljes
klaxa1337 at googlemail.com
Thu Mar 26 03:51:39 CET 2015
I hope this time the patch is formatted correctly. I also attached it
in case it is corrupted again.
I changed the indentation of the code and used ffurl_open() instead of
creating my own listening socket.
I am still having some trouble with the Content-Type header. I would
guess creating functions like http_write_header() as the counterpart
for http_read_header() would be the most appropriate approach?
---
libavformat/http.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/libavformat/http.c b/libavformat/http.c
index da3c9be..472d8dd 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -96,6 +96,8 @@ typedef struct HTTPContext {
int send_expect_100;
char *method;
int reconnect;
+ int listen;
+ int header_sent;
} HTTPContext;
#define OFFSET(x) offsetof(HTTPContext, x)
@@ -127,6 +129,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 }
};
@@ -300,7 +303,9 @@ static int http_open(URLContext *h, const char
*uri, int flags,
AVDictionary **options)
{
HTTPContext *s = h->priv_data;
- int ret;
+ int port, ret = 0;
+ char hostname[1024];
+ char lower_url[100];
if( s->seekable == 1 )
h->is_streamed = 0;
@@ -321,6 +326,20 @@ static int http_open(URLContext *h, const char
*uri, int flags,
"No trailing CRLF found in HTTP header.\n");
}
+ if (s->listen) {
+ av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port,
+ NULL, 0, uri);
+ ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port,
+ NULL);
+ av_dict_set(options, "listen", "1", AV_DICT_APPEND);
+ ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, options);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return ret;
+ }
ret = http_open_cnx(h, options);
if (ret < 0)
av_dict_free(&s->chained_options);
@@ -1100,10 +1119,22 @@ static int http_read(URLContext *h, uint8_t
*buf, int size)
static int http_write(URLContext *h, const uint8_t *buf, int size)
{
char temp[11] = ""; /* 32-bit hex + CRLF + nul */
+ char header[] = "HTTP 200 OK\r\nContent-Type:
application/octet-stream\r\n\r\n";
int ret;
char crlf[] = "\r\n";
HTTPContext *s = h->priv_data;
+ if (s->listen) {
+ if (!s->header_sent) {
+ ret = ffurl_write(s->hd, header, strlen(header));
+ if (ret < 0) {
+ return ff_neterrno();
+ }
+ s->header_sent = 1;
+ }
+ ret = ffurl_write(s->hd, buf, size);
+ return ret < 0 ? ff_neterrno() : ret;
+ }
if (!s->chunked_post) {
/* non-chunked data is sent without any special encoding */
return ffurl_write(s->hd, buf, size);
--
2.3.3
On Sun, Mar 22, 2015 at 10:33 AM, Nicolas George <george at nsup.org> wrote:
> 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
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Implemented-simple-listen-mode-for-http.patch
Type: text/x-patch
Size: 3182 bytes
Desc: not available
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20150326/ec445568/attachment.bin>
More information about the ffmpeg-devel
mailing list