2 * Copyright (C) 2006-2008, 2010-2012, 2014, 2015 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/socket.h>
27 #include <isc/string.h>
37 * o Put in better checks to make certain things are passed in correctly.
38 * This includes a magic number for externally-visible structures,
39 * checking for NULL-ness before dereferencing, etc.
40 * o Make the URL processing external functions which will fill-in a buffer
41 * structure we provide, or return an error and we will render a generic
42 * page and close the client.
45 #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
46 #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
49 #define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0)
50 #define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0)
51 #define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0)
53 #define ENTER(x) do { } while(0)
54 #define EXIT(x) do { } while(0)
55 #define NOTICE(x) do { } while(0)
58 #define HTTP_RECVLEN 1024
59 #define HTTP_SENDGROW 1024
60 #define HTTP_SEND_MAXLEN 10240
62 #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
63 #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
67 isc_httpdmgr_t *mgr; /*%< our parent */
68 ISC_LINK(isc_httpd_t) link;
73 * Received data state.
75 char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
76 isc_uint32_t recvlen; /*%< length recv'd */
77 char *headers; /*%< set in process_request() */
84 * Flags on the httpd client.
89 * Transmit data state.
91 * This is the data buffer we will transmit.
93 * This free function pointer is filled in by the rendering function
94 * we call. The free function is called after the data is transmitted
97 * The bufflist is the list of buffers we are currently transmitting.
98 * The headerdata is where we render our headers to. If we run out of
99 * space when rendering a header, we will change the size of our
100 * buffer. We will not free it until we are finished, and will
101 * allocate an additional HTTP_SENDGROW bytes per header space grow.
103 * We currently use two buffers total, one for the headers (which
104 * we manage) and another for the client to fill in (which it manages,
105 * it provides the space for it, etc) -- we will pass that buffer
106 * structure back to the caller, who is responsible for managing the
107 * space it may have allocated as backing store for it. This second
108 * buffer is bodybuffer, and we only allocate the buffer itself, not
111 isc_bufferlist_t bufflist;
112 char *headerdata; /*%< send header buf */
113 unsigned int headerlen; /*%< current header buffer size */
114 isc_buffer_t headerbuffer;
116 const char *mimetype;
117 unsigned int retcode;
119 isc_buffer_t bodybuffer;
120 isc_httpdfree_t *freecb;
124 /*% lightweight socket manager for httpd output */
125 struct isc_httpdmgr {
127 isc_socket_t *sock; /*%< listening socket */
128 isc_task_t *task; /*%< owning task */
129 isc_timermgr_t *timermgr;
131 isc_httpdclientok_t *client_ok; /*%< client validator */
132 isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
133 void *cb_arg; /*%< argument for the above */
136 ISC_LIST(isc_httpd_t) running; /*%< running clients */
140 ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
141 isc_httpdaction_t *render_404;
142 isc_httpdaction_t *render_500;
148 #define ISC_HTTPD_METHODUNKNOWN 0
149 #define ISC_HTTPD_METHODGET 1
150 #define ISC_HTTPD_METHODPOST 2
155 * _IDLE The client is not doing anything at all. This state should
156 * only occur just after creation, and just before being
159 * _RECV The client is waiting for data after issuing a socket recv().
161 * _RECVDONE Data has been received, and is being processed.
163 * _SEND All data for a response has completed, and a reply was
164 * sent via a socket send() call.
166 * _SENDDONE Send is completed.
168 * Badly formatted state table:
170 * IDLE -> RECV when client has a recv() queued.
172 * RECV -> RECVDONE when recvdone event received.
174 * RECVDONE -> SEND if the data for a reply is at hand.
176 * SEND -> RECV when a senddone event was received.
178 * At any time -> RECV on error. If RECV fails, the client will
179 * self-destroy, closing the socket and freeing memory.
181 #define ISC_HTTPD_STATEIDLE 0
182 #define ISC_HTTPD_STATERECV 1
183 #define ISC_HTTPD_STATERECVDONE 2
184 #define ISC_HTTPD_STATESEND 3
185 #define ISC_HTTPD_STATESENDDONE 4
187 #define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV)
188 #define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
189 #define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND)
190 #define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
193 * Overall magic test that means we're not idle.
195 #define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV)
196 #define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE)
197 #define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND)
198 #define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE)
200 static void isc_httpd_accept(isc_task_t *, isc_event_t *);
201 static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
202 static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
203 static void destroy_client(isc_httpd_t **);
204 static isc_result_t process_request(isc_httpd_t *, int);
205 static void httpdmgr_destroy(isc_httpdmgr_t *);
206 static isc_result_t grow_headerspace(isc_httpd_t *);
207 static void reset_client(isc_httpd_t *httpd);
209 static isc_httpdaction_t render_404;
210 static isc_httpdaction_t render_500;
213 destroy_client(isc_httpd_t **httpdp) {
214 isc_httpd_t *httpd = *httpdp;
215 isc_httpdmgr_t *httpdmgr = httpd->mgr;
219 LOCK(&httpdmgr->lock);
221 isc_socket_detach(&httpd->sock);
222 ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
224 if (httpd->headerlen > 0)
225 isc_mem_put(httpdmgr->mctx, httpd->headerdata,
228 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
230 UNLOCK(&httpdmgr->lock);
232 httpdmgr_destroy(httpdmgr);
236 isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
237 isc_httpdclientok_t *client_ok,
238 isc_httpdondestroy_t *ondestroy, void *cb_arg,
239 isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp)
242 isc_httpdmgr_t *httpd;
244 REQUIRE(mctx != NULL);
245 REQUIRE(sock != NULL);
246 REQUIRE(task != NULL);
247 REQUIRE(tmgr != NULL);
248 REQUIRE(httpdp != NULL && *httpdp == NULL);
250 httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
252 return (ISC_R_NOMEMORY);
254 result = isc_mutex_init(&httpd->lock);
255 if (result != ISC_R_SUCCESS) {
256 isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
260 isc_mem_attach(mctx, &httpd->mctx);
262 isc_socket_attach(sock, &httpd->sock);
264 isc_task_attach(task, &httpd->task);
265 httpd->timermgr = tmgr; /* XXXMLG no attach function? */
266 httpd->client_ok = client_ok;
267 httpd->ondestroy = ondestroy;
268 httpd->cb_arg = cb_arg;
270 ISC_LIST_INIT(httpd->running);
271 ISC_LIST_INIT(httpd->urls);
273 /* XXXMLG ignore errors on isc_socket_listen() */
274 result = isc_socket_listen(sock, SOMAXCONN);
275 if (result != ISC_R_SUCCESS) {
276 UNEXPECTED_ERROR(__FILE__, __LINE__,
277 "isc_socket_listen() failed: %s",
278 isc_result_totext(result));
282 (void)isc_socket_filter(sock, "httpready");
284 result = isc_socket_accept(sock, task, isc_httpd_accept, httpd);
285 if (result != ISC_R_SUCCESS)
288 httpd->render_404 = render_404;
289 httpd->render_500 = render_500;
292 return (ISC_R_SUCCESS);
295 isc_task_detach(&httpd->task);
296 isc_socket_detach(&httpd->sock);
297 isc_mem_detach(&httpd->mctx);
298 (void)isc_mutex_destroy(&httpd->lock);
299 isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
304 httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) {
308 ENTER("httpdmgr_destroy");
310 LOCK(&httpdmgr->lock);
312 if (!MSHUTTINGDOWN(httpdmgr)) {
313 NOTICE("httpdmgr_destroy not shutting down yet");
314 UNLOCK(&httpdmgr->lock);
319 * If all clients are not shut down, don't do anything yet.
321 if (!ISC_LIST_EMPTY(httpdmgr->running)) {
322 NOTICE("httpdmgr_destroy clients still active");
323 UNLOCK(&httpdmgr->lock);
327 NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
329 isc_socket_detach(&httpdmgr->sock);
330 isc_task_detach(&httpdmgr->task);
331 httpdmgr->timermgr = NULL;
334 * Clear out the list of all actions we know about. Just free the
337 url = ISC_LIST_HEAD(httpdmgr->urls);
338 while (url != NULL) {
339 isc_mem_free(httpdmgr->mctx, url->url);
340 ISC_LIST_UNLINK(httpdmgr->urls, url, link);
341 isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
342 url = ISC_LIST_HEAD(httpdmgr->urls);
345 UNLOCK(&httpdmgr->lock);
346 (void)isc_mutex_destroy(&httpdmgr->lock);
348 if (httpdmgr->ondestroy != NULL)
349 (httpdmgr->ondestroy)(httpdmgr->cb_arg);
351 mctx = httpdmgr->mctx;
352 isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
354 EXIT("httpdmgr_destroy");
357 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
358 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
361 process_request(isc_httpd_t *httpd, int length) {
368 httpd->recvlen += length;
370 httpd->recvbuf[httpd->recvlen] = 0;
371 httpd->headers = NULL;
374 * If we don't find a blank line in our buffer, return that we need
377 s = strstr(httpd->recvbuf, "\r\n\r\n");
380 s = strstr(httpd->recvbuf, "\n\n");
384 return (ISC_R_NOTFOUND);
387 * Determine if this is a POST or GET method. Any other values will
388 * cause an error to be returned.
390 if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
391 httpd->method = ISC_HTTPD_METHODGET;
392 p = httpd->recvbuf + 4;
393 } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
394 httpd->method = ISC_HTTPD_METHODPOST;
395 p = httpd->recvbuf + 5;
397 return (ISC_R_RANGE);
401 * From now on, p is the start of our buffer.
408 while (LENGTHOK(s) && BUFLENOK(s) &&
409 (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
412 return (ISC_R_NOTFOUND);
414 return (ISC_R_NOMEMORY);
418 * Make the URL relative.
420 if ((strncmp(p, "http:/", 6) == 0)
421 || (strncmp(p, "https:/", 7) == 0)) {
423 while (*p != '/' && *p != 0)
426 return (ISC_R_RANGE);
429 while (*p != '/' && *p != 0)
432 return (ISC_R_RANGE);
435 while (*p != '/' && *p != 0)
448 * Now, see if there is a ? mark in the URL. If so, this is
449 * part of the query string, and we will split it from the URL.
451 httpd->querystring = strchr(httpd->url, '?');
452 if (httpd->querystring != NULL) {
453 *(httpd->querystring) = 0;
454 httpd->querystring++;
458 * Extract the HTTP/1.X protocol. We will bounce on anything but
461 while (LENGTHOK(s) && BUFLENOK(s) &&
462 (*s != '\n' && *s != '\r' && *s != '\0'))
465 return (ISC_R_NOTFOUND);
467 return (ISC_R_NOMEMORY);
469 if ((strncmp(p, "HTTP/1.0", 8) != 0)
470 && (strncmp(p, "HTTP/1.1", 8) != 0))
471 return (ISC_R_RANGE);
478 if (strstr(s, "Connection: close") != NULL)
479 httpd->flags |= HTTPD_CLOSE;
481 if (strstr(s, "Host: ") != NULL)
482 httpd->flags |= HTTPD_FOUNDHOST;
485 * Standards compliance hooks here.
487 if (strcmp(httpd->protocol, "HTTP/1.1") == 0
488 && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
489 return (ISC_R_RANGE);
493 return (ISC_R_SUCCESS);
497 isc_httpd_accept(isc_task_t *task, isc_event_t *ev) {
499 isc_httpdmgr_t *httpdmgr = ev->ev_arg;
502 isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
503 isc_sockaddr_t peeraddr;
507 LOCK(&httpdmgr->lock);
508 if (MSHUTTINGDOWN(httpdmgr)) {
509 NOTICE("accept shutting down, goto out");
513 if (nev->result == ISC_R_CANCELED) {
514 NOTICE("accept canceled, goto out");
518 if (nev->result != ISC_R_SUCCESS) {
519 /* XXXMLG log failure */
520 NOTICE("accept returned failure, goto requeue");
524 (void)isc_socket_getpeername(nev->newsocket, &peeraddr);
525 if (httpdmgr->client_ok != NULL &&
526 !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
527 isc_socket_detach(&nev->newsocket);
531 httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
533 /* XXXMLG log failure */
534 NOTICE("accept failed to allocate memory, goto requeue");
535 isc_socket_detach(&nev->newsocket);
539 httpd->mgr = httpdmgr;
540 ISC_LINK_INIT(httpd, link);
541 ISC_LIST_APPEND(httpdmgr->running, httpd, link);
542 ISC_HTTPD_SETRECV(httpd);
543 httpd->sock = nev->newsocket;
544 isc_socket_setname(httpd->sock, "httpd", NULL);
548 * Initialize the buffer for our headers.
550 httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
551 if (httpd->headerdata == NULL) {
552 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
553 isc_socket_detach(&nev->newsocket);
556 httpd->headerlen = HTTP_SENDGROW;
557 isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
560 ISC_LIST_INIT(httpd->bufflist);
562 isc_buffer_initnull(&httpd->bodybuffer);
565 r.base = (unsigned char *)httpd->recvbuf;
566 r.length = HTTP_RECVLEN - 1;
567 result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
571 NOTICE("accept queued recv on socket");
574 result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
576 if (result != ISC_R_SUCCESS) {
577 /* XXXMLG what to do? Log failure... */
578 NOTICE("accept could not reaccept due to failure");
582 UNLOCK(&httpdmgr->lock);
584 httpdmgr_destroy(httpdmgr);
592 render_404(const char *url, isc_httpdurl_t *urlinfo,
593 const char *querystring, const char *headers, void *arg,
594 unsigned int *retcode, const char **retmsg,
595 const char **mimetype, isc_buffer_t *b,
596 isc_httpdfree_t **freecb, void **freecb_args)
598 static char msg[] = "No such URL.";
607 *retmsg = "No such URL";
608 *mimetype = "text/plain";
609 isc_buffer_reinit(b, msg, strlen(msg));
610 isc_buffer_add(b, strlen(msg));
614 return (ISC_R_SUCCESS);
618 render_500(const char *url, isc_httpdurl_t *urlinfo,
619 const char *querystring, const char *headers, void *arg,
620 unsigned int *retcode, const char **retmsg,
621 const char **mimetype, isc_buffer_t *b,
622 isc_httpdfree_t **freecb, void **freecb_args)
624 static char msg[] = "Internal server failure.";
633 *retmsg = "Internal server failure";
634 *mimetype = "text/plain";
635 isc_buffer_reinit(b, msg, strlen(msg));
636 isc_buffer_add(b, strlen(msg));
640 return (ISC_R_SUCCESS);
644 isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) {
647 isc_httpd_t *httpd = ev->ev_arg;
648 isc_socketevent_t *sev = (isc_socketevent_t *)ev;
651 char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
655 INSIST(ISC_HTTPD_ISRECV(httpd));
657 if (sev->result != ISC_R_SUCCESS) {
658 NOTICE("recv destroying client");
659 destroy_client(&httpd);
663 result = process_request(httpd, sev->n);
664 if (result == ISC_R_NOTFOUND) {
665 if (httpd->recvlen >= HTTP_RECVLEN - 1) {
666 destroy_client(&httpd);
669 r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
670 r.length = HTTP_RECVLEN - httpd->recvlen - 1;
671 /* check return code? */
672 (void)isc_socket_recv(httpd->sock, &r, 1, task,
673 isc_httpd_recvdone, httpd);
675 } else if (result != ISC_R_SUCCESS) {
676 destroy_client(&httpd);
680 ISC_HTTPD_SETSEND(httpd);
683 * XXXMLG Call function here. Provide an add-header function
684 * which will append the common headers to a response we generate.
686 isc_buffer_initnull(&httpd->bodybuffer);
688 isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
689 url = ISC_LIST_HEAD(httpd->mgr->urls);
690 while (url != NULL) {
691 if (strcmp(httpd->url, url->url) == 0)
693 url = ISC_LIST_NEXT(url, link);
696 result = httpd->mgr->render_404(httpd->url, NULL,
706 result = url->action(httpd->url, url,
710 &httpd->retcode, &httpd->retmsg,
711 &httpd->mimetype, &httpd->bodybuffer,
712 &httpd->freecb, &httpd->freecb_arg);
713 if (result != ISC_R_SUCCESS) {
714 result = httpd->mgr->render_500(httpd->url, url,
723 RUNTIME_CHECK(result == ISC_R_SUCCESS);
726 isc_httpd_response(httpd);
727 isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
728 isc_httpd_addheader(httpd, "Date", datebuf);
729 isc_httpd_addheader(httpd, "Expires", datebuf);
731 if (url != NULL && url->isstatic) {
732 char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
733 isc_time_formathttptimestamp(&url->loadtime,
734 loadbuf, sizeof(loadbuf));
735 isc_httpd_addheader(httpd, "Last-Modified", loadbuf);
736 isc_httpd_addheader(httpd, "Cache-Control: public", NULL);
738 isc_httpd_addheader(httpd, "Last-Modified", datebuf);
739 isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
740 isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
743 isc_httpd_addheader(httpd, "Server: libisc", NULL);
744 isc_httpd_addheaderuint(httpd, "Content-Length",
745 isc_buffer_usedlength(&httpd->bodybuffer));
746 isc_httpd_endheaders(httpd); /* done */
748 ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
750 * Link the data buffer into our send queue, should we have any data
751 * rendered into it. If no data is present, we won't do anything
754 if (isc_buffer_length(&httpd->bodybuffer) > 0)
755 ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
757 /* check return code? */
758 (void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
759 isc_httpd_senddone, httpd);
767 isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
768 isc_httpdmgr_t *httpdmgr;
770 httpdmgr = *httpdmgrp;
773 ENTER("isc_httpdmgr_shutdown");
775 LOCK(&httpdmgr->lock);
777 MSETSHUTTINGDOWN(httpdmgr);
779 isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
781 httpd = ISC_LIST_HEAD(httpdmgr->running);
782 while (httpd != NULL) {
783 isc_socket_cancel(httpd->sock, httpdmgr->task,
785 httpd = ISC_LIST_NEXT(httpd, link);
788 UNLOCK(&httpdmgr->lock);
790 EXIT("isc_httpdmgr_shutdown");
794 grow_headerspace(isc_httpd_t *httpd) {
799 newlen = httpd->headerlen + HTTP_SENDGROW;
800 if (newlen > HTTP_SEND_MAXLEN)
801 return (ISC_R_NOSPACE);
803 newspace = isc_mem_get(httpd->mgr->mctx, newlen);
804 if (newspace == NULL)
805 return (ISC_R_NOMEMORY);
806 isc_buffer_region(&httpd->headerbuffer, &r);
807 isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
809 isc_mem_put(httpd->mgr->mctx, r.base, r.length);
811 return (ISC_R_SUCCESS);
815 isc_httpd_response(isc_httpd_t *httpd) {
817 unsigned int needlen;
819 needlen = strlen(httpd->protocol) + 1; /* protocol + space */
820 needlen += 3 + 1; /* room for response code, always 3 bytes */
821 needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
823 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
824 result = grow_headerspace(httpd);
825 if (result != ISC_R_SUCCESS)
829 sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n",
830 httpd->protocol, httpd->retcode, httpd->retmsg);
831 isc_buffer_add(&httpd->headerbuffer, needlen);
833 return (ISC_R_SUCCESS);
837 isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
841 unsigned int needlen;
843 needlen = strlen(name); /* name itself */
845 needlen += 2 + strlen(val); /* :<space> and val */
846 needlen += 2; /* CRLF */
848 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
849 result = grow_headerspace(httpd);
850 if (result != ISC_R_SUCCESS)
855 sprintf(isc_buffer_used(&httpd->headerbuffer),
856 "%s: %s\r\n", name, val);
858 sprintf(isc_buffer_used(&httpd->headerbuffer),
861 isc_buffer_add(&httpd->headerbuffer, needlen);
863 return (ISC_R_SUCCESS);
867 isc_httpd_endheaders(isc_httpd_t *httpd) {
870 while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
871 result = grow_headerspace(httpd);
872 if (result != ISC_R_SUCCESS)
876 sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
877 isc_buffer_add(&httpd->headerbuffer, 2);
879 return (ISC_R_SUCCESS);
883 isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
885 unsigned int needlen;
886 char buf[sizeof "18446744073709551616"];
888 sprintf(buf, "%d", val);
890 needlen = strlen(name); /* name itself */
891 needlen += 2 + strlen(buf); /* :<space> and val */
892 needlen += 2; /* CRLF */
894 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
895 result = grow_headerspace(httpd);
896 if (result != ISC_R_SUCCESS)
900 sprintf(isc_buffer_used(&httpd->headerbuffer),
901 "%s: %s\r\n", name, buf);
903 isc_buffer_add(&httpd->headerbuffer, needlen);
905 return (ISC_R_SUCCESS);
909 isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) {
910 isc_httpd_t *httpd = ev->ev_arg;
912 isc_socketevent_t *sev = (isc_socketevent_t *)ev;
915 INSIST(ISC_HTTPD_ISSEND(httpd));
918 * First, unlink our header buffer from the socket's bufflist. This
919 * is sort of an evil hack, since we know our buffer will be there,
920 * and we know it's address, so we can just remove it directly.
922 NOTICE("senddone unlinked header");
923 ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
926 * We will always want to clean up our receive buffer, even if we
927 * got an error on send or we are shutting down.
929 * We will pass in the buffer only if there is data in it. If
930 * there is no data, we will pass in a NULL.
932 if (httpd->freecb != NULL) {
933 isc_buffer_t *b = NULL;
934 if (isc_buffer_length(&httpd->bodybuffer) > 0)
935 b = &httpd->bodybuffer;
936 httpd->freecb(b, httpd->freecb_arg);
937 NOTICE("senddone free callback performed");
939 if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
940 ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
941 NOTICE("senddone body buffer unlinked");
944 if (sev->result != ISC_R_SUCCESS) {
945 destroy_client(&httpd);
949 if ((httpd->flags & HTTPD_CLOSE) != 0) {
950 destroy_client(&httpd);
954 ISC_HTTPD_SETRECV(httpd);
956 NOTICE("senddone restarting recv on socket");
960 r.base = (unsigned char *)httpd->recvbuf;
961 r.length = HTTP_RECVLEN - 1;
962 /* check return code? */
963 (void)isc_socket_recv(httpd->sock, &r, 1, task,
964 isc_httpd_recvdone, httpd);
972 reset_client(isc_httpd_t *httpd) {
974 * Catch errors here. We MUST be in RECV mode, and we MUST NOT have
975 * any outstanding buffers. If we have buffers, we have a leak.
977 INSIST(ISC_HTTPD_ISRECV(httpd));
978 INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
979 INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
981 httpd->recvbuf[0] = 0;
983 httpd->headers = NULL;
984 httpd->method = ISC_HTTPD_METHODUNKNOWN;
986 httpd->querystring = NULL;
987 httpd->protocol = NULL;
990 isc_buffer_clear(&httpd->headerbuffer);
991 isc_buffer_invalidate(&httpd->bodybuffer);
995 isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
996 isc_httpdaction_t *func, void *arg)
998 return (isc_httpdmgr_addurl2(httpdmgr, url, ISC_FALSE, func, arg));
1002 isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url,
1003 isc_boolean_t isstatic,
1004 isc_httpdaction_t *func, void *arg)
1006 isc_httpdurl_t *item;
1009 httpdmgr->render_404 = func;
1010 return (ISC_R_SUCCESS);
1013 item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
1015 return (ISC_R_NOMEMORY);
1017 item->url = isc_mem_strdup(httpdmgr->mctx, url);
1018 if (item->url == NULL) {
1019 isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
1020 return (ISC_R_NOMEMORY);
1023 item->action = func;
1024 item->action_arg = arg;
1025 item->isstatic = isstatic;
1026 isc_time_now(&item->loadtime);
1028 ISC_LINK_INIT(item, link);
1029 ISC_LIST_APPEND(httpdmgr->urls, item, link);
1031 return (ISC_R_SUCCESS);