7 #include "stat_cache.h"
10 #include "configfile.h"
14 #include <sys/types.h>
23 int http_response_write_header(server
*srv
, connection
*con
) {
31 if (con
->request
.http_version
== HTTP_VERSION_1_1
) {
32 buffer_copy_string_len(b
, CONST_STR_LEN("HTTP/1.1 "));
34 buffer_copy_string_len(b
, CONST_STR_LEN("HTTP/1.0 "));
36 buffer_append_int(b
, con
->http_status
);
37 buffer_append_string_len(b
, CONST_STR_LEN(" "));
38 buffer_append_string(b
, get_http_status_name(con
->http_status
));
40 /* disable keep-alive if requested */
41 if (con
->request_count
> con
->conf
.max_keep_alive_requests
|| 0 == con
->conf
.max_keep_alive_idle
) {
44 con
->keep_alive_idle
= con
->conf
.max_keep_alive_idle
;
47 if ((con
->parsed_response
& HTTP_UPGRADE
) && con
->request
.http_version
== HTTP_VERSION_1_1
) {
48 response_header_overwrite(srv
, con
, CONST_STR_LEN("Connection"), CONST_STR_LEN("upgrade"));
49 } else if (0 == con
->keep_alive
) {
50 response_header_overwrite(srv
, con
, CONST_STR_LEN("Connection"), CONST_STR_LEN("close"));
51 } else if (con
->request
.http_version
== HTTP_VERSION_1_0
) {/*(&& con->keep_alive != 0)*/
52 response_header_overwrite(srv
, con
, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive"));
56 for (i
= 0; i
< con
->response
.headers
->used
; i
++) {
59 ds
= (data_string
*)con
->response
.headers
->data
[i
];
61 if (buffer_string_is_empty(ds
->value
) || buffer_string_is_empty(ds
->key
)) continue;
62 if (0 == strncasecmp(ds
->key
->ptr
, CONST_STR_LEN("X-Sendfile"))) continue;
63 if (0 == strncasecmp(ds
->key
->ptr
, CONST_STR_LEN("X-LIGHTTPD-"))) {
64 if (0 == strncasecmp(ds
->key
->ptr
+sizeof("X-LIGHTTPD-")-1, CONST_STR_LEN("KBytes-per-second"))) {
65 /* "X-LIGHTTPD-KBytes-per-second" */
66 long limit
= strtol(ds
->value
->ptr
, NULL
, 10);
68 && (limit
< con
->conf
.kbytes_per_second
69 || 0 == con
->conf
.kbytes_per_second
)) {
70 if (limit
> USHRT_MAX
) limit
= USHRT_MAX
;
71 con
->conf
.kbytes_per_second
= limit
;
76 if (0 == strcasecmp(ds
->key
->ptr
, "Date")) have_date
= 1;
77 if (0 == strcasecmp(ds
->key
->ptr
, "Server")) have_server
= 1;
78 if (0 == strcasecmp(ds
->key
->ptr
, "Content-Encoding") && 304 == con
->http_status
) continue;
80 buffer_append_string_len(b
, CONST_STR_LEN("\r\n"));
81 buffer_append_string_buffer(b
, ds
->key
);
82 buffer_append_string_len(b
, CONST_STR_LEN(": "));
85 * the value might contain newlines, encode them with at least one white-space
87 buffer_append_string_encoded(b
, CONST_BUF_LEN(ds
->value
), ENCODING_HTTP_HEADER
);
89 buffer_append_string_buffer(b
, ds
->value
);
95 /* HTTP/1.1 requires a Date: header */
96 buffer_append_string_len(b
, CONST_STR_LEN("\r\nDate: "));
98 /* cache the generated timestamp */
99 if (srv
->cur_ts
!= srv
->last_generated_date_ts
) {
100 buffer_string_prepare_copy(srv
->ts_date_str
, 255);
102 buffer_append_strftime(srv
->ts_date_str
, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv
->cur_ts
)));
104 srv
->last_generated_date_ts
= srv
->cur_ts
;
107 buffer_append_string_buffer(b
, srv
->ts_date_str
);
111 if (!buffer_string_is_empty(con
->conf
.server_tag
)) {
112 buffer_append_string_len(b
, CONST_STR_LEN("\r\nServer: "));
113 buffer_append_string_encoded(b
, CONST_BUF_LEN(con
->conf
.server_tag
), ENCODING_HTTP_HEADER
);
117 buffer_append_string_len(b
, CONST_STR_LEN("\r\n\r\n"));
119 con
->bytes_header
= buffer_string_length(b
);
121 if (con
->conf
.log_response_header
) {
122 log_error_write(srv
, __FILE__
, __LINE__
, "sSb", "Response-Header:", "\n", b
);
125 chunkqueue_prepend_buffer(con
->write_queue
, b
);
131 handler_t
http_response_prepare(server
*srv
, connection
*con
) {
134 /* looks like someone has already done a decision */
135 if (con
->mode
== DIRECT
&&
136 (con
->http_status
!= 0 && con
->http_status
!= 200)) {
137 /* remove a packets in the queue */
138 if (con
->file_finished
== 0) {
139 chunkqueue_reset(con
->write_queue
);
142 return HANDLER_FINISHED
;
145 /* no decision yet, build conf->filename */
146 if (con
->mode
== DIRECT
&& buffer_is_empty(con
->physical
.path
)) {
149 /* we only come here when we have the parse the full request again
151 * a HANDLER_COMEBACK from mod_rewrite and mod_fastcgi might be a
152 * problem here as mod_setenv might get called multiple times
154 * fastcgi-auth might lead to a COMEBACK too
155 * fastcgi again dead server too
157 * mod_compress might add headers twice too
161 config_cond_cache_reset(srv
, con
);
162 config_setup_connection(srv
, con
); /* Perhaps this could be removed at other places. */
164 if (con
->conf
.log_condition_handling
) {
165 log_error_write(srv
, __FILE__
, __LINE__
, "s", "run condition");
172 * - uri.path (secure)
178 * Name according to RFC 2396
185 * (scheme)://(authority)(path)?(query)#fragment
190 /* take initial scheme value from connection-level state
191 * (request con->uri.scheme can be overwritten for later,
192 * for example by mod_extforward or mod_magnet) */
193 buffer_copy_buffer(con
->uri
.scheme
, con
->proto
);
194 buffer_copy_buffer(con
->uri
.authority
, con
->request
.http_host
);
195 buffer_to_lower(con
->uri
.authority
);
197 /** their might be a fragment which has to be cut away */
198 if (NULL
!= (qstr
= strchr(con
->request
.uri
->ptr
, '#'))) {
199 buffer_string_set_length(con
->request
.uri
, qstr
- con
->request
.uri
->ptr
);
202 /** extract query string from request.uri */
203 if (NULL
!= (qstr
= strchr(con
->request
.uri
->ptr
, '?'))) {
204 buffer_copy_string (con
->uri
.query
, qstr
+ 1);
205 buffer_copy_string_len(con
->uri
.path_raw
, con
->request
.uri
->ptr
, qstr
- con
->request
.uri
->ptr
);
207 buffer_reset (con
->uri
.query
);
208 buffer_copy_buffer(con
->uri
.path_raw
, con
->request
.uri
);
211 /* decode url to path
213 * - decode url-encodings (e.g. %20 -> ' ')
214 * - remove path-modifiers (e.g. /../)
217 if (con
->request
.http_method
== HTTP_METHOD_OPTIONS
&&
218 con
->uri
.path_raw
->ptr
[0] == '*' && con
->uri
.path_raw
->ptr
[1] == '\0') {
220 buffer_copy_buffer(con
->uri
.path
, con
->uri
.path_raw
);
222 buffer_copy_buffer(srv
->tmp_buf
, con
->uri
.path_raw
);
223 buffer_urldecode_path(srv
->tmp_buf
);
224 buffer_path_simplify(con
->uri
.path
, srv
->tmp_buf
);
227 con
->conditional_is_valid
[COMP_SERVER_SOCKET
] = 1; /* SERVERsocket */
228 con
->conditional_is_valid
[COMP_HTTP_SCHEME
] = 1; /* Scheme: */
229 con
->conditional_is_valid
[COMP_HTTP_HOST
] = 1; /* Host: */
230 con
->conditional_is_valid
[COMP_HTTP_REMOTE_IP
] = 1; /* Client-IP */
231 con
->conditional_is_valid
[COMP_HTTP_REQUEST_METHOD
] = 1; /* REQUEST_METHOD */
232 con
->conditional_is_valid
[COMP_HTTP_URL
] = 1; /* HTTPurl */
233 con
->conditional_is_valid
[COMP_HTTP_QUERY_STRING
] = 1; /* HTTPqs */
234 con
->conditional_is_valid
[COMP_HTTP_REQUEST_HEADER
] = 1; /* HTTP request header */
235 config_patch_connection(srv
, con
);
237 /* do we have to downgrade to 1.0 ? */
238 if (!con
->conf
.allow_http11
) {
239 con
->request
.http_version
= HTTP_VERSION_1_0
;
242 if (con
->conf
.log_request_handling
) {
243 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- splitting Request-URI");
244 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Request-URI : ", con
->request
.uri
);
245 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "URI-scheme : ", con
->uri
.scheme
);
246 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "URI-authority : ", con
->uri
.authority
);
247 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "URI-path (raw) : ", con
->uri
.path_raw
);
248 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "URI-path (clean): ", con
->uri
.path
);
249 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "URI-query : ", con
->uri
.query
);
252 /* con->conf.max_request_size is in kBytes */
253 if (0 != con
->conf
.max_request_size
&&
254 (off_t
)con
->request
.content_length
> ((off_t
)con
->conf
.max_request_size
<< 10)) {
255 log_error_write(srv
, __FILE__
, __LINE__
, "sos",
256 "request-size too long:", (off_t
) con
->request
.content_length
, "-> 413");
258 con
->http_status
= 413;
259 con
->file_finished
= 1;
261 return HANDLER_FINISHED
;
269 * - based on the raw URL
273 switch(r
= plugins_call_handle_uri_raw(srv
, con
)) {
276 case HANDLER_FINISHED
:
277 case HANDLER_COMEBACK
:
278 case HANDLER_WAIT_FOR_EVENT
:
282 log_error_write(srv
, __FILE__
, __LINE__
, "sd", "handle_uri_raw: unknown return value", r
);
290 * - based on the clean URL
294 switch(r
= plugins_call_handle_uri_clean(srv
, con
)) {
297 case HANDLER_FINISHED
:
298 case HANDLER_COMEBACK
:
299 case HANDLER_WAIT_FOR_EVENT
:
303 log_error_write(srv
, __FILE__
, __LINE__
, "");
307 if (con
->request
.http_method
== HTTP_METHOD_OPTIONS
&&
308 con
->uri
.path
->ptr
[0] == '*' && con
->uri
.path_raw
->ptr
[1] == '\0') {
309 /* option requests are handled directly without checking of the path */
311 response_header_insert(srv
, con
, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));
313 con
->http_status
= 200;
314 con
->file_finished
= 1;
316 return HANDLER_FINISHED
;
323 * logical filename (URI) becomes a physical filename here
333 * ... ISREG() -> ok, go on
334 * ... ISDIR() -> index-file -> redirect
344 * SEARCH DOCUMENT ROOT
349 buffer_copy_buffer(con
->physical
.doc_root
, con
->conf
.document_root
);
350 buffer_copy_buffer(con
->physical
.rel_path
, con
->uri
.path
);
352 #if defined(__WIN32) || defined(__CYGWIN__)
353 /* strip dots from the end and spaces
355 * windows/dos handle those filenames as the same file
357 * foo == foo. == foo..... == "foo... " == "foo.. ./"
359 * This will affect in some cases PATHINFO
361 * on native windows we could prepend the filename with \\?\ to circumvent
362 * this behaviour. I have no idea how to push this through cygwin
366 if (con
->physical
.rel_path
->used
> 1) {
367 buffer
*b
= con
->physical
.rel_path
;
368 size_t len
= buffer_string_length(b
);
370 /* strip trailing " /" or "./" once */
372 b
->ptr
[len
- 1] == '/' &&
373 (b
->ptr
[len
- 2] == ' ' || b
->ptr
[len
- 2] == '.')) {
376 /* strip all trailing " " and "." */
377 while (len
> 0 && ( ' ' == b
->ptr
[len
-1] || '.' == b
->ptr
[len
-1] ) ) --len
;
378 buffer_string_set_length(b
, len
);
382 if (con
->conf
.log_request_handling
) {
383 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- before doc_root");
384 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Doc-Root :", con
->physical
.doc_root
);
385 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Rel-Path :", con
->physical
.rel_path
);
386 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
388 /* the docroot plugin should set the doc_root and might also set the physical.path
389 * for us (all vhost-plugins are supposed to set the doc_root)
391 switch(r
= plugins_call_handle_docroot(srv
, con
)) {
394 case HANDLER_FINISHED
:
395 case HANDLER_COMEBACK
:
396 case HANDLER_WAIT_FOR_EVENT
:
400 log_error_write(srv
, __FILE__
, __LINE__
, "");
404 /* MacOS X and Windows can't distiguish between upper and lower-case
406 * convert to lower-case
408 if (con
->conf
.force_lowercase_filenames
) {
409 buffer_to_lower(con
->physical
.rel_path
);
412 /* the docroot plugins might set the servername, if they don't we take http-host */
413 if (buffer_string_is_empty(con
->server_name
)) {
414 buffer_copy_buffer(con
->server_name
, con
->uri
.authority
);
418 * create physical filename
419 * -> physical.path = docroot + rel_path
423 buffer_copy_buffer(con
->physical
.basedir
, con
->physical
.doc_root
);
424 buffer_copy_buffer(con
->physical
.path
, con
->physical
.doc_root
);
425 buffer_append_slash(con
->physical
.path
);
426 if (!buffer_string_is_empty(con
->physical
.rel_path
) &&
427 con
->physical
.rel_path
->ptr
[0] == '/') {
429 if (buffer_string_length(con
->physical
.rel_path
) < 1) return HANDLER_ERROR
;
431 /* coverity[overflow_sink : FALSE] */
432 buffer_append_string_len(con
->physical
.path
, con
->physical
.rel_path
->ptr
+ 1, buffer_string_length(con
->physical
.rel_path
) - 1);
434 buffer_append_string_buffer(con
->physical
.path
, con
->physical
.rel_path
);
437 if (con
->conf
.log_request_handling
) {
438 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- after doc_root");
439 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Doc-Root :", con
->physical
.doc_root
);
440 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Rel-Path :", con
->physical
.rel_path
);
441 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
444 switch(r
= plugins_call_handle_physical(srv
, con
)) {
447 case HANDLER_FINISHED
:
448 case HANDLER_COMEBACK
:
449 case HANDLER_WAIT_FOR_EVENT
:
453 log_error_write(srv
, __FILE__
, __LINE__
, "");
457 if (con
->conf
.log_request_handling
) {
458 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- logical -> physical");
459 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Doc-Root :", con
->physical
.doc_root
);
460 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Basedir :", con
->physical
.basedir
);
461 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Rel-Path :", con
->physical
.rel_path
);
462 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
467 * Noone catched away the file from normal path of execution yet (like mod_access)
469 * Go on and check of the file exists at all
472 if (con
->mode
== DIRECT
) {
474 char *pathinfo
= NULL
;
476 stat_cache_entry
*sce
= NULL
;
478 if (con
->conf
.log_request_handling
) {
479 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- handling physical path");
480 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
483 if (HANDLER_ERROR
!= stat_cache_get_entry(srv
, con
, con
->physical
.path
, &sce
)) {
486 if (con
->conf
.log_request_handling
) {
487 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- file found");
488 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
491 if ((sce
->is_symlink
!= 0) && !con
->conf
.follow_symlink
) {
492 con
->http_status
= 403;
494 if (con
->conf
.log_request_handling
) {
495 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- access denied due symlink restriction");
496 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
499 buffer_reset(con
->physical
.path
);
500 return HANDLER_FINISHED
;
503 if (S_ISDIR(sce
->st
.st_mode
)) {
504 if (con
->uri
.path
->ptr
[buffer_string_length(con
->uri
.path
) - 1] != '/') {
505 /* redirect to .../ */
507 http_response_redirect_to_directory(srv
, con
);
509 return HANDLER_FINISHED
;
512 } else if (!S_ISREG(sce
->st
.st_mode
) && !sce
->is_symlink
) {
514 } else if (!S_ISREG(sce
->st
.st_mode
)) {
516 /* any special handling of non-reg files ?*/
523 con
->http_status
= 403;
525 if (con
->conf
.log_request_handling
) {
526 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- access denied");
527 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
530 buffer_reset(con
->physical
.path
);
531 return HANDLER_FINISHED
;
533 /* file name to be read was too long. return 404 */
535 con
->http_status
= 404;
537 if (con
->conf
.log_request_handling
) {
538 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- file not found");
539 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
542 buffer_reset(con
->physical
.path
);
543 return HANDLER_FINISHED
;
548 /* we have no idea what happend. let's tell the user so. */
549 con
->http_status
= 500;
550 buffer_reset(con
->physical
.path
);
552 log_error_write(srv
, __FILE__
, __LINE__
, "ssbsb",
553 "file not found ... or so: ", strerror(errno
),
555 "->", con
->physical
.path
);
557 return HANDLER_FINISHED
;
560 /* not found, perhaps PATHINFO */
562 buffer_copy_buffer(srv
->tmp_buf
, con
->physical
.path
);
566 buffer_copy_string_len(con
->physical
.path
, srv
->tmp_buf
->ptr
, slash
- srv
->tmp_buf
->ptr
);
568 buffer_copy_buffer(con
->physical
.path
, srv
->tmp_buf
);
571 if (HANDLER_ERROR
!= stat_cache_get_entry(srv
, con
, con
->physical
.path
, &sce
)) {
572 found
= S_ISREG(sce
->st
.st_mode
);
576 if (pathinfo
!= NULL
) {
579 slash
= strrchr(srv
->tmp_buf
->ptr
, '/');
581 if (pathinfo
!= NULL
) {
586 if (slash
) pathinfo
= slash
;
587 } while ((found
== 0) && (slash
!= NULL
) && ((size_t)(slash
- srv
->tmp_buf
->ptr
) > (buffer_string_length(con
->physical
.basedir
) - 1)));
590 /* no it really doesn't exists */
591 con
->http_status
= 404;
593 if (con
->conf
.log_file_not_found
) {
594 log_error_write(srv
, __FILE__
, __LINE__
, "sbsb",
595 "file not found:", con
->uri
.path
,
596 "->", con
->physical
.path
);
599 buffer_reset(con
->physical
.path
);
601 return HANDLER_FINISHED
;
605 if ((sce
->is_symlink
!= 0) && !con
->conf
.follow_symlink
) {
606 con
->http_status
= 403;
608 if (con
->conf
.log_request_handling
) {
609 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- access denied due symlink restriction");
610 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
613 buffer_reset(con
->physical
.path
);
614 return HANDLER_FINISHED
;
618 /* we have a PATHINFO */
620 size_t len
= strlen(pathinfo
), reqlen
;
621 if (con
->conf
.force_lowercase_filenames
622 && len
<= (reqlen
= buffer_string_length(con
->request
.uri
))
623 && 0 == strncasecmp(con
->request
.uri
->ptr
+ reqlen
- len
, pathinfo
, len
)) {
624 /* attempt to preserve case-insensitive PATH_INFO
625 * (works in common case where mod_alias, mod_magnet, and other modules
626 * have not modified the PATH_INFO portion of request URI, or did so
627 * with exactly the PATH_INFO desired) */
628 buffer_copy_string_len(con
->request
.pathinfo
, con
->request
.uri
->ptr
+ reqlen
- len
, len
);
630 buffer_copy_string_len(con
->request
.pathinfo
, pathinfo
, len
);
637 buffer_string_set_length(con
->uri
.path
, buffer_string_length(con
->uri
.path
) - len
);
640 if (con
->conf
.log_request_handling
) {
641 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- after pathinfo check");
642 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
643 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "URI :", con
->uri
.path
);
644 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Pathinfo :", con
->request
.pathinfo
);
648 if (con
->conf
.log_request_handling
) {
649 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- handling subrequest");
650 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", con
->physical
.path
);
653 /* call the handlers */
654 switch(r
= plugins_call_handle_subrequest_start(srv
, con
)) {
656 /* request was not handled */
658 case HANDLER_FINISHED
:
660 if (con
->conf
.log_request_handling
) {
661 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- subrequest finished");
664 /* something strange happend */
668 /* if we are still here, no one wanted the file, status 403 is ok I think */
670 if (con
->mode
== DIRECT
&& con
->http_status
== 0) {
671 switch (con
->request
.http_method
) {
672 case HTTP_METHOD_OPTIONS
:
673 con
->http_status
= 200;
676 con
->http_status
= 403;
679 return HANDLER_FINISHED
;
684 switch(r
= plugins_call_handle_subrequest(srv
, con
)) {
686 /* request was not handled, looks like we are done */
687 return HANDLER_FINISHED
;
688 case HANDLER_FINISHED
:
689 /* request is finished */
691 /* something strange happend */
696 return HANDLER_COMEBACK
;