Commit c227c173 by Ben Straub

Use http_parser_parse_url to parse urls

parent 56c1cda2
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "netops.h" #include "netops.h"
#include "posix.h" #include "posix.h"
#include "buffer.h" #include "buffer.h"
#include "http_parser.h"
#ifdef GIT_WIN32 #ifdef GIT_WIN32
static void net_set_error(const char *str) static void net_set_error(const char *str)
...@@ -582,7 +583,7 @@ int gitno_connection_data_from_url( ...@@ -582,7 +583,7 @@ int gitno_connection_data_from_url(
const char *service_suffix) const char *service_suffix)
{ {
int error = -1; int error = -1;
const char *default_port = NULL; const char *default_port = NULL, *path_search_start = NULL;
char *original_host = NULL; char *original_host = NULL;
/* service_suffix is optional */ /* service_suffix is optional */
...@@ -594,22 +595,18 @@ int gitno_connection_data_from_url( ...@@ -594,22 +595,18 @@ int gitno_connection_data_from_url(
gitno_connection_data_free_ptrs(data); gitno_connection_data_free_ptrs(data);
if (!git__prefixcmp(url, prefix_http)) { if (!git__prefixcmp(url, prefix_http)) {
url = url + strlen(prefix_http); path_search_start = url + strlen(prefix_http);
default_port = "80"; default_port = "80";
if (data->use_ssl) { if (data->use_ssl) {
giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed"); giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed");
goto cleanup; goto cleanup;
} }
} } else if (!git__prefixcmp(url, prefix_https)) {
path_search_start = url + strlen(prefix_https);
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443"; default_port = "443";
data->use_ssl = true; data->use_ssl = true;
} } else if (url[0] == '/')
if (url[0] == '/')
default_port = data->use_ssl ? "443" : "80"; default_port = data->use_ssl ? "443" : "80";
if (!default_port) { if (!default_port) {
...@@ -618,18 +615,19 @@ int gitno_connection_data_from_url( ...@@ -618,18 +615,19 @@ int gitno_connection_data_from_url(
} }
error = gitno_extract_url_parts( 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); url, default_port);
if (url[0] == '/') { if (url[0] == '/') {
/* Relative redirect; reuse original host name and port */ /* Relative redirect; reuse original host name and port */
path_search_start = url;
git__free(data->host); git__free(data->host);
data->host = original_host; data->host = original_host;
original_host = NULL; original_host = NULL;
} }
if (!error) { if (!error) {
const char *path = strchr(url, '/'); const char *path = strchr(path_search_start, '/');
size_t pathlen = strlen(path); size_t pathlen = strlen(path);
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0; size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
...@@ -663,53 +661,52 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d) ...@@ -663,53 +661,52 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d)
int gitno_extract_url_parts( int gitno_extract_url_parts(
char **host, char **host,
char **port, char **port,
char **path,
char **username, char **username,
char **password, char **password,
const char *url, const char *url,
const char *default_port) const char *default_port)
{ {
char *colon, *slash, *at; struct http_parser_url u = {0};
const char *_host, *_port, *_path, *_userinfo;
/*
* ==> [user[:pass]@]hostname.tld[:port]/resource
*/
/* Check for user and maybe password. Note that this deviates from RFC-1738 if (http_parser_parse_url(url, strlen(url), false, &u)) {
* in that it allows non-encoded colons in the password field. */ giterr_set(GITERR_NET, "Malformed URL '%s'", url);
at = strchr(url, '@'); return GIT_EINVALIDSPEC;
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; _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);
} }
/* Validate URL format. Colons shouldn't be in the path part. */ if (u.field_data[UF_PORT].len)
slash = strchr(url, '/'); *port = git__substrdup(_port, u.field_data[UF_PORT].len);
colon = strchr(url, ':'); else
if (!slash || *port = git__strdup(default_port);
(colon && (slash < colon))) { GITERR_CHECK_ALLOC(*port);
giterr_set(GITERR_NET, "Malformed URL: %s", url);
return GIT_EINVALIDSPEC; if (u.field_data[UF_PATH].len) {
*path = git__substrdup(_path, u.field_data[UF_PATH].len);
GITERR_CHECK_ALLOC(*path);
} }
/* Check for hostname and maybe port */ if (u.field_data[UF_USERINFO].len) {
if (colon) { const char *colon = strchr(_userinfo, ':');
*host = git__substrdup(url, colon-url); if (colon && (colon - _userinfo) < u.field_data[UF_USERINFO].len) {
*port = git__substrdup(colon+1, slash-colon-1); *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 { } else {
*host = git__substrdup(url, slash-url); *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
*port = git__strdup(default_port); }
GITERR_CHECK_ALLOC(*username);
} }
GITERR_CHECK_ALLOC(*host);
GITERR_CHECK_ALLOC(*port);
return 0; return 0;
} }
...@@ -92,6 +92,7 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *data); ...@@ -92,6 +92,7 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *data);
int gitno_extract_url_parts( int gitno_extract_url_parts(
char **host, char **host,
char **port, char **port,
char **path,
char **username, char **username,
char **password, char **password,
const char *url, const char *url,
......
...@@ -179,39 +179,33 @@ static int _git_uploadpack_ls( ...@@ -179,39 +179,33 @@ static int _git_uploadpack_ls(
const char *url, const char *url,
git_smart_subtransport_stream **stream) 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; git_stream *s;
int error = -1;
*stream = NULL; *stream = NULL;
if (!git__prefixcmp(url, prefix_git)) 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; return -1;
s = (git_stream *)*stream; s = (git_stream *)*stream;
if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
goto on_error; if (!(error = gitno_connect(&s->socket, host, port, 0)))
if (gitno_connect(&s->socket, host, port, 0) < 0)
goto on_error;
t->current_stream = s; t->current_stream = s;
git__free(host); git__free(host);
git__free(port); git__free(port);
git__free(path);
git__free(user); git__free(user);
git__free(pass); git__free(pass);
return 0; } else if (*stream)
on_error:
if (*stream)
git_stream_free(*stream); git_stream_free(*stream);
git__free(host); return error;
git__free(port);
return -1;
} }
static int _git_uploadpack( static int _git_uploadpack(
...@@ -235,39 +229,33 @@ static int _git_receivepack_ls( ...@@ -235,39 +229,33 @@ static int _git_receivepack_ls(
const char *url, const char *url,
git_smart_subtransport_stream **stream) 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; git_stream *s;
int error;
*stream = NULL; *stream = NULL;
if (!git__prefixcmp(url, prefix_git)) 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; return -1;
s = (git_stream *)*stream; s = (git_stream *)*stream;
if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
goto on_error; if (!(error = gitno_connect(&s->socket, host, port, 0)))
if (gitno_connect(&s->socket, host, port, 0) < 0)
goto on_error;
t->current_stream = s; t->current_stream = s;
git__free(host); git__free(host);
git__free(port); git__free(port);
git__free(path);
git__free(user); git__free(user);
git__free(pass); git__free(pass);
return 0; } else if (*stream)
on_error:
if (*stream)
git_stream_free(*stream); git_stream_free(*stream);
git__free(host); return error;
git__free(port);
return -1;
} }
static int _git_receivepack( static int _git_receivepack(
......
...@@ -330,7 +330,6 @@ static int _git_ssh_setup_conn( ...@@ -330,7 +330,6 @@ static int _git_ssh_setup_conn(
s = (ssh_stream *)*stream; s = (ssh_stream *)*stream;
if (!git__prefixcmp(url, prefix_ssh)) { if (!git__prefixcmp(url, prefix_ssh)) {
url = url + strlen(prefix_ssh);
if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0) if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0)
goto on_error; goto on_error;
} else { } else {
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "netops.h" #include "netops.h"
char *host, *port, *user, *pass; char *host, *port, *path, *user, *pass;
gitno_connection_data conndata; gitno_connection_data conndata;
void test_network_urlparse__initialize(void) void test_network_urlparse__initialize(void)
{ {
host = port = user = pass = NULL; host = port = path = user = pass = NULL;
memset(&conndata, 0, sizeof(conndata)); memset(&conndata, 0, sizeof(conndata));
} }
...@@ -15,6 +15,7 @@ void test_network_urlparse__cleanup(void) ...@@ -15,6 +15,7 @@ void test_network_urlparse__cleanup(void)
#define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; } #define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; }
FREE_AND_NULL(host); FREE_AND_NULL(host);
FREE_AND_NULL(port); FREE_AND_NULL(port);
FREE_AND_NULL(path);
FREE_AND_NULL(user); FREE_AND_NULL(user);
FREE_AND_NULL(pass); FREE_AND_NULL(pass);
...@@ -23,37 +24,33 @@ void test_network_urlparse__cleanup(void) ...@@ -23,37 +24,33 @@ void test_network_urlparse__cleanup(void)
void test_network_urlparse__trivial(void) void test_network_urlparse__trivial(void)
{ {
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
"example.com/resource", "8080")); "http://example.com/resource", "8080"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "8080"); cl_assert_equal_s(port, "8080");
cl_assert_equal_s(path, "/resource");
cl_assert_equal_p(user, NULL); cl_assert_equal_p(user, NULL);
cl_assert_equal_p(pass, 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) void test_network_urlparse__weird_url(void)
{ {
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
"arrbee:my/bad:password@github.com:1111/strange/words.git", "1")); "https://user:pass%2fis%40bad@hostname.com:1234/", "1"));
cl_assert_equal_s(host, "github.com"); cl_assert_equal_s(host, "hostname.com");
cl_assert_equal_s(port, "1111"); cl_assert_equal_s(port, "1234");
cl_assert_equal_s(user, "arrbee"); cl_assert_equal_s(path, "/");
cl_assert_equal_s(pass, "my/bad:password"); cl_assert_equal_s(user, "user");
cl_assert_equal_s(pass, "pass%2fis%40bad");
} }
void test_network_urlparse__user(void) void test_network_urlparse__user(void)
{ {
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
"user@example.com/resource", "8080")); "https://user@example.com/resource", "8080"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "8080"); cl_assert_equal_s(port, "8080");
cl_assert_equal_s(path, "/resource");
cl_assert_equal_s(user, "user"); cl_assert_equal_s(user, "user");
cl_assert_equal_p(pass, NULL); cl_assert_equal_p(pass, NULL);
} }
...@@ -61,10 +58,11 @@ void test_network_urlparse__user(void) ...@@ -61,10 +58,11 @@ void test_network_urlparse__user(void)
void test_network_urlparse__user_pass(void) void test_network_urlparse__user_pass(void)
{ {
/* user:pass@hostname.tld/resource */ /* user:pass@hostname.tld/resource */
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
"user:pass@example.com/resource", "8080")); "https://user:pass@example.com/resource", "8080"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "8080"); cl_assert_equal_s(port, "8080");
cl_assert_equal_s(path, "/resource");
cl_assert_equal_s(user, "user"); cl_assert_equal_s(user, "user");
cl_assert_equal_s(pass, "pass"); cl_assert_equal_s(pass, "pass");
} }
...@@ -72,10 +70,11 @@ void test_network_urlparse__user_pass(void) ...@@ -72,10 +70,11 @@ void test_network_urlparse__user_pass(void)
void test_network_urlparse__port(void) void test_network_urlparse__port(void)
{ {
/* hostname.tld:port/resource */ /* hostname.tld:port/resource */
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
"example.com:9191/resource", "8080")); "https://example.com:9191/resource", "8080"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "9191"); cl_assert_equal_s(port, "9191");
cl_assert_equal_s(path, "/resource");
cl_assert_equal_p(user, NULL); cl_assert_equal_p(user, NULL);
cl_assert_equal_p(pass, NULL); cl_assert_equal_p(pass, NULL);
} }
...@@ -83,10 +82,11 @@ void test_network_urlparse__port(void) ...@@ -83,10 +82,11 @@ void test_network_urlparse__port(void)
void test_network_urlparse__user_port(void) void test_network_urlparse__user_port(void)
{ {
/* user@hostname.tld:port/resource */ /* user@hostname.tld:port/resource */
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
"user@example.com:9191/resource", "8080")); "https://user@example.com:9191/resource", "8080"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "9191"); cl_assert_equal_s(port, "9191");
cl_assert_equal_s(path, "/resource");
cl_assert_equal_s(user, "user"); cl_assert_equal_s(user, "user");
cl_assert_equal_p(pass, NULL); cl_assert_equal_p(pass, NULL);
} }
...@@ -94,10 +94,11 @@ void test_network_urlparse__user_port(void) ...@@ -94,10 +94,11 @@ void test_network_urlparse__user_port(void)
void test_network_urlparse__user_pass_port(void) void test_network_urlparse__user_pass_port(void)
{ {
/* user:pass@hostname.tld:port/resource */ /* user:pass@hostname.tld:port/resource */
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
"user:pass@example.com:9191/resource", "8080")); "https://user:pass@example.com:9191/resource", "8080"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "9191"); cl_assert_equal_s(port, "9191");
cl_assert_equal_s(path, "/resource");
cl_assert_equal_s(user, "user"); cl_assert_equal_s(user, "user");
cl_assert_equal_s(pass, "pass"); cl_assert_equal_s(pass, "pass");
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment