diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:32 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:32 -0800 |
commit | 8b23a6c7e1aee255004dd19098d4c2462b61b849 (patch) | |
tree | 7a4d682ba51f0ff0364c5ca2509f515bdaf96de9 /proxy/proxy_http_connector.c | |
parent | f721e3ac031f892af46f255a47d7f54a91317b30 (diff) | |
download | external_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.c | 203 |
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; +} |