Home | History | Annotate | Line # | Download | only in isc
httpd.c revision 1.1
      1 /*	$NetBSD: httpd.c,v 1.1 2018/08/12 12:08:23 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * This Source Code Form is subject to the terms of the Mozilla Public
      7  * License, v. 2.0. If a copy of the MPL was not distributed with this
      8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9  *
     10  * See the COPYRIGHT file distributed with this work for additional
     11  * information regarding copyright ownership.
     12  */
     13 
     14 
     15 /*! \file */
     16 
     17 #include <config.h>
     18 
     19 #include <isc/buffer.h>
     20 #include <isc/httpd.h>
     21 #include <isc/mem.h>
     22 #include <isc/print.h>
     23 #include <isc/socket.h>
     24 #include <isc/string.h>
     25 #include <isc/task.h>
     26 #include <isc/time.h>
     27 #include <isc/util.h>
     28 
     29 #include <string.h>
     30 
     31 #ifdef HAVE_ZLIB
     32 #include <zlib.h>
     33 #endif
     34 
     35 /*%
     36  * TODO:
     37  *
     38  *  o  Put in better checks to make certain things are passed in correctly.
     39  *     This includes a magic number for externally-visible structures,
     40  *     checking for NULL-ness before dereferencing, etc.
     41  *  o  Make the URL processing external functions which will fill-in a buffer
     42  *     structure we provide, or return an error and we will render a generic
     43  *     page and close the client.
     44  */
     45 
     46 #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
     47 #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
     48 
     49 #ifdef DEBUG_HTTPD
     50 #define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0)
     51 #define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0)
     52 #define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0)
     53 #else
     54 #define ENTER(x) do { } while(0)
     55 #define EXIT(x) do { } while(0)
     56 #define NOTICE(x) do { } while(0)
     57 #endif
     58 
     59 #define HTTP_RECVLEN			1024
     60 #define HTTP_SENDGROW			1024
     61 #define HTTP_SEND_MAXLEN		10240
     62 
     63 #define HTTPD_CLOSE		0x0001 /* Got a Connection: close header */
     64 #define HTTPD_FOUNDHOST		0x0002 /* Got a Host: header */
     65 #define HTTPD_KEEPALIVE		0x0004 /* Got a Connection: Keep-Alive */
     66 #define HTTPD_ACCEPT_DEFLATE   0x0008
     67 
     68 /*% http client */
     69 struct isc_httpd {
     70 	isc_httpdmgr_t	       *mgr;		/*%< our parent */
     71 	ISC_LINK(isc_httpd_t)	link;
     72 	unsigned int		state;
     73 	isc_socket_t		*sock;
     74 
     75 	/*%
     76 	 * Received data state.
     77 	 */
     78 	char			recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
     79 	isc_uint32_t		recvlen;	/*%< length recv'd */
     80 	char		       *headers;	/*%< set in process_request() */
     81 	unsigned int		method;
     82 	char		       *url;
     83 	char		       *querystring;
     84 	char		       *protocol;
     85 
     86 	/*
     87 	 * Flags on the httpd client.
     88 	 */
     89 	int			flags;
     90 
     91 	/*%
     92 	 * Transmit data state.
     93 	 *
     94 	 * This is the data buffer we will transmit.
     95 	 *
     96 	 * This free function pointer is filled in by the rendering function
     97 	 * we call.  The free function is called after the data is transmitted
     98 	 * to the client.
     99 	 *
    100 	 * The bufflist is the list of buffers we are currently transmitting.
    101 	 * The headerbuffer is where we render our headers to.  If we run out of
    102 	 * space when rendering a header, we will change the size of our
    103 	 * buffer.  We will not free it until we are finished, and will
    104 	 * allocate an additional HTTP_SENDGROW bytes per header space grow.
    105 	 *
    106 	 * We currently use three buffers total, one for the headers (which
    107 	 * we manage), another for the client to fill in (which it manages,
    108 	 * it provides the space for it, etc) -- we will pass that buffer
    109 	 * structure back to the caller, who is responsible for managing the
    110 	 * space it may have allocated as backing store for it.  This second
    111 	 * buffer is bodybuffer, and we only allocate the buffer itself, not
    112 	 * the backing store.
    113 	 * The third buffer is compbuffer, managed by us, that contains the
    114 	 * compressed HTTP data, if compression is used.
    115 	 *
    116 	 */
    117 	isc_bufferlist_t	bufflist;
    118 	isc_buffer_t		headerbuffer;
    119 	isc_buffer_t		compbuffer;
    120 
    121 	const char	       *mimetype;
    122 	unsigned int		retcode;
    123 	const char	       *retmsg;
    124 	isc_buffer_t		bodybuffer;
    125 	isc_httpdfree_t	       *freecb;
    126 	void		       *freecb_arg;
    127 };
    128 
    129 /*% lightweight socket manager for httpd output */
    130 struct isc_httpdmgr {
    131 	isc_mem_t	       *mctx;
    132 	isc_socket_t	       *sock;		/*%< listening socket */
    133 	isc_task_t	       *task;		/*%< owning task */
    134 	isc_timermgr_t	       *timermgr;
    135 
    136 	isc_httpdclientok_t    *client_ok;	/*%< client validator */
    137 	isc_httpdondestroy_t   *ondestroy;	/*%< cleanup callback */
    138 	void		       *cb_arg;		/*%< argument for the above */
    139 
    140 	unsigned int		flags;
    141 	ISC_LIST(isc_httpd_t)	running;	/*%< running clients */
    142 
    143 	isc_mutex_t		lock;
    144 
    145 	ISC_LIST(isc_httpdurl_t) urls;		/*%< urls we manage */
    146 	isc_httpdaction_t      *render_404;
    147 	isc_httpdaction_t      *render_500;
    148 };
    149 
    150 /*%
    151  * HTTP methods.
    152  */
    153 #define ISC_HTTPD_METHODUNKNOWN	0
    154 #define ISC_HTTPD_METHODGET	1
    155 #define ISC_HTTPD_METHODPOST	2
    156 
    157 /*%
    158  * Client states.
    159  *
    160  * _IDLE	The client is not doing anything at all.  This state should
    161  *		only occur just after creation, and just before being
    162  *		destroyed.
    163  *
    164  * _RECV	The client is waiting for data after issuing a socket recv().
    165  *
    166  * _RECVDONE	Data has been received, and is being processed.
    167  *
    168  * _SEND	All data for a response has completed, and a reply was
    169  *		sent via a socket send() call.
    170  *
    171  * _SENDDONE	Send is completed.
    172  *
    173  * Badly formatted state table:
    174  *
    175  *	IDLE -> RECV when client has a recv() queued.
    176  *
    177  *	RECV -> RECVDONE when recvdone event received.
    178  *
    179  *	RECVDONE -> SEND if the data for a reply is at hand.
    180  *
    181  *	SEND -> RECV when a senddone event was received.
    182  *
    183  *	At any time -> RECV on error.  If RECV fails, the client will
    184  *	self-destroy, closing the socket and freeing memory.
    185  */
    186 #define ISC_HTTPD_STATEIDLE	0
    187 #define ISC_HTTPD_STATERECV	1
    188 #define ISC_HTTPD_STATERECVDONE	2
    189 #define ISC_HTTPD_STATESEND	3
    190 #define ISC_HTTPD_STATESENDDONE	4
    191 
    192 #define ISC_HTTPD_ISRECV(c)	((c)->state == ISC_HTTPD_STATERECV)
    193 #define ISC_HTTPD_ISRECVDONE(c)	((c)->state == ISC_HTTPD_STATERECVDONE)
    194 #define ISC_HTTPD_ISSEND(c)	((c)->state == ISC_HTTPD_STATESEND)
    195 #define ISC_HTTPD_ISSENDDONE(c)	((c)->state == ISC_HTTPD_STATESENDDONE)
    196 
    197 /*%
    198  * Overall magic test that means we're not idle.
    199  */
    200 #define ISC_HTTPD_SETRECV(c)	((c)->state = ISC_HTTPD_STATERECV)
    201 #define ISC_HTTPD_SETRECVDONE(c)	((c)->state = ISC_HTTPD_STATERECVDONE)
    202 #define ISC_HTTPD_SETSEND(c)	((c)->state = ISC_HTTPD_STATESEND)
    203 #define ISC_HTTPD_SETSENDDONE(c)	((c)->state = ISC_HTTPD_STATESENDDONE)
    204 
    205 static void isc_httpd_accept(isc_task_t *, isc_event_t *);
    206 static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
    207 static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
    208 static void destroy_client(isc_httpd_t **);
    209 static isc_result_t process_request(isc_httpd_t *, int);
    210 static void httpdmgr_destroy(isc_httpdmgr_t *);
    211 static isc_result_t grow_headerspace(isc_httpd_t *);
    212 static void reset_client(isc_httpd_t *httpd);
    213 
    214 static isc_httpdaction_t render_404;
    215 static isc_httpdaction_t render_500;
    216 
    217 static void (*finishhook)(void) = NULL;
    218 
    219 static void
    220 destroy_client(isc_httpd_t **httpdp) {
    221 	isc_httpd_t *httpd = *httpdp;
    222 	isc_httpdmgr_t *httpdmgr = httpd->mgr;
    223 	isc_region_t r;
    224 
    225 	*httpdp = NULL;
    226 
    227 	LOCK(&httpdmgr->lock);
    228 
    229 	isc_socket_detach(&httpd->sock);
    230 	ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
    231 
    232 	isc_buffer_region(&httpd->headerbuffer, &r);
    233 	if (r.length > 0) {
    234 		isc_mem_put(httpdmgr->mctx, r.base, r.length);
    235 	}
    236 
    237 	isc_buffer_region(&httpd->compbuffer, &r);
    238 	if (r.length > 0) {
    239 		isc_mem_put(httpdmgr->mctx, r.base, r.length);
    240 	}
    241 
    242 	isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
    243 
    244 	UNLOCK(&httpdmgr->lock);
    245 
    246 	if (finishhook != NULL)
    247 		finishhook();
    248 
    249 	httpdmgr_destroy(httpdmgr);
    250 }
    251 
    252 isc_result_t
    253 isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
    254 		    isc_httpdclientok_t *client_ok,
    255 		    isc_httpdondestroy_t *ondestroy, void *cb_arg,
    256 		    isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdmgrp)
    257 {
    258 	isc_result_t result;
    259 	isc_httpdmgr_t *httpdmgr;
    260 
    261 	REQUIRE(mctx != NULL);
    262 	REQUIRE(sock != NULL);
    263 	REQUIRE(task != NULL);
    264 	REQUIRE(tmgr != NULL);
    265 	REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL);
    266 
    267 	httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
    268 	if (httpdmgr == NULL)
    269 		return (ISC_R_NOMEMORY);
    270 
    271 	result = isc_mutex_init(&httpdmgr->lock);
    272 	if (result != ISC_R_SUCCESS) {
    273 		isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
    274 		return (result);
    275 	}
    276 	httpdmgr->mctx = NULL;
    277 	isc_mem_attach(mctx, &httpdmgr->mctx);
    278 	httpdmgr->sock = NULL;
    279 	isc_socket_attach(sock, &httpdmgr->sock);
    280 	httpdmgr->task = NULL;
    281 	isc_task_attach(task, &httpdmgr->task);
    282 	httpdmgr->timermgr = tmgr; /* XXXMLG no attach function? */
    283 	httpdmgr->client_ok = client_ok;
    284 	httpdmgr->ondestroy = ondestroy;
    285 	httpdmgr->cb_arg = cb_arg;
    286 	httpdmgr->flags = 0;
    287 
    288 	ISC_LIST_INIT(httpdmgr->running);
    289 	ISC_LIST_INIT(httpdmgr->urls);
    290 
    291 	/* XXXMLG ignore errors on isc_socket_listen() */
    292 	result = isc_socket_listen(sock, SOMAXCONN);
    293 	if (result != ISC_R_SUCCESS) {
    294 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    295 				 "isc_socket_listen() failed: %s",
    296 				 isc_result_totext(result));
    297 		goto cleanup;
    298 	}
    299 
    300 	(void)isc_socket_filter(sock, "httpready");
    301 
    302 	result = isc_socket_accept(sock, task, isc_httpd_accept, httpdmgr);
    303 	if (result != ISC_R_SUCCESS)
    304 		goto cleanup;
    305 
    306 	httpdmgr->render_404 = render_404;
    307 	httpdmgr->render_500 = render_500;
    308 
    309 	*httpdmgrp = httpdmgr;
    310 	return (ISC_R_SUCCESS);
    311 
    312   cleanup:
    313 	isc_task_detach(&httpdmgr->task);
    314 	isc_socket_detach(&httpdmgr->sock);
    315 	isc_mem_detach(&httpdmgr->mctx);
    316 	(void)isc_mutex_destroy(&httpdmgr->lock);
    317 	isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
    318 	return (result);
    319 }
    320 
    321 static void
    322 httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) {
    323 	isc_mem_t *mctx;
    324 	isc_httpdurl_t *url;
    325 
    326 	ENTER("httpdmgr_destroy");
    327 
    328 	LOCK(&httpdmgr->lock);
    329 
    330 	if (!MSHUTTINGDOWN(httpdmgr)) {
    331 		NOTICE("httpdmgr_destroy not shutting down yet");
    332 		UNLOCK(&httpdmgr->lock);
    333 		return;
    334 	}
    335 
    336 	/*
    337 	 * If all clients are not shut down, don't do anything yet.
    338 	 */
    339 	if (!ISC_LIST_EMPTY(httpdmgr->running)) {
    340 		NOTICE("httpdmgr_destroy clients still active");
    341 		UNLOCK(&httpdmgr->lock);
    342 		return;
    343 	}
    344 
    345 	NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
    346 
    347 	isc_socket_detach(&httpdmgr->sock);
    348 	isc_task_detach(&httpdmgr->task);
    349 	httpdmgr->timermgr = NULL;
    350 
    351 	/*
    352 	 * Clear out the list of all actions we know about.  Just free the
    353 	 * memory.
    354 	 */
    355 	url = ISC_LIST_HEAD(httpdmgr->urls);
    356 	while (url != NULL) {
    357 		isc_mem_free(httpdmgr->mctx, url->url);
    358 		ISC_LIST_UNLINK(httpdmgr->urls, url, link);
    359 		isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
    360 		url = ISC_LIST_HEAD(httpdmgr->urls);
    361 	}
    362 
    363 	UNLOCK(&httpdmgr->lock);
    364 	(void)isc_mutex_destroy(&httpdmgr->lock);
    365 
    366 	if (httpdmgr->ondestroy != NULL)
    367 		(httpdmgr->ondestroy)(httpdmgr->cb_arg);
    368 
    369 	mctx = httpdmgr->mctx;
    370 	isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
    371 
    372 	EXIT("httpdmgr_destroy");
    373 }
    374 
    375 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
    376 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
    377 
    378 /*
    379  * Look for the given header in headers.
    380  * If value is specified look for it terminated with a character in eov.
    381  */
    382 static isc_boolean_t
    383 have_header(isc_httpd_t *httpd, const char *header, const char *value,
    384 	    const char *eov)
    385 {
    386 	char *cr, *nl, *h;
    387 	size_t hlen, vlen = 0;
    388 
    389 	h = httpd->headers;
    390 	hlen = strlen(header);
    391 	if (value != NULL) {
    392 		INSIST(eov != NULL);
    393 		vlen = strlen(value);
    394 	}
    395 
    396 	for (;;) {
    397 		if (strncasecmp(h, header, hlen) != 0) {
    398 			/*
    399 			 * Skip to next line;
    400 			 */
    401 			cr = strchr(h, '\r');
    402 			if (cr != NULL && cr[1] == '\n')
    403 				cr++;
    404 			nl = strchr(h, '\n');
    405 
    406 			/* last header? */
    407 			h = cr;
    408 			if (h == NULL || (nl != NULL && nl < h))
    409 				h = nl;
    410 			if (h == NULL)
    411 				return (ISC_FALSE);
    412 			h++;
    413 			continue;
    414 		}
    415 
    416 		if (value == NULL)
    417 			return (ISC_TRUE);
    418 
    419 		/*
    420 		 * Skip optional leading white space.
    421 		 */
    422 		h += hlen;
    423 		while (*h == ' ' || *h == '\t')
    424 			h++;
    425 		/*
    426 		 * Terminate token search on NULL or EOL.
    427 		 */
    428 		while (*h != 0 && *h != '\r' && *h != '\n') {
    429 			if (strncasecmp(h, value, vlen) == 0)
    430 				if (strchr(eov, h[vlen]) != NULL)
    431 					return (ISC_TRUE);
    432 			/*
    433 			 * Skip to next token.
    434 			 */
    435 			h += strcspn(h, eov);
    436 			if (h[0] == '\r' && h[1] == '\n')
    437 				h++;
    438 			if (h[0] != 0)
    439 				h++;
    440 		}
    441 		return (ISC_FALSE);
    442 	}
    443 }
    444 
    445 static isc_result_t
    446 process_request(isc_httpd_t *httpd, int length) {
    447 	char *s;
    448 	char *p;
    449 	int delim;
    450 
    451 	ENTER("request");
    452 
    453 	httpd->recvlen += length;
    454 
    455 	httpd->recvbuf[httpd->recvlen] = 0;
    456 	httpd->headers = NULL;
    457 
    458 	/*
    459 	 * If we don't find a blank line in our buffer, return that we need
    460 	 * more data.
    461 	 */
    462 	s = strstr(httpd->recvbuf, "\r\n\r\n");
    463 	delim = 2;
    464 	if (s == NULL) {
    465 		s = strstr(httpd->recvbuf, "\n\n");
    466 		delim = 1;
    467 	}
    468 	if (s == NULL)
    469 		return (ISC_R_NOTFOUND);
    470 
    471 	/*
    472 	 * NUL terminate request at the blank line.
    473 	 */
    474 	s[delim] = 0;
    475 
    476 	/*
    477 	 * Determine if this is a POST or GET method.  Any other values will
    478 	 * cause an error to be returned.
    479 	 */
    480 	if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
    481 		httpd->method = ISC_HTTPD_METHODGET;
    482 		p = httpd->recvbuf + 4;
    483 	} else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
    484 		httpd->method = ISC_HTTPD_METHODPOST;
    485 		p = httpd->recvbuf + 5;
    486 	} else {
    487 		return (ISC_R_RANGE);
    488 	}
    489 
    490 	/*
    491 	 * From now on, p is the start of our buffer.
    492 	 */
    493 
    494 	/*
    495 	 * Extract the URL.
    496 	 */
    497 	s = p;
    498 	while (LENGTHOK(s) && BUFLENOK(s) &&
    499 	       (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
    500 		s++;
    501 	if (!LENGTHOK(s))
    502 		return (ISC_R_NOTFOUND);
    503 	if (!BUFLENOK(s))
    504 		return (ISC_R_NOMEMORY);
    505 	*s = 0;
    506 
    507 	/*
    508 	 * Make the URL relative.
    509 	 */
    510 	if ((strncmp(p, "http:/", 6) == 0)
    511 	    || (strncmp(p, "https:/", 7) == 0)) {
    512 		/* Skip first / */
    513 		while (*p != '/' && *p != 0)
    514 			p++;
    515 		if (*p == 0)
    516 			return (ISC_R_RANGE);
    517 		p++;
    518 		/* Skip second / */
    519 		while (*p != '/' && *p != 0)
    520 			p++;
    521 		if (*p == 0)
    522 			return (ISC_R_RANGE);
    523 		p++;
    524 		/* Find third / */
    525 		while (*p != '/' && *p != 0)
    526 			p++;
    527 		if (*p == 0) {
    528 			p--;
    529 			*p = '/';
    530 		}
    531 	}
    532 
    533 	httpd->url = p;
    534 	p = s + 1;
    535 	s = p;
    536 
    537 	/*
    538 	 * Now, see if there is a ? mark in the URL.  If so, this is
    539 	 * part of the query string, and we will split it from the URL.
    540 	 */
    541 	httpd->querystring = strchr(httpd->url, '?');
    542 	if (httpd->querystring != NULL) {
    543 		*(httpd->querystring) = 0;
    544 		httpd->querystring++;
    545 	}
    546 
    547 	/*
    548 	 * Extract the HTTP/1.X protocol.  We will bounce on anything but
    549 	 * HTTP/1.0 or HTTP/1.1 for now.
    550 	 */
    551 	while (LENGTHOK(s) && BUFLENOK(s) &&
    552 	       (*s != '\n' && *s != '\r' && *s != '\0'))
    553 		s++;
    554 	if (!LENGTHOK(s))
    555 		return (ISC_R_NOTFOUND);
    556 	if (!BUFLENOK(s))
    557 		return (ISC_R_NOMEMORY);
    558 	/*
    559 	 * Check that we have the expected eol delimiter.
    560 	 */
    561 	if (strncmp(s, delim == 1 ? "\n" : "\r\n", delim) != 0)
    562 		return (ISC_R_RANGE);
    563 	*s = 0;
    564 	if ((strncmp(p, "HTTP/1.0", 8) != 0)
    565 	    && (strncmp(p, "HTTP/1.1", 8) != 0))
    566 		return (ISC_R_RANGE);
    567 	httpd->protocol = p;
    568 	p = s + delim;	/* skip past eol */
    569 	s = p;
    570 
    571 	httpd->headers = s;
    572 
    573 	if (have_header(httpd, "Connection:", "close", ", \t\r\n"))
    574 		httpd->flags |= HTTPD_CLOSE;
    575 
    576 	if (have_header(httpd, "Host:", NULL, NULL))
    577 		httpd->flags |= HTTPD_FOUNDHOST;
    578 
    579 	if (strncmp(httpd->protocol, "HTTP/1.0", 8) == 0) {
    580 		if (have_header(httpd, "Connection:", "Keep-Alive",
    581 				", \t\r\n"))
    582 			httpd->flags |= HTTPD_KEEPALIVE;
    583 		else
    584 			httpd->flags |= HTTPD_CLOSE;
    585 	}
    586 
    587 	/*
    588 	 * Check for Accept-Encoding:
    589 	 */
    590 #ifdef HAVE_ZLIB
    591 	if (have_header(httpd, "Accept-Encoding:", "deflate", ";, \t\r\n"))
    592 		httpd->flags |= HTTPD_ACCEPT_DEFLATE;
    593 #endif
    594 
    595 	/*
    596 	 * Standards compliance hooks here.
    597 	 */
    598 	if (strcmp(httpd->protocol, "HTTP/1.1") == 0
    599 	    && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
    600 		return (ISC_R_RANGE);
    601 
    602 	EXIT("request");
    603 
    604 	return (ISC_R_SUCCESS);
    605 }
    606 
    607 static void
    608 isc_httpd_accept(isc_task_t *task, isc_event_t *ev) {
    609 	isc_result_t result;
    610 	isc_httpdmgr_t *httpdmgr = ev->ev_arg;
    611 	isc_httpd_t *httpd;
    612 	isc_region_t r;
    613 	isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
    614 	isc_sockaddr_t peeraddr;
    615 	char *headerdata;
    616 
    617 	ENTER("accept");
    618 
    619 	LOCK(&httpdmgr->lock);
    620 	if (MSHUTTINGDOWN(httpdmgr)) {
    621 		NOTICE("accept shutting down, goto out");
    622 		goto out;
    623 	}
    624 
    625 	if (nev->result == ISC_R_CANCELED) {
    626 		NOTICE("accept canceled, goto out");
    627 		goto out;
    628 	}
    629 
    630 	if (nev->result != ISC_R_SUCCESS) {
    631 		/* XXXMLG log failure */
    632 		NOTICE("accept returned failure, goto requeue");
    633 		goto requeue;
    634 	}
    635 
    636 	(void)isc_socket_getpeername(nev->newsocket, &peeraddr);
    637 	if (httpdmgr->client_ok != NULL &&
    638 	    !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
    639 		isc_socket_detach(&nev->newsocket);
    640 		goto requeue;
    641 	}
    642 
    643 	httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
    644 	if (httpd == NULL) {
    645 		/* XXXMLG log failure */
    646 		NOTICE("accept failed to allocate memory, goto requeue");
    647 		isc_socket_detach(&nev->newsocket);
    648 		goto requeue;
    649 	}
    650 
    651 	httpd->mgr = httpdmgr;
    652 	ISC_LINK_INIT(httpd, link);
    653 	ISC_LIST_APPEND(httpdmgr->running, httpd, link);
    654 	ISC_HTTPD_SETRECV(httpd);
    655 	httpd->sock = nev->newsocket;
    656 	isc_socket_setname(httpd->sock, "httpd", NULL);
    657 	httpd->flags = 0;
    658 
    659 	/*
    660 	 * Initialize the buffer for our headers.
    661 	 */
    662 	headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
    663 	if (headerdata == NULL) {
    664 		isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
    665 		isc_socket_detach(&nev->newsocket);
    666 		goto requeue;
    667 	}
    668 	isc_buffer_init(&httpd->headerbuffer, headerdata, HTTP_SENDGROW);
    669 
    670 	ISC_LIST_INIT(httpd->bufflist);
    671 
    672 	isc_buffer_initnull(&httpd->compbuffer);
    673 	isc_buffer_initnull(&httpd->bodybuffer);
    674 	reset_client(httpd);
    675 
    676 	r.base = (unsigned char *)httpd->recvbuf;
    677 	r.length = HTTP_RECVLEN - 1;
    678 	result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
    679 				 httpd);
    680 	/* FIXME!!! */
    681 	POST(result);
    682 	NOTICE("accept queued recv on socket");
    683 
    684  requeue:
    685 	result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
    686 				   httpdmgr);
    687 	if (result != ISC_R_SUCCESS) {
    688 		/* XXXMLG what to do?  Log failure... */
    689 		NOTICE("accept could not reaccept due to failure");
    690 	}
    691 
    692  out:
    693 	UNLOCK(&httpdmgr->lock);
    694 
    695 	httpdmgr_destroy(httpdmgr);
    696 
    697 	isc_event_free(&ev);
    698 
    699 	EXIT("accept");
    700 }
    701 
    702 static isc_result_t
    703 render_404(const char *url, isc_httpdurl_t *urlinfo,
    704 	   const char *querystring, const char *headers, void *arg,
    705 	   unsigned int *retcode, const char **retmsg,
    706 	   const char **mimetype, isc_buffer_t *b,
    707 	   isc_httpdfree_t **freecb, void **freecb_args)
    708 {
    709 	static char msg[] = "No such URL.\r\n";
    710 
    711 	UNUSED(url);
    712 	UNUSED(urlinfo);
    713 	UNUSED(querystring);
    714 	UNUSED(headers);
    715 	UNUSED(arg);
    716 
    717 	*retcode = 404;
    718 	*retmsg = "No such URL";
    719 	*mimetype = "text/plain";
    720 	isc_buffer_reinit(b, msg, strlen(msg));
    721 	isc_buffer_add(b, strlen(msg));
    722 	*freecb = NULL;
    723 	*freecb_args = NULL;
    724 
    725 	return (ISC_R_SUCCESS);
    726 }
    727 
    728 static isc_result_t
    729 render_500(const char *url, isc_httpdurl_t *urlinfo,
    730 	   const char *querystring, const char *headers, void *arg,
    731 	   unsigned int *retcode, const char **retmsg,
    732 	   const char **mimetype, isc_buffer_t *b,
    733 	   isc_httpdfree_t **freecb, void **freecb_args)
    734 {
    735 	static char msg[] = "Internal server failure.\r\n";
    736 
    737 	UNUSED(url);
    738 	UNUSED(urlinfo);
    739 	UNUSED(querystring);
    740 	UNUSED(headers);
    741 	UNUSED(arg);
    742 
    743 	*retcode = 500;
    744 	*retmsg = "Internal server failure";
    745 	*mimetype = "text/plain";
    746 	isc_buffer_reinit(b, msg, strlen(msg));
    747 	isc_buffer_add(b, strlen(msg));
    748 	*freecb = NULL;
    749 	*freecb_args = NULL;
    750 
    751 	return (ISC_R_SUCCESS);
    752 }
    753 
    754 #ifdef HAVE_ZLIB
    755 /*%<
    756  * Reallocates compbuffer to size, does nothing if compbuffer is already
    757  * larger than size.
    758  *
    759  * Requires:
    760  *\li	httpd a valid isc_httpd_t object
    761  *
    762  * Returns:
    763  *\li	#ISC_R_SUCCESS		-- all is well.
    764  *\li	#ISC_R_NOMEMORY		-- not enough memory to extend buffer
    765  */
    766 static isc_result_t
    767 alloc_compspace(isc_httpd_t *httpd, unsigned int size) {
    768 	char *newspace;
    769 	isc_region_t r;
    770 
    771 	isc_buffer_region(&httpd->compbuffer, &r);
    772 	if (size < r.length) {
    773 		return (ISC_R_SUCCESS);
    774 	}
    775 
    776 	newspace = isc_mem_get(httpd->mgr->mctx, size);
    777 	if (newspace == NULL)
    778 		return (ISC_R_NOMEMORY);
    779 	isc_buffer_reinit(&httpd->compbuffer, newspace, size);
    780 
    781 	if (r.base != NULL) {
    782 		isc_mem_put(httpd->mgr->mctx, r.base, r.length);
    783 	}
    784 
    785 	return (ISC_R_SUCCESS);
    786 }
    787 
    788 /*%<
    789  * Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it
    790  * if necessary.
    791  *
    792  * Requires:
    793  *\li	httpd a valid isc_httpd_t object
    794  *
    795  * Returns:
    796  *\li	#ISC_R_SUCCESS	  -- all is well.
    797  *\li	#ISC_R_NOMEMORY	  -- not enough memory to compress data
    798  *\li	#ISC_R_FAILURE	  -- error during compression or compressed
    799  *			     data would be larger than input data
    800  */
    801 static isc_result_t
    802 isc_httpd_compress(isc_httpd_t *httpd) {
    803 	z_stream zstr;
    804 	isc_region_t r;
    805 	isc_result_t result;
    806 	int ret;
    807 	int inputlen;
    808 
    809 	inputlen = isc_buffer_usedlength(&httpd->bodybuffer);
    810 	result = alloc_compspace(httpd, inputlen);
    811 	if (result != ISC_R_SUCCESS) {
    812 		return result;
    813 	}
    814 	isc_buffer_region(&httpd->compbuffer, &r);
    815 
    816 	/*
    817 	 * We're setting output buffer size to input size so it fails if the
    818 	 * compressed data size would be bigger than the input size.
    819 	 */
    820 	memset(&zstr, 0, sizeof(zstr));
    821 	zstr.total_in = zstr.avail_in =
    822 			zstr.total_out = zstr.avail_out = inputlen;
    823 
    824 	zstr.next_in = isc_buffer_base(&httpd->bodybuffer);
    825 	zstr.next_out = r.base;
    826 
    827 	ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION);
    828 	if (ret == Z_OK) {
    829 		ret = deflate(&zstr, Z_FINISH);
    830 	}
    831 	deflateEnd(&zstr);
    832 	if (ret == Z_STREAM_END) {
    833 		isc_buffer_add(&httpd->compbuffer, inputlen - zstr.avail_out);
    834 		return (ISC_R_SUCCESS);
    835 	} else {
    836 		return (ISC_R_FAILURE);
    837 	}
    838 }
    839 #endif
    840 
    841 static void
    842 isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) {
    843 	isc_region_t r;
    844 	isc_result_t result;
    845 	isc_httpd_t *httpd = ev->ev_arg;
    846 	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
    847 	isc_httpdurl_t *url;
    848 	isc_time_t now;
    849 	isc_boolean_t is_compressed = ISC_FALSE;
    850 	char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
    851 
    852 	ENTER("recv");
    853 
    854 	INSIST(ISC_HTTPD_ISRECV(httpd));
    855 
    856 	if (sev->result != ISC_R_SUCCESS) {
    857 		NOTICE("recv destroying client");
    858 		destroy_client(&httpd);
    859 		goto out;
    860 	}
    861 
    862 	result = process_request(httpd, sev->n);
    863 	if (result == ISC_R_NOTFOUND) {
    864 		if (httpd->recvlen >= HTTP_RECVLEN - 1) {
    865 			destroy_client(&httpd);
    866 			goto out;
    867 		}
    868 		r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
    869 		r.length = HTTP_RECVLEN - httpd->recvlen - 1;
    870 		/* check return code? */
    871 		(void)isc_socket_recv(httpd->sock, &r, 1, task,
    872 				      isc_httpd_recvdone, httpd);
    873 		goto out;
    874 	} else if (result != ISC_R_SUCCESS) {
    875 		destroy_client(&httpd);
    876 		goto out;
    877 	}
    878 
    879 	ISC_HTTPD_SETSEND(httpd);
    880 
    881 	/*
    882 	 * XXXMLG Call function here.  Provide an add-header function
    883 	 * which will append the common headers to a response we generate.
    884 	 */
    885 	isc_buffer_initnull(&httpd->bodybuffer);
    886 	isc_time_now(&now);
    887 	isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
    888 	url = ISC_LIST_HEAD(httpd->mgr->urls);
    889 	while (url != NULL) {
    890 		if (strcmp(httpd->url, url->url) == 0)
    891 			break;
    892 		url = ISC_LIST_NEXT(url, link);
    893 	}
    894 	if (url == NULL)
    895 		result = httpd->mgr->render_404(httpd->url, NULL,
    896 						httpd->querystring,
    897 						NULL, NULL,
    898 						&httpd->retcode,
    899 						&httpd->retmsg,
    900 						&httpd->mimetype,
    901 						&httpd->bodybuffer,
    902 						&httpd->freecb,
    903 						&httpd->freecb_arg);
    904 	else
    905 		result = url->action(httpd->url, url,
    906 				     httpd->querystring,
    907 				     httpd->headers,
    908 				     url->action_arg,
    909 				     &httpd->retcode, &httpd->retmsg,
    910 				     &httpd->mimetype, &httpd->bodybuffer,
    911 				     &httpd->freecb, &httpd->freecb_arg);
    912 	if (result != ISC_R_SUCCESS) {
    913 		result = httpd->mgr->render_500(httpd->url, url,
    914 						httpd->querystring,
    915 						NULL, NULL,
    916 						&httpd->retcode,
    917 						&httpd->retmsg,
    918 						&httpd->mimetype,
    919 						&httpd->bodybuffer,
    920 						&httpd->freecb,
    921 						&httpd->freecb_arg);
    922 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    923 	}
    924 
    925 #ifdef HAVE_ZLIB
    926 	if (httpd->flags & HTTPD_ACCEPT_DEFLATE) {
    927 			result = isc_httpd_compress(httpd);
    928 			if (result == ISC_R_SUCCESS) {
    929 				is_compressed = ISC_TRUE;
    930 			}
    931 	}
    932 #endif
    933 
    934 	isc_httpd_response(httpd);
    935 	if ((httpd->flags & HTTPD_KEEPALIVE) != 0)
    936 		isc_httpd_addheader(httpd, "Connection", "Keep-Alive");
    937 	isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
    938 	isc_httpd_addheader(httpd, "Date", datebuf);
    939 	isc_httpd_addheader(httpd, "Expires", datebuf);
    940 
    941 	if (url != NULL && url->isstatic) {
    942 		char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
    943 		isc_time_formathttptimestamp(&url->loadtime,
    944 					     loadbuf, sizeof(loadbuf));
    945 		isc_httpd_addheader(httpd, "Last-Modified", loadbuf);
    946 		isc_httpd_addheader(httpd, "Cache-Control: public", NULL);
    947 	} else {
    948 		isc_httpd_addheader(httpd, "Last-Modified", datebuf);
    949 		isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
    950 		isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
    951 	}
    952 
    953 	isc_httpd_addheader(httpd, "Server: libisc", NULL);
    954 
    955 	if (is_compressed == ISC_TRUE) {
    956 		isc_httpd_addheader(httpd, "Content-Encoding", "deflate");
    957 		isc_httpd_addheaderuint(httpd, "Content-Length",
    958 					isc_buffer_usedlength(&httpd->compbuffer));
    959 	} else {
    960 		isc_httpd_addheaderuint(httpd, "Content-Length",
    961 		isc_buffer_usedlength(&httpd->bodybuffer));
    962 	}
    963 
    964 	isc_httpd_endheaders(httpd);  /* done */
    965 
    966 	ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
    967 	/*
    968 	 * Link the data buffer into our send queue, should we have any data
    969 	 * rendered into it.  If no data is present, we won't do anything
    970 	 * with the buffer.
    971 	 */
    972 	if (is_compressed == ISC_TRUE) {
    973 		ISC_LIST_APPEND(httpd->bufflist, &httpd->compbuffer, link);
    974 	} else {
    975 		if (isc_buffer_length(&httpd->bodybuffer) > 0) {
    976 				ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
    977 		}
    978 	}
    979 
    980 	/* check return code? */
    981 	(void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
    982 			       isc_httpd_senddone, httpd);
    983 
    984  out:
    985 	isc_event_free(&ev);
    986 	EXIT("recv");
    987 }
    988 
    989 void
    990 isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
    991 	isc_httpdmgr_t *httpdmgr;
    992 	isc_httpd_t *httpd;
    993 	httpdmgr = *httpdmgrp;
    994 	*httpdmgrp = NULL;
    995 
    996 	ENTER("isc_httpdmgr_shutdown");
    997 
    998 	LOCK(&httpdmgr->lock);
    999 
   1000 	MSETSHUTTINGDOWN(httpdmgr);
   1001 
   1002 	isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
   1003 
   1004 	httpd = ISC_LIST_HEAD(httpdmgr->running);
   1005 	while (httpd != NULL) {
   1006 		isc_socket_cancel(httpd->sock, httpdmgr->task,
   1007 				  ISC_SOCKCANCEL_ALL);
   1008 		httpd = ISC_LIST_NEXT(httpd, link);
   1009 	}
   1010 
   1011 	UNLOCK(&httpdmgr->lock);
   1012 
   1013 	EXIT("isc_httpdmgr_shutdown");
   1014 }
   1015 
   1016 static isc_result_t
   1017 grow_headerspace(isc_httpd_t *httpd) {
   1018 	char *newspace;
   1019 	unsigned int newlen;
   1020 	isc_region_t r;
   1021 
   1022 	isc_buffer_region(&httpd->headerbuffer, &r);
   1023 	newlen = r.length + HTTP_SENDGROW;
   1024 	if (newlen > HTTP_SEND_MAXLEN)
   1025 		return (ISC_R_NOSPACE);
   1026 
   1027 	newspace = isc_mem_get(httpd->mgr->mctx, newlen);
   1028 	if (newspace == NULL)
   1029 		return (ISC_R_NOMEMORY);
   1030 
   1031 	isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
   1032 
   1033 	isc_mem_put(httpd->mgr->mctx, r.base, r.length);
   1034 
   1035 	return (ISC_R_SUCCESS);
   1036 }
   1037 
   1038 isc_result_t
   1039 isc_httpd_response(isc_httpd_t *httpd) {
   1040 	isc_result_t result;
   1041 	unsigned int needlen;
   1042 
   1043 	needlen = strlen(httpd->protocol) + 1; /* protocol + space */
   1044 	needlen += 3 + 1;  /* room for response code, always 3 bytes */
   1045 	needlen += strlen(httpd->retmsg) + 2;  /* return msg + CRLF */
   1046 
   1047 	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
   1048 		result = grow_headerspace(httpd);
   1049 		if (result != ISC_R_SUCCESS)
   1050 			return (result);
   1051 	}
   1052 
   1053 	return (isc_buffer_printf(&httpd->headerbuffer, "%s %03u %s\r\n",
   1054 				   httpd->protocol, httpd->retcode,
   1055 				   httpd->retmsg));
   1056 }
   1057 
   1058 isc_result_t
   1059 isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
   1060 		    const char *val)
   1061 {
   1062 	isc_result_t result;
   1063 	unsigned int needlen;
   1064 
   1065 	needlen = strlen(name); /* name itself */
   1066 	if (val != NULL)
   1067 		needlen += 2 + strlen(val); /* :<space> and val */
   1068 	needlen += 2; /* CRLF */
   1069 
   1070 	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
   1071 		result = grow_headerspace(httpd);
   1072 		if (result != ISC_R_SUCCESS)
   1073 			return (result);
   1074 	}
   1075 
   1076 	if (val != NULL) {
   1077 		return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n",
   1078 					   name, val));
   1079 	} else {
   1080 		return (isc_buffer_printf(&httpd->headerbuffer, "%s\r\n",
   1081 					   name));
   1082 	}
   1083 }
   1084 
   1085 isc_result_t
   1086 isc_httpd_endheaders(isc_httpd_t *httpd) {
   1087 	isc_result_t result;
   1088 
   1089 	while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
   1090 		result = grow_headerspace(httpd);
   1091 		if (result != ISC_R_SUCCESS)
   1092 			return (result);
   1093 	}
   1094 
   1095 	return (isc_buffer_printf(&httpd->headerbuffer, "\r\n"));
   1096 }
   1097 
   1098 isc_result_t
   1099 isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
   1100 	isc_result_t result;
   1101 	unsigned int needlen;
   1102 	char buf[sizeof "18446744073709551616"];
   1103 
   1104 	snprintf(buf, sizeof(buf), "%d", val);
   1105 
   1106 	needlen = strlen(name); /* name itself */
   1107 	needlen += 2 + strlen(buf); /* :<space> and val */
   1108 	needlen += 2; /* CRLF */
   1109 
   1110 	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
   1111 		result = grow_headerspace(httpd);
   1112 		if (result != ISC_R_SUCCESS)
   1113 			return (result);
   1114 	}
   1115 
   1116 	return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n", name,
   1117 				   buf));
   1118 }
   1119 
   1120 static void
   1121 isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) {
   1122 	isc_httpd_t *httpd = ev->ev_arg;
   1123 	isc_region_t r;
   1124 	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
   1125 
   1126 	ENTER("senddone");
   1127 	INSIST(ISC_HTTPD_ISSEND(httpd));
   1128 
   1129 	/*
   1130 	 * First, unlink our header buffer from the socket's bufflist.  This
   1131 	 * is sort of an evil hack, since we know our buffer will be there,
   1132 	 * and we know it's address, so we can just remove it directly.
   1133 	 */
   1134 	NOTICE("senddone unlinked header");
   1135 	ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
   1136 
   1137 	/*
   1138 	 * We will always want to clean up our receive buffer, even if we
   1139 	 * got an error on send or we are shutting down.
   1140 	 *
   1141 	 * We will pass in the buffer only if there is data in it.  If
   1142 	 * there is no data, we will pass in a NULL.
   1143 	 */
   1144 	if (httpd->freecb != NULL) {
   1145 		isc_buffer_t *b = NULL;
   1146 		if (isc_buffer_length(&httpd->bodybuffer) > 0) {
   1147 			b = &httpd->bodybuffer;
   1148 			httpd->freecb(b, httpd->freecb_arg);
   1149 		}
   1150 		NOTICE("senddone free callback performed");
   1151 	}
   1152 	if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
   1153 		ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
   1154 		NOTICE("senddone body buffer unlinked");
   1155 	} else if (ISC_LINK_LINKED(&httpd->compbuffer, link)) {
   1156 		ISC_LIST_UNLINK(sev->bufferlist, &httpd->compbuffer, link);
   1157 		NOTICE("senddone compressed data unlinked and freed");
   1158 	}
   1159 
   1160 	if (sev->result != ISC_R_SUCCESS) {
   1161 		destroy_client(&httpd);
   1162 		goto out;
   1163 	}
   1164 
   1165 	if ((httpd->flags & HTTPD_CLOSE) != 0) {
   1166 		destroy_client(&httpd);
   1167 		goto out;
   1168 	}
   1169 
   1170 	ISC_HTTPD_SETRECV(httpd);
   1171 
   1172 	NOTICE("senddone restarting recv on socket");
   1173 
   1174 	reset_client(httpd);
   1175 
   1176 	r.base = (unsigned char *)httpd->recvbuf;
   1177 	r.length = HTTP_RECVLEN - 1;
   1178 	/* check return code? */
   1179 	(void)isc_socket_recv(httpd->sock, &r, 1, task,
   1180 			      isc_httpd_recvdone, httpd);
   1181 
   1182 out:
   1183 	isc_event_free(&ev);
   1184 	EXIT("senddone");
   1185 }
   1186 
   1187 static void
   1188 reset_client(isc_httpd_t *httpd) {
   1189 	/*
   1190 	 * Catch errors here.  We MUST be in RECV mode, and we MUST NOT have
   1191 	 * any outstanding buffers.  If we have buffers, we have a leak.
   1192 	 */
   1193 	INSIST(ISC_HTTPD_ISRECV(httpd));
   1194 	INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
   1195 	INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
   1196 
   1197 	httpd->recvbuf[0] = 0;
   1198 	httpd->recvlen = 0;
   1199 	httpd->headers = NULL;
   1200 	httpd->method = ISC_HTTPD_METHODUNKNOWN;
   1201 	httpd->url = NULL;
   1202 	httpd->querystring = NULL;
   1203 	httpd->protocol = NULL;
   1204 	httpd->flags = 0;
   1205 
   1206 	isc_buffer_clear(&httpd->headerbuffer);
   1207 	isc_buffer_clear(&httpd->compbuffer);
   1208 	isc_buffer_invalidate(&httpd->bodybuffer);
   1209 }
   1210 
   1211 isc_result_t
   1212 isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
   1213 		    isc_httpdaction_t *func, void *arg)
   1214 {
   1215 	return (isc_httpdmgr_addurl2(httpdmgr, url, ISC_FALSE, func, arg));
   1216 }
   1217 
   1218 isc_result_t
   1219 isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url,
   1220 		     isc_boolean_t isstatic,
   1221 		     isc_httpdaction_t *func, void *arg)
   1222 {
   1223 	isc_httpdurl_t *item;
   1224 
   1225 	if (url == NULL) {
   1226 		httpdmgr->render_404 = func;
   1227 		return (ISC_R_SUCCESS);
   1228 	}
   1229 
   1230 	item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
   1231 	if (item == NULL)
   1232 		return (ISC_R_NOMEMORY);
   1233 
   1234 	item->url = isc_mem_strdup(httpdmgr->mctx, url);
   1235 	if (item->url == NULL) {
   1236 		isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
   1237 		return (ISC_R_NOMEMORY);
   1238 	}
   1239 
   1240 	item->action = func;
   1241 	item->action_arg = arg;
   1242 	item->isstatic = isstatic;
   1243 	isc_time_now(&item->loadtime);
   1244 
   1245 	ISC_LINK_INIT(item, link);
   1246 	ISC_LIST_APPEND(httpdmgr->urls, item, link);
   1247 
   1248 	return (ISC_R_SUCCESS);
   1249 }
   1250 
   1251 void
   1252 isc_httpd_setfinishhook(void (*fn)(void))
   1253 {
   1254 	finishhook = fn;
   1255 }
   1256