4 * Copyright (C) 2006-2008 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.
19 /* Id: httpd.c,v 1.16 2008/08/08 05:06:49 marka Exp */
25 #include <isc/buffer.h>
26 #include <isc/httpd.h>
28 #include <isc/socket.h>
29 #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
64 * HTTP urls. These are the URLs we manage, and the function to call to
65 * provide the data for it. We pass in the base url (so the same function
66 * can handle multiple requests), and a structure to fill in to return a
67 * result to the client. We also pass in a pointer to be filled in for
68 * the data cleanup function.
72 isc_httpdaction_t
*action
;
74 ISC_LINK(isc_httpdurl_t
) link
;
77 #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
78 #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
82 isc_httpdmgr_t
*mgr
; /*%< our parent */
83 ISC_LINK(isc_httpd_t
) link
;
88 * Received data state.
90 char recvbuf
[HTTP_RECVLEN
]; /*%< receive buffer */
91 isc_uint32_t recvlen
; /*%< length recv'd */
98 * Flags on the httpd client.
103 * Transmit data state.
105 * This is the data buffer we will transmit.
107 * This free function pointer is filled in by the rendering function
108 * we call. The free function is called after the data is transmitted
111 * The bufflist is the list of buffers we are currently transmitting.
112 * The headerdata is where we render our headers to. If we run out of
113 * space when rendering a header, we will change the size of our
114 * buffer. We will not free it until we are finished, and will
115 * allocate an additional HTTP_SENDGROW bytes per header space grow.
117 * We currently use two buffers total, one for the headers (which
118 * we manage) and another for the client to fill in (which it manages,
119 * it provides the space for it, etc) -- we will pass that buffer
120 * structure back to the caller, who is responsible for managing the
121 * space it may have allocated as backing store for it. This second
122 * buffer is bodybuffer, and we only allocate the buffer itself, not
125 isc_bufferlist_t bufflist
;
126 char *headerdata
; /*%< send header buf */
127 unsigned int headerlen
; /*%< current header buffer size */
128 isc_buffer_t headerbuffer
;
130 const char *mimetype
;
131 unsigned int retcode
;
133 isc_buffer_t bodybuffer
;
134 isc_httpdfree_t
*freecb
;
138 /*% lightweight socket manager for httpd output */
139 struct isc_httpdmgr
{
141 isc_socket_t
*sock
; /*%< listening socket */
142 isc_task_t
*task
; /*%< owning task */
143 isc_timermgr_t
*timermgr
;
145 isc_httpdclientok_t
*client_ok
; /*%< client validator */
146 isc_httpdondestroy_t
*ondestroy
; /*%< cleanup callback */
147 void *cb_arg
; /*%< argument for the above */
150 ISC_LIST(isc_httpd_t
) running
; /*%< running clients */
154 ISC_LIST(isc_httpdurl_t
) urls
; /*%< urls we manage */
155 isc_httpdaction_t
*render_404
;
161 #define ISC_HTTPD_METHODUNKNOWN 0
162 #define ISC_HTTPD_METHODGET 1
163 #define ISC_HTTPD_METHODPOST 2
168 * _IDLE The client is not doing anything at all. This state should
169 * only occur just after creation, and just before being
172 * _RECV The client is waiting for data after issuing a socket recv().
174 * _RECVDONE Data has been received, and is being processed.
176 * _SEND All data for a response has completed, and a reply was
177 * sent via a socket send() call.
179 * _SENDDONE Send is completed.
181 * Badly formatted state table:
183 * IDLE -> RECV when client has a recv() queued.
185 * RECV -> RECVDONE when recvdone event received.
187 * RECVDONE -> SEND if the data for a reply is at hand.
189 * SEND -> RECV when a senddone event was received.
191 * At any time -> RECV on error. If RECV fails, the client will
192 * self-destroy, closing the socket and freeing memory.
194 #define ISC_HTTPD_STATEIDLE 0
195 #define ISC_HTTPD_STATERECV 1
196 #define ISC_HTTPD_STATERECVDONE 2
197 #define ISC_HTTPD_STATESEND 3
198 #define ISC_HTTPD_STATESENDDONE 4
200 #define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV)
201 #define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
202 #define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND)
203 #define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
206 * Overall magic test that means we're not idle.
208 #define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV)
209 #define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE)
210 #define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND)
211 #define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE)
213 static void isc_httpd_accept(isc_task_t
*, isc_event_t
*);
214 static void isc_httpd_recvdone(isc_task_t
*, isc_event_t
*);
215 static void isc_httpd_senddone(isc_task_t
*, isc_event_t
*);
216 static void destroy_client(isc_httpd_t
**);
217 static isc_result_t
process_request(isc_httpd_t
*, int);
218 static void httpdmgr_destroy(isc_httpdmgr_t
*);
219 static isc_result_t
grow_headerspace(isc_httpd_t
*);
220 static void reset_client(isc_httpd_t
*httpd
);
221 static isc_result_t
render_404(const char *, const char *,
223 unsigned int *, const char **,
224 const char **, isc_buffer_t
*,
225 isc_httpdfree_t
**, void **);
228 destroy_client(isc_httpd_t
**httpdp
)
230 isc_httpd_t
*httpd
= *httpdp
;
231 isc_httpdmgr_t
*httpdmgr
= httpd
->mgr
;
235 LOCK(&httpdmgr
->lock
);
237 isc_socket_detach(&httpd
->sock
);
238 ISC_LIST_UNLINK(httpdmgr
->running
, httpd
, link
);
240 if (httpd
->headerlen
> 0)
241 isc_mem_put(httpdmgr
->mctx
, httpd
->headerdata
,
244 isc_mem_put(httpdmgr
->mctx
, httpd
, sizeof(isc_httpd_t
));
246 UNLOCK(&httpdmgr
->lock
);
248 httpdmgr_destroy(httpdmgr
);
252 isc_httpdmgr_create(isc_mem_t
*mctx
, isc_socket_t
*sock
, isc_task_t
*task
,
253 isc_httpdclientok_t
*client_ok
,
254 isc_httpdondestroy_t
*ondestroy
, void *cb_arg
,
255 isc_timermgr_t
*tmgr
, isc_httpdmgr_t
**httpdp
)
258 isc_httpdmgr_t
*httpd
;
260 REQUIRE(mctx
!= NULL
);
261 REQUIRE(sock
!= NULL
);
262 REQUIRE(task
!= NULL
);
263 REQUIRE(tmgr
!= NULL
);
264 REQUIRE(httpdp
!= NULL
&& *httpdp
== NULL
);
266 httpd
= isc_mem_get(mctx
, sizeof(isc_httpdmgr_t
));
268 return (ISC_R_NOMEMORY
);
270 result
= isc_mutex_init(&httpd
->lock
);
271 if (result
!= ISC_R_SUCCESS
) {
272 isc_mem_put(mctx
, httpd
, sizeof(isc_httpdmgr_t
));
276 isc_mem_attach(mctx
, &httpd
->mctx
);
278 isc_socket_attach(sock
, &httpd
->sock
);
280 isc_task_attach(task
, &httpd
->task
);
281 httpd
->timermgr
= tmgr
; /* XXXMLG no attach function? */
282 httpd
->client_ok
= client_ok
;
283 httpd
->ondestroy
= ondestroy
;
284 httpd
->cb_arg
= cb_arg
;
286 ISC_LIST_INIT(httpd
->running
);
287 ISC_LIST_INIT(httpd
->urls
);
289 /* XXXMLG ignore errors on isc_socket_listen() */
290 result
= isc_socket_listen(sock
, SOMAXCONN
);
291 if (result
!= ISC_R_SUCCESS
) {
292 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
293 "isc_socket_listen() failed: %s",
294 isc_result_totext(result
));
298 (void)isc_socket_filter(sock
, "httpready");
300 result
= isc_socket_accept(sock
, task
, isc_httpd_accept
, httpd
);
301 if (result
!= ISC_R_SUCCESS
)
304 httpd
->render_404
= render_404
;
307 return (ISC_R_SUCCESS
);
310 isc_task_detach(&httpd
->task
);
311 isc_socket_detach(&httpd
->sock
);
312 isc_mem_detach(&httpd
->mctx
);
313 isc_mutex_destroy(&httpd
->lock
);
314 isc_mem_put(mctx
, httpd
, sizeof(isc_httpdmgr_t
));
319 httpdmgr_destroy(isc_httpdmgr_t
*httpdmgr
)
324 ENTER("httpdmgr_destroy");
326 LOCK(&httpdmgr
->lock
);
328 if (!MSHUTTINGDOWN(httpdmgr
)) {
329 NOTICE("httpdmgr_destroy not shutting down yet");
330 UNLOCK(&httpdmgr
->lock
);
335 * If all clients are not shut down, don't do anything yet.
337 if (!ISC_LIST_EMPTY(httpdmgr
->running
)) {
338 NOTICE("httpdmgr_destroy clients still active");
339 UNLOCK(&httpdmgr
->lock
);
343 NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
345 isc_socket_detach(&httpdmgr
->sock
);
346 isc_task_detach(&httpdmgr
->task
);
347 httpdmgr
->timermgr
= NULL
;
350 * Clear out the list of all actions we know about. Just free the
353 url
= ISC_LIST_HEAD(httpdmgr
->urls
);
354 while (url
!= NULL
) {
355 isc_mem_free(httpdmgr
->mctx
, url
->url
);
356 ISC_LIST_UNLINK(httpdmgr
->urls
, url
, link
);
357 isc_mem_put(httpdmgr
->mctx
, url
, sizeof(isc_httpdurl_t
));
358 url
= ISC_LIST_HEAD(httpdmgr
->urls
);
361 UNLOCK(&httpdmgr
->lock
);
362 isc_mutex_destroy(&httpdmgr
->lock
);
364 if (httpdmgr
->ondestroy
!= NULL
)
365 (httpdmgr
->ondestroy
)(httpdmgr
->cb_arg
);
367 mctx
= httpdmgr
->mctx
;
368 isc_mem_putanddetach(&mctx
, httpdmgr
, sizeof(isc_httpdmgr_t
));
370 EXIT("httpdmgr_destroy");
373 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
374 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
377 process_request(isc_httpd_t
*httpd
, int length
)
385 httpd
->recvlen
+= length
;
387 httpd
->recvbuf
[httpd
->recvlen
] = 0;
390 * If we don't find a blank line in our buffer, return that we need
393 s
= strstr(httpd
->recvbuf
, "\r\n\r\n");
396 s
= strstr(httpd
->recvbuf
, "\n\n");
400 return (ISC_R_NOTFOUND
);
403 * Determine if this is a POST or GET method. Any other values will
404 * cause an error to be returned.
406 if (strncmp(httpd
->recvbuf
, "GET ", 4) == 0) {
407 httpd
->method
= ISC_HTTPD_METHODGET
;
408 p
= httpd
->recvbuf
+ 4;
409 } else if (strncmp(httpd
->recvbuf
, "POST ", 5) == 0) {
410 httpd
->method
= ISC_HTTPD_METHODPOST
;
411 p
= httpd
->recvbuf
+ 5;
413 return (ISC_R_RANGE
);
417 * From now on, p is the start of our buffer.
424 while (LENGTHOK(s
) && BUFLENOK(s
) &&
425 (*s
!= '\n' && *s
!= '\r' && *s
!= '\0' && *s
!= ' '))
428 return (ISC_R_NOTFOUND
);
430 return (ISC_R_NOMEMORY
);
434 * Make the URL relative.
436 if ((strncmp(p
, "http:/", 6) == 0)
437 || (strncmp(p
, "https:/", 7) == 0)) {
439 while (*p
!= '/' && *p
!= 0)
442 return (ISC_R_RANGE
);
445 while (*p
!= '/' && *p
!= 0)
448 return (ISC_R_RANGE
);
451 while (*p
!= '/' && *p
!= 0)
464 * Now, see if there is a ? mark in the URL. If so, this is
465 * part of the query string, and we will split it from the URL.
467 httpd
->querystring
= strchr(httpd
->url
, '?');
468 if (httpd
->querystring
!= NULL
) {
469 *(httpd
->querystring
) = 0;
470 httpd
->querystring
++;
474 * Extract the HTTP/1.X protocol. We will bounce on anything but
477 while (LENGTHOK(s
) && BUFLENOK(s
) &&
478 (*s
!= '\n' && *s
!= '\r' && *s
!= '\0'))
481 return (ISC_R_NOTFOUND
);
483 return (ISC_R_NOMEMORY
);
485 if ((strncmp(p
, "HTTP/1.0", 8) != 0)
486 && (strncmp(p
, "HTTP/1.1", 8) != 0))
487 return (ISC_R_RANGE
);
492 if (strstr(s
, "Connection: close") != NULL
)
493 httpd
->flags
|= HTTPD_CLOSE
;
495 if (strstr(s
, "Host: ") != NULL
)
496 httpd
->flags
|= HTTPD_FOUNDHOST
;
499 * Standards compliance hooks here.
501 if (strcmp(httpd
->protocol
, "HTTP/1.1") == 0
502 && ((httpd
->flags
& HTTPD_FOUNDHOST
) == 0))
503 return (ISC_R_RANGE
);
507 return (ISC_R_SUCCESS
);
511 isc_httpd_accept(isc_task_t
*task
, isc_event_t
*ev
)
514 isc_httpdmgr_t
*httpdmgr
= ev
->ev_arg
;
517 isc_socket_newconnev_t
*nev
= (isc_socket_newconnev_t
*)ev
;
518 isc_sockaddr_t peeraddr
;
522 LOCK(&httpdmgr
->lock
);
523 if (MSHUTTINGDOWN(httpdmgr
)) {
524 NOTICE("accept shutting down, goto out");
528 if (nev
->result
== ISC_R_CANCELED
) {
529 NOTICE("accept canceled, goto out");
533 if (nev
->result
!= ISC_R_SUCCESS
) {
534 /* XXXMLG log failure */
535 NOTICE("accept returned failure, goto requeue");
539 (void)isc_socket_getpeername(nev
->newsocket
, &peeraddr
);
540 if (httpdmgr
->client_ok
!= NULL
&&
541 !(httpdmgr
->client_ok
)(&peeraddr
, httpdmgr
->cb_arg
)) {
542 isc_socket_detach(&nev
->newsocket
);
546 httpd
= isc_mem_get(httpdmgr
->mctx
, sizeof(isc_httpd_t
));
548 /* XXXMLG log failure */
549 NOTICE("accept failed to allocate memory, goto requeue");
550 isc_socket_detach(&nev
->newsocket
);
554 httpd
->mgr
= httpdmgr
;
555 ISC_LINK_INIT(httpd
, link
);
556 ISC_LIST_APPEND(httpdmgr
->running
, httpd
, link
);
557 ISC_HTTPD_SETRECV(httpd
);
558 httpd
->sock
= nev
->newsocket
;
559 isc_socket_setname(httpd
->sock
, "httpd", NULL
);
563 * Initialize the buffer for our headers.
565 httpd
->headerdata
= isc_mem_get(httpdmgr
->mctx
, HTTP_SENDGROW
);
566 if (httpd
->headerdata
== NULL
) {
567 isc_mem_put(httpdmgr
->mctx
, httpd
, sizeof(isc_httpd_t
));
568 isc_socket_detach(&nev
->newsocket
);
571 httpd
->headerlen
= HTTP_SENDGROW
;
572 isc_buffer_init(&httpd
->headerbuffer
, httpd
->headerdata
,
575 ISC_LIST_INIT(httpd
->bufflist
);
577 isc_buffer_initnull(&httpd
->bodybuffer
);
580 r
.base
= (unsigned char *)httpd
->recvbuf
;
581 r
.length
= HTTP_RECVLEN
- 1;
582 result
= isc_socket_recv(httpd
->sock
, &r
, 1, task
, isc_httpd_recvdone
,
584 NOTICE("accept queued recv on socket");
587 result
= isc_socket_accept(httpdmgr
->sock
, task
, isc_httpd_accept
,
589 if (result
!= ISC_R_SUCCESS
) {
590 /* XXXMLG what to do? Log failure... */
591 NOTICE("accept could not reaccept due to failure");
595 UNLOCK(&httpdmgr
->lock
);
597 httpdmgr_destroy(httpdmgr
);
605 render_404(const char *url
, const char *querystring
,
607 unsigned int *retcode
, const char **retmsg
,
608 const char **mimetype
, isc_buffer_t
*b
,
609 isc_httpdfree_t
**freecb
, void **freecb_args
)
611 static char msg
[] = "No such URL.";
618 *retmsg
= "No such URL";
619 *mimetype
= "text/plain";
620 isc_buffer_reinit(b
, msg
, strlen(msg
));
621 isc_buffer_add(b
, strlen(msg
));
625 return (ISC_R_SUCCESS
);
629 isc_httpd_recvdone(isc_task_t
*task
, isc_event_t
*ev
)
633 isc_httpd_t
*httpd
= ev
->ev_arg
;
634 isc_socketevent_t
*sev
= (isc_socketevent_t
*)ev
;
637 char datebuf
[32]; /* Only need 30, but safety first */
641 INSIST(ISC_HTTPD_ISRECV(httpd
));
643 if (sev
->result
!= ISC_R_SUCCESS
) {
644 NOTICE("recv destroying client");
645 destroy_client(&httpd
);
649 result
= process_request(httpd
, sev
->n
);
650 if (result
== ISC_R_NOTFOUND
) {
651 if (httpd
->recvlen
>= HTTP_RECVLEN
- 1) {
652 destroy_client(&httpd
);
655 r
.base
= (unsigned char *)httpd
->recvbuf
+ httpd
->recvlen
;
656 r
.length
= HTTP_RECVLEN
- httpd
->recvlen
- 1;
657 result
= isc_socket_recv(httpd
->sock
, &r
, 1, task
,
658 isc_httpd_recvdone
, httpd
);
660 } else if (result
!= ISC_R_SUCCESS
) {
661 destroy_client(&httpd
);
665 ISC_HTTPD_SETSEND(httpd
);
668 * XXXMLG Call function here. Provide an add-header function
669 * which will append the common headers to a response we generate.
671 isc_buffer_initnull(&httpd
->bodybuffer
);
673 isc_time_formathttptimestamp(&now
, datebuf
, sizeof(datebuf
));
674 url
= ISC_LIST_HEAD(httpd
->mgr
->urls
);
675 while (url
!= NULL
) {
676 if (strcmp(httpd
->url
, url
->url
) == 0)
678 url
= ISC_LIST_NEXT(url
, link
);
681 result
= httpd
->mgr
->render_404(httpd
->url
, httpd
->querystring
,
690 result
= url
->action(httpd
->url
, httpd
->querystring
,
692 &httpd
->retcode
, &httpd
->retmsg
,
693 &httpd
->mimetype
, &httpd
->bodybuffer
,
694 &httpd
->freecb
, &httpd
->freecb_arg
);
695 if (result
!= ISC_R_SUCCESS
) {
696 destroy_client(&httpd
);
700 isc_httpd_response(httpd
);
701 isc_httpd_addheader(httpd
, "Content-Type", httpd
->mimetype
);
702 isc_httpd_addheader(httpd
, "Date", datebuf
);
703 isc_httpd_addheader(httpd
, "Expires", datebuf
);
704 isc_httpd_addheader(httpd
, "Last-Modified", datebuf
);
705 isc_httpd_addheader(httpd
, "Pragma: no-cache", NULL
);
706 isc_httpd_addheader(httpd
, "Cache-Control: no-cache", NULL
);
707 isc_httpd_addheader(httpd
, "Server: libisc", NULL
);
708 isc_httpd_addheaderuint(httpd
, "Content-Length",
709 isc_buffer_usedlength(&httpd
->bodybuffer
));
710 isc_httpd_endheaders(httpd
); /* done */
712 ISC_LIST_APPEND(httpd
->bufflist
, &httpd
->headerbuffer
, link
);
714 * Link the data buffer into our send queue, should we have any data
715 * rendered into it. If no data is present, we won't do anything
718 if (isc_buffer_length(&httpd
->bodybuffer
) > 0)
719 ISC_LIST_APPEND(httpd
->bufflist
, &httpd
->bodybuffer
, link
);
721 result
= isc_socket_sendv(httpd
->sock
, &httpd
->bufflist
, task
,
722 isc_httpd_senddone
, httpd
);
730 isc_httpdmgr_shutdown(isc_httpdmgr_t
**httpdmgrp
)
732 isc_httpdmgr_t
*httpdmgr
;
734 httpdmgr
= *httpdmgrp
;
737 ENTER("isc_httpdmgr_shutdown");
739 LOCK(&httpdmgr
->lock
);
741 MSETSHUTTINGDOWN(httpdmgr
);
743 isc_socket_cancel(httpdmgr
->sock
, httpdmgr
->task
, ISC_SOCKCANCEL_ALL
);
745 httpd
= ISC_LIST_HEAD(httpdmgr
->running
);
746 while (httpd
!= NULL
) {
747 isc_socket_cancel(httpd
->sock
, httpdmgr
->task
,
749 httpd
= ISC_LIST_NEXT(httpd
, link
);
752 UNLOCK(&httpdmgr
->lock
);
754 EXIT("isc_httpdmgr_shutdown");
758 grow_headerspace(isc_httpd_t
*httpd
)
764 newlen
= httpd
->headerlen
+ HTTP_SENDGROW
;
765 if (newlen
> HTTP_SEND_MAXLEN
)
766 return (ISC_R_NOSPACE
);
768 newspace
= isc_mem_get(httpd
->mgr
->mctx
, newlen
);
769 if (newspace
== NULL
)
770 return (ISC_R_NOMEMORY
);
771 isc_buffer_region(&httpd
->headerbuffer
, &r
);
772 isc_buffer_reinit(&httpd
->headerbuffer
, newspace
, newlen
);
774 isc_mem_put(httpd
->mgr
->mctx
, r
.base
, r
.length
);
776 return (ISC_R_SUCCESS
);
780 isc_httpd_response(isc_httpd_t
*httpd
)
783 unsigned int needlen
;
785 needlen
= strlen(httpd
->protocol
) + 1; /* protocol + space */
786 needlen
+= 3 + 1; /* room for response code, always 3 bytes */
787 needlen
+= strlen(httpd
->retmsg
) + 2; /* return msg + CRLF */
789 if (isc_buffer_availablelength(&httpd
->headerbuffer
) < needlen
) {
790 result
= grow_headerspace(httpd
);
791 if (result
!= ISC_R_SUCCESS
)
795 sprintf(isc_buffer_used(&httpd
->headerbuffer
), "%s %03d %s\r\n",
796 httpd
->protocol
, httpd
->retcode
, httpd
->retmsg
);
797 isc_buffer_add(&httpd
->headerbuffer
, needlen
);
799 return (ISC_R_SUCCESS
);
803 isc_httpd_addheader(isc_httpd_t
*httpd
, const char *name
,
807 unsigned int needlen
;
809 needlen
= strlen(name
); /* name itself */
811 needlen
+= 2 + strlen(val
); /* :<space> and val */
812 needlen
+= 2; /* CRLF */
814 if (isc_buffer_availablelength(&httpd
->headerbuffer
) < needlen
) {
815 result
= grow_headerspace(httpd
);
816 if (result
!= ISC_R_SUCCESS
)
821 sprintf(isc_buffer_used(&httpd
->headerbuffer
),
822 "%s: %s\r\n", name
, val
);
824 sprintf(isc_buffer_used(&httpd
->headerbuffer
),
827 isc_buffer_add(&httpd
->headerbuffer
, needlen
);
829 return (ISC_R_SUCCESS
);
833 isc_httpd_endheaders(isc_httpd_t
*httpd
)
837 if (isc_buffer_availablelength(&httpd
->headerbuffer
) < 2) {
838 result
= grow_headerspace(httpd
);
839 if (result
!= ISC_R_SUCCESS
)
843 sprintf(isc_buffer_used(&httpd
->headerbuffer
), "\r\n");
844 isc_buffer_add(&httpd
->headerbuffer
, 2);
846 return (ISC_R_SUCCESS
);
850 isc_httpd_addheaderuint(isc_httpd_t
*httpd
, const char *name
, int val
) {
852 unsigned int needlen
;
853 char buf
[sizeof "18446744073709551616"];
855 sprintf(buf
, "%d", val
);
857 needlen
= strlen(name
); /* name itself */
858 needlen
+= 2 + strlen(buf
); /* :<space> and val */
859 needlen
+= 2; /* CRLF */
861 if (isc_buffer_availablelength(&httpd
->headerbuffer
) < needlen
) {
862 result
= grow_headerspace(httpd
);
863 if (result
!= ISC_R_SUCCESS
)
867 sprintf(isc_buffer_used(&httpd
->headerbuffer
),
868 "%s: %s\r\n", name
, buf
);
870 isc_buffer_add(&httpd
->headerbuffer
, needlen
);
872 return (ISC_R_SUCCESS
);
876 isc_httpd_senddone(isc_task_t
*task
, isc_event_t
*ev
)
878 isc_httpd_t
*httpd
= ev
->ev_arg
;
881 isc_socketevent_t
*sev
= (isc_socketevent_t
*)ev
;
884 INSIST(ISC_HTTPD_ISSEND(httpd
));
887 * First, unlink our header buffer from the socket's bufflist. This
888 * is sort of an evil hack, since we know our buffer will be there,
889 * and we know it's address, so we can just remove it directly.
891 NOTICE("senddone unlinked header");
892 ISC_LIST_UNLINK(sev
->bufferlist
, &httpd
->headerbuffer
, link
);
895 * We will always want to clean up our receive buffer, even if we
896 * got an error on send or we are shutting down.
898 * We will pass in the buffer only if there is data in it. If
899 * there is no data, we will pass in a NULL.
901 if (httpd
->freecb
!= NULL
) {
902 isc_buffer_t
*b
= NULL
;
903 if (isc_buffer_length(&httpd
->bodybuffer
) > 0)
904 b
= &httpd
->bodybuffer
;
905 httpd
->freecb(b
, httpd
->freecb_arg
);
906 NOTICE("senddone free callback performed");
908 if (ISC_LINK_LINKED(&httpd
->bodybuffer
, link
)) {
909 ISC_LIST_UNLINK(sev
->bufferlist
, &httpd
->bodybuffer
, link
);
910 NOTICE("senddone body buffer unlinked");
913 if (sev
->result
!= ISC_R_SUCCESS
) {
914 destroy_client(&httpd
);
918 if ((httpd
->flags
& HTTPD_CLOSE
) != 0) {
919 destroy_client(&httpd
);
923 ISC_HTTPD_SETRECV(httpd
);
925 NOTICE("senddone restarting recv on socket");
929 r
.base
= (unsigned char *)httpd
->recvbuf
;
930 r
.length
= HTTP_RECVLEN
- 1;
931 result
= isc_socket_recv(httpd
->sock
, &r
, 1, task
, isc_httpd_recvdone
,
940 reset_client(isc_httpd_t
*httpd
)
943 * Catch errors here. We MUST be in RECV mode, and we MUST NOT have
944 * any outstanding buffers. If we have buffers, we have a leak.
946 INSIST(ISC_HTTPD_ISRECV(httpd
));
947 INSIST(!ISC_LINK_LINKED(&httpd
->headerbuffer
, link
));
948 INSIST(!ISC_LINK_LINKED(&httpd
->bodybuffer
, link
));
950 httpd
->recvbuf
[0] = 0;
952 httpd
->method
= ISC_HTTPD_METHODUNKNOWN
;
954 httpd
->querystring
= NULL
;
955 httpd
->protocol
= NULL
;
958 isc_buffer_clear(&httpd
->headerbuffer
);
959 isc_buffer_invalidate(&httpd
->bodybuffer
);
963 isc_httpdmgr_addurl(isc_httpdmgr_t
*httpdmgr
, const char *url
,
964 isc_httpdaction_t
*func
, void *arg
)
966 isc_httpdurl_t
*item
;
969 httpdmgr
->render_404
= func
;
970 return (ISC_R_SUCCESS
);
973 item
= isc_mem_get(httpdmgr
->mctx
, sizeof(isc_httpdurl_t
));
975 return (ISC_R_NOMEMORY
);
977 item
->url
= isc_mem_strdup(httpdmgr
->mctx
, url
);
978 if (item
->url
== NULL
) {
979 isc_mem_put(httpdmgr
->mctx
, item
, sizeof(isc_httpdurl_t
));
980 return (ISC_R_NOMEMORY
);
984 item
->action_arg
= arg
;
985 ISC_LINK_INIT(item
, link
);
986 ISC_LIST_APPEND(httpdmgr
->urls
, item
, link
);
988 return (ISC_R_SUCCESS
);