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