Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / bsd / libevent / dist / test / regress_http.c
blob9685afd996591a364afd25fc711f4246970a6b5a
1 /* $NetBSD$ */
2 /*
3 * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #ifdef WIN32
30 #include <winsock2.h>
31 #include <windows.h>
32 #endif
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #ifdef HAVE_SYS_TIME_H
41 #include <sys/time.h>
42 #endif
43 #include <sys/queue.h>
44 #ifndef WIN32
45 #include <sys/socket.h>
46 #include <signal.h>
47 #include <unistd.h>
48 #include <netdb.h>
49 #endif
50 #include <fcntl.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <errno.h>
56 #include "event.h"
57 #include "evhttp.h"
58 #include "log.h"
59 #include "http-internal.h"
61 extern int pair[];
62 extern int test_ok;
64 static struct evhttp *http;
65 /* set if a test needs to call loopexit on a base */
66 static struct event_base *base;
68 void http_suite(void);
70 void http_basic_cb(struct evhttp_request *req, void *arg);
71 static void http_chunked_cb(struct evhttp_request *req, void *arg);
72 void http_post_cb(struct evhttp_request *req, void *arg);
73 void http_dispatcher_cb(struct evhttp_request *req, void *arg);
74 static void http_large_delay_cb(struct evhttp_request *req, void *arg);
76 static struct evhttp *
77 http_setup(short *pport, struct event_base *base)
79 int i;
80 struct evhttp *myhttp;
81 short port = -1;
83 /* Try a few different ports */
84 myhttp = evhttp_new(base);
85 for (i = 0; i < 50; ++i) {
86 if (evhttp_bind_socket(myhttp, "127.0.0.1", 8080 + i) != -1) {
87 port = 8080 + i;
88 break;
92 if (port == -1)
93 event_errx(1, "Could not start web server");
95 /* Register a callback for certain types of requests */
96 evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL);
97 evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, NULL);
98 evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL);
99 evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, NULL);
100 evhttp_set_cb(myhttp, "/", http_dispatcher_cb, NULL);
102 *pport = port;
103 return (myhttp);
106 #ifndef NI_MAXSERV
107 #define NI_MAXSERV 1024
108 #endif
110 static int
111 http_connect(const char *address, u_short port)
113 /* Stupid code for connecting */
114 #ifdef WIN32
115 struct hostent *he;
116 struct sockaddr_in sin;
117 #else
118 struct addrinfo ai, *aitop;
119 char strport[NI_MAXSERV];
120 #endif
121 struct sockaddr *sa;
122 int slen;
123 int fd;
125 #ifdef WIN32
126 if (!(he = gethostbyname(address))) {
127 event_warn("gethostbyname");
129 memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length);
130 sin.sin_family = AF_INET;
131 sin.sin_port = htons(port);
132 slen = sizeof(struct sockaddr_in);
133 sa = (struct sockaddr*)&sin;
134 #else
135 memset(&ai, 0, sizeof (ai));
136 ai.ai_family = AF_INET;
137 ai.ai_socktype = SOCK_STREAM;
138 snprintf(strport, sizeof (strport), "%d", port);
139 if (getaddrinfo(address, strport, &ai, &aitop) != 0) {
140 event_warn("getaddrinfo");
141 return (-1);
143 sa = aitop->ai_addr;
144 slen = aitop->ai_addrlen;
145 #endif
147 fd = socket(AF_INET, SOCK_STREAM, 0);
148 if (fd == -1)
149 event_err(1, "socket failed");
151 if (connect(fd, sa, slen) == -1)
152 event_err(1, "connect failed");
154 #ifndef WIN32
155 freeaddrinfo(aitop);
156 #endif
158 return (fd);
161 static void
162 http_readcb(struct bufferevent *bev, void *arg)
164 const char *what = "This is funny";
166 event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input)));
168 if (evbuffer_find(bev->input,
169 (const unsigned char*) what, strlen(what)) != NULL) {
170 struct evhttp_request *req = evhttp_request_new(NULL, NULL);
171 enum message_read_status done;
173 req->kind = EVHTTP_RESPONSE;
174 done = evhttp_parse_firstline(req, bev->input);
175 if (done != ALL_DATA_READ)
176 goto out;
178 done = evhttp_parse_headers(req, bev->input);
179 if (done != ALL_DATA_READ)
180 goto out;
182 if (done == 1 &&
183 evhttp_find_header(req->input_headers,
184 "Content-Type") != NULL)
185 test_ok++;
187 out:
188 evhttp_request_free(req);
189 bufferevent_disable(bev, EV_READ);
190 if (base)
191 event_base_loopexit(base, NULL);
192 else
193 event_loopexit(NULL);
197 static void
198 http_writecb(struct bufferevent *bev, void *arg)
200 if (EVBUFFER_LENGTH(bev->output) == 0) {
201 /* enable reading of the reply */
202 bufferevent_enable(bev, EV_READ);
203 test_ok++;
207 static void
208 http_errorcb(struct bufferevent *bev, short what, void *arg)
210 test_ok = -2;
211 event_loopexit(NULL);
214 void
215 http_basic_cb(struct evhttp_request *req, void *arg)
217 struct evbuffer *evb = evbuffer_new();
218 int empty = evhttp_find_header(req->input_headers, "Empty") != NULL;
219 event_debug(("%s: called\n", __func__));
220 evbuffer_add_printf(evb, "This is funny");
222 /* For multi-line headers test */
224 const char *multi =
225 evhttp_find_header(req->input_headers,"X-multi");
226 if (multi) {
227 if (strcmp("END", multi + strlen(multi) - 3) == 0)
228 test_ok++;
229 if (evhttp_find_header(req->input_headers, "X-Last"))
230 test_ok++;
234 /* injecting a bad content-length */
235 if (evhttp_find_header(req->input_headers, "X-Negative"))
236 evhttp_add_header(req->output_headers,
237 "Content-Length", "-100");
239 /* allow sending of an empty reply */
240 evhttp_send_reply(req, HTTP_OK, "Everything is fine",
241 !empty ? evb : NULL);
243 evbuffer_free(evb);
246 static char const* const CHUNKS[] = {
247 "This is funny",
248 "but not hilarious.",
249 "bwv 1052"
252 struct chunk_req_state {
253 struct evhttp_request *req;
254 int i;
257 static void
258 http_chunked_trickle_cb(int fd, short events, void *arg)
260 struct evbuffer *evb = evbuffer_new();
261 struct chunk_req_state *state = arg;
262 struct timeval when = { 0, 0 };
264 evbuffer_add_printf(evb, "%s", CHUNKS[state->i]);
265 evhttp_send_reply_chunk(state->req, evb);
266 evbuffer_free(evb);
268 if (++state->i < sizeof(CHUNKS)/sizeof(CHUNKS[0])) {
269 event_once(-1, EV_TIMEOUT,
270 http_chunked_trickle_cb, state, &when);
271 } else {
272 evhttp_send_reply_end(state->req);
273 free(state);
277 static void
278 http_chunked_cb(struct evhttp_request *req, void *arg)
280 struct timeval when = { 0, 0 };
281 struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state));
282 event_debug(("%s: called\n", __func__));
284 memset(state, 0, sizeof(struct chunk_req_state));
285 state->req = req;
287 /* generate a chunked reply */
288 evhttp_send_reply_start(req, HTTP_OK, "Everything is fine");
290 /* but trickle it across several iterations to ensure we're not
291 * assuming it comes all at once */
292 event_once(-1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when);
295 static void
296 http_complete_write(int fd, short what, void *arg)
298 struct bufferevent *bev = arg;
299 const char *http_request = "host\r\n"
300 "Connection: close\r\n"
301 "\r\n";
302 bufferevent_write(bev, http_request, strlen(http_request));
305 static void
306 http_basic_test(void)
308 struct timeval tv;
309 struct bufferevent *bev;
310 int fd;
311 const char *http_request;
312 short port = -1;
314 test_ok = 0;
315 fprintf(stdout, "Testing Basic HTTP Server: ");
317 http = http_setup(&port, NULL);
319 /* bind to a second socket */
320 if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) {
321 fprintf(stdout, "FAILED (bind)\n");
322 exit(1);
325 fd = http_connect("127.0.0.1", port);
327 /* Stupid thing to send a request */
328 bev = bufferevent_new(fd, http_readcb, http_writecb,
329 http_errorcb, NULL);
331 /* first half of the http request */
332 http_request =
333 "GET /test HTTP/1.1\r\n"
334 "Host: some";
336 bufferevent_write(bev, http_request, strlen(http_request));
337 timerclear(&tv);
338 tv.tv_usec = 10000;
339 event_once(-1, EV_TIMEOUT, http_complete_write, bev, &tv);
341 event_dispatch();
343 if (test_ok != 3) {
344 fprintf(stdout, "FAILED\n");
345 exit(1);
348 /* connect to the second port */
349 bufferevent_free(bev);
350 EVUTIL_CLOSESOCKET(fd);
352 fd = http_connect("127.0.0.1", port + 1);
354 /* Stupid thing to send a request */
355 bev = bufferevent_new(fd, http_readcb, http_writecb,
356 http_errorcb, NULL);
358 http_request =
359 "GET /test HTTP/1.1\r\n"
360 "Host: somehost\r\n"
361 "Connection: close\r\n"
362 "\r\n";
364 bufferevent_write(bev, http_request, strlen(http_request));
366 event_dispatch();
368 bufferevent_free(bev);
369 EVUTIL_CLOSESOCKET(fd);
371 evhttp_free(http);
373 if (test_ok != 5) {
374 fprintf(stdout, "FAILED\n");
375 exit(1);
378 fprintf(stdout, "OK\n");
381 static struct evhttp_connection *delayed_client;
383 static void
384 http_delay_reply(int fd, short what, void *arg)
386 struct evhttp_request *req = arg;
388 evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL);
390 ++test_ok;
393 static void
394 http_large_delay_cb(struct evhttp_request *req, void *arg)
396 struct timeval tv;
397 timerclear(&tv);
398 tv.tv_sec = 3;
400 event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv);
402 /* here we close the client connection which will cause an EOF */
403 evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF);
406 void http_request_done(struct evhttp_request *, void *);
407 void http_request_empty_done(struct evhttp_request *, void *);
409 static void
410 http_connection_test(int persistent)
412 short port = -1;
413 struct evhttp_connection *evcon = NULL;
414 struct evhttp_request *req = NULL;
416 test_ok = 0;
417 fprintf(stdout, "Testing Request Connection Pipeline %s: ",
418 persistent ? "(persistent)" : "");
420 http = http_setup(&port, NULL);
422 evcon = evhttp_connection_new("127.0.0.1", port);
423 if (evcon == NULL) {
424 fprintf(stdout, "FAILED\n");
425 exit(1);
429 * At this point, we want to schedule a request to the HTTP
430 * server using our make request method.
433 req = evhttp_request_new(http_request_done, NULL);
435 /* Add the information that we care about */
436 evhttp_add_header(req->output_headers, "Host", "somehost");
438 /* We give ownership of the request to the connection */
439 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
440 fprintf(stdout, "FAILED\n");
441 exit(1);
444 event_dispatch();
446 if (test_ok != 1) {
447 fprintf(stdout, "FAILED\n");
448 exit(1);
451 /* try to make another request over the same connection */
452 test_ok = 0;
454 req = evhttp_request_new(http_request_done, NULL);
456 /* Add the information that we care about */
457 evhttp_add_header(req->output_headers, "Host", "somehost");
460 * if our connections are not supposed to be persistent; request
461 * a close from the server.
463 if (!persistent)
464 evhttp_add_header(req->output_headers, "Connection", "close");
466 /* We give ownership of the request to the connection */
467 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
468 fprintf(stdout, "FAILED\n");
469 exit(1);
472 event_dispatch();
474 /* make another request: request empty reply */
475 test_ok = 0;
477 req = evhttp_request_new(http_request_empty_done, NULL);
479 /* Add the information that we care about */
480 evhttp_add_header(req->output_headers, "Empty", "itis");
482 /* We give ownership of the request to the connection */
483 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
484 fprintf(stdout, "FAILED\n");
485 exit(1);
488 event_dispatch();
490 if (test_ok != 1) {
491 fprintf(stdout, "FAILED\n");
492 exit(1);
495 evhttp_connection_free(evcon);
496 evhttp_free(http);
498 fprintf(stdout, "OK\n");
501 void
502 http_request_done(struct evhttp_request *req, void *arg)
504 const char *what = "This is funny";
506 if (req->response_code != HTTP_OK) {
507 fprintf(stderr, "FAILED\n");
508 exit(1);
511 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
512 fprintf(stderr, "FAILED\n");
513 exit(1);
516 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
517 fprintf(stderr, "FAILED\n");
518 exit(1);
521 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
522 fprintf(stderr, "FAILED\n");
523 exit(1);
526 test_ok = 1;
527 event_loopexit(NULL);
530 /* test date header and content length */
532 void
533 http_request_empty_done(struct evhttp_request *req, void *arg)
535 if (req->response_code != HTTP_OK) {
536 fprintf(stderr, "FAILED\n");
537 exit(1);
540 if (evhttp_find_header(req->input_headers, "Date") == NULL) {
541 fprintf(stderr, "FAILED\n");
542 exit(1);
546 if (evhttp_find_header(req->input_headers, "Content-Length") == NULL) {
547 fprintf(stderr, "FAILED\n");
548 exit(1);
551 if (strcmp(evhttp_find_header(req->input_headers, "Content-Length"),
552 "0")) {
553 fprintf(stderr, "FAILED\n");
554 exit(1);
557 if (EVBUFFER_LENGTH(req->input_buffer) != 0) {
558 fprintf(stderr, "FAILED\n");
559 exit(1);
562 test_ok = 1;
563 event_loopexit(NULL);
567 * HTTP DISPATCHER test
570 void
571 http_dispatcher_cb(struct evhttp_request *req, void *arg)
574 struct evbuffer *evb = evbuffer_new();
575 event_debug(("%s: called\n", __func__));
576 evbuffer_add_printf(evb, "DISPATCHER_TEST");
578 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
580 evbuffer_free(evb);
583 static void
584 http_dispatcher_test_done(struct evhttp_request *req, void *arg)
586 const char *what = "DISPATCHER_TEST";
588 if (req->response_code != HTTP_OK) {
589 fprintf(stderr, "FAILED\n");
590 exit(1);
593 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
594 fprintf(stderr, "FAILED (content type)\n");
595 exit(1);
598 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
599 fprintf(stderr, "FAILED (length %zu vs %zu)\n",
600 EVBUFFER_LENGTH(req->input_buffer), strlen(what));
601 exit(1);
604 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
605 fprintf(stderr, "FAILED (data)\n");
606 exit(1);
609 test_ok = 1;
610 event_loopexit(NULL);
613 static void
614 http_dispatcher_test(void)
616 short port = -1;
617 struct evhttp_connection *evcon = NULL;
618 struct evhttp_request *req = NULL;
620 test_ok = 0;
621 fprintf(stdout, "Testing HTTP Dispatcher: ");
623 http = http_setup(&port, NULL);
625 evcon = evhttp_connection_new("127.0.0.1", port);
626 if (evcon == NULL) {
627 fprintf(stdout, "FAILED\n");
628 exit(1);
631 /* also bind to local host */
632 evhttp_connection_set_local_address(evcon, "127.0.0.1");
635 * At this point, we want to schedule an HTTP GET request
636 * server using our make request method.
639 req = evhttp_request_new(http_dispatcher_test_done, NULL);
640 if (req == NULL) {
641 fprintf(stdout, "FAILED\n");
642 exit(1);
645 /* Add the information that we care about */
646 evhttp_add_header(req->output_headers, "Host", "somehost");
648 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) {
649 fprintf(stdout, "FAILED\n");
650 exit(1);
653 event_dispatch();
655 evhttp_connection_free(evcon);
656 evhttp_free(http);
658 if (test_ok != 1) {
659 fprintf(stdout, "FAILED: %d\n", test_ok);
660 exit(1);
663 fprintf(stdout, "OK\n");
667 * HTTP POST test.
670 void http_postrequest_done(struct evhttp_request *, void *);
672 #define POST_DATA "Okay. Not really printf"
674 static void
675 http_post_test(void)
677 short port = -1;
678 struct evhttp_connection *evcon = NULL;
679 struct evhttp_request *req = NULL;
681 test_ok = 0;
682 fprintf(stdout, "Testing HTTP POST Request: ");
684 http = http_setup(&port, NULL);
686 evcon = evhttp_connection_new("127.0.0.1", port);
687 if (evcon == NULL) {
688 fprintf(stdout, "FAILED\n");
689 exit(1);
693 * At this point, we want to schedule an HTTP POST request
694 * server using our make request method.
697 req = evhttp_request_new(http_postrequest_done, NULL);
698 if (req == NULL) {
699 fprintf(stdout, "FAILED\n");
700 exit(1);
703 /* Add the information that we care about */
704 evhttp_add_header(req->output_headers, "Host", "somehost");
705 evbuffer_add_printf(req->output_buffer, POST_DATA);
707 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
708 fprintf(stdout, "FAILED\n");
709 exit(1);
712 event_dispatch();
714 evhttp_connection_free(evcon);
715 evhttp_free(http);
717 if (test_ok != 1) {
718 fprintf(stdout, "FAILED: %d\n", test_ok);
719 exit(1);
722 fprintf(stdout, "OK\n");
725 void
726 http_post_cb(struct evhttp_request *req, void *arg)
728 struct evbuffer *evb;
729 event_debug(("%s: called\n", __func__));
731 /* Yes, we are expecting a post request */
732 if (req->type != EVHTTP_REQ_POST) {
733 fprintf(stdout, "FAILED (post type)\n");
734 exit(1);
737 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(POST_DATA)) {
738 fprintf(stdout, "FAILED (length: %zu vs %zu)\n",
739 EVBUFFER_LENGTH(req->input_buffer), strlen(POST_DATA));
740 exit(1);
743 if (memcmp(EVBUFFER_DATA(req->input_buffer), POST_DATA,
744 strlen(POST_DATA))) {
745 fprintf(stdout, "FAILED (data)\n");
746 fprintf(stdout, "Got :%s\n", EVBUFFER_DATA(req->input_buffer));
747 fprintf(stdout, "Want:%s\n", POST_DATA);
748 exit(1);
751 evb = evbuffer_new();
752 evbuffer_add_printf(evb, "This is funny");
754 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
756 evbuffer_free(evb);
759 void
760 http_postrequest_done(struct evhttp_request *req, void *arg)
762 const char *what = "This is funny";
764 if (req == NULL) {
765 fprintf(stderr, "FAILED (timeout)\n");
766 exit(1);
769 if (req->response_code != HTTP_OK) {
771 fprintf(stderr, "FAILED (response code)\n");
772 exit(1);
775 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
776 fprintf(stderr, "FAILED (content type)\n");
777 exit(1);
780 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
781 fprintf(stderr, "FAILED (length %zu vs %zu)\n",
782 EVBUFFER_LENGTH(req->input_buffer), strlen(what));
783 exit(1);
786 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
787 fprintf(stderr, "FAILED (data)\n");
788 exit(1);
791 test_ok = 1;
792 event_loopexit(NULL);
795 static void
796 http_failure_readcb(struct bufferevent *bev, void *arg)
798 const char *what = "400 Bad Request";
799 if (evbuffer_find(bev->input, (const unsigned char*) what, strlen(what)) != NULL) {
800 test_ok = 2;
801 bufferevent_disable(bev, EV_READ);
802 event_loopexit(NULL);
807 * Testing that the HTTP server can deal with a malformed request.
809 static void
810 http_failure_test(void)
812 struct bufferevent *bev;
813 int fd;
814 const char *http_request;
815 short port = -1;
817 test_ok = 0;
818 fprintf(stdout, "Testing Bad HTTP Request: ");
820 http = http_setup(&port, NULL);
822 fd = http_connect("127.0.0.1", port);
824 /* Stupid thing to send a request */
825 bev = bufferevent_new(fd, http_failure_readcb, http_writecb,
826 http_errorcb, NULL);
828 http_request = "illegal request\r\n";
830 bufferevent_write(bev, http_request, strlen(http_request));
832 event_dispatch();
834 bufferevent_free(bev);
835 EVUTIL_CLOSESOCKET(fd);
837 evhttp_free(http);
839 if (test_ok != 2) {
840 fprintf(stdout, "FAILED\n");
841 exit(1);
844 fprintf(stdout, "OK\n");
847 static void
848 close_detect_done(struct evhttp_request *req, void *arg)
850 struct timeval tv;
851 if (req == NULL || req->response_code != HTTP_OK) {
853 fprintf(stderr, "FAILED\n");
854 exit(1);
857 test_ok = 1;
859 timerclear(&tv);
860 tv.tv_sec = 3; /* longer than the http time out */
862 event_loopexit(&tv);
865 static void
866 close_detect_launch(int fd, short what, void *arg)
868 struct evhttp_connection *evcon = arg;
869 struct evhttp_request *req;
871 req = evhttp_request_new(close_detect_done, NULL);
873 /* Add the information that we care about */
874 evhttp_add_header(req->output_headers, "Host", "somehost");
876 /* We give ownership of the request to the connection */
877 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
878 fprintf(stdout, "FAILED\n");
879 exit(1);
883 static void
884 close_detect_cb(struct evhttp_request *req, void *arg)
886 struct evhttp_connection *evcon = arg;
887 struct timeval tv;
889 if (req != NULL && req->response_code != HTTP_OK) {
891 fprintf(stderr, "FAILED\n");
892 exit(1);
895 timerclear(&tv);
896 tv.tv_sec = 3; /* longer than the http time out */
898 /* launch a new request on the persistent connection in 6 seconds */
899 event_once(-1, EV_TIMEOUT, close_detect_launch, evcon, &tv);
903 static void
904 http_close_detection(int with_delay)
906 short port = -1;
907 struct evhttp_connection *evcon = NULL;
908 struct evhttp_request *req = NULL;
910 test_ok = 0;
911 fprintf(stdout, "Testing Connection Close Detection%s: ",
912 with_delay ? " (with delay)" : "");
914 http = http_setup(&port, NULL);
916 /* 2 second timeout */
917 evhttp_set_timeout(http, 2);
919 evcon = evhttp_connection_new("127.0.0.1", port);
920 if (evcon == NULL) {
921 fprintf(stdout, "FAILED\n");
922 exit(1);
925 delayed_client = evcon;
928 * At this point, we want to schedule a request to the HTTP
929 * server using our make request method.
932 req = evhttp_request_new(close_detect_cb, evcon);
934 /* Add the information that we care about */
935 evhttp_add_header(req->output_headers, "Host", "somehost");
937 /* We give ownership of the request to the connection */
938 if (evhttp_make_request(evcon,
939 req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) {
940 fprintf(stdout, "FAILED\n");
941 exit(1);
944 event_dispatch();
946 if (test_ok != 1) {
947 fprintf(stdout, "FAILED\n");
948 exit(1);
951 /* at this point, the http server should have no connection */
952 if (TAILQ_FIRST(&http->connections) != NULL) {
953 fprintf(stdout, "FAILED (left connections)\n");
954 exit(1);
957 evhttp_connection_free(evcon);
958 evhttp_free(http);
960 fprintf(stdout, "OK\n");
963 static void
964 http_highport_test(void)
966 int i = -1;
967 struct evhttp *myhttp = NULL;
969 fprintf(stdout, "Testing HTTP Server with high port: ");
971 /* Try a few different ports */
972 for (i = 0; i < 50; ++i) {
973 myhttp = evhttp_start("127.0.0.1", 65535 - i);
974 if (myhttp != NULL) {
975 fprintf(stdout, "OK\n");
976 evhttp_free(myhttp);
977 return;
981 fprintf(stdout, "FAILED\n");
982 exit(1);
985 static void
986 http_bad_header_test(void)
988 struct evkeyvalq headers;
990 fprintf(stdout, "Testing HTTP Header filtering: ");
992 TAILQ_INIT(&headers);
994 if (evhttp_add_header(&headers, "One", "Two") != 0)
995 goto fail;
997 if (evhttp_add_header(&headers, "One\r", "Two") != -1)
998 goto fail;
999 if (evhttp_add_header(&headers, "One", "Two") != 0)
1000 goto fail;
1001 if (evhttp_add_header(&headers, "One", "Two\r\n Three") != 0)
1002 goto fail;
1003 if (evhttp_add_header(&headers, "One\r", "Two") != -1)
1004 goto fail;
1005 if (evhttp_add_header(&headers, "One\n", "Two") != -1)
1006 goto fail;
1007 if (evhttp_add_header(&headers, "One", "Two\r") != -1)
1008 goto fail;
1009 if (evhttp_add_header(&headers, "One", "Two\n") != -1)
1010 goto fail;
1012 evhttp_clear_headers(&headers);
1014 fprintf(stdout, "OK\n");
1015 return;
1016 fail:
1017 fprintf(stdout, "FAILED\n");
1018 exit(1);
1021 static int validate_header(
1022 const struct evkeyvalq* headers,
1023 const char *key, const char *value)
1025 const char *real_val = evhttp_find_header(headers, key);
1026 if (real_val == NULL)
1027 return (-1);
1028 if (strcmp(real_val, value) != 0)
1029 return (-1);
1030 return (0);
1033 static void
1034 http_parse_query_test(void)
1036 struct evkeyvalq headers;
1038 fprintf(stdout, "Testing HTTP query parsing: ");
1040 TAILQ_INIT(&headers);
1042 evhttp_parse_query("http://www.test.com/?q=test", &headers);
1043 if (validate_header(&headers, "q", "test") != 0)
1044 goto fail;
1045 evhttp_clear_headers(&headers);
1047 evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers);
1048 if (validate_header(&headers, "q", "test") != 0)
1049 goto fail;
1050 if (validate_header(&headers, "foo", "bar") != 0)
1051 goto fail;
1052 evhttp_clear_headers(&headers);
1054 evhttp_parse_query("http://www.test.com/?q=test+foo", &headers);
1055 if (validate_header(&headers, "q", "test foo") != 0)
1056 goto fail;
1057 evhttp_clear_headers(&headers);
1059 evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers);
1060 if (validate_header(&headers, "q", "test\nfoo") != 0)
1061 goto fail;
1062 evhttp_clear_headers(&headers);
1064 evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers);
1065 if (validate_header(&headers, "q", "test\rfoo") != 0)
1066 goto fail;
1067 evhttp_clear_headers(&headers);
1069 fprintf(stdout, "OK\n");
1070 return;
1071 fail:
1072 fprintf(stdout, "FAILED\n");
1073 exit(1);
1076 static void
1077 http_base_test(void)
1079 struct bufferevent *bev;
1080 int fd;
1081 const char *http_request;
1082 short port = -1;
1084 test_ok = 0;
1085 fprintf(stdout, "Testing HTTP Server Event Base: ");
1087 base = event_init();
1090 * create another bogus base - which is being used by all subsequen
1091 * tests - yuck!
1093 event_init();
1095 http = http_setup(&port, base);
1097 fd = http_connect("127.0.0.1", port);
1099 /* Stupid thing to send a request */
1100 bev = bufferevent_new(fd, http_readcb, http_writecb,
1101 http_errorcb, NULL);
1102 bufferevent_base_set(base, bev);
1104 http_request =
1105 "GET /test HTTP/1.1\r\n"
1106 "Host: somehost\r\n"
1107 "Connection: close\r\n"
1108 "\r\n";
1110 bufferevent_write(bev, http_request, strlen(http_request));
1112 event_base_dispatch(base);
1114 bufferevent_free(bev);
1115 EVUTIL_CLOSESOCKET(fd);
1117 evhttp_free(http);
1119 event_base_free(base);
1120 base = NULL;
1122 if (test_ok != 2) {
1123 fprintf(stdout, "FAILED\n");
1124 exit(1);
1127 fprintf(stdout, "OK\n");
1131 * the server is going to reply with chunked data.
1134 static void
1135 http_chunked_readcb(struct bufferevent *bev, void *arg)
1137 /* nothing here */
1140 static void
1141 http_chunked_errorcb(struct bufferevent *bev, short what, void *arg)
1143 if (!test_ok)
1144 goto out;
1146 test_ok = -1;
1148 if ((what & EVBUFFER_EOF) != 0) {
1149 struct evhttp_request *req = evhttp_request_new(NULL, NULL);
1150 const char *header;
1151 enum message_read_status done;
1153 req->kind = EVHTTP_RESPONSE;
1154 done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev));
1155 if (done != ALL_DATA_READ)
1156 goto out;
1158 done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev));
1159 if (done != ALL_DATA_READ)
1160 goto out;
1162 header = evhttp_find_header(req->input_headers, "Transfer-Encoding");
1163 if (header == NULL || strcmp(header, "chunked"))
1164 goto out;
1166 header = evhttp_find_header(req->input_headers, "Connection");
1167 if (header == NULL || strcmp(header, "close"))
1168 goto out;
1170 header = evbuffer_readline(EVBUFFER_INPUT(bev));
1171 if (header == NULL)
1172 goto out;
1173 /* 13 chars */
1174 if (strcmp(header, "d"))
1175 goto out;
1176 free((char*)header);
1178 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)),
1179 "This is funny", 13))
1180 goto out;
1182 evbuffer_drain(EVBUFFER_INPUT(bev), 13 + 2);
1184 header = evbuffer_readline(EVBUFFER_INPUT(bev));
1185 if (header == NULL)
1186 goto out;
1187 /* 18 chars */
1188 if (strcmp(header, "12"))
1189 goto out;
1190 free((char *)header);
1192 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)),
1193 "but not hilarious.", 18))
1194 goto out;
1196 evbuffer_drain(EVBUFFER_INPUT(bev), 18 + 2);
1198 header = evbuffer_readline(EVBUFFER_INPUT(bev));
1199 if (header == NULL)
1200 goto out;
1201 /* 8 chars */
1202 if (strcmp(header, "8"))
1203 goto out;
1204 free((char *)header);
1206 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)),
1207 "bwv 1052.", 8))
1208 goto out;
1210 evbuffer_drain(EVBUFFER_INPUT(bev), 8 + 2);
1212 header = evbuffer_readline(EVBUFFER_INPUT(bev));
1213 if (header == NULL)
1214 goto out;
1215 /* 0 chars */
1216 if (strcmp(header, "0"))
1217 goto out;
1218 free((char *)header);
1220 test_ok = 2;
1223 out:
1224 event_loopexit(NULL);
1227 static void
1228 http_chunked_writecb(struct bufferevent *bev, void *arg)
1230 if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0) {
1231 /* enable reading of the reply */
1232 bufferevent_enable(bev, EV_READ);
1233 test_ok++;
1237 static void
1238 http_chunked_request_done(struct evhttp_request *req, void *arg)
1240 if (req->response_code != HTTP_OK) {
1241 fprintf(stderr, "FAILED\n");
1242 exit(1);
1245 if (evhttp_find_header(req->input_headers,
1246 "Transfer-Encoding") == NULL) {
1247 fprintf(stderr, "FAILED\n");
1248 exit(1);
1251 if (EVBUFFER_LENGTH(req->input_buffer) != 13 + 18 + 8) {
1252 fprintf(stderr, "FAILED\n");
1253 exit(1);
1256 if (strncmp((char *)EVBUFFER_DATA(req->input_buffer),
1257 "This is funnybut not hilarious.bwv 1052",
1258 13 + 18 + 8)) {
1259 fprintf(stderr, "FAILED\n");
1260 exit(1);
1263 test_ok = 1;
1264 event_loopexit(NULL);
1267 static void
1268 http_chunked_test(void)
1270 struct bufferevent *bev;
1271 int fd;
1272 const char *http_request;
1273 short port = -1;
1274 struct timeval tv_start, tv_end;
1275 struct evhttp_connection *evcon = NULL;
1276 struct evhttp_request *req = NULL;
1277 int i;
1279 test_ok = 0;
1280 fprintf(stdout, "Testing Chunked HTTP Reply: ");
1282 http = http_setup(&port, NULL);
1284 fd = http_connect("127.0.0.1", port);
1286 /* Stupid thing to send a request */
1287 bev = bufferevent_new(fd,
1288 http_chunked_readcb, http_chunked_writecb,
1289 http_chunked_errorcb, NULL);
1291 http_request =
1292 "GET /chunked HTTP/1.1\r\n"
1293 "Host: somehost\r\n"
1294 "Connection: close\r\n"
1295 "\r\n";
1297 bufferevent_write(bev, http_request, strlen(http_request));
1299 evutil_gettimeofday(&tv_start, NULL);
1301 event_dispatch();
1303 evutil_gettimeofday(&tv_end, NULL);
1304 evutil_timersub(&tv_end, &tv_start, &tv_end);
1306 if (tv_end.tv_sec >= 1) {
1307 fprintf(stdout, "FAILED (time)\n");
1308 exit (1);
1312 if (test_ok != 2) {
1313 fprintf(stdout, "FAILED\n");
1314 exit(1);
1317 /* now try again with the regular connection object */
1318 evcon = evhttp_connection_new("127.0.0.1", port);
1319 if (evcon == NULL) {
1320 fprintf(stdout, "FAILED\n");
1321 exit(1);
1324 /* make two requests to check the keepalive behavior */
1325 for (i = 0; i < 2; i++) {
1326 test_ok = 0;
1327 req = evhttp_request_new(http_chunked_request_done, NULL);
1329 /* Add the information that we care about */
1330 evhttp_add_header(req->output_headers, "Host", "somehost");
1332 /* We give ownership of the request to the connection */
1333 if (evhttp_make_request(evcon, req,
1334 EVHTTP_REQ_GET, "/chunked") == -1) {
1335 fprintf(stdout, "FAILED\n");
1336 exit(1);
1339 event_dispatch();
1341 if (test_ok != 1) {
1342 fprintf(stdout, "FAILED\n");
1343 exit(1);
1347 evhttp_connection_free(evcon);
1348 evhttp_free(http);
1350 fprintf(stdout, "OK\n");
1353 static void
1354 http_multi_line_header_test(void)
1356 struct bufferevent *bev;
1357 int fd;
1358 const char *http_start_request;
1359 short port = -1;
1361 test_ok = 0;
1362 fprintf(stdout, "Testing HTTP Server with multi line: ");
1364 http = http_setup(&port, NULL);
1366 fd = http_connect("127.0.0.1", port);
1368 /* Stupid thing to send a request */
1369 bev = bufferevent_new(fd, http_readcb, http_writecb,
1370 http_errorcb, NULL);
1372 http_start_request =
1373 "GET /test HTTP/1.1\r\n"
1374 "Host: somehost\r\n"
1375 "Connection: close\r\n"
1376 "X-Multi: aaaaaaaa\r\n"
1377 " a\r\n"
1378 "\tEND\r\n"
1379 "X-Last: last\r\n"
1380 "\r\n";
1382 bufferevent_write(bev, http_start_request, strlen(http_start_request));
1384 event_dispatch();
1386 bufferevent_free(bev);
1387 EVUTIL_CLOSESOCKET(fd);
1389 evhttp_free(http);
1391 if (test_ok != 4) {
1392 fprintf(stdout, "FAILED\n");
1393 exit(1);
1396 fprintf(stdout, "OK\n");
1399 static void
1400 http_request_bad(struct evhttp_request *req, void *arg)
1402 if (req != NULL) {
1403 fprintf(stderr, "FAILED\n");
1404 exit(1);
1407 test_ok = 1;
1408 event_loopexit(NULL);
1411 static void
1412 http_negative_content_length_test(void)
1414 short port = -1;
1415 struct evhttp_connection *evcon = NULL;
1416 struct evhttp_request *req = NULL;
1418 test_ok = 0;
1419 fprintf(stdout, "Testing HTTP Negative Content Length: ");
1421 http = http_setup(&port, NULL);
1423 evcon = evhttp_connection_new("127.0.0.1", port);
1424 if (evcon == NULL) {
1425 fprintf(stdout, "FAILED\n");
1426 exit(1);
1430 * At this point, we want to schedule a request to the HTTP
1431 * server using our make request method.
1434 req = evhttp_request_new(http_request_bad, NULL);
1436 /* Cause the response to have a negative content-length */
1437 evhttp_add_header(req->output_headers, "X-Negative", "makeitso");
1439 /* We give ownership of the request to the connection */
1440 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
1441 fprintf(stdout, "FAILED\n");
1442 exit(1);
1445 event_dispatch();
1447 evhttp_free(http);
1449 if (test_ok != 1) {
1450 fprintf(stdout, "FAILED\n");
1451 exit(1);
1454 fprintf(stdout, "OK\n");
1457 void
1458 http_suite(void)
1460 http_base_test();
1461 http_bad_header_test();
1462 http_parse_query_test();
1463 http_basic_test();
1464 http_connection_test(0 /* not-persistent */);
1465 http_connection_test(1 /* persistent */);
1466 http_close_detection(0 /* with delay */);
1467 http_close_detection(1 /* with delay */);
1468 http_post_test();
1469 http_failure_test();
1470 http_highport_test();
1471 http_dispatcher_test();
1473 http_multi_line_header_test();
1474 http_negative_content_length_test();
1476 http_chunked_test();