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