bit more work... worked on err msgs and buffers
[shim.git] / httpconn.c
blob25bec6b104e9116c6d0eae0bcedaeeb1e30d7a67
2 #include <sys/queue.h>
3 #include <assert.h>
4 #include <stdlib.h>
5 #include <string.h>
7 #include <event2/util.h>
8 #include <event2/event.h>
9 #include <event2/bufferevent.h>
10 #include <event2/buffer.h>
12 #include "httpconn.h"
13 #include "headers.h"
14 #include "util.h"
15 #include "log.h"
17 #define METHOD0(conn, slot) \
18 (conn)->cbs->slot((conn), (conn)->cbarg)
19 #define METHOD1(conn, slot, a) \
20 (conn)->cbs->slot((conn), (a), (conn)->cbarg)
21 #define METHOD2(conn, slot, a, b) \
22 (conn)->cbs->slot((conn), (a), (b), (conn)->cbarg)
23 #define METHOD3(conn, slot, a, b, c) \
24 (conn)->cbs->slot((conn), (a), (b), (c), (conn)->cbarg)
25 #define METHOD4(conn, slot, a, b, c, d) \
26 (conn)->cbs->slot((conn), (a), (b), (c), (d), (conn)->cbarg)
28 /* max amount of data we can have backlogged on outbuf before choaking */
29 static size_t max_write_backlog = 50 * 1024;
31 /* the number of seconds to keep an idle connections hanging around */
32 static int idle_client_timeout = 120;
33 static int idle_server_timeout = 120;
35 struct http_conn {
36 enum http_state state;
37 enum http_version vers;
38 enum http_te te;
39 enum http_type type;
40 int is_choaked;
41 int has_body;
42 int read_paused;
43 int msg_complete_on_eof;
44 int persistent;
45 const struct http_cbs *cbs;
46 void *cbarg;
47 ev_int64_t data_remaining;
48 char *firstline;
49 struct header_list *headers;
50 struct bufferevent *bev;
51 struct evbuffer *inbuf_processed;
54 static int
55 method_from_string(enum http_method *m, const char *method)
57 if (!evutil_ascii_strcasecmp(method, "GET"))
58 *m = METH_GET;
59 else if (!evutil_ascii_strcasecmp(method, "HEAD"))
60 *m = METH_HEAD;
61 else if (!evutil_ascii_strcasecmp(method, "POST"))
62 *m = METH_POST;
63 else if (!evutil_ascii_strcasecmp(method, "PUT"))
64 *m = METH_PUT;
65 else if (!evutil_ascii_strcasecmp(method, "CONNECT"))
66 *m = METH_CONNECT;
67 else {
68 log_warn("method_from_string: unknown method, '%s'", method);
69 return -1;
72 return 0;
75 static const char *
76 method_to_string(enum http_method m)
78 switch (m) {
79 case METH_GET:
80 return "GET";
81 case METH_HEAD:
82 return "HEAD";
83 case METH_POST:
84 return "POST";
85 case METH_PUT:
86 return "PUT";
87 case METH_CONNECT:
88 return "CONNECT";
91 log_fatal("method_to_string: unknown method %d", m);
92 return "???";
95 static int
96 version_from_string(enum http_version *v, const char *vers)
98 if (evutil_ascii_strncasecmp(vers, "HTTP/", 5)) {
99 log_warn("version_from_string: bad http-version, '%s'", vers);
100 return -1;
103 vers += 5;
105 /* XXX this only understands 1.0 and 1.1 */
107 if (!strcmp(vers, "1.0"))
108 *v = HTTP_10;
109 else if (!strcmp(vers, "1.1"))
110 *v = HTTP_11;
111 else {
112 log_warn("version_from_string: unknown http-version, '%s'",
113 vers);
114 return -1;
117 return 0;
120 static const char *
121 version_to_string(enum http_version v)
123 switch (v) {
124 case HTTP_UNKNOWN:
125 return "HTTP/??";
126 case HTTP_10:
127 return "HTTP/1.0";
128 case HTTP_11:
129 return "HTTP/1.1";
132 log_fatal("version_to_string: unknown version %d", v);
133 return "???";
136 static const char *
137 error_code_to_reason_string(int code)
139 switch (code) {
140 case 400:
141 return "Bad Request";
142 case 401:
143 return "Unauthorized";
144 case 403:
145 return "Forbidden";
146 case 404:
147 return "Not Found";
148 case 405:
149 return "Method Not Allowed";
150 case 406:
151 return "Not Acceptable";
152 case 407:
153 return "Proxy Authentication Required";
154 case 408:
155 return "Request Timeout";
156 case 409:
157 return "Conflict";
158 case 410:
159 return "Gone";
160 case 411:
161 return "Length Required";
162 case 412:
163 return "Precondition Failed";
164 case 413:
165 return "Request Entity Too Large";
166 case 414:
167 return "Request-URI Too Long";
168 case 415:
169 return "Unsupported Media Type";
170 case 416:
171 return "Requested Range Not Satisfiable";
172 case 417:
173 return "Expectation Failed";
174 case 421:
175 return "There are too many connections from your internet address";
176 case 500:
177 return "Internal Server Error";
178 case 501:
179 return "Not Implemented";
180 case 502:
181 return "Bad Gateway";
182 case 503:
183 return "Service Unavailable";
184 case 504:
185 return "Gateway Timeout";
186 case 505:
187 return "HTTP Version Not Supported";
188 case 530:
189 return "User access denied";
192 return "???";
195 static void
196 begin_message(struct http_conn *conn)
198 // XXX read timeout?
199 assert(conn->headers == NULL && conn->firstline == NULL);
200 assert(!conn->read_paused);
201 conn->headers = mem_calloc(1, sizeof(*conn->headers));
202 TAILQ_INIT(conn->headers);
203 conn->state = HTTP_STATE_IDLE;
204 bufferevent_enable(conn->bev, EV_WRITE | EV_READ);
207 static void
208 end_message(struct http_conn *conn, enum http_conn_error err)
210 if (conn->firstline)
211 mem_free(conn->firstline);
212 if (conn->headers)
213 headers_clear(conn->headers);
215 if (err != ERROR_NONE || !conn->persistent) {
216 conn->state = HTTP_STATE_MANGLED;
217 bufferevent_disable(conn->bev, EV_WRITE | EV_READ);
218 } else
219 begin_message(conn);
221 if (err != ERROR_NONE)
222 METHOD1(conn, on_error, err);
223 else
224 METHOD0(conn, on_msg_complete);
227 static struct http_request *
228 build_request(struct http_conn *conn)
230 struct http_request *req;
231 struct token_list tokens;
232 struct token *method, *url, *vers;
233 enum http_method m;
234 enum http_version v;
235 struct url *u = NULL;
236 size_t ntokens;
238 assert(conn->type == HTTP_CLIENT);
240 TAILQ_INIT(&tokens);
241 req = NULL;
243 ntokens = tokenize(conn->firstline, " ", 4, &tokens);
244 if (ntokens != 3)
245 goto out;
247 method = TAILQ_FIRST(&tokens);
248 url = TAILQ_NEXT(method, next);
249 vers = TAILQ_NEXT(url, next);
250 u = url_tokenize(url->token);
252 if (!u || method_from_string(&m, method->token) < 0 ||
253 version_from_string(&v, vers->token) < 0)
254 goto out;
256 req = mem_calloc(1, sizeof(*req));
257 req->meth = m;
258 req->vers = v;
259 req->url = u;
260 u = NULL;
261 req->headers = conn->headers;
263 out:
264 url_free(u);
265 token_list_clear(&tokens);
267 return req;
270 static struct http_response *
271 build_response(struct http_conn *conn)
273 struct http_response *resp;
274 struct token_list tokens;
275 struct token *vers, *code, *reason;
276 enum http_version v;
277 int c;
278 size_t ntokens;
280 assert(conn->type == HTTP_SERVER);
282 TAILQ_INIT(&tokens);
283 resp = NULL;
285 ntokens = tokenize(conn->firstline, " ", 2, &tokens);
286 if (ntokens != 3)
287 goto out;
289 vers = TAILQ_FIRST(&tokens);
290 code = TAILQ_NEXT(vers, next);
291 reason = TAILQ_NEXT(code, next);
292 c = atoi(code->token);
294 if (version_from_string(&v, vers->token) < 0 || c < 100 || c > 999)
295 goto out;
297 resp = mem_calloc(1, sizeof(*resp));
298 resp->vers = v;
299 resp->code = c;
300 resp->reason = reason->token;
301 reason->token = NULL; /* so token_list_clear will skip this */
302 resp->headers = conn->headers;
304 out:
305 token_list_clear(&tokens);
307 return resp;
310 /* return -1 failure, 0 incomplete, 1 ok */
311 static int
312 parse_chunk_len(struct http_conn *conn)
314 struct evbuffer *inbuf = bufferevent_get_input(conn->bev);
315 char *line;
316 ev_int64_t len;
318 while ((line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF))) {
319 if (*line == '\0') {
320 mem_free(line);
321 continue;
324 len = get_int(line, 16);
325 if (len < 0) {
326 mem_free(line);
327 log_warn("parse_chunk_len: invalid chunk len");
328 return -1;
331 conn->data_remaining = len;
332 return 1;
335 return 0;
338 static void
339 read_chunk(struct http_conn *conn)
341 struct evbuffer *inbuf = bufferevent_get_input(conn->bev);
342 size_t len = evbuffer_get_length(inbuf);
343 char *line;
345 if (conn->data_remaining < 0) {
346 if (parse_chunk_len(conn) < 0)
347 end_message(conn, ERROR_CHUNK_PARSE_FAILED);
348 } else if (conn->data_remaining == 0) {
349 line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF);
350 if (line) {
351 /* XXX doesn't handle trailers */
352 if (*line != '\0')
353 log_warn("http_conn: garbage after last chunk");
354 mem_free(line);
355 end_message(conn, ERROR_NONE);
356 return;
358 } else {
359 /* XXX should mind potential overflow */
360 if (len >= (size_t)conn->data_remaining)
361 len = (size_t)conn->data_remaining;
363 evbuffer_remove_buffer(inbuf, conn->inbuf_processed, len);
364 METHOD1(conn, on_read_body, conn->inbuf_processed);
365 conn->data_remaining -= len;
367 if (conn->data_remaining == 0)
368 conn->data_remaining = -1;
372 static void
373 read_body(struct http_conn *conn)
375 struct evbuffer *inbuf = bufferevent_get_input(conn->bev);
376 size_t len;
378 assert(conn->has_body);
380 if (conn->te == TE_CHUNKED) {
381 read_chunk(conn);
382 return;
385 len = evbuffer_get_length(inbuf);
386 if (len) {
387 /* XXX should mind potential overflow */
388 if (conn->data_remaining >= 0 &&
389 len > (size_t)conn->data_remaining) {
390 len = (size_t)conn->data_remaining;
391 evbuffer_remove_buffer(inbuf, conn->inbuf_processed, len);
392 METHOD1(conn, on_read_body, conn->inbuf_processed);
393 } else {
394 evbuffer_add_buffer(conn->inbuf_processed, inbuf);
395 METHOD1(conn, on_read_body, conn->inbuf_processed);
398 conn->data_remaining -= len;
399 if (conn->data_remaining == 0)
400 end_message(conn, ERROR_NONE);
404 static void
405 check_headers(struct http_conn *conn, struct http_request *req,
406 struct http_response *resp)
408 enum http_version vers;
409 int persistent;
410 char *val;
412 conn->te = TE_IDENTITY;
413 conn->has_body = 1;
414 conn->msg_complete_on_eof = 0;
415 conn->data_remaining = -1;
417 if (conn->type == HTTP_CLIENT) {
418 vers = req->vers;
419 conn->has_body = 0;
420 if (req->meth == METH_POST ||
421 req->meth == METH_PUT)
422 conn->has_body = 1;
423 } else { /* server */
424 vers = resp->vers;
425 if ((resp->code >= 100 && resp->code < 200) ||
426 resp->code == 204 || resp->code == 205 ||
427 resp->code == 304)
428 conn->has_body = 0;
431 /* check headers */
432 if (conn->has_body) {
433 val = headers_find(conn->headers, "transfer-encoding");
434 if (val) {
435 if (!evutil_ascii_strcasecmp(val, "chunked"))
436 conn->te = TE_CHUNKED;
437 mem_free(val);
440 if (conn->te != TE_CHUNKED) {
441 val = headers_find(conn->headers, "content-length");
442 if (val) {
443 ev_int64_t iv;
444 iv = get_int(val, 10);
445 if (iv < 0)
446 log_warn("http_conn: mangled Content-Length");
447 else
448 conn->data_remaining = iv;
449 mem_free(val);
450 if (conn->data_remaining == 0)
451 conn->has_body = 0;
452 } else {
453 conn->msg_complete_on_eof = 1;
457 if (conn->type == HTTP_CLIENT && conn->data_remaining < 0 &&
458 conn->te != TE_CHUNKED) {
459 METHOD1(conn, on_error,
460 ERROR_CLIENT_POST_WITHOUT_LENGTH);
461 return;
465 assert(vers != HTTP_UNKNOWN);
467 persistent = 0;
468 if (!conn->msg_complete_on_eof && vers == HTTP_11)
469 persistent = 1;
471 if (conn->vers != HTTP_UNKNOWN && conn->vers != vers) {
472 log_warn("http_conn: http version changed!");
473 persistent = 0;
475 conn->vers = vers;
477 if (persistent) {
478 val = headers_find(conn->headers, "connection");
479 if (val) {
480 if (evutil_ascii_strcasecmp(val, "close"))
481 persistent = 0;
482 mem_free(val);
485 conn->persistent = persistent;
487 if (req)
488 req->te = conn->te;
489 else if (resp)
490 resp->te = conn->te;
493 static void
494 read_headers(struct http_conn *conn)
496 int failed = 0;
497 struct evbuffer *inbuf = bufferevent_get_input(conn->bev);
498 struct http_request *req = NULL;
499 struct http_response *resp = NULL;
501 assert(conn->state == HTTP_STATE_READ_HEADERS);
503 switch (headers_load(conn->headers, inbuf)) {
504 case -1:
505 end_message(conn, ERROR_HEADER_PARSE_FAILED);
506 return;
507 case 0:
508 return;
509 /* case 1: finished, fall thru */
512 assert(conn->firstline);
514 if (conn->type == HTTP_CLIENT) {
515 req = build_request(conn);
516 if (!req)
517 failed = 1;
518 } else {
519 resp = build_response(conn);
520 if (!resp)
521 failed = 1;
524 mem_free(conn->firstline);
525 conn->firstline = NULL;
527 if (failed) {
528 assert(!req && !resp);
529 end_message(conn, ERROR_HEADER_PARSE_FAILED);
530 return;
533 check_headers(conn, req, resp);
534 conn->headers = NULL;
536 /* ownership of req or resp is now passed on */
537 if (req)
538 METHOD1(conn, on_client_request, req);
539 if (resp)
540 METHOD1(conn, on_server_response, resp);
542 if (!conn->has_body)
543 end_message(conn, ERROR_NONE);
544 else
545 conn->state = HTTP_STATE_READ_BODY;
548 static void
549 http_errorcb(struct bufferevent *bev, short what, void *_conn)
551 enum http_state state;
552 struct http_conn *conn = _conn;
554 if (conn->state == HTTP_STATE_CONNECTING) {
555 if (what & BEV_EVENT_CONNECTED) {
556 begin_message(conn);
557 METHOD0(conn, on_connect);
558 } else {
559 conn->state = HTTP_STATE_MANGLED;
560 METHOD1(conn, on_error, ERROR_CONNECT_FAILED);
562 return;
565 assert(!(what & BEV_EVENT_CONNECTED));
567 state = conn->state;
568 conn->state = HTTP_STATE_MANGLED;
570 if (what & BEV_EVENT_WRITING) {
571 end_message(conn, ERROR_WRITE_FAILED);
572 return;
575 switch (state) {
576 case HTTP_STATE_IDLE:
577 end_message(conn, ERROR_IDLE_CONN_TIMEDOUT);
578 break;
579 case HTTP_STATE_READ_FIRSTLINE:
580 case HTTP_STATE_READ_HEADERS:
581 end_message(conn, ERROR_INCOMPLETE_HEADERS);
582 break;
583 case HTTP_STATE_READ_BODY:
584 if ((what & BEV_EVENT_EOF) && conn->msg_complete_on_eof)
585 end_message(conn, ERROR_NONE);
586 else
587 end_message(conn, ERROR_INCOMPLETE_BODY);
588 break;
589 default:
590 log_fatal("http_conn: errorcb called in invalid state");
594 static void
595 process_one_message(struct http_conn *conn)
597 struct evbuffer *inbuf = bufferevent_get_input(conn->bev);
599 switch (conn->state) {
600 case HTTP_STATE_IDLE:
601 conn->state = HTTP_STATE_READ_FIRSTLINE;
602 // XXX should remove idle timeout at this point?
603 /* fallthru... */
604 case HTTP_STATE_READ_FIRSTLINE:
605 assert(conn->firstline == NULL);
606 conn->firstline = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF);
607 if (conn->firstline)
608 conn->state = HTTP_STATE_READ_HEADERS;
609 break;
610 case HTTP_STATE_READ_HEADERS:
611 read_headers(conn);
612 break;
613 case HTTP_STATE_READ_BODY:
614 read_body(conn);
615 break;
616 default:
617 log_fatal("http_conn: read cb called in invalid state");
621 static void
622 process_inbuf(struct http_conn *conn)
624 struct evbuffer *inbuf = bufferevent_get_input(conn->bev);
626 do {
627 process_one_message(conn);
628 } while (!conn->read_paused &&
629 evbuffer_get_length(inbuf) > 0);
632 static void
633 http_readcb(struct bufferevent *bev, void *_conn)
635 process_inbuf(_conn);
638 static void
639 http_writecb(struct bufferevent *bev, void *_conn)
641 struct http_conn *conn = _conn;
642 struct evbuffer *outbuf = bufferevent_get_output(bev);
644 if (conn->is_choaked) {
645 bufferevent_setwatermark(bev, EV_WRITE, 0, 0);
646 conn->is_choaked = 0;
647 METHOD0(conn, on_write_more);
648 } else if (evbuffer_get_length(outbuf) == 0)
649 METHOD0(conn, on_flush);
652 struct http_conn *
653 http_conn_new(struct event_base *base, evutil_socket_t sock,
654 enum http_type type, const struct http_cbs *cbs, void *cbarg)
656 struct http_conn *conn;
658 conn = mem_calloc(1, sizeof(*conn));
659 conn->type = type;
660 conn->cbs = cbs;
661 conn->cbarg = cbarg;
662 conn->bev = bufferevent_socket_new(base, sock,
663 BEV_OPT_CLOSE_ON_FREE);
664 if (!conn->bev)
665 log_fatal("http_conn: failed to create bufferevent");
667 conn->inbuf_processed = evbuffer_new();
668 if (!conn->inbuf_processed)
669 log_fatal("http_conn: failed to create evbuffer");
671 bufferevent_setcb(conn->bev, http_readcb, http_writecb,
672 http_errorcb, conn);
674 if (sock >= 0)
675 begin_message(conn);
677 return conn;
681 http_conn_connect(struct http_conn *conn, struct evdns_base *dns,
682 int family, const char *host, int port)
684 // XXX need SOCKS
685 conn->state = HTTP_STATE_CONNECTING;
686 return bufferevent_socket_connect_hostname(conn->bev, dns, family,
687 host, port);
690 void
691 http_conn_free(struct http_conn *conn)
693 bufferevent_free(conn->bev);
694 evbuffer_free(conn->inbuf_processed);
695 mem_free(conn);
698 void
699 http_conn_write_request(struct http_conn *conn, struct http_request *req)
701 struct evbuffer *outbuf;
703 assert(conn->type == HTTP_SERVER);
705 headers_remove(req->headers, "connection");
707 conn->output_te = req->te;
708 req->vers = HTTP_11;
710 outbuf = bufferevent_get_output(conn->bev);
712 evbuffer_add_printf(outbuf, "%s %s %s\r\n",
713 method_to_string(req->meth),
714 req->url->query,
715 version_to_string(req->vers));
717 headers_dump(req->headers, outbuf);
720 void
721 http_conn_write_response(struct http_conn *conn, struct http_response *resp)
723 struct evbuffer *outbuf;
725 assert(conn->type == HTTP_CLIENT);
726 assert(conn->vers != HTTP_UNKNOWN);
728 headers_remove(req->headers, "connection");
729 resp->vers = conn->vers;
731 conn->output_te = resp->te;
732 if (conn->vers == HTTP_10) {
733 conn->output_te = TE_IDENTITY;
734 headers_remove(req->headers, "transfer-encoding");
735 headers_add_key_val(req->headers, "Connection", "close");
738 outbuf = bufferevent_get_output(conn->bev);
740 evbuffer_add_printf(outbuf, "%s %d %s\r\n",
741 version_to_string(conn->vers),
742 resp->code,
743 resp->reason);
745 headers_dump(resp->headers, outbuf);
749 http_conn_write_buf(struct http_conn *conn, struct evbuffer *buf)
751 struct evbuffer *outbuf;
753 outbuf = bufferevent_get_output(conn->bev);
755 if (conn->output_te == TE_CHUNKED)
756 evbuffer_add_printf("%x\r\n",
757 (unsigned)evbuffer_get_length(outbuf));
758 evbuffer_add_buffer(outbuf, buf);
759 if (conn->output_te == TE_CHUNKED)
760 evbuffer_add(outbuf, "\r\n", 2);
762 /* have we choaked? */
763 if (evbuffer_get_length(outbuf) > max_write_backlog) {
764 bufferevent_setwatermark(conn->bev, EV_WRITE,
765 max_write_backlog / 2, 0);
766 conn->is_choaked = 1;
767 return 0;
770 return 1;
773 void
774 http_conn_write_finished(struct http_conn *conn)
776 if (conn->output_te == TE_CHUNKED)
777 bufferevent_write(conn->bev, "0\r\n\r\n", 5);
778 conn->output_te = TE_IDENTITY;
783 http_conn_current_message_has_body(struct http_conn *conn)
785 return conn->has_body;
788 void
789 http_conn_set_current_message_bodyless(struct http_conn *conn)
791 assert(conn->type == HTTP_SERVER);
792 conn->has_body = 0;
796 http_conn_is_persistent(struct http_conn *conn)
798 return conn->persistent;
801 void
802 http_conn_stop_reading(struct http_conn *conn)
804 bufferevent_disable(conn->bev, EV_READ);
805 conn->read_paused = 1;
808 void
809 http_conn_start_reading(struct http_conn *conn)
811 struct evbuffer *inbuf = bufferevent_get_input(conn->bev);
813 bufferevent_enable(conn->bev, EV_READ);
814 conn->read_paused = 0;
815 // XXX this might cause recursion
816 if (evbuffer_get_length(inbuf) > 0)
817 process_inbuf(conn);
820 void
821 http_conn_flush(struct http_conn *conn)
823 struct evbuffer *outbuf = bufferevent_get_outbuf(conn->bev);
825 // XXX this might cause recursion
826 if (evbuffer_get_length(outbuf) == 0)
827 METHOD0(conn, on_flush);
830 // XXX this should support more descriptive messages
831 void
832 http_conn_send_error(struct http_conn *conn, int code)
834 char length[64];
835 struct evbuffer *msg;
836 struct http_response resp;
837 struct header_list headers;
839 assert(conn->type == HTTP_CLIENT);
841 TAILQ_INIT(headers);
842 msg = evbuffer_new();
843 resp.headers = &headers;
845 resp.te = TE_IDENTITY;
846 resp.vers = HTTP_11;
847 resp.code = code;
848 resp.reason = code_to_reason_string(code);
850 evbuffer_printf(msg,
851 "<html>\n"
852 "<head>\n"
853 "<title>%s</title>\n"
854 "</head>\n"
855 "<body>\n"
856 "<h1>%d %s</h1>\n"
857 "</body>\n"
858 "</html>\n",
859 resp.reason, code, resp.reason);
861 evutil_snprintf(length, sizeof(length), "%u",
862 (unsigned)evbuffer_get_length(msg));
863 headers_add_key_val(&headers, "Content-Type", "text/html");
864 headers_add_key_val(&headers, "Content-Length", length);
866 http_conn_write_response(conn, &resp);
867 http_conn_write_buf(conn, msg);
868 headers_clear(&headers);
869 evbuffer_free(msg);
872 void
873 http_request_free(struct http_request *req)
875 url_free(req->url);
876 headers_clear(req->headers);
877 mem_free(req);
880 void
881 http_response_free(struct http_response *resp)
883 headers_clear(resp->headers);
884 mem_free(resp->headers);
885 mem_free(resp->reason);
886 mem_free(resp);
889 #ifdef TEST_HTTP
890 #include <netinet/in.h>
891 #include <stdio.h>
892 #include <event2/dns.h>
894 static void
895 proxy_connected(struct http_conn *conn, void *arg)
897 struct http_request req;
898 struct header_list headers;
899 struct evbuffer *buf;
901 TAILQ_INIT(&headers);
902 req.meth = METH_GET;
903 req.url = arg;
904 req.vers = HTTP_11;
905 req.headers = &headers;
907 buf = evbuffer_new();
908 evbuffer_add_printf(buf, "Host: %s\r\n\r\n", req.url->host);
909 headers_load(&headers, buf);
910 evbuffer_free(buf);
912 http_conn_write_request(conn, &req);
915 static void
916 proxy_error(struct http_conn *conn, enum http_conn_error err, void *arg)
918 fprintf(stderr, "error %d\n", err);
919 http_conn_free(conn);
922 static void
923 proxy_response(struct http_conn *conn, struct http_response *resp, void *arg)
925 struct evbuffer *buf;
927 fprintf(stderr, "response: %s, %d, %s\n",
928 version_to_string(resp->vers),
929 resp->code,
930 resp->reason);
932 buf = evbuffer_new();
933 headers_dump(resp->headers, buf);
934 fwrite(evbuffer_pullup(buf, evbuffer_get_length(buf)), evbuffer_get_length(buf), 1, stderr);
935 evbuffer_free(buf);
938 static void
939 proxy_read_body(struct http_conn *conn, struct evbuffer *buf, void *arg)
941 size_t len = evbuffer_get_length(buf);
942 fwrite(evbuffer_pullup(buf, len), len, 1, stderr);
943 evbuffer_drain(buf, len);
946 static void
947 proxy_msg_complete(struct http_conn *conn, void *arg)
949 fprintf(stderr, "\n...MSG COMPLETE...\n");
952 static void
953 proxy_write_more(struct http_conn *conn, void *arg)
957 static struct http_cbs test_proxy_cbs = {
958 proxy_connected,
959 proxy_error,
961 proxy_response,
962 proxy_read_body,
963 proxy_msg_complete,
964 proxy_write_more
968 main(int argc, char **argv)
970 struct event_base *base;
971 struct evdns_base *dns;
972 struct http_conn *http;
973 struct url *url;
975 url = url_tokenize(argv[1]);
976 if (!url)
977 return 0;
979 if (url->port < 0)
980 url->port = 80;
982 base = event_base_new();
983 dns = evdns_base_new(base, 1);
985 http = http_conn_new(base, -1, HTTP_SERVER, &test_proxy_cbs, url);
986 http_conn_connect(http, dns, AF_UNSPEC, url->host, url->port);
988 event_base_dispatch(base);
990 return 0;
993 #endif