1 /* $NetBSD: httpd.c,v 1.8 2015/07/08 17:28:59 christos Exp $ */
4 * Copyright (C) 2006-2008, 2010-2015 Internet Systems Consortium, Inc. ("ISC")
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
25 #include <isc/buffer.h>
26 #include <isc/httpd.h>
28 #include <isc/socket.h>
29 #include <isc/string.h>
39 * o Put in better checks to make certain things are passed in correctly.
40 * This includes a magic number for externally-visible structures,
41 * checking for NULL-ness before dereferencing, etc.
42 * o Make the URL processing external functions which will fill-in a buffer
43 * structure we provide, or return an error and we will render a generic
44 * page and close the client.
47 #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
48 #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
51 #define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (/*CONSTCOND*/0)
52 #define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (/*CONSTCOND*/0)
53 #define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (/*CONSTCOND*/0)
55 #define ENTER(x) do { } while(/*CONSTCOND*/0)
56 #define EXIT(x) do { } while(/*CONSTCOND*/0)
57 #define NOTICE(x) do { } while(/*CONSTCOND*/0)
60 #define HTTP_RECVLEN 1024
61 #define HTTP_SENDGROW 1024
62 #define HTTP_SEND_MAXLEN 10240
64 #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
65 #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
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
**httpdp
)
244 isc_httpdmgr_t
*httpd
;
246 REQUIRE(mctx
!= NULL
);
247 REQUIRE(sock
!= NULL
);
248 REQUIRE(task
!= NULL
);
249 REQUIRE(tmgr
!= NULL
);
250 REQUIRE(httpdp
!= NULL
&& *httpdp
== NULL
);
252 httpd
= isc_mem_get(mctx
, sizeof(isc_httpdmgr_t
));
254 return (ISC_R_NOMEMORY
);
256 result
= isc_mutex_init(&httpd
->lock
);
257 if (result
!= ISC_R_SUCCESS
) {
258 isc_mem_put(mctx
, httpd
, sizeof(isc_httpdmgr_t
));
262 isc_mem_attach(mctx
, &httpd
->mctx
);
264 isc_socket_attach(sock
, &httpd
->sock
);
266 isc_task_attach(task
, &httpd
->task
);
267 httpd
->timermgr
= tmgr
; /* XXXMLG no attach function? */
268 httpd
->client_ok
= client_ok
;
269 httpd
->ondestroy
= ondestroy
;
270 httpd
->cb_arg
= cb_arg
;
272 ISC_LIST_INIT(httpd
->running
);
273 ISC_LIST_INIT(httpd
->urls
);
275 /* XXXMLG ignore errors on isc_socket_listen() */
276 result
= isc_socket_listen(sock
, SOMAXCONN
);
277 if (result
!= ISC_R_SUCCESS
) {
278 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
279 "isc_socket_listen() failed: %s",
280 isc_result_totext(result
));
284 (void)isc_socket_filter(sock
, "httpready");
286 result
= isc_socket_accept(sock
, task
, isc_httpd_accept
, httpd
);
287 if (result
!= ISC_R_SUCCESS
)
290 httpd
->render_404
= render_404
;
291 httpd
->render_500
= render_500
;
294 return (ISC_R_SUCCESS
);
297 isc_task_detach(&httpd
->task
);
298 isc_socket_detach(&httpd
->sock
);
299 isc_mem_detach(&httpd
->mctx
);
300 (void)isc_mutex_destroy(&httpd
->lock
);
301 isc_mem_put(mctx
, httpd
, sizeof(isc_httpdmgr_t
));
306 httpdmgr_destroy(isc_httpdmgr_t
*httpdmgr
) {
310 ENTER("httpdmgr_destroy");
312 LOCK(&httpdmgr
->lock
);
314 if (!MSHUTTINGDOWN(httpdmgr
)) {
315 NOTICE("httpdmgr_destroy not shutting down yet");
316 UNLOCK(&httpdmgr
->lock
);
321 * If all clients are not shut down, don't do anything yet.
323 if (!ISC_LIST_EMPTY(httpdmgr
->running
)) {
324 NOTICE("httpdmgr_destroy clients still active");
325 UNLOCK(&httpdmgr
->lock
);
329 NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
331 isc_socket_detach(&httpdmgr
->sock
);
332 isc_task_detach(&httpdmgr
->task
);
333 httpdmgr
->timermgr
= NULL
;
336 * Clear out the list of all actions we know about. Just free the
339 url
= ISC_LIST_HEAD(httpdmgr
->urls
);
340 while (url
!= NULL
) {
341 isc_mem_free(httpdmgr
->mctx
, url
->url
);
342 ISC_LIST_UNLINK(httpdmgr
->urls
, url
, link
);
343 isc_mem_put(httpdmgr
->mctx
, url
, sizeof(isc_httpdurl_t
));
344 url
= ISC_LIST_HEAD(httpdmgr
->urls
);
347 UNLOCK(&httpdmgr
->lock
);
348 (void)isc_mutex_destroy(&httpdmgr
->lock
);
350 if (httpdmgr
->ondestroy
!= NULL
)
351 (httpdmgr
->ondestroy
)(httpdmgr
->cb_arg
);
353 mctx
= httpdmgr
->mctx
;
354 isc_mem_putanddetach(&mctx
, httpdmgr
, sizeof(isc_httpdmgr_t
));
356 EXIT("httpdmgr_destroy");
359 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
360 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
363 process_request(isc_httpd_t
*httpd
, int length
) {
370 httpd
->recvlen
+= length
;
372 httpd
->recvbuf
[httpd
->recvlen
] = 0;
373 httpd
->headers
= NULL
;
376 * If we don't find a blank line in our buffer, return that we need
379 s
= strstr(httpd
->recvbuf
, "\r\n\r\n");
382 s
= strstr(httpd
->recvbuf
, "\n\n");
386 return (ISC_R_NOTFOUND
);
389 * Determine if this is a POST or GET method. Any other values will
390 * cause an error to be returned.
392 if (strncmp(httpd
->recvbuf
, "GET ", 4) == 0) {
393 httpd
->method
= ISC_HTTPD_METHODGET
;
394 p
= httpd
->recvbuf
+ 4;
395 } else if (strncmp(httpd
->recvbuf
, "POST ", 5) == 0) {
396 httpd
->method
= ISC_HTTPD_METHODPOST
;
397 p
= httpd
->recvbuf
+ 5;
399 return (ISC_R_RANGE
);
403 * From now on, p is the start of our buffer.
410 while (LENGTHOK(s
) && BUFLENOK(s
) &&
411 (*s
!= '\n' && *s
!= '\r' && *s
!= '\0' && *s
!= ' '))
414 return (ISC_R_NOTFOUND
);
416 return (ISC_R_NOMEMORY
);
420 * Make the URL relative.
422 if ((strncmp(p
, "http:/", 6) == 0)
423 || (strncmp(p
, "https:/", 7) == 0)) {
425 while (*p
!= '/' && *p
!= 0)
428 return (ISC_R_RANGE
);
431 while (*p
!= '/' && *p
!= 0)
434 return (ISC_R_RANGE
);
437 while (*p
!= '/' && *p
!= 0)
450 * Now, see if there is a ? mark in the URL. If so, this is
451 * part of the query string, and we will split it from the URL.
453 httpd
->querystring
= strchr(httpd
->url
, '?');
454 if (httpd
->querystring
!= NULL
) {
455 *(httpd
->querystring
) = 0;
456 httpd
->querystring
++;
460 * Extract the HTTP/1.X protocol. We will bounce on anything but
463 while (LENGTHOK(s
) && BUFLENOK(s
) &&
464 (*s
!= '\n' && *s
!= '\r' && *s
!= '\0'))
467 return (ISC_R_NOTFOUND
);
469 return (ISC_R_NOMEMORY
);
471 if ((strncmp(p
, "HTTP/1.0", 8) != 0)
472 && (strncmp(p
, "HTTP/1.1", 8) != 0))
473 return (ISC_R_RANGE
);
480 if (strstr(s
, "Connection: close") != NULL
)
481 httpd
->flags
|= HTTPD_CLOSE
;
483 if (strstr(s
, "Host: ") != NULL
)
484 httpd
->flags
|= HTTPD_FOUNDHOST
;
487 * Standards compliance hooks here.
489 if (strcmp(httpd
->protocol
, "HTTP/1.1") == 0
490 && ((httpd
->flags
& HTTPD_FOUNDHOST
) == 0))
491 return (ISC_R_RANGE
);
495 return (ISC_R_SUCCESS
);
499 isc_httpd_accept(isc_task_t
*task
, isc_event_t
*ev
) {
501 isc_httpdmgr_t
*httpdmgr
= ev
->ev_arg
;
504 isc_socket_newconnev_t
*nev
= (isc_socket_newconnev_t
*)ev
;
505 isc_sockaddr_t peeraddr
;
509 LOCK(&httpdmgr
->lock
);
510 if (MSHUTTINGDOWN(httpdmgr
)) {
511 NOTICE("accept shutting down, goto out");
515 if (nev
->result
== ISC_R_CANCELED
) {
516 NOTICE("accept canceled, goto out");
520 if (nev
->result
!= ISC_R_SUCCESS
) {
521 /* XXXMLG log failure */
522 NOTICE("accept returned failure, goto requeue");
526 (void)isc_socket_getpeername(nev
->newsocket
, &peeraddr
);
527 if (httpdmgr
->client_ok
!= NULL
&&
528 !(httpdmgr
->client_ok
)(&peeraddr
, httpdmgr
->cb_arg
)) {
529 isc_socket_detach(&nev
->newsocket
);
533 httpd
= isc_mem_get(httpdmgr
->mctx
, sizeof(isc_httpd_t
));
535 /* XXXMLG log failure */
536 NOTICE("accept failed to allocate memory, goto requeue");
537 isc_socket_detach(&nev
->newsocket
);
541 httpd
->mgr
= httpdmgr
;
542 ISC_LINK_INIT(httpd
, link
);
543 ISC_LIST_APPEND(httpdmgr
->running
, httpd
, link
);
544 ISC_HTTPD_SETRECV(httpd
);
545 httpd
->sock
= nev
->newsocket
;
546 isc_socket_setname(httpd
->sock
, "httpd", NULL
);
550 * Initialize the buffer for our headers.
552 httpd
->headerdata
= isc_mem_get(httpdmgr
->mctx
, HTTP_SENDGROW
);
553 if (httpd
->headerdata
== NULL
) {
554 isc_mem_put(httpdmgr
->mctx
, httpd
, sizeof(isc_httpd_t
));
555 isc_socket_detach(&nev
->newsocket
);
558 httpd
->headerlen
= HTTP_SENDGROW
;
559 isc_buffer_init(&httpd
->headerbuffer
, httpd
->headerdata
,
562 ISC_LIST_INIT(httpd
->bufflist
);
564 isc_buffer_initnull(&httpd
->bodybuffer
);
567 r
.base
= (unsigned char *)httpd
->recvbuf
;
568 r
.length
= HTTP_RECVLEN
- 1;
569 result
= isc_socket_recv(httpd
->sock
, &r
, 1, task
, isc_httpd_recvdone
,
573 NOTICE("accept queued recv on socket");
576 result
= isc_socket_accept(httpdmgr
->sock
, task
, isc_httpd_accept
,
578 if (result
!= ISC_R_SUCCESS
) {
579 /* XXXMLG what to do? Log failure... */
580 NOTICE("accept could not reaccept due to failure");
584 UNLOCK(&httpdmgr
->lock
);
586 httpdmgr_destroy(httpdmgr
);
594 render_404(const char *url
, isc_httpdurl_t
*urlinfo
,
595 const char *querystring
, const char *headers
, void *arg
,
596 unsigned int *retcode
, const char **retmsg
,
597 const char **mimetype
, isc_buffer_t
*b
,
598 isc_httpdfree_t
**freecb
, void **freecb_args
)
600 static char msg
[] = "No such URL.";
609 *retmsg
= "No such URL";
610 *mimetype
= "text/plain";
611 isc_buffer_reinit(b
, msg
, strlen(msg
));
612 isc_buffer_add(b
, strlen(msg
));
616 return (ISC_R_SUCCESS
);
620 render_500(const char *url
, isc_httpdurl_t
*urlinfo
,
621 const char *querystring
, const char *headers
, void *arg
,
622 unsigned int *retcode
, const char **retmsg
,
623 const char **mimetype
, isc_buffer_t
*b
,
624 isc_httpdfree_t
**freecb
, void **freecb_args
)
626 static char msg
[] = "Internal server failure.";
635 *retmsg
= "Internal server failure";
636 *mimetype
= "text/plain";
637 isc_buffer_reinit(b
, msg
, strlen(msg
));
638 isc_buffer_add(b
, strlen(msg
));
642 return (ISC_R_SUCCESS
);
646 isc_httpd_recvdone(isc_task_t
*task
, isc_event_t
*ev
) {
649 isc_httpd_t
*httpd
= ev
->ev_arg
;
650 isc_socketevent_t
*sev
= (isc_socketevent_t
*)ev
;
653 char datebuf
[ISC_FORMATHTTPTIMESTAMP_SIZE
];
657 INSIST(ISC_HTTPD_ISRECV(httpd
));
659 if (sev
->result
!= ISC_R_SUCCESS
) {
660 NOTICE("recv destroying client");
661 destroy_client(&httpd
);
665 result
= process_request(httpd
, sev
->n
);
666 if (result
== ISC_R_NOTFOUND
) {
667 if (httpd
->recvlen
>= HTTP_RECVLEN
- 1) {
668 destroy_client(&httpd
);
671 r
.base
= (unsigned char *)httpd
->recvbuf
+ httpd
->recvlen
;
672 r
.length
= HTTP_RECVLEN
- httpd
->recvlen
- 1;
673 /* check return code? */
674 (void)isc_socket_recv(httpd
->sock
, &r
, 1, task
,
675 isc_httpd_recvdone
, httpd
);
677 } else if (result
!= ISC_R_SUCCESS
) {
678 destroy_client(&httpd
);
682 ISC_HTTPD_SETSEND(httpd
);
685 * XXXMLG Call function here. Provide an add-header function
686 * which will append the common headers to a response we generate.
688 isc_buffer_initnull(&httpd
->bodybuffer
);
690 isc_time_formathttptimestamp(&now
, datebuf
, sizeof(datebuf
));
691 url
= ISC_LIST_HEAD(httpd
->mgr
->urls
);
692 while (url
!= NULL
) {
693 if (strcmp(httpd
->url
, url
->url
) == 0)
695 url
= ISC_LIST_NEXT(url
, link
);
698 result
= httpd
->mgr
->render_404(httpd
->url
, NULL
,
708 result
= url
->action(httpd
->url
, url
,
712 &httpd
->retcode
, &httpd
->retmsg
,
713 &httpd
->mimetype
, &httpd
->bodybuffer
,
714 &httpd
->freecb
, &httpd
->freecb_arg
);
715 if (result
!= ISC_R_SUCCESS
) {
716 result
= httpd
->mgr
->render_500(httpd
->url
, url
,
725 RUNTIME_CHECK(result
== ISC_R_SUCCESS
);
728 isc_httpd_response(httpd
);
729 isc_httpd_addheader(httpd
, "Content-Type", httpd
->mimetype
);
730 isc_httpd_addheader(httpd
, "Date", datebuf
);
731 isc_httpd_addheader(httpd
, "Expires", datebuf
);
733 if (url
!= NULL
&& url
->isstatic
) {
734 char loadbuf
[ISC_FORMATHTTPTIMESTAMP_SIZE
];
735 isc_time_formathttptimestamp(&url
->loadtime
,
736 loadbuf
, sizeof(loadbuf
));
737 isc_httpd_addheader(httpd
, "Last-Modified", loadbuf
);
738 isc_httpd_addheader(httpd
, "Cache-Control: public", NULL
);
740 isc_httpd_addheader(httpd
, "Last-Modified", datebuf
);
741 isc_httpd_addheader(httpd
, "Pragma: no-cache", NULL
);
742 isc_httpd_addheader(httpd
, "Cache-Control: no-cache", NULL
);
745 isc_httpd_addheader(httpd
, "Server: libisc", NULL
);
746 isc_httpd_addheaderuint(httpd
, "Content-Length",
747 isc_buffer_usedlength(&httpd
->bodybuffer
));
748 isc_httpd_endheaders(httpd
); /* done */
750 ISC_LIST_APPEND(httpd
->bufflist
, &httpd
->headerbuffer
, link
);
752 * Link the data buffer into our send queue, should we have any data
753 * rendered into it. If no data is present, we won't do anything
756 if (isc_buffer_length(&httpd
->bodybuffer
) > 0)
757 ISC_LIST_APPEND(httpd
->bufflist
, &httpd
->bodybuffer
, link
);
759 /* check return code? */
760 (void)isc_socket_sendv(httpd
->sock
, &httpd
->bufflist
, task
,
761 isc_httpd_senddone
, httpd
);
769 isc_httpdmgr_shutdown(isc_httpdmgr_t
**httpdmgrp
) {
770 isc_httpdmgr_t
*httpdmgr
;
772 httpdmgr
= *httpdmgrp
;
775 ENTER("isc_httpdmgr_shutdown");
777 LOCK(&httpdmgr
->lock
);
779 MSETSHUTTINGDOWN(httpdmgr
);
781 isc_socket_cancel(httpdmgr
->sock
, httpdmgr
->task
, ISC_SOCKCANCEL_ALL
);
783 httpd
= ISC_LIST_HEAD(httpdmgr
->running
);
784 while (httpd
!= NULL
) {
785 isc_socket_cancel(httpd
->sock
, httpdmgr
->task
,
787 httpd
= ISC_LIST_NEXT(httpd
, link
);
790 UNLOCK(&httpdmgr
->lock
);
792 EXIT("isc_httpdmgr_shutdown");
796 grow_headerspace(isc_httpd_t
*httpd
) {
801 newlen
= httpd
->headerlen
+ HTTP_SENDGROW
;
802 if (newlen
> HTTP_SEND_MAXLEN
)
803 return (ISC_R_NOSPACE
);
805 newspace
= isc_mem_get(httpd
->mgr
->mctx
, newlen
);
806 if (newspace
== NULL
)
807 return (ISC_R_NOMEMORY
);
808 isc_buffer_region(&httpd
->headerbuffer
, &r
);
809 isc_buffer_reinit(&httpd
->headerbuffer
, newspace
, newlen
);
811 isc_mem_put(httpd
->mgr
->mctx
, r
.base
, r
.length
);
813 return (ISC_R_SUCCESS
);
817 isc_httpd_response(isc_httpd_t
*httpd
) {
819 unsigned int needlen
;
821 needlen
= strlen(httpd
->protocol
) + 1; /* protocol + space */
822 needlen
+= 3 + 1; /* room for response code, always 3 bytes */
823 needlen
+= strlen(httpd
->retmsg
) + 2; /* return msg + CRLF */
825 while (isc_buffer_availablelength(&httpd
->headerbuffer
) < needlen
) {
826 result
= grow_headerspace(httpd
);
827 if (result
!= ISC_R_SUCCESS
)
831 sprintf(isc_buffer_used(&httpd
->headerbuffer
), "%s %03d %s\r\n",
832 httpd
->protocol
, httpd
->retcode
, httpd
->retmsg
);
833 isc_buffer_add(&httpd
->headerbuffer
, needlen
);
835 return (ISC_R_SUCCESS
);
839 isc_httpd_addheader(isc_httpd_t
*httpd
, const char *name
,
843 unsigned int needlen
;
845 needlen
= strlen(name
); /* name itself */
847 needlen
+= 2 + strlen(val
); /* :<space> and val */
848 needlen
+= 2; /* CRLF */
850 while (isc_buffer_availablelength(&httpd
->headerbuffer
) < needlen
) {
851 result
= grow_headerspace(httpd
);
852 if (result
!= ISC_R_SUCCESS
)
857 sprintf(isc_buffer_used(&httpd
->headerbuffer
),
858 "%s: %s\r\n", name
, val
);
860 sprintf(isc_buffer_used(&httpd
->headerbuffer
),
863 isc_buffer_add(&httpd
->headerbuffer
, needlen
);
865 return (ISC_R_SUCCESS
);
869 isc_httpd_endheaders(isc_httpd_t
*httpd
) {
872 while (isc_buffer_availablelength(&httpd
->headerbuffer
) < 2) {
873 result
= grow_headerspace(httpd
);
874 if (result
!= ISC_R_SUCCESS
)
878 sprintf(isc_buffer_used(&httpd
->headerbuffer
), "\r\n");
879 isc_buffer_add(&httpd
->headerbuffer
, 2);
881 return (ISC_R_SUCCESS
);
885 isc_httpd_addheaderuint(isc_httpd_t
*httpd
, const char *name
, int val
) {
887 unsigned int needlen
;
888 char buf
[sizeof "18446744073709551616"];
890 sprintf(buf
, "%d", val
);
892 needlen
= strlen(name
); /* name itself */
893 needlen
+= 2 + strlen(buf
); /* :<space> and val */
894 needlen
+= 2; /* CRLF */
896 while (isc_buffer_availablelength(&httpd
->headerbuffer
) < needlen
) {
897 result
= grow_headerspace(httpd
);
898 if (result
!= ISC_R_SUCCESS
)
902 sprintf(isc_buffer_used(&httpd
->headerbuffer
),
903 "%s: %s\r\n", name
, buf
);
905 isc_buffer_add(&httpd
->headerbuffer
, needlen
);
907 return (ISC_R_SUCCESS
);
911 isc_httpd_senddone(isc_task_t
*task
, isc_event_t
*ev
) {
912 isc_httpd_t
*httpd
= ev
->ev_arg
;
914 isc_socketevent_t
*sev
= (isc_socketevent_t
*)ev
;
917 INSIST(ISC_HTTPD_ISSEND(httpd
));
920 * First, unlink our header buffer from the socket's bufflist. This
921 * is sort of an evil hack, since we know our buffer will be there,
922 * and we know it's address, so we can just remove it directly.
924 NOTICE("senddone unlinked header");
925 ISC_LIST_UNLINK(sev
->bufferlist
, &httpd
->headerbuffer
, link
);
928 * We will always want to clean up our receive buffer, even if we
929 * got an error on send or we are shutting down.
931 * We will pass in the buffer only if there is data in it. If
932 * there is no data, we will pass in a NULL.
934 if (httpd
->freecb
!= NULL
) {
935 isc_buffer_t
*b
= NULL
;
936 if (isc_buffer_length(&httpd
->bodybuffer
) > 0) {
937 b
= &httpd
->bodybuffer
;
938 httpd
->freecb(b
, httpd
->freecb_arg
);
940 NOTICE("senddone free callback performed");
942 if (ISC_LINK_LINKED(&httpd
->bodybuffer
, link
)) {
943 ISC_LIST_UNLINK(sev
->bufferlist
, &httpd
->bodybuffer
, link
);
944 NOTICE("senddone body buffer unlinked");
947 if (sev
->result
!= ISC_R_SUCCESS
) {
948 destroy_client(&httpd
);
952 if ((httpd
->flags
& HTTPD_CLOSE
) != 0) {
953 destroy_client(&httpd
);
957 ISC_HTTPD_SETRECV(httpd
);
959 NOTICE("senddone restarting recv on socket");
963 r
.base
= (unsigned char *)httpd
->recvbuf
;
964 r
.length
= HTTP_RECVLEN
- 1;
965 /* check return code? */
966 (void)isc_socket_recv(httpd
->sock
, &r
, 1, task
,
967 isc_httpd_recvdone
, httpd
);
975 reset_client(isc_httpd_t
*httpd
) {
977 * Catch errors here. We MUST be in RECV mode, and we MUST NOT have
978 * any outstanding buffers. If we have buffers, we have a leak.
980 INSIST(ISC_HTTPD_ISRECV(httpd
));
981 INSIST(!ISC_LINK_LINKED(&httpd
->headerbuffer
, link
));
982 INSIST(!ISC_LINK_LINKED(&httpd
->bodybuffer
, link
));
984 httpd
->recvbuf
[0] = 0;
986 httpd
->headers
= NULL
;
987 httpd
->method
= ISC_HTTPD_METHODUNKNOWN
;
989 httpd
->querystring
= NULL
;
990 httpd
->protocol
= NULL
;
993 isc_buffer_clear(&httpd
->headerbuffer
);
994 isc_buffer_invalidate(&httpd
->bodybuffer
);
998 isc_httpdmgr_addurl(isc_httpdmgr_t
*httpdmgr
, const char *url
,
999 isc_httpdaction_t
*func
, void *arg
)
1001 return (isc_httpdmgr_addurl2(httpdmgr
, url
, ISC_FALSE
, func
, arg
));
1005 isc_httpdmgr_addurl2(isc_httpdmgr_t
*httpdmgr
, const char *url
,
1006 isc_boolean_t isstatic
,
1007 isc_httpdaction_t
*func
, void *arg
)
1009 isc_httpdurl_t
*item
;
1012 httpdmgr
->render_404
= func
;
1013 return (ISC_R_SUCCESS
);
1016 item
= isc_mem_get(httpdmgr
->mctx
, sizeof(isc_httpdurl_t
));
1018 return (ISC_R_NOMEMORY
);
1020 item
->url
= isc_mem_strdup(httpdmgr
->mctx
, url
);
1021 if (item
->url
== NULL
) {
1022 isc_mem_put(httpdmgr
->mctx
, item
, sizeof(isc_httpdurl_t
));
1023 return (ISC_R_NOMEMORY
);
1026 item
->action
= func
;
1027 item
->action_arg
= arg
;
1028 item
->isstatic
= isstatic
;
1029 isc_time_now(&item
->loadtime
);
1031 ISC_LINK_INIT(item
, link
);
1032 ISC_LIST_APPEND(httpdmgr
->urls
, item
, link
);
1034 return (ISC_R_SUCCESS
);