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); 74static void http_badreq_cb(struct evhttp_request *req, void *arg); 75 76static struct evhttp * 77http_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, "/badrequest", http_badreq_cb, NULL); 101 evhttp_set_cb(myhttp, "/", http_dispatcher_cb, NULL); 102 103 *pport = port; 104 return (myhttp); 105} 106 107#ifndef NI_MAXSERV 108#define NI_MAXSERV 1024 109#endif 110 111static int 112http_connect(const char *address, u_short port) 113{ 114 /* Stupid code for connecting */ 115#ifdef WIN32 116 struct hostent *he; 117 struct sockaddr_in sin; 118#else 119 struct addrinfo ai, *aitop; 120 char strport[NI_MAXSERV]; 121#endif 122 struct sockaddr *sa; 123 int slen; 124 int fd; 125 126#ifdef WIN32 127 if (!(he = gethostbyname(address))) { 128 event_warn("gethostbyname"); 129 } 130 memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length); 131 sin.sin_family = AF_INET; 132 sin.sin_port = htons(port); 133 slen = sizeof(struct sockaddr_in); 134 sa = (struct sockaddr*)&sin; 135#else 136 memset(&ai, 0, sizeof (ai)); 137 ai.ai_family = AF_INET; 138 ai.ai_socktype = SOCK_STREAM; 139 snprintf(strport, sizeof (strport), "%d", port); 140 if (getaddrinfo(address, strport, &ai, &aitop) != 0) { 141 event_warn("getaddrinfo"); 142 return (-1); 143 } 144 sa = aitop->ai_addr; 145 slen = aitop->ai_addrlen; 146#endif 147 148 fd = socket(AF_INET, SOCK_STREAM, 0); 149 if (fd == -1) 150 event_err(1, "socket failed"); 151 152 if (connect(fd, sa, slen) == -1) 153 event_err(1, "connect failed"); 154 155#ifndef WIN32 156 freeaddrinfo(aitop); 157#endif 158 159 return (fd); 160} 161 162static void 163http_readcb(struct bufferevent *bev, void *arg) 164{ 165 const char *what = "This is funny"; 166 167 event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); 168 169 if (evbuffer_find(bev->input, 170 (const unsigned char*) what, strlen(what)) != NULL) { 171 struct evhttp_request *req = evhttp_request_new(NULL, NULL); 172 enum message_read_status done; 173 174 req->kind = EVHTTP_RESPONSE; 175 done = evhttp_parse_firstline(req, bev->input); 176 if (done != ALL_DATA_READ) 177 goto out; 178 179 done = evhttp_parse_headers(req, bev->input); 180 if (done != ALL_DATA_READ) 181 goto out; 182 183 if (done == 1 && 184 evhttp_find_header(req->input_headers, 185 "Content-Type") != NULL) 186 test_ok++; 187 188 out: 189 evhttp_request_free(req); 190 bufferevent_disable(bev, EV_READ); 191 if (base) 192 event_base_loopexit(base, NULL); 193 else 194 event_loopexit(NULL); 195 } 196} 197 198static void 199http_writecb(struct bufferevent *bev, void *arg) 200{ 201 if (EVBUFFER_LENGTH(bev->output) == 0) { 202 /* enable reading of the reply */ 203 bufferevent_enable(bev, EV_READ); 204 test_ok++; 205 } 206} 207 208static void 209http_errorcb(struct bufferevent *bev, short what, void *arg) 210{ 211 test_ok = -2; 212 event_loopexit(NULL); 213} 214 215void 216http_basic_cb(struct evhttp_request *req, void *arg) 217{ 218 struct evbuffer *evb = evbuffer_new(); 219 int empty = evhttp_find_header(req->input_headers, "Empty") != NULL; 220 event_debug(("%s: called\n", __func__)); 221 evbuffer_add_printf(evb, "This is funny"); 222 223 /* For multi-line headers test */ 224 { 225 const char *multi = 226 evhttp_find_header(req->input_headers,"X-multi"); 227 if (multi) { 228 if (strcmp("END", multi + strlen(multi) - 3) == 0) 229 test_ok++; 230 if (evhttp_find_header(req->input_headers, "X-Last")) 231 test_ok++; 232 } 233 } 234 235 /* injecting a bad content-length */ 236 if (evhttp_find_header(req->input_headers, "X-Negative")) 237 evhttp_add_header(req->output_headers, 238 "Content-Length", "-100"); 239 240 /* allow sending of an empty reply */ 241 evhttp_send_reply(req, HTTP_OK, "Everything is fine", 242 !empty ? evb : NULL); 243 244 evbuffer_free(evb); 245} 246 247static char const* const CHUNKS[] = { 248 "This is funny", 249 "but not hilarious.", 250 "bwv 1052" 251}; 252 253struct chunk_req_state { 254 struct evhttp_request *req; 255 int i; 256}; 257 258static void 259http_chunked_trickle_cb(int fd, short events, void *arg) 260{ 261 struct evbuffer *evb = evbuffer_new(); 262 struct chunk_req_state *state = arg; 263 struct timeval when = { 0, 0 }; 264 265 evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); 266 evhttp_send_reply_chunk(state->req, evb); 267 evbuffer_free(evb); 268 269 if (++state->i < sizeof(CHUNKS)/sizeof(CHUNKS[0])) { 270 event_once(-1, EV_TIMEOUT, 271 http_chunked_trickle_cb, state, &when); 272 } else { 273 evhttp_send_reply_end(state->req); 274 free(state); 275 } 276} 277 278static void 279http_chunked_cb(struct evhttp_request *req, void *arg) 280{ 281 struct timeval when = { 0, 0 }; 282 struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); 283 event_debug(("%s: called\n", __func__)); 284 285 memset(state, 0, sizeof(struct chunk_req_state)); 286 state->req = req; 287 288 /* generate a chunked reply */ 289 evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); 290 291 /* but trickle it across several iterations to ensure we're not 292 * assuming it comes all at once */ 293 event_once(-1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); 294} 295 296static void 297http_complete_write(int fd, short what, void *arg) 298{ 299 struct bufferevent *bev = arg; 300 const char *http_request = "host\r\n" 301 "Connection: close\r\n" 302 "\r\n"; 303 bufferevent_write(bev, http_request, strlen(http_request)); 304} 305 306static void 307http_basic_test(void) 308{ 309 struct timeval tv; 310 struct bufferevent *bev; 311 int fd; 312 const char *http_request; 313 short port = -1; 314 315 test_ok = 0; 316 fprintf(stdout, "Testing Basic HTTP Server: "); 317 318 http = http_setup(&port, NULL); 319 320 /* bind to a second socket */ 321 if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) { 322 fprintf(stdout, "FAILED (bind)\n"); 323 exit(1); 324 } 325 326 fd = http_connect("127.0.0.1", port); 327 328 /* Stupid thing to send a request */ 329 bev = bufferevent_new(fd, http_readcb, http_writecb, 330 http_errorcb, NULL); 331 332 /* first half of the http request */ 333 http_request = 334 "GET /test HTTP/1.1\r\n" 335 "Host: some"; 336 337 bufferevent_write(bev, http_request, strlen(http_request)); 338 timerclear(&tv); 339 tv.tv_usec = 10000; 340 event_once(-1, EV_TIMEOUT, http_complete_write, bev, &tv); 341 342 event_dispatch(); 343 344 if (test_ok != 3) { 345 fprintf(stdout, "FAILED\n"); 346 exit(1); 347 } 348 349 /* connect to the second port */ 350 bufferevent_free(bev); 351 EVUTIL_CLOSESOCKET(fd); 352 353 fd = http_connect("127.0.0.1", port + 1); 354 355 /* Stupid thing to send a request */ 356 bev = bufferevent_new(fd, http_readcb, http_writecb, 357 http_errorcb, NULL); 358 359 http_request = 360 "GET /test HTTP/1.1\r\n" 361 "Host: somehost\r\n" 362 "Connection: close\r\n" 363 "\r\n"; 364 365 bufferevent_write(bev, http_request, strlen(http_request)); 366 367 event_dispatch(); 368 369 bufferevent_free(bev); 370 EVUTIL_CLOSESOCKET(fd); 371 372 evhttp_free(http); 373 374 if (test_ok != 5) { 375 fprintf(stdout, "FAILED\n"); 376 exit(1); 377 } 378 379 fprintf(stdout, "OK\n"); 380} 381 382static void 383http_badreq_cb(struct evhttp_request *req, void *arg) 384{ 385 struct evbuffer *buf = evbuffer_new(); 386 387 evhttp_add_header(req->output_headers, "Content-Type", "text/xml; charset=UTF-8"); 388 evbuffer_add_printf(buf, "Hello, %s!", "127.0.0.1"); 389 390 evhttp_send_reply(req, HTTP_OK, "OK", buf); 391 evbuffer_free(buf); 392} 393 394static void 395http_badreq_errorcb(struct bufferevent *bev, short what, void *arg) 396{ 397 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); 398 /* ignore */ 399} 400 401static void 402http_badreq_readcb(struct bufferevent *bev, void *arg) 403{ 404 const char *what = "Hello, 127.0.0.1"; 405 const char *bad_request = "400 Bad Request"; 406 407 event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); 408 409 if (evbuffer_find(bev->input, 410 (const unsigned char *) bad_request, strlen(bad_request)) != NULL) { 411 event_debug(("%s: bad request detected", __func__)); 412 test_ok = -10; 413 bufferevent_disable(bev, EV_READ); 414 event_loopexit(NULL); 415 return; 416 } 417 418 if (evbuffer_find(bev->input, 419 (const unsigned char*) what, strlen(what)) != NULL) { 420 struct evhttp_request *req = evhttp_request_new(NULL, NULL); 421 enum message_read_status done; 422 423 req->kind = EVHTTP_RESPONSE; 424 done = evhttp_parse_firstline(req, bev->input); 425 if (done != ALL_DATA_READ) 426 goto out; 427 428 done = evhttp_parse_headers(req, bev->input); 429 if (done != ALL_DATA_READ) 430 goto out; 431 432 if (done == 1 && 433 evhttp_find_header(req->input_headers, 434 "Content-Type") != NULL) 435 test_ok++; 436 437 out: 438 evhttp_request_free(req); 439 evbuffer_drain(bev->input, EVBUFFER_LENGTH(bev->input)); 440 } 441 442 shutdown(bev->ev_read.ev_fd, SHUT_WR); 443} 444 445static void 446http_badreq_successcb(int fd, short what, void *arg) 447{ 448 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); 449 event_loopexit(NULL); 450} 451 452static void 453http_bad_request(void) 454{ 455 struct timeval tv; 456 struct bufferevent *bev; 457 int fd; 458 const char *http_request; 459 short port = -1; 460 461 test_ok = 0; 462 fprintf(stdout, "Testing \"Bad Request\" on connection close: "); 463 464 http = http_setup(&port, NULL); 465 466 /* bind to a second socket */ 467 if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) { 468 fprintf(stdout, "FAILED (bind)\n"); 469 exit(1); 470 } 471 472 /* NULL request test */ 473 fd = http_connect("127.0.0.1", port); 474 475 /* Stupid thing to send a request */ 476 bev = bufferevent_new(fd, http_badreq_readcb, http_writecb, 477 http_badreq_errorcb, NULL); 478 bufferevent_enable(bev, EV_READ); 479 480 /* real NULL request */ 481 http_request = ""; 482 483 shutdown(fd, SHUT_WR); 484 timerclear(&tv); 485 tv.tv_usec = 10000; 486 event_once(-1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); 487 488 event_dispatch(); 489 490 bufferevent_free(bev); 491 EVUTIL_CLOSESOCKET(fd); 492 493 if (test_ok != 0) { 494 fprintf(stdout, "FAILED\n"); 495 exit(1); 496 } 497 498 /* Second answer (BAD REQUEST) on connection close */ 499 500 /* connect to the second port */ 501 fd = http_connect("127.0.0.1", port + 1); 502 503 /* Stupid thing to send a request */ 504 bev = bufferevent_new(fd, http_badreq_readcb, http_writecb, 505 http_badreq_errorcb, NULL); 506 bufferevent_enable(bev, EV_READ); 507 508 /* first half of the http request */ 509 http_request = 510 "GET /badrequest HTTP/1.0\r\n" \ 511 "Connection: Keep-Alive\r\n" \ 512 "\r\n"; 513 514 bufferevent_write(bev, http_request, strlen(http_request)); 515 516 timerclear(&tv); 517 tv.tv_usec = 10000; 518 event_once(-1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); 519 520 event_dispatch(); 521 522 evhttp_free(http); 523 524 if (test_ok != 2) { 525 fprintf(stdout, "FAILED\n"); 526 exit(1); 527 } 528 529 fprintf(stdout, "OK\n"); 530} 531static struct evhttp_connection *delayed_client; 532 533static void 534http_delay_reply(int fd, short what, void *arg) 535{ 536 struct evhttp_request *req = arg; 537 538 evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); 539 540 ++test_ok; 541} 542 543static void 544http_large_delay_cb(struct evhttp_request *req, void *arg) 545{ 546 struct timeval tv; 547 timerclear(&tv); 548 tv.tv_sec = 3; 549 550 event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv); 551 552 /* here we close the client connection which will cause an EOF */ 553 evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF); 554} 555 556void http_request_done(struct evhttp_request *, void *); 557void http_request_empty_done(struct evhttp_request *, void *); 558 559static void 560http_connection_test(int persistent) 561{ 562 short port = -1; 563 struct evhttp_connection *evcon = NULL; 564 struct evhttp_request *req = NULL; 565 566 test_ok = 0; 567 fprintf(stdout, "Testing Request Connection Pipeline %s: ", 568 persistent ? "(persistent)" : ""); 569 570 http = http_setup(&port, NULL); 571 572 evcon = evhttp_connection_new("127.0.0.1", port); 573 if (evcon == NULL) { 574 fprintf(stdout, "FAILED\n"); 575 exit(1); 576 } 577 578 /* 579 * At this point, we want to schedule a request to the HTTP 580 * server using our make request method. 581 */ 582 583 req = evhttp_request_new(http_request_done, NULL); 584 585 /* Add the information that we care about */ 586 evhttp_add_header(req->output_headers, "Host", "somehost"); 587 588 /* We give ownership of the request to the connection */ 589 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 590 fprintf(stdout, "FAILED\n"); 591 exit(1); 592 } 593 594 event_dispatch(); 595 596 if (test_ok != 1) { 597 fprintf(stdout, "FAILED\n"); 598 exit(1); 599 } 600 601 /* try to make another request over the same connection */ 602 test_ok = 0; 603 604 req = evhttp_request_new(http_request_done, NULL); 605 606 /* Add the information that we care about */ 607 evhttp_add_header(req->output_headers, "Host", "somehost"); 608 609 /* 610 * if our connections are not supposed to be persistent; request 611 * a close from the server. 612 */ 613 if (!persistent) 614 evhttp_add_header(req->output_headers, "Connection", "close"); 615 616 /* We give ownership of the request to the connection */ 617 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 618 fprintf(stdout, "FAILED\n"); 619 exit(1); 620 } 621 622 event_dispatch(); 623 624 /* make another request: request empty reply */ 625 test_ok = 0; 626 627 req = evhttp_request_new(http_request_empty_done, NULL); 628 629 /* Add the information that we care about */ 630 evhttp_add_header(req->output_headers, "Empty", "itis"); 631 632 /* We give ownership of the request to the connection */ 633 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 634 fprintf(stdout, "FAILED\n"); 635 exit(1); 636 } 637 638 event_dispatch(); 639 640 if (test_ok != 1) { 641 fprintf(stdout, "FAILED\n"); 642 exit(1); 643 } 644 645 evhttp_connection_free(evcon); 646 evhttp_free(http); 647 648 fprintf(stdout, "OK\n"); 649} 650 651void 652http_request_done(struct evhttp_request *req, void *arg) 653{ 654 const char *what = "This is funny"; 655 656 if (req->response_code != HTTP_OK) { 657 fprintf(stderr, "FAILED\n"); 658 exit(1); 659 } 660 661 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { 662 fprintf(stderr, "FAILED\n"); 663 exit(1); 664 } 665 666 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { 667 fprintf(stderr, "FAILED\n"); 668 exit(1); 669 } 670 671 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { 672 fprintf(stderr, "FAILED\n"); 673 exit(1); 674 } 675 676 test_ok = 1; 677 event_loopexit(NULL); 678} 679 680/* test date header and content length */ 681 682void 683http_request_empty_done(struct evhttp_request *req, void *arg) 684{ 685 if (req->response_code != HTTP_OK) { 686 fprintf(stderr, "FAILED\n"); 687 exit(1); 688 } 689 690 if (evhttp_find_header(req->input_headers, "Date") == NULL) { 691 fprintf(stderr, "FAILED\n"); 692 exit(1); 693 } 694 695 696 if (evhttp_find_header(req->input_headers, "Content-Length") == NULL) { 697 fprintf(stderr, "FAILED\n"); 698 exit(1); 699 } 700 701 if (strcmp(evhttp_find_header(req->input_headers, "Content-Length"), 702 "0")) { 703 fprintf(stderr, "FAILED\n"); 704 exit(1); 705 } 706 707 if (EVBUFFER_LENGTH(req->input_buffer) != 0) { 708 fprintf(stderr, "FAILED\n"); 709 exit(1); 710 } 711 712 test_ok = 1; 713 event_loopexit(NULL); 714} 715 716/* 717 * HTTP DISPATCHER test 718 */ 719 720void 721http_dispatcher_cb(struct evhttp_request *req, void *arg) 722{ 723 724 struct evbuffer *evb = evbuffer_new(); 725 event_debug(("%s: called\n", __func__)); 726 evbuffer_add_printf(evb, "DISPATCHER_TEST"); 727 728 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); 729 730 evbuffer_free(evb); 731} 732 733static void 734http_dispatcher_test_done(struct evhttp_request *req, void *arg) 735{ 736 const char *what = "DISPATCHER_TEST"; 737 738 if (req->response_code != HTTP_OK) { 739 fprintf(stderr, "FAILED\n"); 740 exit(1); 741 } 742 743 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { 744 fprintf(stderr, "FAILED (content type)\n"); 745 exit(1); 746 } 747 748 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { 749 fprintf(stderr, "FAILED (length %zu vs %zu)\n", 750 EVBUFFER_LENGTH(req->input_buffer), strlen(what)); 751 exit(1); 752 } 753 754 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { 755 fprintf(stderr, "FAILED (data)\n"); 756 exit(1); 757 } 758 759 test_ok = 1; 760 event_loopexit(NULL); 761} 762 763static void 764http_dispatcher_test(void) 765{ 766 short port = -1; 767 struct evhttp_connection *evcon = NULL; 768 struct evhttp_request *req = NULL; 769 770 test_ok = 0; 771 fprintf(stdout, "Testing HTTP Dispatcher: "); 772 773 http = http_setup(&port, NULL); 774 775 evcon = evhttp_connection_new("127.0.0.1", port); 776 if (evcon == NULL) { 777 fprintf(stdout, "FAILED\n"); 778 exit(1); 779 } 780 781 /* also bind to local host */ 782 evhttp_connection_set_local_address(evcon, "127.0.0.1"); 783 784 /* 785 * At this point, we want to schedule an HTTP GET request 786 * server using our make request method. 787 */ 788 789 req = evhttp_request_new(http_dispatcher_test_done, NULL); 790 if (req == NULL) { 791 fprintf(stdout, "FAILED\n"); 792 exit(1); 793 } 794 795 /* Add the information that we care about */ 796 evhttp_add_header(req->output_headers, "Host", "somehost"); 797 798 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { 799 fprintf(stdout, "FAILED\n"); 800 exit(1); 801 } 802 803 event_dispatch(); 804 805 evhttp_connection_free(evcon); 806 evhttp_free(http); 807 808 if (test_ok != 1) { 809 fprintf(stdout, "FAILED: %d\n", test_ok); 810 exit(1); 811 } 812 813 fprintf(stdout, "OK\n"); 814} 815 816/* 817 * HTTP POST test. 818 */ 819 820void http_postrequest_done(struct evhttp_request *, void *); 821 822#define POST_DATA "Okay. Not really printf" 823 824static void 825http_post_test(void) 826{ 827 short port = -1; 828 struct evhttp_connection *evcon = NULL; 829 struct evhttp_request *req = NULL; 830 831 test_ok = 0; 832 fprintf(stdout, "Testing HTTP POST Request: "); 833 834 http = http_setup(&port, NULL); 835 836 evcon = evhttp_connection_new("127.0.0.1", port); 837 if (evcon == NULL) { 838 fprintf(stdout, "FAILED\n"); 839 exit(1); 840 } 841 842 /* 843 * At this point, we want to schedule an HTTP POST request 844 * server using our make request method. 845 */ 846 847 req = evhttp_request_new(http_postrequest_done, NULL); 848 if (req == NULL) { 849 fprintf(stdout, "FAILED\n"); 850 exit(1); 851 } 852 853 /* Add the information that we care about */ 854 evhttp_add_header(req->output_headers, "Host", "somehost"); 855 evbuffer_add_printf(req->output_buffer, POST_DATA); 856 857 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { 858 fprintf(stdout, "FAILED\n"); 859 exit(1); 860 } 861 862 event_dispatch(); 863 864 evhttp_connection_free(evcon); 865 evhttp_free(http); 866 867 if (test_ok != 1) { 868 fprintf(stdout, "FAILED: %d\n", test_ok); 869 exit(1); 870 } 871 872 fprintf(stdout, "OK\n"); 873} 874 875void 876http_post_cb(struct evhttp_request *req, void *arg) 877{ 878 struct evbuffer *evb; 879 event_debug(("%s: called\n", __func__)); 880 881 /* Yes, we are expecting a post request */ 882 if (req->type != EVHTTP_REQ_POST) { 883 fprintf(stdout, "FAILED (post type)\n"); 884 exit(1); 885 } 886 887 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(POST_DATA)) { 888 fprintf(stdout, "FAILED (length: %zu vs %zu)\n", 889 EVBUFFER_LENGTH(req->input_buffer), strlen(POST_DATA)); 890 exit(1); 891 } 892 893 if (memcmp(EVBUFFER_DATA(req->input_buffer), POST_DATA, 894 strlen(POST_DATA))) { 895 fprintf(stdout, "FAILED (data)\n"); 896 fprintf(stdout, "Got :%s\n", EVBUFFER_DATA(req->input_buffer)); 897 fprintf(stdout, "Want:%s\n", POST_DATA); 898 exit(1); 899 } 900 901 evb = evbuffer_new(); 902 evbuffer_add_printf(evb, "This is funny"); 903 904 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); 905 906 evbuffer_free(evb); 907} 908 909void 910http_postrequest_done(struct evhttp_request *req, void *arg) 911{ 912 const char *what = "This is funny"; 913 914 if (req == NULL) { 915 fprintf(stderr, "FAILED (timeout)\n"); 916 exit(1); 917 } 918 919 if (req->response_code != HTTP_OK) { 920 921 fprintf(stderr, "FAILED (response code)\n"); 922 exit(1); 923 } 924 925 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { 926 fprintf(stderr, "FAILED (content type)\n"); 927 exit(1); 928 } 929 930 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { 931 fprintf(stderr, "FAILED (length %zu vs %zu)\n", 932 EVBUFFER_LENGTH(req->input_buffer), strlen(what)); 933 exit(1); 934 } 935 936 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { 937 fprintf(stderr, "FAILED (data)\n"); 938 exit(1); 939 } 940 941 test_ok = 1; 942 event_loopexit(NULL); 943} 944 945static void 946http_failure_readcb(struct bufferevent *bev, void *arg) 947{ 948 const char *what = "400 Bad Request"; 949 if (evbuffer_find(bev->input, (const unsigned char*) what, strlen(what)) != NULL) { 950 test_ok = 2; 951 bufferevent_disable(bev, EV_READ); 952 event_loopexit(NULL); 953 } 954} 955 956/* 957 * Testing that the HTTP server can deal with a malformed request. 958 */ 959static void 960http_failure_test(void) 961{ 962 struct bufferevent *bev; 963 int fd; 964 const char *http_request; 965 short port = -1; 966 967 test_ok = 0; 968 fprintf(stdout, "Testing Bad HTTP Request: "); 969 970 http = http_setup(&port, NULL); 971 972 fd = http_connect("127.0.0.1", port); 973 974 /* Stupid thing to send a request */ 975 bev = bufferevent_new(fd, http_failure_readcb, http_writecb, 976 http_errorcb, NULL); 977 978 http_request = "illegal request\r\n"; 979 980 bufferevent_write(bev, http_request, strlen(http_request)); 981 982 event_dispatch(); 983 984 bufferevent_free(bev); 985 EVUTIL_CLOSESOCKET(fd); 986 987 evhttp_free(http); 988 989 if (test_ok != 2) { 990 fprintf(stdout, "FAILED\n"); 991 exit(1); 992 } 993 994 fprintf(stdout, "OK\n"); 995} 996 997static void 998close_detect_done(struct evhttp_request *req, void *arg) 999{ 1000 struct timeval tv; 1001 if (req == NULL || req->response_code != HTTP_OK) { 1002 1003 fprintf(stderr, "FAILED\n"); 1004 exit(1); 1005 } 1006 1007 test_ok = 1; 1008 1009 timerclear(&tv); 1010 tv.tv_sec = 3; /* longer than the http time out */ 1011 1012 event_loopexit(&tv); 1013} 1014 1015static void 1016close_detect_launch(int fd, short what, void *arg) 1017{ 1018 struct evhttp_connection *evcon = arg; 1019 struct evhttp_request *req; 1020 1021 req = evhttp_request_new(close_detect_done, NULL); 1022 1023 /* Add the information that we care about */ 1024 evhttp_add_header(req->output_headers, "Host", "somehost"); 1025 1026 /* We give ownership of the request to the connection */ 1027 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 1028 fprintf(stdout, "FAILED\n"); 1029 exit(1); 1030 } 1031} 1032 1033static void 1034close_detect_cb(struct evhttp_request *req, void *arg) 1035{ 1036 struct evhttp_connection *evcon = arg; 1037 struct timeval tv; 1038 1039 if (req != NULL && req->response_code != HTTP_OK) { 1040 1041 fprintf(stderr, "FAILED\n"); 1042 exit(1); 1043 } 1044 1045 timerclear(&tv); 1046 tv.tv_sec = 3; /* longer than the http time out */ 1047 1048 /* launch a new request on the persistent connection in 6 seconds */ 1049 event_once(-1, EV_TIMEOUT, close_detect_launch, evcon, &tv); 1050} 1051 1052 1053static void 1054http_close_detection(int with_delay) 1055{ 1056 short port = -1; 1057 struct evhttp_connection *evcon = NULL; 1058 struct evhttp_request *req = NULL; 1059 1060 test_ok = 0; 1061 fprintf(stdout, "Testing Connection Close Detection%s: ", 1062 with_delay ? " (with delay)" : ""); 1063 1064 http = http_setup(&port, NULL); 1065 1066 /* 2 second timeout */ 1067 evhttp_set_timeout(http, 2); 1068 1069 evcon = evhttp_connection_new("127.0.0.1", port); 1070 if (evcon == NULL) { 1071 fprintf(stdout, "FAILED\n"); 1072 exit(1); 1073 } 1074 1075 delayed_client = evcon; 1076 1077 /* 1078 * At this point, we want to schedule a request to the HTTP 1079 * server using our make request method. 1080 */ 1081 1082 req = evhttp_request_new(close_detect_cb, evcon); 1083 1084 /* Add the information that we care about */ 1085 evhttp_add_header(req->output_headers, "Host", "somehost"); 1086 1087 /* We give ownership of the request to the connection */ 1088 if (evhttp_make_request(evcon, 1089 req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) { 1090 fprintf(stdout, "FAILED\n"); 1091 exit(1); 1092 } 1093 1094 event_dispatch(); 1095 1096 if (test_ok != 1) { 1097 fprintf(stdout, "FAILED\n"); 1098 exit(1); 1099 } 1100 1101 /* at this point, the http server should have no connection */ 1102 if (TAILQ_FIRST(&http->connections) != NULL) { 1103 fprintf(stdout, "FAILED (left connections)\n"); 1104 exit(1); 1105 } 1106 1107 evhttp_connection_free(evcon); 1108 evhttp_free(http); 1109 1110 fprintf(stdout, "OK\n"); 1111} 1112 1113static void 1114http_highport_test(void) 1115{ 1116 int i = -1; 1117 struct evhttp *myhttp = NULL; 1118 1119 fprintf(stdout, "Testing HTTP Server with high port: "); 1120 1121 /* Try a few different ports */ 1122 for (i = 0; i < 50; ++i) { 1123 myhttp = evhttp_start("127.0.0.1", 65535 - i); 1124 if (myhttp != NULL) { 1125 fprintf(stdout, "OK\n"); 1126 evhttp_free(myhttp); 1127 return; 1128 } 1129 } 1130 1131 fprintf(stdout, "FAILED\n"); 1132 exit(1); 1133} 1134 1135static void 1136http_bad_header_test(void) 1137{ 1138 struct evkeyvalq headers; 1139 1140 fprintf(stdout, "Testing HTTP Header filtering: "); 1141 1142 TAILQ_INIT(&headers); 1143 1144 if (evhttp_add_header(&headers, "One", "Two") != 0) 1145 goto fail; 1146 1147 if (evhttp_add_header(&headers, "One\r", "Two") != -1) 1148 goto fail; 1149 if (evhttp_add_header(&headers, "One", "Two") != 0) 1150 goto fail; 1151 if (evhttp_add_header(&headers, "One", "Two\r\n Three") != 0) 1152 goto fail; 1153 if (evhttp_add_header(&headers, "One\r", "Two") != -1) 1154 goto fail; 1155 if (evhttp_add_header(&headers, "One\n", "Two") != -1) 1156 goto fail; 1157 if (evhttp_add_header(&headers, "One", "Two\r") != -1) 1158 goto fail; 1159 if (evhttp_add_header(&headers, "One", "Two\n") != -1) 1160 goto fail; 1161 1162 evhttp_clear_headers(&headers); 1163 1164 fprintf(stdout, "OK\n"); 1165 return; 1166fail: 1167 fprintf(stdout, "FAILED\n"); 1168 exit(1); 1169} 1170 1171static int validate_header( 1172 const struct evkeyvalq* headers, 1173 const char *key, const char *value) 1174{ 1175 const char *real_val = evhttp_find_header(headers, key); 1176 if (real_val == NULL) 1177 return (-1); 1178 if (strcmp(real_val, value) != 0) 1179 return (-1); 1180 return (0); 1181} 1182 1183static void 1184http_parse_query_test(void) 1185{ 1186 struct evkeyvalq headers; 1187 1188 fprintf(stdout, "Testing HTTP query parsing: "); 1189 1190 TAILQ_INIT(&headers); 1191 1192 evhttp_parse_query("http://www.test.com/?q=test", &headers); 1193 if (validate_header(&headers, "q", "test") != 0) 1194 goto fail; 1195 evhttp_clear_headers(&headers); 1196 1197 evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers); 1198 if (validate_header(&headers, "q", "test") != 0) 1199 goto fail; 1200 if (validate_header(&headers, "foo", "bar") != 0) 1201 goto fail; 1202 evhttp_clear_headers(&headers); 1203 1204 evhttp_parse_query("http://www.test.com/?q=test+foo", &headers); 1205 if (validate_header(&headers, "q", "test foo") != 0) 1206 goto fail; 1207 evhttp_clear_headers(&headers); 1208 1209 evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers); 1210 if (validate_header(&headers, "q", "test\nfoo") != 0) 1211 goto fail; 1212 evhttp_clear_headers(&headers); 1213 1214 evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers); 1215 if (validate_header(&headers, "q", "test\rfoo") != 0) 1216 goto fail; 1217 evhttp_clear_headers(&headers); 1218 1219 fprintf(stdout, "OK\n"); 1220 return; 1221fail: 1222 fprintf(stdout, "FAILED\n"); 1223 exit(1); 1224} 1225 1226static void 1227http_base_test(void) 1228{ 1229 struct bufferevent *bev; 1230 int fd; 1231 const char *http_request; 1232 short port = -1; 1233 1234 test_ok = 0; 1235 fprintf(stdout, "Testing HTTP Server Event Base: "); 1236 1237 base = event_init(); 1238 1239 /* 1240 * create another bogus base - which is being used by all subsequen 1241 * tests - yuck! 1242 */ 1243 event_init(); 1244 1245 http = http_setup(&port, base); 1246 1247 fd = http_connect("127.0.0.1", port); 1248 1249 /* Stupid thing to send a request */ 1250 bev = bufferevent_new(fd, http_readcb, http_writecb, 1251 http_errorcb, NULL); 1252 bufferevent_base_set(base, bev); 1253 1254 http_request = 1255 "GET /test HTTP/1.1\r\n" 1256 "Host: somehost\r\n" 1257 "Connection: close\r\n" 1258 "\r\n"; 1259 1260 bufferevent_write(bev, http_request, strlen(http_request)); 1261 1262 event_base_dispatch(base); 1263 1264 bufferevent_free(bev); 1265 EVUTIL_CLOSESOCKET(fd); 1266 1267 evhttp_free(http); 1268 1269 event_base_free(base); 1270 base = NULL; 1271 1272 if (test_ok != 2) { 1273 fprintf(stdout, "FAILED\n"); 1274 exit(1); 1275 } 1276 1277 fprintf(stdout, "OK\n"); 1278} 1279 1280/* 1281 * the server is going to reply with chunked data. 1282 */ 1283 1284static void 1285http_chunked_readcb(struct bufferevent *bev, void *arg) 1286{ 1287 /* nothing here */ 1288} 1289 1290static void 1291http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) 1292{ 1293 if (!test_ok) 1294 goto out; 1295 1296 test_ok = -1; 1297 1298 if ((what & EVBUFFER_EOF) != 0) { 1299 struct evhttp_request *req = evhttp_request_new(NULL, NULL); 1300 const char *header; 1301 enum message_read_status done; 1302 1303 req->kind = EVHTTP_RESPONSE; 1304 done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev)); 1305 if (done != ALL_DATA_READ) 1306 goto out; 1307 1308 done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev)); 1309 if (done != ALL_DATA_READ) 1310 goto out; 1311 1312 header = evhttp_find_header(req->input_headers, "Transfer-Encoding"); 1313 if (header == NULL || strcmp(header, "chunked")) 1314 goto out; 1315 1316 header = evhttp_find_header(req->input_headers, "Connection"); 1317 if (header == NULL || strcmp(header, "close")) 1318 goto out; 1319 1320 header = evbuffer_readline(EVBUFFER_INPUT(bev)); 1321 if (header == NULL) 1322 goto out; 1323 /* 13 chars */ 1324 if (strcmp(header, "d")) 1325 goto out; 1326 free((char*)header); 1327 1328 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), 1329 "This is funny", 13)) 1330 goto out; 1331 1332 evbuffer_drain(EVBUFFER_INPUT(bev), 13 + 2); 1333 1334 header = evbuffer_readline(EVBUFFER_INPUT(bev)); 1335 if (header == NULL) 1336 goto out; 1337 /* 18 chars */ 1338 if (strcmp(header, "12")) 1339 goto out; 1340 free((char *)header); 1341 1342 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), 1343 "but not hilarious.", 18)) 1344 goto out; 1345 1346 evbuffer_drain(EVBUFFER_INPUT(bev), 18 + 2); 1347 1348 header = evbuffer_readline(EVBUFFER_INPUT(bev)); 1349 if (header == NULL) 1350 goto out; 1351 /* 8 chars */ 1352 if (strcmp(header, "8")) 1353 goto out; 1354 free((char *)header); 1355 1356 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), 1357 "bwv 1052.", 8)) 1358 goto out; 1359 1360 evbuffer_drain(EVBUFFER_INPUT(bev), 8 + 2); 1361 1362 header = evbuffer_readline(EVBUFFER_INPUT(bev)); 1363 if (header == NULL) 1364 goto out; 1365 /* 0 chars */ 1366 if (strcmp(header, "0")) 1367 goto out; 1368 free((char *)header); 1369 1370 test_ok = 2; 1371 } 1372 1373out: 1374 event_loopexit(NULL); 1375} 1376 1377static void 1378http_chunked_writecb(struct bufferevent *bev, void *arg) 1379{ 1380 if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0) { 1381 /* enable reading of the reply */ 1382 bufferevent_enable(bev, EV_READ); 1383 test_ok++; 1384 } 1385} 1386 1387static void 1388http_chunked_request_done(struct evhttp_request *req, void *arg) 1389{ 1390 if (req->response_code != HTTP_OK) { 1391 fprintf(stderr, "FAILED\n"); 1392 exit(1); 1393 } 1394 1395 if (evhttp_find_header(req->input_headers, 1396 "Transfer-Encoding") == NULL) { 1397 fprintf(stderr, "FAILED\n"); 1398 exit(1); 1399 } 1400 1401 if (EVBUFFER_LENGTH(req->input_buffer) != 13 + 18 + 8) { 1402 fprintf(stderr, "FAILED\n"); 1403 exit(1); 1404 } 1405 1406 if (strncmp((char *)EVBUFFER_DATA(req->input_buffer), 1407 "This is funnybut not hilarious.bwv 1052", 1408 13 + 18 + 8)) { 1409 fprintf(stderr, "FAILED\n"); 1410 exit(1); 1411 } 1412 1413 test_ok = 1; 1414 event_loopexit(NULL); 1415} 1416 1417static void 1418http_chunked_test(void) 1419{ 1420 struct bufferevent *bev; 1421 int fd; 1422 const char *http_request; 1423 short port = -1; 1424 struct timeval tv_start, tv_end; 1425 struct evhttp_connection *evcon = NULL; 1426 struct evhttp_request *req = NULL; 1427 int i; 1428 1429 test_ok = 0; 1430 fprintf(stdout, "Testing Chunked HTTP Reply: "); 1431 1432 http = http_setup(&port, NULL); 1433 1434 fd = http_connect("127.0.0.1", port); 1435 1436 /* Stupid thing to send a request */ 1437 bev = bufferevent_new(fd, 1438 http_chunked_readcb, http_chunked_writecb, 1439 http_chunked_errorcb, NULL); 1440 1441 http_request = 1442 "GET /chunked HTTP/1.1\r\n" 1443 "Host: somehost\r\n" 1444 "Connection: close\r\n" 1445 "\r\n"; 1446 1447 bufferevent_write(bev, http_request, strlen(http_request)); 1448 1449 evutil_gettimeofday(&tv_start, NULL); 1450 1451 event_dispatch(); 1452 1453 evutil_gettimeofday(&tv_end, NULL); 1454 evutil_timersub(&tv_end, &tv_start, &tv_end); 1455 1456 if (tv_end.tv_sec >= 1) { 1457 fprintf(stdout, "FAILED (time)\n"); 1458 exit (1); 1459 } 1460 1461 1462 if (test_ok != 2) { 1463 fprintf(stdout, "FAILED\n"); 1464 exit(1); 1465 } 1466 1467 /* now try again with the regular connection object */ 1468 evcon = evhttp_connection_new("127.0.0.1", port); 1469 if (evcon == NULL) { 1470 fprintf(stdout, "FAILED\n"); 1471 exit(1); 1472 } 1473 1474 /* make two requests to check the keepalive behavior */ 1475 for (i = 0; i < 2; i++) { 1476 test_ok = 0; 1477 req = evhttp_request_new(http_chunked_request_done, NULL); 1478 1479 /* Add the information that we care about */ 1480 evhttp_add_header(req->output_headers, "Host", "somehost"); 1481 1482 /* We give ownership of the request to the connection */ 1483 if (evhttp_make_request(evcon, req, 1484 EVHTTP_REQ_GET, "/chunked") == -1) { 1485 fprintf(stdout, "FAILED\n"); 1486 exit(1); 1487 } 1488 1489 event_dispatch(); 1490 1491 if (test_ok != 1) { 1492 fprintf(stdout, "FAILED\n"); 1493 exit(1); 1494 } 1495 } 1496 1497 evhttp_connection_free(evcon); 1498 evhttp_free(http); 1499 1500 fprintf(stdout, "OK\n"); 1501} 1502 1503static void 1504http_multi_line_header_test(void) 1505{ 1506 struct bufferevent *bev; 1507 int fd; 1508 const char *http_start_request; 1509 short port = -1; 1510 1511 test_ok = 0; 1512 fprintf(stdout, "Testing HTTP Server with multi line: "); 1513 1514 http = http_setup(&port, NULL); 1515 1516 fd = http_connect("127.0.0.1", port); 1517 1518 /* Stupid thing to send a request */ 1519 bev = bufferevent_new(fd, http_readcb, http_writecb, 1520 http_errorcb, NULL); 1521 1522 http_start_request = 1523 "GET /test HTTP/1.1\r\n" 1524 "Host: somehost\r\n" 1525 "Connection: close\r\n" 1526 "X-Multi: aaaaaaaa\r\n" 1527 " a\r\n" 1528 "\tEND\r\n" 1529 "X-Last: last\r\n" 1530 "\r\n"; 1531 1532 bufferevent_write(bev, http_start_request, strlen(http_start_request)); 1533 1534 event_dispatch(); 1535 1536 bufferevent_free(bev); 1537 EVUTIL_CLOSESOCKET(fd); 1538 1539 evhttp_free(http); 1540 1541 if (test_ok != 4) { 1542 fprintf(stdout, "FAILED\n"); 1543 exit(1); 1544 } 1545 1546 fprintf(stdout, "OK\n"); 1547} 1548 1549static void 1550http_request_bad(struct evhttp_request *req, void *arg) 1551{ 1552 if (req != NULL) { 1553 fprintf(stderr, "FAILED\n"); 1554 exit(1); 1555 } 1556 1557 test_ok = 1; 1558 event_loopexit(NULL); 1559} 1560 1561static void 1562http_negative_content_length_test(void) 1563{ 1564 short port = -1; 1565 struct evhttp_connection *evcon = NULL; 1566 struct evhttp_request *req = NULL; 1567 1568 test_ok = 0; 1569 fprintf(stdout, "Testing HTTP Negative Content Length: "); 1570 1571 http = http_setup(&port, NULL); 1572 1573 evcon = evhttp_connection_new("127.0.0.1", port); 1574 if (evcon == NULL) { 1575 fprintf(stdout, "FAILED\n"); 1576 exit(1); 1577 } 1578 1579 /* 1580 * At this point, we want to schedule a request to the HTTP 1581 * server using our make request method. 1582 */ 1583 1584 req = evhttp_request_new(http_request_bad, NULL); 1585 1586 /* Cause the response to have a negative content-length */ 1587 evhttp_add_header(req->output_headers, "X-Negative", "makeitso"); 1588 1589 /* We give ownership of the request to the connection */ 1590 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { 1591 fprintf(stdout, "FAILED\n"); 1592 exit(1); 1593 } 1594 1595 event_dispatch(); 1596 1597 evhttp_free(http); 1598 1599 if (test_ok != 1) { 1600 fprintf(stdout, "FAILED\n"); 1601 exit(1); 1602 } 1603 1604 fprintf(stdout, "OK\n"); 1605} 1606 1607/* 1608 * Testing client reset of server chunked connections 1609 */ 1610 1611struct terminate_state { 1612 struct evhttp_request *req; 1613 struct bufferevent *bev; 1614 int fd; 1615} terminate_state; 1616 1617static void 1618terminate_chunked_trickle_cb(int fd, short events, void *arg) 1619{ 1620 struct terminate_state *state = arg; 1621 struct evbuffer *evb = evbuffer_new(); 1622 struct timeval tv; 1623 1624 if (evhttp_request_get_connection(state->req) == NULL) { 1625 test_ok = 1; 1626 evhttp_request_free(state->req); 1627 event_loopexit(NULL); 1628 return; 1629 } 1630 1631 evbuffer_add_printf(evb, "%p", evb); 1632 evhttp_send_reply_chunk(state->req, evb); 1633 evbuffer_free(evb); 1634 1635 tv.tv_sec = 0; 1636 tv.tv_usec = 3000; 1637 event_once(-1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); 1638} 1639 1640static void 1641terminate_chunked_cb(struct evhttp_request *req, void *arg) 1642{ 1643 struct terminate_state *state = arg; 1644 struct timeval tv; 1645 1646 state->req = req; 1647 1648 evhttp_send_reply_start(req, HTTP_OK, "OK"); 1649 1650 tv.tv_sec = 0; 1651 tv.tv_usec = 3000; 1652 event_once(-1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); 1653} 1654 1655static void 1656terminate_chunked_client(int fd, short event, void *arg) 1657{ 1658 struct terminate_state *state = arg; 1659 bufferevent_free(state->bev); 1660 EVUTIL_CLOSESOCKET(state->fd); 1661} 1662 1663static void 1664terminate_readcb(struct bufferevent *bev, void *arg) 1665{ 1666 /* just drop the data */ 1667 evbuffer_drain(bev->output, -1); 1668} 1669 1670 1671static void 1672http_terminate_chunked_test(void) 1673{ 1674 struct bufferevent *bev = NULL; 1675 struct timeval tv; 1676 const char *http_request; 1677 short port = -1; 1678 int fd = -1; 1679 1680 test_ok = 0; 1681 fprintf(stdout, "Testing Terminated Chunked Connection: "); 1682 1683 http = http_setup(&port, NULL); 1684 evhttp_del_cb(http, "/test"); 1685 evhttp_set_cb(http, "/test", terminate_chunked_cb, &terminate_state); 1686 1687 fd = http_connect("127.0.0.1", port); 1688 1689 /* Stupid thing to send a request */ 1690 bev = bufferevent_new(fd, terminate_readcb, http_writecb, 1691 http_errorcb, NULL); 1692 1693 terminate_state.fd = fd; 1694 terminate_state.bev = bev; 1695 1696 /* first half of the http request */ 1697 http_request = 1698 "GET /test HTTP/1.1\r\n" 1699 "Host: some\r\n\r\n"; 1700 1701 bufferevent_write(bev, http_request, strlen(http_request)); 1702 evutil_timerclear(&tv); 1703 tv.tv_usec = 10000; 1704 event_once(-1, EV_TIMEOUT, terminate_chunked_client, &terminate_state, 1705 &tv); 1706 1707 event_dispatch(); 1708 1709 if (test_ok != 1) { 1710 fprintf(stdout, "FAILED\n"); 1711 exit(1); 1712 } 1713 1714 fprintf(stdout, "OK\n"); 1715 1716 if (fd >= 0) 1717 EVUTIL_CLOSESOCKET(fd); 1718 if (http) 1719 evhttp_free(http); 1720} 1721 1722void 1723http_suite(void) 1724{ 1725 http_base_test(); 1726 http_bad_header_test(); 1727 http_parse_query_test(); 1728 http_basic_test(); 1729 http_connection_test(0 /* not-persistent */); 1730 http_connection_test(1 /* persistent */); 1731 http_close_detection(0 /* without delay */); 1732 http_close_detection(1 /* with delay */); 1733 http_bad_request(); 1734 http_post_test(); 1735 http_failure_test(); 1736 http_highport_test(); 1737 http_dispatcher_test(); 1738 1739 http_multi_line_header_test(); 1740 http_negative_content_length_test(); 1741 1742 http_chunked_test(); 1743 http_terminate_chunked_test(); 1744} 1745