| @@ -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 | |||