Annotation of embedaddon/libevent/test/regress_http.c, revision 1.1
1.1 ! misho 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:
! 60: extern int pair[];
! 61: extern int test_ok;
! 62:
! 63: static struct evhttp *http;
! 64: /* set if a test needs to call loopexit on a base */
! 65: static struct event_base *base;
! 66:
! 67: void http_suite(void);
! 68:
! 69: void http_basic_cb(struct evhttp_request *req, void *arg);
! 70: static void http_chunked_cb(struct evhttp_request *req, void *arg);
! 71: void http_post_cb(struct evhttp_request *req, void *arg);
! 72: void http_dispatcher_cb(struct evhttp_request *req, void *arg);
! 73: static void http_large_delay_cb(struct evhttp_request *req, void *arg);
! 74: static void http_badreq_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, "/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:
! 111: static int
! 112: http_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:
! 162: static void
! 163: http_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:
! 198: static void
! 199: http_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:
! 208: static void
! 209: http_errorcb(struct bufferevent *bev, short what, void *arg)
! 210: {
! 211: test_ok = -2;
! 212: event_loopexit(NULL);
! 213: }
! 214:
! 215: void
! 216: http_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:
! 247: static char const* const CHUNKS[] = {
! 248: "This is funny",
! 249: "but not hilarious.",
! 250: "bwv 1052"
! 251: };
! 252:
! 253: struct chunk_req_state {
! 254: struct evhttp_request *req;
! 255: int i;
! 256: };
! 257:
! 258: static void
! 259: http_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:
! 278: static void
! 279: http_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:
! 296: static void
! 297: http_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:
! 306: static void
! 307: http_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:
! 382: static void
! 383: http_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:
! 394: static void
! 395: http_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:
! 401: static void
! 402: http_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:
! 445: static void
! 446: http_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:
! 452: static void
! 453: http_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: }
! 531: static struct evhttp_connection *delayed_client;
! 532:
! 533: static void
! 534: http_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:
! 543: static void
! 544: http_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:
! 556: void http_request_done(struct evhttp_request *, void *);
! 557: void http_request_empty_done(struct evhttp_request *, void *);
! 558:
! 559: static void
! 560: http_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:
! 651: void
! 652: http_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:
! 682: void
! 683: http_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:
! 720: void
! 721: http_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:
! 733: static void
! 734: http_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:
! 763: static void
! 764: http_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:
! 820: void http_postrequest_done(struct evhttp_request *, void *);
! 821:
! 822: #define POST_DATA "Okay. Not really printf"
! 823:
! 824: static void
! 825: http_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:
! 875: void
! 876: http_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:
! 909: void
! 910: http_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:
! 945: static void
! 946: http_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: */
! 959: static void
! 960: http_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:
! 997: static void
! 998: close_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:
! 1015: static void
! 1016: close_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:
! 1033: static void
! 1034: close_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:
! 1053: static void
! 1054: http_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:
! 1113: static void
! 1114: http_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:
! 1135: static void
! 1136: http_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;
! 1166: fail:
! 1167: fprintf(stdout, "FAILED\n");
! 1168: exit(1);
! 1169: }
! 1170:
! 1171: static 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:
! 1183: static void
! 1184: http_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;
! 1221: fail:
! 1222: fprintf(stdout, "FAILED\n");
! 1223: exit(1);
! 1224: }
! 1225:
! 1226: static void
! 1227: http_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:
! 1284: static void
! 1285: http_chunked_readcb(struct bufferevent *bev, void *arg)
! 1286: {
! 1287: /* nothing here */
! 1288: }
! 1289:
! 1290: static void
! 1291: http_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:
! 1373: out:
! 1374: event_loopexit(NULL);
! 1375: }
! 1376:
! 1377: static void
! 1378: http_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:
! 1387: static void
! 1388: http_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:
! 1417: static void
! 1418: http_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:
! 1503: static void
! 1504: http_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:
! 1549: static void
! 1550: http_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:
! 1561: static void
! 1562: http_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:
! 1611: struct terminate_state {
! 1612: struct evhttp_request *req;
! 1613: struct bufferevent *bev;
! 1614: int fd;
! 1615: } terminate_state;
! 1616:
! 1617: static void
! 1618: terminate_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:
! 1640: static void
! 1641: terminate_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:
! 1655: static void
! 1656: terminate_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:
! 1663: static void
! 1664: terminate_readcb(struct bufferevent *bev, void *arg)
! 1665: {
! 1666: /* just drop the data */
! 1667: evbuffer_drain(bev->output, -1);
! 1668: }
! 1669:
! 1670:
! 1671: static void
! 1672: http_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:
! 1722: void
! 1723: http_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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>