aboutsummaryrefslogtreecommitdiffstats
path: root/proxy/proxy_http_connector.c
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:32 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:32 -0800
commit8b23a6c7e1aee255004dd19098d4c2462b61b849 (patch)
tree7a4d682ba51f0ff0364c5ca2509f515bdaf96de9 /proxy/proxy_http_connector.c
parentf721e3ac031f892af46f255a47d7f54a91317b30 (diff)
downloadexternal_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.zip
external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.gz
external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'proxy/proxy_http_connector.c')
-rw-r--r--proxy/proxy_http_connector.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/proxy/proxy_http_connector.c b/proxy/proxy_http_connector.c
new file mode 100644
index 0000000..6f03d9b
--- /dev/null
+++ b/proxy/proxy_http_connector.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+#include "proxy_http_int.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "qemu-common.h"
+
+/* A HttpConnector implements a non-HTTP proxied connection
+ * through the CONNECT method. Many firewalls are configured
+ * to reject these for port 80, so these connections should
+ * use a HttpRewriter instead.
+ */
+
+typedef enum {
+ STATE_NONE = 0,
+ STATE_CONNECTING, /* connecting to the server */
+ STATE_SEND_HEADER, /* connected, sending header to the server */
+ STATE_RECEIVE_ANSWER_LINE1,
+ STATE_RECEIVE_ANSWER_LINE2 /* connected, reading server's answer */
+} ConnectorState;
+
+typedef struct Connection {
+ ProxyConnection root[1];
+ ConnectorState state;
+} Connection;
+
+
+static void
+connection_free( ProxyConnection* root )
+{
+ proxy_connection_done(root);
+ qemu_free(root);
+}
+
+
+
+#define HTTP_VERSION "1.1"
+
+static int
+connection_init( Connection* conn )
+{
+ HttpService* service = (HttpService*) conn->root->service;
+ ProxyConnection* root = conn->root;
+ stralloc_t* str = root->str;
+
+ proxy_connection_rewind(root);
+ stralloc_add_format(str, "CONNECT %s HTTP/" HTTP_VERSION "\r\n",
+ sock_address_to_string(&root->address));
+
+ stralloc_add_bytes(str, service->footer, service->footer_len);
+
+ if (!socket_connect( root->socket, &service->server_addr )) {
+ /* immediate connection ?? */
+ conn->state = STATE_SEND_HEADER;
+ PROXY_LOG("%s: immediate connection", root->name);
+ }
+ else {
+ if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
+ conn->state = STATE_CONNECTING;
+ PROXY_LOG("%s: connecting", root->name);
+ }
+ else {
+ PROXY_LOG("%s: cannot connect to proxy: %s", root->name, errno_str);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+static void
+connection_select( ProxyConnection* root,
+ ProxySelect* sel )
+{
+ unsigned flags;
+ Connection* conn = (Connection*)root;
+
+ switch (conn->state) {
+ case STATE_RECEIVE_ANSWER_LINE1:
+ case STATE_RECEIVE_ANSWER_LINE2:
+ flags = PROXY_SELECT_READ;
+ break;
+
+ case STATE_CONNECTING:
+ case STATE_SEND_HEADER:
+ flags = PROXY_SELECT_WRITE;
+ break;
+
+ default:
+ flags = 0;
+ };
+ proxy_select_set(sel, root->socket, flags);
+}
+
+static void
+connection_poll( ProxyConnection* root,
+ ProxySelect* sel )
+{
+ DataStatus ret = DATA_NEED_MORE;
+ Connection* conn = (Connection*)root;
+ int fd = root->socket;
+
+ if (!proxy_select_poll(sel, fd))
+ return;
+
+ switch (conn->state)
+ {
+ case STATE_CONNECTING:
+ PROXY_LOG("%s: connected to http proxy, sending header", root->name);
+ conn->state = STATE_SEND_HEADER;
+ break;
+
+ case STATE_SEND_HEADER:
+ ret = proxy_connection_send(root, fd);
+ if (ret == DATA_COMPLETED) {
+ conn->state = STATE_RECEIVE_ANSWER_LINE1;
+ PROXY_LOG("%s: header sent, receiving first answer line", root->name);
+ }
+ break;
+
+ case STATE_RECEIVE_ANSWER_LINE1:
+ case STATE_RECEIVE_ANSWER_LINE2:
+ ret = proxy_connection_receive_line(root, root->socket);
+ if (ret == DATA_COMPLETED) {
+ if (conn->state == STATE_RECEIVE_ANSWER_LINE1) {
+ int http1, http2, codenum;
+ const char* line = root->str->s;
+
+ if ( sscanf(line, "HTTP/%d.%d %d", &http1, &http2, &codenum) != 3 ) {
+ PROXY_LOG( "%s: invalid answer from proxy: '%s'",
+ root->name, line );
+ ret = DATA_ERROR;
+ break;
+ }
+
+ /* success is 2xx */
+ if (codenum/2 != 100) {
+ PROXY_LOG( "%s: connection refused, error=%d",
+ root->name, codenum );
+ proxy_connection_free( root, 0, PROXY_EVENT_CONNECTION_REFUSED );
+ return;
+ }
+ PROXY_LOG("%s: receiving second answer line", root->name);
+ conn->state = STATE_RECEIVE_ANSWER_LINE2;
+ proxy_connection_rewind(root);
+ } else {
+ /* ok, we're connected */
+ PROXY_LOG("%s: connection succeeded", root->name);
+ proxy_connection_free( root, 1, PROXY_EVENT_CONNECTED );
+ }
+ }
+ break;
+
+ default:
+ PROXY_LOG("%s: invalid state for read event: %d", root->name, conn->state);
+ }
+
+ if (ret == DATA_ERROR) {
+ proxy_connection_free( root, 0, PROXY_EVENT_SERVER_ERROR );
+ }
+}
+
+
+
+ProxyConnection*
+http_connector_connect( HttpService* service,
+ SockAddress* address )
+{
+ Connection* conn;
+ int s;
+
+ s = socket_create_inet( SOCKET_STREAM );
+ if (s < 0)
+ return NULL;
+
+ conn = qemu_mallocz(sizeof(*conn));
+ if (conn == NULL) {
+ socket_close(s);
+ return NULL;
+ }
+
+ proxy_connection_init( conn->root, s, address, service->root,
+ connection_free,
+ connection_select,
+ connection_poll );
+
+ if ( connection_init( conn ) < 0 ) {
+ connection_free( conn->root );
+ return NULL;
+ }
+
+ return conn->root;
+}