2 * Copyright (C) 2006-2008, 2010-2012, 2014-2016 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.
23 #include <isc/buffer.h>
24 #include <isc/httpd.h>
26 #include <isc/print.h>
27 #include <isc/socket.h>
28 #include <isc/string.h>
38 * o Put in better checks to make certain things are passed in correctly.
39 * This includes a magic number for externally-visible structures,
40 * checking for NULL-ness before dereferencing, etc.
41 * o Make the URL processing external functions which will fill-in a buffer
42 * structure we provide, or return an error and we will render a generic
43 * page and close the client.
46 #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
47 #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
50 #define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0)
51 #define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0)
52 #define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0)
54 #define ENTER(x) do { } while(0)
55 #define EXIT(x) do { } while(0)
56 #define NOTICE(x) do { } while(0)
59 #define HTTP_RECVLEN 1024
60 #define HTTP_SENDGROW 1024
61 #define HTTP_SEND_MAXLEN 10240
63 #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
64 #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
65 #define HTTPD_KEEPALIVE 0x0004 /* Got a Connection: Keep-Alive */
69 isc_httpdmgr_t *mgr; /*%< our parent */
70 ISC_LINK(isc_httpd_t) link;
75 * Received data state.
77 char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
78 isc_uint32_t recvlen; /*%< length recv'd */
79 char *headers; /*%< set in process_request() */
86 * Flags on the httpd client.
91 * Transmit data state.
93 * This is the data buffer we will transmit.
95 * This free function pointer is filled in by the rendering function
96 * we call. The free function is called after the data is transmitted
99 * The bufflist is the list of buffers we are currently transmitting.
100 * The headerdata is where we render our headers to. If we run out of
101 * space when rendering a header, we will change the size of our
102 * buffer. We will not free it until we are finished, and will
103 * allocate an additional HTTP_SENDGROW bytes per header space grow.
105 * We currently use two buffers total, one for the headers (which
106 * we manage) and another for the client to fill in (which it manages,
107 * it provides the space for it, etc) -- we will pass that buffer
108 * structure back to the caller, who is responsible for managing the
109 * space it may have allocated as backing store for it. This second
110 * buffer is bodybuffer, and we only allocate the buffer itself, not
113 isc_bufferlist_t bufflist;
114 char *headerdata; /*%< send header buf */
115 unsigned int headerlen; /*%< current header buffer size */
116 isc_buffer_t headerbuffer;
118 const char *mimetype;
119 unsigned int retcode;
121 isc_buffer_t bodybuffer;
122 isc_httpdfree_t *freecb;
126 /*% lightweight socket manager for httpd output */
127 struct isc_httpdmgr {
129 isc_socket_t *sock; /*%< listening socket */
130 isc_task_t *task; /*%< owning task */
131 isc_timermgr_t *timermgr;
133 isc_httpdclientok_t *client_ok; /*%< client validator */
134 isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
135 void *cb_arg; /*%< argument for the above */
138 ISC_LIST(isc_httpd_t) running; /*%< running clients */
142 ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
143 isc_httpdaction_t *render_404;
144 isc_httpdaction_t *render_500;
150 #define ISC_HTTPD_METHODUNKNOWN 0
151 #define ISC_HTTPD_METHODGET 1
152 #define ISC_HTTPD_METHODPOST 2
157 * _IDLE The client is not doing anything at all. This state should
158 * only occur just after creation, and just before being
161 * _RECV The client is waiting for data after issuing a socket recv().
163 * _RECVDONE Data has been received, and is being processed.
165 * _SEND All data for a response has completed, and a reply was
166 * sent via a socket send() call.
168 * _SENDDONE Send is completed.
170 * Badly formatted state table:
172 * IDLE -> RECV when client has a recv() queued.
174 * RECV -> RECVDONE when recvdone event received.
176 * RECVDONE -> SEND if the data for a reply is at hand.
178 * SEND -> RECV when a senddone event was received.
180 * At any time -> RECV on error. If RECV fails, the client will
181 * self-destroy, closing the socket and freeing memory.
183 #define ISC_HTTPD_STATEIDLE 0
184 #define ISC_HTTPD_STATERECV 1
185 #define ISC_HTTPD_STATERECVDONE 2
186 #define ISC_HTTPD_STATESEND 3
187 #define ISC_HTTPD_STATESENDDONE 4
189 #define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV)
190 #define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
191 #define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND)
192 #define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
195 * Overall magic test that means we're not idle.
197 #define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV)
198 #define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE)
199 #define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND)
200 #define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE)
202 static void isc_httpd_accept(isc_task_t *, isc_event_t *);
203 static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
204 static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
205 static void destroy_client(isc_httpd_t **);
206 static isc_result_t process_request(isc_httpd_t *, int);
207 static void httpdmgr_destroy(isc_httpdmgr_t *);
208 static isc_result_t grow_headerspace(isc_httpd_t *);
209 static void reset_client(isc_httpd_t *httpd);
211 static isc_httpdaction_t render_404;
212 static isc_httpdaction_t render_500;
215 destroy_client(isc_httpd_t **httpdp) {
216 isc_httpd_t *httpd = *httpdp;
217 isc_httpdmgr_t *httpdmgr = httpd->mgr;
221 LOCK(&httpdmgr->lock);
223 isc_socket_detach(&httpd->sock);
224 ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
226 if (httpd->headerlen > 0)
227 isc_mem_put(httpdmgr->mctx, httpd->headerdata,
230 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
232 UNLOCK(&httpdmgr->lock);
234 httpdmgr_destroy(httpdmgr);
238 isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
239 isc_httpdclientok_t *client_ok,
240 isc_httpdondestroy_t *ondestroy, void *cb_arg,
241 isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdmgrp)
244 isc_httpdmgr_t *httpdmgr;
246 REQUIRE(mctx != NULL);
247 REQUIRE(sock != NULL);
248 REQUIRE(task != NULL);
249 REQUIRE(tmgr != NULL);
250 REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL);
252 httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
253 if (httpdmgr == NULL)
254 return (ISC_R_NOMEMORY);
256 result = isc_mutex_init(&httpdmgr->lock);
257 if (result != ISC_R_SUCCESS) {
258 isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
261 httpdmgr->mctx = NULL;
262 isc_mem_attach(mctx, &httpdmgr->mctx);
263 httpdmgr->sock = NULL;
264 isc_socket_attach(sock, &httpdmgr->sock);
265 httpdmgr->task = NULL;
266 isc_task_attach(task, &httpdmgr->task);
267 httpdmgr->timermgr = tmgr; /* XXXMLG no attach function? */
268 httpdmgr->client_ok = client_ok;
269 httpdmgr->ondestroy = ondestroy;
270 httpdmgr->cb_arg = cb_arg;
273 ISC_LIST_INIT(httpdmgr->running);
274 ISC_LIST_INIT(httpdmgr->urls);
276 /* XXXMLG ignore errors on isc_socket_listen() */
277 result = isc_socket_listen(sock, SOMAXCONN);
278 if (result != ISC_R_SUCCESS) {
279 UNEXPECTED_ERROR(__FILE__, __LINE__,
280 "isc_socket_listen() failed: %s",
281 isc_result_totext(result));
285 (void)isc_socket_filter(sock, "httpready");
287 result = isc_socket_accept(sock, task, isc_httpd_accept, httpdmgr);
288 if (result != ISC_R_SUCCESS)
291 httpdmgr->render_404 = render_404;
292 httpdmgr->render_500 = render_500;
294 *httpdmgrp = httpdmgr;
295 return (ISC_R_SUCCESS);
298 isc_task_detach(&httpdmgr->task);
299 isc_socket_detach(&httpdmgr->sock);
300 isc_mem_detach(&httpdmgr->mctx);
301 (void)isc_mutex_destroy(&httpdmgr->lock);
302 isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
307 httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) {
311 ENTER("httpdmgr_destroy");
313 LOCK(&httpdmgr->lock);
315 if (!MSHUTTINGDOWN(httpdmgr)) {
316 NOTICE("httpdmgr_destroy not shutting down yet");
317 UNLOCK(&httpdmgr->lock);
322 * If all clients are not shut down, don't do anything yet.
324 if (!ISC_LIST_EMPTY(httpdmgr->running)) {
325 NOTICE("httpdmgr_destroy clients still active");
326 UNLOCK(&httpdmgr->lock);
330 NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
332 isc_socket_detach(&httpdmgr->sock);
333 isc_task_detach(&httpdmgr->task);
334 httpdmgr->timermgr = NULL;
337 * Clear out the list of all actions we know about. Just free the
340 url = ISC_LIST_HEAD(httpdmgr->urls);
341 while (url != NULL) {
342 isc_mem_free(httpdmgr->mctx, url->url);
343 ISC_LIST_UNLINK(httpdmgr->urls, url, link);
344 isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
345 url = ISC_LIST_HEAD(httpdmgr->urls);
348 UNLOCK(&httpdmgr->lock);
349 (void)isc_mutex_destroy(&httpdmgr->lock);
351 if (httpdmgr->ondestroy != NULL)
352 (httpdmgr->ondestroy)(httpdmgr->cb_arg);
354 mctx = httpdmgr->mctx;
355 isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
357 EXIT("httpdmgr_destroy");
360 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
361 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
364 * Look for the given header in headers.
365 * If value is specified look for it terminated with a character in eov.
368 have_header(isc_httpd_t *httpd, const char *header, const char *value,
372 size_t hlen, vlen = 0;
375 hlen = strlen(header);
378 vlen = strlen(value);
382 if (strncasecmp(h, header, hlen) != 0) {
386 cr = strchr(h, '\r');
387 if (cr != NULL && cr[1] == '\n')
389 nl = strchr(h, '\n');
393 if (h == NULL || (nl != NULL && nl < h))
405 * Skip optional leading white space.
408 while (*h == ' ' || *h == '\t')
411 * Terminate token search on NULL or EOL.
413 while (*h != 0 && *h != '\r' && *h != '\n') {
414 if (strncasecmp(h, value, vlen) == 0)
415 if (strchr(eov, h[vlen]) != NULL)
418 * Skip to next token.
420 h += strcspn(h, eov);
421 if (h[0] == '\r' && h[1] == '\n')
431 process_request(isc_httpd_t *httpd, int length) {
438 httpd->recvlen += length;
440 httpd->recvbuf[httpd->recvlen] = 0;
441 httpd->headers = NULL;
444 * If we don't find a blank line in our buffer, return that we need
447 s = strstr(httpd->recvbuf, "\r\n\r\n");
450 s = strstr(httpd->recvbuf, "\n\n");
454 return (ISC_R_NOTFOUND);
457 * NUL terminate request at the blank line.
462 * Determine if this is a POST or GET method. Any other values will
463 * cause an error to be returned.
465 if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
466 httpd->method = ISC_HTTPD_METHODGET;
467 p = httpd->recvbuf + 4;
468 } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
469 httpd->method = ISC_HTTPD_METHODPOST;
470 p = httpd->recvbuf + 5;
472 return (ISC_R_RANGE);
476 * From now on, p is the start of our buffer.
483 while (LENGTHOK(s) && BUFLENOK(s) &&
484 (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
487 return (ISC_R_NOTFOUND);
489 return (ISC_R_NOMEMORY);
493 * Make the URL relative.
495 if ((strncmp(p, "http:/", 6) == 0)
496 || (strncmp(p, "https:/", 7) == 0)) {
498 while (*p != '/' && *p != 0)
501 return (ISC_R_RANGE);
504 while (*p != '/' && *p != 0)
507 return (ISC_R_RANGE);
510 while (*p != '/' && *p != 0)
523 * Now, see if there is a ? mark in the URL. If so, this is
524 * part of the query string, and we will split it from the URL.
526 httpd->querystring = strchr(httpd->url, '?');
527 if (httpd->querystring != NULL) {
528 *(httpd->querystring) = 0;
529 httpd->querystring++;
533 * Extract the HTTP/1.X protocol. We will bounce on anything but
534 * HTTP/1.0 or HTTP/1.1 for now.
536 while (LENGTHOK(s) && BUFLENOK(s) &&
537 (*s != '\n' && *s != '\r' && *s != '\0'))
540 return (ISC_R_NOTFOUND);
542 return (ISC_R_NOMEMORY);
544 * Check that we have the expected eol delimiter.
546 if (strncmp(s, delim == 1 ? "\n" : "\r\n", delim) != 0)
547 return (ISC_R_RANGE);
549 if ((strncmp(p, "HTTP/1.0", 8) != 0)
550 && (strncmp(p, "HTTP/1.1", 8) != 0))
551 return (ISC_R_RANGE);
553 p = s + delim; /* skip past eol */
558 if (have_header(httpd, "Connection:", "close", ", \t\r\n"))
559 httpd->flags |= HTTPD_CLOSE;
561 if (have_header(httpd, "Host:", NULL, NULL))
562 httpd->flags |= HTTPD_FOUNDHOST;
564 if (strncmp(httpd->protocol, "HTTP/1.0", 8) == 0) {
565 if (have_header(httpd, "Connection:", "Keep-Alive",
567 httpd->flags |= HTTPD_KEEPALIVE;
569 httpd->flags |= HTTPD_CLOSE;
573 * Standards compliance hooks here.
575 if (strcmp(httpd->protocol, "HTTP/1.1") == 0
576 && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
577 return (ISC_R_RANGE);
581 return (ISC_R_SUCCESS);
585 isc_httpd_accept(isc_task_t *task, isc_event_t *ev) {
587 isc_httpdmgr_t *httpdmgr = ev->ev_arg;
590 isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
591 isc_sockaddr_t peeraddr;
595 LOCK(&httpdmgr->lock);
596 if (MSHUTTINGDOWN(httpdmgr)) {
597 NOTICE("accept shutting down, goto out");
601 if (nev->result == ISC_R_CANCELED) {
602 NOTICE("accept canceled, goto out");
606 if (nev->result != ISC_R_SUCCESS) {
607 /* XXXMLG log failure */
608 NOTICE("accept returned failure, goto requeue");
612 (void)isc_socket_getpeername(nev->newsocket, &peeraddr);
613 if (httpdmgr->client_ok != NULL &&
614 !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
615 isc_socket_detach(&nev->newsocket);
619 httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
621 /* XXXMLG log failure */
622 NOTICE("accept failed to allocate memory, goto requeue");
623 isc_socket_detach(&nev->newsocket);
627 httpd->mgr = httpdmgr;
628 ISC_LINK_INIT(httpd, link);
629 ISC_LIST_APPEND(httpdmgr->running, httpd, link);
630 ISC_HTTPD_SETRECV(httpd);
631 httpd->sock = nev->newsocket;
632 isc_socket_setname(httpd->sock, "httpd", NULL);
636 * Initialize the buffer for our headers.
638 httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
639 if (httpd->headerdata == NULL) {
640 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
641 isc_socket_detach(&nev->newsocket);
644 httpd->headerlen = HTTP_SENDGROW;
645 isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
648 ISC_LIST_INIT(httpd->bufflist);
650 isc_buffer_initnull(&httpd->bodybuffer);
653 r.base = (unsigned char *)httpd->recvbuf;
654 r.length = HTTP_RECVLEN - 1;
655 result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
659 NOTICE("accept queued recv on socket");
662 result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
664 if (result != ISC_R_SUCCESS) {
665 /* XXXMLG what to do? Log failure... */
666 NOTICE("accept could not reaccept due to failure");
670 UNLOCK(&httpdmgr->lock);
672 httpdmgr_destroy(httpdmgr);
680 render_404(const char *url, isc_httpdurl_t *urlinfo,
681 const char *querystring, const char *headers, void *arg,
682 unsigned int *retcode, const char **retmsg,
683 const char **mimetype, isc_buffer_t *b,
684 isc_httpdfree_t **freecb, void **freecb_args)
686 static char msg[] = "No such URL.\r\n";
695 *retmsg = "No such URL";
696 *mimetype = "text/plain";
697 isc_buffer_reinit(b, msg, strlen(msg));
698 isc_buffer_add(b, strlen(msg));
702 return (ISC_R_SUCCESS);
706 render_500(const char *url, isc_httpdurl_t *urlinfo,
707 const char *querystring, const char *headers, void *arg,
708 unsigned int *retcode, const char **retmsg,
709 const char **mimetype, isc_buffer_t *b,
710 isc_httpdfree_t **freecb, void **freecb_args)
712 static char msg[] = "Internal server failure.\r\n";
721 *retmsg = "Internal server failure";
722 *mimetype = "text/plain";
723 isc_buffer_reinit(b, msg, strlen(msg));
724 isc_buffer_add(b, strlen(msg));
728 return (ISC_R_SUCCESS);
732 isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) {
735 isc_httpd_t *httpd = ev->ev_arg;
736 isc_socketevent_t *sev = (isc_socketevent_t *)ev;
739 char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
743 INSIST(ISC_HTTPD_ISRECV(httpd));
745 if (sev->result != ISC_R_SUCCESS) {
746 NOTICE("recv destroying client");
747 destroy_client(&httpd);
751 result = process_request(httpd, sev->n);
752 if (result == ISC_R_NOTFOUND) {
753 if (httpd->recvlen >= HTTP_RECVLEN - 1) {
754 destroy_client(&httpd);
757 r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
758 r.length = HTTP_RECVLEN - httpd->recvlen - 1;
759 /* check return code? */
760 (void)isc_socket_recv(httpd->sock, &r, 1, task,
761 isc_httpd_recvdone, httpd);
763 } else if (result != ISC_R_SUCCESS) {
764 destroy_client(&httpd);
768 ISC_HTTPD_SETSEND(httpd);
771 * XXXMLG Call function here. Provide an add-header function
772 * which will append the common headers to a response we generate.
774 isc_buffer_initnull(&httpd->bodybuffer);
776 isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
777 url = ISC_LIST_HEAD(httpd->mgr->urls);
778 while (url != NULL) {
779 if (strcmp(httpd->url, url->url) == 0)
781 url = ISC_LIST_NEXT(url, link);
784 result = httpd->mgr->render_404(httpd->url, NULL,
794 result = url->action(httpd->url, url,
798 &httpd->retcode, &httpd->retmsg,
799 &httpd->mimetype, &httpd->bodybuffer,
800 &httpd->freecb, &httpd->freecb_arg);
801 if (result != ISC_R_SUCCESS) {
802 result = httpd->mgr->render_500(httpd->url, url,
811 RUNTIME_CHECK(result == ISC_R_SUCCESS);
814 isc_httpd_response(httpd);
815 if ((httpd->flags & HTTPD_KEEPALIVE) != 0)
816 isc_httpd_addheader(httpd, "Connection", "Keep-Alive");
817 isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
818 isc_httpd_addheader(httpd, "Date", datebuf);
819 isc_httpd_addheader(httpd, "Expires", datebuf);
821 if (url != NULL && url->isstatic) {
822 char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
823 isc_time_formathttptimestamp(&url->loadtime,
824 loadbuf, sizeof(loadbuf));
825 isc_httpd_addheader(httpd, "Last-Modified", loadbuf);
826 isc_httpd_addheader(httpd, "Cache-Control: public", NULL);
828 isc_httpd_addheader(httpd, "Last-Modified", datebuf);
829 isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
830 isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
833 isc_httpd_addheader(httpd, "Server: libisc", NULL);
834 isc_httpd_addheaderuint(httpd, "Content-Length",
835 isc_buffer_usedlength(&httpd->bodybuffer));
836 isc_httpd_endheaders(httpd); /* done */
838 ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
840 * Link the data buffer into our send queue, should we have any data
841 * rendered into it. If no data is present, we won't do anything
844 if (isc_buffer_length(&httpd->bodybuffer) > 0)
845 ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
847 /* check return code? */
848 (void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
849 isc_httpd_senddone, httpd);
857 isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
858 isc_httpdmgr_t *httpdmgr;
860 httpdmgr = *httpdmgrp;
863 ENTER("isc_httpdmgr_shutdown");
865 LOCK(&httpdmgr->lock);
867 MSETSHUTTINGDOWN(httpdmgr);
869 isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
871 httpd = ISC_LIST_HEAD(httpdmgr->running);
872 while (httpd != NULL) {
873 isc_socket_cancel(httpd->sock, httpdmgr->task,
875 httpd = ISC_LIST_NEXT(httpd, link);
878 UNLOCK(&httpdmgr->lock);
880 EXIT("isc_httpdmgr_shutdown");
884 grow_headerspace(isc_httpd_t *httpd) {
889 newlen = httpd->headerlen + HTTP_SENDGROW;
890 if (newlen > HTTP_SEND_MAXLEN)
891 return (ISC_R_NOSPACE);
893 newspace = isc_mem_get(httpd->mgr->mctx, newlen);
894 if (newspace == NULL)
895 return (ISC_R_NOMEMORY);
896 isc_buffer_region(&httpd->headerbuffer, &r);
897 isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
899 isc_mem_put(httpd->mgr->mctx, r.base, r.length);
901 return (ISC_R_SUCCESS);
905 isc_httpd_response(isc_httpd_t *httpd) {
907 unsigned int needlen;
909 needlen = strlen(httpd->protocol) + 1; /* protocol + space */
910 needlen += 3 + 1; /* room for response code, always 3 bytes */
911 needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
913 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
914 result = grow_headerspace(httpd);
915 if (result != ISC_R_SUCCESS)
919 sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03u %s\r\n",
920 httpd->protocol, httpd->retcode, httpd->retmsg);
921 isc_buffer_add(&httpd->headerbuffer, needlen);
923 return (ISC_R_SUCCESS);
927 isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
931 unsigned int needlen;
933 needlen = strlen(name); /* name itself */
935 needlen += 2 + strlen(val); /* :<space> and val */
936 needlen += 2; /* CRLF */
938 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
939 result = grow_headerspace(httpd);
940 if (result != ISC_R_SUCCESS)
945 sprintf(isc_buffer_used(&httpd->headerbuffer),
946 "%s: %s\r\n", name, val);
948 sprintf(isc_buffer_used(&httpd->headerbuffer),
951 isc_buffer_add(&httpd->headerbuffer, needlen);
953 return (ISC_R_SUCCESS);
957 isc_httpd_endheaders(isc_httpd_t *httpd) {
960 while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
961 result = grow_headerspace(httpd);
962 if (result != ISC_R_SUCCESS)
966 sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
967 isc_buffer_add(&httpd->headerbuffer, 2);
969 return (ISC_R_SUCCESS);
973 isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
975 unsigned int needlen;
976 char buf[sizeof "18446744073709551616"];
978 sprintf(buf, "%d", val);
980 needlen = strlen(name); /* name itself */
981 needlen += 2 + strlen(buf); /* :<space> and val */
982 needlen += 2; /* CRLF */
984 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
985 result = grow_headerspace(httpd);
986 if (result != ISC_R_SUCCESS)
990 sprintf(isc_buffer_used(&httpd->headerbuffer),
991 "%s: %s\r\n", name, buf);
993 isc_buffer_add(&httpd->headerbuffer, needlen);
995 return (ISC_R_SUCCESS);
999 isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) {
1000 isc_httpd_t *httpd = ev->ev_arg;
1002 isc_socketevent_t *sev = (isc_socketevent_t *)ev;
1005 INSIST(ISC_HTTPD_ISSEND(httpd));
1008 * First, unlink our header buffer from the socket's bufflist. This
1009 * is sort of an evil hack, since we know our buffer will be there,
1010 * and we know it's address, so we can just remove it directly.
1012 NOTICE("senddone unlinked header");
1013 ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
1016 * We will always want to clean up our receive buffer, even if we
1017 * got an error on send or we are shutting down.
1019 * We will pass in the buffer only if there is data in it. If
1020 * there is no data, we will pass in a NULL.
1022 if (httpd->freecb != NULL) {
1023 isc_buffer_t *b = NULL;
1024 if (isc_buffer_length(&httpd->bodybuffer) > 0)
1025 b = &httpd->bodybuffer;
1026 httpd->freecb(b, httpd->freecb_arg);
1027 NOTICE("senddone free callback performed");
1029 if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
1030 ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
1031 NOTICE("senddone body buffer unlinked");
1034 if (sev->result != ISC_R_SUCCESS) {
1035 destroy_client(&httpd);
1039 if ((httpd->flags & HTTPD_CLOSE) != 0) {
1040 destroy_client(&httpd);
1044 ISC_HTTPD_SETRECV(httpd);
1046 NOTICE("senddone restarting recv on socket");
1048 reset_client(httpd);
1050 r.base = (unsigned char *)httpd->recvbuf;
1051 r.length = HTTP_RECVLEN - 1;
1052 /* check return code? */
1053 (void)isc_socket_recv(httpd->sock, &r, 1, task,
1054 isc_httpd_recvdone, httpd);
1057 isc_event_free(&ev);
1062 reset_client(isc_httpd_t *httpd) {
1064 * Catch errors here. We MUST be in RECV mode, and we MUST NOT have
1065 * any outstanding buffers. If we have buffers, we have a leak.
1067 INSIST(ISC_HTTPD_ISRECV(httpd));
1068 INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
1069 INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
1071 httpd->recvbuf[0] = 0;
1073 httpd->headers = NULL;
1074 httpd->method = ISC_HTTPD_METHODUNKNOWN;
1076 httpd->querystring = NULL;
1077 httpd->protocol = NULL;
1080 isc_buffer_clear(&httpd->headerbuffer);
1081 isc_buffer_invalidate(&httpd->bodybuffer);
1085 isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
1086 isc_httpdaction_t *func, void *arg)
1088 return (isc_httpdmgr_addurl2(httpdmgr, url, ISC_FALSE, func, arg));
1092 isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url,
1093 isc_boolean_t isstatic,
1094 isc_httpdaction_t *func, void *arg)
1096 isc_httpdurl_t *item;
1099 httpdmgr->render_404 = func;
1100 return (ISC_R_SUCCESS);
1103 item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
1105 return (ISC_R_NOMEMORY);
1107 item->url = isc_mem_strdup(httpdmgr->mctx, url);
1108 if (item->url == NULL) {
1109 isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
1110 return (ISC_R_NOMEMORY);
1113 item->action = func;
1114 item->action_arg = arg;
1115 item->isstatic = isstatic;
1116 isc_time_now(&item->loadtime);
1118 ISC_LINK_INIT(item, link);
1119 ISC_LIST_APPEND(httpdmgr->urls, item, link);
1121 return (ISC_R_SUCCESS);