[FFmpeg-devel] [PATCH] lavf/http: implement directory listing callbacks for Apache
Mariusz Szczepańczyk
mszczepanczyk at gmail.com
Fri Aug 21 18:44:11 CEST 2015
On Fri, Aug 21, 2015 at 1:17 AM, Mariusz Szczepańczyk <
mszczepanczyk at gmail.com> wrote:
>
> On Thu, Aug 20, 2015 at 11:59 PM, Ganesh Ajjanagadde <gajjanag at mit.edu>
> wrote:
>
>> On Thu, Aug 20, 2015 at 5:48 PM, Mariusz Szczepańczyk
>> <mszczepanczyk at gmail.com> wrote:
>> > On Thu, Aug 20, 2015 at 11:38 PM, Ganesh Ajjanagadde <gajjanag at mit.edu>
>> > wrote:
>> >
>> >> On Thu, Aug 20, 2015 at 5:32 PM, Mariusz Szczepańczyk
>> >> <mszczepanczyk at gmail.com> wrote:
>> >> > ---
>> >> > configure | 3 +
>> >> > libavformat/http.c | 194
>> >> +++++++++++++++++++++++++++++++++++++++++++++++++++++
>> >> > 2 files changed, 197 insertions(+)
>> >> >
>> >> > diff --git a/configure b/configure
>> >> > index e67ddf6..401e041 100755
>> >> > --- a/configure
>> >> > +++ b/configure
>> >> > @@ -265,6 +265,7 @@ External library support:
>> >> > --enable-libxcb-shm enable X11 grabbing shm communication
>> >> [autodetect]
>> >> > --enable-libxcb-xfixes enable X11 grabbing mouse rendering
>> >> [autodetect]
>> >> > --enable-libxcb-shape enable X11 grabbing shape rendering
>> >> [autodetect]
>> >> > + --enable-libxml2 enable HTML parsing via libxml2 [no]
>> >> > --enable-libxvid enable Xvid encoding via xvidcore,
>> >> > native MPEG-4/Xvid encoder exists [no]
>> >> > --enable-libzmq enable message passing via libzmq [no]
>> >> > @@ -1428,6 +1429,7 @@ EXTERNAL_LIBRARY_LIST="
>> >> > libxcb_shm
>> >> > libxcb_shape
>> >> > libxcb_xfixes
>> >> > + libxml2
>> >> > libxvid
>> >> > libzmq
>> >> > libzvbi
>> >> > @@ -5309,6 +5311,7 @@ enabled libx265 && require_pkg_config
>> >> x265 x265.h x265_api_get &&
>> >> > { check_cpp_condition x265.h
>> "X265_BUILD
>> >> >= 57" ||
>> >> > die "ERROR: libx265 version must be
>> >=
>> >> 57."; }
>> >> > enabled libxavs && require libxavs xavs.h
>> xavs_encoder_encode
>> >> -lxavs
>> >> > +enabled libxml2 && require_pkg_config libxml-2.0
>> >> libxml/parser.h xmlInitParser
>> >> > enabled libxvid && require libxvid xvid.h xvid_global
>> >> -lxvidcore
>> >> > enabled libzmq && require_pkg_config libzmq zmq.h
>> zmq_ctx_new
>> >> > enabled libzvbi && require libzvbi libzvbi.h
>> vbi_decoder_new
>> >> -lzvbi
>> >> > diff --git a/libavformat/http.c b/libavformat/http.c
>> >> > index 1eb716b..df45958 100644
>> >> > --- a/libavformat/http.c
>> >> > +++ b/libavformat/http.c
>> >> > @@ -21,6 +21,10 @@
>> >> >
>> >> > #include "config.h"
>> >> >
>> >> > +#if CONFIG_LIBXML2
>> >> > +#include <libxml/HTMLparser.h>
>> >> > +#endif /* CONFIG_LIBXML2 */
>> >> > +
>> >> > #if CONFIG_ZLIB
>> >> > #include <zlib.h>
>> >> > #endif /* CONFIG_ZLIB */
>> >> > @@ -54,6 +58,16 @@ typedef enum {
>> >> > FINISH
>> >> > }HandshakeState;
>> >> >
>> >> > +typedef struct AVIODirEntryQueueNode {
>> >> > + struct AVIODirEntry *entry;
>> >> > + struct AVIODirEntryQueueNode *next;
>> >> > +} AVIODirEntryQueueNode;
>> >> > +
>> >> > +typedef struct AVIODirEntryQueue {
>> >> > + struct AVIODirEntryQueueNode *front;
>> >> > + struct AVIODirEntryQueueNode *rear;
>> >> > +} AVIODirEntryQueue;
>> >> > +
>> >> > typedef struct HTTPContext {
>> >> > const AVClass *class;
>> >> > URLContext *hd;
>> >> > @@ -70,6 +84,7 @@ typedef struct HTTPContext {
>> >> > char *mime_type;
>> >> > char *user_agent;
>> >> > char *content_type;
>> >> > + char *server;
>> >> > /* Set if the server correctly handles Connection: close and
>> will
>> >> close
>> >> > * the connection after feeding us the content. */
>> >> > int willclose;
>> >> > @@ -111,6 +126,11 @@ typedef struct HTTPContext {
>> >> > int is_multi_client;
>> >> > HandshakeState handshake_step;
>> >> > int is_connected_server;
>> >> > +#if CONFIG_LIBXML2
>> >> > + htmlParserCtxtPtr html_parser;
>> >> > + AVIODirEntryQueue *entry_queue;
>> >> > + AVIODirEntry *entry;
>> >> > +#endif /* CONFIG_LIBXML2 */
>> >> > } HTTPContext;
>> >> >
>> >> > #define OFFSET(x) offsetof(HTTPContext, x)
>> >> > @@ -808,6 +828,8 @@ static int process_line(URLContext *h, char
>> *line,
>> >> int line_count,
>> >> > if (!strcmp(p, "close"))
>> >> > s->willclose = 1;
>> >> > } else if (!av_strcasecmp(tag, "Server")) {
>> >> > + av_free(s->server);
>> >> > + s->server = av_strdup(p);
>> >> > if (!av_strcasecmp(p, "AkamaiGHost")) {
>> >> > s->is_akamai = 1;
>> >> > } else if (!av_strncasecmp(p, "MediaGateway", 12)) {
>> >> > @@ -1409,6 +1431,7 @@ static int http_close(URLContext *h)
>> >> > if (s->hd)
>> >> > ffurl_closep(&s->hd);
>> >> > av_dict_free(&s->chained_options);
>> >> > + av_freep(&s->server);
>> >> > return ret;
>> >> > }
>> >> >
>> >> > @@ -1471,6 +1494,167 @@ static int http_get_file_handle(URLContext
>> *h)
>> >> > return ffurl_get_file_handle(s->hd);
>> >> > }
>> >> >
>> >> > +#if CONFIG_LIBXML2
>> >> > +static void avio_dir_entry_queue_push(AVIODirEntryQueue *queue,
>> >> AVIODirEntry *entry)
>> >> > +{
>> >> > + AVIODirEntryQueueNode *node;
>> >> > +
>> >> > + if (!queue)
>> >> > + return;
>> >> > +
>> >> > + node = av_mallocz(sizeof(AVIODirEntryQueueNode));
>> >> > + node->entry = entry;
>> >> > + if (!queue->front) {
>> >> > + queue->front = queue->rear = node;
>> >> > + } else {
>> >> > + queue->rear->next = node;
>> >> > + queue->rear = node;
>> >> > + }
>> >> > +}
>> >> > +
>> >> > +static AVIODirEntry *avio_dir_entry_queue_pop(AVIODirEntryQueue
>> *queue)
>> >> > +{
>> >> > + AVIODirEntry *entry;
>> >> > + AVIODirEntryQueueNode *tmp;
>> >> > +
>> >> > + if (!queue || !queue->front)
>> >> > + return NULL;
>> >> > +
>> >> > + tmp = queue->front;
>> >> > + entry = queue->front->entry;
>> >> > + if (queue->front == queue->rear)
>> >> > + queue->front = queue->rear = NULL;
>> >> > + else
>> >> > + queue->front = queue->front->next;
>> >> > +
>> >> > + av_freep(&tmp);
>> >> > +
>> >> > + return entry;
>> >> > +}
>> >> > +
>> >> > +static const char *get_attr(const xmlChar **attrs, const char *key)
>> >> > +{
>> >> > + unsigned char i;
>> >> > +
>> >> > + if (!attrs)
>> >> > + return NULL;
>> >> > +
>> >> > + for (i = 0; attrs[i] && i < UCHAR_MAX - 1; i += 2) {
>> >> > + if (!strcmp(attrs[i], key))
>> >> > + return attrs[i + 1];
>> >> > + }
>> >> > +
>> >> > + return NULL;
>> >> > +}
>> >> > +
>> >> > +static void parse_apache(void *ctx, const xmlChar *tag, const
>> xmlChar
>> >> **attrs)
>> >> > +{
>> >> > + URLContext *h = (URLContext *) ctx;
>> >> > + HTTPContext *s = h->priv_data;
>> >> > + const char *url, *alt, *src;
>> >> > + if (!strcmp(tag, "img")) {
>> >> > + av_freep(&s->entry);
>> >> > + alt = get_attr(attrs, "alt");
>> >> > + src = get_attr(attrs, "src");
>> >> > + if (alt && alt[0] == '['
>> >> > + && alt[strlen(alt) - 1] == ']'
>> >> > + && strcmp(alt, "[PARENTDIR]")) {
>> >> > + if (!src || strcmp(src, "/icons/back.gif")) {
>> >> > + s->entry = ff_alloc_dir_entry();
>> >> > + if (!strcmp(alt, "[DIR]"))
>> >> > + s->entry->type = AVIO_ENTRY_DIRECTORY;
>> >> > + else
>> >> > + s->entry->type = AVIO_ENTRY_FILE;
>> >> > + }
>> >> > + }
>> >> > + } else if (!strcmp(tag, "a")) {
>> >> > + if (s->entry && (url = get_attr(attrs, "href"))
>> >> > + && strcmp(url, "/")) {
>> >> > + s->entry->name = av_strdup(url);
>> >> > + if (s->entry->name[strlen(s->entry->name) - 1] == '/')
>> >> > + s->entry->name[strlen(s->entry->name) - 1] = 0;
>> >> > + avio_dir_entry_queue_push(s->entry_queue, s->entry);
>> >> > + s->entry = NULL;
>> >> > + } else
>> >> > + av_freep(&s->entry);
>> >> > + } else if (!strcmp(tag, "th") && s->entry) {
>> >> > + av_freep(&s->entry);
>> >> > + }
>> >> > +}
>> >> > +
>> >> > +static int http_open_dir(URLContext *h)
>> >> > +{
>> >> > + HTTPContext *s = h->priv_data;
>> >> > + xmlSAXHandler handlers = {};
>> >> > + int ret;
>> >> > +
>> >> > + if (ret = http_open(h, h->filename, 0, NULL) < 0)
>> >> > + goto fail;
>> >> > +
>> >> > + if (!s->mime_type || !strstr(s->mime_type, "text/html")) {
>> >> > + ret = AVERROR(ENOSYS);
>> >> > + goto fail;
>> >> > + }
>> >> > +
>> >> > + if (s->server && strstr(s->server, "Apache"))
>> >> > + handlers.startElement = parse_apache;
>> >> > +
>> >> > + if (!handlers.startElement) {
>> >> > + ret = AVERROR(ENOSYS);
>> >> > + goto fail;
>> >> > + }
>> >> > +
>> >> > + s->entry_queue = av_mallocz(sizeof(AVIODirEntryQueue));
>> >> > + s->html_parser = htmlCreatePushParserCtxt(&handlers, h, NULL, 0,
>> >> h->filename, XML_CHAR_ENCODING_UTF8);
>> >> > + if (!s->html_parser) {
>> >> > + ret = AVERROR(EIO);
>> >> > + goto fail;
>> >> > + }
>> >> > +
>> >> > + return 0;
>> >> > +
>> >> > +fail:
>> >> > + http_close(h);
>> >> > +
>> >> > + return ret;
>> >> > +}
>> >> > +
>> >> > +static int http_read_dir(URLContext *h, AVIODirEntry **next)
>> >> > +{
>> >> > + HTTPContext *s = h->priv_data;
>> >> > + int ret;
>> >> > + char buf[BUFFER_SIZE];
>> >> > +
>> >> > + if ((*next = avio_dir_entry_queue_pop(s->entry_queue)))
>> >> > + return 0;
>> >> > +
>> >> > + while ((ret = ffurl_read(h, (unsigned char *) buf, BUFFER_SIZE -
>> >> 1)) > 0) {
>> >> > + htmlParseChunk(s->html_parser, (const char *) buf, ret, 0);
>> >> > + if ((*next = avio_dir_entry_queue_pop(s->entry_queue)))
>> >> > + return 0;
>> >> > + }
>> >> > +
>> >> > + if (ret < 0)
>> >> > + return ret;
>> >> > +
>> >> > + return 0;
>> >> > +}
>> >> > +
>> >> > +static int http_close_dir(URLContext *h)
>> >> > +{
>> >> > + HTTPContext *s = h->priv_data;
>> >> > + AVIODirEntry *entry;
>> >> > + while (s->entry_queue && (entry =
>> >> avio_dir_entry_queue_pop(s->entry_queue)))
>> >> > + av_freep(&entry);
>> >> > + av_freep(&s->entry_queue);
>> >> > + av_freep(&s->entry);
>> >> > + htmlFreeParserCtxt(s->html_parser);
>> >> > + s->html_parser = NULL;
>> >> > + http_close(h);
>> >> > + return 0;
>> >> > +}
>> >> > +#endif /* CONFIG_LIBXML2 */
>> >> > +
>> >> > #define HTTP_CLASS(flavor) \
>> >> > static const AVClass flavor ## _context_class = { \
>> >> > .class_name = # flavor, \
>> >> > @@ -1493,6 +1677,11 @@ URLProtocol ff_http_protocol = {
>> >> > .url_close = http_close,
>> >> > .url_get_file_handle = http_get_file_handle,
>> >> > .url_shutdown = http_shutdown,
>> >> > +#if CONFIG_LIBXML2
>> >> > + .url_open_dir = http_open_dir,
>> >> > + .url_read_dir = http_read_dir,
>> >> > + .url_close_dir = http_close_dir,
>> >> > +#endif /* CONFIG_LIBXML2 */
>> >> > .priv_data_size = sizeof(HTTPContext),
>> >> > .priv_data_class = &http_context_class,
>> >> > .flags = URL_PROTOCOL_FLAG_NETWORK,
>> >> > @@ -1511,6 +1700,11 @@ URLProtocol ff_https_protocol = {
>> >> > .url_close = http_close,
>> >> > .url_get_file_handle = http_get_file_handle,
>> >> > .url_shutdown = http_shutdown,
>> >> > +#if CONFIG_LIBXML2
>> >> > + .url_open_dir = http_open_dir,
>> >> > + .url_read_dir = http_read_dir,
>> >> > + .url_close_dir = http_close_dir,
>> >> > +#endif /* CONFIG_LIBXML2 */
>> >> > .priv_data_size = sizeof(HTTPContext),
>> >> > .priv_data_class = &https_context_class,
>> >> > .flags = URL_PROTOCOL_FLAG_NETWORK,
>> >> > --
>> >> > 2.4.6
>> >>
>> >> I don't like the commit message: this is not Apache.
>> >> Perhaps what you meant was "similar to Apache" or something like that.
>> >> Will check the actual patch later.
>> >>
>> >
>> > Actually that's what I meant - specifically for directory listing coming
>> > from Apache servers.
>>
>> Thanks for clarifying; I still think commit message can be clearer.
>> How about this:
>>
>> lavf/http: implement callbacks for directory listings from Apache
>>
>> This is roughly of the same length and avoids the confusion.
>>
>>
> Changed the commit line.
>
>
Also, nginx handling inside attachment (as a separate patch).
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0002-lavf-http-implement-callbacks-for-directory-listings-nginx.patch
Type: text/x-patch
Size: 2444 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20150821/bf136d9b/attachment.bin>
More information about the ffmpeg-devel
mailing list