1d54cfbdroberto/*
2047f369cy * Copyright (C) 2006-2008, 2010-2012  Internet Systems Consortium, Inc. ("ISC")
3d54cfbdroberto *
4d54cfbdroberto * Permission to use, copy, modify, and/or distribute this software for any
5d54cfbdroberto * purpose with or without fee is hereby granted, provided that the above
6d54cfbdroberto * copyright notice and this permission notice appear in all copies.
7d54cfbdroberto *
8d54cfbdroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9d54cfbdroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10d54cfbdroberto * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11d54cfbdroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12d54cfbdroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13d54cfbdroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14d54cfbdroberto * PERFORMANCE OF THIS SOFTWARE.
15d54cfbdroberto */
16d54cfbdroberto
17047f369cy/* $Id$ */
18d54cfbdroberto
19d54cfbdroberto/*! \file */
20d54cfbdroberto
21d54cfbdroberto#include <config.h>
22d54cfbdroberto
23d54cfbdroberto#include <isc/buffer.h>
24d54cfbdroberto#include <isc/httpd.h>
25d54cfbdroberto#include <isc/mem.h>
26d54cfbdroberto#include <isc/socket.h>
27d54cfbdroberto#include <isc/string.h>
28d54cfbdroberto#include <isc/task.h>
29d54cfbdroberto#include <isc/util.h>
30d54cfbdroberto
31d54cfbdroberto#include <string.h>
32d54cfbdroberto
33d54cfbdroberto/*%
34d54cfbdroberto * TODO:
35d54cfbdroberto *
36d54cfbdroberto *  o  Put in better checks to make certain things are passed in correctly.
37d54cfbdroberto *     This includes a magic number for externally-visible structures,
38d54cfbdroberto *     checking for NULL-ness before dereferencing, etc.
39d54cfbdroberto *  o  Make the URL processing external functions which will fill-in a buffer
40d54cfbdroberto *     structure we provide, or return an error and we will render a generic
41d54cfbdroberto *     page and close the client.
42d54cfbdroberto */
43d54cfbdroberto
44d54cfbdroberto#define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
45d54cfbdroberto#define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
46d54cfbdroberto
47d54cfbdroberto#ifdef DEBUG_HTTPD
48d54cfbdroberto#define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0)
49d54cfbdroberto#define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0)
50d54cfbdroberto#define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0)
51d54cfbdroberto#else
52d54cfbdroberto#define ENTER(x) do { } while(0)
53d54cfbdroberto#define EXIT(x) do { } while(0)
54d54cfbdroberto#define NOTICE(x) do { } while(0)
55d54cfbdroberto#endif
56d54cfbdroberto
57d54cfbdroberto#define HTTP_RECVLEN			1024
58d54cfbdroberto#define HTTP_SENDGROW			1024
59d54cfbdroberto#define HTTP_SEND_MAXLEN		10240
60d54cfbdroberto
61d54cfbdroberto/*%
62d54cfbdroberto * HTTP urls.  These are the URLs we manage, and the function to call to
63d54cfbdroberto * provide the data for it.  We pass in the base url (so the same function
64d54cfbdroberto * can handle multiple requests), and a structure to fill in to return a
65d54cfbdroberto * result to the client.  We also pass in a pointer to be filled in for
66d54cfbdroberto * the data cleanup function.
67d54cfbdroberto */
68d54cfbdrobertostruct isc_httpdurl {
69d54cfbdroberto	char			       *url;
70d54cfbdroberto	isc_httpdaction_t	       *action;
71d54cfbdroberto	void			       *action_arg;
72d54cfbdroberto	ISC_LINK(isc_httpdurl_t)	link;
73d54cfbdroberto};
74d54cfbdroberto
75d54cfbdroberto#define HTTPD_CLOSE		0x0001 /* Got a Connection: close header */
76d54cfbdroberto#define HTTPD_FOUNDHOST		0x0002 /* Got a Host: header */
77d54cfbdroberto
78d54cfbdroberto/*% http client */
79d54cfbdrobertostruct isc_httpd {
80d54cfbdroberto	isc_httpdmgr_t	       *mgr;		/*%< our parent */
81d54cfbdroberto	ISC_LINK(isc_httpd_t)	link;
82d54cfbdroberto	unsigned int		state;
83d54cfbdroberto	isc_socket_t		*sock;
84d54cfbdroberto
85d54cfbdroberto	/*%
86d54cfbdroberto	 * Received data state.
87d54cfbdroberto	 */
88d54cfbdroberto	char			recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
89d54cfbdroberto	isc_uint32_t		recvlen;	/*%< length recv'd */
90d54cfbdroberto	unsigned int		method;
91d54cfbdroberto	char		       *url;
92d54cfbdroberto	char		       *querystring;
93d54cfbdroberto	char		       *protocol;
94d54cfbdroberto
95d54cfbdroberto	/*
96d54cfbdroberto	 * Flags on the httpd client.
97d54cfbdroberto	 */
98d54cfbdroberto	int			flags;
99d54cfbdroberto
100d54cfbdroberto	/*%
101d54cfbdroberto	 * Transmit data state.
102d54cfbdroberto	 *
103d54cfbdroberto	 * This is the data buffer we will transmit.
104d54cfbdroberto	 *
105d54cfbdroberto	 * This free function pointer is filled in by the rendering function
106d54cfbdroberto	 * we call.  The free function is called after the data is transmitted
107d54cfbdroberto	 * to the client.
108d54cfbdroberto	 *
109d54cfbdroberto	 * The bufflist is the list of buffers we are currently transmitting.
110d54cfbdroberto	 * The headerdata is where we render our headers to.  If we run out of
111d54cfbdroberto	 * space when rendering a header, we will change the size of our
112d54cfbdroberto	 * buffer.  We will not free it until we are finished, and will
113d54cfbdroberto	 * allocate an additional HTTP_SENDGROW bytes per header space grow.
114d54cfbdroberto	 *
115d54cfbdroberto	 * We currently use two buffers total, one for the headers (which
116d54cfbdroberto	 * we manage) and another for the client to fill in (which it manages,
117d54cfbdroberto	 * it provides the space for it, etc) -- we will pass that buffer
118d54cfbdroberto	 * structure back to the caller, who is responsible for managing the
119d54cfbdroberto	 * space it may have allocated as backing store for it.  This second
120d54cfbdroberto	 * buffer is bodybuffer, and we only allocate the buffer itself, not
121d54cfbdroberto	 * the backing store.
122d54cfbdroberto	 */
123d54cfbdroberto	isc_bufferlist_t	bufflist;
124d54cfbdroberto	char		       *headerdata; /*%< send header buf */
125d54cfbdroberto	unsigned int		headerlen;  /*%< current header buffer size */
126d54cfbdroberto	isc_buffer_t		headerbuffer;
127d54cfbdroberto
128d54cfbdroberto	const char	       *mimetype;
129d54cfbdroberto	unsigned int		retcode;
130d54cfbdroberto	const char	       *retmsg;
131d54cfbdroberto	isc_buffer_t		bodybuffer;
132d54cfbdroberto	isc_httpdfree_t	       *freecb;
133d54cfbdroberto	void		       *freecb_arg;
134d54cfbdroberto};
135d54cfbdroberto
136d54cfbdroberto/*% lightweight socket manager for httpd output */
137d54cfbdrobertostruct isc_httpdmgr {
138d54cfbdroberto	isc_mem_t	       *mctx;
139d54cfbdroberto	isc_socket_t	       *sock;		/*%< listening socket */
140d54cfbdroberto	isc_task_t	       *task;		/*%< owning task */
141d54cfbdroberto	isc_timermgr_t	       *timermgr;
142d54cfbdroberto
143d54cfbdroberto	isc_httpdclientok_t    *client_ok;	/*%< client validator */
144d54cfbdroberto	isc_httpdondestroy_t   *ondestroy;	/*%< cleanup callback */
145d54cfbdroberto	void		       *cb_arg;		/*%< argument for the above */
146d54cfbdroberto
147d54cfbdroberto	unsigned int		flags;
148d54cfbdroberto	ISC_LIST(isc_httpd_t)	running;	/*%< running clients */
149d54cfbdroberto
150d54cfbdroberto	isc_mutex_t		lock;
151d54cfbdroberto
152d54cfbdroberto	ISC_LIST(isc_httpdurl_t) urls;		/*%< urls we manage */
153d54cfbdroberto	isc_httpdaction_t      *render_404;
154047f369cy	isc_httpdaction_t      *render_500;
155d54cfbdroberto};
156d54cfbdroberto
157d54cfbdroberto/*%
158d54cfbdroberto * HTTP methods.
159d54cfbdroberto */
160d54cfbdroberto#define ISC_HTTPD_METHODUNKNOWN	0
161d54cfbdroberto#define ISC_HTTPD_METHODGET	1
162d54cfbdroberto#define ISC_HTTPD_METHODPOST	2
163d54cfbdroberto
164d54cfbdroberto/*%
165d54cfbdroberto * Client states.
166d54cfbdroberto *
167d54cfbdroberto * _IDLE	The client is not doing anything at all.  This state should
168d54cfbdroberto *		only occur just after creation, and just before being
169d54cfbdroberto *		destroyed.
170d54cfbdroberto *
171d54cfbdroberto * _RECV	The client is waiting for data after issuing a socket recv().
172d54cfbdroberto *
173d54cfbdroberto * _RECVDONE	Data has been received, and is being processed.
174d54cfbdroberto *
175d54cfbdroberto * _SEND	All data for a response has completed, and a reply was
176d54cfbdroberto *		sent via a socket send() call.
177d54cfbdroberto *
178d54cfbdroberto * _SENDDONE	Send is completed.
179d54cfbdroberto *
180d54cfbdroberto * Badly formatted state table:
181d54cfbdroberto *
182d54cfbdroberto *	IDLE -> RECV when client has a recv() queued.
183d54cfbdroberto *
184d54cfbdroberto *	RECV -> RECVDONE when recvdone event received.
185d54cfbdroberto *
186d54cfbdroberto *	RECVDONE -> SEND if the data for a reply is at hand.
187d54cfbdroberto *
188d54cfbdroberto *	SEND -> RECV when a senddone event was received.
189d54cfbdroberto *
190d54cfbdroberto *	At any time -> RECV on error.  If RECV fails, the client will
191d54cfbdroberto *	self-destroy, closing the socket and freeing memory.
192d54cfbdroberto */
193d54cfbdroberto#define ISC_HTTPD_STATEIDLE	0
194d54cfbdroberto#define ISC_HTTPD_STATERECV	1
195d54cfbdroberto#define ISC_HTTPD_STATERECVDONE	2
196d54cfbdroberto#define ISC_HTTPD_STATESEND	3
197d54cfbdroberto#define ISC_HTTPD_STATESENDDONE	4
198d54cfbdroberto
199d54cfbdroberto#define ISC_HTTPD_ISRECV(c)	((c)->state == ISC_HTTPD_STATERECV)
200d54cfbdroberto#define ISC_HTTPD_ISRECVDONE(c)	((c)->state == ISC_HTTPD_STATERECVDONE)
201d54cfbdroberto#define ISC_HTTPD_ISSEND(c)	((c)->state == ISC_HTTPD_STATESEND)
202d54cfbdroberto#define ISC_HTTPD_ISSENDDONE(c)	((c)->state == ISC_HTTPD_STATESENDDONE)
203d54cfbdroberto
204d54cfbdroberto/*%
205d54cfbdroberto * Overall magic test that means we're not idle.
206d54cfbdroberto */
207d54cfbdroberto#define ISC_HTTPD_SETRECV(c)	((c)->state = ISC_HTTPD_STATERECV)
208d54cfbdroberto#define ISC_HTTPD_SETRECVDONE(c)	((c)->state = ISC_HTTPD_STATERECVDONE)
209d54cfbdroberto#define ISC_HTTPD_SETSEND(c)	((c)->state = ISC_HTTPD_STATESEND)
210d54cfbdroberto#define ISC_HTTPD_SETSENDDONE(c)	((c)->state = ISC_HTTPD_STATESENDDONE)
211d54cfbdroberto
212d54cfbdrobertostatic void isc_httpd_accept(isc_task_t *, isc_event_t *);
213d54cfbdrobertostatic void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
214d54cfbdrobertostatic void isc_httpd_senddone(isc_task_t *, isc_event_t *);
215d54cfbdrobertostatic void destroy_client(isc_httpd_t **);
216d54cfbdrobertostatic isc_result_t process_request(isc_httpd_t *, int);
217d54cfbdrobertostatic void httpdmgr_destroy(isc_httpdmgr_t *);
218d54cfbdrobertostatic isc_result_t grow_headerspace(isc_httpd_t *);
219d54cfbdrobertostatic void reset_client(isc_httpd_t *httpd);
220d54cfbdrobertostatic isc_result_t render_404(const char *, const char *,
221d54cfbdroberto			       void *,
222d54cfbdroberto			       unsigned int *, const char **,
223d54cfbdroberto			       const char **, isc_buffer_t *,
224d54cfbdroberto			       isc_httpdfree_t **, void **);
225047f369cystatic isc_result_t render_500(const char *, const char *,
226047f369cy			       void *,
227047f369cy			       unsigned int *, const char **,
228047f369cy			       const char **, isc_buffer_t *,
229047f369cy			       isc_httpdfree_t **, void **);
230d54cfbdroberto
231d54cfbdrobertostatic void
232d54cfbdrobertodestroy_client(isc_httpd_t **httpdp)
233d54cfbdroberto{
234d54cfbdroberto	isc_httpd_t *httpd = *httpdp;
235d54cfbdroberto	isc_httpdmgr_t *httpdmgr = httpd->mgr;
236d54cfbdroberto
237d54cfbdroberto	*httpdp = NULL;
238d54cfbdroberto
239d54cfbdroberto	LOCK(&httpdmgr->lock);
240d54cfbdroberto
241d54cfbdroberto	isc_socket_detach(&httpd->sock);
242d54cfbdroberto	ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
243d54cfbdroberto
244d54cfbdroberto	if (httpd->headerlen > 0)
245d54cfbdroberto		isc_mem_put(httpdmgr->mctx, httpd->headerdata,
246d54cfbdroberto			    httpd->headerlen);
247d54cfbdroberto
248d54cfbdroberto	isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
249d54cfbdroberto
250d54cfbdroberto	UNLOCK(&httpdmgr->lock);
251d54cfbdroberto
252d54cfbdroberto	httpdmgr_destroy(httpdmgr);
253d54cfbdroberto}
254d54cfbdroberto
255d54cfbdrobertoisc_result_t
256d54cfbdrobertoisc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
257d54cfbdroberto		    isc_httpdclientok_t *client_ok,
258d54cfbdroberto		    isc_httpdondestroy_t *ondestroy, void *cb_arg,
259d54cfbdroberto		    isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp)
260d54cfbdroberto{
261d54cfbdroberto	isc_result_t result;
262d54cfbdroberto	isc_httpdmgr_t *httpd;
263d54cfbdroberto
264d54cfbdroberto	REQUIRE(mctx != NULL);
265d54cfbdroberto	REQUIRE(sock != NULL);
266d54cfbdroberto	REQUIRE(task != NULL);
267d54cfbdroberto	REQUIRE(tmgr != NULL);
268d54cfbdroberto	REQUIRE(httpdp != NULL && *httpdp == NULL);
269d54cfbdroberto
270d54cfbdroberto	httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
271d54cfbdroberto	if (httpd == NULL)
272d54cfbdroberto		return (ISC_R_NOMEMORY);
273d54cfbdroberto
274d54cfbdroberto	result = isc_mutex_init(&httpd->lock);
275d54cfbdroberto	if (result != ISC_R_SUCCESS) {
276d54cfbdroberto		isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
277d54cfbdroberto		return (result);
278d54cfbdroberto	}
279d54cfbdroberto	httpd->mctx = NULL;
280d54cfbdroberto	isc_mem_attach(mctx, &httpd->mctx);
281d54cfbdroberto	httpd->sock = NULL;
282d54cfbdroberto	isc_socket_attach(sock, &httpd->sock);
283d54cfbdroberto	httpd->task = NULL;
284d54cfbdroberto	isc_task_attach(task, &httpd->task);
285d54cfbdroberto	httpd->timermgr = tmgr; /* XXXMLG no attach function? */
286d54cfbdroberto	httpd->client_ok = client_ok;
287d54cfbdroberto	httpd->ondestroy = ondestroy;
288d54cfbdroberto	httpd->cb_arg = cb_arg;
289d54cfbdroberto
290d54cfbdroberto	ISC_LIST_INIT(httpd->running);
291d54cfbdroberto	ISC_LIST_INIT(httpd->urls);
292d54cfbdroberto
293d54cfbdroberto	/* XXXMLG ignore errors on isc_socket_listen() */
294d54cfbdroberto	result = isc_socket_listen(sock, SOMAXCONN);
295d54cfbdroberto	if (result != ISC_R_SUCCESS) {
296d54cfbdroberto		UNEXPECTED_ERROR(__FILE__, __LINE__,
297d54cfbdroberto				 "isc_socket_listen() failed: %s",
298d54cfbdroberto				 isc_result_totext(result));
299d54cfbdroberto		goto cleanup;
300d54cfbdroberto	}
301d54cfbdroberto
302d54cfbdroberto	(void)isc_socket_filter(sock, "httpready");
303d54cfbdroberto
304d54cfbdroberto	result = isc_socket_accept(sock, task, isc_httpd_accept, httpd);
305d54cfbdroberto	if (result != ISC_R_SUCCESS)
306d54cfbdroberto		goto cleanup;
307d54cfbdroberto
308d54cfbdroberto	httpd->render_404 = render_404;
309047f369cy	httpd->render_500 = render_500;
310d54cfbdroberto
311d54cfbdroberto	*httpdp = httpd;
312d54cfbdroberto	return (ISC_R_SUCCESS);
313d54cfbdroberto
314d54cfbdroberto  cleanup:
315d54cfbdroberto	isc_task_detach(&httpd->task);
316d54cfbdroberto	isc_socket_detach(&httpd->sock);
317d54cfbdroberto	isc_mem_detach(&httpd->mctx);
318047f369cy	(void)isc_mutex_destroy(&httpd->lock);
319d54cfbdroberto	isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
320d54cfbdroberto	return (result);
321d54cfbdroberto}
322d54cfbdroberto
323d54cfbdrobertostatic void
324d54cfbdrobertohttpdmgr_destroy(isc_httpdmgr_t *httpdmgr)
325d54cfbdroberto{
326d54cfbdroberto	isc_mem_t *mctx;
327d54cfbdroberto	isc_httpdurl_t *url;
328d54cfbdroberto
329d54cfbdroberto	ENTER("httpdmgr_destroy");
330d54cfbdroberto
331d54cfbdroberto	LOCK(&httpdmgr->lock);
332d54cfbdroberto
333d54cfbdroberto	if (!MSHUTTINGDOWN(httpdmgr)) {
334d54cfbdroberto		NOTICE("httpdmgr_destroy not shutting down yet");
335d54cfbdroberto		UNLOCK(&httpdmgr->lock);
336d54cfbdroberto		return;
337d54cfbdroberto	}
338d54cfbdroberto
339d54cfbdroberto	/*
340d54cfbdroberto	 * If all clients are not shut down, don't do anything yet.
341d54cfbdroberto	 */
342d54cfbdroberto	if (!ISC_LIST_EMPTY(httpdmgr->running)) {
343d54cfbdroberto		NOTICE("httpdmgr_destroy clients still active");
344d54cfbdroberto		UNLOCK(&httpdmgr->lock);
345d54cfbdroberto		return;
346d54cfbdroberto	}
347d54cfbdroberto
348d54cfbdroberto	NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
349d54cfbdroberto
350d54cfbdroberto	isc_socket_detach(&httpdmgr->sock);
351d54cfbdroberto	isc_task_detach(&httpdmgr->task);
352d54cfbdroberto	httpdmgr->timermgr = NULL;
353d54cfbdroberto
354d54cfbdroberto	/*
355d54cfbdroberto	 * Clear out the list of all actions we know about.  Just free the
356d54cfbdroberto	 * memory.
357d54cfbdroberto	 */
358d54cfbdroberto	url = ISC_LIST_HEAD(httpdmgr->urls);
359d54cfbdroberto	while (url != NULL) {
360d54cfbdroberto		isc_mem_free(httpdmgr->mctx, url->url);
361d54cfbdroberto		ISC_LIST_UNLINK(httpdmgr->urls, url, link);
362d54cfbdroberto		isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
363d54cfbdroberto		url = ISC_LIST_HEAD(httpdmgr->urls);
364d54cfbdroberto	}
365d54cfbdroberto
366d54cfbdroberto	UNLOCK(&httpdmgr->lock);
367047f369cy	(void)isc_mutex_destroy(&httpdmgr->lock);
368d54cfbdroberto
369d54cfbdroberto	if (httpdmgr->ondestroy != NULL)
370d54cfbdroberto		(httpdmgr->ondestroy)(httpdmgr->cb_arg);
371d54cfbdroberto
372d54cfbdroberto	mctx = httpdmgr->mctx;
373d54cfbdroberto	isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
374d54cfbdroberto
375d54cfbdroberto	EXIT("httpdmgr_destroy");
376d54cfbdroberto}
377d54cfbdroberto
378d54cfbdroberto#define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
379d54cfbdroberto#define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
380d54cfbdroberto
381d54cfbdrobertostatic isc_result_t
382d54cfbdrobertoprocess_request(isc_httpd_t *httpd, int length)
383d54cfbdroberto{
384d54cfbdroberto	char *s;
385d54cfbdroberto	char *p;
386d54cfbdroberto	int delim;
387d54cfbdroberto
388d54cfbdroberto	ENTER("request");
389d54cfbdroberto
390d54cfbdroberto	httpd->recvlen += length;
391d54cfbdroberto
392d54cfbdroberto	httpd->recvbuf[httpd->recvlen] = 0;
393d54cfbdroberto
394d54cfbdroberto	/*
395d54cfbdroberto	 * If we don't find a blank line in our buffer, return that we need
396d54cfbdroberto	 * more data.
397d54cfbdroberto	 */
398d54cfbdroberto	s = strstr(httpd->recvbuf, "\r\n\r\n");
399d54cfbdroberto	delim = 1;
400d54cfbdroberto	if (s == NULL) {
401d54cfbdroberto		s = strstr(httpd->recvbuf, "\n\n");
402d54cfbdroberto		delim = 2;
403d54cfbdroberto	}
404d54cfbdroberto	if (s == NULL)
405d54cfbdroberto		return (ISC_R_NOTFOUND);
406d54cfbdroberto
407d54cfbdroberto	/*
408d54cfbdroberto	 * Determine if this is a POST or GET method.  Any other values will
409d54cfbdroberto	 * cause an error to be returned.
410d54cfbdroberto	 */
411d54cfbdroberto	if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
412d54cfbdroberto		httpd->method = ISC_HTTPD_METHODGET;
413d54cfbdroberto		p = httpd->recvbuf + 4;
414d54cfbdroberto	} else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
415d54cfbdroberto		httpd->method = ISC_HTTPD_METHODPOST;
416d54cfbdroberto		p = httpd->recvbuf + 5;
417d54cfbdroberto	} else {
418d54cfbdroberto		return (ISC_R_RANGE);
419d54cfbdroberto	}
420d54cfbdroberto
421d54cfbdroberto	/*
422d54cfbdroberto	 * From now on, p is the start of our buffer.
423d54cfbdroberto	 */
424d54cfbdroberto
425d54cfbdroberto	/*
426d54cfbdroberto	 * Extract the URL.
427d54cfbdroberto	 */
428d54cfbdroberto	s = p;
429d54cfbdroberto	while (LENGTHOK(s) && BUFLENOK(s) &&
430d54cfbdroberto	       (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
431d54cfbdroberto		s++;
432d54cfbdroberto	if (!LENGTHOK(s))
433d54cfbdroberto		return (ISC_R_NOTFOUND);
434d54cfbdroberto	if (!BUFLENOK(s))
435d54cfbdroberto		return (ISC_R_NOMEMORY);
436d54cfbdroberto	*s = 0;
437d54cfbdroberto
438d54cfbdroberto	/*
439d54cfbdroberto	 * Make the URL relative.
440d54cfbdroberto	 */
441d54cfbdroberto	if ((strncmp(p, "http:/", 6) == 0)
442d54cfbdroberto	    || (strncmp(p, "https:/", 7) == 0)) {
443d54cfbdroberto		/* Skip first / */
444d54cfbdroberto		while (*p != '/' && *p != 0)
445d54cfbdroberto			p++;
446d54cfbdroberto		if (*p == 0)
447d54cfbdroberto			return (ISC_R_RANGE);
448d54cfbdroberto		p++;
449d54cfbdroberto		/* Skip second / */
450d54cfbdroberto		while (*p != '/' && *p != 0)
451d54cfbdroberto			p++;
452d54cfbdroberto		if (*p == 0)
453d54cfbdroberto			return (ISC_R_RANGE);
454d54cfbdroberto		p++;
455d54cfbdroberto		/* Find third / */
456d54cfbdroberto		while (*p != '/' && *p != 0)
457d54cfbdroberto			p++;
458d54cfbdroberto		if (*p == 0) {
459d54cfbdroberto			p--;
460d54cfbdroberto			*p = '/';
461d54cfbdroberto		}
462d54cfbdroberto	}
463d54cfbdroberto
464d54cfbdroberto	httpd->url = p;
465d54cfbdroberto	p = s + delim;
466d54cfbdroberto	s = p;
467d54cfbdroberto
468d54cfbdroberto	/*
469d54cfbdroberto	 * Now, see if there is a ? mark in the URL.  If so, this is
470d54cfbdroberto	 * part of the query string, and we will split it from the URL.
471d54cfbdroberto	 */
472d54cfbdroberto	httpd->querystring = strchr(httpd->url, '?');
473d54cfbdroberto	if (httpd->querystring != NULL) {
474d54cfbdroberto		*(httpd->querystring) = 0;
475d54cfbdroberto		httpd->querystring++;
476d54cfbdroberto	}
477d54cfbdroberto
478d54cfbdroberto	/*
479d54cfbdroberto	 * Extract the HTTP/1.X protocol.  We will bounce on anything but
480d54cfbdroberto	 * HTTP/1.1 for now.
481d54cfbdroberto	 */
482d54cfbdroberto	while (LENGTHOK(s) && BUFLENOK(s) &&
483d54cfbdroberto	       (*s != '\n' && *s != '\r' && *s != '\0'))
484d54cfbdroberto		s++;
485d54cfbdroberto	if (!LENGTHOK(s))
486d54cfbdroberto		return (ISC_R_NOTFOUND);
487d54cfbdroberto	if (!BUFLENOK(s))
488d54cfbdroberto		return (ISC_R_NOMEMORY);
489d54cfbdroberto	*s = 0;
490d54cfbdroberto	if ((strncmp(p, "HTTP/1.0", 8) != 0)
491d54cfbdroberto	    && (strncmp(p, "HTTP/1.1", 8) != 0))
492d54cfbdroberto		return (ISC_R_RANGE);
493d54cfbdroberto	httpd->protocol = p;
494d54cfbdroberto	p = s + 1;
495d54cfbdroberto	s = p;
496d54cfbdroberto
497d54cfbdroberto	if (strstr(s, "Connection: close") != NULL)
498d54cfbdroberto		httpd->flags |= HTTPD_CLOSE;
499d54cfbdroberto
500d54cfbdroberto	if (strstr(s, "Host: ") != NULL)
501d54cfbdroberto		httpd->flags |= HTTPD_FOUNDHOST;
502d54cfbdroberto
503d54cfbdroberto	/*
504d54cfbdroberto	 * Standards compliance hooks here.
505d54cfbdroberto	 */
506d54cfbdroberto	if (strcmp(httpd->protocol, "HTTP/1.1") == 0
507d54cfbdroberto	    && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
508d54cfbdroberto		return (ISC_R_RANGE);
509d54cfbdroberto
510d54cfbdroberto	EXIT("request");
511d54cfbdroberto
512d54cfbdroberto	return (ISC_R_SUCCESS);
513d54cfbdroberto}
514d54cfbdroberto
515d54cfbdrobertostatic void
516d54cfbdrobertoisc_httpd_accept(isc_task_t *task, isc_event_t *ev)
517d54cfbdroberto{
518d54cfbdroberto	isc_result_t result;
519d54cfbdroberto	isc_httpdmgr_t *httpdmgr = ev->ev_arg;
520d54cfbdroberto	isc_httpd_t *httpd;
521d54cfbdroberto	isc_region_t r;
522d54cfbdroberto	isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
523d54cfbdroberto	isc_sockaddr_t peeraddr;
524d54cfbdroberto
525d54cfbdroberto	ENTER("accept");
526d54cfbdroberto
527d54cfbdroberto	LOCK(&httpdmgr->lock);
528d54cfbdroberto	if (MSHUTTINGDOWN(httpdmgr)) {
529d54cfbdroberto		NOTICE("accept shutting down, goto out");
530d54cfbdroberto		goto out;
531d54cfbdroberto	}
532d54cfbdroberto
533d54cfbdroberto	if (nev->result == ISC_R_CANCELED) {
534d54cfbdroberto		NOTICE("accept canceled, goto out");
535d54cfbdroberto		goto out;
536d54cfbdroberto	}
537d54cfbdroberto
538d54cfbdroberto	if (nev->result != ISC_R_SUCCESS) {
539d54cfbdroberto		/* XXXMLG log failure */
540d54cfbdroberto		NOTICE("accept returned failure, goto requeue");
541d54cfbdroberto		goto requeue;
542d54cfbdroberto	}
543d54cfbdroberto
544d54cfbdroberto	(void)isc_socket_getpeername(nev->newsocket, &peeraddr);
545d54cfbdroberto	if (httpdmgr->client_ok != NULL &&
546d54cfbdroberto	    !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
547d54cfbdroberto		isc_socket_detach(&nev->newsocket);
548d54cfbdroberto		goto requeue;
549d54cfbdroberto	}
550d54cfbdroberto
551d54cfbdroberto	httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
552d54cfbdroberto	if (httpd == NULL) {
553d54cfbdroberto		/* XXXMLG log failure */
554d54cfbdroberto		NOTICE("accept failed to allocate memory, goto requeue");
555d54cfbdroberto		isc_socket_detach(&nev->newsocket);
556d54cfbdroberto		goto requeue;
557d54cfbdroberto	}
558d54cfbdroberto
559d54cfbdroberto	httpd->mgr = httpdmgr;
560d54cfbdroberto	ISC_LINK_INIT(httpd, link);
561d54cfbdroberto	ISC_LIST_APPEND(httpdmgr->running, httpd, link);
562d54cfbdroberto	ISC_HTTPD_SETRECV(httpd);
563d54cfbdroberto	httpd->sock = nev->newsocket;
564d54cfbdroberto	isc_socket_setname(httpd->sock, "httpd", NULL);
565d54cfbdroberto	httpd->flags = 0;
566d54cfbdroberto
567d54cfbdroberto	/*
568d54cfbdroberto	 * Initialize the buffer for our headers.
569d54cfbdroberto	 */
570d54cfbdroberto	httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
571d54cfbdroberto	if (httpd->headerdata == NULL) {
572d54cfbdroberto		isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
573d54cfbdroberto		isc_socket_detach(&nev->newsocket);
574d54cfbdroberto		goto requeue;
575d54cfbdroberto	}
576d54cfbdroberto	httpd->headerlen = HTTP_SENDGROW;
577d54cfbdroberto	isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
578d54cfbdroberto			httpd->headerlen);
579d54cfbdroberto
580d54cfbdroberto	ISC_LIST_INIT(httpd->bufflist);
581d54cfbdroberto
582d54cfbdroberto	isc_buffer_initnull(&httpd->bodybuffer);
583d54cfbdroberto	reset_client(httpd);
584d54cfbdroberto
585d54cfbdroberto	r.base = (unsigned char *)httpd->recvbuf;
586d54cfbdroberto	r.length = HTTP_RECVLEN - 1;
587d54cfbdroberto	result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
588d54cfbdroberto				 httpd);
589047f369cy	/* FIXME!!! */
590047f369cy	POST(result);
591d54cfbdroberto	NOTICE("accept queued recv on socket");
592d54cfbdroberto
593d54cfbdroberto requeue:
594d54cfbdroberto	result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
595d54cfbdroberto				   httpdmgr);
596d54cfbdroberto	if (result != ISC_R_SUCCESS) {
597d54cfbdroberto		/* XXXMLG what to do?  Log failure... */
598d54cfbdroberto		NOTICE("accept could not reaccept due to failure");
599d54cfbdroberto	}
600d54cfbdroberto
601d54cfbdroberto out:
602d54cfbdroberto	UNLOCK(&httpdmgr->lock);
603d54cfbdroberto
604d54cfbdroberto	httpdmgr_destroy(httpdmgr);
605d54cfbdroberto
606d54cfbdroberto	isc_event_free(&ev);
607d54cfbdroberto
608d54cfbdroberto	EXIT("accept");
609d54cfbdroberto}
610d54cfbdroberto
611d54cfbdrobertostatic isc_result_t
612d54cfbdrobertorender_404(const char *url, const char *querystring,
613d54cfbdroberto	   void *arg,
614d54cfbdroberto	   unsigned int *retcode, const char **retmsg,
615d54cfbdroberto	   const char **mimetype, isc_buffer_t *b,
616d54cfbdroberto	   isc_httpdfree_t **freecb, void **freecb_args)
617d54cfbdroberto{
618d54cfbdroberto	static char msg[] = "No such URL.";
619d54cfbdroberto
620d54cfbdroberto	UNUSED(url);
621d54cfbdroberto	UNUSED(querystring);
622d54cfbdroberto	UNUSED(arg);
623d54cfbdroberto
624d54cfbdroberto	*retcode = 404;
625d54cfbdroberto	*retmsg = "No such URL";
626d54cfbdroberto	*mimetype = "text/plain";
627d54cfbdroberto	isc_buffer_reinit(b, msg, strlen(msg));
628d54cfbdroberto	isc_buffer_add(b, strlen(msg));
629d54cfbdroberto	*freecb = NULL;
630d54cfbdroberto	*freecb_args = NULL;
631d54cfbdroberto
632d54cfbdroberto	return (ISC_R_SUCCESS);
633d54cfbdroberto}
634d54cfbdroberto
635047f369cystatic isc_result_t
636047f369cyrender_500(const char *url, const char *querystring,
637047f369cy	   void *arg,
638047f369cy	   unsigned int *retcode, const char **retmsg,
639047f369cy	   const char **mimetype, isc_buffer_t *b,
640047f369cy	   isc_httpdfree_t **freecb, void **freecb_args)
641047f369cy{
642047f369cy	static char msg[] = "Internal server failure.";
643047f369cy
644047f369cy	UNUSED(url);
645047f369cy	UNUSED(querystring);
646047f369cy	UNUSED(arg);
647047f369cy
648047f369cy	*retcode = 500;
649047f369cy	*retmsg = "Internal server failure";
650047f369cy	*mimetype = "text/plain";
651047f369cy	isc_buffer_reinit(b, msg, strlen(msg));
652047f369cy	isc_buffer_add(b, strlen(msg));
653047f369cy	*freecb = NULL;
654047f369cy	*freecb_args = NULL;
655047f369cy
656047f369cy	return (ISC_R_SUCCESS);
657047f369cy}
658047f369cy
659d54cfbdrobertostatic void
660d54cfbdrobertoisc_httpd_recvdone(isc_task_t *task, isc_event_t *ev)
661d54cfbdroberto{
662d54cfbdroberto	isc_region_t r;
663d54cfbdroberto	isc_result_t result;
664d54cfbdroberto	isc_httpd_t *httpd = ev->ev_arg;
665d54cfbdroberto	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
666d54cfbdroberto	isc_httpdurl_t *url;
667d54cfbdroberto	isc_time_t now;
668d54cfbdroberto	char datebuf[32];  /* Only need 30, but safety first */
669d54cfbdroberto
670d54cfbdroberto	ENTER("recv");
671d54cfbdroberto
672d54cfbdroberto	INSIST(ISC_HTTPD_ISRECV(httpd));
673d54cfbdroberto
674d54cfbdroberto	if (sev->result != ISC_R_SUCCESS) {
675d54cfbdroberto		NOTICE("recv destroying client");
676d54cfbdroberto		destroy_client(&httpd);
677d54cfbdroberto		goto out;
678d54cfbdroberto	}
679d54cfbdroberto
680d54cfbdroberto	result = process_request(httpd, sev->n);
681d54cfbdroberto	if (result == ISC_R_NOTFOUND) {
682d54cfbdroberto		if (httpd->recvlen >= HTTP_RECVLEN - 1) {
683d54cfbdroberto			destroy_client(&httpd);
684d54cfbdroberto			goto out;
685d54cfbdroberto		}
686d54cfbdroberto		r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
687d54cfbdroberto		r.length = HTTP_RECVLEN - httpd->recvlen - 1;
688047f369cy		/* check return code? */
689047f369cy		(void)isc_socket_recv(httpd->sock, &r, 1, task,
690047f369cy				      isc_httpd_recvdone, httpd);
691d54cfbdroberto		goto out;
692d54cfbdroberto	} else if (result != ISC_R_SUCCESS) {
693d54cfbdroberto		destroy_client(&httpd);
694d54cfbdroberto		goto out;
695d54cfbdroberto	}
696d54cfbdroberto
697d54cfbdroberto	ISC_HTTPD_SETSEND(httpd);
698d54cfbdroberto
699d54cfbdroberto	/*
700d54cfbdroberto	 * XXXMLG Call function here.  Provide an add-header function
701d54cfbdroberto	 * which will append the common headers to a response we generate.
702d54cfbdroberto	 */
703d54cfbdroberto	isc_buffer_initnull(&httpd->bodybuffer);
704d54cfbdroberto	isc_time_now(&now);
705d54cfbdroberto	isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
706d54cfbdroberto	url = ISC_LIST_HEAD(httpd->mgr->urls);
707d54cfbdroberto	while (url != NULL) {
708d54cfbdroberto		if (strcmp(httpd->url, url->url) == 0)
709d54cfbdroberto			break;
710d54cfbdroberto		url = ISC_LIST_NEXT(url, link);
711d54cfbdroberto	}
712d54cfbdroberto	if (url == NULL)
713d54cfbdroberto		result = httpd->mgr->render_404(httpd->url, httpd->querystring,
714d54cfbdroberto						NULL,
715d54cfbdroberto						&httpd->retcode,
716d54cfbdroberto						&httpd->retmsg,
717d54cfbdroberto						&httpd->mimetype,
718d54cfbdroberto						&httpd->bodybuffer,
719d54cfbdroberto						&httpd->freecb,
720d54cfbdroberto						&httpd->freecb_arg);
721d54cfbdroberto	else
722d54cfbdroberto		result = url->action(httpd->url, httpd->querystring,
723d54cfbdroberto				     url->action_arg,
724d54cfbdroberto				     &httpd->retcode, &httpd->retmsg,
725d54cfbdroberto				     &httpd->mimetype, &httpd->bodybuffer,
726d54cfbdroberto				     &httpd->freecb, &httpd->freecb_arg);
727d54cfbdroberto	if (result != ISC_R_SUCCESS) {
728047f369cy		result = httpd->mgr->render_500(httpd->url, httpd->querystring,
729047f369cy						NULL, &httpd->retcode,
730047f369cy						&httpd->retmsg,
731047f369cy						&httpd->mimetype,
732047f369cy						&httpd->bodybuffer,
733047f369cy						&httpd->freecb,
734047f369cy						&httpd->freecb_arg);
735047f369cy		RUNTIME_CHECK(result == ISC_R_SUCCESS);
736d54cfbdroberto	}
737d54cfbdroberto
738d54cfbdroberto	isc_httpd_response(httpd);
739d54cfbdroberto	isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
740d54cfbdroberto	isc_httpd_addheader(httpd, "Date", datebuf);
741d54cfbdroberto	isc_httpd_addheader(httpd, "Expires", datebuf);
742d54cfbdroberto	isc_httpd_addheader(httpd, "Last-Modified", datebuf);
743d54cfbdroberto	isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
744d54cfbdroberto	isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
745d54cfbdroberto	isc_httpd_addheader(httpd, "Server: libisc", NULL);
746d54cfbdroberto	isc_httpd_addheaderuint(httpd, "Content-Length",
747d54cfbdroberto				isc_buffer_usedlength(&httpd->bodybuffer));
748d54cfbdroberto	isc_httpd_endheaders(httpd);  /* done */
749d54cfbdroberto
750d54cfbdroberto	ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
751d54cfbdroberto	/*
752d54cfbdroberto	 * Link the data buffer into our send queue, should we have any data
753d54cfbdroberto	 * rendered into it.  If no data is present, we won't do anything
754d54cfbdroberto	 * with the buffer.
755d54cfbdroberto	 */
756d54cfbdroberto	if (isc_buffer_length(&httpd->bodybuffer) > 0)
757d54cfbdroberto		ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
758d54cfbdroberto
759047f369cy	/* check return code? */
760047f369cy	(void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
761047f369cy			       isc_httpd_senddone, httpd);
762d54cfbdroberto
763d54cfbdroberto out:
764d54cfbdroberto	isc_event_free(&ev);
765d54cfbdroberto	EXIT("recv");
766d54cfbdroberto}
767d54cfbdroberto
768d54cfbdrobertovoid
769d54cfbdrobertoisc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp)
770d54cfbdroberto{
771d54cfbdroberto	isc_httpdmgr_t *httpdmgr;
772d54cfbdroberto	isc_httpd_t *httpd;
773d54cfbdroberto	httpdmgr = *httpdmgrp;
774d54cfbdroberto	*httpdmgrp = NULL;
775d54cfbdroberto
776d54cfbdroberto	ENTER("isc_httpdmgr_shutdown");
777d54cfbdroberto
778d54cfbdroberto	LOCK(&httpdmgr->lock);
779d54cfbdroberto
780d54cfbdroberto	MSETSHUTTINGDOWN(httpdmgr);
781d54cfbdroberto
782d54cfbdroberto	isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
783d54cfbdroberto
784d54cfbdroberto	httpd = ISC_LIST_HEAD(httpdmgr->running);
785d54cfbdroberto	while (httpd != NULL) {
786d54cfbdroberto		isc_socket_cancel(httpd->sock, httpdmgr->task,
787d54cfbdroberto				  ISC_SOCKCANCEL_ALL);
788d54cfbdroberto		httpd = ISC_LIST_NEXT(httpd, link);
789d54cfbdroberto	}
790d54cfbdroberto
791d54cfbdroberto	UNLOCK(&httpdmgr->lock);
792d54cfbdroberto
793d54cfbdroberto	EXIT("isc_httpdmgr_shutdown");
794d54cfbdroberto}
795d54cfbdroberto
796d54cfbdrobertostatic isc_result_t
797d54cfbdrobertogrow_headerspace(isc_httpd_t *httpd)
798d54cfbdroberto{
799d54cfbdroberto	char *newspace;
800d54cfbdroberto	unsigned int newlen;
801d54cfbdroberto	isc_region_t r;
802d54cfbdroberto
803d54cfbdroberto	newlen = httpd->headerlen + HTTP_SENDGROW;
804d54cfbdroberto	if (newlen > HTTP_SEND_MAXLEN)
805d54cfbdroberto		return (ISC_R_NOSPACE);
806d54cfbdroberto
807d54cfbdroberto	newspace = isc_mem_get(httpd->mgr->mctx, newlen);
808d54cfbdroberto	if (newspace == NULL)
809d54cfbdroberto		return (ISC_R_NOMEMORY);
810d54cfbdroberto	isc_buffer_region(&httpd->headerbuffer, &r);
811d54cfbdroberto	isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
812d54cfbdroberto
813d54cfbdroberto	isc_mem_put(httpd->mgr->mctx, r.base, r.length);
814d54cfbdroberto
815d54cfbdroberto	return (ISC_R_SUCCESS);
816d54cfbdroberto}
817d54cfbdroberto
818d54cfbdrobertoisc_result_t
819d54cfbdrobertoisc_httpd_response(isc_httpd_t *httpd)
820d54cfbdroberto{
821d54cfbdroberto	isc_result_t result;
822d54cfbdroberto	unsigned int needlen;
823d54cfbdroberto
824d54cfbdroberto	needlen = strlen(httpd->protocol) + 1; /* protocol + space */
825d54cfbdroberto	needlen += 3 + 1;  /* room for response code, always 3 bytes */
826d54cfbdroberto	needlen += strlen(httpd->retmsg) + 2;  /* return msg + CRLF */
827d54cfbdroberto
828047f369cy	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
829d54cfbdroberto		result = grow_headerspace(httpd);
830d54cfbdroberto		if (result != ISC_R_SUCCESS)
831d54cfbdroberto			return (result);
832d54cfbdroberto	}
833d54cfbdroberto
834d54cfbdroberto	sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n",
835d54cfbdroberto		httpd->protocol, httpd->retcode, httpd->retmsg);
836d54cfbdroberto	isc_buffer_add(&httpd->headerbuffer, needlen);
837d54cfbdroberto
838d54cfbdroberto	return (ISC_R_SUCCESS);
839d54cfbdroberto}
840d54cfbdroberto
841d54cfbdrobertoisc_result_t
842d54cfbdrobertoisc_httpd_addheader(isc_httpd_t *httpd, const char *name,
843d54cfbdroberto		    const char *val)
844d54cfbdroberto{
845d54cfbdroberto	isc_result_t result;
846d54cfbdroberto	unsigned int needlen;
847d54cfbdroberto
848d54cfbdroberto	needlen = strlen(name); /* name itself */
849d54cfbdroberto	if (val != NULL)
850d54cfbdroberto		needlen += 2 + strlen(val); /* :<space> and val */
851d54cfbdroberto	needlen += 2; /* CRLF */
852d54cfbdroberto
853047f369cy	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
854d54cfbdroberto		result = grow_headerspace(httpd);
855d54cfbdroberto		if (result != ISC_R_SUCCESS)
856d54cfbdroberto			return (result);
857d54cfbdroberto	}
858d54cfbdroberto
859d54cfbdroberto	if (val != NULL)
860d54cfbdroberto		sprintf(isc_buffer_used(&httpd->headerbuffer),
861d54cfbdroberto			"%s: %s\r\n", name, val);
862d54cfbdroberto	else
863d54cfbdroberto		sprintf(isc_buffer_used(&httpd->headerbuffer),
864d54cfbdroberto			"%s\r\n", name);
865d54cfbdroberto
866d54cfbdroberto	isc_buffer_add(&httpd->headerbuffer, needlen);
867d54cfbdroberto
868d54cfbdroberto	return (ISC_R_SUCCESS);
869d54cfbdroberto}
870d54cfbdroberto
871d54cfbdrobertoisc_result_t
872d54cfbdrobertoisc_httpd_endheaders(isc_httpd_t *httpd)
873d54cfbdroberto{
874d54cfbdroberto	isc_result_t result;
875d54cfbdroberto
876047f369cy	while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
877d54cfbdroberto		result = grow_headerspace(httpd);
878d54cfbdroberto		if (result != ISC_R_SUCCESS)
879d54cfbdroberto			return (result);
880d54cfbdroberto	}
881d54cfbdroberto
882d54cfbdroberto	sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
883d54cfbdroberto	isc_buffer_add(&httpd->headerbuffer, 2);
884d54cfbdroberto
885d54cfbdroberto	return (ISC_R_SUCCESS);
886d54cfbdroberto}
887d54cfbdroberto
888d54cfbdrobertoisc_result_t
889d54cfbdrobertoisc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
890d54cfbdroberto	isc_result_t result;
891d54cfbdroberto	unsigned int needlen;
892d54cfbdroberto	char buf[sizeof "18446744073709551616"];
893d54cfbdroberto
894d54cfbdroberto	sprintf(buf, "%d", val);
895d54cfbdroberto
896d54cfbdroberto	needlen = strlen(name); /* name itself */
897d54cfbdroberto	needlen += 2 + strlen(buf); /* :<space> and val */
898d54cfbdroberto	needlen += 2; /* CRLF */
899d54cfbdroberto
900047f369cy	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
901d54cfbdroberto		result = grow_headerspace(httpd);
902d54cfbdroberto		if (result != ISC_R_SUCCESS)
903d54cfbdroberto			return (result);
904d54cfbdroberto	}
905d54cfbdroberto
906d54cfbdroberto	sprintf(isc_buffer_used(&httpd->headerbuffer),
907d54cfbdroberto		"%s: %s\r\n", name, buf);
908d54cfbdroberto
909d54cfbdroberto	isc_buffer_add(&httpd->headerbuffer, needlen);
910d54cfbdroberto
911d54cfbdroberto	return (ISC_R_SUCCESS);
912d54cfbdroberto}
913d54cfbdroberto
914d54cfbdrobertostatic void
915d54cfbdrobertoisc_httpd_senddone(isc_task_t *task, isc_event_t *ev)
916d54cfbdroberto{
917d54cfbdroberto	isc_httpd_t *httpd = ev->ev_arg;
918d54cfbdroberto	isc_region_t r;
919d54cfbdroberto	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
920d54cfbdroberto
921d54cfbdroberto	ENTER("senddone");
922d54cfbdroberto	INSIST(ISC_HTTPD_ISSEND(httpd));
923d54cfbdroberto
924d54cfbdroberto	/*
925d54cfbdroberto	 * First, unlink our header buffer from the socket's bufflist.  This
926d54cfbdroberto	 * is sort of an evil hack, since we know our buffer will be there,
927d54cfbdroberto	 * and we know it's address, so we can just remove it directly.
928d54cfbdroberto	 */
929d54cfbdroberto	NOTICE("senddone unlinked header");
930d54cfbdroberto	ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
931d54cfbdroberto
932d54cfbdroberto	/*
933d54cfbdroberto	 * We will always want to clean up our receive buffer, even if we
934d54cfbdroberto	 * got an error on send or we are shutting down.
935d54cfbdroberto	 *
936d54cfbdroberto	 * We will pass in the buffer only if there is data in it.  If
937d54cfbdroberto	 * there is no data, we will pass in a NULL.
938d54cfbdroberto	 */
939d54cfbdroberto	if (httpd->freecb != NULL) {
940d54cfbdroberto		isc_buffer_t *b = NULL;
941d54cfbdroberto		if (isc_buffer_length(&httpd->bodybuffer) > 0)
942d54cfbdroberto			b = &httpd->bodybuffer;
943d54cfbdroberto		httpd->freecb(b, httpd->freecb_arg);
944d54cfbdroberto		NOTICE("senddone free callback performed");
945d54cfbdroberto	}
946d54cfbdroberto	if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
947d54cfbdroberto		ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
948d54cfbdroberto		NOTICE("senddone body buffer unlinked");
949d54cfbdroberto	}
950d54cfbdroberto
951d54cfbdroberto	if (sev->result != ISC_R_SUCCESS) {
952d54cfbdroberto		destroy_client(&httpd);
953d54cfbdroberto		goto out;
954d54cfbdroberto	}
955d54cfbdroberto
956d54cfbdroberto	if ((httpd->flags & HTTPD_CLOSE) != 0) {
957d54cfbdroberto		destroy_client(&httpd);
958d54cfbdroberto		goto out;
959d54cfbdroberto	}
960d54cfbdroberto
961d54cfbdroberto	ISC_HTTPD_SETRECV(httpd);
962d54cfbdroberto
963d54cfbdroberto	NOTICE("senddone restarting recv on socket");
964d54cfbdroberto
965d54cfbdroberto	reset_client(httpd);
966d54cfbdroberto
967d54cfbdroberto	r.base = (unsigned char *)httpd->recvbuf;
968d54cfbdroberto	r.length = HTTP_RECVLEN - 1;
969047f369cy	/* check return code? */
970047f369cy	(void)isc_socket_recv(httpd->sock, &r, 1, task,
971047f369cy			      isc_httpd_recvdone, httpd);
972d54cfbdroberto
973d54cfbdrobertoout:
974d54cfbdroberto	isc_event_free(&ev);
975d54cfbdroberto	EXIT("senddone");
976d54cfbdroberto}
977d54cfbdroberto
978d54cfbdrobertostatic void
979d54cfbdrobertoreset_client(isc_httpd_t *httpd)
980d54cfbdroberto{
981d54cfbdroberto	/*
982d54cfbdroberto	 * Catch errors here.  We MUST be in RECV mode, and we MUST NOT have
983d54cfbdroberto	 * any outstanding buffers.  If we have buffers, we have a leak.
984d54cfbdroberto	 */
985d54cfbdroberto	INSIST(ISC_HTTPD_ISRECV(httpd));
986d54cfbdroberto	INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
987d54cfbdroberto	INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
988d54cfbdroberto
989d54cfbdroberto	httpd->recvbuf[0] = 0;
990d54cfbdroberto	httpd->recvlen = 0;
991d54cfbdroberto	httpd->method = ISC_HTTPD_METHODUNKNOWN;
992d54cfbdroberto	httpd->url = NULL;
993d54cfbdroberto	httpd->querystring = NULL;
994d54cfbdroberto	httpd->protocol = NULL;
995d54cfbdroberto	httpd->flags = 0;
996d54cfbdroberto
997d54cfbdroberto	isc_buffer_clear(&httpd->headerbuffer);
998d54cfbdroberto	isc_buffer_invalidate(&httpd->bodybuffer);
999d54cfbdroberto}
1000d54cfbdroberto
1001d54cfbdrobertoisc_result_t
1002d54cfbdrobertoisc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
1003d54cfbdroberto		    isc_httpdaction_t *func, void *arg)
1004d54cfbdroberto{
1005d54cfbdroberto	isc_httpdurl_t *item;
1006d54cfbdroberto
1007d54cfbdroberto	if (url == NULL) {
1008d54cfbdroberto		httpdmgr->render_404 = func;
1009d54cfbdroberto		return (ISC_R_SUCCESS);
1010d54cfbdroberto	}
1011d54cfbdroberto
1012d54cfbdroberto	item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
1013d54cfbdroberto	if (item == NULL)
1014d54cfbdroberto		return (ISC_R_NOMEMORY);
1015d54cfbdroberto
1016d54cfbdroberto	item->url = isc_mem_strdup(httpdmgr->mctx, url);
1017d54cfbdroberto	if (item->url == NULL) {
1018d54cfbdroberto		isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
1019d54cfbdroberto		return (ISC_R_NOMEMORY);
1020d54cfbdroberto	}
1021d54cfbdroberto
1022d54cfbdroberto	item->action = func;
1023d54cfbdroberto	item->action_arg = arg;
1024d54cfbdroberto	ISC_LINK_INIT(item, link);
1025d54cfbdroberto	ISC_LIST_APPEND(httpdmgr->urls, item, link);
1026d54cfbdroberto
1027d54cfbdroberto	return (ISC_R_SUCCESS);
1028d54cfbdroberto}
1029