http transport: use HTTP proxies when requested The HTTP transport should understand how to apply proxies when configured with `GIT_PROXY_SPECIFIED` and `GIT_PROXY_SPECIFIED`. When a proxy is configured, the HTTP transport will now connect to the proxy (instead of directly to the git server), and will request the properly-formed URL of the git server endpoint.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
diff --git a/src/transports/http.c b/src/transports/http.c
index 82b9388..7df139b 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -73,6 +73,11 @@ typedef struct {
gitno_connection_data gitserver_data;
bool connected;
+ /* Proxy */
+ git_proxy_options proxy;
+ char *proxy_url;
+ gitno_connection_data proxy_data;
+
/* Parser structures */
http_parser parser;
http_parser_settings settings;
@@ -203,7 +208,16 @@ static int gen_request(
const char *path = t->gitserver_data.path ? t->gitserver_data.path : "/";
size_t i;
- git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
+ if (t->proxy.type == GIT_PROXY_SPECIFIED)
+ git_buf_printf(buf, "%s %s://%s:%s%s%s HTTP/1.1\r\n",
+ s->verb,
+ t->gitserver_data.use_ssl ? "https" : "http",
+ t->gitserver_data.host,
+ t->gitserver_data.port,
+ path, s->service_url);
+ else
+ git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n",
+ s->verb, path, s->service_url);
git_buf_puts(buf, "User-Agent: ");
git_http__user_agent(buf);
@@ -560,42 +574,56 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len)
return 0;
}
-static int apply_proxy_config(http_subtransport *t)
+static int apply_proxy_config_to_stream(http_subtransport *t)
{
- int error;
- git_proxy_t proxy_type;
-
- if (!git_stream_supports_proxy(t->gitserver_stream))
+ /* Only set the proxy configuration on the curl stream. */
+ if (!git_stream_supports_proxy(t->gitserver_stream) ||
+ t->proxy.type == GIT_PROXY_NONE)
return 0;
- proxy_type = t->owner->proxy.type;
+ return git_stream_set_proxy(t->gitserver_stream, &t->proxy);
+}
- if (proxy_type == GIT_PROXY_NONE)
+static int load_proxy_config(http_subtransport *t)
+{
+ int error;
+
+ switch (t->owner->proxy.type) {
+ case GIT_PROXY_NONE:
return 0;
- if (proxy_type == GIT_PROXY_AUTO) {
- char *url;
- git_proxy_options opts = GIT_PROXY_OPTIONS_INIT;
+ case GIT_PROXY_AUTO:
+ git__free(t->proxy_url);
+ t->proxy_url = NULL;
+
+ git_proxy_init_options(&t->proxy, GIT_PROXY_OPTIONS_VERSION);
- if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->gitserver_data.use_ssl, &url)) < 0)
+ if ((error = git_remote__get_http_proxy(t->owner->owner,
+ !!t->gitserver_data.use_ssl, &t->proxy_url)) < 0)
return error;
- opts.credentials = t->owner->proxy.credentials;
- opts.certificate_check = t->owner->proxy.certificate_check;
- opts.payload = t->owner->proxy.payload;
- opts.type = GIT_PROXY_SPECIFIED;
- opts.url = url;
- error = git_stream_set_proxy(t->gitserver_stream, &opts);
- git__free(url);
+ t->proxy.type = GIT_PROXY_SPECIFIED;
+ t->proxy.url = t->proxy_url;
+ t->proxy.credentials = t->owner->proxy.credentials;
+ t->proxy.certificate_check = t->owner->proxy.certificate_check;
+ t->proxy.payload = t->owner->proxy.payload;
+ break;
- return error;
+ case GIT_PROXY_SPECIFIED:
+ memcpy(&t->proxy, &t->owner->proxy, sizeof(git_proxy_options));
+ break;
+
+ default:
+ assert(0);
+ return -1;
}
- return git_stream_set_proxy(t->gitserver_stream, &t->owner->proxy);
+ return gitno_connection_data_from_url(&t->proxy_data, t->proxy.url, NULL);
}
static int http_connect(http_subtransport *t)
{
+ gitno_connection_data *connection_data;
int error;
if (t->connected &&
@@ -610,25 +638,27 @@ static int http_connect(http_subtransport *t)
t->connected = 0;
}
- if (t->gitserver_data.use_ssl) {
- error = git_tls_stream_new(&t->gitserver_stream,
- t->gitserver_data.host, t->gitserver_data.port);
- } else {
+ connection_data = (t->proxy.type == GIT_PROXY_SPECIFIED) ?
+ &t->proxy_data : &t->gitserver_data;
+
#ifdef GIT_CURL
- error = git_curl_stream_new(&t->gitserver_stream,
- t->gitserver_data.host, t->gitserver_data.port);
+ error = git_curl_stream_new(&t->gitserver_stream,
+ t->gitserver_data.host, t->gitserver_data.port);
#else
+ if (connection_data->use_ssl)
+ error = git_tls_stream_new(&t->gitserver_stream,
+ connection_data->host, connection_data->port);
+ else
error = git_socket_stream_new(&t->gitserver_stream,
- t->gitserver_data.host, t->gitserver_data.port);
+ connection_data->host, connection_data->port);
#endif
- }
if (error < 0)
return error;
GITERR_CHECK_VERSION(t->gitserver_stream, GIT_STREAM_VERSION, "git_stream");
- if ((error = apply_proxy_config(t)) < 0)
+ if ((error = apply_proxy_config_to_stream(t)) < 0)
return error;
error = git_stream_connect(t->gitserver_stream);
@@ -1020,11 +1050,22 @@ static int http_action(
http_subtransport *t = (http_subtransport *)subtransport;
int ret;
- if (!stream)
- return -1;
+ assert(stream);
+ /*
+ * If we've seen a redirect then preserve the location that we've
+ * been given. This is important to continue authorization against
+ * the redirect target, not the user-given source; the endpoint may
+ * have redirected us from HTTP->HTTPS and is using an auth mechanism
+ * that would be insecure in plaintext (eg, HTTP Basic).
+ */
if ((!t->gitserver_data.host || !t->gitserver_data.port || !t->gitserver_data.path) &&
- (ret = gitno_connection_data_from_url(&t->gitserver_data, url, NULL)) < 0)
+ (ret = gitno_connection_data_from_url(&t->gitserver_data, url, NULL)) < 0)
+ return ret;
+
+ assert(t->gitserver_data.host && t->gitserver_data.port && t->gitserver_data.path);
+
+ if ((ret = load_proxy_config(t)) < 0)
return ret;
if ((ret = http_connect(t)) < 0)
@@ -1084,6 +1125,12 @@ static int http_close(git_smart_subtransport *subtransport)
gitno_connection_data_free_ptrs(&t->gitserver_data);
memset(&t->gitserver_data, 0x0, sizeof(gitno_connection_data));
+ gitno_connection_data_free_ptrs(&t->proxy_data);
+ memset(&t->proxy_data, 0x0, sizeof(gitno_connection_data));
+
+ git__free(t->proxy_url);
+ t->proxy_url = NULL;
+
return 0;
}