kernel - Update swapcache manual page
[dragonfly.git] / contrib / bind / lib / isc / httpd.c
blob9cccb1e47a96d5f26d8ffa656823ec355d85804f
1 /*
2 * Copyright (C) 2006-2008 Internet Systems Consortium, Inc. ("ISC")
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.
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.
17 /* $Id: httpd.c,v 1.12.12.3 2008/08/08 05:10:34 marka Exp $ */
19 /*! \file */
21 #include <config.h>
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>
31 #include <string.h>
33 /*%
34 * TODO:
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.
44 #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
45 #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
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
57 #define HTTP_RECVLEN 1024
58 #define HTTP_SENDGROW 1024
59 #define HTTP_SEND_MAXLEN 10240
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.
68 struct isc_httpdurl {
69 char *url;
70 isc_httpdaction_t *action;
71 void *action_arg;
72 ISC_LINK(isc_httpdurl_t) link;
75 #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
76 #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
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;
85 /*%
86 * Received data state.
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;
96 * Flags on the httpd client.
98 int flags;
101 * Transmit data state.
103 * This is the data buffer we will transmit.
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.
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.
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.
123 isc_bufferlist_t bufflist;
124 char *headerdata; /*%< send header buf */
125 unsigned int headerlen; /*%< current header buffer size */
126 isc_buffer_t headerbuffer;
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;
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;
143 isc_httpdclientok_t *client_ok; /*%< client validator */
144 isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
145 void *cb_arg; /*%< argument for the above */
147 unsigned int flags;
148 ISC_LIST(isc_httpd_t) running; /*%< running clients */
150 isc_mutex_t lock;
152 ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
153 isc_httpdaction_t *render_404;
157 * HTTP methods.
159 #define ISC_HTTPD_METHODUNKNOWN 0
160 #define ISC_HTTPD_METHODGET 1
161 #define ISC_HTTPD_METHODPOST 2
164 * Client states.
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.
170 * _RECV The client is waiting for data after issuing a socket recv().
172 * _RECVDONE Data has been received, and is being processed.
174 * _SEND All data for a response has completed, and a reply was
175 * sent via a socket send() call.
177 * _SENDDONE Send is completed.
179 * Badly formatted state table:
181 * IDLE -> RECV when client has a recv() queued.
183 * RECV -> RECVDONE when recvdone event received.
185 * RECVDONE -> SEND if the data for a reply is at hand.
187 * SEND -> RECV when a senddone event was received.
189 * At any time -> RECV on error. If RECV fails, the client will
190 * self-destroy, closing the socket and freeing memory.
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
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)
204 * Overall magic test that means we're not idle.
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)
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 **);
225 static void
226 destroy_client(isc_httpd_t **httpdp)
228 isc_httpd_t *httpd = *httpdp;
229 isc_httpdmgr_t *httpdmgr = httpd->mgr;
231 *httpdp = NULL;
233 LOCK(&httpdmgr->lock);
235 isc_socket_detach(&httpd->sock);
236 ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
238 if (httpd->headerlen > 0)
239 isc_mem_put(httpdmgr->mctx, httpd->headerdata,
240 httpd->headerlen);
242 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
244 UNLOCK(&httpdmgr->lock);
246 httpdmgr_destroy(httpdmgr);
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)
255 isc_result_t result;
256 isc_httpdmgr_t *httpd;
258 REQUIRE(mctx != NULL);
259 REQUIRE(sock != NULL);
260 REQUIRE(task != NULL);
261 REQUIRE(tmgr != NULL);
262 REQUIRE(httpdp != NULL && *httpdp == NULL);
264 httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
265 if (httpd == NULL)
266 return (ISC_R_NOMEMORY);
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);
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;
284 ISC_LIST_INIT(httpd->running);
285 ISC_LIST_INIT(httpd->urls);
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;
296 (void)isc_socket_filter(sock, "httpready");
298 result = isc_socket_accept(sock, task, isc_httpd_accept, httpd);
299 if (result != ISC_R_SUCCESS)
300 goto cleanup;
302 httpd->render_404 = render_404;
304 *httpdp = httpd;
305 return (ISC_R_SUCCESS);
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);
316 static void
317 httpdmgr_destroy(isc_httpdmgr_t *httpdmgr)
319 isc_mem_t *mctx;
320 isc_httpdurl_t *url;
322 ENTER("httpdmgr_destroy");
324 LOCK(&httpdmgr->lock);
326 if (!MSHUTTINGDOWN(httpdmgr)) {
327 NOTICE("httpdmgr_destroy not shutting down yet");
328 UNLOCK(&httpdmgr->lock);
329 return;
333 * If all clients are not shut down, don't do anything yet.
335 if (!ISC_LIST_EMPTY(httpdmgr->running)) {
336 NOTICE("httpdmgr_destroy clients still active");
337 UNLOCK(&httpdmgr->lock);
338 return;
341 NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
343 isc_socket_detach(&httpdmgr->sock);
344 isc_task_detach(&httpdmgr->task);
345 httpdmgr->timermgr = NULL;
348 * Clear out the list of all actions we know about. Just free the
349 * memory.
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);
359 UNLOCK(&httpdmgr->lock);
360 isc_mutex_destroy(&httpdmgr->lock);
362 if (httpdmgr->ondestroy != NULL)
363 (httpdmgr->ondestroy)(httpdmgr->cb_arg);
365 mctx = httpdmgr->mctx;
366 isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
368 EXIT("httpdmgr_destroy");
371 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
372 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
374 static isc_result_t
375 process_request(isc_httpd_t *httpd, int length)
377 char *s;
378 char *p;
379 int delim;
381 ENTER("request");
383 httpd->recvlen += length;
385 httpd->recvbuf[httpd->recvlen] = 0;
388 * If we don't find a blank line in our buffer, return that we need
389 * more data.
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;
397 if (s == NULL)
398 return (ISC_R_NOTFOUND);
401 * Determine if this is a POST or GET method. Any other values will
402 * cause an error to be returned.
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);
415 * From now on, p is the start of our buffer.
419 * Extract the URL.
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;
432 * Make the URL relative.
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 = '/';
457 httpd->url = p;
458 p = s + delim;
459 s = p;
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.
465 httpd->querystring = strchr(httpd->url, '?');
466 if (httpd->querystring != NULL) {
467 *(httpd->querystring) = 0;
468 httpd->querystring++;
472 * Extract the HTTP/1.X protocol. We will bounce on anything but
473 * HTTP/1.1 for now.
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;
490 if (strstr(s, "Connection: close") != NULL)
491 httpd->flags |= HTTPD_CLOSE;
493 if (strstr(s, "Host: ") != NULL)
494 httpd->flags |= HTTPD_FOUNDHOST;
497 * Standards compliance hooks here.
499 if (strcmp(httpd->protocol, "HTTP/1.1") == 0
500 && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
501 return (ISC_R_RANGE);
503 EXIT("request");
505 return (ISC_R_SUCCESS);
508 static void
509 isc_httpd_accept(isc_task_t *task, isc_event_t *ev)
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;
518 ENTER("accept");
520 LOCK(&httpdmgr->lock);
521 if (MSHUTTINGDOWN(httpdmgr)) {
522 NOTICE("accept shutting down, goto out");
523 goto out;
526 if (nev->result == ISC_R_CANCELED) {
527 NOTICE("accept canceled, goto out");
528 goto out;
531 if (nev->result != ISC_R_SUCCESS) {
532 /* XXXMLG log failure */
533 NOTICE("accept returned failure, goto requeue");
534 goto requeue;
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;
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;
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;
561 * Initialize the buffer for our headers.
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;
569 httpd->headerlen = HTTP_SENDGROW;
570 isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
571 httpd->headerlen);
573 ISC_LIST_INIT(httpd->bufflist);
575 isc_buffer_initnull(&httpd->bodybuffer);
576 reset_client(httpd);
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");
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");
592 out:
593 UNLOCK(&httpdmgr->lock);
595 httpdmgr_destroy(httpdmgr);
597 isc_event_free(&ev);
599 EXIT("accept");
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)
609 static char msg[] = "No such URL.";
611 UNUSED(url);
612 UNUSED(querystring);
613 UNUSED(arg);
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;
623 return (ISC_R_SUCCESS);
626 static void
627 isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev)
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 */
637 ENTER("recv");
639 INSIST(ISC_HTTPD_ISRECV(httpd));
641 if (sev->result != ISC_R_SUCCESS) {
642 NOTICE("recv destroying client");
643 destroy_client(&httpd);
644 goto out;
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;
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;
663 ISC_HTTPD_SETSEND(httpd);
666 * XXXMLG Call function here. Provide an add-header function
667 * which will append the common headers to a response we generate.
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);
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;
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 */
710 ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
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.
716 if (isc_buffer_length(&httpd->bodybuffer) > 0)
717 ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
719 result = isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
720 isc_httpd_senddone, httpd);
722 out:
723 isc_event_free(&ev);
724 EXIT("recv");
727 void
728 isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp)
730 isc_httpdmgr_t *httpdmgr;
731 isc_httpd_t *httpd;
732 httpdmgr = *httpdmgrp;
733 *httpdmgrp = NULL;
735 ENTER("isc_httpdmgr_shutdown");
737 LOCK(&httpdmgr->lock);
739 MSETSHUTTINGDOWN(httpdmgr);
741 isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
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);
750 UNLOCK(&httpdmgr->lock);
752 EXIT("isc_httpdmgr_shutdown");
755 static isc_result_t
756 grow_headerspace(isc_httpd_t *httpd)
758 char *newspace;
759 unsigned int newlen;
760 isc_region_t r;
762 newlen = httpd->headerlen + HTTP_SENDGROW;
763 if (newlen > HTTP_SEND_MAXLEN)
764 return (ISC_R_NOSPACE);
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);
772 isc_mem_put(httpd->mgr->mctx, r.base, r.length);
774 return (ISC_R_SUCCESS);
777 isc_result_t
778 isc_httpd_response(isc_httpd_t *httpd)
780 isc_result_t result;
781 unsigned int needlen;
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 */
787 if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
788 result = grow_headerspace(httpd);
789 if (result != ISC_R_SUCCESS)
790 return (result);
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);
797 return (ISC_R_SUCCESS);
800 isc_result_t
801 isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
802 const char *val)
804 isc_result_t result;
805 unsigned int needlen;
807 needlen = strlen(name); /* name itself */
808 if (val != NULL)
809 needlen += 2 + strlen(val); /* :<space> and val */
810 needlen += 2; /* CRLF */
812 if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
813 result = grow_headerspace(httpd);
814 if (result != ISC_R_SUCCESS)
815 return (result);
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);
825 isc_buffer_add(&httpd->headerbuffer, needlen);
827 return (ISC_R_SUCCESS);
830 isc_result_t
831 isc_httpd_endheaders(isc_httpd_t *httpd)
833 isc_result_t result;
835 if (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
836 result = grow_headerspace(httpd);
837 if (result != ISC_R_SUCCESS)
838 return (result);
841 sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
842 isc_buffer_add(&httpd->headerbuffer, 2);
844 return (ISC_R_SUCCESS);
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"];
853 sprintf(buf, "%d", val);
855 needlen = strlen(name); /* name itself */
856 needlen += 2 + strlen(buf); /* :<space> and val */
857 needlen += 2; /* CRLF */
859 if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
860 result = grow_headerspace(httpd);
861 if (result != ISC_R_SUCCESS)
862 return (result);
865 sprintf(isc_buffer_used(&httpd->headerbuffer),
866 "%s: %s\r\n", name, buf);
868 isc_buffer_add(&httpd->headerbuffer, needlen);
870 return (ISC_R_SUCCESS);
873 static void
874 isc_httpd_senddone(isc_task_t *task, isc_event_t *ev)
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;
881 ENTER("senddone");
882 INSIST(ISC_HTTPD_ISSEND(httpd));
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.
889 NOTICE("senddone unlinked header");
890 ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
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.
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.
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");
906 if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
907 ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
908 NOTICE("senddone body buffer unlinked");
911 if (sev->result != ISC_R_SUCCESS) {
912 destroy_client(&httpd);
913 goto out;
916 if ((httpd->flags & HTTPD_CLOSE) != 0) {
917 destroy_client(&httpd);
918 goto out;
921 ISC_HTTPD_SETRECV(httpd);
923 NOTICE("senddone restarting recv on socket");
925 reset_client(httpd);
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);
932 out:
933 isc_event_free(&ev);
934 EXIT("senddone");
937 static void
938 reset_client(isc_httpd_t *httpd)
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.
944 INSIST(ISC_HTTPD_ISRECV(httpd));
945 INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
946 INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
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;
956 isc_buffer_clear(&httpd->headerbuffer);
957 isc_buffer_invalidate(&httpd->bodybuffer);
960 isc_result_t
961 isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
962 isc_httpdaction_t *func, void *arg)
964 isc_httpdurl_t *item;
966 if (url == NULL) {
967 httpdmgr->render_404 = func;
968 return (ISC_R_SUCCESS);
971 item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
972 if (item == NULL)
973 return (ISC_R_NOMEMORY);
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);
981 item->action = func;
982 item->action_arg = arg;
983 ISC_LINK_INIT(item, link);
984 ISC_LIST_APPEND(httpdmgr->urls, item, link);
986 return (ISC_R_SUCCESS);