xref: /lighttpd1.4/src/response.c (revision 49e9f0ac)
1 #include "first.h"
2 
3 #include "response.h"
4 #include "base.h"
5 #include "burl.h"
6 #include "fdevent.h"
7 #include "http_header.h"
8 #include "http_kv.h"
9 #include "log.h"
10 #include "stat_cache.h"
11 #include "chunk.h"
12 
13 #include "configfile.h"
14 
15 #include "plugin.h"
16 
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 
20 #include <limits.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 
26 int http_response_write_header(server *srv, connection *con) {
27 	buffer * const b = chunkqueue_prepend_buffer_open(con->write_queue);
28 
29 	if (con->request.http_version == HTTP_VERSION_1_1) {
30 		buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 "));
31 	} else {
32 		buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 "));
33 	}
34 	http_status_append(b, con->http_status);
35 
36 	/* disable keep-alive if requested */
37 	if (con->request_count > con->conf.max_keep_alive_requests || 0 == con->conf.max_keep_alive_idle) {
38 		con->keep_alive = 0;
39 	} else if (0 != con->request.content_length
40 		   && con->request.content_length != con->request_content_queue->bytes_in
41 		   && (con->mode == DIRECT || 0 == con->conf.stream_request_body)) {
42 		con->keep_alive = 0;
43 	} else {
44 		con->keep_alive_idle = con->conf.max_keep_alive_idle;
45 	}
46 
47 	if ((con->response.htags & HTTP_HEADER_UPGRADE) && con->request.http_version == HTTP_VERSION_1_1) {
48 		http_header_response_set(con, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection"), CONST_STR_LEN("upgrade"));
49 	} else if (0 == con->keep_alive) {
50 		http_header_response_set(con, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection"), CONST_STR_LEN("close"));
51 	} else if (con->request.http_version == HTTP_VERSION_1_0) {/*(&& con->keep_alive != 0)*/
52 		http_header_response_set(con, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive"));
53 	}
54 
55 	if (304 == con->http_status && (con->response.htags & HTTP_HEADER_CONTENT_ENCODING)) {
56 		http_header_response_unset(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"));
57 	}
58 
59 	/* add all headers */
60 	for (size_t i = 0; i < con->response.headers->used; ++i) {
61 		const data_string * const ds = (data_string *)con->response.headers->data[i];
62 
63 		if (buffer_string_is_empty(ds->value) || buffer_string_is_empty(ds->key)) continue;
64 		if ((ds->key->ptr[0] & 0xdf) == 'X') {
65 			if (0 == strncasecmp(ds->key->ptr, CONST_STR_LEN("X-Sendfile"))) continue;
66 			if (0 == strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-"))) {
67 				if (0 == strncasecmp(ds->key->ptr+sizeof("X-LIGHTTPD-")-1, CONST_STR_LEN("KBytes-per-second"))) {
68 					/* "X-LIGHTTPD-KBytes-per-second" */
69 					long limit = strtol(ds->value->ptr, NULL, 10);
70 					if (limit > 0
71 					    && (limit < con->conf.kbytes_per_second
72 					        || 0 == con->conf.kbytes_per_second)) {
73 						if (limit > USHRT_MAX) limit= USHRT_MAX;
74 						con->conf.kbytes_per_second = limit;
75 					}
76 				}
77 				continue;
78 			}
79 		}
80 
81 		buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
82 		buffer_append_string_buffer(b, ds->key);
83 		buffer_append_string_len(b, CONST_STR_LEN(": "));
84 		buffer_append_string_buffer(b, ds->value);
85 	}
86 
87 	if (!(con->response.htags & HTTP_HEADER_DATE)) {
88 		/* HTTP/1.1 requires a Date: header */
89 		buffer_append_string_len(b, CONST_STR_LEN("\r\nDate: "));
90 
91 		/* cache the generated timestamp */
92 		if (srv->cur_ts != srv->last_generated_date_ts) {
93 			buffer_clear(srv->ts_date_str);
94 			buffer_append_strftime(srv->ts_date_str, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts)));
95 			srv->last_generated_date_ts = srv->cur_ts;
96 		}
97 
98 		buffer_append_string_buffer(b, srv->ts_date_str);
99 	}
100 
101 	if (!(con->response.htags & HTTP_HEADER_SERVER)) {
102 		if (!buffer_string_is_empty(con->conf.server_tag)) {
103 			buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: "));
104 			buffer_append_string_len(b, CONST_BUF_LEN(con->conf.server_tag));
105 		}
106 	}
107 
108 	buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n"));
109 
110 	con->bytes_header = buffer_string_length(b);
111 
112 	if (con->conf.log_response_header) {
113 		log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b);
114 	}
115 
116 	chunkqueue_prepend_buffer_commit(con->write_queue);
117 	return 0;
118 }
119 
120 static handler_t http_response_physical_path_check(server *srv, connection *con) {
121 	stat_cache_entry *sce = NULL;
122 
123 	if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
124 		/* file exists */
125 	} else {
126 		char *pathinfo = NULL;
127 		switch (errno) {
128 		case EACCES:
129 			con->http_status = 403;
130 
131 			if (con->conf.log_request_handling) {
132 				log_error_write(srv, __FILE__, __LINE__,  "s",  "-- access denied");
133 				log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", con->physical.path);
134 			}
135 
136 			buffer_reset(con->physical.path);
137 			return HANDLER_FINISHED;
138 		case ENAMETOOLONG:
139 			/* file name to be read was too long. return 404 */
140 		case ENOENT:
141 			if (con->request.http_method == HTTP_METHOD_OPTIONS
142 			    && NULL != http_header_response_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Allow"))) {
143 				con->http_status = 200;
144 				return HANDLER_FINISHED;
145 			}
146 
147 			con->http_status = 404;
148 
149 			if (con->conf.log_request_handling) {
150 				log_error_write(srv, __FILE__, __LINE__,  "s",  "-- file not found");
151 				log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", con->physical.path);
152 			}
153 
154 			buffer_reset(con->physical.path);
155 			return HANDLER_FINISHED;
156 		case ENOTDIR:
157 			/* PATH_INFO ! :) */
158 			break;
159 		default:
160 			/* we have no idea what happend. let's tell the user so. */
161 			log_error_write(srv, __FILE__, __LINE__, "ssbsb",
162 					"file not found ... or so: ", strerror(errno),
163 					con->uri.path,
164 					"->", con->physical.path);
165 
166 			con->http_status = 500;
167 			buffer_reset(con->physical.path);
168 
169 			return HANDLER_FINISHED;
170 		}
171 
172 		/* not found, perhaps PATHINFO */
173 
174 		{
175 			/*(might check at startup that s->document_root does not end in '/')*/
176 			size_t len = buffer_string_length(con->physical.basedir);
177 			if (len > 0 && '/' == con->physical.basedir->ptr[len-1]) --len;
178 			pathinfo = con->physical.path->ptr + len;
179 			if ('/' != *pathinfo) {
180 				pathinfo = NULL;
181 			}
182 			else if (pathinfo == con->physical.path->ptr) { /*(basedir is "/")*/
183 				pathinfo = strchr(pathinfo+1, '/');
184 			}
185 		}
186 
187 		for (char *pprev = pathinfo; pathinfo; pprev = pathinfo, pathinfo = strchr(pathinfo+1, '/')) {
188 			stat_cache_entry *nsce = NULL;
189 			buffer_copy_string_len(srv->tmp_buf, con->physical.path->ptr, pathinfo - con->physical.path->ptr);
190 			if (HANDLER_ERROR == stat_cache_get_entry(srv, con, srv->tmp_buf, &nsce)) {
191 				pathinfo = pathinfo != pprev ? pprev : NULL;
192 				break;
193 			}
194 			sce = nsce;
195 			if (!S_ISDIR(sce->st.st_mode)) break;
196 		}
197 
198 		if (NULL == pathinfo || !S_ISREG(sce->st.st_mode)) {
199 			/* no it really doesn't exists */
200 			con->http_status = 404;
201 
202 			if (con->conf.log_file_not_found) {
203 				log_error_write(srv, __FILE__, __LINE__, "sbsb",
204 						"file not found:", con->uri.path,
205 						"->", con->physical.path);
206 			}
207 
208 			buffer_reset(con->physical.path);
209 
210 			return HANDLER_FINISHED;
211 		}
212 
213 		/* we have a PATHINFO */
214 		if (pathinfo) {
215 			size_t len = strlen(pathinfo), reqlen;
216 			if (con->conf.force_lowercase_filenames
217 			    && len <= (reqlen = buffer_string_length(con->request.uri))
218 			    && 0 == strncasecmp(con->request.uri->ptr + reqlen - len, pathinfo, len)) {
219 				/* attempt to preserve case-insensitive PATH_INFO
220 				 * (works in common case where mod_alias, mod_magnet, and other modules
221 				 *  have not modified the PATH_INFO portion of request URI, or did so
222 				 *  with exactly the PATH_INFO desired) */
223 				buffer_copy_string_len(con->request.pathinfo, con->request.uri->ptr + reqlen - len, len);
224 			} else {
225 				buffer_copy_string_len(con->request.pathinfo, pathinfo, len);
226 			}
227 
228 			/*
229 			 * shorten uri.path
230 			 */
231 
232 			buffer_string_set_length(con->uri.path, buffer_string_length(con->uri.path) - len);
233 			buffer_string_set_length(con->physical.path, (size_t)(pathinfo - con->physical.path->ptr));
234 		}
235 	}
236 
237 	if (!con->conf.follow_symlink
238 	    && 0 != stat_cache_path_contains_symlink(srv, con->physical.path)) {
239 		con->http_status = 403;
240 
241 		if (con->conf.log_request_handling) {
242 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- access denied due symlink restriction");
243 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", con->physical.path);
244 		}
245 
246 		buffer_reset(con->physical.path);
247 		return HANDLER_FINISHED;
248 	}
249 
250 	if (S_ISDIR(sce->st.st_mode)) {
251 		if (con->uri.path->ptr[buffer_string_length(con->uri.path) - 1] != '/') {
252 			/* redirect to .../ */
253 
254 			http_response_redirect_to_directory(srv, con, 301);
255 
256 			return HANDLER_FINISHED;
257 		}
258 	} else if (!S_ISREG(sce->st.st_mode)) {
259 		/* any special handling of non-reg files ?*/
260 	}
261 
262 	return HANDLER_GO_ON;
263 }
264 
265 handler_t http_response_prepare(server *srv, connection *con) {
266 	handler_t r;
267 
268 	/* looks like someone has already done a decision */
269 	if (con->mode == DIRECT &&
270 	    (con->http_status != 0 && con->http_status != 200)) {
271 		/* remove a packets in the queue */
272 		if (con->file_finished == 0) {
273 			http_response_body_clear(con, 0);
274 		}
275 
276 		return HANDLER_FINISHED;
277 	}
278 
279 	/* no decision yet, build conf->filename */
280 	if (con->mode == DIRECT && buffer_is_empty(con->physical.path)) {
281 
282 		/* we only come here when we have the parse the full request again
283 		 *
284 		 * a HANDLER_COMEBACK from mod_rewrite and mod_fastcgi might be a
285 		 * problem here as mod_setenv might get called multiple times
286 		 *
287 		 * fastcgi-auth might lead to a COMEBACK too
288 		 * fastcgi again dead server too
289 		 *
290 		 * mod_compress might add headers twice too
291 		 *
292 		 *  */
293 
294 	    if (!con->async_callback) {
295 
296 		config_cond_cache_reset(srv, con);
297 		config_setup_connection(srv, con); /* Perhaps this could be removed at other places. */
298 
299 		if (con->conf.log_condition_handling) {
300 			log_error_write(srv, __FILE__, __LINE__,  "s",  "run condition");
301 		}
302 
303 		/**
304 		 * prepare strings
305 		 *
306 		 * - uri.path_raw
307 		 * - uri.path
308 		 * - uri.query
309 		 *
310 		 */
311 
312 		/**
313 		 * Name according to RFC 2396
314 		 *
315 		 * - scheme
316 		 * - authority
317 		 * - path
318 		 * - query
319 		 *
320 		 * (scheme)://(authority)(path)?(query)#fragment
321 		 *
322 		 *
323 		 */
324 
325 		/* take initial scheme value from connection-level state
326 		 * (request con->uri.scheme can be overwritten for later,
327 		 *  for example by mod_extforward or mod_magnet) */
328 		buffer_copy_buffer(con->uri.scheme, con->proto);
329 		buffer_copy_buffer(con->uri.authority, con->request.http_host);
330 		buffer_to_lower(con->uri.authority);
331 
332 		if (con->request.http_method == HTTP_METHOD_CONNECT
333 		    || (con->request.http_method == HTTP_METHOD_OPTIONS
334 			&& con->request.uri->ptr[0] == '*'
335 			&& con->request.uri->ptr[1] == '\0')) {
336 			/* CONNECT ... (or) OPTIONS * ... */
337 			buffer_copy_buffer(con->uri.path_raw, con->request.uri);
338 			buffer_copy_buffer(con->uri.path, con->uri.path_raw);
339 			buffer_reset(con->uri.query);
340 		} else {
341 			char *qstr;
342 			if (con->conf.http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE) {
343 				/*size_t len = buffer_string_length(con->request.uri);*/
344 				int qs = burl_normalize(con->request.uri, srv->tmp_buf, con->conf.http_parseopts);
345 				if (-2 == qs) {
346 					log_error_write(srv, __FILE__, __LINE__, "sb",
347 							"invalid character in URI -> 400",
348 							con->request.uri);
349 					con->keep_alive = 0;
350 					con->http_status = 400; /* Bad Request */
351 					con->file_finished = 1;
352 					return HANDLER_FINISHED;
353 				}
354 				qstr = (-1 == qs) ? NULL : con->request.uri->ptr+qs;
355 			      #if 0  /* future: might enable here, or below for all requests */
356 				/* (Note: total header size not recalculated on HANDLER_COMEBACK
357 				 *  even if other request headers changed during processing)
358 				 * (If (0 != con->loops_per_request), then the generated request
359 				 *  is too large.  Should a different error be returned?) */
360 				con->header_len -= len;
361 				len = buffer_string_length(con->request.uri);
362 				con->header_len += len;
363 				if (len > MAX_HTTP_REQUEST_URI) {
364 					con->keep_alive = 0;
365 					con->http_status = 414; /* Request-URI Too Long */
366 					con->file_finished = 1;
367 					return HANDLER_FINISHED;
368 				}
369 				if (con->header_len > MAX_HTTP_REQUEST_HEADER) {
370 					log_error_write(srv, __FILE__, __LINE__, "sds",
371 							"request header fields too large:", con->header_len, "-> 431");
372 					con->keep_alive = 0;
373 					con->http_status = 431; /* Request Header Fields Too Large */
374 					con->file_finished = 1;
375 					return HANDLER_FINISHED;
376 				}
377 			      #endif
378 			} else {
379 				size_t rlen = buffer_string_length(con->request.uri);
380 				qstr = memchr(con->request.uri->ptr, '#', rlen);/* discard fragment */
381 				if (qstr) {
382 					rlen = (size_t)(qstr - con->request.uri->ptr);
383 					buffer_string_set_length(con->request.uri, rlen);
384 				}
385 				qstr = memchr(con->request.uri->ptr, '?', rlen);
386 			}
387 
388 			/** extract query string from request.uri */
389 			if (NULL != qstr) {
390 				const char * const pstr = con->request.uri->ptr;
391 				const size_t plen = (size_t)(qstr - pstr);
392 				const size_t rlen = buffer_string_length(con->request.uri);
393 				buffer_copy_string_len(con->uri.query, qstr + 1, rlen - plen - 1);
394 				buffer_copy_string_len(con->uri.path_raw, pstr, plen);
395 			} else {
396 				buffer_reset(con->uri.query);
397 				buffer_copy_buffer(con->uri.path_raw, con->request.uri);
398 			}
399 
400 			/* decode url to path
401 			 *
402 			 * - decode url-encodings  (e.g. %20 -> ' ')
403 			 * - remove path-modifiers (e.g. /../)
404 			 */
405 
406 			buffer_copy_buffer(con->uri.path, con->uri.path_raw);
407 			buffer_urldecode_path(con->uri.path);
408 			buffer_path_simplify(con->uri.path, con->uri.path);
409 			if (buffer_string_is_empty(con->uri.path) || con->uri.path->ptr[0] != '/') {
410 				log_error_write(srv, __FILE__, __LINE__, "sbs",
411 						"uri-path does not begin with '/':", con->uri.path, "-> 400");
412 				con->keep_alive = 0;
413 				con->http_status = 400;
414 				con->file_finished = 1;
415 				return HANDLER_FINISHED;
416 			}
417 		}
418 
419 		con->conditional_is_valid[COMP_SERVER_SOCKET] = 1;       /* SERVERsocket */
420 		con->conditional_is_valid[COMP_HTTP_SCHEME] = 1;         /* Scheme:      */
421 		con->conditional_is_valid[COMP_HTTP_HOST] = 1;           /* Host:        */
422 		con->conditional_is_valid[COMP_HTTP_REMOTE_IP] = 1;      /* Client-IP */
423 		con->conditional_is_valid[COMP_HTTP_REQUEST_METHOD] = 1; /* REQUEST_METHOD */
424 		con->conditional_is_valid[COMP_HTTP_URL] = 1;            /* HTTPurl */
425 		con->conditional_is_valid[COMP_HTTP_QUERY_STRING] = 1;   /* HTTPqs */
426 		con->conditional_is_valid[COMP_HTTP_REQUEST_HEADER] = 1; /* HTTP request header */
427 		config_patch_connection(srv, con);
428 
429 		/* do we have to downgrade to 1.0 ? */
430 		if (!con->conf.allow_http11) {
431 			con->request.http_version = HTTP_VERSION_1_0;
432 		}
433 
434 		if (con->conf.log_request_handling) {
435 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- splitting Request-URI");
436 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Request-URI     : ", con->request.uri);
437 			log_error_write(srv, __FILE__, __LINE__,  "sb", "URI-scheme      : ", con->uri.scheme);
438 			log_error_write(srv, __FILE__, __LINE__,  "sb", "URI-authority   : ", con->uri.authority);
439 			log_error_write(srv, __FILE__, __LINE__,  "sb", "URI-path (raw)  : ", con->uri.path_raw);
440 			log_error_write(srv, __FILE__, __LINE__,  "sb", "URI-path (clean): ", con->uri.path);
441 			log_error_write(srv, __FILE__, __LINE__,  "sb", "URI-query       : ", con->uri.query);
442 		}
443 
444 		/* con->conf.max_request_size is in kBytes */
445 		if (0 != con->conf.max_request_size &&
446 		    (off_t)con->request.content_length > ((off_t)con->conf.max_request_size << 10)) {
447 			log_error_write(srv, __FILE__, __LINE__, "sos",
448 					"request-size too long:", (off_t) con->request.content_length, "-> 413");
449 			con->keep_alive = 0;
450 			con->http_status = 413;
451 			con->file_finished = 1;
452 
453 			return HANDLER_FINISHED;
454 		}
455 
456 
457 	    }
458 	    con->async_callback = 0; /* reset */
459 
460 
461 		/**
462 		 *
463 		 * call plugins
464 		 *
465 		 * - based on the raw URL
466 		 *
467 		 */
468 
469 		switch(r = plugins_call_handle_uri_raw(srv, con)) {
470 		case HANDLER_GO_ON:
471 			break;
472 		case HANDLER_FINISHED:
473 		case HANDLER_COMEBACK:
474 		case HANDLER_WAIT_FOR_EVENT:
475 		case HANDLER_ERROR:
476 			return r;
477 		default:
478 			log_error_write(srv, __FILE__, __LINE__, "sd", "handle_uri_raw: unknown return value", r);
479 			break;
480 		}
481 
482 		/**
483 		 *
484 		 * call plugins
485 		 *
486 		 * - based on the clean URL
487 		 *
488 		 */
489 
490 		switch(r = plugins_call_handle_uri_clean(srv, con)) {
491 		case HANDLER_GO_ON:
492 			break;
493 		case HANDLER_FINISHED:
494 		case HANDLER_COMEBACK:
495 		case HANDLER_WAIT_FOR_EVENT:
496 		case HANDLER_ERROR:
497 			return r;
498 		default:
499 			log_error_write(srv, __FILE__, __LINE__, "");
500 			break;
501 		}
502 
503 		if (con->request.http_method == HTTP_METHOD_OPTIONS &&
504 		    con->uri.path->ptr[0] == '*' && con->uri.path->ptr[1] == '\0') {
505 			/* option requests are handled directly without checking of the path */
506 
507 			http_header_response_append(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));
508 
509 			con->http_status = 200;
510 			con->file_finished = 1;
511 
512 			return HANDLER_FINISHED;
513 		}
514 
515 		if (con->request.http_method == HTTP_METHOD_CONNECT && con->mode == DIRECT) {
516 			con->keep_alive = 0;
517 			con->http_status = 405; /* Method Not Allowed */
518 			con->file_finished = 1;
519 			return HANDLER_FINISHED;
520 		}
521 
522 		/***
523 		 *
524 		 * border
525 		 *
526 		 * logical filename (URI) becomes a physical filename here
527 		 *
528 		 *
529 		 *
530 		 */
531 
532 
533 
534 
535 		/* 1. stat()
536 		 * ... ISREG() -> ok, go on
537 		 * ... ISDIR() -> index-file -> redirect
538 		 *
539 		 * 2. pathinfo()
540 		 * ... ISREG()
541 		 *
542 		 * 3. -> 404
543 		 *
544 		 */
545 
546 		/*
547 		 * SEARCH DOCUMENT ROOT
548 		 */
549 
550 		/* set a default */
551 
552 		buffer_copy_buffer(con->physical.doc_root, con->conf.document_root);
553 		buffer_copy_buffer(con->physical.rel_path, con->uri.path);
554 
555 #if defined(__WIN32) || defined(__CYGWIN__)
556 		/* strip dots from the end and spaces
557 		 *
558 		 * windows/dos handle those filenames as the same file
559 		 *
560 		 * foo == foo. == foo..... == "foo...   " == "foo..  ./"
561 		 *
562 		 * This will affect in some cases PATHINFO
563 		 *
564 		 * on native windows we could prepend the filename with \\?\ to circumvent
565 		 * this behaviour. I have no idea how to push this through cygwin
566 		 *
567 		 * */
568 
569 		if (con->physical.rel_path->used > 1) {
570 			buffer *b = con->physical.rel_path;
571 			size_t len = buffer_string_length(b);
572 
573 			/* strip trailing " /" or "./" once */
574 			if (len > 1 &&
575 			    b->ptr[len - 1] == '/' &&
576 			    (b->ptr[len - 2] == ' ' || b->ptr[len - 2] == '.')) {
577 				len -= 2;
578 			}
579 			/* strip all trailing " " and "." */
580 			while (len > 0 &&  ( ' ' == b->ptr[len-1] || '.' == b->ptr[len-1] ) ) --len;
581 			buffer_string_set_length(b, len);
582 		}
583 #endif
584 
585 		if (con->conf.log_request_handling) {
586 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- before doc_root");
587 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Doc-Root     :", con->physical.doc_root);
588 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Rel-Path     :", con->physical.rel_path);
589 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", con->physical.path);
590 		}
591 		/* the docroot plugin should set the doc_root and might also set the physical.path
592 		 * for us (all vhost-plugins are supposed to set the doc_root)
593 		 * */
594 		switch(r = plugins_call_handle_docroot(srv, con)) {
595 		case HANDLER_GO_ON:
596 			break;
597 		case HANDLER_FINISHED:
598 		case HANDLER_COMEBACK:
599 		case HANDLER_WAIT_FOR_EVENT:
600 		case HANDLER_ERROR:
601 			return r;
602 		default:
603 			log_error_write(srv, __FILE__, __LINE__, "");
604 			break;
605 		}
606 
607 		/* MacOS X and Windows can't distiguish between upper and lower-case
608 		 *
609 		 * convert to lower-case
610 		 */
611 		if (con->conf.force_lowercase_filenames) {
612 			buffer_to_lower(con->physical.rel_path);
613 		}
614 
615 		/* the docroot plugins might set the servername, if they don't we take http-host */
616 		if (buffer_string_is_empty(con->server_name)) {
617 			buffer_copy_buffer(con->server_name, con->uri.authority);
618 		}
619 
620 		/**
621 		 * create physical filename
622 		 * -> physical.path = docroot + rel_path
623 		 *
624 		 */
625 
626 		buffer_copy_buffer(con->physical.basedir, con->physical.doc_root);
627 		buffer_copy_buffer(con->physical.path, con->physical.doc_root);
628 		buffer_append_path_len(con->physical.path, CONST_BUF_LEN(con->physical.rel_path));
629 
630 		if (con->conf.log_request_handling) {
631 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- after doc_root");
632 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Doc-Root     :", con->physical.doc_root);
633 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Rel-Path     :", con->physical.rel_path);
634 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", con->physical.path);
635 		}
636 
637 		if (con->request.http_method == HTTP_METHOD_CONNECT) {
638 			/* do not permit CONNECT requests to hit filesystem hooks
639 			 * since the CONNECT URI bypassed path normalization */
640 			/* (This check is located here so that con->physical.path
641 			 *  is filled in above to avoid repeating work next time
642 			 *  http_response_prepare() is called while processing request) */
643 		} else
644 		switch(r = plugins_call_handle_physical(srv, con)) {
645 		case HANDLER_GO_ON:
646 			break;
647 		case HANDLER_FINISHED:
648 		case HANDLER_COMEBACK:
649 		case HANDLER_WAIT_FOR_EVENT:
650 		case HANDLER_ERROR:
651 			return r;
652 		default:
653 			log_error_write(srv, __FILE__, __LINE__, "");
654 			break;
655 		}
656 
657 		if (con->conf.log_request_handling) {
658 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- logical -> physical");
659 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Doc-Root     :", con->physical.doc_root);
660 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Basedir      :", con->physical.basedir);
661 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Rel-Path     :", con->physical.rel_path);
662 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", con->physical.path);
663 		}
664 	}
665 
666 	/*
667 	 * Noone catched away the file from normal path of execution yet (like mod_access)
668 	 *
669 	 * Go on and check of the file exists at all
670 	 */
671 
672 	if (con->mode == DIRECT) {
673 		if (con->conf.log_request_handling) {
674 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- handling physical path");
675 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", con->physical.path);
676 		}
677 
678 		r = http_response_physical_path_check(srv, con);
679 		if (HANDLER_GO_ON != r) return r;
680 
681 		if (con->conf.log_request_handling) {
682 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- handling subrequest");
683 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", con->physical.path);
684 			log_error_write(srv, __FILE__, __LINE__,  "sb", "URI          :", con->uri.path);
685 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Pathinfo     :", con->request.pathinfo);
686 		}
687 
688 		/* call the handlers */
689 		r = plugins_call_handle_subrequest_start(srv, con);
690 		if (HANDLER_GO_ON != r) {
691 			if (con->conf.log_request_handling) {
692 				log_error_write(srv, __FILE__, __LINE__,  "s",  "-- subrequest finished");
693 			}
694 			return r;
695 		}
696 
697 		/* if we are still here, no one wanted the file, status 403 is ok I think */
698 		if (con->mode == DIRECT && con->http_status == 0) {
699 			con->http_status = (con->request.http_method != HTTP_METHOD_OPTIONS) ? 403 : 200;
700 			return HANDLER_FINISHED;
701 		}
702 
703 	}
704 
705 	r = plugins_call_handle_subrequest(srv, con);
706 	if (HANDLER_GO_ON == r) r = HANDLER_FINISHED; /* request was not handled, looks like we are done */
707 	return r;
708 }
709