1 : /*
2 : * Copyright (C) 2004, 2005, 2006 James Antill
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, write to the Free Software
16 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 : *
18 : * email: james@and.org
19 : */
20 : /* main HTTPD appending APIs, output responses */
21 :
22 : #define EX_UTILS_NO_FUNCS 1
23 : #include "ex_utils.h"
24 :
25 : #include "mk.h"
26 :
27 : #include "vlg.h"
28 :
29 : #define HTTPD_HAVE_GLOBAL_OPTS 1
30 : #include "httpd.h"
31 : #include "httpd_policy.h"
32 :
33 : #include "base64.h"
34 :
35 : #if ! COMPILE_DEBUG
36 : # define HTTP_CONF_MMAP_LIMIT_MIN (16 * 1024) /* a couple of pages */
37 : # define HTTP_CONF_SAFE_PRINT_REQ TRUE
38 : #else
39 : # define HTTP_CONF_MMAP_LIMIT_MIN 8 /* debug... */
40 : # define HTTP_CONF_SAFE_PRINT_REQ FALSE
41 : #endif
42 : #define HTTP_CONF_MMAP_LIMIT_MAX (50 * 1024 * 1024)
43 :
44 : #define HTTPD_CONF_ZIP_LIMIT_MIN 8
45 :
46 : #define CLEN COMPILE_STRLEN
47 :
48 : /* is the cstr a prefix of the vstr */
49 : #define VPREFIX(vstr, p, l, cstr) \
50 : (((l) >= CLEN(cstr)) && \
51 : vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
52 : /* is the cstr a suffix of the vstr */
53 : #define VSUFFIX(vstr, p, l, cstr) \
54 : (((l) >= CLEN(cstr)) && \
55 : vstr_cmp_eod_buf_eq(vstr, p, l, cstr, CLEN(cstr)))
56 :
57 : /* is the cstr a prefix of the vstr, no case */
58 : #define VIPREFIX(vstr, p, l, cstr) \
59 : (((l) >= CLEN(cstr)) && \
60 : vstr_cmp_case_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
61 :
62 : /* for simplicity */
63 : #define VEQ(vstr, p, l, cstr) vstr_cmp_cstr_eq(vstr, p, l, cstr)
64 : #define VIEQ(vstr, p, l, cstr) vstr_cmp_case_cstr_eq(vstr, p, l, cstr)
65 :
66 : #define HTTP__HDR_SET(req, h, p, l) do { \
67 : (req)-> http_hdrs -> hdr_ ## h ->pos = (p); \
68 : (req)-> http_hdrs -> hdr_ ## h ->len = (l); \
69 : } while (FALSE)
70 : #define HTTP__HDR_MULTI_SET(req, h, p, l) do { \
71 : (req)-> http_hdrs -> multi -> hdr_ ## h ->pos = (p); \
72 : (req)-> http_hdrs -> multi -> hdr_ ## h ->len = (l); \
73 : } while (FALSE)
74 :
75 : #define HTTP__XTRA_HDR_INIT(x) do { \
76 : req-> x ## _vs1 = NULL; \
77 : req-> x ## _pos = 0; \
78 : req-> x ## _len = 0; \
79 : } while (FALSE)
80 :
81 : #include <syslog.h>
82 :
83 : static Vlg *vlg = NULL;
84 :
85 :
86 : void httpd_app_init(Vlg *passed_vlg)
87 64 : {
88 32 : ASSERT(passed_vlg && !vlg);
89 64 : vlg = passed_vlg;
90 64 : }
91 :
92 : void httpd_app_exit(void)
93 44 : {
94 22 : ASSERT(vlg);
95 44 : vlg = NULL;
96 44 : }
97 :
98 :
99 : static void http__app_hdr_hdr(Vstr_base *out, const char *hdr)
100 235315 : {
101 235315 : vstr_add_cstr_buf(out, out->len, hdr);
102 235315 : vstr_add_cstr_buf(out, out->len, ": ");
103 235315 : }
104 : static void http__app_hdr_eol(Vstr_base *out)
105 275290 : {
106 275290 : vstr_add_cstr_buf(out, out->len, HTTP_EOL);
107 275290 : }
108 :
109 : void http_app_hdr_cstr(Vstr_base *out, const char *hdr, const char *data)
110 94668 : {
111 94668 : http__app_hdr_hdr(out, hdr);
112 94668 : vstr_add_cstr_buf(out, out->len, data);
113 94668 : http__app_hdr_eol(out);
114 94668 : }
115 :
116 : void http_app_hdr_vstr(Vstr_base *out, const char *hdr,
117 : const Vstr_base *s1, size_t vpos, size_t vlen,
118 : unsigned int type)
119 3648 : {
120 3648 : if (!vlen) return; /* don't allow "empty" headers, use a single space */
121 :
122 3648 : http__app_hdr_hdr(out, hdr);
123 3648 : vstr_add_vstr(out, out->len, s1, vpos, vlen, type);
124 3648 : http__app_hdr_eol(out);
125 : }
126 :
127 : void http_app_hdr_vstr_def(Vstr_base *out, const char *hdr,
128 : const Vstr_base *s1, size_t vpos, size_t vlen)
129 25944 : {
130 25944 : if (!vlen) return; /* don't allow "empty" headers, use a single space */
131 :
132 25944 : http__app_hdr_hdr(out, hdr);
133 25944 : vstr_add_vstr(out, out->len, s1, vpos, vlen, VSTR_TYPE_ADD_DEF);
134 25944 : http__app_hdr_eol(out);
135 : }
136 :
137 : void http_app_hdr_conf_vstr(Vstr_base *out, const char *hdr,
138 : const Vstr_base *s1)
139 39559 : {
140 39559 : if (!s1->len) return; /* don't allow "empty" headers, a single space */
141 :
142 39143 : http__app_hdr_hdr(out, hdr);
143 39143 : vstr_add_vstr(out, out->len, s1, 1, s1->len, VSTR_TYPE_ADD_DEF);
144 39143 : http__app_hdr_eol(out);
145 : }
146 :
147 : /* convert a 4byte number into 4 bytes... */
148 : #define APP_BUF4(x) do { \
149 : buf[(x * 4) + 0] = (num[x] >> 24) & 0xFF; \
150 : buf[(x * 4) + 1] = (num[x] >> 16) & 0xFF; \
151 : buf[(x * 4) + 2] = (num[x] >> 8) & 0xFF; \
152 : buf[(x * 4) + 3] = (num[x] >> 0) & 0xFF; \
153 : } while (FALSE)
154 : static void http_app_hdr_vstr_md5(struct Con *con, Httpd_req_data *req,
155 : const Vstr_base *s1, size_t vpos, size_t vlen)
156 1088 : {
157 1088 : Vstr_base *out = con->evnt->io_w;
158 :
159 1088 : if (!vlen) return; /* don't allow "empty" headers, use a single space */
160 :
161 1088 : http__app_hdr_hdr(out, "Content-MD5");
162 :
163 1088 : if (vlen == (128 / 8)) /* raw bytes ... */
164 704 : vstr_x_conv_base64_encode(out, out->len, s1, vpos, vlen, 0);
165 384 : else if (vlen != (128 / 4)) /* just output, and hope... */
166 0 : vstr_add_vstr(out, out->len, s1, vpos, vlen, VSTR_TYPE_ADD_DEF);
167 : else /* hex encoded bytes... */
168 : { /* NOTE: lots of work, should be compiled to raw bytes in the conf */
169 384 : Vstr_base *xc = req->xtra_content;
170 384 : size_t pos = 0;
171 384 : unsigned int nflags = VSTR_FLAG_PARSE_NUM_NO_BEG_PM;
172 : unsigned int num[4];
173 : unsigned char buf[128 / 8];
174 :
175 192 : ASSERT(xc); /* must be true, if the MD5 headers are used */
176 :
177 384 : num[0] = vstr_parse_uint(s1, vpos + 0, 8, 16 | nflags, NULL, NULL);
178 384 : num[1] = vstr_parse_uint(s1, vpos + 8, 8, 16 | nflags, NULL, NULL);
179 384 : num[2] = vstr_parse_uint(s1, vpos + 16, 8, 16 | nflags, NULL, NULL);
180 384 : num[3] = vstr_parse_uint(s1, vpos + 24, 8, 16 | nflags, NULL, NULL);
181 :
182 384 : APP_BUF4(0);
183 384 : APP_BUF4(1);
184 384 : APP_BUF4(2);
185 384 : APP_BUF4(3);
186 :
187 384 : pos = xc->len + 1;
188 384 : if (!vstr_add_buf(xc, xc->len, buf, sizeof(buf)))
189 0 : return;
190 :
191 : /* raw bytes now... */
192 192 : ASSERT(vstr_sc_posdiff(pos, xc->len) == sizeof(buf));
193 :
194 384 : vstr_x_conv_base64_encode(out, out->len, xc, pos, sizeof(buf), 0);
195 : }
196 :
197 1088 : http__app_hdr_eol(out);
198 : }
199 : #undef APP_BUF4
200 :
201 : void http_app_hdr_fmt(Vstr_base *out, const char *hdr, const char *fmt, ...)
202 5880 : {
203 : va_list ap;
204 :
205 5880 : http__app_hdr_hdr(out, hdr);
206 :
207 5880 : va_start(ap, fmt);
208 5880 : vstr_add_vfmt(out, out->len, fmt, ap);
209 5880 : va_end(ap);
210 :
211 5880 : http__app_hdr_eol(out);
212 5880 : }
213 :
214 : void http_app_hdr_uintmax(Vstr_base *out, const char *hdr, uintmax_t data)
215 39367 : {
216 39367 : http__app_hdr_hdr(out, hdr);
217 39367 : vstr_add_fmt(out, out->len, "%ju", data);
218 39367 : http__app_hdr_eol(out);
219 39367 : }
220 :
221 : #define HTTP__VARY_ADD(x, y) do { \
222 : ASSERT((x) < (sizeof(varies_ptr) / sizeof(varies_ptr[0]))); \
223 : \
224 : varies_ptr[(x)] = (y); \
225 : varies_len[(x)] = CLEN(y); \
226 : ++(x); \
227 : } while (FALSE)
228 :
229 : void http_app_def_hdrs(struct Con *con, struct Httpd_req_data *req,
230 : unsigned int http_ret_code,
231 : const char *http_ret_line, time_t mtime,
232 : const char *custom_content_type,
233 : int use_range,
234 : uintmax_t content_length)
235 39559 : {
236 39559 : Vstr_base *out = con->evnt->io_w;
237 39559 : Date_store *ds = httpd_opts->date;
238 :
239 39559 : if (use_range)
240 39247 : use_range = req->policy->use_range;
241 :
242 39559 : vstr_add_fmt(out, out->len, "%s %03u %s" HTTP_EOL,
243 : "HTTP/1.1", http_ret_code, http_ret_line);
244 39559 : http_app_hdr_cstr(out, "Date", date_rfc1123(ds, req->now));
245 39559 : http_app_hdr_conf_vstr(out, "Server", req->policy->server_name);
246 :
247 39559 : if (mtime)
248 : { /* if mtime in future, chop it #14.29
249 : * for cache validation we don't cmp last-modified == now either */
250 39031 : if (difftime(req->now, mtime) <= 0)
251 1564 : mtime = req->now;
252 39031 : http_app_hdr_cstr(out, "Last-Modified", date_rfc1123(ds, mtime));
253 : }
254 :
255 39559 : switch (con->keep_alive)
256 : {
257 : case HTTP_NON_KEEP_ALIVE:
258 11198 : HTTP_APP_HDR_CONST_CSTR(out, "Connection", "close");
259 11198 : break;
260 :
261 : case HTTP_1_0_KEEP_ALIVE:
262 2432 : HTTP_APP_HDR_CONST_CSTR(out, "Connection", "Keep-Alive");
263 : /* FALLTHROUGH */
264 : case HTTP_1_1_KEEP_ALIVE:
265 28361 : if (req->policy->output_keep_alive_hdr)
266 : {
267 1440 : if (!req->policy->max_requests)
268 0 : http_app_hdr_fmt(out, "Keep-Alive",
269 : "%s=%u", "timeout", req->policy->s->idle_timeout);
270 : else
271 : {
272 : unsigned int left = (req->policy->max_requests -
273 1440 : con->evnt->acct.req_got);
274 :
275 1440 : http_app_hdr_fmt(out, "Keep-Alive",
276 : "%s=%u, %s=%u",
277 : "timeout", req->policy->s->idle_timeout,
278 : "max", left);
279 : }
280 : }
281 :
282 0 : ASSERT_NO_SWITCH_DEF();
283 : }
284 :
285 39559 : switch (http_ret_code)
286 : {
287 : case 200: /* OK */
288 : /* case 206: */ /* OK - partial -- needed? */
289 : case 302: case 307: /* Tmp Redirects */
290 : case 304: /* Not modified */
291 : case 406: /* Not accept - a */
292 : case 412: /* Not accept - precondition */
293 : case 413: /* Too large */
294 : case 416: /* Bad range */
295 : case 417: /* Not accept - expect contained something */
296 22169 : if (use_range && req->policy->use_adv_range)
297 21217 : HTTP_APP_HDR_CONST_CSTR(out, "Accept-Ranges", "bytes");
298 : }
299 :
300 39559 : if (req->vary_star)
301 32 : HTTP_APP_HDR_CONST_CSTR(out, "Vary", "*");
302 39527 : else if (req->vary_a || req->vary_ac || req->vary_ae || req->vary_al ||
303 : req->vary_rf || req->vary_ua ||
304 : req->vary_ims || req->vary_ius || req->vary_ir ||
305 : req->vary_im || req->vary_inm || req->vary_xm)
306 : {
307 : const char *varies_ptr[11];
308 : size_t varies_len[12];
309 25577 : unsigned int num = 0;
310 :
311 25577 : if (req->vary_xm) HTTP__VARY_ADD(num, "X-Moz");
312 25577 : if (req->vary_ua) HTTP__VARY_ADD(num, "User-Agent");
313 25577 : if (req->vary_rf) HTTP__VARY_ADD(num, "Referer");
314 25577 : if (req->vary_ius) HTTP__VARY_ADD(num, "If-Unmodified-Since");
315 25577 : if (req->vary_ir) HTTP__VARY_ADD(num, "If-Range");
316 25577 : if (req->vary_inm) HTTP__VARY_ADD(num, "If-None-Match");
317 25577 : if (req->vary_ims) HTTP__VARY_ADD(num, "If-Modified-Since");
318 25577 : if (req->vary_im) HTTP__VARY_ADD(num, "If-Match");
319 25577 : if (req->vary_al) HTTP__VARY_ADD(num, "Accept-Language");
320 25577 : if (req->vary_ae) HTTP__VARY_ADD(num, "Accept-Encoding");
321 25577 : if (req->vary_ac) HTTP__VARY_ADD(num, "Accept-Charset");
322 25577 : if (req->vary_a) HTTP__VARY_ADD(num, "Accept");
323 :
324 12796 : ASSERT(num && (num <= 12));
325 :
326 25577 : http__app_hdr_hdr(out, "Vary");
327 57266 : while (num-- > 1)
328 : {
329 6112 : vstr_add_buf(out, out->len, varies_ptr[num], varies_len[num]);
330 6112 : vstr_add_cstr_buf(out, out->len, ",");
331 : }
332 12796 : ASSERT(num == 0);
333 :
334 25577 : vstr_add_buf(out, out->len, varies_ptr[0], varies_len[0]);
335 25577 : http__app_hdr_eol(out);
336 : }
337 :
338 39559 : if (con->use_mpbr)
339 : {
340 96 : ASSERT(con->mpbr_ct && !con->mpbr_ct->len);
341 192 : if (req->content_type_vs1 && req->content_type_len)
342 192 : http_app_hdr_vstr_def(con->mpbr_ct, "Content-Type",
343 : HTTP__XTRA_HDR_PARAMS(req, content_type));
344 192 : HTTP_APP_HDR_CONST_CSTR(out, "Content-Type",
345 : "multipart/byteranges; boundary=SEP");
346 : }
347 59935 : else if (req->content_type_vs1 && req->content_type_len)
348 20568 : http_app_hdr_vstr_def(out, "Content-Type",
349 : HTTP__XTRA_HDR_PARAMS(req, content_type));
350 18799 : else if (custom_content_type) /* possible we don't send one */
351 16078 : http_app_hdr_cstr(out, "Content-Type", custom_content_type);
352 :
353 39559 : if (req->content_encoding_bzip2)
354 496 : HTTP_APP_HDR_CONST_CSTR(out, "Content-Encoding", "bzip2");
355 39063 : else if (req->content_encoding_gzip)
356 : {
357 1808 : if (req->content_encoding_xgzip)
358 144 : HTTP_APP_HDR_CONST_CSTR(out, "Content-Encoding", "x-gzip");
359 : else
360 1664 : HTTP_APP_HDR_CONST_CSTR(out, "Content-Encoding", "gzip");
361 : }
362 :
363 39559 : if (!con->use_mpbr)
364 39367 : http_app_hdr_uintmax(out, "Content-Length", content_length);
365 39559 : }
366 : #undef HTTP__VARY_ADD
367 :
368 : void http_app_end_hdrs(Vstr_base *out)
369 39975 : { /* apache contains a workaround for buggy Netscape 2.x, 3.x and 4.0beta */
370 39975 : http__app_hdr_eol(out);
371 39975 : }
372 :
373 : void http_app_hdrs_url(struct Con *con, Httpd_req_data *req)
374 27209 : {
375 27209 : Vstr_base *out = con->evnt->io_w;
376 :
377 27209 : if (HTTPD_VER_GE_1_1(req) && req->cache_control_vs1)
378 0 : http_app_hdr_vstr_def(out, "Cache-Control",
379 : HTTP__XTRA_HDR_PARAMS(req, cache_control));
380 27209 : if (req->expires_vs1 && (req->expires_time > req->f_stat->st_mtime))
381 0 : http_app_hdr_vstr_def(out, "Expires",
382 : HTTP__XTRA_HDR_PARAMS(req, expires));
383 27209 : if (req->p3p_vs1)
384 0 : http_app_hdr_vstr_def(out, "P3P",
385 : HTTP__XTRA_HDR_PARAMS(req, p3p));
386 27209 : }
387 :
388 : void http_app_hdrs_file(struct Con *con, Httpd_req_data *req)
389 23081 : {
390 23081 : Vstr_base *out = con->evnt->io_w;
391 23081 : time_t mtime = req->f_stat->st_mtime;
392 23081 : time_t enc_mtime = req->encoded_mtime;
393 :
394 23081 : if (req->content_disposition_vs1)
395 0 : http_app_hdr_vstr_def(out, "Content-Disposition",
396 : HTTP__XTRA_HDR_PARAMS(req, content_disposition));
397 23081 : if (req->content_language_vs1)
398 2272 : http_app_hdr_vstr_def(out, "Content-Language",
399 : HTTP__XTRA_HDR_PARAMS(req, content_language));
400 :
401 23081 : if (req->content_encoding_bzip2)
402 : {
403 496 : if (req->bzip2_content_md5_vs1 && (req->content_md5_time >= enc_mtime))
404 352 : http_app_hdr_vstr_md5(con, req,
405 : HTTP__XTRA_HDR_PARAMS(req, bzip2_content_md5));
406 : }
407 22585 : else if (req->content_encoding_gzip)
408 : {
409 1808 : if (req->gzip_content_md5_vs1 && (req->content_md5_time >= enc_mtime))
410 352 : http_app_hdr_vstr_md5(con, req,
411 : HTTP__XTRA_HDR_PARAMS(req, gzip_content_md5));
412 : }
413 20777 : else if (req->content_md5_vs1 && (req->content_md5_time >= mtime))
414 384 : http_app_hdr_vstr_md5(con, req,
415 : HTTP__XTRA_HDR_PARAMS(req, content_md5));
416 :
417 : /* we only obey IM and INM when in version 1.1+, but who cares... */
418 23081 : if (req->etag_vs1 && (req->etag_time >= mtime))
419 2720 : http_app_hdr_vstr_def(out, "ETag", HTTP__XTRA_HDR_PARAMS(req, etag));
420 :
421 23081 : if (req->link_vs1)
422 192 : http_app_hdr_vstr_def(out, "Link", HTTP__XTRA_HDR_PARAMS(req, link));
423 23081 : }
424 :
425 : void http_app_hdrs_mpbr(struct Con *con, struct File_sect *fs)
426 416 : {
427 416 : Vstr_base *out = con->evnt->io_w;
428 : uintmax_t range_beg;
429 : uintmax_t range_end;
430 :
431 208 : ASSERT(fs && (fs->fd != -1));
432 :
433 416 : range_beg = fs->off;
434 416 : range_end = range_beg + fs->len - 1;
435 :
436 416 : vstr_add_cstr_ptr(out, out->len, "--SEP" HTTP_EOL);
437 416 : HTTPD_APP_REF_ALLVSTR(out, con->mpbr_ct);
438 416 : http_app_hdr_fmt(out, "Content-Range",
439 : "%s %ju-%ju/%ju", "bytes", range_beg, range_end,
440 : con->mpbr_fs_len);
441 416 : http_app_end_hdrs(out);
442 416 : }
443 :
|