summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl/lib/http_proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/http_proxy.c')
-rw-r--r--Utilities/cmcurl/lib/http_proxy.c386
1 files changed, 312 insertions, 74 deletions
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c
index 4242251..a03a27f 100644
--- a/Utilities/cmcurl/lib/http_proxy.c
+++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,6 +27,9 @@
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
#include <curl/curl.h>
+#ifdef USE_HYPER
+#include <hyper.h>
+#endif
#include "sendf.h"
#include "http.h"
#include "url.h"
@@ -47,15 +50,16 @@
* proxy_ssl_connected connection bit when complete. Can be
* called multiple times.
*/
-static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex)
+static CURLcode https_proxy_connect(struct Curl_easy *data, int sockindex)
{
#ifdef USE_SSL
+ struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS);
if(!conn->bits.proxy_ssl_connected[sockindex]) {
/* perform SSL initialization for this socket */
result =
- Curl_ssl_connect_nonblocking(conn, sockindex,
+ Curl_ssl_connect_nonblocking(data, conn, sockindex,
&conn->bits.proxy_ssl_connected[sockindex]);
if(result)
/* a failed connection is marked for closure to prevent (bad) re-use or
@@ -64,17 +68,17 @@ static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex)
}
return result;
#else
- (void) conn;
+ (void) data;
(void) sockindex;
return CURLE_NOT_BUILT_IN;
#endif
}
-CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
+CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex)
{
- struct Curl_easy *data = conn->data;
+ struct connectdata *conn = data->conn;
if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
- const CURLcode result = https_proxy_connect(conn, sockindex);
+ const CURLcode result = https_proxy_connect(data, sockindex);
if(result)
return result;
if(!conn->bits.proxy_ssl_connected[sockindex])
@@ -102,9 +106,9 @@ CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
* This function might be called several times in the multi interface case
* if the proxy's CONNECT response is not instant.
*/
- prot_save = conn->data->req.p.http;
+ prot_save = data->req.p.http;
memset(&http_proxy, 0, sizeof(http_proxy));
- conn->data->req.p.http = &http_proxy;
+ data->req.p.http = &http_proxy;
connkeep(conn, "HTTP proxy CONNECT");
/* for the secondary socket (FTP), use the "connect to host"
@@ -124,8 +128,8 @@ CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
remote_port = conn->conn_to_port;
else
remote_port = conn->remote_port;
- result = Curl_proxyCONNECT(conn, sockindex, hostname, remote_port);
- conn->data->req.p.http = prot_save;
+ result = Curl_proxyCONNECT(data, sockindex, hostname, remote_port);
+ data->req.p.http = prot_save;
if(CURLE_OK != result)
return result;
Curl_safefree(data->state.aptr.proxyuserpwd);
@@ -149,15 +153,16 @@ bool Curl_connect_ongoing(struct connectdata *conn)
(conn->connect_state->tunnel_state != TUNNEL_COMPLETE);
}
-static CURLcode connect_init(struct connectdata *conn, bool reinit)
+static CURLcode connect_init(struct Curl_easy *data, bool reinit)
{
struct http_connect_state *s;
+ struct connectdata *conn = data->conn;
if(!reinit) {
DEBUGASSERT(!conn->connect_state);
s = calloc(1, sizeof(struct http_connect_state));
if(!s)
return CURLE_OUT_OF_MEMORY;
- infof(conn->data, "allocate connect buffer!\n");
+ infof(data, "allocate connect buffer!\n");
conn->connect_state = s;
Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
}
@@ -173,23 +178,57 @@ static CURLcode connect_init(struct connectdata *conn, bool reinit)
return CURLE_OK;
}
-static void connect_done(struct connectdata *conn)
+static void connect_done(struct Curl_easy *data)
{
+ struct connectdata *conn = data->conn;
struct http_connect_state *s = conn->connect_state;
s->tunnel_state = TUNNEL_COMPLETE;
Curl_dyn_free(&s->rcvbuf);
- infof(conn->data, "CONNECT phase completed!\n");
+ infof(data, "CONNECT phase completed!\n");
+}
+
+static CURLcode CONNECT_host(struct Curl_easy *data,
+ struct connectdata *conn,
+ const char *hostname,
+ int remote_port,
+ char **connecthostp,
+ char **hostp)
+{
+ char *hostheader; /* for CONNECT */
+ char *host = NULL; /* Host: */
+ bool ipv6_ip = conn->bits.ipv6_ip;
+
+ /* the hostname may be different */
+ if(hostname != conn->host.name)
+ ipv6_ip = (strchr(hostname, ':') != NULL);
+ hostheader = /* host:port with IPv6 support */
+ aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
+ remote_port);
+ if(!hostheader)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(!Curl_checkProxyheaders(data, conn, "Host")) {
+ host = aprintf("Host: %s\r\n", hostheader);
+ if(!host) {
+ free(hostheader);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ *connecthostp = hostheader;
+ *hostp = host;
+ return CURLE_OK;
}
-static CURLcode CONNECT(struct connectdata *conn,
+static CURLcode CONNECT(struct Curl_easy *data,
int sockindex,
const char *hostname,
int remote_port)
+#ifndef USE_HYPER
{
int subversion = 0;
- struct Curl_easy *data = conn->data;
struct SingleRequest *k = &data->req;
CURLcode result;
+ struct connectdata *conn = data->conn;
curl_socket_t tunnelsocket = conn->sock[sockindex];
struct http_connect_state *s = conn->connect_state;
char *linep;
@@ -207,8 +246,9 @@ static CURLcode CONNECT(struct connectdata *conn,
timediff_t check;
if(TUNNEL_INIT == s->tunnel_state) {
/* BEGIN CONNECT PHASE */
- char *host_port;
struct dynbuf req;
+ char *hostheader = NULL;
+ char *host = NULL;
infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
hostname, remote_port);
@@ -219,50 +259,28 @@ static CURLcode CONNECT(struct connectdata *conn,
free(data->req.newurl);
data->req.newurl = NULL;
- host_port = aprintf("%s:%d", hostname, remote_port);
- if(!host_port)
- return CURLE_OUT_OF_MEMORY;
-
/* initialize a dynamic send-buffer */
Curl_dyn_init(&req, DYN_HTTP_REQUEST);
- /* Setup the proxy-authorization header, if any */
- result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
+ result = CONNECT_host(data, conn,
+ hostname, remote_port, &hostheader, &host);
+ if(result)
+ return result;
- free(host_port);
+ /* Setup the proxy-authorization header, if any */
+ result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
+ hostheader, TRUE);
if(!result) {
- char *host = NULL;
const char *proxyconn = "";
const char *useragent = "";
const char *httpv =
(conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
- bool ipv6_ip = conn->bits.ipv6_ip;
- char *hostheader;
-
- /* the hostname may be different */
- if(hostname != conn->host.name)
- ipv6_ip = (strchr(hostname, ':') != NULL);
- hostheader = /* host:port with IPv6 support */
- aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
- remote_port);
- if(!hostheader) {
- Curl_dyn_free(&req);
- return CURLE_OUT_OF_MEMORY;
- }
- if(!Curl_checkProxyheaders(conn, "Host")) {
- host = aprintf("Host: %s\r\n", hostheader);
- if(!host) {
- free(hostheader);
- Curl_dyn_free(&req);
- return CURLE_OUT_OF_MEMORY;
- }
- }
- if(!Curl_checkProxyheaders(conn, "Proxy-Connection"))
+ if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection"))
proxyconn = "Proxy-Connection: Keep-Alive\r\n";
- if(!Curl_checkProxyheaders(conn, "User-Agent") &&
+ if(!Curl_checkProxyheaders(data, conn, "User-Agent") &&
data->set.str[STRING_USERAGENT])
useragent = data->state.aptr.uagent;
@@ -281,12 +299,8 @@ static CURLcode CONNECT(struct connectdata *conn,
useragent,
proxyconn);
- if(host)
- free(host);
- free(hostheader);
-
if(!result)
- result = Curl_add_custom_headers(conn, TRUE, &req);
+ result = Curl_add_custom_headers(data, TRUE, &req);
if(!result)
/* CRLF terminate the request */
@@ -295,13 +309,14 @@ static CURLcode CONNECT(struct connectdata *conn,
if(!result) {
/* Send the connect request to the proxy */
/* BLOCKING */
- result = Curl_buffer_send(&req, conn, &data->info.request_size, 0,
+ result = Curl_buffer_send(&req, data, &data->info.request_size, 0,
sockindex);
}
if(result)
failf(data, "Failed sending CONNECT to proxy");
}
-
+ free(host);
+ free(hostheader);
Curl_dyn_free(&req);
if(result)
return result;
@@ -330,12 +345,12 @@ static CURLcode CONNECT(struct connectdata *conn,
/* Read one byte at a time to avoid a race condition. Wait at most one
second before looping to ensure continuous pgrsUpdates. */
- result = Curl_read(conn, tunnelsocket, &byte, 1, &gotbytes);
+ result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes);
if(result == CURLE_AGAIN)
/* socket buffer drained, return */
return CURLE_OK;
- if(Curl_pgrsUpdate(conn))
+ if(Curl_pgrsUpdate(data))
return CURLE_ABORTED_BY_CALLBACK;
if(result) {
@@ -379,7 +394,7 @@ static CURLcode CONNECT(struct connectdata *conn,
/* now parse the chunked piece of data so that we can
properly tell when the stream ends */
- r = Curl_httpchunk_read(conn, &byte, 1, &tookcareof, &extra);
+ r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
if(r == CHUNKE_STOP) {
/* we're done reading chunks! */
infof(data, "chunk reading DONE\n");
@@ -418,7 +433,7 @@ static CURLcode CONNECT(struct connectdata *conn,
if(data->set.include_header)
writetype |= CLIENTWRITE_BODY;
- result = Curl_client_write(conn, writetype, linep, perline);
+ result = Curl_client_write(data, writetype, linep, perline);
if(result)
return result;
}
@@ -460,7 +475,7 @@ static CURLcode CONNECT(struct connectdata *conn,
/* now parse the chunked piece of data so that we can properly
tell when the stream ends */
- r = Curl_httpchunk_read(conn, linep + 1, 1, &gotbytes,
+ r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes,
&extra);
if(r == CHUNKE_STOP) {
/* we're done reading chunks! */
@@ -479,9 +494,12 @@ static CURLcode CONNECT(struct connectdata *conn,
}
else
s->keepon = KEEPON_DONE;
- if(!s->cl)
+
+ if(s->keepon == KEEPON_DONE && !s->cl)
/* we did the full CONNECT treatment, go to COMPLETE */
s->tunnel_state = TUNNEL_COMPLETE;
+
+ DEBUGASSERT(s->keepon == KEEPON_IGNORE || s->keepon == KEEPON_DONE);
continue;
}
@@ -495,7 +513,7 @@ static CURLcode CONNECT(struct connectdata *conn,
if(!auth)
return CURLE_OUT_OF_MEMORY;
- result = Curl_http_input_auth(conn, proxy, auth);
+ result = Curl_http_input_auth(data, proxy, auth);
free(auth);
@@ -530,7 +548,7 @@ static CURLcode CONNECT(struct connectdata *conn,
infof(data, "CONNECT responded chunked\n");
s->chunked_encoding = TRUE;
/* init our chunky engine */
- Curl_httpchunk_init(conn);
+ Curl_httpchunk_init(data);
}
}
else if(Curl_compareheader(linep, "Proxy-Connection:", "close"))
@@ -545,7 +563,7 @@ static CURLcode CONNECT(struct connectdata *conn,
Curl_dyn_reset(&s->rcvbuf);
} /* while there's buffer left and loop is requested */
- if(Curl_pgrsUpdate(conn))
+ if(Curl_pgrsUpdate(data))
return CURLE_ABORTED_BY_CALLBACK;
if(error)
@@ -554,7 +572,7 @@ static CURLcode CONNECT(struct connectdata *conn,
if(data->info.httpproxycode/100 != 2) {
/* Deal with the possibly already received authenticate
headers. 'newurl' is set to a new URL if we must loop. */
- result = Curl_http_auth_act(conn);
+ result = Curl_http_auth_act(data);
if(result)
return result;
@@ -567,7 +585,7 @@ static CURLcode CONNECT(struct connectdata *conn,
if(s->close_connection && data->req.newurl) {
/* Connection closed by server. Don't use it anymore */
- Curl_closesocket(conn, conn->sock[sockindex]);
+ Curl_closesocket(data, conn, conn->sock[sockindex]);
conn->sock[sockindex] = CURL_SOCKET_BAD;
break;
}
@@ -577,7 +595,7 @@ static CURLcode CONNECT(struct connectdata *conn,
* means the HTTP authentication is still going on so if the tunnel
* is complete we start over in INIT state */
if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) {
- connect_init(conn, TRUE); /* reinit */
+ connect_init(data, TRUE); /* reinit */
}
} while(data->req.newurl);
@@ -586,14 +604,14 @@ static CURLcode CONNECT(struct connectdata *conn,
if(s->close_connection && data->req.newurl) {
conn->bits.proxy_connect_closed = TRUE;
infof(data, "Connect me again please\n");
- connect_done(conn);
+ connect_done(data);
}
else {
free(data->req.newurl);
data->req.newurl = NULL;
/* failure, close this connection to avoid re-use */
streamclose(conn, "proxy CONNECT failure");
- Curl_closesocket(conn, conn->sock[sockindex]);
+ Curl_closesocket(data, conn, conn->sock[sockindex]);
conn->sock[sockindex] = CURL_SOCKET_BAD;
}
@@ -628,6 +646,225 @@ static CURLcode CONNECT(struct connectdata *conn,
Curl_dyn_free(&s->rcvbuf);
return CURLE_OK;
}
+#else
+/* The Hyper version of CONNECT */
+{
+ struct connectdata *conn = data->conn;
+ struct hyptransfer *h = &data->hyp;
+ curl_socket_t tunnelsocket = conn->sock[sockindex];
+ struct http_connect_state *s = conn->connect_state;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ hyper_io *io = NULL;
+ hyper_request *req = NULL;
+ hyper_headers *headers = NULL;
+ hyper_clientconn_options *options = NULL;
+ hyper_task *handshake = NULL;
+ hyper_task *task = NULL; /* for the handshake */
+ hyper_task *sendtask = NULL; /* for the send */
+ hyper_clientconn *client = NULL;
+ hyper_error *hypererr = NULL;
+ char *hostheader = NULL; /* for CONNECT */
+ char *host = NULL; /* Host: */
+
+ if(Curl_connect_complete(conn))
+ return CURLE_OK; /* CONNECT is already completed */
+
+ conn->bits.proxy_connect_closed = FALSE;
+
+ do {
+ switch(s->tunnel_state) {
+ case TUNNEL_INIT:
+ /* BEGIN CONNECT PHASE */
+ io = hyper_io_new();
+ if(!io) {
+ failf(data, "Couldn't create hyper IO");
+ goto error;
+ }
+ /* tell Hyper how to read/write network data */
+ hyper_io_set_userdata(io, data);
+ hyper_io_set_read(io, Curl_hyper_recv);
+ hyper_io_set_write(io, Curl_hyper_send);
+ conn->sockfd = tunnelsocket;
+
+ /* create an executor to poll futures */
+ if(!h->exec) {
+ h->exec = hyper_executor_new();
+ if(!h->exec) {
+ failf(data, "Couldn't create hyper executor");
+ goto error;
+ }
+ }
+
+ options = hyper_clientconn_options_new();
+ if(!options) {
+ failf(data, "Couldn't create hyper client options");
+ goto error;
+ }
+
+ hyper_clientconn_options_exec(options, h->exec);
+
+ /* "Both the `io` and the `options` are consumed in this function
+ call" */
+ handshake = hyper_clientconn_handshake(io, options);
+ if(!handshake) {
+ failf(data, "Couldn't create hyper client handshake");
+ goto error;
+ }
+ io = NULL;
+ options = NULL;
+
+ if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
+ failf(data, "Couldn't hyper_executor_push the handshake");
+ goto error;
+ }
+ handshake = NULL; /* ownership passed on */
+
+ task = hyper_executor_poll(h->exec);
+ if(!task) {
+ failf(data, "Couldn't hyper_executor_poll the handshake");
+ goto error;
+ }
+
+ client = hyper_task_value(task);
+ hyper_task_free(task);
+ req = hyper_request_new();
+ if(!req) {
+ failf(data, "Couldn't hyper_request_new");
+ goto error;
+ }
+ if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
+ strlen("CONNECT"))) {
+ failf(data, "error setting method");
+ goto error;
+ }
+
+ result = CONNECT_host(data, conn, hostname, remote_port,
+ &hostheader, &host);
+ if(result)
+ goto error;
+
+ if(hyper_request_set_uri(req, (uint8_t *)hostheader,
+ strlen(hostheader))) {
+ failf(data, "error setting path");
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ /* Setup the proxy-authorization header, if any */
+ result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
+ hostheader, TRUE);
+ if(result)
+ goto error;
+ Curl_safefree(hostheader);
+
+ /* default is 1.1 */
+ if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
+ (HYPERE_OK != hyper_request_set_version(req,
+ HYPER_HTTP_VERSION_1_0))) {
+ failf(data, "error settting HTTP version");
+ goto error;
+ }
+
+ headers = hyper_request_headers(req);
+ if(!headers) {
+ failf(data, "hyper_request_headers");
+ goto error;
+ }
+ if(host && Curl_hyper_header(data, headers, host))
+ goto error;
+ Curl_safefree(host);
+
+ if(data->state.aptr.proxyuserpwd &&
+ Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd))
+ goto error;
+
+ if(data->set.str[STRING_USERAGENT] &&
+ *data->set.str[STRING_USERAGENT] &&
+ data->state.aptr.uagent &&
+ Curl_hyper_header(data, headers, data->state.aptr.uagent))
+ goto error;
+
+ if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection") &&
+ Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"))
+ goto error;
+
+ sendtask = hyper_clientconn_send(client, req);
+ if(!sendtask) {
+ failf(data, "hyper_clientconn_send");
+ goto error;
+ }
+
+ if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
+ failf(data, "Couldn't hyper_executor_push the send");
+ goto error;
+ }
+
+ hyper_clientconn_free(client);
+
+ do {
+ task = hyper_executor_poll(h->exec);
+ if(task) {
+ bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
+ if(error)
+ hypererr = hyper_task_value(task);
+ hyper_task_free(task);
+ if(error)
+ goto error;
+ }
+ } while(task);
+ s->tunnel_state = TUNNEL_CONNECT;
+ /* FALLTHROUGH */
+ case TUNNEL_CONNECT: {
+ int didwhat;
+ bool done = FALSE;
+ result = Curl_hyper_stream(data, conn, &didwhat, &done,
+ CURL_CSELECT_IN | CURL_CSELECT_OUT);
+ if(result)
+ goto error;
+ if(!done)
+ break;
+ fprintf(stderr, "done\n");
+ s->tunnel_state = TUNNEL_COMPLETE;
+ if(h->exec) {
+ hyper_executor_free(h->exec);
+ h->exec = NULL;
+ }
+ if(h->read_waker) {
+ hyper_waker_free(h->read_waker);
+ h->read_waker = NULL;
+ }
+ if(h->write_waker) {
+ hyper_waker_free(h->write_waker);
+ h->write_waker = NULL;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ } while(data->req.newurl);
+
+ result = CURLE_OK;
+ error:
+ free(host);
+ free(hostheader);
+ if(io)
+ hyper_io_free(io);
+
+ if(options)
+ hyper_clientconn_options_free(options);
+
+ if(handshake)
+ hyper_task_free(handshake);
+
+ if(hypererr) {
+ uint8_t errbuf[256];
+ size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
+ failf(data, "Hyper: %.*s", (int)errlen, errbuf);
+ hyper_error_free(hypererr);
+ }
+ return result;
+}
+
+#endif
void Curl_connect_free(struct Curl_easy *data)
{
@@ -645,21 +882,22 @@ void Curl_connect_free(struct Curl_easy *data)
* this proxy. After that, the socket can be used just as a normal socket.
*/
-CURLcode Curl_proxyCONNECT(struct connectdata *conn,
+CURLcode Curl_proxyCONNECT(struct Curl_easy *data,
int sockindex,
const char *hostname,
int remote_port)
{
CURLcode result;
+ struct connectdata *conn = data->conn;
if(!conn->connect_state) {
- result = connect_init(conn, FALSE);
+ result = connect_init(data, FALSE);
if(result)
return result;
}
- result = CONNECT(conn, sockindex, hostname, remote_port);
+ result = CONNECT(data, sockindex, hostname, remote_port);
if(result || Curl_connect_complete(conn))
- connect_done(conn);
+ connect_done(data);
return result;
}