summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2019-03-11 11:43:08 +0000
committerEdward Thomson <ethomson@edwardthomson.com>2019-06-10 19:58:22 +0100
commitc6ab183e9c960b74471a7d106a4deb0c8b28a8ec (patch)
treec95f2f36156abd863337e7b6a18e9ec693a5a565
parentf4584a1e8bf3095b90f5564c0f89878ef79855c5 (diff)
downloadlibgit2-c6ab183e9c960b74471a7d106a4deb0c8b28a8ec.tar.gz
net: rename gitno_connection_data to git_net_url
"Connection data" is an imprecise and largely incorrect name; these structures are actually parsed URLs. Provide a parser that takes a URL string and produces a URL structure (if it is valid). Separate the HTTP redirect handling logic from URL parsing, keeping a `gitno_connection_data_handle_redirect` whose only job is redirect handling logic and does not parse URLs itself.
-rw-r--r--src/net.c184
-rw-r--r--src/net.h36
-rw-r--r--src/netops.c211
-rw-r--r--src/netops.h28
-rw-r--r--src/transports/auth.c8
-rw-r--r--src/transports/auth.h6
-rw-r--r--src/transports/auth_negotiate.c8
-rw-r--r--src/transports/auth_negotiate.h2
-rw-r--r--src/transports/git.c27
-rw-r--r--src/transports/http.c48
-rw-r--r--src/transports/ssh.c56
-rw-r--r--src/transports/winhttp.c78
-rw-r--r--tests/network/redirect.c113
-rw-r--r--tests/network/urlparse.c262
14 files changed, 587 insertions, 480 deletions
diff --git a/src/net.c b/src/net.c
new file mode 100644
index 000000000..e35abbb94
--- /dev/null
+++ b/src/net.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "net.h"
+#include "netops.h"
+
+#include <ctype.h>
+#include "git2/errors.h"
+
+#include "posix.h"
+#include "buffer.h"
+#include "http_parser.h"
+#include "global.h"
+
+#define DEFAULT_PORT_HTTP "80"
+#define DEFAULT_PORT_HTTPS "443"
+#define DEFAULT_PORT_GIT "9418"
+#define DEFAULT_PORT_SSH "22"
+
+static const char *default_port_for_scheme(const char *scheme)
+{
+ if (strcmp(scheme, "http") == 0)
+ return DEFAULT_PORT_HTTP;
+ else if (strcmp(scheme, "https") == 0)
+ return DEFAULT_PORT_HTTPS;
+ else if (strcmp(scheme, "git") == 0)
+ return DEFAULT_PORT_GIT;
+ else if (strcmp(scheme, "ssh") == 0)
+ return DEFAULT_PORT_SSH;
+
+ return NULL;
+}
+
+int git_net_url_parse(git_net_url *url, const char *given)
+{
+ struct http_parser_url u = {0};
+ bool has_scheme, has_host, has_port, has_path, has_query, has_userinfo;
+ git_buf scheme = GIT_BUF_INIT,
+ host = GIT_BUF_INIT,
+ port = GIT_BUF_INIT,
+ path = GIT_BUF_INIT,
+ username = GIT_BUF_INIT,
+ password = GIT_BUF_INIT,
+ query = GIT_BUF_INIT;
+ int error = GIT_EINVALIDSPEC;
+
+ if (http_parser_parse_url(given, strlen(given), false, &u)) {
+ git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given);
+ goto done;
+ }
+
+ has_scheme = !!(u.field_set & (1 << UF_SCHEMA));
+ has_host = !!(u.field_set & (1 << UF_HOST));
+ has_port = !!(u.field_set & (1 << UF_PORT));
+ has_path = !!(u.field_set & (1 << UF_PATH));
+ has_query = !!(u.field_set & (1 << UF_QUERY));
+ has_userinfo = !!(u.field_set & (1 << UF_USERINFO));
+
+ if (has_scheme) {
+ const char *url_scheme = given + u.field_data[UF_SCHEMA].off;
+ size_t url_scheme_len = u.field_data[UF_SCHEMA].len;
+ git_buf_put(&scheme, url_scheme, url_scheme_len);
+ git__strntolower(scheme.ptr, scheme.size);
+ } else {
+ git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given);
+ goto done;
+ }
+
+ if (has_host) {
+ const char *url_host = given + u.field_data[UF_HOST].off;
+ size_t url_host_len = u.field_data[UF_HOST].len;
+ git_buf_decode_percent(&host, url_host, url_host_len);
+ }
+
+ if (has_port) {
+ const char *url_port = given + u.field_data[UF_PORT].off;
+ size_t url_port_len = u.field_data[UF_PORT].len;
+ git_buf_put(&port, url_port, url_port_len);
+ } else {
+ const char *default_port = default_port_for_scheme(scheme.ptr);
+
+ if (default_port == NULL) {
+ git_error_set(GIT_ERROR_NET, "unknown scheme for URL '%s'", given);
+ goto done;
+ }
+
+ git_buf_puts(&port, default_port);
+ }
+
+ if (has_path) {
+ const char *url_path = given + u.field_data[UF_PATH].off;
+ size_t url_path_len = u.field_data[UF_PATH].len;
+ git_buf_decode_percent(&path, url_path, url_path_len);
+ } else {
+ git_buf_puts(&path, "/");
+ }
+
+ if (has_query) {
+ const char *url_query = given + u.field_data[UF_QUERY].off;
+ size_t url_query_len = u.field_data[UF_QUERY].len;
+ git_buf_decode_percent(&query, url_query, url_query_len);
+ }
+
+ if (has_userinfo) {
+ const char *url_userinfo = given + u.field_data[UF_USERINFO].off;
+ size_t url_userinfo_len = u.field_data[UF_USERINFO].len;
+ const char *colon = memchr(url_userinfo, ':', url_userinfo_len);
+
+ if (colon) {
+ const char *url_username = url_userinfo;
+ size_t url_username_len = colon - url_userinfo;
+ const char *url_password = colon + 1;
+ size_t url_password_len = url_userinfo_len - (url_username_len + 1);
+
+ git_buf_decode_percent(&username, url_username, url_username_len);
+ git_buf_decode_percent(&password, url_password, url_password_len);
+ } else {
+ git_buf_decode_percent(&username, url_userinfo, url_userinfo_len);
+ }
+ }
+
+ if (git_buf_oom(&scheme) ||
+ git_buf_oom(&host) ||
+ git_buf_oom(&port) ||
+ git_buf_oom(&path) ||
+ git_buf_oom(&query) ||
+ git_buf_oom(&username) ||
+ git_buf_oom(&password))
+ return -1;
+
+ url->scheme = git_buf_detach(&scheme);
+ url->host = git_buf_detach(&host);
+ url->port = git_buf_detach(&port);
+ url->path = git_buf_detach(&path);
+ url->query = git_buf_detach(&query);
+ url->username = git_buf_detach(&username);
+ url->password = git_buf_detach(&password);
+
+ error = 0;
+
+done:
+ git_buf_dispose(&scheme);
+ git_buf_dispose(&host);
+ git_buf_dispose(&port);
+ git_buf_dispose(&path);
+ git_buf_dispose(&query);
+ git_buf_dispose(&username);
+ git_buf_dispose(&password);
+ return error;
+}
+
+int git_net_url_is_default_port(git_net_url *url)
+{
+ return (strcmp(url->port, default_port_for_scheme(url->scheme)) == 0);
+}
+
+void git_net_url_swap(git_net_url *a, git_net_url *b)
+{
+ git_net_url tmp = GIT_NET_URL_INIT;
+
+ memcpy(&tmp, a, sizeof(git_net_url));
+ memcpy(a, b, sizeof(git_net_url));
+ memcpy(b, &tmp, sizeof(git_net_url));
+}
+
+void git_net_url_dispose(git_net_url *url)
+{
+ if (url->username)
+ git__memzero(url->username, strlen(url->username));
+
+ if (url->password)
+ git__memzero(url->password, strlen(url->password));
+
+ git__free(url->scheme); url->scheme = NULL;
+ git__free(url->host); url->host = NULL;
+ git__free(url->port); url->port = NULL;
+ git__free(url->path); url->path = NULL;
+ git__free(url->username); url->username = NULL;
+ git__free(url->password); url->password = NULL;
+}
diff --git a/src/net.h b/src/net.h
new file mode 100644
index 000000000..6df129089
--- /dev/null
+++ b/src/net.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_net_h__
+#define INCLUDE_net_h__
+
+#include "common.h"
+
+typedef struct git_net_url {
+ char *scheme;
+ char *host;
+ char *port;
+ char *path;
+ char *query;
+ char *username;
+ char *password;
+} git_net_url;
+
+#define GIT_NET_URL_INIT { NULL }
+
+/** Parses a string containing a URL into a structure. */
+int git_net_url_parse(git_net_url *url, const char *str);
+
+/** Returns nonzero if the URL is on the default port. */
+int git_net_url_is_default_port(git_net_url *url);
+
+/** Swaps the contents of one URL for another. */
+void git_net_url_swap(git_net_url *a, git_net_url *b);
+
+/** Disposes the contents of the structure. */
+void git_net_url_dispose(git_net_url *url);
+
+#endif
diff --git a/src/netops.c b/src/netops.c
index ecbc2aebe..708f694e3 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -119,192 +119,79 @@ int gitno__match_host(const char *pattern, const char *host)
return -1;
}
-static const char *default_port_http = "80";
-static const char *default_port_https = "443";
-
-const char *gitno__default_port(
- gitno_connection_data *data)
-{
- return data->use_ssl ? default_port_https : default_port_http;
-}
-
-static const char *prefix_http = "http://";
-static const char *prefix_https = "https://";
-
-int gitno_connection_data_from_url(
- gitno_connection_data *data,
- const char *url,
+int gitno_connection_data_handle_redirect(
+ git_net_url *url,
+ const char *redirect_str,
const char *service_suffix)
{
- int error = -1;
- const char *default_port = NULL, *path_search_start = NULL;
- char *original_host = NULL;
-
- /* service_suffix is optional */
- assert(data && url);
+ git_net_url tmp = GIT_NET_URL_INIT;
+ int error = 0;
- /* Save these for comparison later */
- original_host = data->host;
- data->host = NULL;
- gitno_connection_data_free_ptrs(data);
+ assert(url && redirect_str);
- if (!git__prefixcmp(url, prefix_http)) {
- path_search_start = url + strlen(prefix_http);
- default_port = default_port_http;
+ if (redirect_str[0] == '/') {
+ git__free(url->path);
- if (data->use_ssl) {
- git_error_set(GIT_ERROR_NET, "redirect from HTTPS to HTTP is not allowed");
- goto cleanup;
+ if ((url->path = git__strdup(redirect_str)) == NULL) {
+ error = -1;
+ goto done;
}
- } else if (!git__prefixcmp(url, prefix_https)) {
- path_search_start = url + strlen(prefix_https);
- default_port = default_port_https;
- data->use_ssl = true;
- } else if (url[0] == '/')
- default_port = gitno__default_port(data);
-
- if (!default_port) {
- git_error_set(GIT_ERROR_NET, "unrecognized URL prefix");
- goto cleanup;
- }
+ } else {
+ git_net_url *original = url;
- error = gitno_extract_url_parts(
- &data->host, &data->port, &data->path, &data->user, &data->pass,
- url, default_port);
+ if ((error = git_net_url_parse(&tmp, redirect_str)) < 0)
+ goto done;
- 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;
- }
+ /* Validate that this is a legal redirection */
- if (!error) {
- const char *path = strchr(path_search_start, '/');
- size_t pathlen = strlen(path);
- size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
-
- if (suffixlen &&
- !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen)) {
- git__free(data->path);
- data->path = git__strndup(path, pathlen - suffixlen);
- } else {
- git__free(data->path);
- data->path = git__strdup(path);
- }
+ if (original->scheme &&
+ strcmp(original->scheme, tmp.scheme) != 0 &&
+ strcmp(tmp.scheme, "https") != 0) {
+ git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
+ original->scheme, tmp.scheme);
- /* Check for errors in the resulting data */
- if (original_host && url[0] != '/' && strcmp(original_host, data->host)) {
- git_error_set(GIT_ERROR_NET, "cross host redirect not allowed");
error = -1;
+ goto done;
}
- }
-cleanup:
- if (original_host) git__free(original_host);
- return error;
-}
+ if (original->host &&
+ git__strcasecmp(original->host, tmp.host) != 0) {
+ git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
+ original->host, tmp.host);
-void gitno_connection_data_free_ptrs(gitno_connection_data *d)
-{
- git__free(d->host); d->host = NULL;
- git__free(d->port); d->port = NULL;
- git__free(d->path); d->path = NULL;
- git__free(d->user); d->user = NULL;
- git__free(d->pass); d->pass = NULL;
-}
-
-int gitno_extract_url_parts(
- char **host_out,
- char **port_out,
- char **path_out,
- char **username_out,
- char **password_out,
- const char *url,
- const char *default_port)
-{
- struct http_parser_url u = {0};
- bool has_host, has_port, has_path, has_userinfo;
- git_buf host = GIT_BUF_INIT,
- port = GIT_BUF_INIT,
- path = GIT_BUF_INIT,
- username = GIT_BUF_INIT,
- password = GIT_BUF_INIT;
- int error = 0;
+ error = -1;
+ goto done;
+ }
- if (http_parser_parse_url(url, strlen(url), false, &u)) {
- git_error_set(GIT_ERROR_NET, "malformed URL '%s'", url);
- error = GIT_EINVALIDSPEC;
- goto done;
+ git_net_url_swap(url, &tmp);
}
- has_host = !!(u.field_set & (1 << UF_HOST));
- has_port = !!(u.field_set & (1 << UF_PORT));
- has_path = !!(u.field_set & (1 << UF_PATH));
- has_userinfo = !!(u.field_set & (1 << UF_USERINFO));
+ /* Remove the service suffix if it was given to us */
+ if (service_suffix) {
+ const char *service_query = strchr(service_suffix, '?');
+ size_t suffix_len = service_query ?
+ (size_t)(service_query - service_suffix) : strlen(service_suffix);
+ size_t path_len = strlen(url->path);
- if (has_host) {
- const char *url_host = url + u.field_data[UF_HOST].off;
- size_t url_host_len = u.field_data[UF_HOST].len;
- git_buf_decode_percent(&host, url_host, url_host_len);
- }
+ if (suffix_len && path_len >= suffix_len) {
+ size_t suffix_offset = path_len - suffix_len;
- if (has_port) {
- const char *url_port = url + u.field_data[UF_PORT].off;
- size_t url_port_len = u.field_data[UF_PORT].len;
- git_buf_put(&port, url_port, url_port_len);
- } else {
- git_buf_puts(&port, default_port);
- }
+ if (git__strncmp(url->path + suffix_offset, service_suffix, suffix_len) == 0 &&
+ (!service_query || git__strcmp(url->query, service_query + 1) == 0)) {
+ /* Ensure we leave a minimum of '/' as the path */
+ if (suffix_offset == 0)
+ suffix_offset++;
- if (has_path && path_out) {
- const char *url_path = url + u.field_data[UF_PATH].off;
- size_t url_path_len = u.field_data[UF_PATH].len;
- git_buf_decode_percent(&path, url_path, url_path_len);
- } else if (path_out) {
- git_error_set(GIT_ERROR_NET, "invalid url, missing path");
- error = GIT_EINVALIDSPEC;
- goto done;
- }
+ url->path[suffix_offset] = '\0';
- if (has_userinfo) {
- const char *url_userinfo = url + u.field_data[UF_USERINFO].off;
- size_t url_userinfo_len = u.field_data[UF_USERINFO].len;
- const char *colon = memchr(url_userinfo, ':', url_userinfo_len);
-
- if (colon) {
- const char *url_username = url_userinfo;
- size_t url_username_len = colon - url_userinfo;
- const char *url_password = colon + 1;
- size_t url_password_len = url_userinfo_len - (url_username_len + 1);
-
- git_buf_decode_percent(&username, url_username, url_username_len);
- git_buf_decode_percent(&password, url_password, url_password_len);
- } else {
- git_buf_decode_percent(&username, url_userinfo, url_userinfo_len);
+ git__free(url->query);
+ url->query = NULL;
+ }
}
}
- if (git_buf_oom(&host) ||
- git_buf_oom(&port) ||
- git_buf_oom(&path) ||
- git_buf_oom(&username) ||
- git_buf_oom(&password))
- return -1;
-
- *host_out = git_buf_detach(&host);
- *port_out = git_buf_detach(&port);
- if (path_out)
- *path_out = git_buf_detach(&path);
- *username_out = git_buf_detach(&username);
- *password_out = git_buf_detach(&password);
-
done:
- git_buf_dispose(&host);
- git_buf_dispose(&port);
- git_buf_dispose(&path);
- git_buf_dispose(&username);
- git_buf_dispose(&password);
+ git_net_url_dispose(&tmp);
return error;
}
+
diff --git a/src/netops.h b/src/netops.h
index f376bd911..4c4bf78b0 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -11,6 +11,7 @@
#include "posix.h"
#include "stream.h"
+#include "net.h"
#ifdef GIT_OPENSSL
# include <openssl/ssl.h>
@@ -64,38 +65,15 @@ int gitno_recv(gitno_buffer *buf);
void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons);
-typedef struct gitno_connection_data {
- char *host;
- char *port;
- char *path;
- char *user;
- char *pass;
- bool use_ssl;
-} gitno_connection_data;
-
/*
* This replaces all the pointers in `data` with freshly-allocated strings,
* that the caller is responsible for freeing.
* `gitno_connection_data_free_ptrs` is good for this.
*/
-int gitno_connection_data_from_url(
- gitno_connection_data *data,
+int gitno_connection_data_handle_redirect(
+ git_net_url *data,
const char *url,
const char *service_suffix);
-/* This frees all the pointers IN the struct, but not the struct itself. */
-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,
- const char *default_port);
-
-const char *gitno__default_port(gitno_connection_data *data);
-
#endif
diff --git a/src/transports/auth.c b/src/transports/auth.c
index 6c69282b4..ea6cb6502 100644
--- a/src/transports/auth.c
+++ b/src/transports/auth.c
@@ -56,18 +56,18 @@ static git_http_auth_context basic_context = {
};
int git_http_auth_basic(
- git_http_auth_context **out, const gitno_connection_data *connection_data)
+ git_http_auth_context **out, const git_net_url *url)
{
- GIT_UNUSED(connection_data);
+ GIT_UNUSED(url);
*out = &basic_context;
return 0;
}
int git_http_auth_dummy(
- git_http_auth_context **out, const gitno_connection_data *connection_data)
+ git_http_auth_context **out, const git_net_url *url)
{
- GIT_UNUSED(connection_data);
+ GIT_UNUSED(url);
*out = NULL;
return 0;
diff --git a/src/transports/auth.h b/src/transports/auth.h
index e5cf7eff0..7b64770d6 100644
--- a/src/transports/auth.h
+++ b/src/transports/auth.h
@@ -50,15 +50,15 @@ typedef struct {
/** Function to initialize an authentication context */
int (*init_context)(
git_http_auth_context **out,
- const gitno_connection_data *connection_data);
+ const git_net_url *url);
} git_http_auth_scheme;
int git_http_auth_dummy(
git_http_auth_context **out,
- const gitno_connection_data *connection_data);
+ const git_net_url *url);
int git_http_auth_basic(
git_http_auth_context **out,
- const gitno_connection_data *connection_data);
+ const git_net_url *url);
#endif
diff --git a/src/transports/auth_negotiate.c b/src/transports/auth_negotiate.c
index 25c865c15..03d333626 100644
--- a/src/transports/auth_negotiate.c
+++ b/src/transports/auth_negotiate.c
@@ -194,7 +194,7 @@ static void negotiate_context_free(git_http_auth_context *c)
static int negotiate_init_context(
http_auth_negotiate_context *ctx,
- const gitno_connection_data *connection_data)
+ const git_net_url *url)
{
OM_uint32 status_major, status_minor;
gss_OID item, *oid;
@@ -235,7 +235,7 @@ static int negotiate_init_context(
}
git_buf_puts(&ctx->target, "HTTP@");
- git_buf_puts(&ctx->target, connection_data->host);
+ git_buf_puts(&ctx->target, url->host);
if (git_buf_oom(&ctx->target))
return -1;
@@ -248,7 +248,7 @@ static int negotiate_init_context(
int git_http_auth_negotiate(
git_http_auth_context **out,
- const gitno_connection_data *connection_data)
+ const git_net_url *url)
{
http_auth_negotiate_context *ctx;
@@ -257,7 +257,7 @@ int git_http_auth_negotiate(
ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
GIT_ERROR_CHECK_ALLOC(ctx);
- if (negotiate_init_context(ctx, connection_data) < 0) {
+ if (negotiate_init_context(ctx, url) < 0) {
git__free(ctx);
return -1;
}
diff --git a/src/transports/auth_negotiate.h b/src/transports/auth_negotiate.h
index 15a528aaf..d3f2ba2a1 100644
--- a/src/transports/auth_negotiate.h
+++ b/src/transports/auth_negotiate.h
@@ -16,7 +16,7 @@
extern int git_http_auth_negotiate(
git_http_auth_context **out,
- const gitno_connection_data *connection_data);
+ const git_net_url *url);
#else
diff --git a/src/transports/git.c b/src/transports/git.c
index 9fd3b47fc..e48b7f961 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -192,8 +192,9 @@ static int _git_uploadpack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
+ git_net_url urldata = GIT_NET_URL_INIT;
const char *stream_url = url;
+ const char *host, *port;
git_proto_stream *s;
int error;
@@ -202,17 +203,15 @@ static int _git_uploadpack_ls(
if (!git__prefixcmp(url, prefix_git))
stream_url += strlen(prefix_git);
- if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0)
+ if ((error = git_net_url_parse(&urldata, url)) < 0)
return error;
- error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream);
+ host = urldata.host;
+ port = urldata.port ? urldata.port : GIT_DEFAULT_PORT;
- git__free(host);
- git__free(port);
- git__free(path);
- git__free(user);
- git__free(pass);
+ error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream);
+ git_net_url_dispose(&urldata);
if (error < 0) {
git_proto_stream_free(*stream);
@@ -251,7 +250,7 @@ static int _git_receivepack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
+ git_net_url urldata = GIT_NET_URL_INIT;
const char *stream_url = url;
git_proto_stream *s;
int error;
@@ -260,16 +259,12 @@ static int _git_receivepack_ls(
if (!git__prefixcmp(url, prefix_git))
stream_url += strlen(prefix_git);
- if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0)
+ if ((error = git_net_url_parse(&urldata, url)) < 0)
return error;
- error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, host, port, stream);
+ error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, urldata.host, urldata.port, stream);
- git__free(host);
- git__free(port);
- git__free(path);
- git__free(user);
- git__free(pass);
+ git_net_url_dispose(&urldata);
if (error < 0) {
git_proto_stream_free(*stream);
diff --git a/src/transports/http.c b/src/transports/http.c
index 9d77e627f..a93d36b9e 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -12,6 +12,7 @@
#include "git2.h"
#include "http_parser.h"
#include "buffer.h"
+#include "net.h"
#include "netops.h"
#include "global.h"
#include "remote.h"
@@ -71,7 +72,7 @@ typedef struct {
} http_stream;
typedef struct {
- gitno_connection_data url;
+ git_net_url url;
git_stream *stream;
git_cred *cred;
@@ -191,10 +192,10 @@ static int apply_credentials(
git_http_auth_context *context;
/* Apply the credentials given to us in the URL */
- if (!cred && server->url.user && server->url.pass) {
+ if (!cred && server->url.username && server->url.password) {
if (!server->url_cred &&
git_cred_userpass_plaintext_new(&server->url_cred,
- server->url.user, server->url.pass) < 0)
+ server->url.username, server->url.password) < 0)
return -1;
cred = server->url_cred;
@@ -226,7 +227,7 @@ static int gen_request(
if (t->proxy_opts.type == GIT_PROXY_SPECIFIED)
git_buf_printf(buf, "%s %s://%s:%s%s%s HTTP/1.1\r\n",
s->verb,
- t->server.url.use_ssl ? "https" : "http",
+ t->server.url.scheme,
t->server.url.host,
t->server.url.port,
path, s->service_url);
@@ -238,9 +239,10 @@ static int gen_request(
git_http__user_agent(buf);
git_buf_puts(buf, "\r\n");
git_buf_printf(buf, "Host: %s", t->server.url.host);
- if (strcmp(t->server.url.port, gitno__default_port(&t->server.url)) != 0) {
+
+ if (!git_net_url_is_default_port(&t->server.url))
git_buf_printf(buf, ":%s", t->server.url.port);
- }
+
git_buf_puts(buf, "\r\n");
if (s->chunked || content_length > 0) {
@@ -484,7 +486,7 @@ static int on_headers_complete(http_parser *parser)
SERVER_TYPE_PROXY,
t->proxy_opts.credentials,
t->proxy_opts.payload,
- t->proxy.url.user,
+ t->proxy.url.username,
proxy_auth_types);
/* Check for an authentication failure. */
@@ -495,7 +497,7 @@ static int on_headers_complete(http_parser *parser)
SERVER_TYPE_REMOTE,
t->owner->cred_acquire_cb,
t->owner->cred_acquire_payload,
- t->server.url.user,
+ t->server.url.username,
server_auth_types);
/* Check for a redirect.
@@ -507,7 +509,7 @@ static int on_headers_complete(http_parser *parser)
parser->status_code == 308) &&
t->location) {
- if (gitno_connection_data_from_url(&t->server.url, t->location, s->service_url) < 0)
+ if (gitno_connection_data_handle_redirect(&t->server.url, t->location, s->service_url) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
/* Set the redirect URL on the stream. This is a transfer of
@@ -676,7 +678,7 @@ static int load_proxy_config(http_subtransport *t)
git_proxy_init_options(&t->proxy_opts, GIT_PROXY_OPTIONS_VERSION);
if ((error = git_remote__get_http_proxy(t->owner->owner,
- !!t->server.url.use_ssl, &t->proxy_url)) < 0)
+ !strcmp(t->server.url.scheme, "https"), &t->proxy_url)) < 0)
return error;
if (!t->proxy_url)
@@ -698,10 +700,11 @@ static int load_proxy_config(http_subtransport *t)
return -1;
}
- if ((error = gitno_connection_data_from_url(&t->proxy.url, t->proxy_opts.url, NULL)) < 0)
+ git_net_url_dispose(&t->proxy.url);
+ if ((error = git_net_url_parse(&t->proxy.url, t->proxy_opts.url)) < 0)
return error;
- if (t->proxy.url.use_ssl) {
+ if (!strcmp(t->proxy.url.scheme, "https")) {
git_error_set(GIT_ERROR_NET, "SSL connections to proxy are not supported");
return -1;
}
@@ -711,7 +714,7 @@ static int load_proxy_config(http_subtransport *t)
static int check_certificate(
git_stream *stream,
- gitno_connection_data *url,
+ git_net_url *url,
int is_valid,
git_transport_certificate_check_cb cert_cb,
void *cert_cb_payload)
@@ -740,7 +743,7 @@ static int check_certificate(
static int stream_connect(
git_stream *stream,
- gitno_connection_data *url,
+ git_net_url *url,
git_transport_certificate_check_cb cert_cb,
void *cb_payload)
{
@@ -812,7 +815,7 @@ static int proxy_headers_complete(http_parser *parser)
SERVER_TYPE_PROXY,
t->proxy_opts.credentials,
t->proxy_opts.payload,
- t->proxy.url.user,
+ t->proxy.url.username,
proxy_auth_types);
if (parser->status_code != 200) {
@@ -927,7 +930,7 @@ done:
static int http_connect(http_subtransport *t)
{
- gitno_connection_data *url;
+ git_net_url *url;
git_stream *proxy_stream = NULL, *stream = NULL;
git_transport_certificate_check_cb cert_cb;
void *cb_payload;
@@ -965,7 +968,7 @@ static int http_connect(http_subtransport *t)
cb_payload = t->owner->message_cb_payload;
}
- if (url->use_ssl)
+ if (strcmp(url->scheme, "https") == 0)
error = git_tls_stream_new(&stream, url->host, url->port);
else
error = git_socket_stream_new(&stream, url->host, url->port);
@@ -982,7 +985,7 @@ static int http_connect(http_subtransport *t)
* an HTTPS connection, then we need to build a CONNECT tunnel.
*/
if (t->proxy_opts.type == GIT_PROXY_SPECIFIED &&
- t->server.url.use_ssl) {
+ strcmp(t->server.url.scheme, "https") == 0) {
proxy_stream = stream;
stream = NULL;
@@ -1378,7 +1381,7 @@ static int http_action(
* that would be insecure in plaintext (eg, HTTP Basic).
*/
if ((!t->server.url.host || !t->server.url.port || !t->server.url.path) &&
- (ret = gitno_connection_data_from_url(&t->server.url, url, NULL)) < 0)
+ (ret = git_net_url_parse(&t->server.url, url)) < 0)
return ret;
assert(t->server.url.host && t->server.url.port && t->server.url.path);
@@ -1445,11 +1448,8 @@ static int http_close(git_smart_subtransport *subtransport)
free_auth_contexts(&t->server.auth_contexts);
free_auth_contexts(&t->proxy.auth_contexts);
- gitno_connection_data_free_ptrs(&t->server.url);
- memset(&t->server.url, 0x0, sizeof(gitno_connection_data));
-
- gitno_connection_data_free_ptrs(&t->proxy.url);
- memset(&t->proxy.url, 0x0, sizeof(gitno_connection_data));
+ git_net_url_dispose(&t->server.url);
+ git_net_url_dispose(&t->proxy.url);
git__free(t->proxy_url);
t->proxy_url = NULL;
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 9b5d8a528..caa3a17f5 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -14,6 +14,7 @@
#include "global.h"
#include "git2.h"
#include "buffer.h"
+#include "net.h"
#include "netops.h"
#include "smart.h"
#include "cred.h"
@@ -258,8 +259,7 @@ static int ssh_stream_alloc(
}
static int git_ssh_extract_url_parts(
- char **host,
- char **username,
+ git_net_url *urldata,
const char *url)
{
char *colon, *at;
@@ -271,11 +271,11 @@ static int git_ssh_extract_url_parts(
at = strchr(url, '@');
if (at) {
start = at + 1;
- *username = git__substrdup(url, at - url);
- GIT_ERROR_CHECK_ALLOC(*username);
+ urldata->username = git__substrdup(url, at - url);
+ GIT_ERROR_CHECK_ALLOC(urldata->username);
} else {
start = url;
- *username = NULL;
+ urldata->username = NULL;
}
if (colon == NULL || (colon < start)) {
@@ -283,8 +283,8 @@ static int git_ssh_extract_url_parts(
return -1;
}
- *host = git__substrdup(start, colon - start);
- GIT_ERROR_CHECK_ALLOC(*host);
+ urldata->host = git__substrdup(start, colon - start);
+ GIT_ERROR_CHECK_ALLOC(urldata->host);
return 0;
}
@@ -506,14 +506,15 @@ static int _git_ssh_session_create(
return 0;
}
+#define SSH_DEFAULT_PORT "22"
+
static int _git_ssh_setup_conn(
ssh_subtransport *t,
const char *url,
const char *cmd,
git_smart_subtransport_stream **stream)
{
- char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
- const char *default_port="22";
+ git_net_url urldata = GIT_NET_URL_INIT;
int auth_methods, error = 0;
size_t i;
ssh_stream *s;
@@ -535,19 +536,22 @@ static int _git_ssh_setup_conn(
const char *p = ssh_prefixes[i];
if (!git__prefixcmp(url, p)) {
- if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0)
+ if ((error = git_net_url_parse(&urldata, url)) < 0)
goto done;
goto post_extract;
}
}
- if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0)
+ if ((error = git_ssh_extract_url_parts(&urldata, url)) < 0)
goto done;
- port = git__strdup(default_port);
- GIT_ERROR_CHECK_ALLOC(port);
+
+ if (urldata.port == NULL)
+ urldata.port = git__strdup(SSH_DEFAULT_PORT);
+
+ GIT_ERROR_CHECK_ALLOC(urldata.port);
post_extract:
- if ((error = git_socket_stream_new(&s->io, host, port)) < 0 ||
+ if ((error = git_socket_stream_new(&s->io, urldata.host, urldata.port)) < 0 ||
(error = git_stream_connect(s->io)) < 0)
goto done;
@@ -583,7 +587,7 @@ post_extract:
cert_ptr = &cert;
- error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, host, t->owner->message_cb_payload);
+ error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, urldata.host, t->owner->message_cb_payload);
if (error < 0 && error != GIT_PASSTHROUGH) {
if (!git_error_last())
@@ -594,21 +598,21 @@ post_extract:
}
/* we need the username to ask for auth methods */
- if (!user) {
+ if (!urldata.username) {
if ((error = request_creds(&cred, t, NULL, GIT_CREDTYPE_USERNAME)) < 0)
goto done;
- user = git__strdup(((git_cred_username *) cred)->username);
+ urldata.username = git__strdup(((git_cred_username *) cred)->username);
cred->free(cred);
cred = NULL;
- if (!user)
+ if (!urldata.username)
goto done;
- } else if (user && pass) {
- if ((error = git_cred_userpass_plaintext_new(&cred, user, pass)) < 0)
+ } else if (urldata.username && urldata.password) {
+ if ((error = git_cred_userpass_plaintext_new(&cred, urldata.username, urldata.password)) < 0)
goto done;
}
- if ((error = list_auth_methods(&auth_methods, session, user)) < 0)
+ if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0)
goto done;
error = GIT_EAUTH;
@@ -622,10 +626,10 @@ post_extract:
cred = NULL;
}
- if ((error = request_creds(&cred, t, user, auth_methods)) < 0)
+ if ((error = request_creds(&cred, t, urldata.username, auth_methods)) < 0)
goto done;
- if (strcmp(user, git_cred__username(cred))) {
+ if (strcmp(urldata.username, git_cred__username(cred))) {
git_error_set(GIT_ERROR_SSH, "username does not match previous request");
error = -1;
goto done;
@@ -662,11 +666,7 @@ done:
if (cred)
cred->free(cred);
- git__free(host);
- git__free(port);
- git__free(path);
- git__free(user);
- git__free(pass);
+ git_net_url_dispose(&urldata);
return error;
}
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index f922cb480..395a1a10d 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -103,8 +103,8 @@ typedef struct {
typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
- gitno_connection_data connection_data;
- gitno_connection_data proxy_connection_data;
+ git_net_url url;
+ git_net_url proxy_url;
git_cred *cred;
git_cred *url_cred;
git_cred *proxy_cred;
@@ -280,7 +280,7 @@ static int certificate_check(winhttp_stream *s, int valid)
return GIT_ECERTIFICATE;
}
- if (t->owner->certificate_check_cb == NULL || !t->connection_data.use_ssl)
+ if (t->owner->certificate_check_cb == NULL || git__strcmp(t->url.scheme, "https") != 0)
return 0;
if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) {
@@ -292,7 +292,7 @@ static int certificate_check(winhttp_stream *s, int valid)
cert.parent.cert_type = GIT_CERT_X509;
cert.data = cert_ctx->pbCertEncoded;
cert.len = cert_ctx->cbCertEncoded;
- error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->message_cb_payload);
+ error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->url.host, t->owner->message_cb_payload);
CertFreeCertificateContext(cert_ctx);
if (error == GIT_PASSTHROUGH)
@@ -329,9 +329,6 @@ static void winhttp_stream_close(winhttp_stream *s)
s->sent_request = 0;
}
-#define SCHEME_HTTP "http://"
-#define SCHEME_HTTPS "https://"
-
static int winhttp_stream_connect(winhttp_stream *s)
{
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
@@ -348,7 +345,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
const git_proxy_options *proxy_opts;
/* Prepare URL */
- git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
+ git_buf_printf(&buf, "%s%s", t->url.path, s->service_url);
if (git_buf_oom(&buf))
return -1;
@@ -367,7 +364,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
NULL,
WINHTTP_NO_REFERER,
types,
- t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0);
+ git__strcmp(t->url.scheme, "https") == 0 ? WINHTTP_FLAG_SECURE : 0);
if (!s->request) {
git_error_set(GIT_ERROR_OS, "failed to open request");
@@ -382,7 +379,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
proxy_opts = &t->owner->proxy;
if (proxy_opts->type == GIT_PROXY_AUTO) {
/* Set proxy if necessary */
- if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
+ if (git_remote__get_http_proxy(t->owner->owner, (strcmp(t->url.scheme, "https") == 0), &proxy_url) < 0)
goto on_error;
}
else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {
@@ -395,38 +392,33 @@ static int winhttp_stream_connect(winhttp_stream *s)
WINHTTP_PROXY_INFO proxy_info;
wchar_t *proxy_wide;
- if (!git__prefixcmp(proxy_url, SCHEME_HTTP)) {
- t->proxy_connection_data.use_ssl = false;
- } else if (!git__prefixcmp(proxy_url, SCHEME_HTTPS)) {
- t->proxy_connection_data.use_ssl = true;
- } else {
- git_error_set(GIT_ERROR_NET, "invalid URL: '%s'", proxy_url);
- return -1;
- }
+ git_net_url_dispose(&t->proxy_url);
- gitno_connection_data_free_ptrs(&t->proxy_connection_data);
+ if ((error = git_net_url_parse(&t->proxy_url, proxy_url)) < 0)
+ goto on_error;
- if ((error = gitno_extract_url_parts(&t->proxy_connection_data.host, &t->proxy_connection_data.port, NULL,
- &t->proxy_connection_data.user, &t->proxy_connection_data.pass, proxy_url, NULL)) < 0)
+ if (strcmp(t->proxy_url.scheme, "http") != 0 && strcmp(t->proxy_url.scheme, "https") != 0) {
+ git_error_set(GIT_ERROR_NET, "invalid URL: '%s'", proxy_url);
+ error = -1;
goto on_error;
+ }
- if (t->proxy_connection_data.user && t->proxy_connection_data.pass) {
+ if (t->proxy_url.username && t->proxy_url.password) {
if (t->proxy_cred) {
t->proxy_cred->free(t->proxy_cred);
}
- if ((error = git_cred_userpass_plaintext_new(&t->proxy_cred, t->proxy_connection_data.user, t->proxy_connection_data.pass)) < 0)
+ if ((error = git_cred_userpass_plaintext_new(&t->proxy_cred, t->proxy_url.username, t->proxy_url.password)) < 0)
goto on_error;
}
- if (t->proxy_connection_data.use_ssl)
- git_buf_PUTS(&processed_url, SCHEME_HTTPS);
- else
- git_buf_PUTS(&processed_url, SCHEME_HTTP);
+ git_buf_puts(&processed_url, t->proxy_url.scheme);
+ git_buf_PUTS(&processed_url, "://");
+
+ git_buf_puts(&processed_url, t->proxy_url.host);
- git_buf_puts(&processed_url, t->proxy_connection_data.host);
- if (t->proxy_connection_data.port)
- git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port);
+ if (!git_net_url_is_default_port(&t->proxy_url))
+ git_buf_printf(&processed_url, ":%s", t->proxy_url.port);
if (git_buf_oom(&processed_url)) {
error = -1;
@@ -543,7 +535,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
/* If requested, disable certificate validation */
- if (t->connection_data.use_ssl) {
+ if (strcmp(t->url.scheme, "https") == 0) {
int flags;
if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
@@ -562,9 +554,9 @@ static int winhttp_stream_connect(winhttp_stream *s)
/* If no other credentials have been applied and the URL has username and
* password, use those */
- if (!t->cred && t->connection_data.user && t->connection_data.pass) {
+ if (!t->cred && t->url.username && t->url.password) {
if (!t->url_cred &&
- git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0)
+ git_cred_userpass_plaintext_new(&t->url_cred, t->url.username, t->url.password) < 0)
goto on_error;
if (apply_userpass_credential(s->request, GIT_WINHTTP_AUTH_BASIC, t->url_cred) < 0)
goto on_error;
@@ -741,12 +733,12 @@ static int winhttp_connect(
t->connection = NULL;
/* Prepare port */
- if (git__strntol32(&port, t->connection_data.port,
- strlen(t->connection_data.port), NULL, 10) < 0)
+ if (git__strntol32(&port, t->url.port,
+ strlen(t->url.port), NULL, 10) < 0)
return -1;
/* Prepare host */
- if (git__utf8_to_16_alloc(&wide_host, t->connection_data.host) < 0) {
+ if (git__utf8_to_16_alloc(&wide_host, t->url.host) < 0) {
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
return -1;
}
@@ -1062,7 +1054,7 @@ replay:
if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */
- if (gitno_connection_data_from_url(&t->connection_data, location8, s->service_url) < 0) {
+ if (gitno_connection_data_handle_redirect(&t->url, location8, s->service_url) < 0) {
git__free(location8);
return -1;
}
@@ -1112,7 +1104,7 @@ replay:
/* Start with the user-supplied credential callback, if present */
if (t->owner->cred_acquire_cb) {
cred_error = t->owner->cred_acquire_cb(&t->cred, t->owner->url,
- t->connection_data.user, allowed_types, t->owner->cred_acquire_payload);
+ t->url.username, allowed_types, t->owner->cred_acquire_payload);
/* Treat GIT_PASSTHROUGH as though git_cred_acquire_cb isn't set */
if (cred_error == GIT_PASSTHROUGH)
@@ -1124,7 +1116,7 @@ replay:
/* Invoke the fallback credentials acquisition callback if necessary */
if (cred_error > 0) {
cred_error = fallback_cred_acquire_cb(&t->cred, t->owner->url,
- t->connection_data.user, allowed_types, NULL);
+ t->url.username, allowed_types, NULL);
if (cred_error < 0)
return cred_error;
@@ -1496,7 +1488,7 @@ static int winhttp_action(
int ret = -1;
if (!t->connection)
- if ((ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0 ||
+ if ((ret = git_net_url_parse(&t->url, url)) < 0 ||
(ret = winhttp_connect(t)) < 0)
return ret;
@@ -1538,10 +1530,8 @@ static int winhttp_close(git_smart_subtransport *subtransport)
{
winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
- gitno_connection_data_free_ptrs(&t->connection_data);
- memset(&t->connection_data, 0x0, sizeof(gitno_connection_data));
- gitno_connection_data_free_ptrs(&t->proxy_connection_data);
- memset(&t->proxy_connection_data, 0x0, sizeof(gitno_connection_data));
+ git_net_url_dispose(&t->url);
+ git_net_url_dispose(&t->proxy_url);
if (t->cred) {
t->cred->free(t->cred);
diff --git a/tests/network/redirect.c b/tests/network/redirect.c
new file mode 100644
index 000000000..3fc0b1826
--- /dev/null
+++ b/tests/network/redirect.c
@@ -0,0 +1,113 @@
+#include "clar_libgit2.h"
+#include "net.h"
+#include "netops.h"
+
+static git_net_url conndata;
+
+void test_network_redirect__initialize(void)
+{
+ memset(&conndata, 0, sizeof(conndata));
+}
+
+void test_network_redirect__cleanup(void)
+{
+ git_net_url_dispose(&conndata);
+}
+
+void test_network_redirect__redirect_http(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "http://example.com/foo/bar/baz"));
+ cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
+ "http://example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_network_redirect__redirect_ssl(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://example.com/foo/bar/baz"));
+ cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
+ "https://example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_network_redirect__redirect_leaves_root_path(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://example.com/foo/bar/baz"));
+ cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
+ "https://example.com/foo/bar/baz", "/foo/bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_network_redirect__redirect_encoded_username_password(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz"));
+ cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
+ "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_s(conndata.username, "user/name");
+ cl_assert_equal_s(conndata.password, "pass@word%zyx%v");
+}
+
+void test_network_redirect__redirect_cross_host_denied(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "https://bar.com/bar/baz"));
+ cl_git_fail_with(gitno_connection_data_handle_redirect(&conndata,
+ "https://foo.com/bar/baz", NULL),
+ -1);
+}
+
+void test_network_redirect__redirect_http_downgrade_denied(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz"));
+ cl_git_fail_with(gitno_connection_data_handle_redirect(&conndata,
+ "http://foo.com/bar/baz", NULL),
+ -1);
+}
+
+void test_network_redirect__redirect_relative(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://foo.com/bar/baz/biff"));
+ cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
+ "/zap/baz/biff?bam", NULL));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "foo.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_network_redirect__redirect_relative_ssl(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz/biff"));
+ cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
+ "/zap/baz/biff?bam", NULL));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "foo.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
diff --git a/tests/network/urlparse.c b/tests/network/urlparse.c
index 4a3096baa..c2362e628 100644
--- a/tests/network/urlparse.c
+++ b/tests/network/urlparse.c
@@ -1,220 +1,144 @@
#include "clar_libgit2.h"
-#include "netops.h"
+#include "net.h"
-static char *host, *port, *path, *user, *pass;
-static gitno_connection_data conndata;
+static git_net_url conndata;
void test_network_urlparse__initialize(void)
{
- host = port = path = user = pass = NULL;
memset(&conndata, 0, sizeof(conndata));
}
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);
-
- gitno_connection_data_free_ptrs(&conndata);
+ git_net_url_dispose(&conndata);
}
void test_network_urlparse__trivial(void)
{
- 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);
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
}
void test_network_urlparse__root(void)
{
- cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
- "http://example.com/", "8080"));
- cl_assert_equal_s(host, "example.com");
- cl_assert_equal_s(port, "8080");
- cl_assert_equal_s(path, "/");
- cl_assert_equal_p(user, NULL);
- cl_assert_equal_p(pass, NULL);
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
}
-void test_network_urlparse__just_hostname(void)
+void test_network_urlparse__implied_root(void)
{
- cl_git_fail_with(GIT_EINVALIDSPEC,
- gitno_extract_url_parts(&host, &port, &path, &user, &pass,
- "http://example.com", "8080"));
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_urlparse__implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
}
void test_network_urlparse__encoded_password(void)
{
- 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/is@bad");
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@hostname.com:1234/"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "hostname.com");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
}
void test_network_urlparse__user(void)
{
- 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);
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
}
void test_network_urlparse__user_pass(void)
{
/* user:pass@hostname.tld/resource */
- 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");
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
}
void test_network_urlparse__port(void)
{
/* hostname.tld:port/resource */
- 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);
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
}
void test_network_urlparse__user_port(void)
{
/* user@hostname.tld:port/resource */
- 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);
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
}
void test_network_urlparse__user_pass_port(void)
{
/* user:pass@hostname.tld:port/resource */
- 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");
-}
-
-void test_network_urlparse__optional_path(void)
-{
- cl_git_fail(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
- "https://user:pass@example.com:9191", "8080"));
-
- cl_git_pass(gitno_extract_url_parts(&host, &port, NULL, &user, &pass,
- "https://user:pass@example.com:9191", "8080"));
-}
-
-void test_network_urlparse__connection_data_http(void)
-{
- cl_git_pass(gitno_connection_data_from_url(&conndata,
- "http://example.com/foo/bar/baz", "bar/baz"));
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "80");
- cl_assert_equal_s(conndata.path, "/foo/");
- cl_assert_equal_p(conndata.user, NULL);
- cl_assert_equal_p(conndata.pass, NULL);
- cl_assert_equal_i(conndata.use_ssl, false);
-}
-
-void test_network_urlparse__connection_data_ssl(void)
-{
- cl_git_pass(gitno_connection_data_from_url(&conndata,
- "https://example.com/foo/bar/baz", "bar/baz"));
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "443");
- cl_assert_equal_s(conndata.path, "/foo/");
- cl_assert_equal_p(conndata.user, NULL);
- cl_assert_equal_p(conndata.pass, NULL);
- cl_assert_equal_i(conndata.use_ssl, true);
-}
-
-void test_network_urlparse__encoded_username_password(void)
-{
- cl_git_pass(gitno_connection_data_from_url(&conndata,
- "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "443");
- cl_assert_equal_s(conndata.path, "/foo/");
- cl_assert_equal_s(conndata.user, "user/name");
- cl_assert_equal_s(conndata.pass, "pass@word%zyx%v");
- cl_assert_equal_i(conndata.use_ssl, true);
-}
-
-void test_network_urlparse__connection_data_cross_host_redirect(void)
-{
- conndata.host = git__strdup("bar.com");
- cl_git_fail_with(gitno_connection_data_from_url(&conndata,
- "https://foo.com/bar/baz", NULL),
- -1);
-}
-
-void test_network_urlparse__connection_data_http_downgrade(void)
-{
- conndata.use_ssl = true;
- cl_git_fail_with(gitno_connection_data_from_url(&conndata,
- "http://foo.com/bar/baz", NULL),
- -1);
-}
-
-void test_network_urlparse__connection_data_relative_redirect(void)
-{
- cl_git_pass(gitno_connection_data_from_url(&conndata,
- "http://foo.com/bar/baz/biff", NULL));
- cl_git_pass(gitno_connection_data_from_url(&conndata,
- "/zap/baz/biff?bam", NULL));
- cl_assert_equal_s(conndata.host, "foo.com");
- cl_assert_equal_s(conndata.port, "80");
- cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
- cl_assert_equal_p(conndata.user, NULL);
- cl_assert_equal_p(conndata.pass, NULL);
- cl_assert_equal_i(conndata.use_ssl, false);
-}
-
-void test_network_urlparse__connection_data_relative_redirect_ssl(void)
-{
- cl_git_pass(gitno_connection_data_from_url(&conndata,
- "https://foo.com/bar/baz/biff", NULL));
- cl_git_pass(gitno_connection_data_from_url(&conndata,
- "/zap/baz/biff?bam", NULL));
- cl_assert_equal_s(conndata.host, "foo.com");
- cl_assert_equal_s(conndata.port, "443");
- cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
- cl_assert_equal_p(conndata.user, NULL);
- cl_assert_equal_p(conndata.pass, NULL);
- cl_assert_equal_i(conndata.use_ssl, true);
-}
-
-/* Run this under valgrind */
-void test_network_urlparse__connection_data_cleanup(void)
-{
- cl_git_pass(gitno_connection_data_from_url(&conndata,
- "http://foo.com/bar/baz/biff", "baz/biff"));
- cl_git_pass(gitno_connection_data_from_url(&conndata,
- "https://foo.com/bar/baz/biff", "baz/biff"));
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
}