httpd.c revision 1.2.2.2 1 1.2.2.2 pgoyette /* $NetBSD: httpd.c,v 1.2.2.2 2018/09/06 06:55:05 pgoyette Exp $ */
2 1.2.2.2 pgoyette
3 1.2.2.2 pgoyette /*
4 1.2.2.2 pgoyette * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 1.2.2.2 pgoyette *
6 1.2.2.2 pgoyette * This Source Code Form is subject to the terms of the Mozilla Public
7 1.2.2.2 pgoyette * License, v. 2.0. If a copy of the MPL was not distributed with this
8 1.2.2.2 pgoyette * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 1.2.2.2 pgoyette *
10 1.2.2.2 pgoyette * See the COPYRIGHT file distributed with this work for additional
11 1.2.2.2 pgoyette * information regarding copyright ownership.
12 1.2.2.2 pgoyette */
13 1.2.2.2 pgoyette
14 1.2.2.2 pgoyette
15 1.2.2.2 pgoyette /*! \file */
16 1.2.2.2 pgoyette
17 1.2.2.2 pgoyette #include <config.h>
18 1.2.2.2 pgoyette
19 1.2.2.2 pgoyette #include <isc/buffer.h>
20 1.2.2.2 pgoyette #include <isc/httpd.h>
21 1.2.2.2 pgoyette #include <isc/mem.h>
22 1.2.2.2 pgoyette #include <isc/print.h>
23 1.2.2.2 pgoyette #include <isc/socket.h>
24 1.2.2.2 pgoyette #include <isc/string.h>
25 1.2.2.2 pgoyette #include <isc/task.h>
26 1.2.2.2 pgoyette #include <isc/time.h>
27 1.2.2.2 pgoyette #include <isc/util.h>
28 1.2.2.2 pgoyette
29 1.2.2.2 pgoyette #include <string.h>
30 1.2.2.2 pgoyette
31 1.2.2.2 pgoyette #ifdef HAVE_ZLIB
32 1.2.2.2 pgoyette #include <zlib.h>
33 1.2.2.2 pgoyette #endif
34 1.2.2.2 pgoyette
35 1.2.2.2 pgoyette /*%
36 1.2.2.2 pgoyette * TODO:
37 1.2.2.2 pgoyette *
38 1.2.2.2 pgoyette * o Put in better checks to make certain things are passed in correctly.
39 1.2.2.2 pgoyette * This includes a magic number for externally-visible structures,
40 1.2.2.2 pgoyette * checking for NULL-ness before dereferencing, etc.
41 1.2.2.2 pgoyette * o Make the URL processing external functions which will fill-in a buffer
42 1.2.2.2 pgoyette * structure we provide, or return an error and we will render a generic
43 1.2.2.2 pgoyette * page and close the client.
44 1.2.2.2 pgoyette */
45 1.2.2.2 pgoyette
46 1.2.2.2 pgoyette #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
47 1.2.2.2 pgoyette #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
48 1.2.2.2 pgoyette
49 1.2.2.2 pgoyette #ifdef DEBUG_HTTPD
50 1.2.2.2 pgoyette #define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (/*CONSTCOND*/0)
51 1.2.2.2 pgoyette #define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (/*CONSTCOND*/0)
52 1.2.2.2 pgoyette #define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (/*CONSTCOND*/0)
53 1.2.2.2 pgoyette #else
54 1.2.2.2 pgoyette #define ENTER(x) do { } while(/*CONSTCOND*/0)
55 1.2.2.2 pgoyette #define EXIT(x) do { } while(/*CONSTCOND*/0)
56 1.2.2.2 pgoyette #define NOTICE(x) do { } while(/*CONSTCOND*/0)
57 1.2.2.2 pgoyette #endif
58 1.2.2.2 pgoyette
59 1.2.2.2 pgoyette #define HTTP_RECVLEN 1024
60 1.2.2.2 pgoyette #define HTTP_SENDGROW 1024
61 1.2.2.2 pgoyette #define HTTP_SEND_MAXLEN 10240
62 1.2.2.2 pgoyette
63 1.2.2.2 pgoyette #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
64 1.2.2.2 pgoyette #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
65 1.2.2.2 pgoyette #define HTTPD_KEEPALIVE 0x0004 /* Got a Connection: Keep-Alive */
66 1.2.2.2 pgoyette #define HTTPD_ACCEPT_DEFLATE 0x0008
67 1.2.2.2 pgoyette
68 1.2.2.2 pgoyette /*% http client */
69 1.2.2.2 pgoyette struct isc_httpd {
70 1.2.2.2 pgoyette isc_httpdmgr_t *mgr; /*%< our parent */
71 1.2.2.2 pgoyette ISC_LINK(isc_httpd_t) link;
72 1.2.2.2 pgoyette unsigned int state;
73 1.2.2.2 pgoyette isc_socket_t *sock;
74 1.2.2.2 pgoyette
75 1.2.2.2 pgoyette /*%
76 1.2.2.2 pgoyette * Received data state.
77 1.2.2.2 pgoyette */
78 1.2.2.2 pgoyette char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
79 1.2.2.2 pgoyette isc_uint32_t recvlen; /*%< length recv'd */
80 1.2.2.2 pgoyette char *headers; /*%< set in process_request() */
81 1.2.2.2 pgoyette unsigned int method;
82 1.2.2.2 pgoyette char *url;
83 1.2.2.2 pgoyette char *querystring;
84 1.2.2.2 pgoyette char *protocol;
85 1.2.2.2 pgoyette
86 1.2.2.2 pgoyette /*
87 1.2.2.2 pgoyette * Flags on the httpd client.
88 1.2.2.2 pgoyette */
89 1.2.2.2 pgoyette int flags;
90 1.2.2.2 pgoyette
91 1.2.2.2 pgoyette /*%
92 1.2.2.2 pgoyette * Transmit data state.
93 1.2.2.2 pgoyette *
94 1.2.2.2 pgoyette * This is the data buffer we will transmit.
95 1.2.2.2 pgoyette *
96 1.2.2.2 pgoyette * This free function pointer is filled in by the rendering function
97 1.2.2.2 pgoyette * we call. The free function is called after the data is transmitted
98 1.2.2.2 pgoyette * to the client.
99 1.2.2.2 pgoyette *
100 1.2.2.2 pgoyette * The bufflist is the list of buffers we are currently transmitting.
101 1.2.2.2 pgoyette * The headerbuffer is where we render our headers to. If we run out of
102 1.2.2.2 pgoyette * space when rendering a header, we will change the size of our
103 1.2.2.2 pgoyette * buffer. We will not free it until we are finished, and will
104 1.2.2.2 pgoyette * allocate an additional HTTP_SENDGROW bytes per header space grow.
105 1.2.2.2 pgoyette *
106 1.2.2.2 pgoyette * We currently use three buffers total, one for the headers (which
107 1.2.2.2 pgoyette * we manage), another for the client to fill in (which it manages,
108 1.2.2.2 pgoyette * it provides the space for it, etc) -- we will pass that buffer
109 1.2.2.2 pgoyette * structure back to the caller, who is responsible for managing the
110 1.2.2.2 pgoyette * space it may have allocated as backing store for it. This second
111 1.2.2.2 pgoyette * buffer is bodybuffer, and we only allocate the buffer itself, not
112 1.2.2.2 pgoyette * the backing store.
113 1.2.2.2 pgoyette * The third buffer is compbuffer, managed by us, that contains the
114 1.2.2.2 pgoyette * compressed HTTP data, if compression is used.
115 1.2.2.2 pgoyette *
116 1.2.2.2 pgoyette */
117 1.2.2.2 pgoyette isc_bufferlist_t bufflist;
118 1.2.2.2 pgoyette isc_buffer_t headerbuffer;
119 1.2.2.2 pgoyette isc_buffer_t compbuffer;
120 1.2.2.2 pgoyette
121 1.2.2.2 pgoyette const char *mimetype;
122 1.2.2.2 pgoyette unsigned int retcode;
123 1.2.2.2 pgoyette const char *retmsg;
124 1.2.2.2 pgoyette isc_buffer_t bodybuffer;
125 1.2.2.2 pgoyette isc_httpdfree_t *freecb;
126 1.2.2.2 pgoyette void *freecb_arg;
127 1.2.2.2 pgoyette };
128 1.2.2.2 pgoyette
129 1.2.2.2 pgoyette /*% lightweight socket manager for httpd output */
130 1.2.2.2 pgoyette struct isc_httpdmgr {
131 1.2.2.2 pgoyette isc_mem_t *mctx;
132 1.2.2.2 pgoyette isc_socket_t *sock; /*%< listening socket */
133 1.2.2.2 pgoyette isc_task_t *task; /*%< owning task */
134 1.2.2.2 pgoyette isc_timermgr_t *timermgr;
135 1.2.2.2 pgoyette
136 1.2.2.2 pgoyette isc_httpdclientok_t *client_ok; /*%< client validator */
137 1.2.2.2 pgoyette isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
138 1.2.2.2 pgoyette void *cb_arg; /*%< argument for the above */
139 1.2.2.2 pgoyette
140 1.2.2.2 pgoyette unsigned int flags;
141 1.2.2.2 pgoyette ISC_LIST(isc_httpd_t) running; /*%< running clients */
142 1.2.2.2 pgoyette
143 1.2.2.2 pgoyette isc_mutex_t lock;
144 1.2.2.2 pgoyette
145 1.2.2.2 pgoyette ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
146 1.2.2.2 pgoyette isc_httpdaction_t *render_404;
147 1.2.2.2 pgoyette isc_httpdaction_t *render_500;
148 1.2.2.2 pgoyette };
149 1.2.2.2 pgoyette
150 1.2.2.2 pgoyette /*%
151 1.2.2.2 pgoyette * HTTP methods.
152 1.2.2.2 pgoyette */
153 1.2.2.2 pgoyette #define ISC_HTTPD_METHODUNKNOWN 0
154 1.2.2.2 pgoyette #define ISC_HTTPD_METHODGET 1
155 1.2.2.2 pgoyette #define ISC_HTTPD_METHODPOST 2
156 1.2.2.2 pgoyette
157 1.2.2.2 pgoyette /*%
158 1.2.2.2 pgoyette * Client states.
159 1.2.2.2 pgoyette *
160 1.2.2.2 pgoyette * _IDLE The client is not doing anything at all. This state should
161 1.2.2.2 pgoyette * only occur just after creation, and just before being
162 1.2.2.2 pgoyette * destroyed.
163 1.2.2.2 pgoyette *
164 1.2.2.2 pgoyette * _RECV The client is waiting for data after issuing a socket recv().
165 1.2.2.2 pgoyette *
166 1.2.2.2 pgoyette * _RECVDONE Data has been received, and is being processed.
167 1.2.2.2 pgoyette *
168 1.2.2.2 pgoyette * _SEND All data for a response has completed, and a reply was
169 1.2.2.2 pgoyette * sent via a socket send() call.
170 1.2.2.2 pgoyette *
171 1.2.2.2 pgoyette * _SENDDONE Send is completed.
172 1.2.2.2 pgoyette *
173 1.2.2.2 pgoyette * Badly formatted state table:
174 1.2.2.2 pgoyette *
175 1.2.2.2 pgoyette * IDLE -> RECV when client has a recv() queued.
176 1.2.2.2 pgoyette *
177 1.2.2.2 pgoyette * RECV -> RECVDONE when recvdone event received.
178 1.2.2.2 pgoyette *
179 1.2.2.2 pgoyette * RECVDONE -> SEND if the data for a reply is at hand.
180 1.2.2.2 pgoyette *
181 1.2.2.2 pgoyette * SEND -> RECV when a senddone event was received.
182 1.2.2.2 pgoyette *
183 1.2.2.2 pgoyette * At any time -> RECV on error. If RECV fails, the client will
184 1.2.2.2 pgoyette * self-destroy, closing the socket and freeing memory.
185 1.2.2.2 pgoyette */
186 1.2.2.2 pgoyette #define ISC_HTTPD_STATEIDLE 0
187 1.2.2.2 pgoyette #define ISC_HTTPD_STATERECV 1
188 1.2.2.2 pgoyette #define ISC_HTTPD_STATERECVDONE 2
189 1.2.2.2 pgoyette #define ISC_HTTPD_STATESEND 3
190 1.2.2.2 pgoyette #define ISC_HTTPD_STATESENDDONE 4
191 1.2.2.2 pgoyette
192 1.2.2.2 pgoyette #define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV)
193 1.2.2.2 pgoyette #define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
194 1.2.2.2 pgoyette #define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND)
195 1.2.2.2 pgoyette #define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
196 1.2.2.2 pgoyette
197 1.2.2.2 pgoyette /*%
198 1.2.2.2 pgoyette * Overall magic test that means we're not idle.
199 1.2.2.2 pgoyette */
200 1.2.2.2 pgoyette #define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV)
201 1.2.2.2 pgoyette #define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE)
202 1.2.2.2 pgoyette #define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND)
203 1.2.2.2 pgoyette #define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE)
204 1.2.2.2 pgoyette
205 1.2.2.2 pgoyette static void isc_httpd_accept(isc_task_t *, isc_event_t *);
206 1.2.2.2 pgoyette static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
207 1.2.2.2 pgoyette static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
208 1.2.2.2 pgoyette static void destroy_client(isc_httpd_t **);
209 1.2.2.2 pgoyette static isc_result_t process_request(isc_httpd_t *, int);
210 1.2.2.2 pgoyette static void httpdmgr_destroy(isc_httpdmgr_t *);
211 1.2.2.2 pgoyette static isc_result_t grow_headerspace(isc_httpd_t *);
212 1.2.2.2 pgoyette static void reset_client(isc_httpd_t *httpd);
213 1.2.2.2 pgoyette
214 1.2.2.2 pgoyette static isc_httpdaction_t render_404;
215 1.2.2.2 pgoyette static isc_httpdaction_t render_500;
216 1.2.2.2 pgoyette
217 1.2.2.2 pgoyette static void (*finishhook)(void) = NULL;
218 1.2.2.2 pgoyette
219 1.2.2.2 pgoyette static void
220 1.2.2.2 pgoyette destroy_client(isc_httpd_t **httpdp) {
221 1.2.2.2 pgoyette isc_httpd_t *httpd = *httpdp;
222 1.2.2.2 pgoyette isc_httpdmgr_t *httpdmgr = httpd->mgr;
223 1.2.2.2 pgoyette isc_region_t r;
224 1.2.2.2 pgoyette
225 1.2.2.2 pgoyette *httpdp = NULL;
226 1.2.2.2 pgoyette
227 1.2.2.2 pgoyette LOCK(&httpdmgr->lock);
228 1.2.2.2 pgoyette
229 1.2.2.2 pgoyette isc_socket_detach(&httpd->sock);
230 1.2.2.2 pgoyette ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
231 1.2.2.2 pgoyette
232 1.2.2.2 pgoyette isc_buffer_region(&httpd->headerbuffer, &r);
233 1.2.2.2 pgoyette if (r.length > 0) {
234 1.2.2.2 pgoyette isc_mem_put(httpdmgr->mctx, r.base, r.length);
235 1.2.2.2 pgoyette }
236 1.2.2.2 pgoyette
237 1.2.2.2 pgoyette isc_buffer_region(&httpd->compbuffer, &r);
238 1.2.2.2 pgoyette if (r.length > 0) {
239 1.2.2.2 pgoyette isc_mem_put(httpdmgr->mctx, r.base, r.length);
240 1.2.2.2 pgoyette }
241 1.2.2.2 pgoyette
242 1.2.2.2 pgoyette isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
243 1.2.2.2 pgoyette
244 1.2.2.2 pgoyette UNLOCK(&httpdmgr->lock);
245 1.2.2.2 pgoyette
246 1.2.2.2 pgoyette if (finishhook != NULL)
247 1.2.2.2 pgoyette finishhook();
248 1.2.2.2 pgoyette
249 1.2.2.2 pgoyette httpdmgr_destroy(httpdmgr);
250 1.2.2.2 pgoyette }
251 1.2.2.2 pgoyette
252 1.2.2.2 pgoyette isc_result_t
253 1.2.2.2 pgoyette isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
254 1.2.2.2 pgoyette isc_httpdclientok_t *client_ok,
255 1.2.2.2 pgoyette isc_httpdondestroy_t *ondestroy, void *cb_arg,
256 1.2.2.2 pgoyette isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdmgrp)
257 1.2.2.2 pgoyette {
258 1.2.2.2 pgoyette isc_result_t result;
259 1.2.2.2 pgoyette isc_httpdmgr_t *httpdmgr;
260 1.2.2.2 pgoyette
261 1.2.2.2 pgoyette REQUIRE(mctx != NULL);
262 1.2.2.2 pgoyette REQUIRE(sock != NULL);
263 1.2.2.2 pgoyette REQUIRE(task != NULL);
264 1.2.2.2 pgoyette REQUIRE(tmgr != NULL);
265 1.2.2.2 pgoyette REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL);
266 1.2.2.2 pgoyette
267 1.2.2.2 pgoyette httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
268 1.2.2.2 pgoyette if (httpdmgr == NULL)
269 1.2.2.2 pgoyette return (ISC_R_NOMEMORY);
270 1.2.2.2 pgoyette
271 1.2.2.2 pgoyette result = isc_mutex_init(&httpdmgr->lock);
272 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS) {
273 1.2.2.2 pgoyette isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
274 1.2.2.2 pgoyette return (result);
275 1.2.2.2 pgoyette }
276 1.2.2.2 pgoyette httpdmgr->mctx = NULL;
277 1.2.2.2 pgoyette isc_mem_attach(mctx, &httpdmgr->mctx);
278 1.2.2.2 pgoyette httpdmgr->sock = NULL;
279 1.2.2.2 pgoyette isc_socket_attach(sock, &httpdmgr->sock);
280 1.2.2.2 pgoyette httpdmgr->task = NULL;
281 1.2.2.2 pgoyette isc_task_attach(task, &httpdmgr->task);
282 1.2.2.2 pgoyette httpdmgr->timermgr = tmgr; /* XXXMLG no attach function? */
283 1.2.2.2 pgoyette httpdmgr->client_ok = client_ok;
284 1.2.2.2 pgoyette httpdmgr->ondestroy = ondestroy;
285 1.2.2.2 pgoyette httpdmgr->cb_arg = cb_arg;
286 1.2.2.2 pgoyette httpdmgr->flags = 0;
287 1.2.2.2 pgoyette
288 1.2.2.2 pgoyette ISC_LIST_INIT(httpdmgr->running);
289 1.2.2.2 pgoyette ISC_LIST_INIT(httpdmgr->urls);
290 1.2.2.2 pgoyette
291 1.2.2.2 pgoyette /* XXXMLG ignore errors on isc_socket_listen() */
292 1.2.2.2 pgoyette result = isc_socket_listen(sock, SOMAXCONN);
293 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS) {
294 1.2.2.2 pgoyette UNEXPECTED_ERROR(__FILE__, __LINE__,
295 1.2.2.2 pgoyette "isc_socket_listen() failed: %s",
296 1.2.2.2 pgoyette isc_result_totext(result));
297 1.2.2.2 pgoyette goto cleanup;
298 1.2.2.2 pgoyette }
299 1.2.2.2 pgoyette
300 1.2.2.2 pgoyette (void)isc_socket_filter(sock, "httpready");
301 1.2.2.2 pgoyette
302 1.2.2.2 pgoyette result = isc_socket_accept(sock, task, isc_httpd_accept, httpdmgr);
303 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS)
304 1.2.2.2 pgoyette goto cleanup;
305 1.2.2.2 pgoyette
306 1.2.2.2 pgoyette httpdmgr->render_404 = render_404;
307 1.2.2.2 pgoyette httpdmgr->render_500 = render_500;
308 1.2.2.2 pgoyette
309 1.2.2.2 pgoyette *httpdmgrp = httpdmgr;
310 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
311 1.2.2.2 pgoyette
312 1.2.2.2 pgoyette cleanup:
313 1.2.2.2 pgoyette isc_task_detach(&httpdmgr->task);
314 1.2.2.2 pgoyette isc_socket_detach(&httpdmgr->sock);
315 1.2.2.2 pgoyette isc_mem_detach(&httpdmgr->mctx);
316 1.2.2.2 pgoyette (void)isc_mutex_destroy(&httpdmgr->lock);
317 1.2.2.2 pgoyette isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
318 1.2.2.2 pgoyette return (result);
319 1.2.2.2 pgoyette }
320 1.2.2.2 pgoyette
321 1.2.2.2 pgoyette static void
322 1.2.2.2 pgoyette httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) {
323 1.2.2.2 pgoyette isc_mem_t *mctx;
324 1.2.2.2 pgoyette isc_httpdurl_t *url;
325 1.2.2.2 pgoyette
326 1.2.2.2 pgoyette ENTER("httpdmgr_destroy");
327 1.2.2.2 pgoyette
328 1.2.2.2 pgoyette LOCK(&httpdmgr->lock);
329 1.2.2.2 pgoyette
330 1.2.2.2 pgoyette if (!MSHUTTINGDOWN(httpdmgr)) {
331 1.2.2.2 pgoyette NOTICE("httpdmgr_destroy not shutting down yet");
332 1.2.2.2 pgoyette UNLOCK(&httpdmgr->lock);
333 1.2.2.2 pgoyette return;
334 1.2.2.2 pgoyette }
335 1.2.2.2 pgoyette
336 1.2.2.2 pgoyette /*
337 1.2.2.2 pgoyette * If all clients are not shut down, don't do anything yet.
338 1.2.2.2 pgoyette */
339 1.2.2.2 pgoyette if (!ISC_LIST_EMPTY(httpdmgr->running)) {
340 1.2.2.2 pgoyette NOTICE("httpdmgr_destroy clients still active");
341 1.2.2.2 pgoyette UNLOCK(&httpdmgr->lock);
342 1.2.2.2 pgoyette return;
343 1.2.2.2 pgoyette }
344 1.2.2.2 pgoyette
345 1.2.2.2 pgoyette NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
346 1.2.2.2 pgoyette
347 1.2.2.2 pgoyette isc_socket_detach(&httpdmgr->sock);
348 1.2.2.2 pgoyette isc_task_detach(&httpdmgr->task);
349 1.2.2.2 pgoyette httpdmgr->timermgr = NULL;
350 1.2.2.2 pgoyette
351 1.2.2.2 pgoyette /*
352 1.2.2.2 pgoyette * Clear out the list of all actions we know about. Just free the
353 1.2.2.2 pgoyette * memory.
354 1.2.2.2 pgoyette */
355 1.2.2.2 pgoyette url = ISC_LIST_HEAD(httpdmgr->urls);
356 1.2.2.2 pgoyette while (url != NULL) {
357 1.2.2.2 pgoyette isc_mem_free(httpdmgr->mctx, url->url);
358 1.2.2.2 pgoyette ISC_LIST_UNLINK(httpdmgr->urls, url, link);
359 1.2.2.2 pgoyette isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
360 1.2.2.2 pgoyette url = ISC_LIST_HEAD(httpdmgr->urls);
361 1.2.2.2 pgoyette }
362 1.2.2.2 pgoyette
363 1.2.2.2 pgoyette UNLOCK(&httpdmgr->lock);
364 1.2.2.2 pgoyette (void)isc_mutex_destroy(&httpdmgr->lock);
365 1.2.2.2 pgoyette
366 1.2.2.2 pgoyette if (httpdmgr->ondestroy != NULL)
367 1.2.2.2 pgoyette (httpdmgr->ondestroy)(httpdmgr->cb_arg);
368 1.2.2.2 pgoyette
369 1.2.2.2 pgoyette mctx = httpdmgr->mctx;
370 1.2.2.2 pgoyette isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
371 1.2.2.2 pgoyette
372 1.2.2.2 pgoyette EXIT("httpdmgr_destroy");
373 1.2.2.2 pgoyette }
374 1.2.2.2 pgoyette
375 1.2.2.2 pgoyette #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
376 1.2.2.2 pgoyette #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
377 1.2.2.2 pgoyette
378 1.2.2.2 pgoyette /*
379 1.2.2.2 pgoyette * Look for the given header in headers.
380 1.2.2.2 pgoyette * If value is specified look for it terminated with a character in eov.
381 1.2.2.2 pgoyette */
382 1.2.2.2 pgoyette static isc_boolean_t
383 1.2.2.2 pgoyette have_header(isc_httpd_t *httpd, const char *header, const char *value,
384 1.2.2.2 pgoyette const char *eov)
385 1.2.2.2 pgoyette {
386 1.2.2.2 pgoyette char *cr, *nl, *h;
387 1.2.2.2 pgoyette size_t hlen, vlen = 0;
388 1.2.2.2 pgoyette
389 1.2.2.2 pgoyette h = httpd->headers;
390 1.2.2.2 pgoyette hlen = strlen(header);
391 1.2.2.2 pgoyette if (value != NULL) {
392 1.2.2.2 pgoyette INSIST(eov != NULL);
393 1.2.2.2 pgoyette vlen = strlen(value);
394 1.2.2.2 pgoyette }
395 1.2.2.2 pgoyette
396 1.2.2.2 pgoyette for (;;) {
397 1.2.2.2 pgoyette if (strncasecmp(h, header, hlen) != 0) {
398 1.2.2.2 pgoyette /*
399 1.2.2.2 pgoyette * Skip to next line;
400 1.2.2.2 pgoyette */
401 1.2.2.2 pgoyette cr = strchr(h, '\r');
402 1.2.2.2 pgoyette if (cr != NULL && cr[1] == '\n')
403 1.2.2.2 pgoyette cr++;
404 1.2.2.2 pgoyette nl = strchr(h, '\n');
405 1.2.2.2 pgoyette
406 1.2.2.2 pgoyette /* last header? */
407 1.2.2.2 pgoyette h = cr;
408 1.2.2.2 pgoyette if (h == NULL || (nl != NULL && nl < h))
409 1.2.2.2 pgoyette h = nl;
410 1.2.2.2 pgoyette if (h == NULL)
411 1.2.2.2 pgoyette return (ISC_FALSE);
412 1.2.2.2 pgoyette h++;
413 1.2.2.2 pgoyette continue;
414 1.2.2.2 pgoyette }
415 1.2.2.2 pgoyette
416 1.2.2.2 pgoyette if (value == NULL)
417 1.2.2.2 pgoyette return (ISC_TRUE);
418 1.2.2.2 pgoyette
419 1.2.2.2 pgoyette /*
420 1.2.2.2 pgoyette * Skip optional leading white space.
421 1.2.2.2 pgoyette */
422 1.2.2.2 pgoyette h += hlen;
423 1.2.2.2 pgoyette while (*h == ' ' || *h == '\t')
424 1.2.2.2 pgoyette h++;
425 1.2.2.2 pgoyette /*
426 1.2.2.2 pgoyette * Terminate token search on NULL or EOL.
427 1.2.2.2 pgoyette */
428 1.2.2.2 pgoyette while (*h != 0 && *h != '\r' && *h != '\n') {
429 1.2.2.2 pgoyette if (strncasecmp(h, value, vlen) == 0)
430 1.2.2.2 pgoyette if (strchr(eov, h[vlen]) != NULL)
431 1.2.2.2 pgoyette return (ISC_TRUE);
432 1.2.2.2 pgoyette /*
433 1.2.2.2 pgoyette * Skip to next token.
434 1.2.2.2 pgoyette */
435 1.2.2.2 pgoyette h += strcspn(h, eov);
436 1.2.2.2 pgoyette if (h[0] == '\r' && h[1] == '\n')
437 1.2.2.2 pgoyette h++;
438 1.2.2.2 pgoyette if (h[0] != 0)
439 1.2.2.2 pgoyette h++;
440 1.2.2.2 pgoyette }
441 1.2.2.2 pgoyette return (ISC_FALSE);
442 1.2.2.2 pgoyette }
443 1.2.2.2 pgoyette }
444 1.2.2.2 pgoyette
445 1.2.2.2 pgoyette static isc_result_t
446 1.2.2.2 pgoyette process_request(isc_httpd_t *httpd, int length) {
447 1.2.2.2 pgoyette char *s;
448 1.2.2.2 pgoyette char *p;
449 1.2.2.2 pgoyette int delim;
450 1.2.2.2 pgoyette
451 1.2.2.2 pgoyette ENTER("request");
452 1.2.2.2 pgoyette
453 1.2.2.2 pgoyette httpd->recvlen += length;
454 1.2.2.2 pgoyette
455 1.2.2.2 pgoyette httpd->recvbuf[httpd->recvlen] = 0;
456 1.2.2.2 pgoyette httpd->headers = NULL;
457 1.2.2.2 pgoyette
458 1.2.2.2 pgoyette /*
459 1.2.2.2 pgoyette * If we don't find a blank line in our buffer, return that we need
460 1.2.2.2 pgoyette * more data.
461 1.2.2.2 pgoyette */
462 1.2.2.2 pgoyette s = strstr(httpd->recvbuf, "\r\n\r\n");
463 1.2.2.2 pgoyette delim = 2;
464 1.2.2.2 pgoyette if (s == NULL) {
465 1.2.2.2 pgoyette s = strstr(httpd->recvbuf, "\n\n");
466 1.2.2.2 pgoyette delim = 1;
467 1.2.2.2 pgoyette }
468 1.2.2.2 pgoyette if (s == NULL)
469 1.2.2.2 pgoyette return (ISC_R_NOTFOUND);
470 1.2.2.2 pgoyette
471 1.2.2.2 pgoyette /*
472 1.2.2.2 pgoyette * NUL terminate request at the blank line.
473 1.2.2.2 pgoyette */
474 1.2.2.2 pgoyette s[delim] = 0;
475 1.2.2.2 pgoyette
476 1.2.2.2 pgoyette /*
477 1.2.2.2 pgoyette * Determine if this is a POST or GET method. Any other values will
478 1.2.2.2 pgoyette * cause an error to be returned.
479 1.2.2.2 pgoyette */
480 1.2.2.2 pgoyette if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
481 1.2.2.2 pgoyette httpd->method = ISC_HTTPD_METHODGET;
482 1.2.2.2 pgoyette p = httpd->recvbuf + 4;
483 1.2.2.2 pgoyette } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
484 1.2.2.2 pgoyette httpd->method = ISC_HTTPD_METHODPOST;
485 1.2.2.2 pgoyette p = httpd->recvbuf + 5;
486 1.2.2.2 pgoyette } else {
487 1.2.2.2 pgoyette return (ISC_R_RANGE);
488 1.2.2.2 pgoyette }
489 1.2.2.2 pgoyette
490 1.2.2.2 pgoyette /*
491 1.2.2.2 pgoyette * From now on, p is the start of our buffer.
492 1.2.2.2 pgoyette */
493 1.2.2.2 pgoyette
494 1.2.2.2 pgoyette /*
495 1.2.2.2 pgoyette * Extract the URL.
496 1.2.2.2 pgoyette */
497 1.2.2.2 pgoyette s = p;
498 1.2.2.2 pgoyette while (LENGTHOK(s) && BUFLENOK(s) &&
499 1.2.2.2 pgoyette (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
500 1.2.2.2 pgoyette s++;
501 1.2.2.2 pgoyette if (!LENGTHOK(s))
502 1.2.2.2 pgoyette return (ISC_R_NOTFOUND);
503 1.2.2.2 pgoyette if (!BUFLENOK(s))
504 1.2.2.2 pgoyette return (ISC_R_NOMEMORY);
505 1.2.2.2 pgoyette *s = 0;
506 1.2.2.2 pgoyette
507 1.2.2.2 pgoyette /*
508 1.2.2.2 pgoyette * Make the URL relative.
509 1.2.2.2 pgoyette */
510 1.2.2.2 pgoyette if ((strncmp(p, "http:/", 6) == 0)
511 1.2.2.2 pgoyette || (strncmp(p, "https:/", 7) == 0)) {
512 1.2.2.2 pgoyette /* Skip first / */
513 1.2.2.2 pgoyette while (*p != '/' && *p != 0)
514 1.2.2.2 pgoyette p++;
515 1.2.2.2 pgoyette if (*p == 0)
516 1.2.2.2 pgoyette return (ISC_R_RANGE);
517 1.2.2.2 pgoyette p++;
518 1.2.2.2 pgoyette /* Skip second / */
519 1.2.2.2 pgoyette while (*p != '/' && *p != 0)
520 1.2.2.2 pgoyette p++;
521 1.2.2.2 pgoyette if (*p == 0)
522 1.2.2.2 pgoyette return (ISC_R_RANGE);
523 1.2.2.2 pgoyette p++;
524 1.2.2.2 pgoyette /* Find third / */
525 1.2.2.2 pgoyette while (*p != '/' && *p != 0)
526 1.2.2.2 pgoyette p++;
527 1.2.2.2 pgoyette if (*p == 0) {
528 1.2.2.2 pgoyette p--;
529 1.2.2.2 pgoyette *p = '/';
530 1.2.2.2 pgoyette }
531 1.2.2.2 pgoyette }
532 1.2.2.2 pgoyette
533 1.2.2.2 pgoyette httpd->url = p;
534 1.2.2.2 pgoyette p = s + 1;
535 1.2.2.2 pgoyette s = p;
536 1.2.2.2 pgoyette
537 1.2.2.2 pgoyette /*
538 1.2.2.2 pgoyette * Now, see if there is a ? mark in the URL. If so, this is
539 1.2.2.2 pgoyette * part of the query string, and we will split it from the URL.
540 1.2.2.2 pgoyette */
541 1.2.2.2 pgoyette httpd->querystring = strchr(httpd->url, '?');
542 1.2.2.2 pgoyette if (httpd->querystring != NULL) {
543 1.2.2.2 pgoyette *(httpd->querystring) = 0;
544 1.2.2.2 pgoyette httpd->querystring++;
545 1.2.2.2 pgoyette }
546 1.2.2.2 pgoyette
547 1.2.2.2 pgoyette /*
548 1.2.2.2 pgoyette * Extract the HTTP/1.X protocol. We will bounce on anything but
549 1.2.2.2 pgoyette * HTTP/1.0 or HTTP/1.1 for now.
550 1.2.2.2 pgoyette */
551 1.2.2.2 pgoyette while (LENGTHOK(s) && BUFLENOK(s) &&
552 1.2.2.2 pgoyette (*s != '\n' && *s != '\r' && *s != '\0'))
553 1.2.2.2 pgoyette s++;
554 1.2.2.2 pgoyette if (!LENGTHOK(s))
555 1.2.2.2 pgoyette return (ISC_R_NOTFOUND);
556 1.2.2.2 pgoyette if (!BUFLENOK(s))
557 1.2.2.2 pgoyette return (ISC_R_NOMEMORY);
558 1.2.2.2 pgoyette /*
559 1.2.2.2 pgoyette * Check that we have the expected eol delimiter.
560 1.2.2.2 pgoyette */
561 1.2.2.2 pgoyette if (strncmp(s, delim == 1 ? "\n" : "\r\n", delim) != 0)
562 1.2.2.2 pgoyette return (ISC_R_RANGE);
563 1.2.2.2 pgoyette *s = 0;
564 1.2.2.2 pgoyette if ((strncmp(p, "HTTP/1.0", 8) != 0)
565 1.2.2.2 pgoyette && (strncmp(p, "HTTP/1.1", 8) != 0))
566 1.2.2.2 pgoyette return (ISC_R_RANGE);
567 1.2.2.2 pgoyette httpd->protocol = p;
568 1.2.2.2 pgoyette p = s + delim; /* skip past eol */
569 1.2.2.2 pgoyette s = p;
570 1.2.2.2 pgoyette
571 1.2.2.2 pgoyette httpd->headers = s;
572 1.2.2.2 pgoyette
573 1.2.2.2 pgoyette if (have_header(httpd, "Connection:", "close", ", \t\r\n"))
574 1.2.2.2 pgoyette httpd->flags |= HTTPD_CLOSE;
575 1.2.2.2 pgoyette
576 1.2.2.2 pgoyette if (have_header(httpd, "Host:", NULL, NULL))
577 1.2.2.2 pgoyette httpd->flags |= HTTPD_FOUNDHOST;
578 1.2.2.2 pgoyette
579 1.2.2.2 pgoyette if (strncmp(httpd->protocol, "HTTP/1.0", 8) == 0) {
580 1.2.2.2 pgoyette if (have_header(httpd, "Connection:", "Keep-Alive",
581 1.2.2.2 pgoyette ", \t\r\n"))
582 1.2.2.2 pgoyette httpd->flags |= HTTPD_KEEPALIVE;
583 1.2.2.2 pgoyette else
584 1.2.2.2 pgoyette httpd->flags |= HTTPD_CLOSE;
585 1.2.2.2 pgoyette }
586 1.2.2.2 pgoyette
587 1.2.2.2 pgoyette /*
588 1.2.2.2 pgoyette * Check for Accept-Encoding:
589 1.2.2.2 pgoyette */
590 1.2.2.2 pgoyette #ifdef HAVE_ZLIB
591 1.2.2.2 pgoyette if (have_header(httpd, "Accept-Encoding:", "deflate", ";, \t\r\n"))
592 1.2.2.2 pgoyette httpd->flags |= HTTPD_ACCEPT_DEFLATE;
593 1.2.2.2 pgoyette #endif
594 1.2.2.2 pgoyette
595 1.2.2.2 pgoyette /*
596 1.2.2.2 pgoyette * Standards compliance hooks here.
597 1.2.2.2 pgoyette */
598 1.2.2.2 pgoyette if (strcmp(httpd->protocol, "HTTP/1.1") == 0
599 1.2.2.2 pgoyette && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
600 1.2.2.2 pgoyette return (ISC_R_RANGE);
601 1.2.2.2 pgoyette
602 1.2.2.2 pgoyette EXIT("request");
603 1.2.2.2 pgoyette
604 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
605 1.2.2.2 pgoyette }
606 1.2.2.2 pgoyette
607 1.2.2.2 pgoyette static void
608 1.2.2.2 pgoyette isc_httpd_accept(isc_task_t *task, isc_event_t *ev) {
609 1.2.2.2 pgoyette isc_result_t result;
610 1.2.2.2 pgoyette isc_httpdmgr_t *httpdmgr = ev->ev_arg;
611 1.2.2.2 pgoyette isc_httpd_t *httpd;
612 1.2.2.2 pgoyette isc_region_t r;
613 1.2.2.2 pgoyette isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
614 1.2.2.2 pgoyette isc_sockaddr_t peeraddr;
615 1.2.2.2 pgoyette char *headerdata;
616 1.2.2.2 pgoyette
617 1.2.2.2 pgoyette ENTER("accept");
618 1.2.2.2 pgoyette
619 1.2.2.2 pgoyette LOCK(&httpdmgr->lock);
620 1.2.2.2 pgoyette if (MSHUTTINGDOWN(httpdmgr)) {
621 1.2.2.2 pgoyette NOTICE("accept shutting down, goto out");
622 1.2.2.2 pgoyette goto out;
623 1.2.2.2 pgoyette }
624 1.2.2.2 pgoyette
625 1.2.2.2 pgoyette if (nev->result == ISC_R_CANCELED) {
626 1.2.2.2 pgoyette NOTICE("accept canceled, goto out");
627 1.2.2.2 pgoyette goto out;
628 1.2.2.2 pgoyette }
629 1.2.2.2 pgoyette
630 1.2.2.2 pgoyette if (nev->result != ISC_R_SUCCESS) {
631 1.2.2.2 pgoyette /* XXXMLG log failure */
632 1.2.2.2 pgoyette NOTICE("accept returned failure, goto requeue");
633 1.2.2.2 pgoyette goto requeue;
634 1.2.2.2 pgoyette }
635 1.2.2.2 pgoyette
636 1.2.2.2 pgoyette (void)isc_socket_getpeername(nev->newsocket, &peeraddr);
637 1.2.2.2 pgoyette if (httpdmgr->client_ok != NULL &&
638 1.2.2.2 pgoyette !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
639 1.2.2.2 pgoyette isc_socket_detach(&nev->newsocket);
640 1.2.2.2 pgoyette goto requeue;
641 1.2.2.2 pgoyette }
642 1.2.2.2 pgoyette
643 1.2.2.2 pgoyette httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
644 1.2.2.2 pgoyette if (httpd == NULL) {
645 1.2.2.2 pgoyette /* XXXMLG log failure */
646 1.2.2.2 pgoyette NOTICE("accept failed to allocate memory, goto requeue");
647 1.2.2.2 pgoyette isc_socket_detach(&nev->newsocket);
648 1.2.2.2 pgoyette goto requeue;
649 1.2.2.2 pgoyette }
650 1.2.2.2 pgoyette
651 1.2.2.2 pgoyette httpd->mgr = httpdmgr;
652 1.2.2.2 pgoyette ISC_LINK_INIT(httpd, link);
653 1.2.2.2 pgoyette ISC_LIST_APPEND(httpdmgr->running, httpd, link);
654 1.2.2.2 pgoyette ISC_HTTPD_SETRECV(httpd);
655 1.2.2.2 pgoyette httpd->sock = nev->newsocket;
656 1.2.2.2 pgoyette isc_socket_setname(httpd->sock, "httpd", NULL);
657 1.2.2.2 pgoyette httpd->flags = 0;
658 1.2.2.2 pgoyette
659 1.2.2.2 pgoyette /*
660 1.2.2.2 pgoyette * Initialize the buffer for our headers.
661 1.2.2.2 pgoyette */
662 1.2.2.2 pgoyette headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
663 1.2.2.2 pgoyette if (headerdata == NULL) {
664 1.2.2.2 pgoyette isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
665 1.2.2.2 pgoyette isc_socket_detach(&nev->newsocket);
666 1.2.2.2 pgoyette goto requeue;
667 1.2.2.2 pgoyette }
668 1.2.2.2 pgoyette isc_buffer_init(&httpd->headerbuffer, headerdata, HTTP_SENDGROW);
669 1.2.2.2 pgoyette
670 1.2.2.2 pgoyette ISC_LIST_INIT(httpd->bufflist);
671 1.2.2.2 pgoyette
672 1.2.2.2 pgoyette isc_buffer_initnull(&httpd->compbuffer);
673 1.2.2.2 pgoyette isc_buffer_initnull(&httpd->bodybuffer);
674 1.2.2.2 pgoyette reset_client(httpd);
675 1.2.2.2 pgoyette
676 1.2.2.2 pgoyette r.base = (unsigned char *)httpd->recvbuf;
677 1.2.2.2 pgoyette r.length = HTTP_RECVLEN - 1;
678 1.2.2.2 pgoyette result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
679 1.2.2.2 pgoyette httpd);
680 1.2.2.2 pgoyette /* FIXME!!! */
681 1.2.2.2 pgoyette POST(result);
682 1.2.2.2 pgoyette NOTICE("accept queued recv on socket");
683 1.2.2.2 pgoyette
684 1.2.2.2 pgoyette requeue:
685 1.2.2.2 pgoyette result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
686 1.2.2.2 pgoyette httpdmgr);
687 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS) {
688 1.2.2.2 pgoyette /* XXXMLG what to do? Log failure... */
689 1.2.2.2 pgoyette NOTICE("accept could not reaccept due to failure");
690 1.2.2.2 pgoyette }
691 1.2.2.2 pgoyette
692 1.2.2.2 pgoyette out:
693 1.2.2.2 pgoyette UNLOCK(&httpdmgr->lock);
694 1.2.2.2 pgoyette
695 1.2.2.2 pgoyette httpdmgr_destroy(httpdmgr);
696 1.2.2.2 pgoyette
697 1.2.2.2 pgoyette isc_event_free(&ev);
698 1.2.2.2 pgoyette
699 1.2.2.2 pgoyette EXIT("accept");
700 1.2.2.2 pgoyette }
701 1.2.2.2 pgoyette
702 1.2.2.2 pgoyette static isc_result_t
703 1.2.2.2 pgoyette render_404(const char *url, isc_httpdurl_t *urlinfo,
704 1.2.2.2 pgoyette const char *querystring, const char *headers, void *arg,
705 1.2.2.2 pgoyette unsigned int *retcode, const char **retmsg,
706 1.2.2.2 pgoyette const char **mimetype, isc_buffer_t *b,
707 1.2.2.2 pgoyette isc_httpdfree_t **freecb, void **freecb_args)
708 1.2.2.2 pgoyette {
709 1.2.2.2 pgoyette static char msg[] = "No such URL.\r\n";
710 1.2.2.2 pgoyette
711 1.2.2.2 pgoyette UNUSED(url);
712 1.2.2.2 pgoyette UNUSED(urlinfo);
713 1.2.2.2 pgoyette UNUSED(querystring);
714 1.2.2.2 pgoyette UNUSED(headers);
715 1.2.2.2 pgoyette UNUSED(arg);
716 1.2.2.2 pgoyette
717 1.2.2.2 pgoyette *retcode = 404;
718 1.2.2.2 pgoyette *retmsg = "No such URL";
719 1.2.2.2 pgoyette *mimetype = "text/plain";
720 1.2.2.2 pgoyette isc_buffer_reinit(b, msg, strlen(msg));
721 1.2.2.2 pgoyette isc_buffer_add(b, strlen(msg));
722 1.2.2.2 pgoyette *freecb = NULL;
723 1.2.2.2 pgoyette *freecb_args = NULL;
724 1.2.2.2 pgoyette
725 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
726 1.2.2.2 pgoyette }
727 1.2.2.2 pgoyette
728 1.2.2.2 pgoyette static isc_result_t
729 1.2.2.2 pgoyette render_500(const char *url, isc_httpdurl_t *urlinfo,
730 1.2.2.2 pgoyette const char *querystring, const char *headers, void *arg,
731 1.2.2.2 pgoyette unsigned int *retcode, const char **retmsg,
732 1.2.2.2 pgoyette const char **mimetype, isc_buffer_t *b,
733 1.2.2.2 pgoyette isc_httpdfree_t **freecb, void **freecb_args)
734 1.2.2.2 pgoyette {
735 1.2.2.2 pgoyette static char msg[] = "Internal server failure.\r\n";
736 1.2.2.2 pgoyette
737 1.2.2.2 pgoyette UNUSED(url);
738 1.2.2.2 pgoyette UNUSED(urlinfo);
739 1.2.2.2 pgoyette UNUSED(querystring);
740 1.2.2.2 pgoyette UNUSED(headers);
741 1.2.2.2 pgoyette UNUSED(arg);
742 1.2.2.2 pgoyette
743 1.2.2.2 pgoyette *retcode = 500;
744 1.2.2.2 pgoyette *retmsg = "Internal server failure";
745 1.2.2.2 pgoyette *mimetype = "text/plain";
746 1.2.2.2 pgoyette isc_buffer_reinit(b, msg, strlen(msg));
747 1.2.2.2 pgoyette isc_buffer_add(b, strlen(msg));
748 1.2.2.2 pgoyette *freecb = NULL;
749 1.2.2.2 pgoyette *freecb_args = NULL;
750 1.2.2.2 pgoyette
751 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
752 1.2.2.2 pgoyette }
753 1.2.2.2 pgoyette
754 1.2.2.2 pgoyette #ifdef HAVE_ZLIB
755 1.2.2.2 pgoyette /*%<
756 1.2.2.2 pgoyette * Reallocates compbuffer to size, does nothing if compbuffer is already
757 1.2.2.2 pgoyette * larger than size.
758 1.2.2.2 pgoyette *
759 1.2.2.2 pgoyette * Requires:
760 1.2.2.2 pgoyette *\li httpd a valid isc_httpd_t object
761 1.2.2.2 pgoyette *
762 1.2.2.2 pgoyette * Returns:
763 1.2.2.2 pgoyette *\li #ISC_R_SUCCESS -- all is well.
764 1.2.2.2 pgoyette *\li #ISC_R_NOMEMORY -- not enough memory to extend buffer
765 1.2.2.2 pgoyette */
766 1.2.2.2 pgoyette static isc_result_t
767 1.2.2.2 pgoyette alloc_compspace(isc_httpd_t *httpd, unsigned int size) {
768 1.2.2.2 pgoyette char *newspace;
769 1.2.2.2 pgoyette isc_region_t r;
770 1.2.2.2 pgoyette
771 1.2.2.2 pgoyette isc_buffer_region(&httpd->compbuffer, &r);
772 1.2.2.2 pgoyette if (size < r.length) {
773 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
774 1.2.2.2 pgoyette }
775 1.2.2.2 pgoyette
776 1.2.2.2 pgoyette newspace = isc_mem_get(httpd->mgr->mctx, size);
777 1.2.2.2 pgoyette if (newspace == NULL)
778 1.2.2.2 pgoyette return (ISC_R_NOMEMORY);
779 1.2.2.2 pgoyette isc_buffer_reinit(&httpd->compbuffer, newspace, size);
780 1.2.2.2 pgoyette
781 1.2.2.2 pgoyette if (r.base != NULL) {
782 1.2.2.2 pgoyette isc_mem_put(httpd->mgr->mctx, r.base, r.length);
783 1.2.2.2 pgoyette }
784 1.2.2.2 pgoyette
785 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
786 1.2.2.2 pgoyette }
787 1.2.2.2 pgoyette
788 1.2.2.2 pgoyette /*%<
789 1.2.2.2 pgoyette * Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it
790 1.2.2.2 pgoyette * if necessary.
791 1.2.2.2 pgoyette *
792 1.2.2.2 pgoyette * Requires:
793 1.2.2.2 pgoyette *\li httpd a valid isc_httpd_t object
794 1.2.2.2 pgoyette *
795 1.2.2.2 pgoyette * Returns:
796 1.2.2.2 pgoyette *\li #ISC_R_SUCCESS -- all is well.
797 1.2.2.2 pgoyette *\li #ISC_R_NOMEMORY -- not enough memory to compress data
798 1.2.2.2 pgoyette *\li #ISC_R_FAILURE -- error during compression or compressed
799 1.2.2.2 pgoyette * data would be larger than input data
800 1.2.2.2 pgoyette */
801 1.2.2.2 pgoyette static isc_result_t
802 1.2.2.2 pgoyette isc_httpd_compress(isc_httpd_t *httpd) {
803 1.2.2.2 pgoyette z_stream zstr;
804 1.2.2.2 pgoyette isc_region_t r;
805 1.2.2.2 pgoyette isc_result_t result;
806 1.2.2.2 pgoyette int ret;
807 1.2.2.2 pgoyette int inputlen;
808 1.2.2.2 pgoyette
809 1.2.2.2 pgoyette inputlen = isc_buffer_usedlength(&httpd->bodybuffer);
810 1.2.2.2 pgoyette result = alloc_compspace(httpd, inputlen);
811 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS) {
812 1.2.2.2 pgoyette return result;
813 1.2.2.2 pgoyette }
814 1.2.2.2 pgoyette isc_buffer_region(&httpd->compbuffer, &r);
815 1.2.2.2 pgoyette
816 1.2.2.2 pgoyette /*
817 1.2.2.2 pgoyette * We're setting output buffer size to input size so it fails if the
818 1.2.2.2 pgoyette * compressed data size would be bigger than the input size.
819 1.2.2.2 pgoyette */
820 1.2.2.2 pgoyette memset(&zstr, 0, sizeof(zstr));
821 1.2.2.2 pgoyette zstr.total_in = zstr.avail_in =
822 1.2.2.2 pgoyette zstr.total_out = zstr.avail_out = inputlen;
823 1.2.2.2 pgoyette
824 1.2.2.2 pgoyette zstr.next_in = isc_buffer_base(&httpd->bodybuffer);
825 1.2.2.2 pgoyette zstr.next_out = r.base;
826 1.2.2.2 pgoyette
827 1.2.2.2 pgoyette ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION);
828 1.2.2.2 pgoyette if (ret == Z_OK) {
829 1.2.2.2 pgoyette ret = deflate(&zstr, Z_FINISH);
830 1.2.2.2 pgoyette }
831 1.2.2.2 pgoyette deflateEnd(&zstr);
832 1.2.2.2 pgoyette if (ret == Z_STREAM_END) {
833 1.2.2.2 pgoyette isc_buffer_add(&httpd->compbuffer, inputlen - zstr.avail_out);
834 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
835 1.2.2.2 pgoyette } else {
836 1.2.2.2 pgoyette return (ISC_R_FAILURE);
837 1.2.2.2 pgoyette }
838 1.2.2.2 pgoyette }
839 1.2.2.2 pgoyette #endif
840 1.2.2.2 pgoyette
841 1.2.2.2 pgoyette static void
842 1.2.2.2 pgoyette isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) {
843 1.2.2.2 pgoyette isc_region_t r;
844 1.2.2.2 pgoyette isc_result_t result;
845 1.2.2.2 pgoyette isc_httpd_t *httpd = ev->ev_arg;
846 1.2.2.2 pgoyette isc_socketevent_t *sev = (isc_socketevent_t *)ev;
847 1.2.2.2 pgoyette isc_httpdurl_t *url;
848 1.2.2.2 pgoyette isc_time_t now;
849 1.2.2.2 pgoyette isc_boolean_t is_compressed = ISC_FALSE;
850 1.2.2.2 pgoyette char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
851 1.2.2.2 pgoyette
852 1.2.2.2 pgoyette ENTER("recv");
853 1.2.2.2 pgoyette
854 1.2.2.2 pgoyette INSIST(ISC_HTTPD_ISRECV(httpd));
855 1.2.2.2 pgoyette
856 1.2.2.2 pgoyette if (sev->result != ISC_R_SUCCESS) {
857 1.2.2.2 pgoyette NOTICE("recv destroying client");
858 1.2.2.2 pgoyette destroy_client(&httpd);
859 1.2.2.2 pgoyette goto out;
860 1.2.2.2 pgoyette }
861 1.2.2.2 pgoyette
862 1.2.2.2 pgoyette result = process_request(httpd, sev->n);
863 1.2.2.2 pgoyette if (result == ISC_R_NOTFOUND) {
864 1.2.2.2 pgoyette if (httpd->recvlen >= HTTP_RECVLEN - 1) {
865 1.2.2.2 pgoyette destroy_client(&httpd);
866 1.2.2.2 pgoyette goto out;
867 1.2.2.2 pgoyette }
868 1.2.2.2 pgoyette r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
869 1.2.2.2 pgoyette r.length = HTTP_RECVLEN - httpd->recvlen - 1;
870 1.2.2.2 pgoyette /* check return code? */
871 1.2.2.2 pgoyette (void)isc_socket_recv(httpd->sock, &r, 1, task,
872 1.2.2.2 pgoyette isc_httpd_recvdone, httpd);
873 1.2.2.2 pgoyette goto out;
874 1.2.2.2 pgoyette } else if (result != ISC_R_SUCCESS) {
875 1.2.2.2 pgoyette destroy_client(&httpd);
876 1.2.2.2 pgoyette goto out;
877 1.2.2.2 pgoyette }
878 1.2.2.2 pgoyette
879 1.2.2.2 pgoyette ISC_HTTPD_SETSEND(httpd);
880 1.2.2.2 pgoyette
881 1.2.2.2 pgoyette /*
882 1.2.2.2 pgoyette * XXXMLG Call function here. Provide an add-header function
883 1.2.2.2 pgoyette * which will append the common headers to a response we generate.
884 1.2.2.2 pgoyette */
885 1.2.2.2 pgoyette isc_buffer_initnull(&httpd->bodybuffer);
886 1.2.2.2 pgoyette isc_time_now(&now);
887 1.2.2.2 pgoyette isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
888 1.2.2.2 pgoyette url = ISC_LIST_HEAD(httpd->mgr->urls);
889 1.2.2.2 pgoyette while (url != NULL) {
890 1.2.2.2 pgoyette if (strcmp(httpd->url, url->url) == 0)
891 1.2.2.2 pgoyette break;
892 1.2.2.2 pgoyette url = ISC_LIST_NEXT(url, link);
893 1.2.2.2 pgoyette }
894 1.2.2.2 pgoyette if (url == NULL)
895 1.2.2.2 pgoyette result = httpd->mgr->render_404(httpd->url, NULL,
896 1.2.2.2 pgoyette httpd->querystring,
897 1.2.2.2 pgoyette NULL, NULL,
898 1.2.2.2 pgoyette &httpd->retcode,
899 1.2.2.2 pgoyette &httpd->retmsg,
900 1.2.2.2 pgoyette &httpd->mimetype,
901 1.2.2.2 pgoyette &httpd->bodybuffer,
902 1.2.2.2 pgoyette &httpd->freecb,
903 1.2.2.2 pgoyette &httpd->freecb_arg);
904 1.2.2.2 pgoyette else
905 1.2.2.2 pgoyette result = url->action(httpd->url, url,
906 1.2.2.2 pgoyette httpd->querystring,
907 1.2.2.2 pgoyette httpd->headers,
908 1.2.2.2 pgoyette url->action_arg,
909 1.2.2.2 pgoyette &httpd->retcode, &httpd->retmsg,
910 1.2.2.2 pgoyette &httpd->mimetype, &httpd->bodybuffer,
911 1.2.2.2 pgoyette &httpd->freecb, &httpd->freecb_arg);
912 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS) {
913 1.2.2.2 pgoyette result = httpd->mgr->render_500(httpd->url, url,
914 1.2.2.2 pgoyette httpd->querystring,
915 1.2.2.2 pgoyette NULL, NULL,
916 1.2.2.2 pgoyette &httpd->retcode,
917 1.2.2.2 pgoyette &httpd->retmsg,
918 1.2.2.2 pgoyette &httpd->mimetype,
919 1.2.2.2 pgoyette &httpd->bodybuffer,
920 1.2.2.2 pgoyette &httpd->freecb,
921 1.2.2.2 pgoyette &httpd->freecb_arg);
922 1.2.2.2 pgoyette RUNTIME_CHECK(result == ISC_R_SUCCESS);
923 1.2.2.2 pgoyette }
924 1.2.2.2 pgoyette
925 1.2.2.2 pgoyette #ifdef HAVE_ZLIB
926 1.2.2.2 pgoyette if (httpd->flags & HTTPD_ACCEPT_DEFLATE) {
927 1.2.2.2 pgoyette result = isc_httpd_compress(httpd);
928 1.2.2.2 pgoyette if (result == ISC_R_SUCCESS) {
929 1.2.2.2 pgoyette is_compressed = ISC_TRUE;
930 1.2.2.2 pgoyette }
931 1.2.2.2 pgoyette }
932 1.2.2.2 pgoyette #endif
933 1.2.2.2 pgoyette
934 1.2.2.2 pgoyette isc_httpd_response(httpd);
935 1.2.2.2 pgoyette if ((httpd->flags & HTTPD_KEEPALIVE) != 0)
936 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Connection", "Keep-Alive");
937 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
938 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Date", datebuf);
939 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Expires", datebuf);
940 1.2.2.2 pgoyette
941 1.2.2.2 pgoyette if (url != NULL && url->isstatic) {
942 1.2.2.2 pgoyette char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
943 1.2.2.2 pgoyette isc_time_formathttptimestamp(&url->loadtime,
944 1.2.2.2 pgoyette loadbuf, sizeof(loadbuf));
945 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Last-Modified", loadbuf);
946 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Cache-Control: public", NULL);
947 1.2.2.2 pgoyette } else {
948 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Last-Modified", datebuf);
949 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
950 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
951 1.2.2.2 pgoyette }
952 1.2.2.2 pgoyette
953 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Server: libisc", NULL);
954 1.2.2.2 pgoyette
955 1.2.2.2 pgoyette if (is_compressed == ISC_TRUE) {
956 1.2.2.2 pgoyette isc_httpd_addheader(httpd, "Content-Encoding", "deflate");
957 1.2.2.2 pgoyette isc_httpd_addheaderuint(httpd, "Content-Length",
958 1.2.2.2 pgoyette isc_buffer_usedlength(&httpd->compbuffer));
959 1.2.2.2 pgoyette } else {
960 1.2.2.2 pgoyette isc_httpd_addheaderuint(httpd, "Content-Length",
961 1.2.2.2 pgoyette isc_buffer_usedlength(&httpd->bodybuffer));
962 1.2.2.2 pgoyette }
963 1.2.2.2 pgoyette
964 1.2.2.2 pgoyette isc_httpd_endheaders(httpd); /* done */
965 1.2.2.2 pgoyette
966 1.2.2.2 pgoyette ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
967 1.2.2.2 pgoyette /*
968 1.2.2.2 pgoyette * Link the data buffer into our send queue, should we have any data
969 1.2.2.2 pgoyette * rendered into it. If no data is present, we won't do anything
970 1.2.2.2 pgoyette * with the buffer.
971 1.2.2.2 pgoyette */
972 1.2.2.2 pgoyette if (is_compressed == ISC_TRUE) {
973 1.2.2.2 pgoyette ISC_LIST_APPEND(httpd->bufflist, &httpd->compbuffer, link);
974 1.2.2.2 pgoyette } else {
975 1.2.2.2 pgoyette if (isc_buffer_length(&httpd->bodybuffer) > 0) {
976 1.2.2.2 pgoyette ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
977 1.2.2.2 pgoyette }
978 1.2.2.2 pgoyette }
979 1.2.2.2 pgoyette
980 1.2.2.2 pgoyette /* check return code? */
981 1.2.2.2 pgoyette (void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
982 1.2.2.2 pgoyette isc_httpd_senddone, httpd);
983 1.2.2.2 pgoyette
984 1.2.2.2 pgoyette out:
985 1.2.2.2 pgoyette isc_event_free(&ev);
986 1.2.2.2 pgoyette EXIT("recv");
987 1.2.2.2 pgoyette }
988 1.2.2.2 pgoyette
989 1.2.2.2 pgoyette void
990 1.2.2.2 pgoyette isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
991 1.2.2.2 pgoyette isc_httpdmgr_t *httpdmgr;
992 1.2.2.2 pgoyette isc_httpd_t *httpd;
993 1.2.2.2 pgoyette httpdmgr = *httpdmgrp;
994 1.2.2.2 pgoyette *httpdmgrp = NULL;
995 1.2.2.2 pgoyette
996 1.2.2.2 pgoyette ENTER("isc_httpdmgr_shutdown");
997 1.2.2.2 pgoyette
998 1.2.2.2 pgoyette LOCK(&httpdmgr->lock);
999 1.2.2.2 pgoyette
1000 1.2.2.2 pgoyette MSETSHUTTINGDOWN(httpdmgr);
1001 1.2.2.2 pgoyette
1002 1.2.2.2 pgoyette isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
1003 1.2.2.2 pgoyette
1004 1.2.2.2 pgoyette httpd = ISC_LIST_HEAD(httpdmgr->running);
1005 1.2.2.2 pgoyette while (httpd != NULL) {
1006 1.2.2.2 pgoyette isc_socket_cancel(httpd->sock, httpdmgr->task,
1007 1.2.2.2 pgoyette ISC_SOCKCANCEL_ALL);
1008 1.2.2.2 pgoyette httpd = ISC_LIST_NEXT(httpd, link);
1009 1.2.2.2 pgoyette }
1010 1.2.2.2 pgoyette
1011 1.2.2.2 pgoyette UNLOCK(&httpdmgr->lock);
1012 1.2.2.2 pgoyette
1013 1.2.2.2 pgoyette EXIT("isc_httpdmgr_shutdown");
1014 1.2.2.2 pgoyette }
1015 1.2.2.2 pgoyette
1016 1.2.2.2 pgoyette static isc_result_t
1017 1.2.2.2 pgoyette grow_headerspace(isc_httpd_t *httpd) {
1018 1.2.2.2 pgoyette char *newspace;
1019 1.2.2.2 pgoyette unsigned int newlen;
1020 1.2.2.2 pgoyette isc_region_t r;
1021 1.2.2.2 pgoyette
1022 1.2.2.2 pgoyette isc_buffer_region(&httpd->headerbuffer, &r);
1023 1.2.2.2 pgoyette newlen = r.length + HTTP_SENDGROW;
1024 1.2.2.2 pgoyette if (newlen > HTTP_SEND_MAXLEN)
1025 1.2.2.2 pgoyette return (ISC_R_NOSPACE);
1026 1.2.2.2 pgoyette
1027 1.2.2.2 pgoyette newspace = isc_mem_get(httpd->mgr->mctx, newlen);
1028 1.2.2.2 pgoyette if (newspace == NULL)
1029 1.2.2.2 pgoyette return (ISC_R_NOMEMORY);
1030 1.2.2.2 pgoyette
1031 1.2.2.2 pgoyette isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
1032 1.2.2.2 pgoyette
1033 1.2.2.2 pgoyette isc_mem_put(httpd->mgr->mctx, r.base, r.length);
1034 1.2.2.2 pgoyette
1035 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
1036 1.2.2.2 pgoyette }
1037 1.2.2.2 pgoyette
1038 1.2.2.2 pgoyette isc_result_t
1039 1.2.2.2 pgoyette isc_httpd_response(isc_httpd_t *httpd) {
1040 1.2.2.2 pgoyette isc_result_t result;
1041 1.2.2.2 pgoyette unsigned int needlen;
1042 1.2.2.2 pgoyette
1043 1.2.2.2 pgoyette needlen = strlen(httpd->protocol) + 1; /* protocol + space */
1044 1.2.2.2 pgoyette needlen += 3 + 1; /* room for response code, always 3 bytes */
1045 1.2.2.2 pgoyette needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
1046 1.2.2.2 pgoyette
1047 1.2.2.2 pgoyette while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
1048 1.2.2.2 pgoyette result = grow_headerspace(httpd);
1049 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS)
1050 1.2.2.2 pgoyette return (result);
1051 1.2.2.2 pgoyette }
1052 1.2.2.2 pgoyette
1053 1.2.2.2 pgoyette return (isc_buffer_printf(&httpd->headerbuffer, "%s %03u %s\r\n",
1054 1.2.2.2 pgoyette httpd->protocol, httpd->retcode,
1055 1.2.2.2 pgoyette httpd->retmsg));
1056 1.2.2.2 pgoyette }
1057 1.2.2.2 pgoyette
1058 1.2.2.2 pgoyette isc_result_t
1059 1.2.2.2 pgoyette isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
1060 1.2.2.2 pgoyette const char *val)
1061 1.2.2.2 pgoyette {
1062 1.2.2.2 pgoyette isc_result_t result;
1063 1.2.2.2 pgoyette unsigned int needlen;
1064 1.2.2.2 pgoyette
1065 1.2.2.2 pgoyette needlen = strlen(name); /* name itself */
1066 1.2.2.2 pgoyette if (val != NULL)
1067 1.2.2.2 pgoyette needlen += 2 + strlen(val); /* :<space> and val */
1068 1.2.2.2 pgoyette needlen += 2; /* CRLF */
1069 1.2.2.2 pgoyette
1070 1.2.2.2 pgoyette while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
1071 1.2.2.2 pgoyette result = grow_headerspace(httpd);
1072 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS)
1073 1.2.2.2 pgoyette return (result);
1074 1.2.2.2 pgoyette }
1075 1.2.2.2 pgoyette
1076 1.2.2.2 pgoyette if (val != NULL) {
1077 1.2.2.2 pgoyette return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n",
1078 1.2.2.2 pgoyette name, val));
1079 1.2.2.2 pgoyette } else {
1080 1.2.2.2 pgoyette return (isc_buffer_printf(&httpd->headerbuffer, "%s\r\n",
1081 1.2.2.2 pgoyette name));
1082 1.2.2.2 pgoyette }
1083 1.2.2.2 pgoyette }
1084 1.2.2.2 pgoyette
1085 1.2.2.2 pgoyette isc_result_t
1086 1.2.2.2 pgoyette isc_httpd_endheaders(isc_httpd_t *httpd) {
1087 1.2.2.2 pgoyette isc_result_t result;
1088 1.2.2.2 pgoyette
1089 1.2.2.2 pgoyette while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
1090 1.2.2.2 pgoyette result = grow_headerspace(httpd);
1091 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS)
1092 1.2.2.2 pgoyette return (result);
1093 1.2.2.2 pgoyette }
1094 1.2.2.2 pgoyette
1095 1.2.2.2 pgoyette return (isc_buffer_printf(&httpd->headerbuffer, "\r\n"));
1096 1.2.2.2 pgoyette }
1097 1.2.2.2 pgoyette
1098 1.2.2.2 pgoyette isc_result_t
1099 1.2.2.2 pgoyette isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
1100 1.2.2.2 pgoyette isc_result_t result;
1101 1.2.2.2 pgoyette unsigned int needlen;
1102 1.2.2.2 pgoyette char buf[sizeof "18446744073709551616"];
1103 1.2.2.2 pgoyette
1104 1.2.2.2 pgoyette snprintf(buf, sizeof(buf), "%d", val);
1105 1.2.2.2 pgoyette
1106 1.2.2.2 pgoyette needlen = strlen(name); /* name itself */
1107 1.2.2.2 pgoyette needlen += 2 + strlen(buf); /* :<space> and val */
1108 1.2.2.2 pgoyette needlen += 2; /* CRLF */
1109 1.2.2.2 pgoyette
1110 1.2.2.2 pgoyette while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
1111 1.2.2.2 pgoyette result = grow_headerspace(httpd);
1112 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS)
1113 1.2.2.2 pgoyette return (result);
1114 1.2.2.2 pgoyette }
1115 1.2.2.2 pgoyette
1116 1.2.2.2 pgoyette return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n", name,
1117 1.2.2.2 pgoyette buf));
1118 1.2.2.2 pgoyette }
1119 1.2.2.2 pgoyette
1120 1.2.2.2 pgoyette static void
1121 1.2.2.2 pgoyette isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) {
1122 1.2.2.2 pgoyette isc_httpd_t *httpd = ev->ev_arg;
1123 1.2.2.2 pgoyette isc_region_t r;
1124 1.2.2.2 pgoyette isc_socketevent_t *sev = (isc_socketevent_t *)ev;
1125 1.2.2.2 pgoyette
1126 1.2.2.2 pgoyette ENTER("senddone");
1127 1.2.2.2 pgoyette INSIST(ISC_HTTPD_ISSEND(httpd));
1128 1.2.2.2 pgoyette
1129 1.2.2.2 pgoyette /*
1130 1.2.2.2 pgoyette * First, unlink our header buffer from the socket's bufflist. This
1131 1.2.2.2 pgoyette * is sort of an evil hack, since we know our buffer will be there,
1132 1.2.2.2 pgoyette * and we know it's address, so we can just remove it directly.
1133 1.2.2.2 pgoyette */
1134 1.2.2.2 pgoyette NOTICE("senddone unlinked header");
1135 1.2.2.2 pgoyette ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
1136 1.2.2.2 pgoyette
1137 1.2.2.2 pgoyette /*
1138 1.2.2.2 pgoyette * We will always want to clean up our receive buffer, even if we
1139 1.2.2.2 pgoyette * got an error on send or we are shutting down.
1140 1.2.2.2 pgoyette *
1141 1.2.2.2 pgoyette * We will pass in the buffer only if there is data in it. If
1142 1.2.2.2 pgoyette * there is no data, we will pass in a NULL.
1143 1.2.2.2 pgoyette */
1144 1.2.2.2 pgoyette if (httpd->freecb != NULL) {
1145 1.2.2.2 pgoyette isc_buffer_t *b = NULL;
1146 1.2.2.2 pgoyette if (isc_buffer_length(&httpd->bodybuffer) > 0) {
1147 1.2.2.2 pgoyette b = &httpd->bodybuffer;
1148 1.2.2.2 pgoyette httpd->freecb(b, httpd->freecb_arg);
1149 1.2.2.2 pgoyette }
1150 1.2.2.2 pgoyette NOTICE("senddone free callback performed");
1151 1.2.2.2 pgoyette }
1152 1.2.2.2 pgoyette if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
1153 1.2.2.2 pgoyette ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
1154 1.2.2.2 pgoyette NOTICE("senddone body buffer unlinked");
1155 1.2.2.2 pgoyette } else if (ISC_LINK_LINKED(&httpd->compbuffer, link)) {
1156 1.2.2.2 pgoyette ISC_LIST_UNLINK(sev->bufferlist, &httpd->compbuffer, link);
1157 1.2.2.2 pgoyette NOTICE("senddone compressed data unlinked and freed");
1158 1.2.2.2 pgoyette }
1159 1.2.2.2 pgoyette
1160 1.2.2.2 pgoyette if (sev->result != ISC_R_SUCCESS) {
1161 1.2.2.2 pgoyette destroy_client(&httpd);
1162 1.2.2.2 pgoyette goto out;
1163 1.2.2.2 pgoyette }
1164 1.2.2.2 pgoyette
1165 1.2.2.2 pgoyette if ((httpd->flags & HTTPD_CLOSE) != 0) {
1166 1.2.2.2 pgoyette destroy_client(&httpd);
1167 1.2.2.2 pgoyette goto out;
1168 1.2.2.2 pgoyette }
1169 1.2.2.2 pgoyette
1170 1.2.2.2 pgoyette ISC_HTTPD_SETRECV(httpd);
1171 1.2.2.2 pgoyette
1172 1.2.2.2 pgoyette NOTICE("senddone restarting recv on socket");
1173 1.2.2.2 pgoyette
1174 1.2.2.2 pgoyette reset_client(httpd);
1175 1.2.2.2 pgoyette
1176 1.2.2.2 pgoyette r.base = (unsigned char *)httpd->recvbuf;
1177 1.2.2.2 pgoyette r.length = HTTP_RECVLEN - 1;
1178 1.2.2.2 pgoyette /* check return code? */
1179 1.2.2.2 pgoyette (void)isc_socket_recv(httpd->sock, &r, 1, task,
1180 1.2.2.2 pgoyette isc_httpd_recvdone, httpd);
1181 1.2.2.2 pgoyette
1182 1.2.2.2 pgoyette out:
1183 1.2.2.2 pgoyette isc_event_free(&ev);
1184 1.2.2.2 pgoyette EXIT("senddone");
1185 1.2.2.2 pgoyette }
1186 1.2.2.2 pgoyette
1187 1.2.2.2 pgoyette static void
1188 1.2.2.2 pgoyette reset_client(isc_httpd_t *httpd) {
1189 1.2.2.2 pgoyette /*
1190 1.2.2.2 pgoyette * Catch errors here. We MUST be in RECV mode, and we MUST NOT have
1191 1.2.2.2 pgoyette * any outstanding buffers. If we have buffers, we have a leak.
1192 1.2.2.2 pgoyette */
1193 1.2.2.2 pgoyette INSIST(ISC_HTTPD_ISRECV(httpd));
1194 1.2.2.2 pgoyette INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
1195 1.2.2.2 pgoyette INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
1196 1.2.2.2 pgoyette
1197 1.2.2.2 pgoyette httpd->recvbuf[0] = 0;
1198 1.2.2.2 pgoyette httpd->recvlen = 0;
1199 1.2.2.2 pgoyette httpd->headers = NULL;
1200 1.2.2.2 pgoyette httpd->method = ISC_HTTPD_METHODUNKNOWN;
1201 1.2.2.2 pgoyette httpd->url = NULL;
1202 1.2.2.2 pgoyette httpd->querystring = NULL;
1203 1.2.2.2 pgoyette httpd->protocol = NULL;
1204 1.2.2.2 pgoyette httpd->flags = 0;
1205 1.2.2.2 pgoyette
1206 1.2.2.2 pgoyette isc_buffer_clear(&httpd->headerbuffer);
1207 1.2.2.2 pgoyette isc_buffer_clear(&httpd->compbuffer);
1208 1.2.2.2 pgoyette isc_buffer_invalidate(&httpd->bodybuffer);
1209 1.2.2.2 pgoyette }
1210 1.2.2.2 pgoyette
1211 1.2.2.2 pgoyette isc_result_t
1212 1.2.2.2 pgoyette isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
1213 1.2.2.2 pgoyette isc_httpdaction_t *func, void *arg)
1214 1.2.2.2 pgoyette {
1215 1.2.2.2 pgoyette return (isc_httpdmgr_addurl2(httpdmgr, url, ISC_FALSE, func, arg));
1216 1.2.2.2 pgoyette }
1217 1.2.2.2 pgoyette
1218 1.2.2.2 pgoyette isc_result_t
1219 1.2.2.2 pgoyette isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url,
1220 1.2.2.2 pgoyette isc_boolean_t isstatic,
1221 1.2.2.2 pgoyette isc_httpdaction_t *func, void *arg)
1222 1.2.2.2 pgoyette {
1223 1.2.2.2 pgoyette isc_httpdurl_t *item;
1224 1.2.2.2 pgoyette
1225 1.2.2.2 pgoyette if (url == NULL) {
1226 1.2.2.2 pgoyette httpdmgr->render_404 = func;
1227 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
1228 1.2.2.2 pgoyette }
1229 1.2.2.2 pgoyette
1230 1.2.2.2 pgoyette item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
1231 1.2.2.2 pgoyette if (item == NULL)
1232 1.2.2.2 pgoyette return (ISC_R_NOMEMORY);
1233 1.2.2.2 pgoyette
1234 1.2.2.2 pgoyette item->url = isc_mem_strdup(httpdmgr->mctx, url);
1235 1.2.2.2 pgoyette if (item->url == NULL) {
1236 1.2.2.2 pgoyette isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
1237 1.2.2.2 pgoyette return (ISC_R_NOMEMORY);
1238 1.2.2.2 pgoyette }
1239 1.2.2.2 pgoyette
1240 1.2.2.2 pgoyette item->action = func;
1241 1.2.2.2 pgoyette item->action_arg = arg;
1242 1.2.2.2 pgoyette item->isstatic = isstatic;
1243 1.2.2.2 pgoyette isc_time_now(&item->loadtime);
1244 1.2.2.2 pgoyette
1245 1.2.2.2 pgoyette ISC_LINK_INIT(item, link);
1246 1.2.2.2 pgoyette ISC_LIST_APPEND(httpdmgr->urls, item, link);
1247 1.2.2.2 pgoyette
1248 1.2.2.2 pgoyette return (ISC_R_SUCCESS);
1249 1.2.2.2 pgoyette }
1250 1.2.2.2 pgoyette
1251 1.2.2.2 pgoyette void
1252 1.2.2.2 pgoyette isc_httpd_setfinishhook(void (*fn)(void))
1253 1.2.2.2 pgoyette {
1254 1.2.2.2 pgoyette finishhook = fn;
1255 1.2.2.2 pgoyette }
1256