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 $ */
23 #include <isc/buffer.h>
24 #include <isc/httpd.h>
26 #include <isc/socket.h>
27 #include <isc/string.h>
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)
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)
52 #define ENTER(x) do { } while(0)
53 #define EXIT(x) do { } while(0)
54 #define NOTICE(x) do { } while(0)
57 #define HTTP_RECVLEN 1024
58 #define HTTP_SENDGROW 1024
59 #define HTTP_SEND_MAXLEN 10240
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.
70 isc_httpdaction_t
*action
;
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 */
80 isc_httpdmgr_t
*mgr
; /*%< our parent */
81 ISC_LINK(isc_httpd_t
) link
;
86 * Received data state.
88 char recvbuf
[HTTP_RECVLEN
]; /*%< receive buffer */
89 isc_uint32_t recvlen
; /*%< length recv'd */
96 * Flags on the httpd client.
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
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
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
;
131 isc_buffer_t bodybuffer
;
132 isc_httpdfree_t
*freecb
;
136 /*% lightweight socket manager for httpd output */
137 struct isc_httpdmgr
{
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 */
148 ISC_LIST(isc_httpd_t
) running
; /*%< running clients */
152 ISC_LIST(isc_httpdurl_t
) urls
; /*%< urls we manage */
153 isc_httpdaction_t
*render_404
;
159 #define ISC_HTTPD_METHODUNKNOWN 0
160 #define ISC_HTTPD_METHODGET 1
161 #define ISC_HTTPD_METHODPOST 2
166 * _IDLE The client is not doing anything at all. This state should
167 * only occur just after creation, and just before being
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 *,
221 unsigned int *, const char **,
222 const char **, isc_buffer_t
*,
223 isc_httpdfree_t
**, void **);
226 destroy_client(isc_httpd_t
**httpdp
)
228 isc_httpd_t
*httpd
= *httpdp
;
229 isc_httpdmgr_t
*httpdmgr
= httpd
->mgr
;
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
,
242 isc_mem_put(httpdmgr
->mctx
, httpd
, sizeof(isc_httpd_t
));
244 UNLOCK(&httpdmgr
->lock
);
246 httpdmgr_destroy(httpdmgr
);
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
)
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
));
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
));
274 isc_mem_attach(mctx
, &httpd
->mctx
);
276 isc_socket_attach(sock
, &httpd
->sock
);
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
));
296 (void)isc_socket_filter(sock
, "httpready");
298 result
= isc_socket_accept(sock
, task
, isc_httpd_accept
, httpd
);
299 if (result
!= ISC_R_SUCCESS
)
302 httpd
->render_404
= render_404
;
305 return (ISC_R_SUCCESS
);
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
));
317 httpdmgr_destroy(isc_httpdmgr_t
*httpdmgr
)
322 ENTER("httpdmgr_destroy");
324 LOCK(&httpdmgr
->lock
);
326 if (!MSHUTTINGDOWN(httpdmgr
)) {
327 NOTICE("httpdmgr_destroy not shutting down yet");
328 UNLOCK(&httpdmgr
->lock
);
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
);
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
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)
375 process_request(isc_httpd_t
*httpd
, int length
)
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
391 s
= strstr(httpd
->recvbuf
, "\r\n\r\n");
394 s
= strstr(httpd
->recvbuf
, "\n\n");
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;
411 return (ISC_R_RANGE
);
415 * From now on, p is the start of our buffer.
422 while (LENGTHOK(s
) && BUFLENOK(s
) &&
423 (*s
!= '\n' && *s
!= '\r' && *s
!= '\0' && *s
!= ' '))
426 return (ISC_R_NOTFOUND
);
428 return (ISC_R_NOMEMORY
);
432 * Make the URL relative.
434 if ((strncmp(p
, "http:/", 6) == 0)
435 || (strncmp(p
, "https:/", 7) == 0)) {
437 while (*p
!= '/' && *p
!= 0)
440 return (ISC_R_RANGE
);
443 while (*p
!= '/' && *p
!= 0)
446 return (ISC_R_RANGE
);
449 while (*p
!= '/' && *p
!= 0)
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
475 while (LENGTHOK(s
) && BUFLENOK(s
) &&
476 (*s
!= '\n' && *s
!= '\r' && *s
!= '\0'))
479 return (ISC_R_NOTFOUND
);
481 return (ISC_R_NOMEMORY
);
483 if ((strncmp(p
, "HTTP/1.0", 8) != 0)
484 && (strncmp(p
, "HTTP/1.1", 8) != 0))
485 return (ISC_R_RANGE
);
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
);
505 return (ISC_R_SUCCESS
);
509 isc_httpd_accept(isc_task_t
*task
, isc_event_t
*ev
)
512 isc_httpdmgr_t
*httpdmgr
= ev
->ev_arg
;
515 isc_socket_newconnev_t
*nev
= (isc_socket_newconnev_t
*)ev
;
516 isc_sockaddr_t peeraddr
;
520 LOCK(&httpdmgr
->lock
);
521 if (MSHUTTINGDOWN(httpdmgr
)) {
522 NOTICE("accept shutting down, goto out");
526 if (nev
->result
== ISC_R_CANCELED
) {
527 NOTICE("accept canceled, goto out");
531 if (nev
->result
!= ISC_R_SUCCESS
) {
532 /* XXXMLG log failure */
533 NOTICE("accept returned failure, 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
);
544 httpd
= isc_mem_get(httpdmgr
->mctx
, sizeof(isc_httpd_t
));
546 /* XXXMLG log failure */
547 NOTICE("accept failed to allocate memory, goto requeue");
548 isc_socket_detach(&nev
->newsocket
);
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
);
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
);
569 httpd
->headerlen
= HTTP_SENDGROW
;
570 isc_buffer_init(&httpd
->headerbuffer
, httpd
->headerdata
,
573 ISC_LIST_INIT(httpd
->bufflist
);
575 isc_buffer_initnull(&httpd
->bodybuffer
);
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
,
582 NOTICE("accept queued recv on socket");
585 result
= isc_socket_accept(httpdmgr
->sock
, task
, isc_httpd_accept
,
587 if (result
!= ISC_R_SUCCESS
) {
588 /* XXXMLG what to do? Log failure... */
589 NOTICE("accept could not reaccept due to failure");
593 UNLOCK(&httpdmgr
->lock
);
595 httpdmgr_destroy(httpdmgr
);
603 render_404(const char *url
, const char *querystring
,
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.";
616 *retmsg
= "No such URL";
617 *mimetype
= "text/plain";
618 isc_buffer_reinit(b
, msg
, strlen(msg
));
619 isc_buffer_add(b
, strlen(msg
));
623 return (ISC_R_SUCCESS
);
627 isc_httpd_recvdone(isc_task_t
*task
, isc_event_t
*ev
)
631 isc_httpd_t
*httpd
= ev
->ev_arg
;
632 isc_socketevent_t
*sev
= (isc_socketevent_t
*)ev
;
635 char datebuf
[32]; /* Only need 30, but safety first */
639 INSIST(ISC_HTTPD_ISRECV(httpd
));
641 if (sev
->result
!= ISC_R_SUCCESS
) {
642 NOTICE("recv destroying client");
643 destroy_client(&httpd
);
647 result
= process_request(httpd
, sev
->n
);
648 if (result
== ISC_R_NOTFOUND
) {
649 if (httpd
->recvlen
>= HTTP_RECVLEN
- 1) {
650 destroy_client(&httpd
);
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
);
658 } else if (result
!= ISC_R_SUCCESS
) {
659 destroy_client(&httpd
);
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
);
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)
676 url
= ISC_LIST_NEXT(url
, link
);
679 result
= httpd
->mgr
->render_404(httpd
->url
, httpd
->querystring
,
688 result
= url
->action(httpd
->url
, httpd
->querystring
,
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
);
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
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
);
728 isc_httpdmgr_shutdown(isc_httpdmgr_t
**httpdmgrp
)
730 isc_httpdmgr_t
*httpdmgr
;
732 httpdmgr
= *httpdmgrp
;
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
,
747 httpd
= ISC_LIST_NEXT(httpd
, link
);
750 UNLOCK(&httpdmgr
->lock
);
752 EXIT("isc_httpdmgr_shutdown");
756 grow_headerspace(isc_httpd_t
*httpd
)
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
);
778 isc_httpd_response(isc_httpd_t
*httpd
)
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
)
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
);
801 isc_httpd_addheader(isc_httpd_t
*httpd
, const char *name
,
805 unsigned int needlen
;
807 needlen
= strlen(name
); /* name itself */
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
)
819 sprintf(isc_buffer_used(&httpd
->headerbuffer
),
820 "%s: %s\r\n", name
, val
);
822 sprintf(isc_buffer_used(&httpd
->headerbuffer
),
825 isc_buffer_add(&httpd
->headerbuffer
, needlen
);
827 return (ISC_R_SUCCESS
);
831 isc_httpd_endheaders(isc_httpd_t
*httpd
)
835 if (isc_buffer_availablelength(&httpd
->headerbuffer
) < 2) {
836 result
= grow_headerspace(httpd
);
837 if (result
!= ISC_R_SUCCESS
)
841 sprintf(isc_buffer_used(&httpd
->headerbuffer
), "\r\n");
842 isc_buffer_add(&httpd
->headerbuffer
, 2);
844 return (ISC_R_SUCCESS
);
848 isc_httpd_addheaderuint(isc_httpd_t
*httpd
, const char *name
, int val
) {
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
)
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
);
874 isc_httpd_senddone(isc_task_t
*task
, isc_event_t
*ev
)
876 isc_httpd_t
*httpd
= ev
->ev_arg
;
879 isc_socketevent_t
*sev
= (isc_socketevent_t
*)ev
;
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
);
916 if ((httpd
->flags
& HTTPD_CLOSE
) != 0) {
917 destroy_client(&httpd
);
921 ISC_HTTPD_SETRECV(httpd
);
923 NOTICE("senddone restarting recv on socket");
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
,
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;
950 httpd
->method
= ISC_HTTPD_METHODUNKNOWN
;
952 httpd
->querystring
= NULL
;
953 httpd
->protocol
= NULL
;
956 isc_buffer_clear(&httpd
->headerbuffer
);
957 isc_buffer_invalidate(&httpd
->bodybuffer
);
961 isc_httpdmgr_addurl(isc_httpdmgr_t
*httpdmgr
, const char *url
,
962 isc_httpdaction_t
*func
, void *arg
)
964 isc_httpdurl_t
*item
;
967 httpdmgr
->render_404
= func
;
968 return (ISC_R_SUCCESS
);
971 item
= isc_mem_get(httpdmgr
->mctx
, sizeof(isc_httpdurl_t
));
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
);
982 item
->action_arg
= arg
;
983 ISC_LINK_INIT(item
, link
);
984 ISC_LIST_APPEND(httpdmgr
->urls
, item
, link
);
986 return (ISC_R_SUCCESS
);