summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Straub <bs@github.com>2013-11-04 11:42:14 -0800
committerBen Straub <bs@github.com>2013-11-04 11:42:14 -0800
commitc227c173b84c8107a8933aeed947f16d82224377 (patch)
treede2c6457d89387222b7a331c398ce3f67aed0b41
parent56c1cda28a4b33fb305d99a2c7985a37efd3839d (diff)
downloadlibgit2-c227c173b84c8107a8933aeed947f16d82224377.tar.gz
Use http_parser_parse_url to parse urls
-rw-r--r--src/netops.c89
-rw-r--r--src/netops.h1
-rw-r--r--src/transports/git.c72
-rw-r--r--src/transports/ssh.c1
-rw-r--r--tests-clar/network/urlparse.c55
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");
}