blob: af3f5e7087984609832aed85f9e244c15c723455 [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12#include "proxy_http_int.h"
13#include <stdio.h>
14#include <string.h>
15#include "qemu-common.h"
16#include "android/utils/system.h" /* strsep */
17
18/* this implements a transparent HTTP rewriting proxy
19 *
20 * this is needed because the HTTP spec mandates that
21 * any query made to a proxy uses an absolute URI as
22 * in:
23 *
24 * GET http://www.example.com/index.html HTTP/1.1
25 *
26 * while the Android browser will think it's talking to
27 * a normal web server and will issue a:
28 *
29 * GET /index.html HTTP/1.1
30 * Host: www.example.com
31 *
32 * what we do here is thus the following:
33 *
34 * - read the request header
35 * - rewrite the request's URI to use absolute URI
36 * - send the rewritten header to the proxy
37 * - then read the rest of the request, and tunnel it to the
38 * proxy as well
39 * - read the answer as-is and send it back to the system
40 *
41 * this sounds all easy, but the rules for computing the
42 * sizes of HTTP Message Bodies makes the implementation
43 * a *bit* funky.
44 */
45
46/* define D_ACTIVE to 1 to dump additionnal debugging
47 * info when -debug-proxy is used. These are only needed
48 * when debugging the proxy code.
49 */
50#define D_ACTIVE 1
51
52#if D_ACTIVE
53# define D(...) PROXY_LOG(__VA_ARGS__)
54#else
55# define D(...) ((void)0)
56#endif
57
58
59/** *************************************************************
60 **
61 ** HTTP HEADERS
62 **
63 **/
64
David Turner47356942009-04-05 14:23:06 -070065/* HttpHeader is a simple structure used to hold a (key,value)
66 * pair in a linked list.
67 */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080068typedef struct HttpHeader {
69 struct HttpHeader* next;
70 const char* key;
71 const char* value;
72} HttpHeader;
73
74static void
75http_header_free( HttpHeader* h )
76{
77 if (h) {
78 qemu_free((char*)h->value);
79 qemu_free(h);
80 }
81}
82
83static int
84http_header_append( HttpHeader* h, const char* value )
85{
86 int old = strlen(h->value);
87 int new = strlen(value);
88 char* s = realloc((char*)h->value, old+new+1);
89 if (s == NULL)
90 return -1;
91 memcpy(s + old, value, new+1);
92 h->value = (const char*)s;
93 return 0;
94}
95
96static HttpHeader*
97http_header_alloc( const char* key, const char* value )
98{
99 int len = strlen(key)+1;
100 HttpHeader* h = malloc(sizeof(*h) + len+1);
101 if (h) {
102 h->next = NULL;
103 h->key = (const char*)(h+1);
104 memcpy( (char*)h->key, key, len );
105 h->value = qemu_strdup(value);
106 }
107 return h;
108}
109
David Turner47356942009-04-05 14:23:06 -0700110/** *************************************************************
111 **
112 ** HTTP HEADERS LIST
113 **
114 **/
115
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800116typedef struct {
117 HttpHeader* first;
118 HttpHeader* last;
119} HttpHeaderList;
120
121static void
122http_header_list_init( HttpHeaderList* l )
123{
124 l->first = l->last = NULL;
125}
126
127static void
128http_header_list_done( HttpHeaderList* l )
129{
130 while (l->first) {
131 HttpHeader* h = l->first;
132 l->first = h->next;
133 http_header_free(h);
134 }
135 l->last = NULL;
136}
137
138static void
139http_header_list_add( HttpHeaderList* l,
140 HttpHeader* h )
141{
142 if (!l->first) {
143 l->first = h;
144 } else {
145 l->last->next = h;
146 }
147 h->next = NULL;
148 l->last = h;
149}
150
151static const char*
152http_header_list_find( HttpHeaderList* l,
153 const char* key )
154{
155 HttpHeader* h;
156 for (h = l->first; h; h = h->next)
157 if (!strcasecmp(h->key, key))
158 return h->value;
159
160 return NULL;
161}
162
163/** *************************************************************
164 **
165 ** HTTP REQUEST AND REPLY
166 **
167 **/
168
169typedef enum {
170 HTTP_REQUEST_UNSUPPORTED = 0,
171 HTTP_REQUEST_GET,
172 HTTP_REQUEST_HEAD,
173 HTTP_REQUEST_POST,
174 HTTP_REQUEST_PUT,
175 HTTP_REQUEST_DELETE,
176} HttpRequestType;
177
David Turner47356942009-04-05 14:23:06 -0700178/* HttpRequest is used both to store information about a specific
179 * request and the corresponding reply
180 */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800181typedef struct {
David Turner47356942009-04-05 14:23:06 -0700182 HttpRequestType req_type; /* request type */
183 char* req_method; /* "GET", "POST", "HEAD", etc... */
184 char* req_uri; /* the request URI */
185 char* req_version; /* "HTTP/1.0" or "HTTP/1.1" */
186 char* rep_version; /* reply version string */
187 int rep_code; /* reply code as decimal */
188 char* rep_readable; /* human-friendly reply/error message */
189 HttpHeaderList headers[1]; /* headers */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800190} HttpRequest;
191
192
193static HttpRequest*
194http_request_alloc( const char* method,
195 const char* uri,
196 const char* version )
197{
198 HttpRequest* r = malloc(sizeof(*r));
199
200 r->req_method = qemu_strdup(method);
201 r->req_uri = qemu_strdup(uri);
202 r->req_version = qemu_strdup(version);
203 r->rep_version = NULL;
204 r->rep_code = -1;
205 r->rep_readable = NULL;
206
207 if (!strcmp(method,"GET")) {
208 r->req_type = HTTP_REQUEST_GET;
209 } else if (!strcmp(method,"POST")) {
210 r->req_type = HTTP_REQUEST_POST;
211 } else if (!strcmp(method,"HEAD")) {
212 r->req_type = HTTP_REQUEST_HEAD;
213 } else if (!strcmp(method,"PUT")) {
214 r->req_type = HTTP_REQUEST_PUT;
215 } else if (!strcmp(method,"DELETE")) {
216 r->req_type = HTTP_REQUEST_DELETE;
217 } else
218 r->req_type = HTTP_REQUEST_UNSUPPORTED;
219
220 http_header_list_init(r->headers);
221 return r;
222}
223
224static void
225http_request_replace_uri( HttpRequest* r,
226 const char* uri )
227{
228 const char* old = r->req_uri;
229 r->req_uri = qemu_strdup(uri);
230 qemu_free((char*)old);
231}
232
233static void
234http_request_free( HttpRequest* r )
235{
236 if (r) {
237 http_header_list_done(r->headers);
238
239 qemu_free(r->req_method);
240 qemu_free(r->req_uri);
241 qemu_free(r->req_version);
242 qemu_free(r->rep_version);
243 qemu_free(r->rep_readable);
244 qemu_free(r);
245 }
246}
247
248static char*
249http_request_find_header( HttpRequest* r,
250 const char* key )
251{
252 return (char*)http_header_list_find(r->headers, key);
253}
254
255
256static int
257http_request_add_header( HttpRequest* r,
258 const char* key,
259 const char* value )
260{
261 HttpHeader* h = http_header_alloc(key,value);
262 if (h) {
263 http_header_list_add(r->headers, h);
264 return 0;
265 }
266 return -1;
267}
268
269static int
270http_request_add_to_last_header( HttpRequest* r,
271 const char* line )
272{
273 if (r->headers->last) {
274 return http_header_append( r->headers->last, line );
275 } else {
276 return -1;
277 }
278}
279
280static int
281http_request_set_reply( HttpRequest* r,
282 const char* version,
283 const char* code,
284 const char* readable )
285{
286 if (strcmp(version,"HTTP/1.0") && strcmp(version,"HTTP/1.1")) {
287 PROXY_LOG("%s: bad reply protocol: %s", __FUNCTION__, version);
288 return -1;
289 }
290 r->rep_code = atoi(code);
291 if (r->rep_code == 0) {
292 PROXY_LOG("%s: bad reply code: %d", __FUNCTION__, code);
293 return -1;
294 }
295
296 r->rep_version = qemu_strdup(version);
297 r->rep_readable = qemu_strdup(readable);
298
299 /* reset the list of headers */
300 http_header_list_done(r->headers);
301 return 0;
302}
303
304/** *************************************************************
305 **
306 ** REWRITER CONNECTION
307 **
308 **/
309
310typedef enum {
311 STATE_CONNECTING = 0,
312 STATE_CREATE_SOCKET_PAIR,
313 STATE_REQUEST_FIRST_LINE,
314 STATE_REQUEST_HEADERS,
315 STATE_REQUEST_SEND,
316 STATE_REQUEST_BODY,
317 STATE_REPLY_FIRST_LINE,
318 STATE_REPLY_HEADERS,
319 STATE_REPLY_SEND,
320 STATE_REPLY_BODY,
321} ConnectionState;
322
323/* root->socket is connected to the proxy server. while
324 * slirp_fd is connected to the slirp code through a
325 * socket_pair() we created for this specific purpose.
326 */
327
328typedef enum {
329 BODY_NONE = 0,
330 BODY_KNOWN_LENGTH,
331 BODY_UNTIL_CLOSE,
332 BODY_CHUNKED,
333 BODY_MODE_MAX
334} BodyMode;
335
336static const char* const body_mode_str[BODY_MODE_MAX] = {
337 "NONE", "KNOWN_LENGTH", "UNTIL_CLOSE", "CHUNKED"
338};
339
340typedef struct {
341 ProxyConnection root[1];
342 int slirp_fd;
343 ConnectionState state;
344 HttpRequest* request;
345 BodyMode body_mode;
346 int64_t body_length;
347 int64_t body_total;
348 int64_t body_sent;
349 int64_t chunk_length;
350 int64_t chunk_total;
351 char body_has_data;
352 char body_is_full;
353 char body_is_closed;
354 char parse_chunk_header;
355 char parse_chunk_trailer;
356} RewriteConnection;
357
358
359static void
360rewrite_connection_free( ProxyConnection* root )
361{
362 RewriteConnection* conn = (RewriteConnection*)root;
363
364 if (conn->slirp_fd >= 0) {
365 socket_close(conn->slirp_fd);
366 conn->slirp_fd = -1;
367 }
368 http_request_free(conn->request);
369 proxy_connection_done(root);
370 qemu_free(conn);
371}
372
373
374static int
375rewrite_connection_init( RewriteConnection* conn )
376{
377 HttpService* service = (HttpService*) conn->root->service;
378 ProxyConnection* root = conn->root;
379
380 conn->slirp_fd = -1;
381 conn->state = STATE_CONNECTING;
382
383 if (socket_connect( root->socket, &service->server_addr ) < 0) {
David 'Digit' Turnerce0f4b02010-03-25 11:11:29 -0700384 if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800385 PROXY_LOG("%s: connecting", conn->root->name);
386 }
387 else {
388 PROXY_LOG("%s: cannot connect to proxy: %s", root->name, errno_str);
389 return -1;
390 }
391 }
392 else {
393 PROXY_LOG("%s: immediate connection", root->name);
394 conn->state = STATE_CREATE_SOCKET_PAIR;
395 }
396 return 0;
397}
398
399static int
400rewrite_connection_create_sockets( RewriteConnection* conn )
401{
402 /* immediate connection to the proxy. now create a socket
403 * pair and send a 'success' event to slirp */
404 int slirp_1;
405 ProxyConnection* root = conn->root;
406
407 if (socket_pair( &slirp_1, &conn->slirp_fd ) < 0) {
408 PROXY_LOG("%s: coult not create socket pair: %s",
409 root->name, errno_str);
410 return -1;
411 }
412
413 root->ev_func( root->ev_opaque, slirp_1, PROXY_EVENT_CONNECTED );
414 conn->state = STATE_REQUEST_FIRST_LINE;
415 return 0;
416}
417
418
419/* read the first line of a given HTTP request. returns -1/0/+1 */
420static DataStatus
421rewrite_connection_read_request( RewriteConnection* conn )
422{
423 ProxyConnection* root = conn->root;
424 DataStatus ret;
425
426 ret = proxy_connection_receive_line(root, conn->slirp_fd);
427 if (ret == DATA_COMPLETED) {
428 /* now parse the first line to see if we can handle it */
429 char* line = root->str->s;
430 char* method;
431 char* uri;
432 char* version;
433 char* p = line;
434
435 method = strsep(&p, " ");
436 if (p == NULL) {
437 PROXY_LOG("%s: can't parse method in '%'",
438 root->name, line);
439 return DATA_ERROR;
440 }
441 uri = strsep(&p, " ");
442 if (p == NULL) {
443 PROXY_LOG( "%s: can't parse URI in '%s'",
444 root->name, line);
445 return DATA_ERROR;
446 }
447 version = strsep(&p, " ");
448 if (p != NULL) {
449 PROXY_LOG( "%s: extra data after version in '%s'",
450 root->name, line);
451 return DATA_ERROR;
452 }
453 if (conn->request)
454 http_request_free(conn->request);
455
456 conn->request = http_request_alloc( method, uri, version );
457 if (!conn->request)
458 return DATA_ERROR;
459
460 proxy_connection_rewind(root);
461 }
462 return ret;
463}
464
465
466static DataStatus
467rewrite_connection_read_reply( RewriteConnection* conn )
468{
469 ProxyConnection* root = conn->root;
470 DataStatus ret;
471
472 ret = proxy_connection_receive_line( root, root->socket );
473 if (ret == DATA_COMPLETED) {
474 HttpRequest* request = conn->request;
475
476 char* line = stralloc_cstr( root->str );
477 char* p = line;
478 char* protocol;
479 char* number;
480 char* readable;
481
482 protocol = strsep(&p, " ");
483 if (p == NULL) {
484 PROXY_LOG("%s: can't parse response protocol: '%s'",
485 root->name, line);
486 return DATA_ERROR;
487 }
488 number = strsep(&p, " ");
489 if (p == NULL) {
490 PROXY_LOG("%s: can't parse response number: '%s'",
491 root->name, line);
492 return DATA_ERROR;
493 }
494 readable = p;
495
496 if (http_request_set_reply(request, protocol, number, readable) < 0)
497 return DATA_ERROR;
498
499 proxy_connection_rewind(root);
500 }
501 return ret;
502}
503
504
505static DataStatus
506rewrite_connection_read_headers( RewriteConnection* conn,
507 int fd )
508{
509 int ret;
510 ProxyConnection* root = conn->root;
511
512 for (;;) {
513 char* line;
514 stralloc_t* str = root->str;
515
516 ret = proxy_connection_receive_line(root, fd);
517 if (ret != DATA_COMPLETED)
518 break;
519
520 str->n = 0;
521 line = str->s;
522
523 if (line[0] == 0) {
524 /* an empty line means the end of headers */
525 ret = 1;
526 break;
527 }
528
529 /* it this a continuation ? */
530 if (line[0] == ' ' || line[0] == '\t') {
531 ret = http_request_add_to_last_header( conn->request, line );
532 }
533 else {
534 char* key;
535 char* value;
536
537 value = line;
538 key = strsep(&value, ":");
539 if (value == NULL) {
540 PROXY_LOG("%s: can't parse header '%s'", root->name, line);
541 ret = -1;
542 break;
543 }
544 value += strspn(value, " ");
545 if (http_request_add_header(conn->request, key, value) < 0)
546 ret = -1;
547 }
548 if (ret == DATA_ERROR)
549 break;
550 }
551 return ret;
552}
553
554static int
555rewrite_connection_rewrite_request( RewriteConnection* conn )
556{
557 ProxyConnection* root = conn->root;
558 HttpService* service = (HttpService*) root->service;
559 HttpRequest* r = conn->request;
560 stralloc_t* str = root->str;
561 HttpHeader* h;
562
563 proxy_connection_rewind(conn->root);
564
565 /* only rewrite the URI if it is not absolute */
566 if (r->req_uri[0] == '/') {
567 char* host = http_request_find_header(r, "Host");
568 if (host == NULL) {
569 PROXY_LOG("%s: uh oh, not Host: in request ?", root->name);
570 } else {
571 /* now create new URI */
572 stralloc_add_str(str, "http://");
573 stralloc_add_str(str, host);
574 stralloc_add_str(str, r->req_uri);
575 http_request_replace_uri(r, stralloc_cstr(str));
576 proxy_connection_rewind(root);
577 }
578 }
579
580 stralloc_format( str, "%s %s %s\r\n", r->req_method, r->req_uri, r->req_version );
581 for (h = r->headers->first; h; h = h->next) {
582 stralloc_add_format( str, "%s: %s\r\n", h->key, h->value );
583 }
584 /* add the service's footer - includes final \r\n */
585 stralloc_add_bytes( str, service->footer, service->footer_len );
586
587 return 0;
588}
589
590static int
591rewrite_connection_rewrite_reply( RewriteConnection* conn )
592{
593 HttpRequest* r = conn->request;
594 ProxyConnection* root = conn->root;
595 stralloc_t* str = root->str;
596 HttpHeader* h;
597
598 proxy_connection_rewind(root);
599 stralloc_format(str, "%s %d %s\r\n", r->rep_version, r->rep_code, r->rep_readable);
600 for (h = r->headers->first; h; h = h->next) {
601 stralloc_add_format(str, "%s: %s\r\n", h->key, h->value);
602 }
603 stralloc_add_str(str, "\r\n");
604
605 return 0;
606}
607
608
609static int
610rewrite_connection_get_body_length( RewriteConnection* conn,
611 int is_request )
612{
613 HttpRequest* r = conn->request;
614 ProxyConnection* root = conn->root;
615 char* content_length;
616 char* transfer_encoding;
617
618 conn->body_mode = BODY_NONE;
619 conn->body_length = 0;
620 conn->body_total = 0;
621 conn->body_sent = 0;
622 conn->body_is_closed = 0;
623 conn->body_is_full = 0;
624 conn->body_has_data = 0;
625
626 proxy_connection_rewind(root);
627
628 if (is_request) {
629 /* only POST and PUT should have a body */
630 if (r->req_type != HTTP_REQUEST_POST &&
631 r->req_type != HTTP_REQUEST_PUT)
632 {
633 return 0;
634 }
635 } else {
636 /* HTTP 1.1 Section 4.3 Message Body states that HEAD requests must not have
637 * a message body, as well as any 1xx, 204 and 304 replies */
638 if (r->req_type == HTTP_REQUEST_HEAD || r->rep_code/100 == 1 ||
639 r->rep_code == 204 || r->rep_code == 304)
640 return 0;
641 }
642
643 content_length = http_request_find_header(r, "Content-Length");
644 if (content_length != NULL) {
645 char* end;
646 int64_t body_len = strtoll( content_length, &end, 10 );
647 if (*end != '\0' || *content_length == '\0' || body_len < 0) {
648 PROXY_LOG("%s: bad content length: %s", root->name, content_length);
649 return DATA_ERROR;
650 }
651 if (body_len > 0) {
652 conn->body_mode = BODY_KNOWN_LENGTH;
653 conn->body_length = body_len;
654 }
655 } else {
David Turner47356942009-04-05 14:23:06 -0700656 transfer_encoding = http_request_find_header(r, "Transfer-Encoding");
657 if (transfer_encoding && !strcasecmp(transfer_encoding, "Chunked")) {
658 conn->body_mode = BODY_CHUNKED;
659 conn->parse_chunk_header = 0;
660 conn->parse_chunk_trailer = 0;
661 conn->chunk_length = -1;
662 conn->chunk_total = 0;
663 }
664 }
665 if (conn->body_mode == BODY_NONE) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800666 char* connection = http_request_find_header(r, "Proxy-Connection");
667
668 if (!connection)
669 connection = http_request_find_header(r, "Connection");
670
671 if (!connection || strcasecmp(connection, "Close")) {
672 /* hum, we can't support this at all */
673 PROXY_LOG("%s: can't determine content length, and client wants"
674 " to keep connection opened",
675 root->name);
676 return -1;
677 }
678 /* a negative value means that the data ends when the client
679 * disconnects the connection.
680 */
681 conn->body_mode = BODY_UNTIL_CLOSE;
682 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800683 D("%s: body_length=%lld body_mode=%s",
684 root->name, conn->body_length,
685 body_mode_str[conn->body_mode]);
686
687 proxy_connection_rewind(root);
688 return 0;
689}
690
691#define MAX_BODY_BUFFER 65536
692
693static DataStatus
694rewrite_connection_read_body( RewriteConnection* conn, int fd )
695{
696 ProxyConnection* root = conn->root;
697 stralloc_t* str = root->str;
698 int wanted = 0, current, avail;
699 DataStatus ret;
700
701 if (conn->body_is_closed) {
702 return DATA_NEED_MORE;
703 }
704
705 /* first, determine how many bytes we want to read. */
706 switch (conn->body_mode) {
707 case BODY_NONE:
708 D("%s: INTERNAL ERROR: SHOULDN'T BE THERE", root->name);
709 return DATA_COMPLETED;
710
711 case BODY_KNOWN_LENGTH:
712 {
713 if (conn->body_length == 0)
714 return DATA_COMPLETED;
715
716 if (conn->body_length > MAX_BODY_BUFFER)
717 wanted = MAX_BODY_BUFFER;
718 else
719 wanted = (int)conn->body_length;
720 }
721 break;
722
723 case BODY_UNTIL_CLOSE:
724 wanted = MAX_BODY_BUFFER;
725 break;
726
727 case BODY_CHUNKED:
728 if (conn->chunk_length < 0) {
729 /* chunk_length < 0 means we need to read a chunk header */
730 /* ensure that 'str' is flushed before doing this */
731 if (!conn->parse_chunk_header) {
732 if (conn->body_has_data)
733 return DATA_NEED_MORE;
734 D("%s: waiting chunk header", root->name);
735 conn->parse_chunk_header = 1;
736 }
737 ret = proxy_connection_receive_line(root, fd);
738 if (ret == DATA_COMPLETED) {
739 char* line = str->s;
740 char* end;
741 long long length;
742
743 length = strtoll(line, &end, 16);
744 if (line[0] == ' ' || (end[0] != '\0' && end[0] != ';')) {
745 PROXY_LOG("%s: invalid chunk header: %s",
746 root->name, line);
747 return DATA_ERROR;
748 }
749 if (length < 0) {
750 PROXY_LOG("%s: invalid chunk length %lld",
751 root->name, length);
752 return DATA_ERROR;
753 }
David 'Digit' Turner1fee27e2010-07-23 12:45:33 -0700754 /* proxy_connection_receive_line() did remove the
755 * trailing \r\n, but we must preserve it when we
756 * send the chunk size to the proxy.
757 */
758 stralloc_add_str(root->str, "\r\n");
759
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800760 conn->chunk_length = length;
761 conn->chunk_total = 0;
762 if (length == 0) {
763 /* the last chunk, no we need to add the trailer */
764 conn->parse_chunk_trailer = 0;
765 }
766 conn->parse_chunk_header = 0;
767 }
768 }
769
770 if (conn->chunk_length == 0) {
771 /* chunk_length == 0 means we're reading the chunk trailer */
772 /* ensure that 'str' is flushed before reading the trailer */
773 if (!conn->parse_chunk_trailer) {
774 if (conn->body_has_data)
775 return DATA_NEED_MORE;
776 conn->parse_chunk_trailer = 1;
777 }
778 ret = rewrite_connection_read_headers(conn, fd);
779 if (ret == DATA_COMPLETED) {
780 conn->body_is_closed = 1;
781 }
782 return ret;
783 }
784
785 /* if we get here, body_length > 0 */
786 if (conn->chunk_length > MAX_BODY_BUFFER)
787 wanted = MAX_BODY_BUFFER;
788 else
789 wanted = (int)conn->chunk_length;
790 break;
791
792 default:
793 ;
794 }
795
796 /* we don't want more than MAX_BODY_BUFFER bytes in the
797 * buffer we used to pass the body */
798 current = str->n;
799 avail = MAX_BODY_BUFFER - current;
800 if (avail <= 0) {
801 /* wait for some flush */
802 conn->body_is_full = 1;
803 D("%s: waiting to flush %d bytes",
804 root->name, current);
805 return DATA_NEED_MORE;
806 }
807
808 if (wanted > avail)
809 wanted = avail;
810
811 ret = proxy_connection_receive(root, fd, wanted);
812 conn->body_has_data = (str->n > 0);
813 conn->body_is_full = (str->n == MAX_BODY_BUFFER);
814
815 if (ret == DATA_ERROR) {
816 if (conn->body_mode == BODY_UNTIL_CLOSE) {
817 /* a disconnection here is normal and signals the
818 * end of the body */
819 conn->body_total += root->str_recv;
820 D("%s: body completed by close (%lld bytes)",
821 root->name, conn->body_total);
822 conn->body_is_closed = 1;
823 ret = DATA_COMPLETED;
824 }
825 } else {
826 avail = root->str_recv;
827 ret = DATA_NEED_MORE; /* we're not really done yet */
828
829 switch (conn->body_mode) {
830 case BODY_CHUNKED:
831 conn->chunk_total += avail;
832 conn->chunk_length -= avail;
833
834 if (conn->chunk_length == 0) {
835 D("%s: chunk completed (%lld bytes)",
836 root->name, conn->chunk_length);
837 conn->body_total += conn->chunk_total;
838 conn->chunk_total = 0;
839 conn->chunk_length = -1;
840 }
841 break;
842
843 case BODY_KNOWN_LENGTH:
844 conn->body_length -= avail;
845 conn->body_total += avail;
846
847 if (conn->body_length == 0) {
848 D("%s: body completed (%lld bytes)",
849 root->name, conn->body_total);
850 conn->body_is_closed = 1;
851 ret = DATA_COMPLETED;
852 }
853 break;
854
855 case BODY_UNTIL_CLOSE:
856 conn->body_total += avail;
857 break;
858
859 default:
860 ;
861 }
862 }
863 return ret;
864}
865
866static DataStatus
867rewrite_connection_send_body( RewriteConnection* conn, int fd )
868{
869 ProxyConnection* root = conn->root;
870 stralloc_t* str = root->str;
871 DataStatus ret = DATA_NEED_MORE;
872
873 if (conn->body_has_data) {
874 ret = proxy_connection_send(root, fd);
875 if (ret != DATA_ERROR) {
876 int pos = root->str_pos;
877
878 memmove(str->s, str->s+pos, str->n-pos);
879 str->n -= pos;
880 root->str_pos = 0;
881 conn->body_is_full = (str->n == MAX_BODY_BUFFER);
882 conn->body_has_data = (str->n > 0);
883 conn->body_sent += root->str_sent;
884
885 /* ensure that we return DATA_COMPLETED only when
886 * we have sent everything, and there is no more
887 * body pieces to read */
888 if (ret == DATA_COMPLETED) {
889 if (!conn->body_is_closed || conn->body_has_data)
890 ret = DATA_NEED_MORE;
891 else {
892 D("%s: sent all body (%lld bytes)",
893 root->name, conn->body_sent);
894 }
895 }
896 D("%s: sent closed=%d data=%d n=%d ret=%d",
897 root->name, conn->body_is_closed,
898 conn->body_has_data, str->n,
899 ret);
900 }
901 }
902 return ret;
903}
904
905
906static void
907rewrite_connection_select( ProxyConnection* root,
908 ProxySelect* sel )
909{
910 RewriteConnection* conn = (RewriteConnection*)root;
911 int slirp = conn->slirp_fd;
912 int proxy = root->socket;
913
914 switch (conn->state) {
915 case STATE_CONNECTING:
916 case STATE_CREATE_SOCKET_PAIR:
917 /* try to connect to the proxy server */
918 proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
919 break;
920
921 case STATE_REQUEST_FIRST_LINE:
922 case STATE_REQUEST_HEADERS:
923 proxy_select_set( sel, slirp, PROXY_SELECT_READ );
924 break;
925
926 case STATE_REQUEST_SEND:
927 proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
928 break;
929
930 case STATE_REQUEST_BODY:
931 if (!conn->body_is_closed && !conn->body_is_full)
932 proxy_select_set( sel, slirp, PROXY_SELECT_READ );
933
934 if (conn->body_has_data)
935 proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
936 break;
937
938 case STATE_REPLY_FIRST_LINE:
939 case STATE_REPLY_HEADERS:
940 proxy_select_set( sel, proxy, PROXY_SELECT_READ );
941 break;
942
943 case STATE_REPLY_SEND:
944 proxy_select_set( sel, slirp, PROXY_SELECT_WRITE );
945 break;
946
947 case STATE_REPLY_BODY:
948 if (conn->body_has_data)
949 proxy_select_set( sel, slirp, PROXY_SELECT_WRITE );
950
951 if (!conn->body_is_closed && !conn->body_is_full)
952 proxy_select_set( sel, proxy, PROXY_SELECT_READ );
953 break;
954 default:
955 ;
956 };
957}
958
959static void
960rewrite_connection_poll( ProxyConnection* root,
961 ProxySelect* sel )
962{
963 RewriteConnection* conn = (RewriteConnection*)root;
964
965 int slirp = conn->slirp_fd;
966 int proxy = root->socket;
967 int has_slirp = proxy_select_poll(sel, slirp);
968 int has_proxy = proxy_select_poll(sel, proxy);
969 DataStatus ret = DATA_NEED_MORE;
970
971 switch (conn->state) {
972 case STATE_CONNECTING:
973 if (has_proxy) {
974 PROXY_LOG("%s: connected to proxy", root->name);
975 conn->state = STATE_CREATE_SOCKET_PAIR;
976 }
977 break;
978
979 case STATE_CREATE_SOCKET_PAIR:
980 if (has_proxy) {
981 if (rewrite_connection_create_sockets(conn) < 0) {
982 ret = DATA_ERROR;
983 } else {
984 D("%s: socket pair created", root->name);
985 conn->state = STATE_REQUEST_FIRST_LINE;
986 }
987 }
988 break;
989
990 case STATE_REQUEST_FIRST_LINE:
991 if (has_slirp) {
992 ret = rewrite_connection_read_request(conn);
993 if (ret == DATA_COMPLETED) {
994 PROXY_LOG("%s: request first line ok", root->name);
995 conn->state = STATE_REQUEST_HEADERS;
996 }
997 }
998 break;
999
1000 case STATE_REQUEST_HEADERS:
1001 if (has_slirp) {
1002 ret = rewrite_connection_read_headers(conn, slirp);
1003 if (ret == DATA_COMPLETED) {
1004 PROXY_LOG("%s: request headers ok", root->name);
1005 if (rewrite_connection_rewrite_request(conn) < 0)
1006 ret = DATA_ERROR;
1007 else
1008 conn->state = STATE_REQUEST_SEND;
1009 }
1010 }
1011 break;
1012
1013 case STATE_REQUEST_SEND:
1014 if (has_proxy) {
1015 ret = proxy_connection_send(root, proxy);
1016 if (ret == DATA_COMPLETED) {
1017 if (rewrite_connection_get_body_length(conn, 1) < 0) {
1018 ret = DATA_ERROR;
1019 } else if (conn->body_mode != BODY_NONE) {
1020 PROXY_LOG("%s: request sent, waiting for body",
1021 root->name);
1022 conn->state = STATE_REQUEST_BODY;
1023 } else {
1024 PROXY_LOG("%s: request sent, waiting for reply",
1025 root->name);
1026 conn->state = STATE_REPLY_FIRST_LINE;
1027 }
1028 }
1029 }
1030 break;
1031
1032 case STATE_REQUEST_BODY:
1033 if (has_slirp) {
1034 ret = rewrite_connection_read_body(conn, slirp);
1035 }
1036 if (ret != DATA_ERROR && has_proxy) {
1037 ret = rewrite_connection_send_body(conn, proxy);
1038 if (ret == DATA_COMPLETED) {
1039 PROXY_LOG("%s: request body ok, waiting for reply",
1040 root->name);
1041 conn->state = STATE_REPLY_FIRST_LINE;
1042 }
1043 }
1044 break;
1045
1046 case STATE_REPLY_FIRST_LINE:
1047 if (has_proxy) {
1048 ret = rewrite_connection_read_reply(conn);
1049 if (ret == DATA_COMPLETED) {
1050 PROXY_LOG("%s: reply first line ok", root->name);
1051 conn->state = STATE_REPLY_HEADERS;
1052 }
1053 }
1054 break;
1055
1056 case STATE_REPLY_HEADERS:
1057 if (has_proxy) {
1058 ret = rewrite_connection_read_headers(conn, proxy);
1059 if (ret == DATA_COMPLETED) {
1060 PROXY_LOG("%s: reply headers ok", root->name);
1061 if (rewrite_connection_rewrite_reply(conn) < 0)
1062 ret = DATA_ERROR;
1063 else
1064 conn->state = STATE_REPLY_SEND;
1065 }
1066 }
1067 break;
1068
1069 case STATE_REPLY_SEND:
1070 if (has_slirp) {
1071 ret = proxy_connection_send(conn->root, slirp);
1072 if (ret == DATA_COMPLETED) {
1073 if (rewrite_connection_get_body_length(conn, 0) < 0) {
1074 ret = DATA_ERROR;
1075 } else if (conn->body_mode != BODY_NONE) {
1076 PROXY_LOG("%s: reply sent, waiting for body",
1077 root->name);
1078 conn->state = STATE_REPLY_BODY;
1079 } else {
1080 PROXY_LOG("%s: reply sent, looping to waiting request",
1081 root->name);
1082 conn->state = STATE_REQUEST_FIRST_LINE;
1083 }
1084 }
1085 }
1086 break;
1087
1088 case STATE_REPLY_BODY:
1089 if (has_proxy) {
1090 ret = rewrite_connection_read_body(conn, proxy);
1091 }
1092 if (ret != DATA_ERROR && has_slirp) {
1093 ret = rewrite_connection_send_body(conn, slirp);
1094 if (ret == DATA_COMPLETED) {
1095 if (conn->body_mode == BODY_UNTIL_CLOSE) {
1096 PROXY_LOG("%s: closing connection", root->name);
1097 ret = DATA_ERROR;
1098 } else {
1099 PROXY_LOG("%s: reply body ok, looping to waiting request",
1100 root->name);
1101 conn->state = STATE_REQUEST_FIRST_LINE;
1102 }
1103 }
1104 }
1105 break;
1106
1107 default:
1108 ;
1109 }
1110 if (ret == DATA_ERROR)
1111 proxy_connection_free(root, 0, PROXY_EVENT_NONE);
1112
1113 return;
1114}
1115
1116
1117ProxyConnection*
1118http_rewriter_connect( HttpService* service,
1119 SockAddress* address )
1120{
1121 RewriteConnection* conn;
1122 int s;
1123
David 'Digit' Turner9b98dbd2010-07-30 15:35:00 -07001124 s = socket_create(address->family, SOCKET_STREAM );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001125 if (s < 0)
1126 return NULL;
1127
1128 conn = qemu_mallocz(sizeof(*conn));
1129 if (conn == NULL) {
1130 socket_close(s);
1131 return NULL;
1132 }
1133
1134 proxy_connection_init( conn->root, s, address, service->root,
1135 rewrite_connection_free,
1136 rewrite_connection_select,
1137 rewrite_connection_poll );
1138
1139 if ( rewrite_connection_init( conn ) < 0 ) {
1140 rewrite_connection_free( conn->root );
1141 return NULL;
1142 }
1143
1144 return conn->root;
1145}