[FFmpeg-devel] [PATCH] HTTP cookie support
Stefano Sabatini
stefasab at gmail.com
Sun Jan 13 14:43:06 CET 2013
On date Saturday 2013-01-12 13:31:31 -0500, Micah Galizia encoded:
> Hello,
>
> I'm submitting two patches to add support for cookies to the HTTP protocol.
> It contains the recommendations made by both Michael and Sefano in a prior
> thread. The first is makes the code change and the second updates
> protocols.texi to include the new protocol option.
>
> Thanks in advance!
> --
> "The mark of an immature man is that he wants to die nobly for a cause,
> while the mark of the mature man is that he wants to live humbly for
> one." --W. Stekel
> From 030d545d4c2a9fe12e77df1670dd46a9a461d3d9 Mon Sep 17 00:00:00 2001
> From: Micah Galizia <micahgalizia at gmail.com>
> Date: Sat, 12 Jan 2013 13:25:19 -0500
> Subject: [PATCH 1/2] add HTTP protocol cookie support
>
> ---
> libavformat/http.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 114 insertions(+)
>
> diff --git a/libavformat/http.c b/libavformat/http.c
> index a9d952b..a2fdb93 100644
> --- a/libavformat/http.c
> +++ b/libavformat/http.c
> @@ -64,6 +64,7 @@ typedef struct {
> int is_akamai;
> int rw_timeout;
> char *mime_type;
> + char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name)
> } HTTPContext;
>
> #define OFFSET(x) offsetof(HTTPContext, x)
> @@ -80,6 +81,7 @@ static const AVOption options[] = {
> {"post_data", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E },
> {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
> {"mime_type", "set MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 },
> +{"cookies", "cookies to be sent in applicable future requests. Uses newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 },
nit: "set cookies ... . Use ..."
> {NULL}
> };
> #define HTTP_CLASS(flavor)\
> @@ -359,11 +361,115 @@ static int process_line(URLContext *h, char *line, int line_count,
> s->is_akamai = 1;
> } else if (!av_strcasecmp (tag, "Content-Type")) {
> av_free(s->mime_type); s->mime_type = av_strdup(p);
> + } else if (!av_strcasecmp (tag, "Set-Cookie")) {
> + if (!s->cookies) {
> + if (!(s->cookies = av_strdup(p)))
> + return AVERROR(ENOMEM);
> + } else {
> + char *tmp = s->cookies;
> + size_t str_size = strlen(tmp) + strlen(p) + 2;
> + if (!(s->cookies = av_malloc(str_size))) {
> + s->cookies = tmp;
> + return AVERROR(ENOMEM);
> + }
> + snprintf(s->cookies, str_size, "%s\n%s", tmp, p);
> + av_free(tmp);
Alternatively this may realloc cookies (would require to store
cookies_size in the context), or even use a list of pair-values
couples, so you don't need to parse them later (but feel free to keep
the current implementation).
> + }
> }
> }
> return 1;
> }
>
> +static int get_cookies(HTTPContext *s, char **cookies, const char *path,
> + const char *domain)
Please add a doxy here, something like:
Create a string containing the cookies name-values pairs which are
stored in the HTTP protocol context matching path and domain. The
string is stored into *cookies.
> +{
> + // cookie strings will look like Set-Cookie header field values. Multiple
> + // Set-Cookie fields will result in multiple values delimited by a newline
> + const char *set_cookies = s->cookies;
> +
> + if (!set_cookies) return AVERROR(EINVAL);
> +
> + while (*set_cookies) {
> + int domain_offset = 0;
> + char *cdomain = NULL, *cpath = NULL, *cvalue = NULL;
> + char *cookie = av_get_token(&set_cookies, "\n"), *c = cookie;
> +
> + while (*cookie) {
> + char *param = av_get_token((const char**)&cookie, "; ");
Considering that HTTP cookies don't support '\-escaping, av_strtok() might
be a safer choice.
> +
> + if (!av_strncasecmp("path=", param, 5)) {
> + cpath = av_strdup(¶m[5]);
> + } else if (!av_strncasecmp("domain=", param, 7)) {
> + cdomain = av_strdup(¶m[7]);
> + } else if (!av_strncasecmp("secure", param, 6) ||
> + !av_strncasecmp("comment", param, 7) ||
> + !av_strncasecmp("max-age", param, 7) ||
> + !av_strncasecmp("version", param, 7)) {
> + // ignore Comment, Max-Age, Secure and Version
> + } else {
> + cvalue = av_strdup(param);
> + }
> +
> + if (*cookie == ';') cookie++;
> + av_free(param);
> + }
> +
> + // ensure all of the necessary values are valid
> + if (!cdomain || !cpath || !cvalue) {
> + goto done_cookie;
> + }
> +
You may issue a warning in case the set cookie is not valid.
> + // check if the request path matches the cookie path
> + if (av_strncasecmp(path, cpath, strlen(cpath)))
> + goto done_cookie;
> +
> + // the domain should be at least the size of our cookie domain and
and?
> + domain_offset = strlen(domain) - strlen(cdomain);
> + if (domain_offset < 0)
> + goto done_cookie;
> +
> + // match the cookie domain
> + if (av_strcasecmp(&domain[domain_offset], cdomain))
> + goto done_cookie;
> +
> + // cookie parameters match, so copy the value
> + if (!*cookies) {
> + if (!(*cookies = av_strdup(cvalue))) {
> + av_free(cdomain);
> + av_free(cpath);
> + av_free(cvalue);
> + av_free(c);
> + return AVERROR(ENOMEM);
> + }
> + } else {
> + char *tmp = *cookies;
> + size_t str_size = strlen(cvalue) + strlen(*cookies) + 3;
> + if (!(*cookies = av_malloc(str_size))) {
> + av_free(tmp);
> + av_free(cdomain);
> + av_free(cpath);
> + av_free(cvalue);
> + av_free(c);
This part could be factorized, you set ret to ENOMEM, go to
done_cookie and return ret if ret < 0.
> + return AVERROR(ENOMEM);
> + }
> + snprintf(*cookies, str_size, "%s; %s", tmp, cvalue);
> + av_free(tmp);
> + }
> +
> + done_cookie:
> + av_free(cdomain);
> + av_free(cpath);
> + av_free(cvalue);
> +
> + av_free(c);
> +
> + // advance the token
> + if (*set_cookies == '\n') set_cookies++;
> + }
> +
> + return 0;
> +}
> +
> static inline int has_header(const char *str, const char *header)
> {
> /* header + 2 to skip over CRLF prefix. (make sure you have one!) */
> @@ -460,6 +566,14 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
> 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)) {
> + len += av_strlcatf(headers + len, sizeof(headers) - len,
> + "Cookie: %s\r\n", cookies);
> + av_free(cookies);
> + }
> + }
>
> /* now add in custom headers */
> if (s->headers)
> --
> 1.7.10.4
>
> From d2cc05b6315173f2423ab2c744a2f18a949eb888 Mon Sep 17 00:00:00 2001
> From: Micah Galizia <micahgalizia at gmail.com>
> Date: Sat, 12 Jan 2013 13:25:33 -0500
> Subject: [PATCH 2/2] document HTTP protocol cookie support
>
> ---
> doc/protocols.texi | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
>
> diff --git a/doc/protocols.texi b/doc/protocols.texi
> index 6fdd08d..17fcf3a 100644
> --- a/doc/protocols.texi
> +++ b/doc/protocols.texi
> @@ -164,8 +164,27 @@ not specified.
>
> @item mime_type
> Set MIME type.
> +
> + at item cookies
> +Set the cookies to be sent in future requests. The format of each cookie is the
> +same as the value of a Set-Cookie HTTP response field. Multiple cookies can be
> +delimited by a newline character.
> @end table
>
> + at subsection HTTP Cookies
> +
> +Some HTTP requests will be denied unless cookie values are passed in with the
> +request. The -cookies flag allows these cookies to be specified. At
The @option{cookies} option allows ...
> +the very least, each cookie must specify a value along with a path and domain.
> +HTTP requests that match both the domain and path will automatically include the
> +cookie value in the HTTP Cookie header field. Multiple cookies can be delimited
> +by a newline.
> +
> +The required syntax to play a stream specifying a cookie is:
> + at example
> +ffplay http://somedomain.com/somestream.m3u8 -cookies "value; path=/; domain=somedomain.com;"
Note: although I'm not sure how the options are parsed in this case, I
supppose it would be safer to specify the option affecting the
protocol *before* the corresponding input.
In this case:
ffplay -cookies "value; path=/; domain=somedomain.com;" http://somedomain.com/somestream.m3u8
Also "value" might be replaced by a more concrete example, like
KEY=VALUE; ATTRS
Alternatively we may want to support a syntax of the kind:
ffplay -proto http=cookies="....":... URI
to force protocol and options.
[...]
--
FFmpeg = Faithless Fostering Murdering Puritan Elaborated Geek
More information about the ffmpeg-devel
mailing list