Annotation of embedaddon/lighttpd/src/http-header-glue.c, revision 1.1.1.2
1.1.1.2 ! misho 1: #include "first.h"
! 2:
1.1 misho 3: #include "base.h"
4: #include "array.h"
5: #include "buffer.h"
6: #include "log.h"
7: #include "etag.h"
1.1.1.2 ! misho 8: #include "http_chunk.h"
1.1 misho 9: #include "response.h"
1.1.1.2 ! misho 10: #include "stat_cache.h"
1.1 misho 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:
1.1.1.2 ! misho 130: buffer_copy_buffer(o, con->uri.scheme);
1.1 misho 131: buffer_append_string_len(o, CONST_STR_LEN("://"));
1.1.1.2 ! misho 132: if (!buffer_is_empty(con->uri.authority)) {
1.1 misho 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:
1.1.1.2 ! misho 145: if (-1 == getsockname(con->fd, (struct sockaddr *)&our_addr, &our_addr_len)
! 146: || our_addr_len > (socklen_t)sizeof(our_addr)) {
1.1 misho 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:
1.1.1.2 ! misho 171: buffer_append_string_len(o, CONST_STR_LEN("["));
1.1 misho 172: buffer_append_string(o,
173: inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr,
174: dst, sizeof(dst)));
1.1.1.2 ! misho 175: buffer_append_string_len(o, CONST_STR_LEN("]"));
1.1 misho 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(":"));
1.1.1.2 ! misho 207: buffer_append_int(o, srv->srvconf.port);
1.1 misho 208: }
209: }
210: }
1.1.1.2 ! misho 211: buffer_append_string_encoded(o, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI);
1.1 misho 212: buffer_append_string_len(o, CONST_STR_LEN("/"));
1.1.1.2 ! misho 213: if (!buffer_string_is_empty(con->uri.query)) {
1.1 misho 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;
1.1.1.2 ! misho 245: buffer_string_prepare_copy(srv->mtime_cache[i].str, 1023);
1.1 misho 246: tm = gmtime(&(srv->mtime_cache[i].mtime));
1.1.1.2 ! misho 247: buffer_append_strftime(srv->mtime_cache[i].str, "%a, %d %b %Y %H:%M:%S GMT", tm);
1.1 misho 248:
249: return srv->mtime_cache[i].str;
250: }
251:
252:
253: int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
1.1.1.2 ! misho 254: int head_or_get =
! 255: ( HTTP_METHOD_GET == con->request.http_method
! 256: || HTTP_METHOD_HEAD == con->request.http_method);
1.1 misho 257: UNUSED(srv);
1.1.1.2 ! misho 258:
1.1 misho 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) {
1.1.1.2 ! misho 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) {
1.1 misho 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: }
1.1.1.2 ! misho 283: } else if (con->request.http_if_modified_since && head_or_get) {
! 284: /* last-modified handling */
1.1 misho 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;
1.1.1.2 ! misho 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: }
1.1 misho 762: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>