xref: /openbsd-src/usr.sbin/httpd/server_http.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: server_http.c,v 1.9 2014/07/17 11:35:26 stsp Exp $	*/
2 
3 /*
4  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/time.h>
22 #include <sys/stat.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <sys/tree.h>
26 #include <sys/hash.h>
27 
28 #include <net/if.h>
29 #include <netinet/in_systm.h>
30 #include <netinet/in.h>
31 #include <netinet/ip.h>
32 #include <netinet/tcp.h>
33 #include <arpa/inet.h>
34 
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <stdio.h>
41 #include <err.h>
42 #include <pwd.h>
43 #include <event.h>
44 #include <fnmatch.h>
45 
46 #include <openssl/ssl.h>
47 
48 #include "httpd.h"
49 #include "http.h"
50 
51 static int	 server_httpmethod_cmp(const void *, const void *);
52 static int	 server_httperror_cmp(const void *, const void *);
53 void		 server_httpdesc_free(struct http_descriptor *);
54 
55 static struct httpd	*env = NULL;
56 
57 static struct http_method	 http_methods[] = HTTP_METHODS;
58 static struct http_error	 http_errors[] = HTTP_ERRORS;
59 
60 void
61 server_http(struct httpd *x_env)
62 {
63 	if (x_env != NULL)
64 		env = x_env;
65 
66 	DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid());
67 
68 	/* Sort the HTTP lookup arrays */
69 	qsort(http_methods, sizeof(http_methods) /
70 	    sizeof(http_methods[0]) - 1,
71 	    sizeof(http_methods[0]), server_httpmethod_cmp);
72 	qsort(http_errors, sizeof(http_errors) /
73 	    sizeof(http_errors[0]) - 1,
74 	    sizeof(http_errors[0]), server_httperror_cmp);
75 }
76 
77 void
78 server_http_init(struct server *srv)
79 {
80 	/* nothing */
81 }
82 
83 int
84 server_httpdesc_init(struct client *clt)
85 {
86 	struct http_descriptor	*desc;
87 
88 	if ((desc = calloc(1, sizeof(*desc))) == NULL)
89 		return (-1);
90 
91 	RB_INIT(&desc->http_headers);
92 	clt->clt_desc = desc;
93 
94 	return (0);
95 }
96 
97 void
98 server_httpdesc_free(struct http_descriptor *desc)
99 {
100 	if (desc->http_path != NULL) {
101 		free(desc->http_path);
102 		desc->http_path = NULL;
103 	}
104 	if (desc->http_query != NULL) {
105 		free(desc->http_query);
106 		desc->http_query = NULL;
107 	}
108 	if (desc->http_version != NULL) {
109 		free(desc->http_version);
110 		desc->http_version = NULL;
111 	}
112 	kv_purge(&desc->http_headers);
113 }
114 
115 void
116 server_read_http(struct bufferevent *bev, void *arg)
117 {
118 	struct client		*clt = arg;
119 	struct http_descriptor	*desc = clt->clt_desc;
120 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
121 	char			*line = NULL, *key, *value;
122 	const char		*errstr;
123 	size_t			 size, linelen;
124 	struct kv		*hdr = NULL;
125 
126 	getmonotime(&clt->clt_tv_last);
127 
128 	size = EVBUFFER_LENGTH(src);
129 	DPRINTF("%s: session %d: size %lu, to read %lld",
130 	    __func__, clt->clt_id, size, clt->clt_toread);
131 	if (!size) {
132 		clt->clt_toread = TOREAD_HTTP_HEADER;
133 		goto done;
134 	}
135 
136 	while (!clt->clt_done && (line = evbuffer_readline(src)) != NULL) {
137 		linelen = strlen(line);
138 
139 		/*
140 		 * An empty line indicates the end of the request.
141 		 * libevent already stripped the \r\n for us.
142 		 */
143 		if (!linelen) {
144 			clt->clt_done = 1;
145 			free(line);
146 			break;
147 		}
148 		key = line;
149 
150 		/* Limit the total header length minus \r\n */
151 		clt->clt_headerlen += linelen;
152 		if (clt->clt_headerlen > SERVER_MAXHEADERLENGTH) {
153 			free(line);
154 			server_abort_http(clt, 413, "request too large");
155 			return;
156 		}
157 
158 		/*
159 		 * The first line is the GET/POST/PUT/... request,
160 		 * subsequent lines are HTTP headers.
161 		 */
162 		if (++clt->clt_line == 1)
163 			value = strchr(key, ' ');
164 		else if (*key == ' ' || *key == '\t')
165 			/* Multiline headers wrap with a space or tab */
166 			value = NULL;
167 		else
168 			value = strchr(key, ':');
169 		if (value == NULL) {
170 			if (clt->clt_line == 1) {
171 				free(line);
172 				server_abort_http(clt, 400, "malformed");
173 				return;
174 			}
175 
176 			/* Append line to the last header, if present */
177 			if (kv_extend(&desc->http_headers,
178 			    desc->http_lastheader, line) == NULL) {
179 				free(line);
180 				goto fail;
181 			}
182 
183 			free(line);
184 			continue;
185 		}
186 		if (*value == ':') {
187 			*value++ = '\0';
188 			value += strspn(value, " \t\r\n");
189 		} else {
190 			*value++ = '\0';
191 		}
192 
193 		DPRINTF("%s: session %d: header '%s: %s'", __func__,
194 		    clt->clt_id, key, value);
195 
196 		/*
197 		 * Identify and handle specific HTTP request methods
198 		 */
199 		if (clt->clt_line == 1) {
200 			if ((desc->http_method = server_httpmethod_byname(key))
201 			    == HTTP_METHOD_NONE)
202 				goto fail;
203 
204 			/*
205 			 * Decode request path and query
206 			 */
207 			desc->http_path = strdup(value);
208 			if (desc->http_path == NULL) {
209 				free(line);
210 				goto fail;
211 			}
212 			desc->http_version = strchr(desc->http_path, ' ');
213 			if (desc->http_version != NULL)
214 				*desc->http_version++ = '\0';
215 			desc->http_query = strchr(desc->http_path, '?');
216 			if (desc->http_query != NULL)
217 				*desc->http_query++ = '\0';
218 
219 			/*
220 			 * Have to allocate the strings because they could
221 			 * be changed independetly by the filters later.
222 			 */
223 			if (desc->http_version != NULL &&
224 			    (desc->http_version =
225 			    strdup(desc->http_version)) == NULL) {
226 				free(line);
227 				goto fail;
228 			}
229 			if (desc->http_query != NULL &&
230 			    (desc->http_query =
231 			    strdup(desc->http_query)) == NULL) {
232 				free(line);
233 				goto fail;
234 			}
235 		} else if (desc->http_method != HTTP_METHOD_NONE &&
236 		    strcasecmp("Content-Length", key) == 0) {
237 			if (desc->http_method == HTTP_METHOD_TRACE ||
238 			    desc->http_method == HTTP_METHOD_CONNECT) {
239 				/*
240 				 * These method should not have a body
241 				 * and thus no Content-Length header.
242 				 */
243 				server_abort_http(clt, 400, "malformed");
244 				goto abort;
245 			}
246 
247 			/*
248 			 * Need to read data from the client after the
249 			 * HTTP header.
250 			 * XXX What about non-standard clients not using
251 			 * the carriage return? And some browsers seem to
252 			 * include the line length in the content-length.
253 			 */
254 			clt->clt_toread = strtonum(value, 0, LLONG_MAX,
255 			    &errstr);
256 			if (errstr) {
257 				server_abort_http(clt, 500, errstr);
258 				goto abort;
259 			}
260 		}
261 
262 		if (strcasecmp("Transfer-Encoding", key) == 0 &&
263 		    strcasecmp("chunked", value) == 0)
264 			desc->http_chunked = 1;
265 
266 		if (clt->clt_line != 1) {
267 			if ((hdr = kv_add(&desc->http_headers, key,
268 			    value)) == NULL) {
269 				free(line);
270 				goto fail;
271 			}
272 			desc->http_lastheader = hdr;
273 		}
274 
275 		free(line);
276 	}
277 	if (clt->clt_done) {
278 		if (desc->http_method == HTTP_METHOD_NONE) {
279 			server_abort_http(clt, 406, "no method");
280 			return;
281 		}
282 
283 		switch (desc->http_method) {
284 		case HTTP_METHOD_CONNECT:
285 			/* Data stream */
286 			clt->clt_toread = TOREAD_UNLIMITED;
287 			bev->readcb = server_read;
288 			break;
289 		case HTTP_METHOD_DELETE:
290 		case HTTP_METHOD_GET:
291 		case HTTP_METHOD_HEAD:
292 		case HTTP_METHOD_OPTIONS:
293 			clt->clt_toread = 0;
294 			break;
295 		case HTTP_METHOD_POST:
296 		case HTTP_METHOD_PUT:
297 		case HTTP_METHOD_RESPONSE:
298 			/* HTTP request payload */
299 			if (clt->clt_toread > 0)
300 				bev->readcb = server_read_httpcontent;
301 
302 			/* Single-pass HTTP body */
303 			if (clt->clt_toread < 0) {
304 				clt->clt_toread = TOREAD_UNLIMITED;
305 				bev->readcb = server_read;
306 			}
307 			break;
308 		default:
309 			/* HTTP handler */
310 			clt->clt_toread = TOREAD_HTTP_HEADER;
311 			bev->readcb = server_read_http;
312 			break;
313 		}
314 		if (desc->http_chunked) {
315 			/* Chunked transfer encoding */
316 			clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH;
317 			bev->readcb = server_read_httpchunks;
318 		}
319 
320  done:
321 		if (clt->clt_toread <= 0) {
322 			server_response(env, clt);
323 			return;
324 		}
325 	}
326 	if (clt->clt_done) {
327 		server_close(clt, "done");
328 		return;
329 	}
330 	if (EVBUFFER_LENGTH(src) && bev->readcb != server_read_http)
331 		bev->readcb(bev, arg);
332 	bufferevent_enable(bev, EV_READ);
333 	return;
334  fail:
335 	server_abort_http(clt, 500, strerror(errno));
336 	return;
337  abort:
338 	free(line);
339 }
340 
341 void
342 server_read_httpcontent(struct bufferevent *bev, void *arg)
343 {
344 	struct client		*clt = arg;
345 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
346 	size_t			 size;
347 
348 	getmonotime(&clt->clt_tv_last);
349 
350 	size = EVBUFFER_LENGTH(src);
351 	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
352 	    clt->clt_id, size, clt->clt_toread);
353 	if (!size)
354 		return;
355 
356 	if (clt->clt_toread > 0) {
357 		/* Read content data */
358 		if ((off_t)size > clt->clt_toread) {
359 			size = clt->clt_toread;
360 			if (server_bufferevent_write_chunk(clt,
361 			    src, size) == -1)
362 				goto fail;
363 			clt->clt_toread = 0;
364 		} else {
365 			if (server_bufferevent_write_buffer(clt, src) == -1)
366 				goto fail;
367 			clt->clt_toread -= size;
368 		}
369 		DPRINTF("%s: done, size %lu, to read %lld", __func__,
370 		    size, clt->clt_toread);
371 	}
372 	if (clt->clt_toread == 0) {
373 		clt->clt_toread = TOREAD_HTTP_HEADER;
374 		bev->readcb = server_read_http;
375 	}
376 	if (clt->clt_done)
377 		goto done;
378 	if (bev->readcb != server_read_httpcontent)
379 		bev->readcb(bev, arg);
380 	bufferevent_enable(bev, EV_READ);
381 	return;
382  done:
383 	server_close(clt, "last http content read");
384 	return;
385  fail:
386 	server_close(clt, strerror(errno));
387 }
388 
389 void
390 server_read_httpchunks(struct bufferevent *bev, void *arg)
391 {
392 	struct client		*clt = arg;
393 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
394 	char			*line;
395 	long long		 llval;
396 	size_t			 size;
397 
398 	getmonotime(&clt->clt_tv_last);
399 
400 	size = EVBUFFER_LENGTH(src);
401 	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
402 	    clt->clt_id, size, clt->clt_toread);
403 	if (!size)
404 		return;
405 
406 	if (clt->clt_toread > 0) {
407 		/* Read chunk data */
408 		if ((off_t)size > clt->clt_toread) {
409 			size = clt->clt_toread;
410 			if (server_bufferevent_write_chunk(clt, src, size)
411 			    == -1)
412 				goto fail;
413 			clt->clt_toread = 0;
414 		} else {
415 			if (server_bufferevent_write_buffer(clt, src) == -1)
416 				goto fail;
417 			clt->clt_toread -= size;
418 		}
419 		DPRINTF("%s: done, size %lu, to read %lld", __func__,
420 		    size, clt->clt_toread);
421 	}
422 	switch (clt->clt_toread) {
423 	case TOREAD_HTTP_CHUNK_LENGTH:
424 		line = evbuffer_readline(src);
425 		if (line == NULL) {
426 			/* Ignore empty line, continue */
427 			bufferevent_enable(bev, EV_READ);
428 			return;
429 		}
430 		if (strlen(line) == 0) {
431 			free(line);
432 			goto next;
433 		}
434 
435 		/*
436 		 * Read prepended chunk size in hex, ignore the trailer.
437 		 * The returned signed value must not be negative.
438 		 */
439 		if (sscanf(line, "%llx", &llval) != 1 || llval < 0) {
440 			free(line);
441 			server_close(clt, "invalid chunk size");
442 			return;
443 		}
444 
445 		if (server_bufferevent_print(clt, line) == -1 ||
446 		    server_bufferevent_print(clt, "\r\n") == -1) {
447 			free(line);
448 			goto fail;
449 		}
450 		free(line);
451 
452 		if ((clt->clt_toread = llval) == 0) {
453 			DPRINTF("%s: last chunk", __func__);
454 			clt->clt_toread = TOREAD_HTTP_CHUNK_TRAILER;
455 		}
456 		break;
457 	case TOREAD_HTTP_CHUNK_TRAILER:
458 		/* Last chunk is 0 bytes followed by trailer and empty line */
459 		line = evbuffer_readline(src);
460 		if (line == NULL) {
461 			/* Ignore empty line, continue */
462 			bufferevent_enable(bev, EV_READ);
463 			return;
464 		}
465 		if (server_bufferevent_print(clt, line) == -1 ||
466 		    server_bufferevent_print(clt, "\r\n") == -1) {
467 			free(line);
468 			goto fail;
469 		}
470 		if (strlen(line) == 0) {
471 			/* Switch to HTTP header mode */
472 			clt->clt_toread = TOREAD_HTTP_HEADER;
473 			bev->readcb = server_read_http;
474 		}
475 		free(line);
476 		break;
477 	case 0:
478 		/* Chunk is terminated by an empty newline */
479 		line = evbuffer_readline(src);
480 		if (line != NULL)
481 			free(line);
482 		if (server_bufferevent_print(clt, "\r\n") == -1)
483 			goto fail;
484 		clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH;
485 		break;
486 	}
487 
488  next:
489 	if (clt->clt_done)
490 		goto done;
491 	if (EVBUFFER_LENGTH(src))
492 		bev->readcb(bev, arg);
493 	bufferevent_enable(bev, EV_READ);
494 	return;
495 
496  done:
497 	server_close(clt, "last http chunk read (done)");
498 	return;
499  fail:
500 	server_close(clt, strerror(errno));
501 }
502 
503 void
504 server_reset_http(struct client *clt)
505 {
506 	struct http_descriptor	*desc = clt->clt_desc;
507 
508 	server_httpdesc_free(desc);
509 	desc->http_method = 0;
510 	desc->http_chunked = 0;
511 	clt->clt_headerlen = 0;
512 	clt->clt_line = 0;
513 	clt->clt_done = 0;
514 	clt->clt_bev->readcb = server_read_http;
515 }
516 
517 void
518 server_abort_http(struct client *clt, u_int code, const char *msg)
519 {
520 	struct server		*srv = clt->clt_server;
521 	struct bufferevent	*bev = clt->clt_bev;
522 	const char		*httperr = NULL, *text = "";
523 	char			*httpmsg;
524 	time_t			 t;
525 	struct tm		*lt;
526 	char			 tmbuf[32], hbuf[128];
527 	const char		*style;
528 
529 	if ((httperr = server_httperror_byid(code)) == NULL)
530 		httperr = "Unknown Error";
531 
532 	if (bev == NULL)
533 		goto done;
534 
535 	/* Some system information */
536 	if (print_host(&srv->srv_conf.ss, hbuf, sizeof(hbuf)) == NULL)
537 		goto done;
538 
539 	/* RFC 2616 "tolerates" asctime() */
540 	time(&t);
541 	lt = localtime(&t);
542 	tmbuf[0] = '\0';
543 	if (asctime_r(lt, tmbuf) != NULL)
544 		tmbuf[strlen(tmbuf) - 1] = '\0';	/* skip final '\n' */
545 
546 	/* Do not send details of the Internal Server Error */
547 	if (code != 500)
548 		text = msg;
549 
550 	/* A CSS stylesheet allows minimal customization by the user */
551 	style = "body { background-color: white; color: black; font-family: "
552 	    "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }"
553 	    "blink { animation:blink 1s; animation-iteration-count: infinite;"
554 	    "-webkit-animation:blink 1s;"
555 	    "-webkit-animation-iteration-count: infinite;}"
556 	    "@keyframes blink { 0%{opacity:0.0;} 50%{opacity:0.0;}"
557 	    "50.01%{opacity:1.0;} 100%{opacity:1.0;} }"
558 	    "@-webkit-keyframes blink { 0%{opacity:0.0;} 50%{opacity:0.0;}"
559 	    "50.01%{opacity:1.0;} 100%{opacity:1.0;} }";
560 	/* Generate simple HTTP+HTML error document */
561 	if (asprintf(&httpmsg,
562 	    "HTTP/1.0 %03d %s\r\n"
563 	    "Date: %s\r\n"
564 	    "Server: %s\r\n"
565 	    "Connection: close\r\n"
566 	    "Content-Type: text/html\r\n"
567 	    "\r\n"
568 	    "<!DOCTYPE HTML PUBLIC "
569 	    "\"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
570 	    "<html>\n"
571 	    "<head>\n"
572 	    "<title>%03d %s</title>\n"
573 	    "<style type=\"text/css\"><!--\n%s\n--></style>\n"
574 	    "</head>\n"
575 	    "<body>\n"
576 	    "<h1><blink>%s</blink></h1>\n"
577 	    "<div id='m'>%s</div>\n"
578 	    "<hr><address>%s at %s port %d</address>\n"
579 	    "</body>\n"
580 	    "</html>\n",
581 	    code, httperr, tmbuf, HTTPD_SERVERNAME,
582 	    code, httperr, style, httperr, text,
583 	    HTTPD_SERVERNAME, hbuf, ntohs(srv->srv_conf.port)) == -1)
584 		goto done;
585 
586 	/* Dump the message without checking for success */
587 	server_dump(clt, httpmsg, strlen(httpmsg));
588 	free(httpmsg);
589 
590  done:
591 	if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1)
592 		server_close(clt, msg);
593 	else {
594 		server_close(clt, httpmsg);
595 		free(httpmsg);
596 	}
597 }
598 
599 void
600 server_close_http(struct client *clt)
601 {
602 	struct http_descriptor *desc	= clt->clt_desc;
603 
604 	if (desc == NULL)
605 		return;
606 	server_httpdesc_free(desc);
607 	free(desc);
608 }
609 
610 int
611 server_response(struct httpd *httpd, struct client *clt)
612 {
613 	struct http_descriptor	*desc	= clt->clt_desc;
614 	struct kv		*kv, key;
615 	int			 ret;
616 
617 	if (desc->http_path == NULL)
618 		goto fail;
619 
620 	if (strcmp(desc->http_version, "HTTP/1.1") == 0) {
621 		/* Host header is mandatory */
622 		key.kv_key = "Host";
623 		if ((kv = kv_find(&desc->http_headers, &key)) == NULL)
624 			goto fail;
625 
626 		/* Is the connection persistent? */
627 		key.kv_key = "Connection";
628 		if ((kv = kv_find(&desc->http_headers, &key)) != NULL &&
629 		    strcasecmp("close", kv->kv_value) == 0)
630 			clt->clt_persist = 0;
631 		else
632 			clt->clt_persist++;
633 	} else {
634 		/* Is the connection persistent? */
635 		key.kv_key = "Connection";
636 		if ((kv = kv_find(&desc->http_headers, &key)) != NULL &&
637 		    strcasecmp("keep-alive", kv->kv_value) == 0)
638 			clt->clt_persist++;
639 		else
640 			clt->clt_persist = 0;
641 	}
642 
643 	if ((ret = server_file(httpd, clt)) == -1)
644 		return (-1);
645 
646 	server_reset_http(clt);
647 
648 	return (0);
649  fail:
650 	server_abort_http(clt, 400, "bad request");
651 	return (-1);
652 }
653 
654 int
655 server_response_http(struct client *clt, u_int code,
656     struct media_type *media, size_t size)
657 {
658 	struct http_descriptor	*desc = clt->clt_desc;
659 	const char		*error;
660 	struct kv		*ct, *cl;
661 
662 	if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
663 		return (-1);
664 
665 	kv_purge(&desc->http_headers);
666 
667 	/* Add error codes */
668 	if (kv_setkey(&desc->http_pathquery, "%lu", code) == -1 ||
669 	    kv_set(&desc->http_pathquery, "%s", error) == -1)
670 		return (-1);
671 
672 	/* Add headers */
673 	if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
674 		return (-1);
675 
676 	/* Is it a persistent connection? */
677 	if (clt->clt_persist) {
678 		if (kv_add(&desc->http_headers,
679 		    "Connection", "keep-alive") == NULL)
680 			return (-1);
681 	} else if (kv_add(&desc->http_headers, "Connection", "close") == NULL)
682 		return (-1);
683 
684 	/* Set media type */
685 	if ((ct = kv_add(&desc->http_headers, "Content-Type", NULL)) == NULL ||
686 	    kv_set(ct, "%s/%s",
687 	    media == NULL ? "application" : media->media_type,
688 	    media == NULL ? "octet-stream" : media->media_subtype) == -1)
689 		return (-1);
690 
691 	/* Set content length, if specified */
692 	if (size && ((cl =
693 	    kv_add(&desc->http_headers, "Content-Length", NULL)) == NULL ||
694 	    kv_set(cl, "%ld", size) == -1))
695 		return (-1);
696 
697 	/* Write completed header */
698 	if (server_writeresponse_http(clt) == -1 ||
699 	    server_bufferevent_print(clt, "\r\n") == -1 ||
700 	    server_writeheader_http(clt) == -1 ||
701 	    server_bufferevent_print(clt, "\r\n") == -1)
702 		return (-1);
703 
704 	if (desc->http_method == HTTP_METHOD_HEAD) {
705 		bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
706 		if (clt->clt_persist)
707 			clt->clt_toread = TOREAD_HTTP_HEADER;
708 		else
709 			clt->clt_toread = TOREAD_HTTP_NONE;
710 		clt->clt_done = 0;
711 		return (0);
712 	}
713 
714 	return (1);
715 }
716 
717 int
718 server_writeresponse_http(struct client *clt)
719 {
720 	struct http_descriptor	*desc = (struct http_descriptor *)clt->clt_desc;
721 
722 	DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version,
723 	    desc->http_rescode, desc->http_resmesg);
724 
725 	if (server_bufferevent_print(clt, desc->http_version) == -1 ||
726 	    server_bufferevent_print(clt, " ") == -1 ||
727 	    server_bufferevent_print(clt, desc->http_rescode) == -1 ||
728 	    server_bufferevent_print(clt, " ") == -1 ||
729 	    server_bufferevent_print(clt, desc->http_resmesg) == -1)
730 		return (-1);
731 
732 	return (0);
733 }
734 
735 int
736 server_writeheader_kv(struct client *clt, struct kv *hdr)
737 {
738 	char			*ptr;
739 	const char		*key;
740 
741 	if (hdr->kv_flags & KV_FLAG_INVALID)
742 		return (0);
743 
744 	/* The key might have been updated in the parent */
745 	if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL)
746 		key = hdr->kv_parent->kv_key;
747 	else
748 		key = hdr->kv_key;
749 
750 	ptr = hdr->kv_value;
751 	if (server_bufferevent_print(clt, key) == -1 ||
752 	    (ptr != NULL &&
753 	    (server_bufferevent_print(clt, ": ") == -1 ||
754 	    server_bufferevent_print(clt, ptr) == -1 ||
755 	    server_bufferevent_print(clt, "\r\n") == -1)))
756 		return (-1);
757 	DPRINTF("%s: %s: %s", __func__, key,
758 	    hdr->kv_value == NULL ? "" : hdr->kv_value);
759 
760 	return (0);
761 }
762 
763 int
764 server_writeheader_http(struct client *clt)
765 {
766 	struct kv		*hdr, *kv;
767 	struct http_descriptor	*desc = (struct http_descriptor *)clt->clt_desc;
768 
769 	RB_FOREACH(hdr, kvtree, &desc->http_headers) {
770 		if (server_writeheader_kv(clt, hdr) == -1)
771 			return (-1);
772 		TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) {
773 			if (server_writeheader_kv(clt, kv) == -1)
774 				return (-1);
775 		}
776 	}
777 
778 	return (0);
779 }
780 
781 enum httpmethod
782 server_httpmethod_byname(const char *name)
783 {
784 	enum httpmethod		 id = HTTP_METHOD_NONE;
785 	struct http_method	 method, *res = NULL;
786 
787 	/* Set up key */
788 	method.method_name = name;
789 
790 	if ((res = bsearch(&method, http_methods,
791 	    sizeof(http_methods) / sizeof(http_methods[0]) - 1,
792 	    sizeof(http_methods[0]), server_httpmethod_cmp)) != NULL)
793 		id = res->method_id;
794 
795 	return (id);
796 }
797 
798 const char *
799 server_httpmethod_byid(u_int id)
800 {
801 	const char	*name = NULL;
802 	int		 i;
803 
804 	for (i = 0; http_methods[i].method_name != NULL; i++) {
805 		if (http_methods[i].method_id == id) {
806 			name = http_methods[i].method_name;
807 			break;
808 		}
809 	}
810 
811 	return (name);
812 }
813 
814 static int
815 server_httpmethod_cmp(const void *a, const void *b)
816 {
817 	const struct http_method *ma = a;
818 	const struct http_method *mb = b;
819 
820 	/*
821 	 * RFC 2616 section 5.1.1 says that the method is case
822 	 * sensitive so we don't do a strcasecmp here.
823 	 */
824 	return (strcmp(ma->method_name, mb->method_name));
825 }
826 
827 const char *
828 server_httperror_byid(u_int id)
829 {
830 	struct http_error	 error, *res = NULL;
831 
832 	/* Set up key */
833 	error.error_code = (int)id;
834 
835 	res = bsearch(&error, http_errors,
836 	    sizeof(http_errors) / sizeof(http_errors[0]) - 1,
837 	    sizeof(http_errors[0]), server_httperror_cmp);
838 
839 	return (res->error_name);
840 }
841 
842 static int
843 server_httperror_cmp(const void *a, const void *b)
844 {
845 	const struct http_error *ea = a;
846 	const struct http_error *eb = b;
847 	return (ea->error_code - eb->error_code);
848 }
849