@@ -21,6 +21,31 @@ | |||
#include "libavformat/url.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) | |||
{ | |||
char buf[200], buf2[200]; | |||
@@ -51,6 +76,15 @@ static void test2(const char *url) | |||
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"); | |||
test(NULL, "baz"); | |||
test("/foo/bar", "baz"); | |||
@@ -27,6 +27,7 @@ | |||
#if CONFIG_NETWORK | |||
#include "network.h" | |||
#endif | |||
#include "libavutil/avassert.h" | |||
#include "libavutil/avstring.h" | |||
/** | |||
@@ -78,6 +79,76 @@ int ff_url_join(char *str, int size, const char *proto, | |||
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) | |||
{ | |||
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 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 */ |
@@ -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: | |||
(null) baz => baz | |||
/foo/bar baz => /foo/baz | |||