1: #include "first.h"
2:
3: #include "base.h"
4: #include "array.h"
5: #include "buffer.h"
6: #include "log.h"
7: #include "etag.h"
8: #include "http_chunk.h"
9: #include "response.h"
10: #include "stat_cache.h"
11:
12: #include <string.h>
13: #include <errno.h>
14:
15: #include <time.h>
16:
17: /*
18: * This was 'borrowed' from tcpdump.
19: *
20: *
21: * This is fun.
22: *
23: * In older BSD systems, socket addresses were fixed-length, and
24: * "sizeof (struct sockaddr)" gave the size of the structure.
25: * All addresses fit within a "struct sockaddr".
26: *
27: * In newer BSD systems, the socket address is variable-length, and
28: * there's an "sa_len" field giving the length of the structure;
29: * this allows socket addresses to be longer than 2 bytes of family
30: * and 14 bytes of data.
31: *
32: * Some commercial UNIXes use the old BSD scheme, some use the RFC 2553
33: * variant of the old BSD scheme (with "struct sockaddr_storage" rather
34: * than "struct sockaddr"), and some use the new BSD scheme.
35: *
36: * Some versions of GNU libc use neither scheme, but has an "SA_LEN()"
37: * macro that determines the size based on the address family. Other
38: * versions don't have "SA_LEN()" (as it was in drafts of RFC 2553
39: * but not in the final version). On the latter systems, we explicitly
40: * check the AF_ type to determine the length; we assume that on
41: * all those systems we have "struct sockaddr_storage".
42: */
43:
44: #ifdef HAVE_IPV6
45: # ifndef SA_LEN
46: # ifdef HAVE_SOCKADDR_SA_LEN
47: # define SA_LEN(addr) ((addr)->sa_len)
48: # else /* HAVE_SOCKADDR_SA_LEN */
49: # ifdef HAVE_STRUCT_SOCKADDR_STORAGE
50: static size_t get_sa_len(const struct sockaddr *addr) {
51: switch (addr->sa_family) {
52:
53: # ifdef AF_INET
54: case AF_INET:
55: return (sizeof (struct sockaddr_in));
56: # endif
57:
58: # ifdef AF_INET6
59: case AF_INET6:
60: return (sizeof (struct sockaddr_in6));
61: # endif
62:
63: default:
64: return (sizeof (struct sockaddr));
65:
66: }
67: }
68: # define SA_LEN(addr) (get_sa_len(addr))
69: # else /* HAVE_SOCKADDR_STORAGE */
70: # define SA_LEN(addr) (sizeof (struct sockaddr))
71: # endif /* HAVE_SOCKADDR_STORAGE */
72: # endif /* HAVE_SOCKADDR_SA_LEN */
73: # endif /* SA_LEN */
74: #endif
75:
76:
77:
78:
79: int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
80: data_string *ds;
81:
82: UNUSED(srv);
83:
84: if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
85: ds = data_response_init();
86: }
87: buffer_copy_string_len(ds->key, key, keylen);
88: buffer_copy_string_len(ds->value, value, vallen);
89:
90: array_insert_unique(con->response.headers, (data_unset *)ds);
91:
92: return 0;
93: }
94:
95: int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
96: data_string *ds;
97:
98: UNUSED(srv);
99:
100: /* if there already is a key by this name overwrite the value */
101: if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
102: buffer_copy_string(ds->value, value);
103:
104: return 0;
105: }
106:
107: return response_header_insert(srv, con, key, keylen, value, vallen);
108: }
109:
110: int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
111: data_string *ds;
112:
113: UNUSED(srv);
114:
115: /* if there already is a key by this name append the value */
116: if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
117: buffer_append_string_len(ds->value, CONST_STR_LEN(", "));
118: buffer_append_string_len(ds->value, value, vallen);
119: return 0;
120: }
121:
122: return response_header_insert(srv, con, key, keylen, value, vallen);
123: }
124:
125: int http_response_redirect_to_directory(server *srv, connection *con) {
126: buffer *o;
127:
128: o = buffer_init();
129:
130: buffer_copy_buffer(o, con->uri.scheme);
131: buffer_append_string_len(o, CONST_STR_LEN("://"));
132: if (!buffer_is_empty(con->uri.authority)) {
133: buffer_append_string_buffer(o, con->uri.authority);
134: } else {
135: /* get the name of the currently connected socket */
136: struct hostent *he;
137: #ifdef HAVE_IPV6
138: char hbuf[256];
139: #endif
140: sock_addr our_addr;
141: socklen_t our_addr_len;
142:
143: our_addr_len = sizeof(our_addr);
144:
145: if (-1 == getsockname(con->fd, (struct sockaddr *)&our_addr, &our_addr_len)
146: || our_addr_len > (socklen_t)sizeof(our_addr)) {
147: con->http_status = 500;
148:
149: log_error_write(srv, __FILE__, __LINE__, "ss",
150: "can't get sockname", strerror(errno));
151:
152: buffer_free(o);
153: return 0;
154: }
155:
156:
157: /* Lookup name: secondly try to get hostname for bind address */
158: switch(our_addr.plain.sa_family) {
159: #ifdef HAVE_IPV6
160: case AF_INET6:
161: if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6),
162: SA_LEN((const struct sockaddr *)&our_addr.ipv6),
163: hbuf, sizeof(hbuf), NULL, 0, 0)) {
164:
165: char dst[INET6_ADDRSTRLEN];
166:
167: log_error_write(srv, __FILE__, __LINE__,
168: "SSS", "NOTICE: getnameinfo failed: ",
169: strerror(errno), ", using ip-address instead");
170:
171: buffer_append_string_len(o, CONST_STR_LEN("["));
172: buffer_append_string(o,
173: inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr,
174: dst, sizeof(dst)));
175: buffer_append_string_len(o, CONST_STR_LEN("]"));
176: } else {
177: buffer_append_string(o, hbuf);
178: }
179: break;
180: #endif
181: case AF_INET:
182: if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) {
183: log_error_write(srv, __FILE__, __LINE__,
184: "SdS", "NOTICE: gethostbyaddr failed: ",
185: h_errno, ", using ip-address instead");
186:
187: buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr));
188: } else {
189: buffer_append_string(o, he->h_name);
190: }
191: break;
192: default:
193: log_error_write(srv, __FILE__, __LINE__,
194: "S", "ERROR: unsupported address-type");
195:
196: buffer_free(o);
197: return -1;
198: }
199:
200: {
201: unsigned short default_port = 80;
202: if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
203: default_port = 443;
204: }
205: if (default_port != srv->srvconf.port) {
206: buffer_append_string_len(o, CONST_STR_LEN(":"));
207: buffer_append_int(o, srv->srvconf.port);
208: }
209: }
210: }
211: buffer_append_string_encoded(o, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI);
212: buffer_append_string_len(o, CONST_STR_LEN("/"));
213: if (!buffer_string_is_empty(con->uri.query)) {
214: buffer_append_string_len(o, CONST_STR_LEN("?"));
215: buffer_append_string_buffer(o, con->uri.query);
216: }
217:
218: response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o));
219:
220: con->http_status = 301;
221: con->file_finished = 1;
222:
223: buffer_free(o);
224:
225: return 0;
226: }
227:
228: buffer * strftime_cache_get(server *srv, time_t last_mod) {
229: struct tm *tm;
230: size_t i;
231:
232: for (i = 0; i < FILE_CACHE_MAX; i++) {
233: /* found cache-entry */
234: if (srv->mtime_cache[i].mtime == last_mod) return srv->mtime_cache[i].str;
235:
236: /* found empty slot */
237: if (srv->mtime_cache[i].mtime == 0) break;
238: }
239:
240: if (i == FILE_CACHE_MAX) {
241: i = 0;
242: }
243:
244: srv->mtime_cache[i].mtime = last_mod;
245: buffer_string_prepare_copy(srv->mtime_cache[i].str, 1023);
246: tm = gmtime(&(srv->mtime_cache[i].mtime));
247: buffer_append_strftime(srv->mtime_cache[i].str, "%a, %d %b %Y %H:%M:%S GMT", tm);
248:
249: return srv->mtime_cache[i].str;
250: }
251:
252:
253: int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
254: int head_or_get =
255: ( HTTP_METHOD_GET == con->request.http_method
256: || HTTP_METHOD_HEAD == con->request.http_method);
257: UNUSED(srv);
258:
259: /*
260: * 14.26 If-None-Match
261: * [...]
262: * If none of the entity tags match, then the server MAY perform the
263: * requested method as if the If-None-Match header field did not exist,
264: * but MUST also ignore any If-Modified-Since header field(s) in the
265: * request. That is, if no entity tags match, then the server MUST NOT
266: * return a 304 (Not Modified) response.
267: */
268:
269: if (con->request.http_if_none_match) {
270: /* use strong etag checking for now: weak comparison must not be used
271: * for ranged requests
272: */
273: if (etag_is_equal(con->physical.etag, con->request.http_if_none_match, 0)) {
274: if (head_or_get) {
275: con->http_status = 304;
276: return HANDLER_FINISHED;
277: } else {
278: con->http_status = 412;
279: con->mode = DIRECT;
280: return HANDLER_FINISHED;
281: }
282: }
283: } else if (con->request.http_if_modified_since && head_or_get) {
284: /* last-modified handling */
285: size_t used_len;
286: char *semicolon;
287:
288: if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) {
289: used_len = strlen(con->request.http_if_modified_since);
290: } else {
291: used_len = semicolon - con->request.http_if_modified_since;
292: }
293:
294: if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) {
295: if ('\0' == mtime->ptr[used_len]) con->http_status = 304;
296: return HANDLER_FINISHED;
297: } else {
298: char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
299: time_t t_header, t_file;
300: struct tm tm;
301:
302: /* convert to timestamp */
303: if (used_len >= sizeof(buf)) return HANDLER_GO_ON;
304:
305: strncpy(buf, con->request.http_if_modified_since, used_len);
306: buf[used_len] = '\0';
307:
308: if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
309: /**
310: * parsing failed, let's get out of here
311: */
312: return HANDLER_GO_ON;
313: }
314: tm.tm_isdst = 0;
315: t_header = mktime(&tm);
316:
317: strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
318: tm.tm_isdst = 0;
319: t_file = mktime(&tm);
320:
321: if (t_file > t_header) return HANDLER_GO_ON;
322:
323: con->http_status = 304;
324: return HANDLER_FINISHED;
325: }
326: }
327:
328: return HANDLER_GO_ON;
329: }
330:
331:
332: static int http_response_parse_range(server *srv, connection *con, buffer *path, stat_cache_entry *sce) {
333: int multipart = 0;
334: int error;
335: off_t start, end;
336: const char *s, *minus;
337: char *boundary = "fkj49sn38dcn3";
338: data_string *ds;
339: buffer *content_type = NULL;
340:
341: start = 0;
342: end = sce->st.st_size - 1;
343:
344: con->response.content_length = 0;
345:
346: if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) {
347: content_type = ds->value;
348: }
349:
350: for (s = con->request.http_range, error = 0;
351: !error && *s && NULL != (minus = strchr(s, '-')); ) {
352: char *err;
353: off_t la, le;
354:
355: if (s == minus) {
356: /* -<stop> */
357:
358: le = strtoll(s, &err, 10);
359:
360: if (le == 0) {
361: /* RFC 2616 - 14.35.1 */
362:
363: con->http_status = 416;
364: error = 1;
365: } else if (*err == '\0') {
366: /* end */
367: s = err;
368:
369: end = sce->st.st_size - 1;
370: start = sce->st.st_size + le;
371: } else if (*err == ',') {
372: multipart = 1;
373: s = err + 1;
374:
375: end = sce->st.st_size - 1;
376: start = sce->st.st_size + le;
377: } else {
378: error = 1;
379: }
380:
381: } else if (*(minus+1) == '\0' || *(minus+1) == ',') {
382: /* <start>- */
383:
384: la = strtoll(s, &err, 10);
385:
386: if (err == minus) {
387: /* ok */
388:
389: if (*(err + 1) == '\0') {
390: s = err + 1;
391:
392: end = sce->st.st_size - 1;
393: start = la;
394:
395: } else if (*(err + 1) == ',') {
396: multipart = 1;
397: s = err + 2;
398:
399: end = sce->st.st_size - 1;
400: start = la;
401: } else {
402: error = 1;
403: }
404: } else {
405: /* error */
406: error = 1;
407: }
408: } else {
409: /* <start>-<stop> */
410:
411: la = strtoll(s, &err, 10);
412:
413: if (err == minus) {
414: le = strtoll(minus+1, &err, 10);
415:
416: /* RFC 2616 - 14.35.1 */
417: if (la > le) {
418: error = 1;
419: }
420:
421: if (*err == '\0') {
422: /* ok, end*/
423: s = err;
424:
425: end = le;
426: start = la;
427: } else if (*err == ',') {
428: multipart = 1;
429: s = err + 1;
430:
431: end = le;
432: start = la;
433: } else {
434: /* error */
435:
436: error = 1;
437: }
438: } else {
439: /* error */
440:
441: error = 1;
442: }
443: }
444:
445: if (!error) {
446: if (start < 0) start = 0;
447:
448: /* RFC 2616 - 14.35.1 */
449: if (end > sce->st.st_size - 1) end = sce->st.st_size - 1;
450:
451: if (start > sce->st.st_size - 1) {
452: error = 1;
453:
454: con->http_status = 416;
455: }
456: }
457:
458: if (!error) {
459: if (multipart) {
460: /* write boundary-header */
461: buffer *b = buffer_init();
462:
463: buffer_copy_string_len(b, CONST_STR_LEN("\r\n--"));
464: buffer_append_string(b, boundary);
465:
466: /* write Content-Range */
467: buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes "));
468: buffer_append_int(b, start);
469: buffer_append_string_len(b, CONST_STR_LEN("-"));
470: buffer_append_int(b, end);
471: buffer_append_string_len(b, CONST_STR_LEN("/"));
472: buffer_append_int(b, sce->st.st_size);
473:
474: buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: "));
475: buffer_append_string_buffer(b, content_type);
476:
477: /* write END-OF-HEADER */
478: buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n"));
479:
480: con->response.content_length += buffer_string_length(b);
481: chunkqueue_append_buffer(con->write_queue, b);
482: buffer_free(b);
483: }
484:
485: chunkqueue_append_file(con->write_queue, path, start, end - start + 1);
486: con->response.content_length += end - start + 1;
487: }
488: }
489:
490: /* something went wrong */
491: if (error) return -1;
492:
493: if (multipart) {
494: /* add boundary end */
495: buffer *b = buffer_init();
496:
497: buffer_copy_string_len(b, "\r\n--", 4);
498: buffer_append_string(b, boundary);
499: buffer_append_string_len(b, "--\r\n", 4);
500:
501: con->response.content_length += buffer_string_length(b);
502: chunkqueue_append_buffer(con->write_queue, b);
503: buffer_free(b);
504:
505: /* set header-fields */
506:
507: buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("multipart/byteranges; boundary="));
508: buffer_append_string(srv->tmp_buf, boundary);
509:
510: /* overwrite content-type */
511: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(srv->tmp_buf));
512: } else {
513: /* add Content-Range-header */
514:
515: buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("bytes "));
516: buffer_append_int(srv->tmp_buf, start);
517: buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("-"));
518: buffer_append_int(srv->tmp_buf, end);
519: buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/"));
520: buffer_append_int(srv->tmp_buf, sce->st.st_size);
521:
522: response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(srv->tmp_buf));
523: }
524:
525: /* ok, the file is set-up */
526: return 0;
527: }
528:
529:
530: void http_response_send_file (server *srv, connection *con, buffer *path) {
531: stat_cache_entry *sce = NULL;
532: buffer *mtime = NULL;
533: data_string *ds;
534: int allow_caching = (0 == con->http_status || 200 == con->http_status);
535:
536: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, path, &sce)) {
537: con->http_status = (errno == ENOENT) ? 404 : 403;
538:
539: log_error_write(srv, __FILE__, __LINE__, "sbsb",
540: "not a regular file:", con->uri.path,
541: "->", path);
542:
543: return;
544: }
545:
546: /* we only handline regular files */
547: #ifdef HAVE_LSTAT
548: if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
549: con->http_status = 403;
550:
551: if (con->conf.log_request_handling) {
552: log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction");
553: log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", path);
554: }
555:
556: return;
557: }
558: #endif
559: if (!S_ISREG(sce->st.st_mode)) {
560: con->http_status = 403;
561:
562: if (con->conf.log_file_not_found) {
563: log_error_write(srv, __FILE__, __LINE__, "sbsb",
564: "not a regular file:", con->uri.path,
565: "->", sce->name);
566: }
567:
568: return;
569: }
570:
571: /* mod_compress might set several data directly, don't overwrite them */
572:
573: /* set response content-type, if not set already */
574:
575: if (NULL == array_get_element(con->response.headers, "Content-Type")) {
576: if (buffer_string_is_empty(sce->content_type)) {
577: /* we are setting application/octet-stream, but also announce that
578: * this header field might change in the seconds few requests
579: *
580: * This should fix the aggressive caching of FF and the script download
581: * seen by the first installations
582: */
583: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));
584:
585: allow_caching = 0;
586: } else {
587: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
588: }
589: }
590:
591: if (con->conf.range_requests) {
592: response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
593: }
594:
595: if (allow_caching) {
596: if (con->etag_flags != 0 && !buffer_string_is_empty(sce->etag)) {
597: if (NULL == array_get_element(con->response.headers, "ETag")) {
598: /* generate e-tag */
599: etag_mutate(con->physical.etag, sce->etag);
600:
601: response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
602: }
603: }
604:
605: /* prepare header */
606: if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) {
607: mtime = strftime_cache_get(srv, sce->st.st_mtime);
608: response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
609: } else {
610: mtime = ds->value;
611: }
612:
613: if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
614: return;
615: }
616: }
617:
618: if (con->request.http_range && con->conf.range_requests
619: && (200 == con->http_status || 0 == con->http_status)
620: && NULL == array_get_element(con->response.headers, "Content-Encoding")) {
621: int do_range_request = 1;
622: /* check if we have a conditional GET */
623:
624: if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
625: /* if the value is the same as our ETag, we do a Range-request,
626: * otherwise a full 200 */
627:
628: if (ds->value->ptr[0] == '"') {
629: /**
630: * client wants a ETag
631: */
632: if (!con->physical.etag) {
633: do_range_request = 0;
634: } else if (!buffer_is_equal(ds->value, con->physical.etag)) {
635: do_range_request = 0;
636: }
637: } else if (!mtime) {
638: /**
639: * we don't have a Last-Modified and can match the If-Range:
640: *
641: * sending all
642: */
643: do_range_request = 0;
644: } else if (!buffer_is_equal(ds->value, mtime)) {
645: do_range_request = 0;
646: }
647: }
648:
649: if (do_range_request) {
650: /* content prepared, I'm done */
651: con->file_finished = 1;
652:
653: if (0 == http_response_parse_range(srv, con, path, sce)) {
654: con->http_status = 206;
655: }
656: return;
657: }
658: }
659:
660: /* if we are still here, prepare body */
661:
662: /* we add it here for all requests
663: * the HEAD request will drop it afterwards again
664: */
665: if (0 == sce->st.st_size || 0 == http_chunk_append_file(srv, con, path)) {
666: con->http_status = 200;
667: con->file_finished = 1;
668: } else {
669: con->http_status = 403;
670: }
671: }
672:
673: void http_response_xsendfile (server *srv, connection *con, buffer *path, const array *xdocroot) {
674: const int status = con->http_status;
675: int valid = 1;
676:
677: con->file_started = 1;
678:
679: /* reset Content-Length, if set by backend
680: * Content-Length might later be set to size of X-Sendfile static file,
681: * determined by open(), fstat() to reduces race conditions if the file
682: * is modified between stat() (stat_cache_get_entry()) and open(). */
683: if (con->parsed_response & HTTP_CONTENT_LENGTH) {
684: data_string *ds = (data_string *) array_get_element(con->response.headers, "Content-Length");
685: if (ds) buffer_reset(ds->value);
686: con->parsed_response &= ~HTTP_CONTENT_LENGTH;
687: con->response.content_length = -1;
688: }
689:
690: buffer_urldecode_path(path);
691: buffer_path_simplify(path, path);
692: if (con->conf.force_lowercase_filenames) {
693: buffer_to_lower(path);
694: }
695:
696: /* check that path is under xdocroot(s)
697: * - xdocroot should have trailing slash appended at config time
698: * - con->conf.force_lowercase_filenames is not a server-wide setting,
699: * and so can not be definitively applied to xdocroot at config time*/
700: if (xdocroot->used) {
701: size_t i, xlen = buffer_string_length(path);
702: for (i = 0; i < xdocroot->used; ++i) {
703: data_string *ds = (data_string *)xdocroot->data[i];
704: size_t dlen = buffer_string_length(ds->value);
705: if (dlen <= xlen
706: && (!con->conf.force_lowercase_filenames
707: ? 0 == memcmp(path->ptr, ds->value->ptr, dlen)
708: : 0 == strncasecmp(path->ptr, ds->value->ptr, dlen))) {
709: break;
710: }
711: }
712: if (i == xdocroot->used) {
713: log_error_write(srv, __FILE__, __LINE__, "SBs",
714: "X-Sendfile (", path,
715: ") not under configured x-sendfile-docroot(s)");
716: con->http_status = 403;
717: valid = 0;
718: }
719: }
720:
721: if (valid) http_response_send_file(srv, con, path);
722:
723: if (con->http_status >= 400 && status < 300) {
724: con->mode = DIRECT;
725: } else if (0 != status && 200 != status) {
726: con->http_status = status;
727: }
728: }
729:
730: void http_response_backend_error (server *srv, connection *con) {
731: UNUSED(srv);
732: if (con->file_started) {
733: /*(response might have been already started, kill the connection)*/
734: /*(mode == DIRECT to avoid later call to http_response_backend_done())*/
735: con->mode = DIRECT; /*(avoid sending final chunked block)*/
736: con->keep_alive = 0; /*(no keep-alive; final chunked block not sent)*/
737: con->file_finished = 1;
738: } /*(else error status set later by http_response_backend_done())*/
739: }
740:
741: void http_response_backend_done (server *srv, connection *con) {
742: /* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
743: * i.e. not called from handle_connection_close or connection_reset
744: * hooks, except maybe from errdoc handler, which later resets state)*/
745: switch (con->state) {
746: case CON_STATE_HANDLE_REQUEST:
747: case CON_STATE_READ_POST:
748: if (!con->file_started) {
749: /* Send an error if we haven't sent any data yet */
750: con->http_status = 500;
751: con->mode = DIRECT;
752: break;
753: } /* else fall through */
754: case CON_STATE_WRITE:
755: if (!con->file_finished) {
756: http_chunk_close(srv, con);
757: con->file_finished = 1;
758: }
759: default:
760: break;
761: }
762: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>