| @@ -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; | ||||
| @@ -340,4 +340,45 @@ const AVClass *ff_urlcontext_child_class_next(const AVClass *prev); | |||||
| 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 | ||||