1/* 2 * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#ifdef WIN32 29#include <winsock2.h> 30#include <windows.h> 31#endif 32 33#ifdef HAVE_CONFIG_H 34#include "config.h" 35#endif 36 37#include <sys/types.h> 38#include <sys/stat.h> 39#ifdef HAVE_SYS_TIME_H 40#include <sys/time.h> 41#endif 42#include <sys/queue.h> 43#ifndef WIN32 44#include <sys/socket.h> 45#include <signal.h> 46#include <unistd.h> 47#include <netdb.h> 48#endif 49#include <fcntl.h> 50#include <stdlib.h> 51#include <stdio.h> 52#include <string.h> 53#include <errno.h> 54 55#include "event.h" 56#include "evhttp.h" 57#include "log.h" 58#include "http-internal.h" 59 60extern int pair[]; 61extern int test_ok; 62 63static struct evhttp *http; 64/* set if a test needs to call loopexit on a base */ 65static struct event_base *base; 66 67void http_suite(void); 68 69void http_basic_cb(struct evhttp_request *req, void *arg); 70static void http_chunked_cb(struct evhttp_request *req, void *arg); 71void http_post_cb(struct evhttp_request *req, void *arg); 72void http_dispatcher_cb(struct evhttp_request *req, void *arg); 73static void http_large_delay_cb(struct evhttp_request *req, void *arg); 74 75static struct evhttp * 76http_setup(short *pport, struct event_base *base) 77{ 78 int i; 79 struct evhttp *myhttp; 80 short port = -1; 81 82 /* Try a few different ports */ 83 myhttp = evhttp_new(base); 84 for (i = 0; i < 50; ++i) { 85 if (evhttp_bind_socket(myhttp, "127.0.0.1", 8080 + i) != -1) { 86 port = 8080 + i; 87 break; 88 } 89 } 90 91 if (port == -1) 92 event_errx(1, "Could not start web server"); 93 94 /* Register a callback for certain types of requests */ 95 evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL); 96 evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, NULL); 97 evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL); 98 evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, NULL); 99 evhttp_set_cb(myhttp, "/", http_dispatcher_cb, NULL); 100 101 *pport = port; 102 return (myhttp); 103} 104 105#ifndef NI_MAXSERV 106#define NI_MAXSERV 1024 107#endif 108 109static int 110http_connect(const char *address, u_short port) 111{ 112 /* Stupid code for connecting */ 113#ifdef WIN32 114 struct hostent *he; 115 struct sockaddr_in sin; 116#else 117 struct addrinfo ai, *aitop; 118 char strport[NI_MAXSERV]; 119#endif 120 struct sockaddr *sa; 121 int slen; 122 int fd; 123 124#ifdef WIN32 125 if (!(he = gethostbyname(address))) { 126 event_warn("gethostbyname"); 127 } 128 memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length); 129 sin.sin_family = AF_INET; 130 sin.sin_port = htons(port); 131 slen = sizeof(struct sockaddr_in); 132 sa = (struct sockaddr*)&sin; 133#else 134 memset(&ai, 0, sizeof (ai)); 135 ai.ai_family = AF_INET; 136 ai.ai_socktype = SOCK_STREAM; 137 snprintf(strport, sizeof (strport), "%d", port); 138 if (getaddrinfo(address, strport, &ai, &aitop) != 0) { 139 event_warn("getaddrinfo"); 140 return (-1); 141 } 142 sa = aitop->ai_addr; 143 slen = aitop->ai_addrlen; 144#endif 145 146 fd = socket(AF_INET, SOCK_STREAM, 0); 147 if (fd == -1) 148 event_err(1, "socket failed"); 149 150 if (connect(fd, sa, slen) == -1) 151 event_err(1, "connect failed"); 152 153#ifndef WIN32 154 freeaddrinfo(aitop); 155#endif 156 157 return (fd); 158} 159 160static void 161http_readcb(struct bufferevent *bev, void *arg) 162{ 163 const char *what = "This is funny"; 164 165 event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); 166 167 if (evbuffer_find(bev->input, 168 (const unsigned char*) what, strlen(what)) != NULL) { 169 struct evhttp_request *req = evhttp_request_new(NULL, NULL); 170 enum message_read_status done; 171 172 req->kind = EVHTTP_RESPONSE; 173 done = evhttp_parse_firstline(req, bev->input); 174 if (done != ALL_DATA_READ) 175 goto out; 176 177 done = evhttp_parse_headers(req, bev->input); 178 if (done != ALL_DATA_READ) 179 goto out; 180 181 if (done == 1 && 182 evhttp_find_header(req->input_headers, 183 "Content-Type") != NULL) 184 test_ok++; 185 186 out: 187 evhttp_request_free(req); 188 bufferevent_disable(bev, EV_READ); 189 if (base) 190 event_base_loopexit(base, NULL); 191 else 192 event_loopexit(NULL); 193 } 194} 195 196static void 197http_writecb(struct bufferevent *bev, void *arg) 198{ 199 if (EVBUFFER_LENGTH(bev->output) == 0) { 200 /* enable reading of the reply */ 201 bufferevent_enable(bev, EV_READ); 202 test_ok++; 203 } 204} 205 206static void 207http_errorcb(struct bufferevent *bev, short what, void *arg) 208{ 209 test_ok = -2; 210 event_loopexit(NULL); 211} 212 213void 214http_basic_cb(struct evhttp_request *req, void *arg) 215{ 216 struct evbuffer *evb = evbuffer_new(); 217 int empty = evhttp_find_header(req->input_headers, "Empty") != NULL; 218 event_debug(("%s: called\n", __func__)); 219 evbuffer_add_printf(evb, "This is funny"); 220 221 /* For multi-line headers test */ 222 { 223 const char *multi = 224 evhttp_find_header(req->input_headers,"X-multi"); 225 if (multi) { 226 if (strcmp("END", multi + strlen(multi) - 3) == 0) 227 test_ok++; 228 if (evhttp_find_header(req->input_headers, "X-Last")) 229 test_ok++; 230 } 231 } 232 233 /* injecting a bad content-length */ 234 if (evhttp_find_header(req->input_headers, "X-Negative")) 235 evhttp_add_header(req->output_headers, 236 "Content-Length", "-100"); 237 238 /* allow sending of an empty reply */ 239 evhttp_send_reply(req, HTTP_OK, "Everything is fine", 240 !empty ? evb : NULL); 241 242 evbuffer_free(evb); 243} 244 245static char const* const CHUNKS[] = { 246 "This is funny", 247 "but not hilarious.", 248 "bwv 1052" 249}; 250 251struct chunk_req_state { 252 struct evhttp_request *req; 253 int i; 254}; 255 256static void 257http_chunked_trickle_cb(int fd, short events, void *arg) 258{ 259 struct evbuffer *evb = evbuffer_new(); 260 struct chunk_req_state *state = arg; 261 struct timeval when = { 0, 0 }; 262 263 evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); 264 evhttp_send_reply_chunk(state->req, evb); 265 evbuffer_free(evb); 266 267 if (++state->i < sizeof(CHUNKS)/sizeof(CHUNKS[0])) { 268 event_once(-1, EV_TIMEOUT, 269 http_chunked_trickle_cb, state, &when); 270 } else { 271 evhttp_send_reply_end(state->req); 272 free(state); 273 } 274} 275 276static void 277http_chunked_cb(struct evhttp_request *req, void *arg) 278{ 279 struct timeval when = { 0, 0 }; 280 struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); 281 event_debug(("%s: called\n", __func__)); 282 283 memset(state, 0, sizeof(struct chunk_req_state)); 284 state->req = req; 285 286 /* generate a chunked reply */ 287 evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); 288 289 /* but trickle it across several iterations to ensure we're not 290 * assuming it comes all at once */ 291 event_once(-1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); 292} 293 294static void 295http_complete_write(int fd, short what, void *arg) 296{ 297 struct bufferevent *bev = arg; 298 const char *http_request = "host\r\n" 299 "Connection: close\r\n" 300 "\r\n"; 301 bufferevent_write(bev, http_request, strlen(http_request)); 302} 303 304static void 305http_basic_test(void) 306{ 307 struct timeval tv; 308 struct bufferevent *bev; 309 int fd; 310 const char *http_request; 311 short port = -1; 312 313 test_ok = 0; 314 fprintf(stdout, "Testing Basic HTTP Server: "); 315 316 http = http_setup(&port, NULL); 317 318 /* bind to a second socket */ 319 if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) { 320 fprintf(stdout, "FAILED (bind)\n"); 321 exit(1); 322 } 323 324 fd = http_connect("127.0.0.1", port); 325 326 /* Stupid thing to send a request */ 327 bev = bufferevent_new(fd, http_readcb, http_writecb, 328 http_errorcb, NULL); 329 330 /* first half of the http request */ 331 http_request = 332 "GET /test HTTP/1.1\r\n" 333 "Host: some"; 334 335 bufferevent_write(bev, http_request, strlen(http_request)); 336 timerclear(&tv); 337 tv.tv_usec = 10000; 338 event_once(-1, EV_TIMEOUT, http_complete_write, bev, &tv); 339 340 event_dispatch(); 341 342 if (test_ok != 3) { 343 fprintf(stdout, "FAILED\n"); 344 exit(1); 345 } 346 347 /* connect to the second port */ 348 bufferevent_free(bev); 349 EVUTIL_CLOSESOCKET(fd); 350 351 fd = http_connect("127.0.0.1", port + 1); 352 353 /* Stupid thing to send a request */ 354 bev = bufferevent_new(fd, http_readcb, http_writecb, 355 http_errorcb, NULL); 356 357 http_request = 358 "GET /test HTTP/1.1\r\n" 359 "Host: somehost\r\n" 360 "Connection: close\r\n" 361 "\r\n"; 362 363 bufferevent_write(bev, http_request, strlen(http_request)); 364 365 event_dispatch(); 366 367 bufferevent_free(bev); 368 EVUTIL_CLOSESOCKET(fd); 369 370 evhttp_free(http); 371 372 if (test_ok != 5) { 373 fprintf(stdout, "FAILED\n"); 374 exit(1); 375 } 376 377 fprintf(stdout, "OK\n"); 378} 379 380static struct evhttp_connection *delayed_client; 381 382static void 383http_delay_reply(int fd, short what, void *arg) 384{ 385 struct evhttp_request *req = arg; 386 387 evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); 388 389 ++test_ok; 390} 391 392static void 393http_large_delay_cb(struct evhttp_request *req, void *arg) 394{ 395 struct timeval tv; 396 timerclear(&tv); 397 tv.tv_sec = 3; 398 399 event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv); 400 401 /* here we close the client connection which will cause an EOF */ 402 evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF); 403} 404 405void http_request_done(struct evhttp_request *, void *); 406void http_request_empty_done(struct evhttp_request *, void *); 407 408static void 409http_connection_test(int persistent) 410{ 411 short port = -1; 412 struct evhttp_connection *evcon = NULL; 413 struct evhttp_request *req = NULL; 414 415 test_ok = 0; 416 fprintf(stdout, "Testing Request Connection Pipeline %s: ", 417 persistent ? "(persistent)" : ""); 418 419 http = http_setup(&port, NULL); 420 421 evcon = evhttp_connection_new("127.0.0.1", port); 422 if (evcon == NULL) { 423 fprintf(stdout, "FAILED\n"); 424 exit(1); 425 } 426 427 /* 428 * At this point, we want to schedule a request to the HTTP 429 * server using our make request method. 430 */ 431 432 req = evhttp_request_new(http_request_done, NULL); 433 434 /* Add the information that we care about */ 435 evhttp_add_header(req->output_headers, "Host", "somehost"); 436 437 /* We give ownership of the request to the connection */ 438 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 439 fprintf(stdout, "FAILED\n"); 440 exit(1); 441 } 442 443 event_dispatch(); 444 445 if (test_ok != 1) { 446 fprintf(stdout, "FAILED\n"); 447 exit(1); 448 } 449 450 /* try to make another request over the same connection */ 451 test_ok = 0; 452 453 req = evhttp_request_new(http_request_done, NULL); 454 455 /* Add the information that we care about */ 456 evhttp_add_header(req->output_headers, "Host", "somehost"); 457 458 /* 459 * if our connections are not supposed to be persistent; request 460 * a close from the server. 461 */ 462 if (!persistent) 463 evhttp_add_header(req->output_headers, "Connection", "close"); 464 465 /* We give ownership of the request to the connection */ 466 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 467 fprintf(stdout, "FAILED\n"); 468 exit(1); 469 } 470 471 event_dispatch(); 472 473 /* make another request: request empty reply */ 474 test_ok = 0; 475 476 req = evhttp_request_new(http_request_empty_done, NULL); 477 478 /* Add the information that we care about */ 479 evhttp_add_header(req->output_headers, "Empty", "itis"); 480 481 /* We give ownership of the request to the connection */ 482 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 483 fprintf(stdout, "FAILED\n"); 484 exit(1); 485 } 486 487 event_dispatch(); 488 489 if (test_ok != 1) { 490 fprintf(stdout, "FAILED\n"); 491 exit(1); 492 } 493 494 evhttp_connection_free(evcon); 495 evhttp_free(http); 496 497 fprintf(stdout, "OK\n"); 498} 499 500void 501http_request_done(struct evhttp_request *req, void *arg) 502{ 503 const char *what = "This is funny"; 504 505 if (req->response_code != HTTP_OK) { 506 fprintf(stderr, "FAILED\n"); 507 exit(1); 508 } 509 510 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { 511 fprintf(stderr, "FAILED\n"); 512 exit(1); 513 } 514 515 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { 516 fprintf(stderr, "FAILED\n"); 517 exit(1); 518 } 519 520 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { 521 fprintf(stderr, "FAILED\n"); 522 exit(1); 523 } 524 525 test_ok = 1; 526 event_loopexit(NULL); 527} 528 529/* test date header and content length */ 530 531void 532http_request_empty_done(struct evhttp_request *req, void *arg) 533{ 534 if (req->response_code != HTTP_OK) { 535 fprintf(stderr, "FAILED\n"); 536 exit(1); 537 } 538 539 if (evhttp_find_header(req->input_headers, "Date") == NULL) { 540 fprintf(stderr, "FAILED\n"); 541 exit(1); 542 } 543 544 545 if (evhttp_find_header(req->input_headers, "Content-Length") == NULL) { 546 fprintf(stderr, "FAILED\n"); 547 exit(1); 548 } 549 550 if (strcmp(evhttp_find_header(req->input_headers, "Content-Length"), 551 "0")) { 552 fprintf(stderr, "FAILED\n"); 553 exit(1); 554 } 555 556 if (EVBUFFER_LENGTH(req->input_buffer) != 0) { 557 fprintf(stderr, "FAILED\n"); 558 exit(1); 559 } 560 561 test_ok = 1; 562 event_loopexit(NULL); 563} 564 565/* 566 * HTTP DISPATCHER test 567 */ 568 569void 570http_dispatcher_cb(struct evhttp_request *req, void *arg) 571{ 572 573 struct evbuffer *evb = evbuffer_new(); 574 event_debug(("%s: called\n", __func__)); 575 evbuffer_add_printf(evb, "DISPATCHER_TEST"); 576 577 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); 578 579 evbuffer_free(evb); 580} 581 582static void 583http_dispatcher_test_done(struct evhttp_request *req, void *arg) 584{ 585 const char *what = "DISPATCHER_TEST"; 586 587 if (req->response_code != HTTP_OK) { 588 fprintf(stderr, "FAILED\n"); 589 exit(1); 590 } 591 592 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { 593 fprintf(stderr, "FAILED (content type)\n"); 594 exit(1); 595 } 596 597 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { 598 fprintf(stderr, "FAILED (length %zu vs %zu)\n", 599 EVBUFFER_LENGTH(req->input_buffer), strlen(what)); 600 exit(1); 601 } 602 603 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { 604 fprintf(stderr, "FAILED (data)\n"); 605 exit(1); 606 } 607 608 test_ok = 1; 609 event_loopexit(NULL); 610} 611 612static void 613http_dispatcher_test(void) 614{ 615 short port = -1; 616 struct evhttp_connection *evcon = NULL; 617 struct evhttp_request *req = NULL; 618 619 test_ok = 0; 620 fprintf(stdout, "Testing HTTP Dispatcher: "); 621 622 http = http_setup(&port, NULL); 623 624 evcon = evhttp_connection_new("127.0.0.1", port); 625 if (evcon == NULL) { 626 fprintf(stdout, "FAILED\n"); 627 exit(1); 628 } 629 630 /* also bind to local host */ 631 evhttp_connection_set_local_address(evcon, "127.0.0.1"); 632 633 /* 634 * At this point, we want to schedule an HTTP GET request 635 * server using our make request method. 636 */ 637 638 req = evhttp_request_new(http_dispatcher_test_done, NULL); 639 if (req == NULL) { 640 fprintf(stdout, "FAILED\n"); 641 exit(1); 642 } 643 644 /* Add the information that we care about */ 645 evhttp_add_header(req->output_headers, "Host", "somehost"); 646 647 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { 648 fprintf(stdout, "FAILED\n"); 649 exit(1); 650 } 651 652 event_dispatch(); 653 654 evhttp_connection_free(evcon); 655 evhttp_free(http); 656 657 if (test_ok != 1) { 658 fprintf(stdout, "FAILED: %d\n", test_ok); 659 exit(1); 660 } 661 662 fprintf(stdout, "OK\n"); 663} 664 665/* 666 * HTTP POST test. 667 */ 668 669void http_postrequest_done(struct evhttp_request *, void *); 670 671#define POST_DATA "Okay. Not really printf" 672 673static void 674http_post_test(void) 675{ 676 short port = -1; 677 struct evhttp_connection *evcon = NULL; 678 struct evhttp_request *req = NULL; 679 680 test_ok = 0; 681 fprintf(stdout, "Testing HTTP POST Request: "); 682 683 http = http_setup(&port, NULL); 684 685 evcon = evhttp_connection_new("127.0.0.1", port); 686 if (evcon == NULL) { 687 fprintf(stdout, "FAILED\n"); 688 exit(1); 689 } 690 691 /* 692 * At this point, we want to schedule an HTTP POST request 693 * server using our make request method. 694 */ 695 696 req = evhttp_request_new(http_postrequest_done, NULL); 697 if (req == NULL) { 698 fprintf(stdout, "FAILED\n"); 699 exit(1); 700 } 701 702 /* Add the information that we care about */ 703 evhttp_add_header(req->output_headers, "Host", "somehost"); 704 evbuffer_add_printf(req->output_buffer, POST_DATA); 705 706 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { 707 fprintf(stdout, "FAILED\n"); 708 exit(1); 709 } 710 711 event_dispatch(); 712 713 evhttp_connection_free(evcon); 714 evhttp_free(http); 715 716 if (test_ok != 1) { 717 fprintf(stdout, "FAILED: %d\n", test_ok); 718 exit(1); 719 } 720 721 fprintf(stdout, "OK\n"); 722} 723 724void 725http_post_cb(struct evhttp_request *req, void *arg) 726{ 727 struct evbuffer *evb; 728 event_debug(("%s: called\n", __func__)); 729 730 /* Yes, we are expecting a post request */ 731 if (req->type != EVHTTP_REQ_POST) { 732 fprintf(stdout, "FAILED (post type)\n"); 733 exit(1); 734 } 735 736 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(POST_DATA)) { 737 fprintf(stdout, "FAILED (length: %zu vs %zu)\n", 738 EVBUFFER_LENGTH(req->input_buffer), strlen(POST_DATA)); 739 exit(1); 740 } 741 742 if (memcmp(EVBUFFER_DATA(req->input_buffer), POST_DATA, 743 strlen(POST_DATA))) { 744 fprintf(stdout, "FAILED (data)\n"); 745 fprintf(stdout, "Got :%s\n", EVBUFFER_DATA(req->input_buffer)); 746 fprintf(stdout, "Want:%s\n", POST_DATA); 747 exit(1); 748 } 749 750 evb = evbuffer_new(); 751 evbuffer_add_printf(evb, "This is funny"); 752 753 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); 754 755 evbuffer_free(evb); 756} 757 758void 759http_postrequest_done(struct evhttp_request *req, void *arg) 760{ 761 const char *what = "This is funny"; 762 763 if (req == NULL) { 764 fprintf(stderr, "FAILED (timeout)\n"); 765 exit(1); 766 } 767 768 if (req->response_code != HTTP_OK) { 769 770 fprintf(stderr, "FAILED (response code)\n"); 771 exit(1); 772 } 773 774 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { 775 fprintf(stderr, "FAILED (content type)\n"); 776 exit(1); 777 } 778 779 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { 780 fprintf(stderr, "FAILED (length %zu vs %zu)\n", 781 EVBUFFER_LENGTH(req->input_buffer), strlen(what)); 782 exit(1); 783 } 784 785 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { 786 fprintf(stderr, "FAILED (data)\n"); 787 exit(1); 788 } 789 790 test_ok = 1; 791 event_loopexit(NULL); 792} 793 794static void 795http_failure_readcb(struct bufferevent *bev, void *arg) 796{ 797 const char *what = "400 Bad Request"; 798 if (evbuffer_find(bev->input, (const unsigned char*) what, strlen(what)) != NULL) { 799 test_ok = 2; 800 bufferevent_disable(bev, EV_READ); 801 event_loopexit(NULL); 802 } 803} 804 805/* 806 * Testing that the HTTP server can deal with a malformed request. 807 */ 808static void 809http_failure_test(void) 810{ 811 struct bufferevent *bev; 812 int fd; 813 const char *http_request; 814 short port = -1; 815 816 test_ok = 0; 817 fprintf(stdout, "Testing Bad HTTP Request: "); 818 819 http = http_setup(&port, NULL); 820 821 fd = http_connect("127.0.0.1", port); 822 823 /* Stupid thing to send a request */ 824 bev = bufferevent_new(fd, http_failure_readcb, http_writecb, 825 http_errorcb, NULL); 826 827 http_request = "illegal request\r\n"; 828 829 bufferevent_write(bev, http_request, strlen(http_request)); 830 831 event_dispatch(); 832 833 bufferevent_free(bev); 834 EVUTIL_CLOSESOCKET(fd); 835 836 evhttp_free(http); 837 838 if (test_ok != 2) { 839 fprintf(stdout, "FAILED\n"); 840 exit(1); 841 } 842 843 fprintf(stdout, "OK\n"); 844} 845 846static void 847close_detect_done(struct evhttp_request *req, void *arg) 848{ 849 struct timeval tv; 850 if (req == NULL || req->response_code != HTTP_OK) { 851 852 fprintf(stderr, "FAILED\n"); 853 exit(1); 854 } 855 856 test_ok = 1; 857 858 timerclear(&tv); 859 tv.tv_sec = 3; /* longer than the http time out */ 860 861 event_loopexit(&tv); 862} 863 864static void 865close_detect_launch(int fd, short what, void *arg) 866{ 867 struct evhttp_connection *evcon = arg; 868 struct evhttp_request *req; 869 870 req = evhttp_request_new(close_detect_done, NULL); 871 872 /* Add the information that we care about */ 873 evhttp_add_header(req->output_headers, "Host", "somehost"); 874 875 /* We give ownership of the request to the connection */ 876 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 877 fprintf(stdout, "FAILED\n"); 878 exit(1); 879 } 880} 881 882static void 883close_detect_cb(struct evhttp_request *req, void *arg) 884{ 885 struct evhttp_connection *evcon = arg; 886 struct timeval tv; 887 888 if (req != NULL && req->response_code != HTTP_OK) { 889 890 fprintf(stderr, "FAILED\n"); 891 exit(1); 892 } 893 894 timerclear(&tv); 895 tv.tv_sec = 3; /* longer than the http time out */ 896 897 /* launch a new request on the persistent connection in 6 seconds */ 898 event_once(-1, EV_TIMEOUT, close_detect_launch, evcon, &tv); 899} 900 901 902static void 903http_close_detection(int with_delay) 904{ 905 short port = -1; 906 struct evhttp_connection *evcon = NULL; 907 struct evhttp_request *req = NULL; 908 909 test_ok = 0; 910 fprintf(stdout, "Testing Connection Close Detection%s: ", 911 with_delay ? " (with delay)" : ""); 912 913 http = http_setup(&port, NULL); 914 915 /* 2 second timeout */ 916 evhttp_set_timeout(http, 2); 917 918 evcon = evhttp_connection_new("127.0.0.1", port); 919 if (evcon == NULL) { 920 fprintf(stdout, "FAILED\n"); 921 exit(1); 922 } 923 924 delayed_client = evcon; 925 926 /* 927 * At this point, we want to schedule a request to the HTTP 928 * server using our make request method. 929 */ 930 931 req = evhttp_request_new(close_detect_cb, evcon); 932 933 /* Add the information that we care about */ 934 evhttp_add_header(req->output_headers, "Host", "somehost"); 935 936 /* We give ownership of the request to the connection */ 937 if (evhttp_make_request(evcon, 938 req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) { 939 fprintf(stdout, "FAILED\n"); 940 exit(1); 941 } 942 943 event_dispatch(); 944 945 if (test_ok != 1) { 946 fprintf(stdout, "FAILED\n"); 947 exit(1); 948 } 949 950 /* at this point, the http server should have no connection */ 951 if (TAILQ_FIRST(&http->connections) != NULL) { 952 fprintf(stdout, "FAILED (left connections)\n"); 953 exit(1); 954 } 955 956 evhttp_connection_free(evcon); 957 evhttp_free(http); 958 959 fprintf(stdout, "OK\n"); 960} 961 962static void 963http_highport_test(void) 964{ 965 int i = -1; 966 struct evhttp *myhttp = NULL; 967 968 fprintf(stdout, "Testing HTTP Server with high port: "); 969 970 /* Try a few different ports */ 971 for (i = 0; i < 50; ++i) { 972 myhttp = evhttp_start("127.0.0.1", 65535 - i); 973 if (myhttp != NULL) { 974 fprintf(stdout, "OK\n"); 975 evhttp_free(myhttp); 976 return; 977 } 978 } 979 980 fprintf(stdout, "FAILED\n"); 981 exit(1); 982} 983 984static void 985http_bad_header_test(void) 986{ 987 struct evkeyvalq headers; 988 989 fprintf(stdout, "Testing HTTP Header filtering: "); 990 991 TAILQ_INIT(&headers); 992 993 if (evhttp_add_header(&headers, "One", "Two") != 0) 994 goto fail; 995 996 if (evhttp_add_header(&headers, "One\r", "Two") != -1) 997 goto fail; 998 if (evhttp_add_header(&headers, "One", "Two") != 0) 999 goto fail; 1000 if (evhttp_add_header(&headers, "One", "Two\r\n Three") != 0) 1001 goto fail; 1002 if (evhttp_add_header(&headers, "One\r", "Two") != -1) 1003 goto fail; 1004 if (evhttp_add_header(&headers, "One\n", "Two") != -1) 1005 goto fail; 1006 if (evhttp_add_header(&headers, "One", "Two\r") != -1) 1007 goto fail; 1008 if (evhttp_add_header(&headers, "One", "Two\n") != -1) 1009 goto fail; 1010 1011 evhttp_clear_headers(&headers); 1012 1013 fprintf(stdout, "OK\n"); 1014 return; 1015fail: 1016 fprintf(stdout, "FAILED\n"); 1017 exit(1); 1018} 1019 1020static int validate_header( 1021 const struct evkeyvalq* headers, 1022 const char *key, const char *value) 1023{ 1024 const char *real_val = evhttp_find_header(headers, key); 1025 if (real_val == NULL) 1026 return (-1); 1027 if (strcmp(real_val, value) != 0) 1028 return (-1); 1029 return (0); 1030} 1031 1032static void 1033http_parse_query_test(void) 1034{ 1035 struct evkeyvalq headers; 1036 1037 fprintf(stdout, "Testing HTTP query parsing: "); 1038 1039 TAILQ_INIT(&headers); 1040 1041 evhttp_parse_query("http://www.test.com/?q=test", &headers); 1042 if (validate_header(&headers, "q", "test") != 0) 1043 goto fail; 1044 evhttp_clear_headers(&headers); 1045 1046 evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers); 1047 if (validate_header(&headers, "q", "test") != 0) 1048 goto fail; 1049 if (validate_header(&headers, "foo", "bar") != 0) 1050 goto fail; 1051 evhttp_clear_headers(&headers); 1052 1053 evhttp_parse_query("http://www.test.com/?q=test+foo", &headers); 1054 if (validate_header(&headers, "q", "test foo") != 0) 1055 goto fail; 1056 evhttp_clear_headers(&headers); 1057 1058 evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers); 1059 if (validate_header(&headers, "q", "test\nfoo") != 0) 1060 goto fail; 1061 evhttp_clear_headers(&headers); 1062 1063 evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers); 1064 if (validate_header(&headers, "q", "test\rfoo") != 0) 1065 goto fail; 1066 evhttp_clear_headers(&headers); 1067 1068 fprintf(stdout, "OK\n"); 1069 return; 1070fail: 1071 fprintf(stdout, "FAILED\n"); 1072 exit(1); 1073} 1074 1075static void 1076http_base_test(void) 1077{ 1078 struct bufferevent *bev; 1079 int fd; 1080 const char *http_request; 1081 short port = -1; 1082 1083 test_ok = 0; 1084 fprintf(stdout, "Testing HTTP Server Event Base: "); 1085 1086 base = event_init(); 1087 1088 /* 1089 * create another bogus base - which is being used by all subsequen 1090 * tests - yuck! 1091 */ 1092 event_init(); 1093 1094 http = http_setup(&port, base); 1095 1096 fd = http_connect("127.0.0.1", port); 1097 1098 /* Stupid thing to send a request */ 1099 bev = bufferevent_new(fd, http_readcb, http_writecb, 1100 http_errorcb, NULL); 1101 bufferevent_base_set(base, bev); 1102 1103 http_request = 1104 "GET /test HTTP/1.1\r\n" 1105 "Host: somehost\r\n" 1106 "Connection: close\r\n" 1107 "\r\n"; 1108 1109 bufferevent_write(bev, http_request, strlen(http_request)); 1110 1111 event_base_dispatch(base); 1112 1113 bufferevent_free(bev); 1114 EVUTIL_CLOSESOCKET(fd); 1115 1116 evhttp_free(http); 1117 1118 event_base_free(base); 1119 base = NULL; 1120 1121 if (test_ok != 2) { 1122 fprintf(stdout, "FAILED\n"); 1123 exit(1); 1124 } 1125 1126 fprintf(stdout, "OK\n"); 1127} 1128 1129/* 1130 * the server is going to reply with chunked data. 1131 */ 1132 1133static void 1134http_chunked_readcb(struct bufferevent *bev, void *arg) 1135{ 1136 /* nothing here */ 1137} 1138 1139static void 1140http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) 1141{ 1142 if (!test_ok) 1143 goto out; 1144 1145 test_ok = -1; 1146 1147 if ((what & EVBUFFER_EOF) != 0) { 1148 struct evhttp_request *req = evhttp_request_new(NULL, NULL); 1149 const char *header; 1150 enum message_read_status done; 1151 1152 req->kind = EVHTTP_RESPONSE; 1153 done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev)); 1154 if (done != ALL_DATA_READ) 1155 goto out; 1156 1157 done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev)); 1158 if (done != ALL_DATA_READ) 1159 goto out; 1160 1161 header = evhttp_find_header(req->input_headers, "Transfer-Encoding"); 1162 if (header == NULL || strcmp(header, "chunked")) 1163 goto out; 1164 1165 header = evhttp_find_header(req->input_headers, "Connection"); 1166 if (header == NULL || strcmp(header, "close")) 1167 goto out; 1168 1169 header = evbuffer_readline(EVBUFFER_INPUT(bev)); 1170 if (header == NULL) 1171 goto out; 1172 /* 13 chars */ 1173 if (strcmp(header, "d")) 1174 goto out; 1175 free((char*)header); 1176 1177 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), 1178 "This is funny", 13)) 1179 goto out; 1180 1181 evbuffer_drain(EVBUFFER_INPUT(bev), 13 + 2); 1182 1183 header = evbuffer_readline(EVBUFFER_INPUT(bev)); 1184 if (header == NULL) 1185 goto out; 1186 /* 18 chars */ 1187 if (strcmp(header, "12")) 1188 goto out; 1189 free((char *)header); 1190 1191 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), 1192 "but not hilarious.", 18)) 1193 goto out; 1194 1195 evbuffer_drain(EVBUFFER_INPUT(bev), 18 + 2); 1196 1197 header = evbuffer_readline(EVBUFFER_INPUT(bev)); 1198 if (header == NULL) 1199 goto out; 1200 /* 8 chars */ 1201 if (strcmp(header, "8")) 1202 goto out; 1203 free((char *)header); 1204 1205 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), 1206 "bwv 1052.", 8)) 1207 goto out; 1208 1209 evbuffer_drain(EVBUFFER_INPUT(bev), 8 + 2); 1210 1211 header = evbuffer_readline(EVBUFFER_INPUT(bev)); 1212 if (header == NULL) 1213 goto out; 1214 /* 0 chars */ 1215 if (strcmp(header, "0")) 1216 goto out; 1217 free((char *)header); 1218 1219 test_ok = 2; 1220 } 1221 1222out: 1223 event_loopexit(NULL); 1224} 1225 1226static void 1227http_chunked_writecb(struct bufferevent *bev, void *arg) 1228{ 1229 if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0) { 1230 /* enable reading of the reply */ 1231 bufferevent_enable(bev, EV_READ); 1232 test_ok++; 1233 } 1234} 1235 1236static void 1237http_chunked_request_done(struct evhttp_request *req, void *arg) 1238{ 1239 if (req->response_code != HTTP_OK) { 1240 fprintf(stderr, "FAILED\n"); 1241 exit(1); 1242 } 1243 1244 if (evhttp_find_header(req->input_headers, 1245 "Transfer-Encoding") == NULL) { 1246 fprintf(stderr, "FAILED\n"); 1247 exit(1); 1248 } 1249 1250 if (EVBUFFER_LENGTH(req->input_buffer) != 13 + 18 + 8) { 1251 fprintf(stderr, "FAILED\n"); 1252 exit(1); 1253 } 1254 1255 if (strncmp((char *)EVBUFFER_DATA(req->input_buffer), 1256 "This is funnybut not hilarious.bwv 1052", 1257 13 + 18 + 8)) { 1258 fprintf(stderr, "FAILED\n"); 1259 exit(1); 1260 } 1261 1262 test_ok = 1; 1263 event_loopexit(NULL); 1264} 1265 1266static void 1267http_chunked_test(void) 1268{ 1269 struct bufferevent *bev; 1270 int fd; 1271 const char *http_request; 1272 short port = -1; 1273 struct timeval tv_start, tv_end; 1274 struct evhttp_connection *evcon = NULL; 1275 struct evhttp_request *req = NULL; 1276 int i; 1277 1278 test_ok = 0; 1279 fprintf(stdout, "Testing Chunked HTTP Reply: "); 1280 1281 http = http_setup(&port, NULL); 1282 1283 fd = http_connect("127.0.0.1", port); 1284 1285 /* Stupid thing to send a request */ 1286 bev = bufferevent_new(fd, 1287 http_chunked_readcb, http_chunked_writecb, 1288 http_chunked_errorcb, NULL); 1289 1290 http_request = 1291 "GET /chunked HTTP/1.1\r\n" 1292 "Host: somehost\r\n" 1293 "Connection: close\r\n" 1294 "\r\n"; 1295 1296 bufferevent_write(bev, http_request, strlen(http_request)); 1297 1298 evutil_gettimeofday(&tv_start, NULL); 1299 1300 event_dispatch(); 1301 1302 evutil_gettimeofday(&tv_end, NULL); 1303 evutil_timersub(&tv_end, &tv_start, &tv_end); 1304 1305 if (tv_end.tv_sec >= 1) { 1306 fprintf(stdout, "FAILED (time)\n"); 1307 exit (1); 1308 } 1309 1310 1311 if (test_ok != 2) { 1312 fprintf(stdout, "FAILED\n"); 1313 exit(1); 1314 } 1315 1316 /* now try again with the regular connection object */ 1317 evcon = evhttp_connection_new("127.0.0.1", port); 1318 if (evcon == NULL) { 1319 fprintf(stdout, "FAILED\n"); 1320 exit(1); 1321 } 1322 1323 /* make two requests to check the keepalive behavior */ 1324 for (i = 0; i < 2; i++) { 1325 test_ok = 0; 1326 req = evhttp_request_new(http_chunked_request_done, NULL); 1327 1328 /* Add the information that we care about */ 1329 evhttp_add_header(req->output_headers, "Host", "somehost"); 1330 1331 /* We give ownership of the request to the connection */ 1332 if (evhttp_make_request(evcon, req, 1333 EVHTTP_REQ_GET, "/chunked") == -1) { 1334 fprintf(stdout, "FAILED\n"); 1335 exit(1); 1336 } 1337 1338 event_dispatch(); 1339 1340 if (test_ok != 1) { 1341 fprintf(stdout, "FAILED\n"); 1342 exit(1); 1343 } 1344 } 1345 1346 evhttp_connection_free(evcon); 1347 evhttp_free(http); 1348 1349 fprintf(stdout, "OK\n"); 1350} 1351 1352static void 1353http_multi_line_header_test(void) 1354{ 1355 struct bufferevent *bev; 1356 int fd; 1357 const char *http_start_request; 1358 short port = -1; 1359 1360 test_ok = 0; 1361 fprintf(stdout, "Testing HTTP Server with multi line: "); 1362 1363 http = http_setup(&port, NULL); 1364 1365 fd = http_connect("127.0.0.1", port); 1366 1367 /* Stupid thing to send a request */ 1368 bev = bufferevent_new(fd, http_readcb, http_writecb, 1369 http_errorcb, NULL); 1370 1371 http_start_request = 1372 "GET /test HTTP/1.1\r\n" 1373 "Host: somehost\r\n" 1374 "Connection: close\r\n" 1375 "X-Multi: aaaaaaaa\r\n" 1376 " a\r\n" 1377 "\tEND\r\n" 1378 "X-Last: last\r\n" 1379 "\r\n"; 1380 1381 bufferevent_write(bev, http_start_request, strlen(http_start_request)); 1382 1383 event_dispatch(); 1384 1385 bufferevent_free(bev); 1386 EVUTIL_CLOSESOCKET(fd); 1387 1388 evhttp_free(http); 1389 1390 if (test_ok != 4) { 1391 fprintf(stdout, "FAILED\n"); 1392 exit(1); 1393 } 1394 1395 fprintf(stdout, "OK\n"); 1396} 1397 1398static void 1399http_request_bad(struct evhttp_request *req, void *arg) 1400{ 1401 if (req != NULL) { 1402 fprintf(stderr, "FAILED\n"); 1403 exit(1); 1404 } 1405 1406 test_ok = 1; 1407 event_loopexit(NULL); 1408} 1409 1410static void 1411http_negative_content_length_test(void) 1412{ 1413 short port = -1; 1414 struct evhttp_connection *evcon = NULL; 1415 struct evhttp_request *req = NULL; 1416 1417 test_ok = 0; 1418 fprintf(stdout, "Testing HTTP Negative Content Length: "); 1419 1420 http = http_setup(&port, NULL); 1421 1422 evcon = evhttp_connection_new("127.0.0.1", port); 1423 if (evcon == NULL) { 1424 fprintf(stdout, "FAILED\n"); 1425 exit(1); 1426 } 1427 1428 /* 1429 * At this point, we want to schedule a request to the HTTP 1430 * server using our make request method. 1431 */ 1432 1433 req = evhttp_request_new(http_request_bad, NULL); 1434 1435 /* Cause the response to have a negative content-length */ 1436 evhttp_add_header(req->output_headers, "X-Negative", "makeitso"); 1437 1438 /* We give ownership of the request to the connection */ 1439 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 1440 fprintf(stdout, "FAILED\n"); 1441 exit(1); 1442 } 1443 1444 event_dispatch(); 1445 1446 evhttp_free(http); 1447 1448 if (test_ok != 1) { 1449 fprintf(stdout, "FAILED\n"); 1450 exit(1); 1451 } 1452 1453 fprintf(stdout, "OK\n"); 1454} 1455 1456void 1457http_suite(void) 1458{ 1459 http_base_test(); 1460 http_bad_header_test(); 1461 http_parse_query_test(); 1462 http_basic_test(); 1463 http_connection_test(0 /* not-persistent */); 1464 http_connection_test(1 /* persistent */); 1465 http_close_detection(0 /* with delay */); 1466 http_close_detection(1 /* with delay */); 1467 http_post_test(); 1468 http_failure_test(); 1469 http_highport_test(); 1470 http_dispatcher_test(); 1471 1472 http_multi_line_header_test(); 1473 http_negative_content_length_test(); 1474 1475 http_chunked_test(); 1476} 1477