@@ -21,6 +21,31 @@ | |||||
#include "libavformat/url.h" | #include "libavformat/url.h" | ||||
#include "libavformat/avformat.h" | #include "libavformat/avformat.h" | ||||
static void test_decompose(const char *url) | |||||
{ | |||||
URLComponents uc; | |||||
int len, ret; | |||||
printf("%s =>\n", url); | |||||
ret = ff_url_decompose(&uc, url, NULL); | |||||
if (ret < 0) { | |||||
printf(" error: %s\n", av_err2str(ret)); | |||||
return; | |||||
} | |||||
#define PRINT_COMPONENT(comp) \ | |||||
len = uc.url_component_end_##comp - uc.comp; \ | |||||
if (len) printf(" "#comp": %.*s\n", len, uc.comp); | |||||
PRINT_COMPONENT(scheme); | |||||
PRINT_COMPONENT(authority); | |||||
PRINT_COMPONENT(userinfo); | |||||
PRINT_COMPONENT(host); | |||||
PRINT_COMPONENT(port); | |||||
PRINT_COMPONENT(path); | |||||
PRINT_COMPONENT(query); | |||||
PRINT_COMPONENT(fragment); | |||||
printf("\n"); | |||||
} | |||||
static void test(const char *base, const char *rel) | static void test(const char *base, const char *rel) | ||||
{ | { | ||||
char buf[200], buf2[200]; | char buf[200], buf2[200]; | ||||
@@ -51,6 +76,15 @@ static void test2(const char *url) | |||||
int main(void) | int main(void) | ||||
{ | { | ||||
printf("Testing ff_url_decompose:\n\n"); | |||||
test_decompose("http://user:pass@ffmpeg:8080/dir/file?query#fragment"); | |||||
test_decompose("http://ffmpeg/dir/file"); | |||||
test_decompose("file:///dev/null"); | |||||
test_decompose("file:/dev/null"); | |||||
test_decompose("http://[::1]/dev/null"); | |||||
test_decompose("http://[::1]:8080/dev/null"); | |||||
test_decompose("//ffmpeg/dev/null"); | |||||
printf("Testing ff_make_absolute_url:\n"); | printf("Testing ff_make_absolute_url:\n"); | ||||
test(NULL, "baz"); | test(NULL, "baz"); | ||||
test("/foo/bar", "baz"); | test("/foo/bar", "baz"); | ||||
@@ -27,6 +27,7 @@ | |||||
#if CONFIG_NETWORK | #if CONFIG_NETWORK | ||||
#include "network.h" | #include "network.h" | ||||
#endif | #endif | ||||
#include "libavutil/avassert.h" | |||||
#include "libavutil/avstring.h" | #include "libavutil/avstring.h" | ||||
/** | /** | ||||
@@ -78,6 +79,76 @@ int ff_url_join(char *str, int size, const char *proto, | |||||
return strlen(str); | return strlen(str); | ||||
} | } | ||||
static const char *find_delim(const char *delim, const char *cur, const char *end) | |||||
{ | |||||
while (cur < end && !strchr(delim, *cur)) | |||||
cur++; | |||||
return cur; | |||||
} | |||||
int ff_url_decompose(URLComponents *uc, const char *url, const char *end) | |||||
{ | |||||
const char *cur, *aend, *p; | |||||
av_assert0(url); | |||||
if (!end) | |||||
end = url + strlen(url); | |||||
cur = uc->url = url; | |||||
/* scheme */ | |||||
uc->scheme = cur; | |||||
p = find_delim(":/", cur, end); /* lavf "schemes" can contain options */ | |||||
if (*p == ':') | |||||
cur = p + 1; | |||||
/* authority */ | |||||
uc->authority = cur; | |||||
if (end - cur >= 2 && cur[0] == '/' && cur[1] == '/') { | |||||
cur += 2; | |||||
aend = find_delim("/?#", cur, end); | |||||
/* userinfo */ | |||||
uc->userinfo = cur; | |||||
p = find_delim("@", cur, aend); | |||||
if (*p == '@') | |||||
cur = p + 1; | |||||
/* host */ | |||||
uc->host = cur; | |||||
if (*cur == '[') { /* hello IPv6, thanks for using colons! */ | |||||
p = find_delim("]", cur, aend); | |||||
if (*p != ']') | |||||
return AVERROR(EINVAL); | |||||
if (p + 1 < aend && p[1] != ':') | |||||
return AVERROR(EINVAL); | |||||
cur = p + 1; | |||||
} else { | |||||
cur = find_delim(":", cur, aend); | |||||
} | |||||
/* port */ | |||||
uc->port = cur; | |||||
cur = aend; | |||||
} else { | |||||
uc->userinfo = uc->host = uc->port = cur; | |||||
} | |||||
/* path */ | |||||
uc->path = cur; | |||||
cur = find_delim("?#", cur, end); | |||||
/* query */ | |||||
uc->query = cur; | |||||
if (*cur == '?') | |||||
cur = find_delim("#", cur, end); | |||||
/* fragment */ | |||||
uc->fragment = cur; | |||||
uc->end = end; | |||||
return 0; | |||||
} | |||||
static void trim_double_dot_url(char *buf, const char *rel, int size) | static void trim_double_dot_url(char *buf, const char *rel, int size) | ||||
{ | { | ||||
const char *p = rel; | const char *p = rel; | ||||
@@ -344,4 +344,45 @@ const AVClass *ff_urlcontext_child_class_iterate(void **iter); | |||||
const URLProtocol **ffurl_get_protocols(const char *whitelist, | const URLProtocol **ffurl_get_protocols(const char *whitelist, | ||||
const char *blacklist); | const char *blacklist); | ||||
typedef struct URLComponents { | |||||
const char *url; /**< whole URL, for reference */ | |||||
const char *scheme; /**< possibly including lavf-specific options */ | |||||
const char *authority; /**< "//" if it is a real URL */ | |||||
const char *userinfo; /**< including final '@' if present */ | |||||
const char *host; | |||||
const char *port; /**< including initial ':' if present */ | |||||
const char *path; | |||||
const char *query; /**< including initial '?' if present */ | |||||
const char *fragment; /**< including initial '#' if present */ | |||||
const char *end; | |||||
} URLComponents; | |||||
#define url_component_end_scheme authority | |||||
#define url_component_end_authority userinfo | |||||
#define url_component_end_userinfo host | |||||
#define url_component_end_host port | |||||
#define url_component_end_port path | |||||
#define url_component_end_path query | |||||
#define url_component_end_query fragment | |||||
#define url_component_end_fragment end | |||||
#define url_component_end_authority_full path | |||||
#define URL_COMPONENT_HAVE(uc, component) \ | |||||
((uc).url_component_end_##component > (uc).component) | |||||
/** | |||||
* Parse an URL to find the components. | |||||
* | |||||
* Each component runs until the start of the next component, | |||||
* possibly including a mandatory delimiter. | |||||
* | |||||
* @param uc structure to fill with pointers to the components. | |||||
* @param url URL to parse. | |||||
* @param end end of the URL, or NULL to parse to the end of string. | |||||
* | |||||
* @return >= 0 for success or an AVERROR code, especially if the URL is | |||||
* malformed. | |||||
*/ | |||||
int ff_url_decompose(URLComponents *uc, const char *url, const char *end); | |||||
#endif /* AVFORMAT_URL_H */ | #endif /* AVFORMAT_URL_H */ |
@@ -1,3 +1,48 @@ | |||||
Testing ff_url_decompose: | |||||
http://user:pass@ffmpeg:8080/dir/file?query#fragment => | |||||
scheme: http: | |||||
authority: // | |||||
userinfo: user:pass@ | |||||
host: ffmpeg | |||||
port: :8080 | |||||
path: /dir/file | |||||
query: ?query | |||||
fragment: #fragment | |||||
http://ffmpeg/dir/file => | |||||
scheme: http: | |||||
authority: // | |||||
host: ffmpeg | |||||
path: /dir/file | |||||
file:///dev/null => | |||||
scheme: file: | |||||
authority: // | |||||
path: /dev/null | |||||
file:/dev/null => | |||||
scheme: file: | |||||
path: /dev/null | |||||
http://[::1]/dev/null => | |||||
scheme: http: | |||||
authority: // | |||||
host: [::1] | |||||
path: /dev/null | |||||
http://[::1]:8080/dev/null => | |||||
scheme: http: | |||||
authority: // | |||||
host: [::1] | |||||
port: :8080 | |||||
path: /dev/null | |||||
//ffmpeg/dev/null => | |||||
authority: // | |||||
host: ffmpeg | |||||
path: /dev/null | |||||
Testing ff_make_absolute_url: | Testing ff_make_absolute_url: | ||||
(null) baz => baz | (null) baz => baz | ||||
/foo/bar baz => /foo/baz | /foo/bar baz => /foo/baz | ||||