diff options
author | Ben Straub <bs@github.com> | 2013-11-04 11:42:14 -0800 |
---|---|---|
committer | Ben Straub <bs@github.com> | 2013-11-04 11:42:14 -0800 |
commit | c227c173b84c8107a8933aeed947f16d82224377 (patch) | |
tree | de2c6457d89387222b7a331c398ce3f67aed0b41 | |
parent | 56c1cda28a4b33fb305d99a2c7985a37efd3839d (diff) | |
download | libgit2-c227c173b84c8107a8933aeed947f16d82224377.tar.gz |
Use http_parser_parse_url to parse urls
-rw-r--r-- | src/netops.c | 89 | ||||
-rw-r--r-- | src/netops.h | 1 | ||||
-rw-r--r-- | src/transports/git.c | 72 | ||||
-rw-r--r-- | src/transports/ssh.c | 1 | ||||
-rw-r--r-- | tests-clar/network/urlparse.c | 55 |
5 files changed, 102 insertions, 116 deletions
diff --git a/src/netops.c b/src/netops.c index d2acdb240..9653344fe 100644 --- a/src/netops.c +++ b/src/netops.c @@ -32,6 +32,7 @@ #include "netops.h" #include "posix.h" #include "buffer.h" +#include "http_parser.h" #ifdef GIT_WIN32 static void net_set_error(const char *str) @@ -582,7 +583,7 @@ int gitno_connection_data_from_url( const char *service_suffix) { int error = -1; - const char *default_port = NULL; + const char *default_port = NULL, *path_search_start = NULL; char *original_host = NULL; /* service_suffix is optional */ @@ -594,22 +595,18 @@ int gitno_connection_data_from_url( gitno_connection_data_free_ptrs(data); if (!git__prefixcmp(url, prefix_http)) { - url = url + strlen(prefix_http); + path_search_start = url + strlen(prefix_http); default_port = "80"; if (data->use_ssl) { giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed"); goto cleanup; } - } - - if (!git__prefixcmp(url, prefix_https)) { - url += strlen(prefix_https); + } else if (!git__prefixcmp(url, prefix_https)) { + path_search_start = url + strlen(prefix_https); default_port = "443"; data->use_ssl = true; - } - - if (url[0] == '/') + } else if (url[0] == '/') default_port = data->use_ssl ? "443" : "80"; if (!default_port) { @@ -618,18 +615,19 @@ int gitno_connection_data_from_url( } error = gitno_extract_url_parts( - &data->host, &data->port, &data->user, &data->pass, + &data->host, &data->port, &data->path, &data->user, &data->pass, url, default_port); if (url[0] == '/') { /* Relative redirect; reuse original host name and port */ + path_search_start = url; git__free(data->host); data->host = original_host; original_host = NULL; } if (!error) { - const char *path = strchr(url, '/'); + const char *path = strchr(path_search_start, '/'); size_t pathlen = strlen(path); size_t suffixlen = service_suffix ? strlen(service_suffix) : 0; @@ -663,53 +661,52 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d) int gitno_extract_url_parts( char **host, char **port, + char **path, char **username, char **password, const char *url, const char *default_port) { - char *colon, *slash, *at; - - /* - * ==> [user[:pass]@]hostname.tld[:port]/resource - */ + struct http_parser_url u = {0}; + const char *_host, *_port, *_path, *_userinfo; - /* Check for user and maybe password. Note that this deviates from RFC-1738 - * in that it allows non-encoded colons in the password field. */ - at = strchr(url, '@'); - if (at) { - colon = strchr(url, ':'); - if (colon && colon < at) { - /* user:pass */ - *username = git__substrdup(url, colon-url); - *password = git__substrdup(colon+1, at-colon-1); - GITERR_CHECK_ALLOC(*password); - } else { - *username = git__substrdup(url, at-url); - } - GITERR_CHECK_ALLOC(*username); - url = at + 1; + if (http_parser_parse_url(url, strlen(url), false, &u)) { + giterr_set(GITERR_NET, "Malformed URL '%s'", url); + return GIT_EINVALIDSPEC; } - /* Validate URL format. Colons shouldn't be in the path part. */ - slash = strchr(url, '/'); - colon = strchr(url, ':'); - if (!slash || - (colon && (slash < colon))) { - giterr_set(GITERR_NET, "Malformed URL: %s", url); - return GIT_EINVALIDSPEC; + _host = url+u.field_data[UF_HOST].off; + _port = url+u.field_data[UF_PORT].off; + _path = url+u.field_data[UF_PATH].off; + _userinfo = url+u.field_data[UF_USERINFO].off; + + if (u.field_data[UF_HOST].len) { + *host = git__substrdup(_host, u.field_data[UF_HOST].len); + GITERR_CHECK_ALLOC(*host); } - /* Check for hostname and maybe port */ - if (colon) { - *host = git__substrdup(url, colon-url); - *port = git__substrdup(colon+1, slash-colon-1); - } else { - *host = git__substrdup(url, slash-url); + if (u.field_data[UF_PORT].len) + *port = git__substrdup(_port, u.field_data[UF_PORT].len); + else *port = git__strdup(default_port); - } - GITERR_CHECK_ALLOC(*host); GITERR_CHECK_ALLOC(*port); + if (u.field_data[UF_PATH].len) { + *path = git__substrdup(_path, u.field_data[UF_PATH].len); + GITERR_CHECK_ALLOC(*path); + } + + if (u.field_data[UF_USERINFO].len) { + const char *colon = strchr(_userinfo, ':'); + if (colon && (colon - _userinfo) < u.field_data[UF_USERINFO].len) { + *username = git__substrdup(_userinfo, colon - _userinfo); + *password = git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)); + GITERR_CHECK_ALLOC(*password); + } else { + *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len); + } + GITERR_CHECK_ALLOC(*username); + } + return 0; } diff --git a/src/netops.h b/src/netops.h index 5c105d6e3..666d66b12 100644 --- a/src/netops.h +++ b/src/netops.h @@ -92,6 +92,7 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *data); int gitno_extract_url_parts( char **host, char **port, + char **path, char **username, char **password, const char *url, diff --git a/src/transports/git.c b/src/transports/git.c index 79a9e7dd4..5dcd4eff7 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -179,39 +179,33 @@ static int _git_uploadpack_ls( const char *url, git_smart_subtransport_stream **stream) { - char *host=NULL, *port=NULL, *user=NULL, *pass=NULL; + char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; + const char *stream_url = url; git_stream *s; + int error = -1; *stream = NULL; - if (!git__prefixcmp(url, prefix_git)) - url += strlen(prefix_git); + stream_url += strlen(prefix_git); - if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0) + if (git_stream_alloc(t, stream_url, cmd_uploadpack, stream) < 0) return -1; s = (git_stream *)*stream; - if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) - goto on_error; - - if (gitno_connect(&s->socket, host, port, 0) < 0) - goto on_error; - - t->current_stream = s; - git__free(host); - git__free(port); - git__free(user); - git__free(pass); - return 0; + if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { + if (!(error = gitno_connect(&s->socket, host, port, 0))) + t->current_stream = s; -on_error: - if (*stream) + git__free(host); + git__free(port); + git__free(path); + git__free(user); + git__free(pass); + } else if (*stream) git_stream_free(*stream); - git__free(host); - git__free(port); - return -1; + return error; } static int _git_uploadpack( @@ -235,39 +229,33 @@ static int _git_receivepack_ls( const char *url, git_smart_subtransport_stream **stream) { - char *host=NULL, *port=NULL, *user=NULL, *pass=NULL; + char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; + const char *stream_url = url; git_stream *s; + int error; *stream = NULL; - if (!git__prefixcmp(url, prefix_git)) - url += strlen(prefix_git); + stream_url += strlen(prefix_git); - if (git_stream_alloc(t, url, cmd_receivepack, stream) < 0) + if (git_stream_alloc(t, stream_url, cmd_receivepack, stream) < 0) return -1; s = (git_stream *)*stream; - if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) - goto on_error; - - if (gitno_connect(&s->socket, host, port, 0) < 0) - goto on_error; - - t->current_stream = s; - git__free(host); - git__free(port); - git__free(user); - git__free(pass); - return 0; + if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { + if (!(error = gitno_connect(&s->socket, host, port, 0))) + t->current_stream = s; -on_error: - if (*stream) + git__free(host); + git__free(port); + git__free(path); + git__free(user); + git__free(pass); + } else if (*stream) git_stream_free(*stream); - git__free(host); - git__free(port); - return -1; + return error; } static int _git_receivepack( diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 4e2834b49..db950e53d 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -330,7 +330,6 @@ static int _git_ssh_setup_conn( s = (ssh_stream *)*stream; if (!git__prefixcmp(url, prefix_ssh)) { - url = url + strlen(prefix_ssh); if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0) goto on_error; } else { diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c index 9fc304a07..4babb0fa7 100644 --- a/tests-clar/network/urlparse.c +++ b/tests-clar/network/urlparse.c @@ -1,12 +1,12 @@ #include "clar_libgit2.h" #include "netops.h" -char *host, *port, *user, *pass; +char *host, *port, *path, *user, *pass; gitno_connection_data conndata; void test_network_urlparse__initialize(void) { - host = port = user = pass = NULL; + host = port = path = user = pass = NULL; memset(&conndata, 0, sizeof(conndata)); } @@ -15,6 +15,7 @@ void test_network_urlparse__cleanup(void) #define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; } FREE_AND_NULL(host); FREE_AND_NULL(port); + FREE_AND_NULL(path); FREE_AND_NULL(user); FREE_AND_NULL(pass); @@ -23,37 +24,33 @@ void test_network_urlparse__cleanup(void) void test_network_urlparse__trivial(void) { - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "example.com/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "http://example.com/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "8080"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_p(user, NULL); cl_assert_equal_p(pass, NULL); } -void test_network_urlparse__bad_url(void) -{ - cl_git_fail_with(gitno_extract_url_parts(&host, &port, &user, &pass, - "github.com/git://github.com/foo/bar.git.git", "443"), - GIT_EINVALIDSPEC); -} - void test_network_urlparse__weird_url(void) { - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "arrbee:my/bad:password@github.com:1111/strange/words.git", "1")); - cl_assert_equal_s(host, "github.com"); - cl_assert_equal_s(port, "1111"); - cl_assert_equal_s(user, "arrbee"); - cl_assert_equal_s(pass, "my/bad:password"); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user:pass%2fis%40bad@hostname.com:1234/", "1")); + cl_assert_equal_s(host, "hostname.com"); + cl_assert_equal_s(port, "1234"); + cl_assert_equal_s(path, "/"); + cl_assert_equal_s(user, "user"); + cl_assert_equal_s(pass, "pass%2fis%40bad"); } void test_network_urlparse__user(void) { - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "user@example.com/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user@example.com/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "8080"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(user, "user"); cl_assert_equal_p(pass, NULL); } @@ -61,10 +58,11 @@ void test_network_urlparse__user(void) void test_network_urlparse__user_pass(void) { /* user:pass@hostname.tld/resource */ - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "user:pass@example.com/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user:pass@example.com/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "8080"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(user, "user"); cl_assert_equal_s(pass, "pass"); } @@ -72,10 +70,11 @@ void test_network_urlparse__user_pass(void) void test_network_urlparse__port(void) { /* hostname.tld:port/resource */ - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "example.com:9191/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://example.com:9191/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "9191"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_p(user, NULL); cl_assert_equal_p(pass, NULL); } @@ -83,10 +82,11 @@ void test_network_urlparse__port(void) void test_network_urlparse__user_port(void) { /* user@hostname.tld:port/resource */ - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "user@example.com:9191/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user@example.com:9191/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "9191"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(user, "user"); cl_assert_equal_p(pass, NULL); } @@ -94,10 +94,11 @@ void test_network_urlparse__user_port(void) void test_network_urlparse__user_pass_port(void) { /* user:pass@hostname.tld:port/resource */ - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "user:pass@example.com:9191/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user:pass@example.com:9191/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "9191"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(user, "user"); cl_assert_equal_s(pass, "pass"); } |