From f721e3ac031f892af46f255a47d7f54a91317b30 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 18:28:35 -0800 Subject: auto import from //depot/cupcake/@135843 --- proxy/proxy_http_rewriter.c | 1125 ------------------------------------------- 1 file changed, 1125 deletions(-) delete mode 100644 proxy/proxy_http_rewriter.c (limited to 'proxy/proxy_http_rewriter.c') diff --git a/proxy/proxy_http_rewriter.c b/proxy/proxy_http_rewriter.c deleted file mode 100644 index 812bf9c..0000000 --- a/proxy/proxy_http_rewriter.c +++ /dev/null @@ -1,1125 +0,0 @@ -/* 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 -#include -#include "qemu-common.h" -#include "android/utils/system.h" /* strsep */ - -/* this implements a transparent HTTP rewriting proxy - * - * this is needed because the HTTP spec mandates that - * any query made to a proxy uses an absolute URI as - * in: - * - * GET http://www.example.com/index.html HTTP/1.1 - * - * while the Android browser will think it's talking to - * a normal web server and will issue a: - * - * GET /index.html HTTP/1.1 - * Host: www.example.com - * - * what we do here is thus the following: - * - * - read the request header - * - rewrite the request's URI to use absolute URI - * - send the rewritten header to the proxy - * - then read the rest of the request, and tunnel it to the - * proxy as well - * - read the answer as-is and send it back to the system - * - * this sounds all easy, but the rules for computing the - * sizes of HTTP Message Bodies makes the implementation - * a *bit* funky. - */ - -/* define D_ACTIVE to 1 to dump additionnal debugging - * info when -debug-proxy is used. These are only needed - * when debugging the proxy code. - */ -#define D_ACTIVE 1 - -#if D_ACTIVE -# define D(...) PROXY_LOG(__VA_ARGS__) -#else -# define D(...) ((void)0) -#endif - - -/** ************************************************************* - ** - ** HTTP HEADERS - ** - **/ - -typedef struct HttpHeader { - struct HttpHeader* next; - const char* key; - const char* value; -} HttpHeader; - -static void -http_header_free( HttpHeader* h ) -{ - if (h) { - qemu_free((char*)h->value); - qemu_free(h); - } -} - -static int -http_header_append( HttpHeader* h, const char* value ) -{ - int old = strlen(h->value); - int new = strlen(value); - char* s = realloc((char*)h->value, old+new+1); - if (s == NULL) - return -1; - memcpy(s + old, value, new+1); - h->value = (const char*)s; - return 0; -} - -static HttpHeader* -http_header_alloc( const char* key, const char* value ) -{ - int len = strlen(key)+1; - HttpHeader* h = malloc(sizeof(*h) + len+1); - if (h) { - h->next = NULL; - h->key = (const char*)(h+1); - memcpy( (char*)h->key, key, len ); - h->value = qemu_strdup(value); - } - return h; -} - -typedef struct { - HttpHeader* first; - HttpHeader* last; -} HttpHeaderList; - -static void -http_header_list_init( HttpHeaderList* l ) -{ - l->first = l->last = NULL; -} - -static void -http_header_list_done( HttpHeaderList* l ) -{ - while (l->first) { - HttpHeader* h = l->first; - l->first = h->next; - http_header_free(h); - } - l->last = NULL; -} - -static void -http_header_list_add( HttpHeaderList* l, - HttpHeader* h ) -{ - if (!l->first) { - l->first = h; - } else { - l->last->next = h; - } - h->next = NULL; - l->last = h; -} - -static const char* -http_header_list_find( HttpHeaderList* l, - const char* key ) -{ - HttpHeader* h; - for (h = l->first; h; h = h->next) - if (!strcasecmp(h->key, key)) - return h->value; - - return NULL; -} - -/** ************************************************************* - ** - ** HTTP REQUEST AND REPLY - ** - **/ - -typedef enum { - HTTP_REQUEST_UNSUPPORTED = 0, - HTTP_REQUEST_GET, - HTTP_REQUEST_HEAD, - HTTP_REQUEST_POST, - HTTP_REQUEST_PUT, - HTTP_REQUEST_DELETE, -} HttpRequestType; - -typedef struct { - HttpRequestType req_type; - char* req_method; - char* req_uri; - char* req_version; - char* rep_version; - int rep_code; - char* rep_readable; - HttpHeaderList headers[1]; -} HttpRequest; - - -static HttpRequest* -http_request_alloc( const char* method, - const char* uri, - const char* version ) -{ - HttpRequest* r = malloc(sizeof(*r)); - - r->req_method = qemu_strdup(method); - r->req_uri = qemu_strdup(uri); - r->req_version = qemu_strdup(version); - r->rep_version = NULL; - r->rep_code = -1; - r->rep_readable = NULL; - - if (!strcmp(method,"GET")) { - r->req_type = HTTP_REQUEST_GET; - } else if (!strcmp(method,"POST")) { - r->req_type = HTTP_REQUEST_POST; - } else if (!strcmp(method,"HEAD")) { - r->req_type = HTTP_REQUEST_HEAD; - } else if (!strcmp(method,"PUT")) { - r->req_type = HTTP_REQUEST_PUT; - } else if (!strcmp(method,"DELETE")) { - r->req_type = HTTP_REQUEST_DELETE; - } else - r->req_type = HTTP_REQUEST_UNSUPPORTED; - - http_header_list_init(r->headers); - return r; -} - -static void -http_request_replace_uri( HttpRequest* r, - const char* uri ) -{ - const char* old = r->req_uri; - r->req_uri = qemu_strdup(uri); - qemu_free((char*)old); -} - -static void -http_request_free( HttpRequest* r ) -{ - if (r) { - http_header_list_done(r->headers); - - qemu_free(r->req_method); - qemu_free(r->req_uri); - qemu_free(r->req_version); - qemu_free(r->rep_version); - qemu_free(r->rep_readable); - qemu_free(r); - } -} - -static char* -http_request_find_header( HttpRequest* r, - const char* key ) -{ - return (char*)http_header_list_find(r->headers, key); -} - - -static int -http_request_add_header( HttpRequest* r, - const char* key, - const char* value ) -{ - HttpHeader* h = http_header_alloc(key,value); - if (h) { - http_header_list_add(r->headers, h); - return 0; - } - return -1; -} - -static int -http_request_add_to_last_header( HttpRequest* r, - const char* line ) -{ - if (r->headers->last) { - return http_header_append( r->headers->last, line ); - } else { - return -1; - } -} - -static int -http_request_set_reply( HttpRequest* r, - const char* version, - const char* code, - const char* readable ) -{ - if (strcmp(version,"HTTP/1.0") && strcmp(version,"HTTP/1.1")) { - PROXY_LOG("%s: bad reply protocol: %s", __FUNCTION__, version); - return -1; - } - r->rep_code = atoi(code); - if (r->rep_code == 0) { - PROXY_LOG("%s: bad reply code: %d", __FUNCTION__, code); - return -1; - } - - r->rep_version = qemu_strdup(version); - r->rep_readable = qemu_strdup(readable); - - /* reset the list of headers */ - http_header_list_done(r->headers); - return 0; -} - -/** ************************************************************* - ** - ** REWRITER CONNECTION - ** - **/ - -typedef enum { - STATE_CONNECTING = 0, - STATE_CREATE_SOCKET_PAIR, - STATE_REQUEST_FIRST_LINE, - STATE_REQUEST_HEADERS, - STATE_REQUEST_SEND, - STATE_REQUEST_BODY, - STATE_REPLY_FIRST_LINE, - STATE_REPLY_HEADERS, - STATE_REPLY_SEND, - STATE_REPLY_BODY, -} ConnectionState; - -/* root->socket is connected to the proxy server. while - * slirp_fd is connected to the slirp code through a - * socket_pair() we created for this specific purpose. - */ - -typedef enum { - BODY_NONE = 0, - BODY_KNOWN_LENGTH, - BODY_UNTIL_CLOSE, - BODY_CHUNKED, - BODY_MODE_MAX -} BodyMode; - -static const char* const body_mode_str[BODY_MODE_MAX] = { - "NONE", "KNOWN_LENGTH", "UNTIL_CLOSE", "CHUNKED" -}; - -typedef struct { - ProxyConnection root[1]; - int slirp_fd; - ConnectionState state; - HttpRequest* request; - BodyMode body_mode; - int64_t body_length; - int64_t body_total; - int64_t body_sent; - int64_t chunk_length; - int64_t chunk_total; - char body_has_data; - char body_is_full; - char body_is_closed; - char parse_chunk_header; - char parse_chunk_trailer; -} RewriteConnection; - - -static void -rewrite_connection_free( ProxyConnection* root ) -{ - RewriteConnection* conn = (RewriteConnection*)root; - - if (conn->slirp_fd >= 0) { - socket_close(conn->slirp_fd); - conn->slirp_fd = -1; - } - http_request_free(conn->request); - proxy_connection_done(root); - qemu_free(conn); -} - - -static int -rewrite_connection_init( RewriteConnection* conn ) -{ - HttpService* service = (HttpService*) conn->root->service; - ProxyConnection* root = conn->root; - - conn->slirp_fd = -1; - conn->state = STATE_CONNECTING; - - if (socket_connect( root->socket, &service->server_addr ) < 0) { - if (errno == EINPROGRESS || errno == EWOULDBLOCK) { - PROXY_LOG("%s: connecting", conn->root->name); - } - else { - PROXY_LOG("%s: cannot connect to proxy: %s", root->name, errno_str); - return -1; - } - } - else { - PROXY_LOG("%s: immediate connection", root->name); - conn->state = STATE_CREATE_SOCKET_PAIR; - } - return 0; -} - -static int -rewrite_connection_create_sockets( RewriteConnection* conn ) -{ - /* immediate connection to the proxy. now create a socket - * pair and send a 'success' event to slirp */ - int slirp_1; - ProxyConnection* root = conn->root; - - if (socket_pair( &slirp_1, &conn->slirp_fd ) < 0) { - PROXY_LOG("%s: coult not create socket pair: %s", - root->name, errno_str); - return -1; - } - - root->ev_func( root->ev_opaque, slirp_1, PROXY_EVENT_CONNECTED ); - conn->state = STATE_REQUEST_FIRST_LINE; - return 0; -} - - -/* read the first line of a given HTTP request. returns -1/0/+1 */ -static DataStatus -rewrite_connection_read_request( RewriteConnection* conn ) -{ - ProxyConnection* root = conn->root; - DataStatus ret; - - ret = proxy_connection_receive_line(root, conn->slirp_fd); - if (ret == DATA_COMPLETED) { - /* now parse the first line to see if we can handle it */ - char* line = root->str->s; - char* method; - char* uri; - char* version; - char* p = line; - - method = strsep(&p, " "); - if (p == NULL) { - PROXY_LOG("%s: can't parse method in '%'", - root->name, line); - return DATA_ERROR; - } - uri = strsep(&p, " "); - if (p == NULL) { - PROXY_LOG( "%s: can't parse URI in '%s'", - root->name, line); - return DATA_ERROR; - } - version = strsep(&p, " "); - if (p != NULL) { - PROXY_LOG( "%s: extra data after version in '%s'", - root->name, line); - return DATA_ERROR; - } - if (conn->request) - http_request_free(conn->request); - - conn->request = http_request_alloc( method, uri, version ); - if (!conn->request) - return DATA_ERROR; - - proxy_connection_rewind(root); - } - return ret; -} - - -static DataStatus -rewrite_connection_read_reply( RewriteConnection* conn ) -{ - ProxyConnection* root = conn->root; - DataStatus ret; - - ret = proxy_connection_receive_line( root, root->socket ); - if (ret == DATA_COMPLETED) { - HttpRequest* request = conn->request; - - char* line = stralloc_cstr( root->str ); - char* p = line; - char* protocol; - char* number; - char* readable; - - protocol = strsep(&p, " "); - if (p == NULL) { - PROXY_LOG("%s: can't parse response protocol: '%s'", - root->name, line); - return DATA_ERROR; - } - number = strsep(&p, " "); - if (p == NULL) { - PROXY_LOG("%s: can't parse response number: '%s'", - root->name, line); - return DATA_ERROR; - } - readable = p; - - if (http_request_set_reply(request, protocol, number, readable) < 0) - return DATA_ERROR; - - proxy_connection_rewind(root); - } - return ret; -} - - -static DataStatus -rewrite_connection_read_headers( RewriteConnection* conn, - int fd ) -{ - int ret; - ProxyConnection* root = conn->root; - - for (;;) { - char* line; - stralloc_t* str = root->str; - - ret = proxy_connection_receive_line(root, fd); - if (ret != DATA_COMPLETED) - break; - - str->n = 0; - line = str->s; - - if (line[0] == 0) { - /* an empty line means the end of headers */ - ret = 1; - break; - } - - /* it this a continuation ? */ - if (line[0] == ' ' || line[0] == '\t') { - ret = http_request_add_to_last_header( conn->request, line ); - } - else { - char* key; - char* value; - - value = line; - key = strsep(&value, ":"); - if (value == NULL) { - PROXY_LOG("%s: can't parse header '%s'", root->name, line); - ret = -1; - break; - } - value += strspn(value, " "); - if (http_request_add_header(conn->request, key, value) < 0) - ret = -1; - } - if (ret == DATA_ERROR) - break; - } - return ret; -} - -static int -rewrite_connection_rewrite_request( RewriteConnection* conn ) -{ - ProxyConnection* root = conn->root; - HttpService* service = (HttpService*) root->service; - HttpRequest* r = conn->request; - stralloc_t* str = root->str; - HttpHeader* h; - - proxy_connection_rewind(conn->root); - - /* only rewrite the URI if it is not absolute */ - if (r->req_uri[0] == '/') { - char* host = http_request_find_header(r, "Host"); - if (host == NULL) { - PROXY_LOG("%s: uh oh, not Host: in request ?", root->name); - } else { - /* now create new URI */ - stralloc_add_str(str, "http://"); - stralloc_add_str(str, host); - stralloc_add_str(str, r->req_uri); - http_request_replace_uri(r, stralloc_cstr(str)); - proxy_connection_rewind(root); - } - } - - stralloc_format( str, "%s %s %s\r\n", r->req_method, r->req_uri, r->req_version ); - for (h = r->headers->first; h; h = h->next) { - stralloc_add_format( str, "%s: %s\r\n", h->key, h->value ); - } - /* add the service's footer - includes final \r\n */ - stralloc_add_bytes( str, service->footer, service->footer_len ); - - return 0; -} - -static int -rewrite_connection_rewrite_reply( RewriteConnection* conn ) -{ - HttpRequest* r = conn->request; - ProxyConnection* root = conn->root; - stralloc_t* str = root->str; - HttpHeader* h; - - proxy_connection_rewind(root); - stralloc_format(str, "%s %d %s\r\n", r->rep_version, r->rep_code, r->rep_readable); - for (h = r->headers->first; h; h = h->next) { - stralloc_add_format(str, "%s: %s\r\n", h->key, h->value); - } - stralloc_add_str(str, "\r\n"); - - return 0; -} - - -static int -rewrite_connection_get_body_length( RewriteConnection* conn, - int is_request ) -{ - HttpRequest* r = conn->request; - ProxyConnection* root = conn->root; - char* content_length; - char* transfer_encoding; - - conn->body_mode = BODY_NONE; - conn->body_length = 0; - conn->body_total = 0; - conn->body_sent = 0; - conn->body_is_closed = 0; - conn->body_is_full = 0; - conn->body_has_data = 0; - - proxy_connection_rewind(root); - - if (is_request) { - /* only POST and PUT should have a body */ - if (r->req_type != HTTP_REQUEST_POST && - r->req_type != HTTP_REQUEST_PUT) - { - return 0; - } - } else { - /* HTTP 1.1 Section 4.3 Message Body states that HEAD requests must not have - * a message body, as well as any 1xx, 204 and 304 replies */ - if (r->req_type == HTTP_REQUEST_HEAD || r->rep_code/100 == 1 || - r->rep_code == 204 || r->rep_code == 304) - return 0; - } - - content_length = http_request_find_header(r, "Content-Length"); - if (content_length != NULL) { - char* end; - int64_t body_len = strtoll( content_length, &end, 10 ); - if (*end != '\0' || *content_length == '\0' || body_len < 0) { - PROXY_LOG("%s: bad content length: %s", root->name, content_length); - return DATA_ERROR; - } - if (body_len > 0) { - conn->body_mode = BODY_KNOWN_LENGTH; - conn->body_length = body_len; - } - } else { - char* connection = http_request_find_header(r, "Proxy-Connection"); - - if (!connection) - connection = http_request_find_header(r, "Connection"); - - if (!connection || strcasecmp(connection, "Close")) { - /* hum, we can't support this at all */ - PROXY_LOG("%s: can't determine content length, and client wants" - " to keep connection opened", - root->name); - return -1; - } - /* a negative value means that the data ends when the client - * disconnects the connection. - */ - conn->body_mode = BODY_UNTIL_CLOSE; - } - transfer_encoding = http_request_find_header(r, "Transfer-Encoding"); - if (transfer_encoding && !strcasecmp(transfer_encoding, "Chunked")) { - conn->body_mode = BODY_CHUNKED; - conn->parse_chunk_header = 0; - conn->parse_chunk_trailer = 0; - conn->chunk_length = -1; - conn->chunk_total = 0; - } - D("%s: body_length=%lld body_mode=%s", - root->name, conn->body_length, - body_mode_str[conn->body_mode]); - - proxy_connection_rewind(root); - return 0; -} - -#define MAX_BODY_BUFFER 65536 - -static DataStatus -rewrite_connection_read_body( RewriteConnection* conn, int fd ) -{ - ProxyConnection* root = conn->root; - stralloc_t* str = root->str; - int wanted = 0, current, avail; - DataStatus ret; - - if (conn->body_is_closed) { - return DATA_NEED_MORE; - } - - /* first, determine how many bytes we want to read. */ - switch (conn->body_mode) { - case BODY_NONE: - D("%s: INTERNAL ERROR: SHOULDN'T BE THERE", root->name); - return DATA_COMPLETED; - - case BODY_KNOWN_LENGTH: - { - if (conn->body_length == 0) - return DATA_COMPLETED; - - if (conn->body_length > MAX_BODY_BUFFER) - wanted = MAX_BODY_BUFFER; - else - wanted = (int)conn->body_length; - } - break; - - case BODY_UNTIL_CLOSE: - wanted = MAX_BODY_BUFFER; - break; - - case BODY_CHUNKED: - if (conn->chunk_length < 0) { - /* chunk_length < 0 means we need to read a chunk header */ - /* ensure that 'str' is flushed before doing this */ - if (!conn->parse_chunk_header) { - if (conn->body_has_data) - return DATA_NEED_MORE; - D("%s: waiting chunk header", root->name); - conn->parse_chunk_header = 1; - } - ret = proxy_connection_receive_line(root, fd); - if (ret == DATA_COMPLETED) { - char* line = str->s; - char* end; - long long length; - - length = strtoll(line, &end, 16); - if (line[0] == ' ' || (end[0] != '\0' && end[0] != ';')) { - PROXY_LOG("%s: invalid chunk header: %s", - root->name, line); - return DATA_ERROR; - } - if (length < 0) { - PROXY_LOG("%s: invalid chunk length %lld", - root->name, length); - return DATA_ERROR; - } - conn->chunk_length = length; - conn->chunk_total = 0; - if (length == 0) { - /* the last chunk, no we need to add the trailer */ - conn->parse_chunk_trailer = 0; - } - conn->parse_chunk_header = 0; - } - } - - if (conn->chunk_length == 0) { - /* chunk_length == 0 means we're reading the chunk trailer */ - /* ensure that 'str' is flushed before reading the trailer */ - if (!conn->parse_chunk_trailer) { - if (conn->body_has_data) - return DATA_NEED_MORE; - conn->parse_chunk_trailer = 1; - } - ret = rewrite_connection_read_headers(conn, fd); - if (ret == DATA_COMPLETED) { - conn->body_is_closed = 1; - } - return ret; - } - - /* if we get here, body_length > 0 */ - if (conn->chunk_length > MAX_BODY_BUFFER) - wanted = MAX_BODY_BUFFER; - else - wanted = (int)conn->chunk_length; - break; - - default: - ; - } - - /* we don't want more than MAX_BODY_BUFFER bytes in the - * buffer we used to pass the body */ - current = str->n; - avail = MAX_BODY_BUFFER - current; - if (avail <= 0) { - /* wait for some flush */ - conn->body_is_full = 1; - D("%s: waiting to flush %d bytes", - root->name, current); - return DATA_NEED_MORE; - } - - if (wanted > avail) - wanted = avail; - - ret = proxy_connection_receive(root, fd, wanted); - conn->body_has_data = (str->n > 0); - conn->body_is_full = (str->n == MAX_BODY_BUFFER); - - if (ret == DATA_ERROR) { - if (conn->body_mode == BODY_UNTIL_CLOSE) { - /* a disconnection here is normal and signals the - * end of the body */ - conn->body_total += root->str_recv; - D("%s: body completed by close (%lld bytes)", - root->name, conn->body_total); - conn->body_is_closed = 1; - ret = DATA_COMPLETED; - } - } else { - avail = root->str_recv; - ret = DATA_NEED_MORE; /* we're not really done yet */ - - switch (conn->body_mode) { - case BODY_CHUNKED: - conn->chunk_total += avail; - conn->chunk_length -= avail; - - if (conn->chunk_length == 0) { - D("%s: chunk completed (%lld bytes)", - root->name, conn->chunk_length); - conn->body_total += conn->chunk_total; - conn->chunk_total = 0; - conn->chunk_length = -1; - } - break; - - case BODY_KNOWN_LENGTH: - conn->body_length -= avail; - conn->body_total += avail; - - if (conn->body_length == 0) { - D("%s: body completed (%lld bytes)", - root->name, conn->body_total); - conn->body_is_closed = 1; - ret = DATA_COMPLETED; - } - break; - - case BODY_UNTIL_CLOSE: - conn->body_total += avail; - break; - - default: - ; - } - } - return ret; -} - -static DataStatus -rewrite_connection_send_body( RewriteConnection* conn, int fd ) -{ - ProxyConnection* root = conn->root; - stralloc_t* str = root->str; - DataStatus ret = DATA_NEED_MORE; - - if (conn->body_has_data) { - ret = proxy_connection_send(root, fd); - if (ret != DATA_ERROR) { - int pos = root->str_pos; - - memmove(str->s, str->s+pos, str->n-pos); - str->n -= pos; - root->str_pos = 0; - conn->body_is_full = (str->n == MAX_BODY_BUFFER); - conn->body_has_data = (str->n > 0); - conn->body_sent += root->str_sent; - - /* ensure that we return DATA_COMPLETED only when - * we have sent everything, and there is no more - * body pieces to read */ - if (ret == DATA_COMPLETED) { - if (!conn->body_is_closed || conn->body_has_data) - ret = DATA_NEED_MORE; - else { - D("%s: sent all body (%lld bytes)", - root->name, conn->body_sent); - } - } - D("%s: sent closed=%d data=%d n=%d ret=%d", - root->name, conn->body_is_closed, - conn->body_has_data, str->n, - ret); - } - } - return ret; -} - - -static void -rewrite_connection_select( ProxyConnection* root, - ProxySelect* sel ) -{ - RewriteConnection* conn = (RewriteConnection*)root; - int slirp = conn->slirp_fd; - int proxy = root->socket; - - switch (conn->state) { - case STATE_CONNECTING: - case STATE_CREATE_SOCKET_PAIR: - /* try to connect to the proxy server */ - proxy_select_set( sel, proxy, PROXY_SELECT_WRITE ); - break; - - case STATE_REQUEST_FIRST_LINE: - case STATE_REQUEST_HEADERS: - proxy_select_set( sel, slirp, PROXY_SELECT_READ ); - break; - - case STATE_REQUEST_SEND: - proxy_select_set( sel, proxy, PROXY_SELECT_WRITE ); - break; - - case STATE_REQUEST_BODY: - if (!conn->body_is_closed && !conn->body_is_full) - proxy_select_set( sel, slirp, PROXY_SELECT_READ ); - - if (conn->body_has_data) - proxy_select_set( sel, proxy, PROXY_SELECT_WRITE ); - break; - - case STATE_REPLY_FIRST_LINE: - case STATE_REPLY_HEADERS: - proxy_select_set( sel, proxy, PROXY_SELECT_READ ); - break; - - case STATE_REPLY_SEND: - proxy_select_set( sel, slirp, PROXY_SELECT_WRITE ); - break; - - case STATE_REPLY_BODY: - if (conn->body_has_data) - proxy_select_set( sel, slirp, PROXY_SELECT_WRITE ); - - if (!conn->body_is_closed && !conn->body_is_full) - proxy_select_set( sel, proxy, PROXY_SELECT_READ ); - break; - default: - ; - }; -} - -static void -rewrite_connection_poll( ProxyConnection* root, - ProxySelect* sel ) -{ - RewriteConnection* conn = (RewriteConnection*)root; - - int slirp = conn->slirp_fd; - int proxy = root->socket; - int has_slirp = proxy_select_poll(sel, slirp); - int has_proxy = proxy_select_poll(sel, proxy); - DataStatus ret = DATA_NEED_MORE; - - switch (conn->state) { - case STATE_CONNECTING: - if (has_proxy) { - PROXY_LOG("%s: connected to proxy", root->name); - conn->state = STATE_CREATE_SOCKET_PAIR; - } - break; - - case STATE_CREATE_SOCKET_PAIR: - if (has_proxy) { - if (rewrite_connection_create_sockets(conn) < 0) { - ret = DATA_ERROR; - } else { - D("%s: socket pair created", root->name); - conn->state = STATE_REQUEST_FIRST_LINE; - } - } - break; - - case STATE_REQUEST_FIRST_LINE: - if (has_slirp) { - ret = rewrite_connection_read_request(conn); - if (ret == DATA_COMPLETED) { - PROXY_LOG("%s: request first line ok", root->name); - conn->state = STATE_REQUEST_HEADERS; - } - } - break; - - case STATE_REQUEST_HEADERS: - if (has_slirp) { - ret = rewrite_connection_read_headers(conn, slirp); - if (ret == DATA_COMPLETED) { - PROXY_LOG("%s: request headers ok", root->name); - if (rewrite_connection_rewrite_request(conn) < 0) - ret = DATA_ERROR; - else - conn->state = STATE_REQUEST_SEND; - } - } - break; - - case STATE_REQUEST_SEND: - if (has_proxy) { - ret = proxy_connection_send(root, proxy); - if (ret == DATA_COMPLETED) { - if (rewrite_connection_get_body_length(conn, 1) < 0) { - ret = DATA_ERROR; - } else if (conn->body_mode != BODY_NONE) { - PROXY_LOG("%s: request sent, waiting for body", - root->name); - conn->state = STATE_REQUEST_BODY; - } else { - PROXY_LOG("%s: request sent, waiting for reply", - root->name); - conn->state = STATE_REPLY_FIRST_LINE; - } - } - } - break; - - case STATE_REQUEST_BODY: - if (has_slirp) { - ret = rewrite_connection_read_body(conn, slirp); - } - if (ret != DATA_ERROR && has_proxy) { - ret = rewrite_connection_send_body(conn, proxy); - if (ret == DATA_COMPLETED) { - PROXY_LOG("%s: request body ok, waiting for reply", - root->name); - conn->state = STATE_REPLY_FIRST_LINE; - } - } - break; - - case STATE_REPLY_FIRST_LINE: - if (has_proxy) { - ret = rewrite_connection_read_reply(conn); - if (ret == DATA_COMPLETED) { - PROXY_LOG("%s: reply first line ok", root->name); - conn->state = STATE_REPLY_HEADERS; - } - } - break; - - case STATE_REPLY_HEADERS: - if (has_proxy) { - ret = rewrite_connection_read_headers(conn, proxy); - if (ret == DATA_COMPLETED) { - PROXY_LOG("%s: reply headers ok", root->name); - if (rewrite_connection_rewrite_reply(conn) < 0) - ret = DATA_ERROR; - else - conn->state = STATE_REPLY_SEND; - } - } - break; - - case STATE_REPLY_SEND: - if (has_slirp) { - ret = proxy_connection_send(conn->root, slirp); - if (ret == DATA_COMPLETED) { - if (rewrite_connection_get_body_length(conn, 0) < 0) { - ret = DATA_ERROR; - } else if (conn->body_mode != BODY_NONE) { - PROXY_LOG("%s: reply sent, waiting for body", - root->name); - conn->state = STATE_REPLY_BODY; - } else { - PROXY_LOG("%s: reply sent, looping to waiting request", - root->name); - conn->state = STATE_REQUEST_FIRST_LINE; - } - } - } - break; - - case STATE_REPLY_BODY: - if (has_proxy) { - ret = rewrite_connection_read_body(conn, proxy); - } - if (ret != DATA_ERROR && has_slirp) { - ret = rewrite_connection_send_body(conn, slirp); - if (ret == DATA_COMPLETED) { - if (conn->body_mode == BODY_UNTIL_CLOSE) { - PROXY_LOG("%s: closing connection", root->name); - ret = DATA_ERROR; - } else { - PROXY_LOG("%s: reply body ok, looping to waiting request", - root->name); - conn->state = STATE_REQUEST_FIRST_LINE; - } - } - } - break; - - default: - ; - } - if (ret == DATA_ERROR) - proxy_connection_free(root, 0, PROXY_EVENT_NONE); - - return; -} - - -ProxyConnection* -http_rewriter_connect( HttpService* service, - SockAddress* address ) -{ - RewriteConnection* 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, - rewrite_connection_free, - rewrite_connection_select, - rewrite_connection_poll ); - - if ( rewrite_connection_init( conn ) < 0 ) { - rewrite_connection_free( conn->root ); - return NULL; - } - - return conn->root; -} -- cgit v1.1