1 /* $NetBSD: regress_http.c,v 1.2 2010/11/11 14:08:45 pgoyette Exp $ */ 2 /* 3 * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu> 4 * All rights reserved. 5 * 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. 16 * 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. 27 */ 28 29 #ifdef WIN32 30 #include <winsock2.h> 31 #include <windows.h> 32 #endif 33 34 #ifdef HAVE_CONFIG_H 35 #include "config.h" 36 #endif 37 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> 55 56 #include "event.h" 57 #include "evhttp.h" 58 #include "log.h" 59 #include "http-internal.h" 60 61 extern int pair[]; 62 extern int test_ok; 63 64 static struct evhttp *http; 65 /* set if a test needs to call loopexit on a base */ 66 static struct event_base *base; 67 68 void http_suite(void); 69 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); 75 76 static struct evhttp * 77 http_setup(short *pport, struct event_base *base) 78 { 79 int i; 80 struct evhttp *myhttp; 81 short port = -1; 82 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; 89 } 90 } 91 92 if (port == -1) 93 event_errx(1, "Could not start web server"); 94 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); 101 102 *pport = port; 103 return (myhttp); 104 } 105 106 #ifndef NI_MAXSERV 107 #define NI_MAXSERV 1024 108 #endif 109 110 static int 111 http_connect(const char *address, u_short port) 112 { 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; 124 125 #ifdef WIN32 126 if (!(he = gethostbyname(address))) { 127 event_warn("gethostbyname"); 128 } 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); 142 } 143 sa = aitop->ai_addr; 144 slen = aitop->ai_addrlen; 145 #endif 146 147 fd = socket(AF_INET, SOCK_STREAM, 0); 148 if (fd == -1) 149 event_err(1, "socket failed"); 150 151 if (connect(fd, sa, slen) == -1) 152 event_err(1, "connect failed"); 153 154 #ifndef WIN32 155 freeaddrinfo(aitop); 156 #endif 157 158 return (fd); 159 } 160 161 static void 162 http_readcb(struct bufferevent *bev, void *arg) 163 { 164 const char *what = "This is funny"; 165 166 event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); 167 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; 172 173 req->kind = EVHTTP_RESPONSE; 174 done = evhttp_parse_firstline(req, bev->input); 175 if (done != ALL_DATA_READ) 176 goto out; 177 178 done = evhttp_parse_headers(req, bev->input); 179 if (done != ALL_DATA_READ) 180 goto out; 181 182 if (done == 1 && 183 evhttp_find_header(req->input_headers, 184 "Content-Type") != NULL) 185 test_ok++; 186 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); 194 } 195 } 196 197 static void 198 http_writecb(struct bufferevent *bev, void *arg) 199 { 200 if (EVBUFFER_LENGTH(bev->output) == 0) { 201 /* enable reading of the reply */ 202 bufferevent_enable(bev, EV_READ); 203 test_ok++; 204 } 205 } 206 207 static void 208 http_errorcb(struct bufferevent *bev, short what, void *arg) 209 { 210 test_ok = -2; 211 event_loopexit(NULL); 212 } 213 214 void 215 http_basic_cb(struct evhttp_request *req, void *arg) 216 { 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"); 221 222 /* For multi-line headers test */ 223 { 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++; 231 } 232 } 233 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"); 238 239 /* allow sending of an empty reply */ 240 evhttp_send_reply(req, HTTP_OK, "Everything is fine", 241 !empty ? evb : NULL); 242 243 evbuffer_free(evb); 244 } 245 246 static char const* const CHUNKS[] = { 247 "This is funny", 248 "but not hilarious.", 249 "bwv 1052" 250 }; 251 252 struct chunk_req_state { 253 struct evhttp_request *req; 254 int i; 255 }; 256 257 static void 258 http_chunked_trickle_cb(int fd, short events, void *arg) 259 { 260 struct evbuffer *evb = evbuffer_new(); 261 struct chunk_req_state *state = arg; 262 struct timeval when = { 0, 0 }; 263 264 evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); 265 evhttp_send_reply_chunk(state->req, evb); 266 evbuffer_free(evb); 267 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); 274 } 275 } 276 277 static void 278 http_chunked_cb(struct evhttp_request *req, void *arg) 279 { 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__)); 283 284 memset(state, 0, sizeof(struct chunk_req_state)); 285 state->req = req; 286 287 /* generate a chunked reply */ 288 evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); 289 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); 293 } 294 295 static void 296 http_complete_write(int fd, short what, void *arg) 297 { 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)); 303 } 304 305 static void 306 http_basic_test(void) 307 { 308 struct timeval tv; 309 struct bufferevent *bev; 310 int fd; 311 const char *http_request; 312 short port = -1; 313 314 test_ok = 0; 315 fprintf(stdout, "Testing Basic HTTP Server: "); 316 317 http = http_setup(&port, NULL); 318 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); 323 } 324 325 fd = http_connect("127.0.0.1", port); 326 327 /* Stupid thing to send a request */ 328 bev = bufferevent_new(fd, http_readcb, http_writecb, 329 http_errorcb, NULL); 330 331 /* first half of the http request */ 332 http_request = 333 "GET /test HTTP/1.1\r\n" 334 "Host: some"; 335 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); 340 341 event_dispatch(); 342 343 if (test_ok != 3) { 344 fprintf(stdout, "FAILED\n"); 345 exit(1); 346 } 347 348 /* connect to the second port */ 349 bufferevent_free(bev); 350 EVUTIL_CLOSESOCKET(fd); 351 352 fd = http_connect("127.0.0.1", port + 1); 353 354 /* Stupid thing to send a request */ 355 bev = bufferevent_new(fd, http_readcb, http_writecb, 356 http_errorcb, NULL); 357 358 http_request = 359 "GET /test HTTP/1.1\r\n" 360 "Host: somehost\r\n" 361 "Connection: close\r\n" 362 "\r\n"; 363 364 bufferevent_write(bev, http_request, strlen(http_request)); 365 366 event_dispatch(); 367 368 bufferevent_free(bev); 369 EVUTIL_CLOSESOCKET(fd); 370 371 evhttp_free(http); 372 373 if (test_ok != 5) { 374 fprintf(stdout, "FAILED\n"); 375 exit(1); 376 } 377 378 fprintf(stdout, "OK\n"); 379 } 380 381 static struct evhttp_connection *delayed_client; 382 383 static void 384 http_delay_reply(int fd, short what, void *arg) 385 { 386 struct evhttp_request *req = arg; 387 388 evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); 389 390 ++test_ok; 391 } 392 393 static void 394 http_large_delay_cb(struct evhttp_request *req, void *arg) 395 { 396 struct timeval tv; 397 timerclear(&tv); 398 tv.tv_sec = 3; 399 400 event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv); 401 402 /* here we close the client connection which will cause an EOF */ 403 evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF); 404 } 405 406 void http_request_done(struct evhttp_request *, void *); 407 void http_request_empty_done(struct evhttp_request *, void *); 408 409 static void 410 http_connection_test(int persistent) 411 { 412 short port = -1; 413 struct evhttp_connection *evcon = NULL; 414 struct evhttp_request *req = NULL; 415 416 test_ok = 0; 417 fprintf(stdout, "Testing Request Connection Pipeline %s: ", 418 persistent ? "(persistent)" : ""); 419 420 http = http_setup(&port, NULL); 421 422 evcon = evhttp_connection_new("127.0.0.1", port); 423 if (evcon == NULL) { 424 fprintf(stdout, "FAILED\n"); 425 exit(1); 426 } 427 428 /* 429 * At this point, we want to schedule a request to the HTTP 430 * server using our make request method. 431 */ 432 433 req = evhttp_request_new(http_request_done, NULL); 434 435 /* Add the information that we care about */ 436 evhttp_add_header(req->output_headers, "Host", "somehost"); 437 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); 442 } 443 444 event_dispatch(); 445 446 if (test_ok != 1) { 447 fprintf(stdout, "FAILED\n"); 448 exit(1); 449 } 450 451 /* try to make another request over the same connection */ 452 test_ok = 0; 453 454 req = evhttp_request_new(http_request_done, NULL); 455 456 /* Add the information that we care about */ 457 evhttp_add_header(req->output_headers, "Host", "somehost"); 458 459 /* 460 * if our connections are not supposed to be persistent; request 461 * a close from the server. 462 */ 463 if (!persistent) 464 evhttp_add_header(req->output_headers, "Connection", "close"); 465 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); 470 } 471 472 event_dispatch(); 473 474 /* make another request: request empty reply */ 475 test_ok = 0; 476 477 req = evhttp_request_new(http_request_empty_done, NULL); 478 479 /* Add the information that we care about */ 480 evhttp_add_header(req->output_headers, "Empty", "itis"); 481 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); 486 } 487 488 event_dispatch(); 489 490 if (test_ok != 1) { 491 fprintf(stdout, "FAILED\n"); 492 exit(1); 493 } 494 495 evhttp_connection_free(evcon); 496 evhttp_free(http); 497 498 fprintf(stdout, "OK\n"); 499 } 500 501 void 502 http_request_done(struct evhttp_request *req, void *arg) 503 { 504 const char *what = "This is funny"; 505 506 if (req->response_code != HTTP_OK) { 507 fprintf(stderr, "FAILED\n"); 508 exit(1); 509 } 510 511 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { 512 fprintf(stderr, "FAILED\n"); 513 exit(1); 514 } 515 516 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { 517 fprintf(stderr, "FAILED\n"); 518 exit(1); 519 } 520 521 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { 522 fprintf(stderr, "FAILED\n"); 523 exit(1); 524 } 525 526 test_ok = 1; 527 event_loopexit(NULL); 528 } 529 530 /* test date header and content length */ 531 532 void 533 http_request_empty_done(struct evhttp_request *req, void *arg) 534 { 535 if (req->response_code != HTTP_OK) { 536 fprintf(stderr, "FAILED\n"); 537 exit(1); 538 } 539 540 if (evhttp_find_header(req->input_headers, "Date") == NULL) { 541 fprintf(stderr, "FAILED\n"); 542 exit(1); 543 } 544 545 546 if (evhttp_find_header(req->input_headers, "Content-Length") == NULL) { 547 fprintf(stderr, "FAILED\n"); 548 exit(1); 549 } 550 551 if (strcmp(evhttp_find_header(req->input_headers, "Content-Length"), 552 "0")) { 553 fprintf(stderr, "FAILED\n"); 554 exit(1); 555 } 556 557 if (EVBUFFER_LENGTH(req->input_buffer) != 0) { 558 fprintf(stderr, "FAILED\n"); 559 exit(1); 560 } 561 562 test_ok = 1; 563 event_loopexit(NULL); 564 } 565 566 /* 567 * HTTP DISPATCHER test 568 */ 569 570 void 571 http_dispatcher_cb(struct evhttp_request *req, void *arg) 572 { 573 574 struct evbuffer *evb = evbuffer_new(); 575 event_debug(("%s: called\n", __func__)); 576 evbuffer_add_printf(evb, "DISPATCHER_TEST"); 577 578 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); 579 580 evbuffer_free(evb); 581 } 582 583 static void 584 http_dispatcher_test_done(struct evhttp_request *req, void *arg) 585 { 586 const char *what = "DISPATCHER_TEST"; 587 588 if (req->response_code != HTTP_OK) { 589 fprintf(stderr, "FAILED\n"); 590 exit(1); 591 } 592 593 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { 594 fprintf(stderr, "FAILED (content type)\n"); 595 exit(1); 596 } 597 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); 602 } 603 604 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { 605 fprintf(stderr, "FAILED (data)\n"); 606 exit(1); 607 } 608 609 test_ok = 1; 610 event_loopexit(NULL); 611 } 612 613 static void 614 http_dispatcher_test(void) 615 { 616 short port = -1; 617 struct evhttp_connection *evcon = NULL; 618 struct evhttp_request *req = NULL; 619 620 test_ok = 0; 621 fprintf(stdout, "Testing HTTP Dispatcher: "); 622 623 http = http_setup(&port, NULL); 624 625 evcon = evhttp_connection_new("127.0.0.1", port); 626 if (evcon == NULL) { 627 fprintf(stdout, "FAILED\n"); 628 exit(1); 629 } 630 631 /* also bind to local host */ 632 evhttp_connection_set_local_address(evcon, "127.0.0.1"); 633 634 /* 635 * At this point, we want to schedule an HTTP GET request 636 * server using our make request method. 637 */ 638 639 req = evhttp_request_new(http_dispatcher_test_done, NULL); 640 if (req == NULL) { 641 fprintf(stdout, "FAILED\n"); 642 exit(1); 643 } 644 645 /* Add the information that we care about */ 646 evhttp_add_header(req->output_headers, "Host", "somehost"); 647 648 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { 649 fprintf(stdout, "FAILED\n"); 650 exit(1); 651 } 652 653 event_dispatch(); 654 655 evhttp_connection_free(evcon); 656 evhttp_free(http); 657 658 if (test_ok != 1) { 659 fprintf(stdout, "FAILED: %d\n", test_ok); 660 exit(1); 661 } 662 663 fprintf(stdout, "OK\n"); 664 } 665 666 /* 667 * HTTP POST test. 668 */ 669 670 void http_postrequest_done(struct evhttp_request *, void *); 671 672 #define POST_DATA "Okay. Not really printf" 673 674 static void 675 http_post_test(void) 676 { 677 short port = -1; 678 struct evhttp_connection *evcon = NULL; 679 struct evhttp_request *req = NULL; 680 681 test_ok = 0; 682 fprintf(stdout, "Testing HTTP POST Request: "); 683 684 http = http_setup(&port, NULL); 685 686 evcon = evhttp_connection_new("127.0.0.1", port); 687 if (evcon == NULL) { 688 fprintf(stdout, "FAILED\n"); 689 exit(1); 690 } 691 692 /* 693 * At this point, we want to schedule an HTTP POST request 694 * server using our make request method. 695 */ 696 697 req = evhttp_request_new(http_postrequest_done, NULL); 698 if (req == NULL) { 699 fprintf(stdout, "FAILED\n"); 700 exit(1); 701 } 702 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); 706 707 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { 708 fprintf(stdout, "FAILED\n"); 709 exit(1); 710 } 711 712 event_dispatch(); 713 714 evhttp_connection_free(evcon); 715 evhttp_free(http); 716 717 if (test_ok != 1) { 718 fprintf(stdout, "FAILED: %d\n", test_ok); 719 exit(1); 720 } 721 722 fprintf(stdout, "OK\n"); 723 } 724 725 void 726 http_post_cb(struct evhttp_request *req, void *arg) 727 { 728 struct evbuffer *evb; 729 event_debug(("%s: called\n", __func__)); 730 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); 735 } 736 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); 741 } 742 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); 749 } 750 751 evb = evbuffer_new(); 752 evbuffer_add_printf(evb, "This is funny"); 753 754 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); 755 756 evbuffer_free(evb); 757 } 758 759 void 760 http_postrequest_done(struct evhttp_request *req, void *arg) 761 { 762 const char *what = "This is funny"; 763 764 if (req == NULL) { 765 fprintf(stderr, "FAILED (timeout)\n"); 766 exit(1); 767 } 768 769 if (req->response_code != HTTP_OK) { 770 771 fprintf(stderr, "FAILED (response code)\n"); 772 exit(1); 773 } 774 775 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { 776 fprintf(stderr, "FAILED (content type)\n"); 777 exit(1); 778 } 779 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); 784 } 785 786 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { 787 fprintf(stderr, "FAILED (data)\n"); 788 exit(1); 789 } 790 791 test_ok = 1; 792 event_loopexit(NULL); 793 } 794 795 static void 796 http_failure_readcb(struct bufferevent *bev, void *arg) 797 { 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); 803 } 804 } 805 806 /* 807 * Testing that the HTTP server can deal with a malformed request. 808 */ 809 static void 810 http_failure_test(void) 811 { 812 struct bufferevent *bev; 813 int fd; 814 const char *http_request; 815 short port = -1; 816 817 test_ok = 0; 818 fprintf(stdout, "Testing Bad HTTP Request: "); 819 820 http = http_setup(&port, NULL); 821 822 fd = http_connect("127.0.0.1", port); 823 824 /* Stupid thing to send a request */ 825 bev = bufferevent_new(fd, http_failure_readcb, http_writecb, 826 http_errorcb, NULL); 827 828 http_request = "illegal request\r\n"; 829 830 bufferevent_write(bev, http_request, strlen(http_request)); 831 832 event_dispatch(); 833 834 bufferevent_free(bev); 835 EVUTIL_CLOSESOCKET(fd); 836 837 evhttp_free(http); 838 839 if (test_ok != 2) { 840 fprintf(stdout, "FAILED\n"); 841 exit(1); 842 } 843 844 fprintf(stdout, "OK\n"); 845 } 846 847 static void 848 close_detect_done(struct evhttp_request *req, void *arg) 849 { 850 struct timeval tv; 851 if (req == NULL || req->response_code != HTTP_OK) { 852 853 fprintf(stderr, "FAILED\n"); 854 exit(1); 855 } 856 857 test_ok = 1; 858 859 timerclear(&tv); 860 tv.tv_sec = 5; /* longer than the http time out */ 861 862 event_loopexit(&tv); 863 } 864 865 static void 866 close_detect_launch(int fd, short what, void *arg) 867 { 868 struct evhttp_connection *evcon = arg; 869 struct evhttp_request *req; 870 871 req = evhttp_request_new(close_detect_done, NULL); 872 873 /* Add the information that we care about */ 874 evhttp_add_header(req->output_headers, "Host", "somehost"); 875 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); 880 } 881 } 882 883 static void 884 close_detect_cb(struct evhttp_request *req, void *arg) 885 { 886 struct evhttp_connection *evcon = arg; 887 struct timeval tv; 888 889 if (req != NULL && req->response_code != HTTP_OK) { 890 891 fprintf(stderr, "FAILED\n"); 892 exit(1); 893 } 894 895 timerclear(&tv); 896 tv.tv_sec = 5; /* longer than the http time out */ 897 898 /* launch a new request on the persistent connection in 6 seconds */ 899 event_once(-1, EV_TIMEOUT, close_detect_launch, evcon, &tv); 900 } 901 902 903 static void 904 http_close_detection(int with_delay) 905 { 906 short port = -1; 907 struct evhttp_connection *evcon = NULL; 908 struct evhttp_request *req = NULL; 909 910 test_ok = 0; 911 fprintf(stdout, "Testing Connection Close Detection%s: ", 912 with_delay ? " (with delay)" : ""); 913 914 http = http_setup(&port, NULL); 915 916 /* 2 second timeout */ 917 evhttp_set_timeout(http, 2); 918 919 evcon = evhttp_connection_new("127.0.0.1", port); 920 if (evcon == NULL) { 921 fprintf(stdout, "FAILED\n"); 922 exit(1); 923 } 924 925 delayed_client = evcon; 926 927 /* 928 * At this point, we want to schedule a request to the HTTP 929 * server using our make request method. 930 */ 931 932 req = evhttp_request_new(close_detect_cb, evcon); 933 934 /* Add the information that we care about */ 935 evhttp_add_header(req->output_headers, "Host", "somehost"); 936 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); 942 } 943 944 event_dispatch(); 945 946 if (test_ok != 1) { 947 fprintf(stdout, "FAILED\n"); 948 exit(1); 949 } 950 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); 955 } 956 957 evhttp_connection_free(evcon); 958 evhttp_free(http); 959 960 fprintf(stdout, "OK\n"); 961 } 962 963 static void 964 http_highport_test(void) 965 { 966 int i = -1; 967 struct evhttp *myhttp = NULL; 968 969 fprintf(stdout, "Testing HTTP Server with high port: "); 970 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; 978 } 979 } 980 981 fprintf(stdout, "FAILED\n"); 982 exit(1); 983 } 984 985 static void 986 http_bad_header_test(void) 987 { 988 struct evkeyvalq headers; 989 990 fprintf(stdout, "Testing HTTP Header filtering: "); 991 992 TAILQ_INIT(&headers); 993 994 if (evhttp_add_header(&headers, "One", "Two") != 0) 995 goto fail; 996 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; 1011 1012 evhttp_clear_headers(&headers); 1013 1014 fprintf(stdout, "OK\n"); 1015 return; 1016 fail: 1017 fprintf(stdout, "FAILED\n"); 1018 exit(1); 1019 } 1020 1021 static int validate_header( 1022 const struct evkeyvalq* headers, 1023 const char *key, const char *value) 1024 { 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); 1031 } 1032 1033 static void 1034 http_parse_query_test(void) 1035 { 1036 struct evkeyvalq headers; 1037 1038 fprintf(stdout, "Testing HTTP query parsing: "); 1039 1040 TAILQ_INIT(&headers); 1041 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); 1046 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); 1053 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); 1058 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); 1063 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); 1068 1069 fprintf(stdout, "OK\n"); 1070 return; 1071 fail: 1072 fprintf(stdout, "FAILED\n"); 1073 exit(1); 1074 } 1075 1076 static void 1077 http_base_test(void) 1078 { 1079 struct bufferevent *bev; 1080 int fd; 1081 const char *http_request; 1082 short port = -1; 1083 1084 test_ok = 0; 1085 fprintf(stdout, "Testing HTTP Server Event Base: "); 1086 1087 base = event_init(); 1088 1089 /* 1090 * create another bogus base - which is being used by all subsequen 1091 * tests - yuck! 1092 */ 1093 event_init(); 1094 1095 http = http_setup(&port, base); 1096 1097 fd = http_connect("127.0.0.1", port); 1098 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); 1103 1104 http_request = 1105 "GET /test HTTP/1.1\r\n" 1106 "Host: somehost\r\n" 1107 "Connection: close\r\n" 1108 "\r\n"; 1109 1110 bufferevent_write(bev, http_request, strlen(http_request)); 1111 1112 event_base_dispatch(base); 1113 1114 bufferevent_free(bev); 1115 EVUTIL_CLOSESOCKET(fd); 1116 1117 evhttp_free(http); 1118 1119 event_base_free(base); 1120 base = NULL; 1121 1122 if (test_ok != 2) { 1123 fprintf(stdout, "FAILED\n"); 1124 exit(1); 1125 } 1126 1127 fprintf(stdout, "OK\n"); 1128 } 1129 1130 /* 1131 * the server is going to reply with chunked data. 1132 */ 1133 1134 static void 1135 http_chunked_readcb(struct bufferevent *bev, void *arg) 1136 { 1137 /* nothing here */ 1138 } 1139 1140 static void 1141 http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) 1142 { 1143 if (!test_ok) 1144 goto out; 1145 1146 test_ok = -1; 1147 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; 1152 1153 req->kind = EVHTTP_RESPONSE; 1154 done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev)); 1155 if (done != ALL_DATA_READ) 1156 goto out; 1157 1158 done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev)); 1159 if (done != ALL_DATA_READ) 1160 goto out; 1161 1162 header = evhttp_find_header(req->input_headers, "Transfer-Encoding"); 1163 if (header == NULL || strcmp(header, "chunked")) 1164 goto out; 1165 1166 header = evhttp_find_header(req->input_headers, "Connection"); 1167 if (header == NULL || strcmp(header, "close")) 1168 goto out; 1169 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); 1177 1178 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), 1179 "This is funny", 13)) 1180 goto out; 1181 1182 evbuffer_drain(EVBUFFER_INPUT(bev), 13 + 2); 1183 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); 1191 1192 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), 1193 "but not hilarious.", 18)) 1194 goto out; 1195 1196 evbuffer_drain(EVBUFFER_INPUT(bev), 18 + 2); 1197 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); 1205 1206 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), 1207 "bwv 1052.", 8)) 1208 goto out; 1209 1210 evbuffer_drain(EVBUFFER_INPUT(bev), 8 + 2); 1211 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); 1219 1220 test_ok = 2; 1221 } 1222 1223 out: 1224 event_loopexit(NULL); 1225 } 1226 1227 static void 1228 http_chunked_writecb(struct bufferevent *bev, void *arg) 1229 { 1230 if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0) { 1231 /* enable reading of the reply */ 1232 bufferevent_enable(bev, EV_READ); 1233 test_ok++; 1234 } 1235 } 1236 1237 static void 1238 http_chunked_request_done(struct evhttp_request *req, void *arg) 1239 { 1240 if (req->response_code != HTTP_OK) { 1241 fprintf(stderr, "FAILED\n"); 1242 exit(1); 1243 } 1244 1245 if (evhttp_find_header(req->input_headers, 1246 "Transfer-Encoding") == NULL) { 1247 fprintf(stderr, "FAILED\n"); 1248 exit(1); 1249 } 1250 1251 if (EVBUFFER_LENGTH(req->input_buffer) != 13 + 18 + 8) { 1252 fprintf(stderr, "FAILED\n"); 1253 exit(1); 1254 } 1255 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); 1261 } 1262 1263 test_ok = 1; 1264 event_loopexit(NULL); 1265 } 1266 1267 static void 1268 http_chunked_test(void) 1269 { 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; 1278 1279 test_ok = 0; 1280 fprintf(stdout, "Testing Chunked HTTP Reply: "); 1281 1282 http = http_setup(&port, NULL); 1283 1284 fd = http_connect("127.0.0.1", port); 1285 1286 /* Stupid thing to send a request */ 1287 bev = bufferevent_new(fd, 1288 http_chunked_readcb, http_chunked_writecb, 1289 http_chunked_errorcb, NULL); 1290 1291 http_request = 1292 "GET /chunked HTTP/1.1\r\n" 1293 "Host: somehost\r\n" 1294 "Connection: close\r\n" 1295 "\r\n"; 1296 1297 bufferevent_write(bev, http_request, strlen(http_request)); 1298 1299 evutil_gettimeofday(&tv_start, NULL); 1300 1301 event_dispatch(); 1302 1303 evutil_gettimeofday(&tv_end, NULL); 1304 evutil_timersub(&tv_end, &tv_start, &tv_end); 1305 1306 if (tv_end.tv_sec >= 1) { 1307 fprintf(stdout, "FAILED (time)\n"); 1308 exit (1); 1309 } 1310 1311 1312 if (test_ok != 2) { 1313 fprintf(stdout, "FAILED\n"); 1314 exit(1); 1315 } 1316 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); 1322 } 1323 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); 1328 1329 /* Add the information that we care about */ 1330 evhttp_add_header(req->output_headers, "Host", "somehost"); 1331 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); 1337 } 1338 1339 event_dispatch(); 1340 1341 if (test_ok != 1) { 1342 fprintf(stdout, "FAILED\n"); 1343 exit(1); 1344 } 1345 } 1346 1347 evhttp_connection_free(evcon); 1348 evhttp_free(http); 1349 1350 fprintf(stdout, "OK\n"); 1351 } 1352 1353 static void 1354 http_multi_line_header_test(void) 1355 { 1356 struct bufferevent *bev; 1357 int fd; 1358 const char *http_start_request; 1359 short port = -1; 1360 1361 test_ok = 0; 1362 fprintf(stdout, "Testing HTTP Server with multi line: "); 1363 1364 http = http_setup(&port, NULL); 1365 1366 fd = http_connect("127.0.0.1", port); 1367 1368 /* Stupid thing to send a request */ 1369 bev = bufferevent_new(fd, http_readcb, http_writecb, 1370 http_errorcb, NULL); 1371 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"; 1381 1382 bufferevent_write(bev, http_start_request, strlen(http_start_request)); 1383 1384 event_dispatch(); 1385 1386 bufferevent_free(bev); 1387 EVUTIL_CLOSESOCKET(fd); 1388 1389 evhttp_free(http); 1390 1391 if (test_ok != 4) { 1392 fprintf(stdout, "FAILED\n"); 1393 exit(1); 1394 } 1395 1396 fprintf(stdout, "OK\n"); 1397 } 1398 1399 static void 1400 http_request_bad(struct evhttp_request *req, void *arg) 1401 { 1402 if (req != NULL) { 1403 fprintf(stderr, "FAILED\n"); 1404 exit(1); 1405 } 1406 1407 test_ok = 1; 1408 event_loopexit(NULL); 1409 } 1410 1411 static void 1412 http_negative_content_length_test(void) 1413 { 1414 short port = -1; 1415 struct evhttp_connection *evcon = NULL; 1416 struct evhttp_request *req = NULL; 1417 1418 test_ok = 0; 1419 fprintf(stdout, "Testing HTTP Negative Content Length: "); 1420 1421 http = http_setup(&port, NULL); 1422 1423 evcon = evhttp_connection_new("127.0.0.1", port); 1424 if (evcon == NULL) { 1425 fprintf(stdout, "FAILED\n"); 1426 exit(1); 1427 } 1428 1429 /* 1430 * At this point, we want to schedule a request to the HTTP 1431 * server using our make request method. 1432 */ 1433 1434 req = evhttp_request_new(http_request_bad, NULL); 1435 1436 /* Cause the response to have a negative content-length */ 1437 evhttp_add_header(req->output_headers, "X-Negative", "makeitso"); 1438 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); 1443 } 1444 1445 event_dispatch(); 1446 1447 evhttp_free(http); 1448 1449 if (test_ok != 1) { 1450 fprintf(stdout, "FAILED\n"); 1451 exit(1); 1452 } 1453 1454 fprintf(stdout, "OK\n"); 1455 } 1456 1457 void 1458 http_suite(void) 1459 { 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(); 1472 1473 http_multi_line_header_test(); 1474 http_negative_content_length_test(); 1475 1476 http_chunked_test(); 1477 } 1478