xref: /lighttpd1.4/src/http-header-glue.c (revision 34709192)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "array.h"
5 #include "buffer.h"
6 #include "fdevent.h"
7 #include "log.h"
8 #include "etag.h"
9 #include "http_chunk.h"
10 #include "http_header.h"
11 #include "response.h"
12 #include "sock_addr.h"
13 #include "stat_cache.h"
14 #include "settings.h"   /* MAX_HTTP_REQUEST_HEADER MAX_READ_LIMIT */
15 
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 
21 #include <time.h>
22 
23 #include "sys-socket.h"
24 #include <unistd.h>
25 
26 
27 static int http_response_buffer_append_authority(request_st * const r, buffer * const o) {
28 	if (!buffer_string_is_empty(&r->uri.authority)) {
29 		buffer_append_string_buffer(o, &r->uri.authority);
30 	} else {
31 		/* get the name of the currently connected socket */
32 		sock_addr our_addr;
33 		socklen_t our_addr_len;
34 
35 		our_addr.plain.sa_family = 0;
36 		our_addr_len = sizeof(our_addr);
37 
38 		if (-1 == getsockname(r->con->fd, (struct sockaddr *)&our_addr, &our_addr_len)
39 		    || our_addr_len > (socklen_t)sizeof(our_addr)) {
40 			r->http_status = 500;
41 			log_perror(r->conf.errh, __FILE__, __LINE__, "can't get sockname");
42 			return -1;
43 		}
44 
45 		if (our_addr.plain.sa_family == AF_INET
46 		    && our_addr.ipv4.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
47 			static char lhost[32];
48 			static size_t lhost_len = 0;
49 			if (0 != lhost_len) {
50 				buffer_append_string_len(o, lhost, lhost_len);
51 			}
52 			else {
53 				size_t olen = buffer_string_length(o);
54 				if (0 == sock_addr_nameinfo_append_buffer(o, &our_addr, r->conf.errh)) {
55 					lhost_len = buffer_string_length(o) - olen;
56 					if (lhost_len < sizeof(lhost)) {
57 						memcpy(lhost, o->ptr+olen, lhost_len+1); /*(+1 for '\0')*/
58 					}
59 					else {
60 						lhost_len = 0;
61 					}
62 				}
63 				else {
64 					lhost_len = sizeof("localhost")-1;
65 					memcpy(lhost, "localhost", lhost_len+1); /*(+1 for '\0')*/
66 					buffer_append_string_len(o, lhost, lhost_len);
67 				}
68 			}
69 		} else if (!buffer_string_is_empty(r->server_name)) {
70 			buffer_append_string_buffer(o, r->server_name);
71 		} else
72 		/* Lookup name: secondly try to get hostname for bind address */
73 		if (0 != sock_addr_nameinfo_append_buffer(o, &our_addr, r->conf.errh)) {
74 			r->http_status = 500;
75 			return -1;
76 		}
77 
78 		{
79 			unsigned short listen_port = sock_addr_get_port(&our_addr);
80 			unsigned short default_port = 80;
81 			if (buffer_is_equal_string(&r->uri.scheme, CONST_STR_LEN("https"))) {
82 				default_port = 443;
83 			}
84 			if (0 == listen_port) listen_port = r->con->srv->srvconf.port;
85 			if (default_port != listen_port) {
86 				buffer_append_string_len(o, CONST_STR_LEN(":"));
87 				buffer_append_int(o, listen_port);
88 			}
89 		}
90 	}
91 	return 0;
92 }
93 
94 int http_response_redirect_to_directory(request_st * const r, int status) {
95 	buffer *o = r->tmp_buf;
96 	buffer_copy_buffer(o, &r->uri.scheme);
97 	buffer_append_string_len(o, CONST_STR_LEN("://"));
98 	if (0 != http_response_buffer_append_authority(r, o)) {
99 		return -1;
100 	}
101 	buffer_append_string_encoded(o, CONST_BUF_LEN(&r->uri.path), ENCODING_REL_URI);
102 	buffer_append_string_len(o, CONST_STR_LEN("/"));
103 	if (!buffer_string_is_empty(&r->uri.query)) {
104 		buffer_append_string_len(o, CONST_STR_LEN("?"));
105 		buffer_append_string_buffer(o, &r->uri.query);
106 	}
107 
108 	if (status >= 300) {
109 		http_header_response_set(r, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location"), CONST_BUF_LEN(o));
110 		r->http_status = status;
111 		r->resp_body_finished = 1;
112 	}
113 	else {
114 		http_header_response_set(r, HTTP_HEADER_CONTENT_LOCATION, CONST_STR_LEN("Content-Location"), CONST_BUF_LEN(o));
115 	}
116 
117 	return 0;
118 }
119 
120 #define MTIME_CACHE_MAX 16
121 struct mtime_cache_type {
122     time_t mtime;  /* key */
123     buffer str;    /* buffer for string representation */
124 };
125 static struct mtime_cache_type mtime_cache[MTIME_CACHE_MAX];
126 static char mtime_cache_str[MTIME_CACHE_MAX][30];
127 /* 30-chars for "%a, %d %b %Y %H:%M:%S GMT" */
128 
129 void strftime_cache_reset(void) {
130     for (int i = 0; i < MTIME_CACHE_MAX; ++i) {
131         mtime_cache[i].mtime = (time_t)-1;
132         mtime_cache[i].str.ptr = mtime_cache_str[i];
133         mtime_cache[i].str.used = sizeof(mtime_cache_str[0]);
134         mtime_cache[i].str.size = sizeof(mtime_cache_str[0]);
135     }
136 }
137 
138 const buffer * strftime_cache_get(const time_t last_mod) {
139     static int mtime_cache_idx;
140 
141     for (int j = 0; j < MTIME_CACHE_MAX; ++j) {
142         if (mtime_cache[j].mtime == last_mod)
143             return &mtime_cache[j].str; /* found cache-entry */
144     }
145 
146     if (++mtime_cache_idx == MTIME_CACHE_MAX) mtime_cache_idx = 0;
147 
148     const int i = mtime_cache_idx;
149     mtime_cache[i].mtime = last_mod;
150     strftime(mtime_cache[i].str.ptr, sizeof(mtime_cache_str[0]),
151              "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime_cache[i].mtime));
152 
153     return &mtime_cache[i].str;
154 }
155 
156 
157 int http_response_handle_cachable(request_st * const r, const buffer * const mtime) {
158 	const buffer *vb;
159 
160 	/*
161 	 * 14.26 If-None-Match
162 	 *    [...]
163 	 *    If none of the entity tags match, then the server MAY perform the
164 	 *    requested method as if the If-None-Match header field did not exist,
165 	 *    but MUST also ignore any If-Modified-Since header field(s) in the
166 	 *    request. That is, if no entity tags match, then the server MUST NOT
167 	 *    return a 304 (Not Modified) response.
168 	 */
169 
170 	if ((vb = http_header_request_get(r, HTTP_HEADER_IF_NONE_MATCH, CONST_STR_LEN("If-None-Match")))) {
171 		/*(weak etag comparison must not be used for ranged requests)*/
172 		int range_request =
173 		  (r->conf.range_requests
174 		   && (200 == r->http_status || 0 == r->http_status)
175 		   && NULL != http_header_request_get(r, HTTP_HEADER_RANGE, CONST_STR_LEN("Range")));
176 		if (etag_is_equal(&r->physical.etag, vb->ptr, !range_request)) {
177 			if (http_method_get_or_head(r->http_method)) {
178 				r->http_status = 304;
179 				return HANDLER_FINISHED;
180 			} else {
181 				r->http_status = 412;
182 				r->handler_module = NULL;
183 				return HANDLER_FINISHED;
184 			}
185 		}
186 	} else if (http_method_get_or_head(r->http_method)
187 		   && (vb = http_header_request_get(r, HTTP_HEADER_IF_MODIFIED_SINCE, CONST_STR_LEN("If-Modified-Since")))) {
188 		/* last-modified handling */
189 		size_t used_len;
190 		char *semicolon;
191 
192 		if (NULL == (semicolon = strchr(vb->ptr, ';'))) {
193 			used_len = buffer_string_length(vb);
194 		} else {
195 			used_len = semicolon - vb->ptr;
196 		}
197 
198 		if (buffer_is_equal_string(mtime, vb->ptr, used_len)) {
199 			if ('\0' == mtime->ptr[used_len]) r->http_status = 304;
200 			return HANDLER_FINISHED;
201 		} else {
202 			char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
203 			time_t t_header, t_file;
204 			struct tm tm;
205 
206 			/* convert to timestamp */
207 			if (used_len >= sizeof(buf)) return HANDLER_GO_ON;
208 
209 			memcpy(buf, vb->ptr, used_len);
210 			buf[used_len] = '\0';
211 
212 			if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
213 				/**
214 				 * parsing failed, let's get out of here
215 				 */
216 				return HANDLER_GO_ON;
217 			}
218 			tm.tm_isdst = 0;
219 			t_header = mktime(&tm);
220 
221 			strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
222 			tm.tm_isdst = 0;
223 			t_file = mktime(&tm);
224 
225 			if (t_file > t_header) return HANDLER_GO_ON;
226 
227 			r->http_status = 304;
228 			return HANDLER_FINISHED;
229 		}
230 	}
231 
232 	return HANDLER_GO_ON;
233 }
234 
235 
236 void http_response_body_clear (request_st * const r, int preserve_length) {
237     r->resp_send_chunked = 0;
238     if (r->resp_htags & HTTP_HEADER_TRANSFER_ENCODING) {
239         http_header_response_unset(r, HTTP_HEADER_TRANSFER_ENCODING, CONST_STR_LEN("Transfer-Encoding"));
240     }
241     if (!preserve_length) { /* preserve for HEAD responses and no-content responses (204, 205, 304) */
242         r->content_length = -1;
243         if (r->resp_htags & HTTP_HEADER_CONTENT_LENGTH) {
244             http_header_response_unset(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
245         }
246         /*(if not preserving Content-Length, do not preserve trailers, if any)*/
247         r->resp_decode_chunked = 0;
248         if (r->gw_dechunk) {
249             free(r->gw_dechunk->b.ptr);
250             free(r->gw_dechunk);
251             r->gw_dechunk = NULL;
252         }
253     }
254     chunkqueue_reset(r->write_queue);
255 }
256 
257 
258 static int http_response_parse_range(request_st * const r, buffer * const path, stat_cache_entry * const sce, const char * const range) {
259 	int multipart = 0;
260 	int error;
261 	off_t start, end;
262 	const char *s, *minus;
263 	static const char boundary[] = "fkj49sn38dcn3";
264 	const buffer *content_type = http_header_response_get(r, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"));
265 
266 	start = 0;
267 	end = sce->st.st_size - 1;
268 
269 	r->content_length = 0;
270 
271 	for (s = range, error = 0;
272 	     !error && *s && NULL != (minus = strchr(s, '-')); ) {
273 		char *err;
274 		off_t la = 0, le;
275 		*((const char **)&err) = s; /*(quiet clang --analyze)*/
276 
277 		if (s != minus) {
278 			la = strtoll(s, &err, 10);
279 			if (err != minus) {
280 				/* should not have multiple range-unit in Range, but
281 				 * handle just in case multiple Range headers merged */
282 				while (*s == ' ' || *s == '\t') ++s;
283 				if (0 != strncmp(s, "bytes=", 6)) return -1;
284 				s += 6;
285 				if (s != minus) {
286 					la = strtoll(s, &err, 10);
287 					if (err != minus) return -1;
288 				}
289 			}
290 		}
291 
292 		if (s == minus) {
293 			/* -<stop> */
294 
295 			le = strtoll(s, &err, 10);
296 
297 			if (le == 0) {
298 				/* RFC 2616 - 14.35.1 */
299 
300 				r->http_status = 416;
301 				error = 1;
302 			} else if (*err == '\0') {
303 				/* end */
304 				s = err;
305 
306 				end = sce->st.st_size - 1;
307 				start = sce->st.st_size + le;
308 			} else if (*err == ',') {
309 				multipart = 1;
310 				s = err + 1;
311 
312 				end = sce->st.st_size - 1;
313 				start = sce->st.st_size + le;
314 			} else {
315 				error = 1;
316 			}
317 
318 		} else if (*(minus+1) == '\0' || *(minus+1) == ',') {
319 			/* <start>- */
320 
321 				/* ok */
322 
323 				if (*(err + 1) == '\0') {
324 					s = err + 1;
325 
326 					end = sce->st.st_size - 1;
327 					start = la;
328 
329 				} else if (*(err + 1) == ',') {
330 					multipart = 1;
331 					s = err + 2;
332 
333 					end = sce->st.st_size - 1;
334 					start = la;
335 				} else {
336 					error = 1;
337 				}
338 		} else {
339 			/* <start>-<stop> */
340 
341 				le = strtoll(minus+1, &err, 10);
342 
343 				/* RFC 2616 - 14.35.1 */
344 				if (la > le) {
345 					error = 1;
346 				}
347 
348 				if (*err == '\0') {
349 					/* ok, end*/
350 					s = err;
351 
352 					end = le;
353 					start = la;
354 				} else if (*err == ',') {
355 					multipart = 1;
356 					s = err + 1;
357 
358 					end = le;
359 					start = la;
360 				} else {
361 					/* error */
362 
363 					error = 1;
364 				}
365 		}
366 
367 		if (!error) {
368 			if (start < 0) start = 0;
369 
370 			/* RFC 2616 - 14.35.1 */
371 			if (end > sce->st.st_size - 1) end = sce->st.st_size - 1;
372 
373 			if (start > sce->st.st_size - 1) {
374 				error = 1;
375 
376 				r->http_status = 416;
377 			}
378 		}
379 
380 		if (!error) {
381 			if (multipart) {
382 				/* write boundary-header */
383 				buffer *b = r->tmp_buf;
384 				buffer_copy_string_len(b, CONST_STR_LEN("\r\n--"));
385 				buffer_append_string_len(b, boundary, sizeof(boundary)-1);
386 
387 				/* write Content-Range */
388 				buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes "));
389 				buffer_append_int(b, start);
390 				buffer_append_string_len(b, CONST_STR_LEN("-"));
391 				buffer_append_int(b, end);
392 				buffer_append_string_len(b, CONST_STR_LEN("/"));
393 				buffer_append_int(b, sce->st.st_size);
394 
395 				if (content_type) {
396 					buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: "));
397 					buffer_append_string_buffer(b, content_type);
398 				}
399 
400 				/* write END-OF-HEADER */
401 				buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n"));
402 
403 				r->content_length += buffer_string_length(b);
404 				chunkqueue_append_mem(r->write_queue, CONST_BUF_LEN(b));
405 			}
406 
407 			chunkqueue_append_file(r->write_queue, path, start, end - start + 1);
408 			r->content_length += end - start + 1;
409 		}
410 	}
411 
412 	/* something went wrong */
413 	if (error) return -1;
414 
415 	buffer * const tb = r->tmp_buf;
416 
417 	if (multipart) {
418 		/* add boundary end */
419 		buffer_copy_string_len(tb, "\r\n--", 4);
420 		buffer_append_string_len(tb, boundary, sizeof(boundary)-1);
421 		buffer_append_string_len(tb, "--\r\n", 4);
422 
423 		r->content_length += buffer_string_length(tb);
424 		chunkqueue_append_mem(r->write_queue, CONST_BUF_LEN(tb));
425 
426 		/* set header-fields */
427 
428 		buffer_copy_string_len(tb, CONST_STR_LEN("multipart/byteranges; boundary="));
429 		buffer_append_string_len(tb, boundary, sizeof(boundary)-1);
430 
431 		/* overwrite content-type */
432 		http_header_response_set(r, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(tb));
433 	} else {
434 		/* add Content-Range-header */
435 
436 		buffer_copy_string_len(tb, CONST_STR_LEN("bytes "));
437 		buffer_append_int(tb, start);
438 		buffer_append_string_len(tb, CONST_STR_LEN("-"));
439 		buffer_append_int(tb, end);
440 		buffer_append_string_len(tb, CONST_STR_LEN("/"));
441 		buffer_append_int(tb, sce->st.st_size);
442 
443 		http_header_response_set(r, HTTP_HEADER_OTHER, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(tb));
444 	}
445 
446 	/* ok, the file is set-up */
447 	return 0;
448 }
449 
450 
451 void http_response_send_file (request_st * const r, buffer * const path) {
452 	stat_cache_entry * const sce = stat_cache_get_entry(path);
453 	const buffer *mtime = NULL;
454 	const buffer *vb;
455 	int allow_caching = (0 == r->http_status || 200 == r->http_status);
456 
457 	if (NULL == sce) {
458 		r->http_status = (errno == ENOENT) ? 404 : 403;
459 		log_error(r->conf.errh, __FILE__, __LINE__,
460 		  "not a regular file: %s -> %s", r->uri.path.ptr, path->ptr);
461 		return;
462 	}
463 
464 	if (!r->conf.follow_symlink
465 	    && 0 != stat_cache_path_contains_symlink(path, r->conf.errh)) {
466 		r->http_status = 403;
467 		if (r->conf.log_request_handling) {
468 			log_error(r->conf.errh, __FILE__, __LINE__,
469 			  "-- access denied due symlink restriction");
470 			log_error(r->conf.errh, __FILE__, __LINE__,
471 			  "Path         : %s", path->ptr);
472 		}
473 		return;
474 	}
475 
476 	/* we only handle regular files */
477 	if (!S_ISREG(sce->st.st_mode)) {
478 		r->http_status = 403;
479 		if (r->conf.log_file_not_found) {
480 			log_error(r->conf.errh, __FILE__, __LINE__,
481 			  "not a regular file: %s -> %s",
482 			  r->uri.path.ptr, path->ptr);
483 		}
484 		return;
485 	}
486 
487 	/*(Note: O_NOFOLLOW affects only the final path segment,
488 	 * the target file, not any intermediate symlinks along path)*/
489 	const int fd = (0 != sce->st.st_size)
490 	  ? fdevent_open_cloexec(path->ptr, r->conf.follow_symlink, O_RDONLY, 0)
491 	  : -1;
492 	if (fd < 0 && 0 != sce->st.st_size) {
493 		r->http_status = (errno == ENOENT) ? 404 : 403;
494 		if (r->conf.log_request_handling) {
495 			log_perror(r->conf.errh, __FILE__, __LINE__,
496 			  "file open failed: %s", path->ptr);
497 		}
498 		return;
499 	}
500 
501 	/* set response content-type, if not set already */
502 
503 	if (NULL == http_header_response_get(r, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"))) {
504 		const buffer *content_type = stat_cache_content_type_get(sce, r);
505 		if (buffer_string_is_empty(content_type)) {
506 			/* we are setting application/octet-stream, but also announce that
507 			 * this header field might change in the seconds few requests
508 			 *
509 			 * This should fix the aggressive caching of FF and the script download
510 			 * seen by the first installations
511 			 */
512 			http_header_response_set(r, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));
513 
514 			allow_caching = 0;
515 		} else {
516 			http_header_response_set(r, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(content_type));
517 		}
518 	}
519 
520 	if (r->conf.range_requests) {
521 		http_header_response_append(r, HTTP_HEADER_OTHER, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
522 	}
523 
524 	if (allow_caching) {
525 		const buffer *etag = (0 != r->conf.etag_flags)
526 		  ? stat_cache_etag_get(sce, r->conf.etag_flags)
527 		  : NULL;
528 		if (!buffer_string_is_empty(etag)) {
529 			if (NULL == http_header_response_get(r, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"))) {
530 				/* generate e-tag */
531 				etag_mutate(&r->physical.etag, etag);
532 
533 				http_header_response_set(r, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(&r->physical.etag));
534 			}
535 		}
536 
537 		/* prepare header */
538 		if (NULL == (mtime = http_header_response_get(r, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified")))) {
539 			mtime = strftime_cache_get(sce->st.st_mtime);
540 			http_header_response_set(r, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
541 		}
542 
543 		if (HANDLER_FINISHED == http_response_handle_cachable(r, mtime)) {
544 			if (fd >= 0) close(fd);
545 			return;
546 		}
547 	}
548 
549 	if (fd < 0) { /* 0-length file */
550 		r->http_status = 200;
551 		r->resp_body_finished = 1;
552 		return;
553 	}
554 
555 	if (r->conf.range_requests
556 	    && (200 == r->http_status || 0 == r->http_status)
557 	    && NULL != (vb = http_header_request_get(r, HTTP_HEADER_RANGE, CONST_STR_LEN("Range")))
558 	    && NULL == http_header_response_get(r, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"))) {
559 		const buffer *range = vb;
560 		int do_range_request = 1;
561 		/* check if we have a conditional GET */
562 
563 		if (NULL != (vb = http_header_request_get(r, HTTP_HEADER_OTHER, CONST_STR_LEN("If-Range")))) {
564 			/* if the value is the same as our ETag, we do a Range-request,
565 			 * otherwise a full 200 */
566 
567 			if (vb->ptr[0] == '"') {
568 				/**
569 				 * client wants a ETag
570 				 */
571 				if (!buffer_is_equal(vb, &r->physical.etag)) {
572 					do_range_request = 0;
573 				}
574 			} else if (!mtime) {
575 				/**
576 				 * we don't have a Last-Modified and can match the If-Range:
577 				 *
578 				 * sending all
579 				 */
580 				do_range_request = 0;
581 			} else if (!buffer_is_equal(vb, mtime)) {
582 				do_range_request = 0;
583 			}
584 		}
585 
586 		if (do_range_request
587 		    && !buffer_string_is_empty(range)
588 		    && 0 == strncmp(range->ptr, "bytes=", 6)) {
589 			/* support only "bytes" byte-unit */
590 			/* content prepared, I'm done */
591 			r->resp_body_finished = 1;
592 
593 			if (0 == http_response_parse_range(r, path, sce, range->ptr+6)) {
594 				r->http_status = 206;
595 			}
596 			close(fd);
597 			return;
598 		}
599 	}
600 
601 	/* if we are still here, prepare body */
602 
603 	/* we add it here for all requests
604 	 * the HEAD request will drop it afterwards again
605 	 */
606 
607 	if (0 == http_chunk_append_file_fd(r, path, fd, sce->st.st_size)) {
608 		r->http_status = 200;
609 		r->resp_body_finished = 1;
610 	}
611 	else {
612 		r->http_status = 500;
613 	}
614 }
615 
616 
617 static void http_response_xsendfile (request_st * const r, buffer * const path, const array * const xdocroot) {
618 	const int status = r->http_status;
619 	int valid = 1;
620 
621 	/* reset Content-Length, if set by backend
622 	 * Content-Length might later be set to size of X-Sendfile static file,
623 	 * determined by open(), fstat() to reduces race conditions if the file
624 	 * is modified between stat() (stat_cache_get_entry()) and open(). */
625 	if (r->resp_htags & HTTP_HEADER_CONTENT_LENGTH) {
626 		http_header_response_unset(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
627 		r->content_length = -1;
628 	}
629 
630 	buffer_urldecode_path(path);
631 	if (!buffer_is_valid_UTF8(path)) {
632 		log_error(r->conf.errh, __FILE__, __LINE__,
633 		  "X-Sendfile invalid UTF-8 after url-decode: %s", path->ptr);
634 		if (r->http_status < 400) {
635 			r->http_status = 502;
636 			r->handler_module = NULL;
637 		}
638 		return;
639 	}
640 	buffer_path_simplify(path, path);
641 	if (r->conf.force_lowercase_filenames) {
642 		buffer_to_lower(path);
643 	}
644 
645 	/* check that path is under xdocroot(s)
646 	 * - xdocroot should have trailing slash appended at config time
647 	 * - r->conf.force_lowercase_filenames is not a server-wide setting,
648 	 *   and so can not be definitively applied to xdocroot at config time*/
649 	if (xdocroot) {
650 		size_t i, xlen = buffer_string_length(path);
651 		for (i = 0; i < xdocroot->used; ++i) {
652 			data_string *ds = (data_string *)xdocroot->data[i];
653 			size_t dlen = buffer_string_length(&ds->value);
654 			if (dlen <= xlen
655 			    && (!r->conf.force_lowercase_filenames
656 				? 0 == memcmp(path->ptr, ds->value.ptr, dlen)
657 				: buffer_eq_icase_ssn(path->ptr, ds->value.ptr, dlen))) {
658 				break;
659 			}
660 		}
661 		if (i == xdocroot->used && 0 != i) {
662 			log_error(r->conf.errh, __FILE__, __LINE__,
663 			  "X-Sendfile (%s) not under configured x-sendfile-docroot(s)", path->ptr);
664 			r->http_status = 403;
665 			valid = 0;
666 		}
667 	}
668 
669 	if (valid) http_response_send_file(r, path);
670 
671 	if (r->http_status >= 400 && status < 300) {
672 		r->handler_module = NULL;
673 	} else if (0 != status && 200 != status) {
674 		r->http_status = status;
675 	}
676 }
677 
678 
679 static void http_response_xsendfile2(request_st * const r, const buffer * const value, const array * const xdocroot) {
680     const char *pos = value->ptr;
681     buffer * const b = r->tmp_buf;
682     const int status = r->http_status;
683 
684     /* reset Content-Length, if set by backend */
685     if (r->resp_htags & HTTP_HEADER_CONTENT_LENGTH) {
686         http_header_response_unset(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
687         r->content_length = -1;
688     }
689 
690     while (*pos) {
691         const char *filename, *range;
692         stat_cache_entry *sce;
693         off_t begin_range, end_range, range_len;
694 
695         while (' ' == *pos) pos++;
696         if (!*pos) break;
697 
698         filename = pos;
699         if (NULL == (range = strchr(pos, ' '))) {
700             /* missing range */
701             log_error(r->conf.errh, __FILE__, __LINE__,
702               "Couldn't find range after filename: %s", filename);
703             r->http_status = 502;
704             break;
705         }
706         buffer_copy_string_len(b, filename, range - filename);
707 
708         /* find end of range */
709         for (pos = ++range; *pos && *pos != ' ' && *pos != ','; pos++) ;
710 
711         buffer_urldecode_path(b);
712         if (!buffer_is_valid_UTF8(b)) {
713             log_error(r->conf.errh, __FILE__, __LINE__,
714               "X-Sendfile2 invalid UTF-8 after url-decode: %s", b->ptr);
715             r->http_status = 502;
716             break;
717         }
718         buffer_path_simplify(b, b);
719         if (r->conf.force_lowercase_filenames) {
720             buffer_to_lower(b);
721         }
722         if (xdocroot) {
723             size_t i, xlen = buffer_string_length(b);
724             for (i = 0; i < xdocroot->used; ++i) {
725                 data_string *ds = (data_string *)xdocroot->data[i];
726                 size_t dlen = buffer_string_length(&ds->value);
727                 if (dlen <= xlen
728                     && (!r->conf.force_lowercase_filenames
729                     ? 0 == memcmp(b->ptr, ds->value.ptr, dlen)
730                     : buffer_eq_icase_ssn(b->ptr, ds->value.ptr, dlen))) {
731                     break;
732                 }
733             }
734             if (i == xdocroot->used && 0 != i) {
735                 log_error(r->conf.errh, __FILE__, __LINE__,
736                   "X-Sendfile2 (%s) not under configured x-sendfile-docroot(s)",
737                   b->ptr);
738                 r->http_status = 403;
739                 break;
740             }
741         }
742 
743         sce = stat_cache_get_entry(b);
744         if (NULL == sce) {
745             log_error(r->conf.errh, __FILE__, __LINE__,
746               "send-file error: couldn't get stat_cache entry for "
747               "X-Sendfile2: %s", b->ptr);
748             r->http_status = 404;
749             break;
750         } else if (!S_ISREG(sce->st.st_mode)) {
751             log_error(r->conf.errh, __FILE__, __LINE__,
752               "send-file error: wrong filetype for X-Sendfile2: %s", b->ptr);
753             r->http_status = 502;
754             break;
755         }
756         /* found the file */
757 
758         /* parse range */
759         end_range = sce->st.st_size - 1;
760         {
761             char *rpos = NULL;
762             errno = 0;
763             begin_range = strtoll(range, &rpos, 10);
764             if (errno != 0 || begin_range < 0 || rpos == range)
765                 goto range_failed;
766             if ('-' != *rpos++) goto range_failed;
767             if (rpos != pos) {
768                 range = rpos;
769                 end_range = strtoll(range, &rpos, 10);
770                 if (errno != 0 || end_range < 0 || rpos == range)
771                     goto range_failed;
772             }
773             if (rpos != pos) goto range_failed;
774 
775             goto range_success;
776 
777 range_failed:
778             log_error(r->conf.errh, __FILE__, __LINE__,
779               "Couldn't decode range after filename: %s", filename);
780             r->http_status = 502;
781             break;
782 
783 range_success: ;
784         }
785 
786         /* no parameters accepted */
787 
788         while (*pos == ' ') pos++;
789         if (*pos != '\0' && *pos != ',') {
790             r->http_status = 502;
791             break;
792         }
793 
794         range_len = end_range - begin_range + 1;
795         if (range_len < 0) {
796             r->http_status = 502;
797             break;
798         }
799         if (range_len != 0) {
800             if (0 !=
801                 http_chunk_append_file_range(r, b, begin_range, range_len)) {
802                 r->http_status = 502;
803                 break;
804             }
805         }
806 
807         if (*pos == ',') pos++;
808     }
809 
810     if (r->http_status >= 400 && status < 300) {
811 	r->handler_module = NULL;
812     } else if (0 != status && 200 != status) {
813         r->http_status = status;
814     }
815 }
816 
817 
818 void http_response_backend_error (request_st * const r) {
819 	if (r->resp_body_started) {
820 		/*(response might have been already started, kill the connection)*/
821 		/*(mode == DIRECT to avoid later call to http_response_backend_done())*/
822 		r->handler_module = NULL;  /*(avoid sending final chunked block)*/
823 		r->keep_alive = 0;
824 		r->resp_body_finished = 1;
825 	} /*(else error status set later by http_response_backend_done())*/
826 }
827 
828 void http_response_backend_done (request_st * const r) {
829 	/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
830 	 *  i.e. not called from handle_connection_close or handle_request_reset
831 	 *  hooks, except maybe from errdoc handler, which later resets state)*/
832 	switch (r->state) {
833 	case CON_STATE_HANDLE_REQUEST:
834 	case CON_STATE_READ_POST:
835 		if (!r->resp_body_started) {
836 			/* Send an error if we haven't sent any data yet */
837 			r->http_status = 500;
838 			r->handler_module = NULL;
839 			break;
840 		} /* else fall through */
841 	case CON_STATE_WRITE:
842 		if (!r->resp_body_finished) {
843 			http_chunk_close(r);
844 			r->resp_body_finished = 1;
845 		}
846 	default:
847 		break;
848 	}
849 }
850 
851 
852 void http_response_upgrade_read_body_unknown(request_st * const r) {
853     /* act as transparent proxy */
854     if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST))
855         r->conf.stream_request_body |=
856           (FDEVENT_STREAM_REQUEST_BUFMIN | FDEVENT_STREAM_REQUEST);
857     if (!(r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE))
858         r->conf.stream_response_body |=
859           (FDEVENT_STREAM_RESPONSE_BUFMIN | FDEVENT_STREAM_RESPONSE);
860     r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
861     r->reqbody_length = -2;
862     r->keep_alive = 0;
863 }
864 
865 
866 static handler_t http_response_process_local_redir(request_st * const r, size_t blen) {
867     /* [RFC3875] The Common Gateway Interface (CGI) Version 1.1
868      * [RFC3875] 6.2.2 Local Redirect Response
869      *
870      *    The CGI script can return a URI path and query-string
871      *    ('local-pathquery') for a local resource in a Location header field.
872      *    This indicates to the server that it should reprocess the request
873      *    using the path specified.
874      *
875      *      local-redir-response = local-Location NL
876      *
877      *    The script MUST NOT return any other header fields or a message-body,
878      *    and the server MUST generate the response that it would have produced
879      *    in response to a request containing the URL
880      *
881      *      scheme "://" server-name ":" server-port local-pathquery
882      *
883      * (Might not have begun to receive body yet, but do skip local-redir
884      *  if we already have started receiving a response body (blen > 0))
885      * (Also, while not required by the RFC, do not send local-redir back
886      *  to same URL, since CGI should have handled it internally if it
887      *  really wanted to do that internally)
888      */
889 
890     /* r->http_status >= 300 && r->http_status < 400) */
891     size_t ulen = buffer_string_length(&r->uri.path);
892     const buffer *vb = http_header_response_get(r, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location"));
893     if (NULL != vb
894         && vb->ptr[0] == '/'
895         && (0 != strncmp(vb->ptr, r->uri.path.ptr, ulen)
896             || (   vb->ptr[ulen] != '\0'
897                 && vb->ptr[ulen] != '/'
898                 && vb->ptr[ulen] != '?'))
899         && 0 == blen
900         && !(r->resp_htags & HTTP_HEADER_STATUS) /*no "Status" or NPH response*/
901         && 1 == r->resp_headers.used) {
902         if (++r->loops_per_request > 5) {
903             log_error(r->conf.errh, __FILE__, __LINE__,
904               "too many internal loops while processing request: %s",
905               r->target_orig.ptr);
906             r->http_status = 500; /* Internal Server Error */
907             r->handler_module = NULL;
908             return HANDLER_FINISHED;
909         }
910 
911         buffer_copy_buffer(&r->target, vb);
912 
913         if (r->reqbody_length) {
914             if (r->reqbody_length
915                 != r->reqbody_queue->bytes_in) {
916                 r->keep_alive = 0;
917             }
918             r->reqbody_length = 0;
919             chunkqueue_reset(r->reqbody_queue);
920         }
921 
922         if (r->http_status != 307 && r->http_status != 308) {
923             /* Note: request body (if any) sent to initial dynamic handler
924              * and is not available to the internal redirect */
925             r->http_method = HTTP_METHOD_GET;
926         }
927 
928         /*(caller must reset request as follows)*/
929         /*connection_response_reset(r);*/ /*(sets r->http_status = 0)*/
930         /*plugins_call_handle_request_reset(r);*/
931 
932         return HANDLER_COMEBACK;
933     }
934 
935     return HANDLER_GO_ON;
936 }
937 
938 
939 static int http_response_process_headers(request_st * const r, http_response_opts * const opts, buffer * const hdrs) {
940     char *ns;
941     const char *s;
942     int line = 0;
943     int status_is_set = 0;
944 
945     for (s = hdrs->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1, ++line) {
946         const char *key, *value;
947         int key_len;
948         enum http_header_e id;
949 
950         /* strip the \n */
951         ns[0] = '\0';
952         if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
953 
954         if (0 == line && 0 == strncmp(s, "HTTP/1.", 7)) {
955             /* non-parsed headers ... we parse them anyway */
956             if ((s[7] == '1' || s[7] == '0') && s[8] == ' ') {
957                 /* after the space should be a status code for us */
958                 int status = http_header_str_to_code(s+9);
959                 if (status >= 100 && status < 1000) {
960                     status_is_set = 1;
961                     r->resp_htags |= HTTP_HEADER_STATUS;
962                     r->http_status = status;
963                 } /* else we expected 3 digits and didn't get them */
964             }
965 
966             if (0 == r->http_status) {
967                 log_error(r->conf.errh, __FILE__, __LINE__,
968                   "invalid HTTP status line: %s", s);
969                 r->http_status = 502; /* Bad Gateway */
970                 r->handler_module = NULL;
971                 return -1;
972             }
973 
974             continue;
975         }
976 
977         /* parse the headers */
978         key = s;
979         if (NULL == (value = strchr(s, ':'))) {
980             /* we expect: "<key>: <value>\r\n" */
981             continue;
982         }
983 
984         key_len = value - key;
985         do { ++value; } while (*value == ' ' || *value == '\t'); /* skip LWS */
986         id = http_header_hkey_get(key, key_len);
987 
988         if (opts->authorizer) {
989             if (0 == r->http_status || 200 == r->http_status) {
990                 if (id == HTTP_HEADER_STATUS) {
991                     int status = http_header_str_to_code(value);
992                     if (status >= 100 && status < 1000) {
993                         r->http_status = status;
994                     } else {
995                         r->http_status = 502; /* Bad Gateway */
996                         break;
997                     }
998                 }
999                 else if (id == HTTP_HEADER_OTHER && key_len > 9
1000                          && (key[0] & 0xdf) == 'V'
1001                          && buffer_eq_icase_ssn(key,
1002                                                 CONST_STR_LEN("Variable-"))) {
1003                     http_header_env_append(r, key + 9, key_len - 9, value, strlen(value));
1004                 }
1005                 continue;
1006             }
1007         }
1008 
1009         switch (id) {
1010           case HTTP_HEADER_STATUS:
1011             {
1012                 if (opts->backend == BACKEND_PROXY) break; /*(pass w/o parse)*/
1013                 int status = http_header_str_to_code(value);
1014                 if (status >= 100 && status < 1000) {
1015                     r->http_status = status;
1016                     status_is_set = 1;
1017                 } else {
1018                     r->http_status = 502;
1019                     r->handler_module = NULL;
1020                 }
1021                 continue; /* do not send Status to client */
1022             }
1023             break;
1024           case HTTP_HEADER_UPGRADE:
1025             /*(technically, should also verify Connection: upgrade)*/
1026             /*(flag only for mod_proxy and mod_cgi (for now))*/
1027             if (opts->backend != BACKEND_PROXY
1028                 && opts->backend != BACKEND_CGI) {
1029                 id = HTTP_HEADER_OTHER;
1030             }
1031             break;
1032           case HTTP_HEADER_CONNECTION:
1033             if (opts->backend == BACKEND_PROXY) continue;
1034             /*(should parse for tokens and do case-insensitive match for "close"
1035              * but this is an imperfect though simplistic attempt to honor
1036              * backend request to close)*/
1037             if (NULL != strstr(value, "lose")) r->keep_alive = 0;
1038             break;
1039           case HTTP_HEADER_CONTENT_LENGTH:
1040             r->content_length = strtoul(value, NULL, 10);
1041             if (*value == '+') ++value;
1042             break;
1043           case HTTP_HEADER_TRANSFER_ENCODING:
1044             /*(assumes "Transfer-Encoding: chunked"; does not verify)*/
1045             r->resp_decode_chunked = 1;
1046             r->gw_dechunk = calloc(1, sizeof(response_dechunk));
1047             /* XXX: future: might consider using chunk_buffer_acquire()
1048              *      and chunk_buffer_release() for r->gw_dechunk->b */
1049             force_assert(r->gw_dechunk);
1050             continue;
1051           default:
1052             break;
1053         }
1054 
1055         http_header_response_insert(r, id, key, key_len, value, strlen(value));
1056     }
1057 
1058     /* CGI/1.1 rev 03 - 7.2.1.2 */
1059     /* (proxy requires Status-Line, so never true for proxy)*/
1060     if (!status_is_set && (r->resp_htags & HTTP_HEADER_LOCATION)) {
1061         r->http_status = 302;
1062     }
1063 
1064     return 0;
1065 }
1066 
1067 
1068 handler_t http_response_parse_headers(request_st * const r, http_response_opts * const opts, buffer * const b) {
1069     /**
1070      * possible formats of response headers:
1071      *
1072      * proxy or NPH (non-parsed headers):
1073      *
1074      *   HTTP/1.0 200 Ok\n
1075      *   Header: Value\n
1076      *   \n
1077      *
1078      * CGI:
1079      *
1080      *   Header: Value\n
1081      *   Status: 200\n
1082      *   \n
1083      *
1084      * and different mixes of \n and \r\n combinations
1085      *
1086      * Some users also forget about CGI and just send a response
1087      * and hope we handle it. No headers, no header-content separator
1088      */
1089 
1090     int is_nph = (0 == strncmp(b->ptr, "HTTP/1.", 7)); /*nph (non-parsed hdrs)*/
1091     int is_header_end = 0;
1092     size_t last_eol = 0;
1093     size_t i = 0, header_len = buffer_string_length(b);
1094     const char *bstart;
1095     size_t blen;
1096 
1097     if (b->ptr[0] == '\n' || (b->ptr[0] == '\r' && b->ptr[1] == '\n')) {
1098         /* no HTTP headers */
1099         i = (b->ptr[0] == '\n') ? 0 : 1;
1100         is_header_end = 1;
1101     } else if (is_nph || b->ptr[(i = strcspn(b->ptr, ":\n"))] == ':') {
1102         /* HTTP headers */
1103         ++i;
1104         for (char *c; NULL != (c = strchr(b->ptr+i, '\n')); ++i) {
1105             i = (uintptr_t)(c - b->ptr);
1106             /**
1107              * check if we saw a \n(\r)?\n sequence
1108              */
1109             if (last_eol > 0 &&
1110                 ((i - last_eol == 1) ||
1111                  (i - last_eol == 2 && b->ptr[i - 1] == '\r'))) {
1112                 is_header_end = 1;
1113                 break;
1114             }
1115 
1116             last_eol = i;
1117         }
1118     } else if (i == header_len) { /* (no newline yet; partial header line?) */
1119     } else if (opts->backend == BACKEND_CGI) {
1120         /* no HTTP headers, but a body (special-case for CGI compat) */
1121         /* no colon found; does not appear to be HTTP headers */
1122         if (0 != http_chunk_append_buffer(r, b)) {
1123             return HANDLER_ERROR;
1124         }
1125         r->http_status = 200; /* OK */
1126         r->resp_body_started = 1;
1127         return HANDLER_GO_ON;
1128     } else {
1129         /* invalid response headers */
1130         r->http_status = 502; /* Bad Gateway */
1131         r->handler_module = NULL;
1132         return HANDLER_FINISHED;
1133     }
1134 
1135     if (!is_header_end) {
1136         /*(reuse MAX_HTTP_REQUEST_HEADER as max size
1137          * for response headers from backends)*/
1138         if (header_len > MAX_HTTP_REQUEST_HEADER) {
1139             log_error(r->conf.errh, __FILE__, __LINE__,
1140               "response headers too large for %s", r->uri.path.ptr);
1141             r->http_status = 502; /* Bad Gateway */
1142             r->handler_module = NULL;
1143             return HANDLER_FINISHED;
1144         }
1145         return HANDLER_GO_ON;
1146     }
1147 
1148     /* the body starts after the EOL */
1149     bstart = b->ptr + (i + 1);
1150     blen = header_len - (i + 1);
1151 
1152     /* strip the last \r?\n */
1153     if (i > 0 && (b->ptr[i - 1] == '\r')) {
1154         i--;
1155     }
1156 
1157     buffer_string_set_length(b, i);
1158 
1159     if (opts->backend == BACKEND_PROXY && !is_nph) {
1160         /* invalid response Status-Line from HTTP proxy */
1161         r->http_status = 502; /* Bad Gateway */
1162         r->handler_module = NULL;
1163         return HANDLER_FINISHED;
1164     }
1165 
1166     if (0 != http_response_process_headers(r, opts, b)) {
1167         return HANDLER_ERROR;
1168     }
1169 
1170     r->resp_body_started = 1;
1171 
1172     if (opts->authorizer
1173         && (r->http_status == 0 || r->http_status == 200)) {
1174         return HANDLER_GO_ON;
1175     }
1176 
1177     if (NULL == r->handler_module) {
1178         return HANDLER_FINISHED;
1179     }
1180 
1181     if (opts->local_redir && r->http_status >= 300 && r->http_status < 400){
1182         /*(r->resp_htags & HTTP_HEADER_LOCATION)*/
1183         handler_t rc = http_response_process_local_redir(r, blen);
1184         if (NULL == r->handler_module)
1185             r->resp_body_started = 0;
1186         if (rc != HANDLER_GO_ON) return rc;
1187     }
1188 
1189     if (opts->xsendfile_allow) {
1190         buffer *vb;
1191         /* X-Sendfile2 is deprecated; historical for fastcgi */
1192         if (opts->backend == BACKEND_FASTCGI
1193             && NULL != (vb = http_header_response_get(r, HTTP_HEADER_OTHER, CONST_STR_LEN("X-Sendfile2")))) {
1194             http_response_xsendfile2(r, vb, opts->xsendfile_docroot);
1195             /* http_header_response_unset() shortcut for HTTP_HEADER_OTHER */
1196             buffer_clear(vb); /*(do not send to client)*/
1197             if (NULL == r->handler_module)
1198                 r->resp_body_started = 0;
1199             return HANDLER_FINISHED;
1200         } else if (NULL != (vb = http_header_response_get(r, HTTP_HEADER_OTHER, CONST_STR_LEN("X-Sendfile")))
1201                    || (opts->backend == BACKEND_FASTCGI /* X-LIGHTTPD-send-file is deprecated; historical for fastcgi */
1202                        && NULL != (vb = http_header_response_get(r, HTTP_HEADER_OTHER, CONST_STR_LEN("X-LIGHTTPD-send-file"))))) {
1203             http_response_xsendfile(r, vb, opts->xsendfile_docroot);
1204             /* http_header_response_unset() shortcut for HTTP_HEADER_OTHER */
1205             buffer_clear(vb); /*(do not send to client)*/
1206             if (NULL == r->handler_module)
1207                 r->resp_body_started = 0;
1208             return HANDLER_FINISHED;
1209         }
1210     }
1211 
1212     if (blen > 0) {
1213         if (0 != http_chunk_decode_append_mem(r, bstart, blen))
1214             return HANDLER_ERROR;
1215     }
1216 
1217     /* (callback for response headers complete) */
1218     return (opts->headers) ? opts->headers(r, opts) : HANDLER_GO_ON;
1219 }
1220 
1221 
1222 handler_t http_response_read(request_st * const r, http_response_opts * const opts, buffer * const b, fdnode * const fdn) {
1223     const int fd = fdn->fd;
1224     while (1) {
1225         ssize_t n;
1226         size_t avail = buffer_string_space(b);
1227         unsigned int toread = 0;
1228 
1229         if (0 == fdevent_ioctl_fionread(fd, opts->fdfmt, (int *)&toread)) {
1230             if (avail < toread) {
1231                 size_t blen = buffer_string_length(b);
1232                 if (toread + blen < 4096)
1233                     toread = 4095 - blen;
1234                 else if (toread > MAX_READ_LIMIT)
1235                     toread = MAX_READ_LIMIT;
1236             }
1237             else if (0 == toread) {
1238               #if 0
1239                 return (fdevent_fdnode_interest(fdn) & FDEVENT_IN)
1240                   ? HANDLER_FINISHED  /* read finished */
1241                   : HANDLER_GO_ON;    /* optimistic read; data not ready */
1242               #else
1243                 if (!(fdevent_fdnode_interest(fdn) & FDEVENT_IN)) {
1244                     if (!(r->conf.stream_response_body
1245                           & FDEVENT_STREAM_RESPONSE_POLLRDHUP))
1246                         return HANDLER_GO_ON;/*optimistic read; data not ready*/
1247                 }
1248                 if (0 == avail) /* let read() below indicate if EOF or EAGAIN */
1249                     toread = 1024;
1250               #endif
1251             }
1252         }
1253         else if (avail < 1024) {
1254             toread = 4095 - avail;
1255         }
1256 
1257         if (r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) {
1258             off_t cqlen = chunkqueue_length(r->write_queue);
1259             if (cqlen + (off_t)toread > 65536 - 4096) {
1260                 if (!r->con->is_writable) {
1261                     /*(defer removal of FDEVENT_IN interest since
1262                      * connection_state_machine() might be able to send data
1263                      * immediately, unless !con->is_writable, where
1264                      * connection_state_machine() might not loop back to call
1265                      * mod_proxy_handle_subrequest())*/
1266                     fdevent_fdnode_event_clr(r->con->srv->ev, fdn, FDEVENT_IN);
1267                 }
1268                 if (cqlen >= 65536-1) return HANDLER_GO_ON;
1269                 toread = 65536 - 1 - (unsigned int)cqlen;
1270                 /* Note: heuristic is fuzzy in that it limits how much to read
1271                  * from backend based on how much is pending to write to client.
1272                  * Modules where data from backend is framed (e.g. FastCGI) may
1273                  * want to limit how much is buffered from backend while waiting
1274                  * for a complete data frame or data packet from backend. */
1275             }
1276         }
1277 
1278         if (avail < toread) {
1279             /*(add avail+toread to reduce allocations when ioctl EOPNOTSUPP)*/
1280             avail = avail ? avail - 1 + toread : toread;
1281             buffer_string_prepare_append(b, avail);
1282         }
1283 
1284         n = read(fd, b->ptr+buffer_string_length(b), avail);
1285 
1286         if (n < 0) {
1287             switch (errno) {
1288               case EAGAIN:
1289              #ifdef EWOULDBLOCK
1290              #if EWOULDBLOCK != EAGAIN
1291               case EWOULDBLOCK:
1292              #endif
1293              #endif
1294               case EINTR:
1295                 return HANDLER_GO_ON;
1296               default:
1297                 log_perror(r->conf.errh, __FILE__, __LINE__,
1298                   "read() %d %d", r->con->fd, fd);
1299                 return HANDLER_ERROR;
1300             }
1301         }
1302 
1303         buffer_commit(b, (size_t)n);
1304       #ifdef __COVERITY__
1305         /* Coverity Scan overlooks the effect of buffer_commit() */
1306         b->ptr[buffer_string_length(b)+n] = '\0';
1307       #endif
1308 
1309         if (NULL != opts->parse) {
1310             handler_t rc = opts->parse(r, opts, b, (size_t)n);
1311             if (rc != HANDLER_GO_ON) return rc;
1312         } else if (0 == n) {
1313             /* note: no further data is sent to backend after read EOF on socket
1314              * (not checking for half-closed TCP socket)
1315              * (backend should read all data desired prior to closing socket,
1316              *  though might send app-level close data frame, if applicable) */
1317             return HANDLER_FINISHED; /* read finished */
1318         } else if (0 == r->resp_body_started) {
1319             /* split header from body */
1320             handler_t rc = http_response_parse_headers(r, opts, b);
1321             if (rc != HANDLER_GO_ON) return rc;
1322             /* accumulate response in b until headers completed (or error) */
1323             if (r->resp_body_started) buffer_clear(b);
1324         } else {
1325             if (0 != http_chunk_decode_append_buffer(r, b)) {
1326                 /* error writing to tempfile;
1327                  * truncate response or send 500 if nothing sent yet */
1328                 return HANDLER_ERROR;
1329             }
1330             buffer_clear(b);
1331         }
1332 
1333         if (r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) {
1334             if (chunkqueue_length(r->write_queue) > 65536 - 4096) {
1335                 /*(defer removal of FDEVENT_IN interest since
1336                  * connection_state_machine() might be able to send
1337                  * data immediately, unless !con->is_writable, where
1338                  * connection_state_machine() might not loop back to
1339                  * call the subrequest handler)*/
1340                 if (!r->con->is_writable)
1341                     fdevent_fdnode_event_clr(r->con->srv->ev, fdn, FDEVENT_IN);
1342                 break;
1343             }
1344         }
1345 
1346         if ((size_t)n < avail)
1347             break; /* emptied kernel read buffer or partial read */
1348     }
1349 
1350     return HANDLER_GO_ON;
1351 }
1352 
1353 
1354 int http_cgi_headers (request_st * const r, http_cgi_opts * const opts, http_cgi_header_append_cb cb, void *vdata) {
1355 
1356     /* CGI-SPEC 6.1.2, FastCGI spec 6.3 and SCGI spec */
1357 
1358     int rc = 0;
1359     const connection * const con = r->con;
1360     server_socket * const srv_sock = con->srv_socket;
1361     buffer * const tb = r->tmp_buf;
1362     const char *s;
1363     size_t n;
1364     char buf[LI_ITOSTRING_LENGTH];
1365     sock_addr *addr;
1366     sock_addr addrbuf;
1367     char b2[INET6_ADDRSTRLEN + 1];
1368 
1369     /* (CONTENT_LENGTH must be first for SCGI) */
1370     if (!opts->authorizer) {
1371         rc |= cb(vdata, CONST_STR_LEN("CONTENT_LENGTH"),
1372                  buf, li_itostrn(buf,sizeof(buf),r->reqbody_length));
1373     }
1374 
1375     if (!buffer_string_is_empty(&r->uri.query)) {
1376         rc |= cb(vdata, CONST_STR_LEN("QUERY_STRING"),
1377                         CONST_BUF_LEN(&r->uri.query));
1378     } else {
1379         rc |= cb(vdata, CONST_STR_LEN("QUERY_STRING"),
1380                         CONST_STR_LEN(""));
1381     }
1382     if (!buffer_string_is_empty(opts->strip_request_uri)) {
1383         /**
1384          * /app1/index/list
1385          *
1386          * stripping /app1 or /app1/ should lead to
1387          *
1388          * /index/list
1389          *
1390          */
1391         size_t len = buffer_string_length(opts->strip_request_uri);
1392         if ('/' == opts->strip_request_uri->ptr[len-1]) {
1393             --len;
1394         }
1395 
1396         if (buffer_string_length(&r->target_orig) >= len
1397             && 0 == memcmp(r->target_orig.ptr,
1398                            opts->strip_request_uri->ptr, len)
1399             && r->target_orig.ptr[len] == '/') {
1400             rc |= cb(vdata, CONST_STR_LEN("REQUEST_URI"),
1401                             r->target_orig.ptr+len,
1402                             buffer_string_length(&r->target_orig)-len);
1403         } else {
1404             rc |= cb(vdata, CONST_STR_LEN("REQUEST_URI"),
1405                             CONST_BUF_LEN(&r->target_orig));
1406         }
1407     } else {
1408         rc |= cb(vdata, CONST_STR_LEN("REQUEST_URI"),
1409                         CONST_BUF_LEN(&r->target_orig));
1410     }
1411     if (!buffer_is_equal(&r->target, &r->target_orig)) {
1412         rc |= cb(vdata, CONST_STR_LEN("REDIRECT_URI"),
1413                         CONST_BUF_LEN(&r->target));
1414     }
1415     /* set REDIRECT_STATUS for php compiled with --force-redirect
1416      * (if REDIRECT_STATUS has not already been set by error handler) */
1417     if (0 == r->error_handler_saved_status) {
1418         rc |= cb(vdata, CONST_STR_LEN("REDIRECT_STATUS"),
1419                         CONST_STR_LEN("200"));
1420     }
1421 
1422     /*
1423      * SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to
1424      * http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html
1425      * (6.1.14, 6.1.6, 6.1.7)
1426      */
1427     if (!opts->authorizer) {
1428         rc |= cb(vdata, CONST_STR_LEN("SCRIPT_NAME"),
1429                         CONST_BUF_LEN(&r->uri.path));
1430         if (!buffer_string_is_empty(&r->pathinfo)) {
1431             rc |= cb(vdata, CONST_STR_LEN("PATH_INFO"),
1432                             CONST_BUF_LEN(&r->pathinfo));
1433             /* PATH_TRANSLATED is only defined if PATH_INFO is set */
1434             if (!buffer_string_is_empty(opts->docroot)) {
1435                 buffer_copy_buffer(tb, opts->docroot);
1436             } else {
1437                 buffer_copy_buffer(tb, &r->physical.basedir);
1438             }
1439             buffer_append_string_buffer(tb, &r->pathinfo);
1440             rc |= cb(vdata, CONST_STR_LEN("PATH_TRANSLATED"),
1441                             CONST_BUF_LEN(tb));
1442         }
1443     }
1444 
1445    /*
1446     * SCRIPT_FILENAME and DOCUMENT_ROOT for php
1447     * The PHP manual http://www.php.net/manual/en/reserved.variables.php
1448     * treatment of PATH_TRANSLATED is different from the one of CGI specs.
1449     * (see php.ini cgi.fix_pathinfo = 1 config parameter)
1450     */
1451 
1452     if (!buffer_string_is_empty(opts->docroot)) {
1453         /* alternate docroot, e.g. for remote FastCGI or SCGI server */
1454         buffer_copy_buffer(tb, opts->docroot);
1455         buffer_append_string_buffer(tb, &r->uri.path);
1456         rc |= cb(vdata, CONST_STR_LEN("SCRIPT_FILENAME"),
1457                         CONST_BUF_LEN(tb));
1458         rc |= cb(vdata, CONST_STR_LEN("DOCUMENT_ROOT"),
1459                         CONST_BUF_LEN(opts->docroot));
1460     } else {
1461         if (opts->break_scriptfilename_for_php) {
1462             /* php.ini config cgi.fix_pathinfo = 1 need a broken SCRIPT_FILENAME
1463              * to find out what PATH_INFO is itself
1464              *
1465              * see src/sapi/cgi_main.c, init_request_info()
1466              */
1467             buffer_copy_buffer(tb, &r->physical.path);
1468             buffer_append_string_buffer(tb, &r->pathinfo);
1469             rc |= cb(vdata, CONST_STR_LEN("SCRIPT_FILENAME"),
1470                             CONST_BUF_LEN(tb));
1471         } else {
1472             rc |= cb(vdata, CONST_STR_LEN("SCRIPT_FILENAME"),
1473                             CONST_BUF_LEN(&r->physical.path));
1474         }
1475         rc |= cb(vdata, CONST_STR_LEN("DOCUMENT_ROOT"),
1476                         CONST_BUF_LEN(&r->physical.basedir));
1477     }
1478 
1479     s = get_http_method_name(r->http_method);
1480     force_assert(s);
1481     rc |= cb(vdata, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
1482 
1483     s = get_http_version_name(r->http_version);
1484     force_assert(s);
1485     rc |= cb(vdata, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
1486 
1487     rc |= cb(vdata, CONST_STR_LEN("SERVER_SOFTWARE"),
1488                     CONST_BUF_LEN(r->conf.server_tag));
1489 
1490     rc |= cb(vdata, CONST_STR_LEN("GATEWAY_INTERFACE"),
1491                     CONST_STR_LEN("CGI/1.1"));
1492 
1493     rc |= cb(vdata, CONST_STR_LEN("REQUEST_SCHEME"),
1494                     CONST_BUF_LEN(&r->uri.scheme));
1495 
1496     if (buffer_is_equal_string(&r->uri.scheme, CONST_STR_LEN("https"))) {
1497         rc |= cb(vdata, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
1498     }
1499 
1500     addr = &srv_sock->addr;
1501     rc |= cb(vdata, CONST_STR_LEN("SERVER_PORT"),
1502              buf, li_utostrn(buf,sizeof(buf),sock_addr_get_port(addr)));
1503 
1504     switch (addr->plain.sa_family) {
1505     case AF_INET:
1506     case AF_INET6:
1507         if (sock_addr_is_addr_wildcard(addr)) {
1508             socklen_t addrlen = sizeof(addrbuf);
1509             if (0 == getsockname(con->fd,(struct sockaddr *)&addrbuf,&addrlen)){
1510                 addr = &addrbuf;
1511             } else {
1512                 s = "";
1513                 break;
1514             }
1515         }
1516         s = sock_addr_inet_ntop(addr, b2, sizeof(b2)-1);
1517         if (NULL == s) s = "";
1518         break;
1519     default:
1520         s = "";
1521         break;
1522     }
1523     force_assert(s);
1524     rc |= cb(vdata, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
1525 
1526     if (!buffer_string_is_empty(r->server_name)) {
1527         size_t len = buffer_string_length(r->server_name);
1528 
1529         if (r->server_name->ptr[0] == '[') {
1530             const char *colon = strstr(r->server_name->ptr, "]:");
1531             if (colon) len = (colon + 1) - r->server_name->ptr;
1532         } else {
1533             const char *colon = strchr(r->server_name->ptr, ':');
1534             if (colon) len = colon - r->server_name->ptr;
1535         }
1536 
1537         rc |= cb(vdata, CONST_STR_LEN("SERVER_NAME"),
1538                         r->server_name->ptr, len);
1539     } else {
1540         /* set to be same as SERVER_ADDR (above) */
1541         rc |= cb(vdata, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
1542     }
1543 
1544     rc |= cb(vdata, CONST_STR_LEN("REMOTE_ADDR"),
1545                     CONST_BUF_LEN(con->dst_addr_buf));
1546 
1547     rc |= cb(vdata, CONST_STR_LEN("REMOTE_PORT"), buf,
1548              li_utostrn(buf,sizeof(buf),sock_addr_get_port(&con->dst_addr)));
1549 
1550     for (n = 0; n < r->rqst_headers.used; n++) {
1551         data_string *ds = (data_string *)r->rqst_headers.data[n];
1552         if (!buffer_string_is_empty(&ds->value) && !buffer_is_empty(&ds->key)) {
1553             /* Security: Do not emit HTTP_PROXY in environment.
1554              * Some executables use HTTP_PROXY to configure
1555              * outgoing proxy.  See also https://httpoxy.org/ */
1556             if (buffer_is_equal_caseless_string(&ds->key,
1557                                                 CONST_STR_LEN("Proxy"))) {
1558                 continue;
1559             }
1560             buffer_copy_string_encoded_cgi_varnames(tb,
1561                                                     CONST_BUF_LEN(&ds->key), 1);
1562             rc |= cb(vdata, CONST_BUF_LEN(tb),
1563                             CONST_BUF_LEN(&ds->value));
1564         }
1565     }
1566 
1567     con->srv->request_env(r);
1568 
1569     for (n = 0; n < r->env.used; n++) {
1570         data_string *ds = (data_string *)r->env.data[n];
1571         if (!buffer_is_empty(&ds->value) && !buffer_is_empty(&ds->key)) {
1572             buffer_copy_string_encoded_cgi_varnames(tb,
1573                                                     CONST_BUF_LEN(&ds->key), 0);
1574             rc |= cb(vdata, CONST_BUF_LEN(tb),
1575                             CONST_BUF_LEN(&ds->value));
1576         }
1577     }
1578 
1579     return rc;
1580 }
1581