dovecot-2.2: lib-http: Added support for creating CONNECT tunnel...
dovecot at dovecot.org
dovecot at dovecot.org
Sat Oct 12 11:14:58 EEST 2013
details: http://hg.dovecot.org/dovecot-2.2/rev/c3884a6acde6
changeset: 16855:c3884a6acde6
user: Stephan Bosch <stephan at rename-it.nl>
date: Sat Oct 12 11:05:08 2013 +0300
description:
lib-http: Added support for creating CONNECT tunnels through HTTP.
diffstat:
src/lib-http/http-client-connection.c | 131 +++++++++++++++++++++++++++------
src/lib-http/http-client-host.c | 54 +++++++------
src/lib-http/http-client-peer.c | 27 +++++-
src/lib-http/http-client-private.h | 60 +++++++++++---
src/lib-http/http-client-request.c | 89 +++++++++++++++++++---
src/lib-http/http-client.h | 27 +++++++
src/lib-http/http-response-parser.c | 10 +-
src/lib-http/http-response-parser.h | 4 +-
src/lib-http/http-response.h | 6 +
9 files changed, 319 insertions(+), 89 deletions(-)
diffs (truncated from 918 to 300 lines):
diff -r 462ae2cb8094 -r c3884a6acde6 src/lib-http/http-client-connection.c
--- a/src/lib-http/http-client-connection.c Sat Oct 12 11:00:15 2013 +0300
+++ b/src/lib-http/http-client-connection.c Sat Oct 12 11:05:08 2013 +0300
@@ -57,7 +57,7 @@
bool http_client_connection_is_ready(struct http_client_connection *conn)
{
return (conn->connected && !conn->output_locked &&
- !conn->close_indicated &&
+ !conn->close_indicated && !conn->tunneling &&
http_client_connection_count_pending(conn) <
conn->client->set.max_pipelined_requests);
}
@@ -173,7 +173,8 @@
{
unsigned int timeout, count;
- if (array_count(&conn->request_wait_list) == 0 &&
+ if (array_is_created(&conn->request_wait_list) &&
+ array_count(&conn->request_wait_list) == 0 &&
conn->incoming_payload == NULL &&
conn->client->set.max_idle_time_msecs > 0) {
@@ -302,6 +303,9 @@
return -1;
}
+ if (req->connect_tunnel)
+ conn->tunneling = TRUE;
+
/* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21;
Section 6.1.2.1:
@@ -388,6 +392,7 @@
{
struct http_client_connection *conn = req->conn;
+ i_assert(conn != NULL);
i_assert(conn->pending_request == req);
i_assert(conn->incoming_payload != NULL);
i_assert(conn->conn.io == NULL);
@@ -479,7 +484,8 @@
}
if (conn->incoming_payload == NULL) {
- i_assert(conn->conn.io != NULL);
+ i_assert(conn->conn.io != NULL ||
+ conn->peer->addr.type == HTTP_CLIENT_PEER_ADDR_RAW);
return TRUE;
}
@@ -495,7 +501,7 @@
struct http_client_request *req = NULL;
int finished = 0, ret;
const char *error;
- bool no_payload = FALSE;
+ enum http_response_payload_type payload_type;
i_assert(conn->incoming_payload == NULL);
@@ -516,18 +522,16 @@
req_idx = array_idx(&conn->request_wait_list, 0);
req = req_idx[0];
- /* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21
- Section 3.3.2:
-
- A server MAY send a Content-Length header field in a response to a
- HEAD request [...]
- */
- no_payload = (strcmp(req->method, "HEAD") == 0);
+ /* determine whether to expect a response payload */
+ payload_type = http_client_request_get_payload_type(req);
+ } else {
+ req = NULL;
+ payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED;
}
// FIXME: handle somehow if server replies before request->input is at EOF
while ((ret=http_response_parse_next
- (conn->http_parser, no_payload, &response, &error)) > 0) {
+ (conn->http_parser, payload_type, &response, &error)) > 0) {
bool aborted;
if (req == NULL) {
@@ -621,13 +625,15 @@
if (array_count(&conn->request_wait_list) > 0) {
req_idx = array_idx(&conn->request_wait_list, 0);
req = req_idx[0];
- no_payload = (strcmp(req->method, "HEAD") == 0);
+
+ /* determine whether to expect a response payload */
+ payload_type = http_client_request_get_payload_type(req);
} else {
/* no more requests waiting for the connection */
if (conn->to_requests != NULL)
timeout_remove(&conn->to_requests);
req = NULL;
- no_payload = FALSE;
+ payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED;
}
}
@@ -702,6 +708,33 @@
return 1;
}
+void
+http_client_connection_start_tunnel(struct http_client_connection **_conn,
+ struct http_client_tunnel *tunnel)
+{
+ struct http_client_connection *conn = *_conn;
+
+ i_assert(conn->tunneling);
+
+ /* claim connection streams */
+ memset(tunnel, 0, sizeof(*tunnel));
+ tunnel->input = conn->conn.input;
+ tunnel->output = conn->conn.output;
+ tunnel->fd_in = conn->conn.fd_in;
+ tunnel->fd_out = conn->conn.fd_out;
+
+ /* detach from connection */
+ conn->conn.input = NULL;
+ conn->conn.output = NULL;
+ conn->conn.fd_in = -1;
+ conn->conn.fd_out = -1;
+ conn->closing = TRUE;
+ conn->connected = FALSE;
+ connection_disconnect(&conn->conn);
+
+ http_client_connection_unref(_conn);
+}
+
static void
http_client_connection_ready(struct http_client_connection *conn)
{
@@ -725,6 +758,34 @@
&conn->conn.input, &conn->conn.output);
}
+ /* direct tunneling connections handle connect requests just by providing a
+ raw connection */
+ if (conn->peer->addr.type == HTTP_CLIENT_PEER_ADDR_RAW) {
+ struct http_client_request *req;
+
+ req = http_client_peer_claim_request(conn->peer, FALSE);
+ if (req != NULL) {
+ struct http_response response;
+
+ http_client_request_ref(req);
+ req->conn = conn;
+ conn->tunneling = TRUE;
+
+ memset(&response, 0, sizeof(response));
+ response.status = 200;
+ response.reason = "OK";
+
+ (void)http_client_connection_return_response(conn, req, &response);
+ http_client_request_unref(&req);
+ return;
+ }
+
+ http_client_connection_debug(conn,
+ "No raw connect requests pending; closing useless connection");
+ http_client_connection_unref(&conn);
+ return;
+ }
+
/* start protocol I/O */
conn->http_parser = http_response_parser_init
(conn->conn.input, &conn->client->set.response_hdr_limits);
@@ -881,13 +942,27 @@
struct http_client_connection *conn;
static unsigned int id = 0;
const struct http_client_peer_addr *addr = &peer->addr;
+ const char *conn_type = "UNKNOWN";
+
+ switch (peer->addr.type) {
+ case HTTP_CLIENT_PEER_ADDR_HTTP:
+ conn_type = "HTTP";
+ break;
+ case HTTP_CLIENT_PEER_ADDR_HTTPS:
+ conn_type = "HTTPS";
+ break;
+ case HTTP_CLIENT_PEER_ADDR_RAW:
+ conn_type = "Raw";
+ break;
+ }
conn = i_new(struct http_client_connection, 1);
conn->refcount = 1;
conn->client = peer->client;
conn->id = id++;
conn->peer = peer;
- i_array_init(&conn->request_wait_list, 16);
+ if (peer->addr.type != HTTP_CLIENT_PEER_ADDR_RAW)
+ i_array_init(&conn->request_wait_list, 16);
connection_init_client_ip
(peer->client->conn_list, &conn->conn, &addr->ip, addr->port);
@@ -896,8 +971,9 @@
array_append(&peer->conns, &conn, 1);
http_client_connection_debug(conn,
- "Connection created (%d parallel connections exist)%s",
- array_count(&peer->conns), (conn->to_input == NULL ? "" : " [broken]"));
+ "%s connection created (%d parallel connections exist)%s",
+ conn_type, array_count(&peer->conns),
+ (conn->to_input == NULL ? "" : " [broken]"));
return conn;
}
@@ -910,6 +986,7 @@
{
struct http_client_connection *conn = *_conn;
struct http_client_connection *const *conn_idx;
+ ARRAY_TYPE(http_client_connection) *conn_arr;
struct http_client_peer *peer = conn->peer;
struct http_client_request **req;
@@ -931,17 +1008,19 @@
connection_disconnect(&conn->conn);
- /* abort all pending requests */
- array_foreach_modifiable(&conn->request_wait_list, req) {
- i_assert((*req)->submitted);
- http_client_request_error(*req, HTTP_CLIENT_REQUEST_ERROR_ABORTED,
- "Aborting");
+ if (array_is_created(&conn->request_wait_list)) {
+ /* abort all pending requests */
+ array_foreach_modifiable(&conn->request_wait_list, req) {
+ i_assert((*req)->submitted);
+ http_client_request_error(*req, HTTP_CLIENT_REQUEST_ERROR_ABORTED,
+ "Aborting");
+ }
+ array_free(&conn->request_wait_list);
}
if (conn->pending_request != NULL) {
http_client_request_error(conn->pending_request,
HTTP_CLIENT_REQUEST_ERROR_ABORTED, "Aborting");
}
- array_free(&conn->request_wait_list);
if (conn->http_parser != NULL)
http_response_parser_deinit(&conn->http_parser);
@@ -964,10 +1043,10 @@
timeout_remove(&conn->to_response);
/* remove this connection from the list */
- array_foreach(&conn->peer->conns, conn_idx) {
+ conn_arr = &conn->peer->conns;
+ array_foreach(conn_arr, conn_idx) {
if (*conn_idx == conn) {
- array_delete(&conn->peer->conns,
- array_foreach_idx(&conn->peer->conns, conn_idx), 1);
+ array_delete(conn_arr, array_foreach_idx(conn_arr, conn_idx), 1);
break;
}
}
diff -r 462ae2cb8094 -r c3884a6acde6 src/lib-http/http-client-host.c
--- a/src/lib-http/http-client-host.c Sat Oct 12 11:00:15 2013 +0300
+++ b/src/lib-http/http-client-host.c Sat Oct 12 11:05:08 2013 +0300
@@ -46,13 +46,13 @@
static struct http_client_host_port *
http_client_host_port_find(struct http_client_host *host,
- in_port_t port, const char *https_name)
+ const struct http_client_peer_addr *addr)
{
struct http_client_host_port *hport;
array_foreach_modifiable(&host->ports, hport) {
- if (hport->addr.port == port &&
- null_strcmp(hport->addr.https_name, https_name) == 0)
+ if (hport->addr.type == addr->type && hport->addr.port == addr->port &&
+ null_strcmp(hport->addr.https_name, addr->https_name) == 0)
return hport;
}
@@ -61,16 +61,17 @@
static struct http_client_host_port *
http_client_host_port_init(struct http_client_host *host,
- in_port_t port, const char *https_name)
+ const struct http_client_peer_addr *addr)
{
struct http_client_host_port *hport;
- hport = http_client_host_port_find(host, port, https_name);
+ hport = http_client_host_port_find(host, addr);
if (hport == NULL) {
hport = array_append_space(&host->ports);
hport->host = host;
- hport->addr.port = port;
- hport->addr.https_name = i_strdup(https_name);
+ hport->addr = *addr;
+ hport->https_name = i_strdup(addr->https_name);
+ hport->addr.https_name = hport->https_name;
hport->ips_connect_idx = 0;
More information about the dovecot-cvs
mailing list