aboutsummaryrefslogtreecommitdiffstats
path: root/proxy/proxy_http_rewriter.c
diff options
context:
space:
mode:
Diffstat (limited to 'proxy/proxy_http_rewriter.c')
-rw-r--r--proxy/proxy_http_rewriter.c1125
1 files changed, 0 insertions, 1125 deletions
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 <stdio.h>
-#include <string.h>
-#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;
-}