9 #include "http_chunk.h"
11 #include "sock_addr.h"
12 #include "stat_cache.h"
19 #include "sys-strings.h"
20 #include "sys-socket.h"
24 int response_header_insert(server
*srv
, connection
*con
, const char *key
, size_t keylen
, const char *value
, size_t vallen
) {
29 if (NULL
== (ds
= (data_string
*)array_get_unused_element(con
->response
.headers
, TYPE_STRING
))) {
30 ds
= data_response_init();
32 buffer_copy_string_len(ds
->key
, key
, keylen
);
33 buffer_copy_string_len(ds
->value
, value
, vallen
);
35 array_insert_unique(con
->response
.headers
, (data_unset
*)ds
);
40 int response_header_overwrite(server
*srv
, connection
*con
, const char *key
, size_t keylen
, const char *value
, size_t vallen
) {
45 /* if there already is a key by this name overwrite the value */
46 if (NULL
!= (ds
= (data_string
*)array_get_element_klen(con
->response
.headers
, key
, keylen
))) {
47 buffer_copy_string_len(ds
->value
, value
, vallen
);
52 return response_header_insert(srv
, con
, key
, keylen
, value
, vallen
);
55 int response_header_append(server
*srv
, connection
*con
, const char *key
, size_t keylen
, const char *value
, size_t vallen
) {
60 /* if there already is a key by this name append the value */
61 if (NULL
!= (ds
= (data_string
*)array_get_element_klen(con
->response
.headers
, key
, keylen
))) {
62 buffer_append_string_len(ds
->value
, CONST_STR_LEN(", "));
63 buffer_append_string_len(ds
->value
, value
, vallen
);
67 return response_header_insert(srv
, con
, key
, keylen
, value
, vallen
);
70 int http_response_redirect_to_directory(server
*srv
, connection
*con
) {
75 buffer_copy_buffer(o
, con
->uri
.scheme
);
76 buffer_append_string_len(o
, CONST_STR_LEN("://"));
77 if (!buffer_is_empty(con
->uri
.authority
)) {
78 buffer_append_string_buffer(o
, con
->uri
.authority
);
80 /* get the name of the currently connected socket */
82 socklen_t our_addr_len
;
84 our_addr_len
= sizeof(our_addr
);
86 if (-1 == getsockname(con
->fd
, (struct sockaddr
*)&our_addr
, &our_addr_len
)
87 || our_addr_len
> (socklen_t
)sizeof(our_addr
)) {
88 con
->http_status
= 500;
90 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
91 "can't get sockname", strerror(errno
));
97 /* Lookup name: secondly try to get hostname for bind address */
98 if (0 != sock_addr_nameinfo_append_buffer(srv
, o
, &our_addr
)) {
99 con
->http_status
= 500;
103 unsigned short default_port
= 80;
104 if (buffer_is_equal_caseless_string(con
->uri
.scheme
, CONST_STR_LEN("https"))) {
107 if (default_port
!= srv
->srvconf
.port
) {
108 buffer_append_string_len(o
, CONST_STR_LEN(":"));
109 buffer_append_int(o
, srv
->srvconf
.port
);
113 buffer_append_string_encoded(o
, CONST_BUF_LEN(con
->uri
.path
), ENCODING_REL_URI
);
114 buffer_append_string_len(o
, CONST_STR_LEN("/"));
115 if (!buffer_string_is_empty(con
->uri
.query
)) {
116 buffer_append_string_len(o
, CONST_STR_LEN("?"));
117 buffer_append_string_buffer(o
, con
->uri
.query
);
120 response_header_insert(srv
, con
, CONST_STR_LEN("Location"), CONST_BUF_LEN(o
));
122 con
->http_status
= 301;
123 con
->file_finished
= 1;
130 buffer
* strftime_cache_get(server
*srv
, time_t last_mod
) {
134 for (int j
= 0; j
< FILE_CACHE_MAX
; ++j
) {
135 if (srv
->mtime_cache
[j
].mtime
== last_mod
)
136 return srv
->mtime_cache
[j
].str
; /* found cache-entry */
139 if (++i
== FILE_CACHE_MAX
) {
143 srv
->mtime_cache
[i
].mtime
= last_mod
;
144 tm
= gmtime(&(srv
->mtime_cache
[i
].mtime
));
145 buffer_string_set_length(srv
->mtime_cache
[i
].str
, 0);
146 buffer_append_strftime(srv
->mtime_cache
[i
].str
, "%a, %d %b %Y %H:%M:%S GMT", tm
);
148 return srv
->mtime_cache
[i
].str
;
152 int http_response_handle_cachable(server
*srv
, connection
*con
, buffer
*mtime
) {
154 ( HTTP_METHOD_GET
== con
->request
.http_method
155 || HTTP_METHOD_HEAD
== con
->request
.http_method
);
159 * 14.26 If-None-Match
161 * If none of the entity tags match, then the server MAY perform the
162 * requested method as if the If-None-Match header field did not exist,
163 * but MUST also ignore any If-Modified-Since header field(s) in the
164 * request. That is, if no entity tags match, then the server MUST NOT
165 * return a 304 (Not Modified) response.
168 if (con
->request
.http_if_none_match
) {
169 /* use strong etag checking for now: weak comparison must not be used
170 * for ranged requests
172 if (etag_is_equal(con
->physical
.etag
, con
->request
.http_if_none_match
, 0)) {
174 con
->http_status
= 304;
175 return HANDLER_FINISHED
;
177 con
->http_status
= 412;
179 return HANDLER_FINISHED
;
182 } else if (con
->request
.http_if_modified_since
&& head_or_get
) {
183 /* last-modified handling */
187 if (NULL
== (semicolon
= strchr(con
->request
.http_if_modified_since
, ';'))) {
188 used_len
= strlen(con
->request
.http_if_modified_since
);
190 used_len
= semicolon
- con
->request
.http_if_modified_since
;
193 if (0 == strncmp(con
->request
.http_if_modified_since
, mtime
->ptr
, used_len
)) {
194 if ('\0' == mtime
->ptr
[used_len
]) con
->http_status
= 304;
195 return HANDLER_FINISHED
;
197 char buf
[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
198 time_t t_header
, t_file
;
201 /* convert to timestamp */
202 if (used_len
>= sizeof(buf
)) return HANDLER_GO_ON
;
204 strncpy(buf
, con
->request
.http_if_modified_since
, used_len
);
205 buf
[used_len
] = '\0';
207 if (NULL
== strptime(buf
, "%a, %d %b %Y %H:%M:%S GMT", &tm
)) {
209 * parsing failed, let's get out of here
211 return HANDLER_GO_ON
;
214 t_header
= mktime(&tm
);
216 strptime(mtime
->ptr
, "%a, %d %b %Y %H:%M:%S GMT", &tm
);
218 t_file
= mktime(&tm
);
220 if (t_file
> t_header
) return HANDLER_GO_ON
;
222 con
->http_status
= 304;
223 return HANDLER_FINISHED
;
227 return HANDLER_GO_ON
;
231 static int http_response_parse_range(server
*srv
, connection
*con
, buffer
*path
, stat_cache_entry
*sce
) {
235 const char *s
, *minus
;
236 char *boundary
= "fkj49sn38dcn3";
238 buffer
*content_type
= NULL
;
241 end
= sce
->st
.st_size
- 1;
243 con
->response
.content_length
= 0;
245 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->response
.headers
, "Content-Type"))) {
246 content_type
= ds
->value
;
249 for (s
= con
->request
.http_range
, error
= 0;
250 !error
&& *s
&& NULL
!= (minus
= strchr(s
, '-')); ) {
257 le
= strtoll(s
, &err
, 10);
260 /* RFC 2616 - 14.35.1 */
262 con
->http_status
= 416;
264 } else if (*err
== '\0') {
268 end
= sce
->st
.st_size
- 1;
269 start
= sce
->st
.st_size
+ le
;
270 } else if (*err
== ',') {
274 end
= sce
->st
.st_size
- 1;
275 start
= sce
->st
.st_size
+ le
;
280 } else if (*(minus
+1) == '\0' || *(minus
+1) == ',') {
283 la
= strtoll(s
, &err
, 10);
288 if (*(err
+ 1) == '\0') {
291 end
= sce
->st
.st_size
- 1;
294 } else if (*(err
+ 1) == ',') {
298 end
= sce
->st
.st_size
- 1;
310 la
= strtoll(s
, &err
, 10);
313 le
= strtoll(minus
+1, &err
, 10);
315 /* RFC 2616 - 14.35.1 */
326 } else if (*err
== ',') {
345 if (start
< 0) start
= 0;
347 /* RFC 2616 - 14.35.1 */
348 if (end
> sce
->st
.st_size
- 1) end
= sce
->st
.st_size
- 1;
350 if (start
> sce
->st
.st_size
- 1) {
353 con
->http_status
= 416;
359 /* write boundary-header */
360 buffer
*b
= buffer_init();
362 buffer_copy_string_len(b
, CONST_STR_LEN("\r\n--"));
363 buffer_append_string(b
, boundary
);
365 /* write Content-Range */
366 buffer_append_string_len(b
, CONST_STR_LEN("\r\nContent-Range: bytes "));
367 buffer_append_int(b
, start
);
368 buffer_append_string_len(b
, CONST_STR_LEN("-"));
369 buffer_append_int(b
, end
);
370 buffer_append_string_len(b
, CONST_STR_LEN("/"));
371 buffer_append_int(b
, sce
->st
.st_size
);
373 buffer_append_string_len(b
, CONST_STR_LEN("\r\nContent-Type: "));
374 buffer_append_string_buffer(b
, content_type
);
376 /* write END-OF-HEADER */
377 buffer_append_string_len(b
, CONST_STR_LEN("\r\n\r\n"));
379 con
->response
.content_length
+= buffer_string_length(b
);
380 chunkqueue_append_buffer(con
->write_queue
, b
);
384 chunkqueue_append_file(con
->write_queue
, path
, start
, end
- start
+ 1);
385 con
->response
.content_length
+= end
- start
+ 1;
389 /* something went wrong */
390 if (error
) return -1;
393 /* add boundary end */
394 buffer
*b
= buffer_init();
396 buffer_copy_string_len(b
, "\r\n--", 4);
397 buffer_append_string(b
, boundary
);
398 buffer_append_string_len(b
, "--\r\n", 4);
400 con
->response
.content_length
+= buffer_string_length(b
);
401 chunkqueue_append_buffer(con
->write_queue
, b
);
404 /* set header-fields */
406 buffer_copy_string_len(srv
->tmp_buf
, CONST_STR_LEN("multipart/byteranges; boundary="));
407 buffer_append_string(srv
->tmp_buf
, boundary
);
409 /* overwrite content-type */
410 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(srv
->tmp_buf
));
412 /* add Content-Range-header */
414 buffer_copy_string_len(srv
->tmp_buf
, CONST_STR_LEN("bytes "));
415 buffer_append_int(srv
->tmp_buf
, start
);
416 buffer_append_string_len(srv
->tmp_buf
, CONST_STR_LEN("-"));
417 buffer_append_int(srv
->tmp_buf
, end
);
418 buffer_append_string_len(srv
->tmp_buf
, CONST_STR_LEN("/"));
419 buffer_append_int(srv
->tmp_buf
, sce
->st
.st_size
);
421 response_header_insert(srv
, con
, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(srv
->tmp_buf
));
424 /* ok, the file is set-up */
429 void http_response_send_file (server
*srv
, connection
*con
, buffer
*path
) {
430 stat_cache_entry
*sce
= NULL
;
431 buffer
*mtime
= NULL
;
433 int allow_caching
= (0 == con
->http_status
|| 200 == con
->http_status
);
435 if (HANDLER_ERROR
== stat_cache_get_entry(srv
, con
, path
, &sce
)) {
436 con
->http_status
= (errno
== ENOENT
) ? 404 : 403;
438 log_error_write(srv
, __FILE__
, __LINE__
, "sbsb",
439 "not a regular file:", con
->uri
.path
,
445 /* we only handline regular files */
447 if ((sce
->is_symlink
== 1) && !con
->conf
.follow_symlink
) {
448 con
->http_status
= 403;
450 if (con
->conf
.log_request_handling
) {
451 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- access denied due symlink restriction");
452 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Path :", path
);
458 if (!S_ISREG(sce
->st
.st_mode
)) {
459 con
->http_status
= 403;
461 if (con
->conf
.log_file_not_found
) {
462 log_error_write(srv
, __FILE__
, __LINE__
, "sbsb",
463 "not a regular file:", con
->uri
.path
,
470 /* mod_compress might set several data directly, don't overwrite them */
472 /* set response content-type, if not set already */
474 if (NULL
== array_get_element(con
->response
.headers
, "Content-Type")) {
475 if (buffer_string_is_empty(sce
->content_type
)) {
476 /* we are setting application/octet-stream, but also announce that
477 * this header field might change in the seconds few requests
479 * This should fix the aggressive caching of FF and the script download
480 * seen by the first installations
482 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));
486 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce
->content_type
));
490 if (con
->conf
.range_requests
) {
491 response_header_overwrite(srv
, con
, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
495 if (con
->etag_flags
!= 0 && !buffer_string_is_empty(sce
->etag
)) {
496 if (NULL
== array_get_element(con
->response
.headers
, "ETag")) {
498 etag_mutate(con
->physical
.etag
, sce
->etag
);
500 response_header_overwrite(srv
, con
, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con
->physical
.etag
));
505 if (NULL
== (ds
= (data_string
*)array_get_element(con
->response
.headers
, "Last-Modified"))) {
506 mtime
= strftime_cache_get(srv
, sce
->st
.st_mtime
);
507 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime
));
512 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, mtime
)) {
517 if (con
->request
.http_range
&& con
->conf
.range_requests
518 && (200 == con
->http_status
|| 0 == con
->http_status
)
519 && NULL
== array_get_element(con
->response
.headers
, "Content-Encoding")) {
520 int do_range_request
= 1;
521 /* check if we have a conditional GET */
523 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, "If-Range"))) {
524 /* if the value is the same as our ETag, we do a Range-request,
525 * otherwise a full 200 */
527 if (ds
->value
->ptr
[0] == '"') {
529 * client wants a ETag
531 if (!con
->physical
.etag
) {
532 do_range_request
= 0;
533 } else if (!buffer_is_equal(ds
->value
, con
->physical
.etag
)) {
534 do_range_request
= 0;
538 * we don't have a Last-Modified and can match the If-Range:
542 do_range_request
= 0;
543 } else if (!buffer_is_equal(ds
->value
, mtime
)) {
544 do_range_request
= 0;
548 if (do_range_request
) {
549 /* content prepared, I'm done */
550 con
->file_finished
= 1;
552 if (0 == http_response_parse_range(srv
, con
, path
, sce
)) {
553 con
->http_status
= 206;
559 /* if we are still here, prepare body */
561 /* we add it here for all requests
562 * the HEAD request will drop it afterwards again
564 if (0 == sce
->st
.st_size
|| 0 == http_chunk_append_file(srv
, con
, path
)) {
565 con
->http_status
= 200;
566 con
->file_finished
= 1;
568 con
->http_status
= 403;
573 static void http_response_xsendfile (server
*srv
, connection
*con
, buffer
*path
, const array
*xdocroot
) {
574 const int status
= con
->http_status
;
577 /* reset Content-Length, if set by backend
578 * Content-Length might later be set to size of X-Sendfile static file,
579 * determined by open(), fstat() to reduces race conditions if the file
580 * is modified between stat() (stat_cache_get_entry()) and open(). */
581 if (con
->parsed_response
& HTTP_CONTENT_LENGTH
) {
582 data_string
*ds
= (data_string
*) array_get_element(con
->response
.headers
, "Content-Length");
583 if (ds
) buffer_reset(ds
->value
);
584 con
->parsed_response
&= ~HTTP_CONTENT_LENGTH
;
585 con
->response
.content_length
= -1;
588 buffer_urldecode_path(path
);
589 buffer_path_simplify(path
, path
);
590 if (con
->conf
.force_lowercase_filenames
) {
591 buffer_to_lower(path
);
594 /* check that path is under xdocroot(s)
595 * - xdocroot should have trailing slash appended at config time
596 * - con->conf.force_lowercase_filenames is not a server-wide setting,
597 * and so can not be definitively applied to xdocroot at config time*/
598 if (xdocroot
->used
) {
599 size_t i
, xlen
= buffer_string_length(path
);
600 for (i
= 0; i
< xdocroot
->used
; ++i
) {
601 data_string
*ds
= (data_string
*)xdocroot
->data
[i
];
602 size_t dlen
= buffer_string_length(ds
->value
);
604 && (!con
->conf
.force_lowercase_filenames
605 ? 0 == memcmp(path
->ptr
, ds
->value
->ptr
, dlen
)
606 : 0 == strncasecmp(path
->ptr
, ds
->value
->ptr
, dlen
))) {
610 if (i
== xdocroot
->used
) {
611 log_error_write(srv
, __FILE__
, __LINE__
, "SBs",
612 "X-Sendfile (", path
,
613 ") not under configured x-sendfile-docroot(s)");
614 con
->http_status
= 403;
619 if (valid
) http_response_send_file(srv
, con
, path
);
621 if (con
->http_status
>= 400 && status
< 300) {
623 } else if (0 != status
&& 200 != status
) {
624 con
->http_status
= status
;
629 static void http_response_xsendfile2(server
*srv
, connection
*con
, const buffer
*value
, const array
*xdocroot
) {
630 const char *pos
= value
->ptr
;
631 buffer
*b
= srv
->tmp_buf
;
632 const int status
= con
->http_status
;
634 /* reset Content-Length, if set by backend */
635 if (con
->parsed_response
& HTTP_CONTENT_LENGTH
) {
636 data_string
*ds
= (data_string
*)
637 array_get_element(con
->response
.headers
, "Content-Length");
638 if (ds
) buffer_reset(ds
->value
);
639 con
->parsed_response
&= ~HTTP_CONTENT_LENGTH
;
640 con
->response
.content_length
= -1;
644 const char *filename
, *range
;
645 stat_cache_entry
*sce
;
646 off_t begin_range
, end_range
, range_len
;
648 while (' ' == *pos
) pos
++;
652 if (NULL
== (range
= strchr(pos
, ' '))) {
654 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
655 "Couldn't find range after filename:", filename
);
656 con
->http_status
= 502;
659 buffer_copy_string_len(b
, filename
, range
- filename
);
661 /* find end of range */
662 for (pos
= ++range
; *pos
&& *pos
!= ' ' && *pos
!= ','; pos
++) ;
664 buffer_urldecode_path(b
);
665 buffer_path_simplify(b
, b
);
666 if (con
->conf
.force_lowercase_filenames
) {
669 if (xdocroot
->used
) {
670 size_t i
, xlen
= buffer_string_length(b
);
671 for (i
= 0; i
< xdocroot
->used
; ++i
) {
672 data_string
*ds
= (data_string
*)xdocroot
->data
[i
];
673 size_t dlen
= buffer_string_length(ds
->value
);
675 && (!con
->conf
.force_lowercase_filenames
676 ? 0 == memcmp(b
->ptr
, ds
->value
->ptr
, dlen
)
677 : 0 == strncasecmp(b
->ptr
, ds
->value
->ptr
, dlen
))) {
681 if (i
== xdocroot
->used
) {
682 log_error_write(srv
, __FILE__
, __LINE__
, "SBs",
684 ") not under configured x-sendfile-docroot(s)");
685 con
->http_status
= 403;
690 if (HANDLER_ERROR
== stat_cache_get_entry(srv
, con
, b
, &sce
)) {
691 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "send-file error: "
692 "couldn't get stat_cache entry for X-Sendfile2:",
694 con
->http_status
= 404;
696 } else if (!S_ISREG(sce
->st
.st_mode
)) {
697 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
698 "send-file error: wrong filetype for X-Sendfile2:",
700 con
->http_status
= 502;
706 end_range
= sce
->st
.st_size
- 1;
710 begin_range
= strtoll(range
, &rpos
, 10);
711 if (errno
!= 0 || begin_range
< 0 || rpos
== range
)
713 if ('-' != *rpos
++) goto range_failed
;
716 end_range
= strtoll(range
, &rpos
, 10);
717 if (errno
!= 0 || end_range
< 0 || rpos
== range
)
720 if (rpos
!= pos
) goto range_failed
;
725 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
726 "Couldn't decode range after filename:", filename
);
727 con
->http_status
= 502;
733 /* no parameters accepted */
735 while (*pos
== ' ') pos
++;
736 if (*pos
!= '\0' && *pos
!= ',') {
737 con
->http_status
= 502;
741 range_len
= end_range
- begin_range
+ 1;
743 con
->http_status
= 502;
746 if (range_len
!= 0) {
747 if (0 != http_chunk_append_file_range(srv
, con
, b
,
748 begin_range
, range_len
)) {
749 con
->http_status
= 502;
754 if (*pos
== ',') pos
++;
757 if (con
->http_status
>= 400 && status
< 300) {
759 } else if (0 != status
&& 200 != status
) {
760 con
->http_status
= status
;
765 void http_response_backend_error (server
*srv
, connection
*con
) {
767 if (con
->file_started
) {
768 /*(response might have been already started, kill the connection)*/
769 /*(mode == DIRECT to avoid later call to http_response_backend_done())*/
770 con
->mode
= DIRECT
; /*(avoid sending final chunked block)*/
771 con
->keep_alive
= 0; /*(no keep-alive; final chunked block not sent)*/
772 con
->file_finished
= 1;
773 } /*(else error status set later by http_response_backend_done())*/
776 void http_response_backend_done (server
*srv
, connection
*con
) {
777 /* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
778 * i.e. not called from handle_connection_close or connection_reset
779 * hooks, except maybe from errdoc handler, which later resets state)*/
780 switch (con
->state
) {
781 case CON_STATE_HANDLE_REQUEST
:
782 case CON_STATE_READ_POST
:
783 if (!con
->file_started
) {
784 /* Send an error if we haven't sent any data yet */
785 con
->http_status
= 500;
788 } /* else fall through */
789 case CON_STATE_WRITE
:
790 if (!con
->file_finished
) {
791 http_chunk_close(srv
, con
);
792 con
->file_finished
= 1;
800 void http_response_upgrade_read_body_unknown(server
*srv
, connection
*con
) {
801 /* act as transparent proxy */
803 if (!(con
->conf
.stream_request_body
& FDEVENT_STREAM_REQUEST
))
804 con
->conf
.stream_request_body
|=
805 (FDEVENT_STREAM_REQUEST_BUFMIN
| FDEVENT_STREAM_REQUEST
);
806 if (!(con
->conf
.stream_response_body
& FDEVENT_STREAM_RESPONSE
))
807 con
->conf
.stream_response_body
|=
808 (FDEVENT_STREAM_RESPONSE_BUFMIN
| FDEVENT_STREAM_RESPONSE
);
809 con
->conf
.stream_request_body
|= FDEVENT_STREAM_REQUEST_POLLIN
;
810 con
->request
.content_length
= -2;
815 static handler_t
http_response_process_local_redir(server
*srv
, connection
*con
, size_t blen
) {
816 /* [RFC3875] The Common Gateway Interface (CGI) Version 1.1
817 * [RFC3875] 6.2.2 Local Redirect Response
819 * The CGI script can return a URI path and query-string
820 * ('local-pathquery') for a local resource in a Location header field.
821 * This indicates to the server that it should reprocess the request
822 * using the path specified.
824 * local-redir-response = local-Location NL
826 * The script MUST NOT return any other header fields or a message-body,
827 * and the server MUST generate the response that it would have produced
828 * in response to a request containing the URL
830 * scheme "://" server-name ":" server-port local-pathquery
832 * (Might not have begun to receive body yet, but do skip local-redir
833 * if we already have started receiving a response body (blen > 0))
834 * (Also, while not required by the RFC, do not send local-redir back
835 * to same URL, since CGI should have handled it internally if it
836 * really wanted to do that internally)
839 /* con->http_status >= 300 && con->http_status < 400) */
840 size_t ulen
= buffer_string_length(con
->uri
.path
);
841 data_string
*ds
= (data_string
*)
842 array_get_element(con
->response
.headers
, "Location");
844 && ds
->value
->ptr
[0] == '/'
845 && (0 != strncmp(ds
->value
->ptr
, con
->uri
.path
->ptr
, ulen
)
846 || (ds
->value
->ptr
[ulen
] != '\0'
847 && ds
->value
->ptr
[ulen
] != '/'
848 && ds
->value
->ptr
[ulen
] != '?'))
850 && !(con
->parsed_response
& HTTP_STATUS
) /*no "Status" or NPH response*/
851 && 1 == con
->response
.headers
->used
) {
852 if (++con
->loops_per_request
> 5) {
853 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
854 "too many internal loops while processing request:",
855 con
->request
.orig_uri
);
856 con
->http_status
= 500; /* Internal Server Error */
858 return HANDLER_FINISHED
;
861 buffer_copy_buffer(con
->request
.uri
, ds
->value
);
863 if (con
->request
.content_length
) {
864 if (con
->request
.content_length
865 != con
->request_content_queue
->bytes_in
) {
868 con
->request
.content_length
= 0;
869 chunkqueue_reset(con
->request_content_queue
);
872 if (con
->http_status
!= 307 && con
->http_status
!= 308) {
873 /* Note: request body (if any) sent to initial dynamic handler
874 * and is not available to the internal redirect */
875 con
->request
.http_method
= HTTP_METHOD_GET
;
878 /*(caller must reset request as follows)*/
879 /*connection_response_reset(srv, con);*/ /*(sets con->http_status = 0)*/
880 /*plugins_call_connection_reset(srv, con);*/
882 return HANDLER_COMEBACK
;
885 return HANDLER_GO_ON
;
889 static int http_response_process_headers(server
*srv
, connection
*con
, http_response_opts
*opts
, buffer
*hdrs
) {
894 for (s
= hdrs
->ptr
; NULL
!= (ns
= strchr(s
, '\n')); s
= ns
+ 1, ++line
) {
895 const char *key
, *value
;
901 if (ns
> s
&& ns
[-1] == '\r') ns
[-1] = '\0';
903 if (0 == line
&& 0 == strncmp(s
, "HTTP/1.", 7)) {
904 /* non-parsed headers ... we parse them anyway */
905 if ((s
[7] == '1' || s
[7] == '0') && s
[8] == ' ') {
906 /* after the space should be a status code for us */
907 int status
= strtol(s
+9, NULL
, 10);
908 if (status
>= 100 && status
< 1000) {
909 con
->parsed_response
|= HTTP_STATUS
;
910 con
->http_status
= status
;
911 } /* else we expected 3 digits and didn't get them */
914 if (0 == con
->http_status
) {
915 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
916 "invalid HTTP status line:", s
);
917 con
->http_status
= 502; /* Bad Gateway */
925 /* parse the headers */
927 if (NULL
== (value
= strchr(s
, ':'))) {
928 /* we expect: "<key>: <value>\r\n" */
932 key_len
= value
- key
;
933 do { ++value
; } while (*value
== ' ' || *value
== '\t'); /* skip LWS */
935 if (opts
->authorizer
) {
936 if (0 == con
->http_status
|| 200 == con
->http_status
) {
937 if (key_len
== 6 && 0 == strncasecmp(key
, "Status", key_len
)) {
938 int status
= strtol(value
, NULL
, 10);
939 if (status
>= 100 && status
< 1000) {
940 con
->http_status
= status
;
942 con
->http_status
= 502; /* Bad Gateway */
945 } else if (key_len
> 9
946 && 0==strncasecmp(key
, CONST_STR_LEN("Variable-"))) {
948 array_get_unused_element(con
->environment
, TYPE_STRING
);
949 if (NULL
== ds
) ds
= data_string_init();
950 buffer_copy_string_len(ds
->key
, key
+ 9, key_len
- 9);
951 buffer_copy_string(ds
->value
, value
);
953 array_insert_unique(con
->environment
, (data_unset
*)ds
);
961 if (0 == strncasecmp(key
, "Date", key_len
)) {
962 con
->parsed_response
|= HTTP_DATE
;
966 if (0 == strncasecmp(key
, "Status", key_len
)) {
968 if (opts
->backend
== BACKEND_PROXY
) break; /*(pass w/o parse)*/
969 status
= strtol(value
, NULL
, 10);
970 if (status
>= 100 && status
< 1000) {
971 con
->http_status
= status
;
972 con
->parsed_response
|= HTTP_STATUS
;
974 con
->http_status
= 502;
977 continue; /* do not send Status to client */
981 if (0 == strncasecmp(key
, "Upgrade", key_len
)) {
982 /*(technically, should also verify Connection: upgrade)*/
983 /*(flag only for mod_proxy and mod_cgi (for now))*/
984 if (opts
->backend
== BACKEND_PROXY
985 || opts
->backend
== BACKEND_CGI
) {
986 con
->parsed_response
|= HTTP_UPGRADE
;
991 if (0 == strncasecmp(key
, "Location", key_len
)) {
992 con
->parsed_response
|= HTTP_LOCATION
;
996 if (0 == strncasecmp(key
, "Connection", key_len
)) {
997 if (opts
->backend
== BACKEND_PROXY
) continue;
998 con
->response
.keep_alive
=
999 (0 == strcasecmp(value
, "Keep-Alive")) ? 1 : 0;
1000 con
->parsed_response
|= HTTP_CONNECTION
;
1002 else if (0 == strncasecmp(key
, "Set-Cookie", key_len
)) {
1003 con
->parsed_response
|= HTTP_SET_COOKIE
;
1007 if (0 == strncasecmp(key
, "Content-Length", key_len
)) {
1008 con
->response
.content_length
= strtoul(value
, NULL
, 10);
1009 con
->parsed_response
|= HTTP_CONTENT_LENGTH
;
1013 if (0 == strncasecmp(key
, "Content-Location", key_len
)) {
1014 con
->parsed_response
|= HTTP_CONTENT_LOCATION
;
1018 if (0 == strncasecmp(key
, "Transfer-Encoding", key_len
)) {
1019 if (opts
->backend
== BACKEND_PROXY
) continue;
1020 con
->parsed_response
|= HTTP_TRANSFER_ENCODING
;
1027 ds
= (data_string
*)
1028 array_get_unused_element(con
->response
.headers
, TYPE_STRING
);
1029 if (NULL
== ds
) ds
= data_response_init();
1030 buffer_copy_string_len(ds
->key
, key
, key_len
);
1031 buffer_copy_string(ds
->value
, value
);
1033 array_insert_unique(con
->response
.headers
, (data_unset
*)ds
);
1036 /* CGI/1.1 rev 03 - 7.2.1.2 */
1037 /* (proxy requires Status-Line, so never true for proxy)*/
1038 if ((con
->parsed_response
& HTTP_LOCATION
) &&
1039 !(con
->parsed_response
& HTTP_STATUS
)) {
1040 con
->http_status
= 302;
1047 handler_t
http_response_parse_headers(server
*srv
, connection
*con
, http_response_opts
*opts
, buffer
*b
) {
1049 * possible formats of response headers:
1051 * proxy or NPH (non-parsed headers):
1063 * and different mixes of \n and \r\n combinations
1065 * Some users also forget about CGI and just send a response
1066 * and hope we handle it. No headers, no header-content separator
1069 int is_nph
= (0 == strncmp(b
->ptr
, "HTTP/1.", 7)); /*nph (non-parsed hdrs)*/
1070 int is_header_end
= 0;
1071 size_t last_eol
= 0;
1072 size_t i
= 0, header_len
= buffer_string_length(b
);
1076 if (b
->ptr
[0] == '\n' || (b
->ptr
[0] == '\r' && b
->ptr
[1] == '\n')) {
1077 /* no HTTP headers */
1078 i
= (b
->ptr
[0] == '\n') ? 1 : 2;
1080 } else if (is_nph
|| b
->ptr
[(i
= strcspn(b
->ptr
, ":\n"))] == ':') {
1083 for (char *c
; NULL
!= (c
= strchr(b
->ptr
+i
, '\n')); ++i
) {
1084 i
= (uintptr_t)(c
- b
->ptr
);
1086 * check if we saw a \n(\r)?\n sequence
1089 ((i
- last_eol
== 1) ||
1090 (i
- last_eol
== 2 && b
->ptr
[i
- 1] == '\r'))) {
1097 } else if (opts
->backend
== BACKEND_CGI
) {
1098 /* no HTTP headers, but a body (special-case for CGI compat) */
1099 /* no colon found; does not appear to be HTTP headers */
1100 if (0 != http_chunk_append_buffer(srv
, con
, b
)) {
1101 return HANDLER_ERROR
;
1103 con
->http_status
= 200; /* OK */
1104 con
->file_started
= 1;
1105 return HANDLER_GO_ON
;
1107 /* invalid response headers */
1108 con
->http_status
= 502; /* Bad Gateway */
1110 return HANDLER_FINISHED
;
1113 if (!is_header_end
) {
1114 /*(reuse MAX_HTTP_REQUEST_HEADER as max size
1115 * for response headers from backends)*/
1116 if (header_len
> MAX_HTTP_REQUEST_HEADER
) {
1117 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
1118 "response headers too large for", con
->uri
.path
);
1119 con
->http_status
= 502; /* Bad Gateway */
1121 return HANDLER_FINISHED
;
1123 return HANDLER_GO_ON
;
1126 /* the body starts after the EOL */
1127 bstart
= b
->ptr
+ (i
+ 1);
1128 blen
= header_len
- (i
+ 1);
1130 /* strip the last \r?\n */
1131 if (i
> 0 && (b
->ptr
[i
- 1] == '\r')) {
1135 buffer_string_set_length(b
, i
);
1137 if (opts
->backend
== BACKEND_PROXY
&& !is_nph
) {
1138 /* invalid response Status-Line from HTTP proxy */
1139 con
->http_status
= 502; /* Bad Gateway */
1141 return HANDLER_FINISHED
;
1144 if (0 != http_response_process_headers(srv
, con
, opts
, b
)) {
1145 return HANDLER_ERROR
;
1148 con
->file_started
= 1;
1150 if (opts
->authorizer
1151 && (con
->http_status
== 0 || con
->http_status
== 200)) {
1152 return HANDLER_GO_ON
;
1155 if (con
->mode
== DIRECT
) {
1156 return HANDLER_FINISHED
;
1159 if (opts
->local_redir
&& con
->http_status
>= 300 && con
->http_status
< 400){
1160 /*(con->parsed_response & HTTP_LOCATION)*/
1161 handler_t rc
= http_response_process_local_redir(srv
, con
, blen
);
1162 if (con
->mode
== DIRECT
) con
->file_started
= 0;
1163 if (rc
!= HANDLER_GO_ON
) return rc
;
1166 if (opts
->xsendfile_allow
) {
1168 /* X-Sendfile2 is deprecated; historical for fastcgi */
1169 if (opts
->backend
== BACKEND_FASTCGI
1170 && NULL
!= (ds
= (data_string
*) array_get_element(con
->response
.headers
, "X-Sendfile2"))) {
1171 http_response_xsendfile2(srv
, con
, ds
->value
, opts
->xsendfile_docroot
);
1172 buffer_reset(ds
->value
); /*(do not send to client)*/
1173 if (con
->mode
== DIRECT
) con
->file_started
= 0;
1174 return HANDLER_FINISHED
;
1175 } else if (NULL
!= (ds
= (data_string
*) array_get_element(con
->response
.headers
, "X-Sendfile"))
1176 || (opts
->backend
== BACKEND_FASTCGI
/* X-LIGHTTPD-send-file is deprecated; historical for fastcgi */
1177 && NULL
!= (ds
= (data_string
*) array_get_element(con
->response
.headers
, "X-LIGHTTPD-send-file")))) {
1178 http_response_xsendfile(srv
, con
, ds
->value
, opts
->xsendfile_docroot
);
1179 buffer_reset(ds
->value
); /*(do not send to client)*/
1180 if (con
->mode
== DIRECT
) con
->file_started
= 0;
1181 return HANDLER_FINISHED
;
1186 if (0 != http_chunk_append_mem(srv
, con
, bstart
, blen
)) {
1187 return HANDLER_ERROR
;
1191 /* (callback for response headers complete) */
1192 return (opts
->headers
) ? opts
->headers(srv
, con
, opts
) : HANDLER_GO_ON
;
1196 handler_t
http_response_read(server
*srv
, connection
*con
, http_response_opts
*opts
, buffer
*b
, int fd
, int *fde_ndx
) {
1199 size_t avail
= buffer_string_space(b
);
1200 unsigned int toread
= 4096;
1202 if (0 == fdevent_ioctl_fionread(fd
, opts
->fdfmt
, (int *)&toread
)) {
1203 if (avail
< toread
) {
1206 else if (toread
> MAX_READ_LIMIT
)
1207 toread
= MAX_READ_LIMIT
;
1209 else if (0 == toread
) {
1211 return (fdevent_event_get_interest(srv
->ev
, fd
) & FDEVENT_IN
)
1212 ? HANDLER_FINISHED
/* read finished */
1213 : HANDLER_GO_ON
; /* optimistic read; data not ready */
1215 if (!(fdevent_event_get_interest(srv
->ev
, fd
) & FDEVENT_IN
))
1216 return HANDLER_GO_ON
; /* optimistic read; data not ready */
1217 toread
= 4096; /* let read() below indicate if EOF or EAGAIN */
1222 if (con
->conf
.stream_response_body
& FDEVENT_STREAM_RESPONSE_BUFMIN
) {
1223 off_t cqlen
= chunkqueue_length(con
->write_queue
);
1224 if (cqlen
+ (off_t
)toread
> 65536 - 4096) {
1225 if (!con
->is_writable
) {
1226 /*(defer removal of FDEVENT_IN interest since
1227 * connection_state_machine() might be able to send data
1228 * immediately, unless !con->is_writable, where
1229 * connection_state_machine() might not loop back to call
1230 * mod_proxy_handle_subrequest())*/
1231 fdevent_event_clr(srv
->ev
, fde_ndx
, fd
, FDEVENT_IN
);
1233 if (cqlen
>= 65536-1) return HANDLER_GO_ON
;
1234 toread
= 65536 - 1 - (unsigned int)cqlen
;
1235 /* Note: heuristic is fuzzy in that it limits how much to read
1236 * from backend based on how much is pending to write to client.
1237 * Modules where data from backend is framed (e.g. FastCGI) may
1238 * want to limit how much is buffered from backend while waiting
1239 * for a complete data frame or data packet from backend. */
1243 if (avail
< toread
) {
1244 /*(add avail+toread to reduce allocations when ioctl EOPNOTSUPP)*/
1245 avail
= avail
? avail
- 1 + toread
: toread
;
1246 buffer_string_prepare_append(b
, avail
);
1249 n
= read(fd
, b
->ptr
+buffer_string_length(b
), avail
);
1255 #if EWOULDBLOCK != EAGAIN
1260 return HANDLER_GO_ON
;
1262 log_error_write(srv
, __FILE__
, __LINE__
, "ssdd",
1263 "read():", strerror(errno
), con
->fd
, fd
);
1264 return HANDLER_ERROR
;
1268 buffer_commit(b
, (size_t)n
);
1270 if (NULL
!= opts
->parse
) {
1271 handler_t rc
= opts
->parse(srv
, con
, opts
, b
, (size_t)n
);
1272 if (rc
!= HANDLER_GO_ON
) return rc
;
1273 } else if (0 == n
) {
1274 /* note: no further data is sent to backend after read EOF on socket
1275 * (not checking for half-closed TCP socket)
1276 * (backend should read all data desired prior to closing socket,
1277 * though might send app-level close data frame, if applicable) */
1278 return HANDLER_FINISHED
; /* read finished */
1279 } else if (0 == con
->file_started
) {
1280 /* split header from body */
1281 handler_t rc
= http_response_parse_headers(srv
, con
, opts
, b
);
1282 if (rc
!= HANDLER_GO_ON
) return rc
;
1283 /* accumulate response in b until headers completed (or error) */
1284 if (con
->file_started
) buffer_string_set_length(b
, 0);
1286 if (0 != http_chunk_append_buffer(srv
, con
, b
)) {
1287 /* error writing to tempfile;
1288 * truncate response or send 500 if nothing sent yet */
1289 return HANDLER_ERROR
;
1291 buffer_string_set_length(b
, 0);
1294 if ((con
->conf
.stream_response_body
& FDEVENT_STREAM_RESPONSE_BUFMIN
)
1295 && chunkqueue_length(con
->write_queue
) > 65536 - 4096) {
1296 if (!con
->is_writable
) {
1297 /*(defer removal of FDEVENT_IN interest since
1298 * connection_state_machine() might be able to send
1299 * data immediately, unless !con->is_writable, where
1300 * connection_state_machine() might not loop back to
1301 * call the subrequest handler)*/
1302 fdevent_event_clr(srv
->ev
, fde_ndx
, fd
, FDEVENT_IN
);
1307 if ((size_t)n
< avail
)
1308 break; /* emptied kernel read buffer or partial read */
1311 return HANDLER_GO_ON
;
1315 int http_cgi_headers (server
*srv
, connection
*con
, http_cgi_opts
*opts
, http_cgi_header_append_cb cb
, void *vdata
) {
1317 /* CGI-SPEC 6.1.2, FastCGI spec 6.3 and SCGI spec */
1320 server_socket
*srv_sock
= con
->srv_socket
;
1323 char buf
[LI_ITOSTRING_LENGTH
];
1326 char b2
[INET6_ADDRSTRLEN
+ 1];
1328 /* (CONTENT_LENGTH must be first for SCGI) */
1329 if (!opts
->authorizer
) {
1330 li_itostrn(buf
, sizeof(buf
), con
->request
.content_length
);
1331 rc
|= cb(vdata
, CONST_STR_LEN("CONTENT_LENGTH"), buf
, strlen(buf
));
1334 if (!buffer_string_is_empty(con
->uri
.query
)) {
1335 rc
|= cb(vdata
, CONST_STR_LEN("QUERY_STRING"),
1336 CONST_BUF_LEN(con
->uri
.query
));
1338 rc
|= cb(vdata
, CONST_STR_LEN("QUERY_STRING"),
1341 if (!buffer_string_is_empty(opts
->strip_request_uri
)) {
1345 * stripping /app1 or /app1/ should lead to
1350 size_t len
= buffer_string_length(opts
->strip_request_uri
);
1351 if ('/' == opts
->strip_request_uri
->ptr
[len
-1]) {
1355 if (buffer_string_length(con
->request
.orig_uri
) >= len
1356 && 0 == memcmp(con
->request
.orig_uri
->ptr
,
1357 opts
->strip_request_uri
->ptr
, len
)
1358 && con
->request
.orig_uri
->ptr
[len
] == '/') {
1359 rc
|= cb(vdata
, CONST_STR_LEN("REQUEST_URI"),
1360 con
->request
.orig_uri
->ptr
+len
,
1361 buffer_string_length(con
->request
.orig_uri
) - len
);
1363 rc
|= cb(vdata
, CONST_STR_LEN("REQUEST_URI"),
1364 CONST_BUF_LEN(con
->request
.orig_uri
));
1367 rc
|= cb(vdata
, CONST_STR_LEN("REQUEST_URI"),
1368 CONST_BUF_LEN(con
->request
.orig_uri
));
1370 if (!buffer_is_equal(con
->request
.uri
, con
->request
.orig_uri
)) {
1371 rc
|= cb(vdata
, CONST_STR_LEN("REDIRECT_URI"),
1372 CONST_BUF_LEN(con
->request
.uri
));
1374 /* set REDIRECT_STATUS for php compiled with --force-redirect
1375 * (if REDIRECT_STATUS has not already been set by error handler) */
1376 if (0 == con
->error_handler_saved_status
) {
1377 rc
|= cb(vdata
, CONST_STR_LEN("REDIRECT_STATUS"),
1378 CONST_STR_LEN("200"));
1382 * SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to
1383 * http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html
1384 * (6.1.14, 6.1.6, 6.1.7)
1386 if (!opts
->authorizer
) {
1387 rc
|= cb(vdata
, CONST_STR_LEN("SCRIPT_NAME"),
1388 CONST_BUF_LEN(con
->uri
.path
));
1389 if (!buffer_string_is_empty(con
->request
.pathinfo
)) {
1390 rc
|= cb(vdata
, CONST_STR_LEN("PATH_INFO"),
1391 CONST_BUF_LEN(con
->request
.pathinfo
));
1392 /* PATH_TRANSLATED is only defined if PATH_INFO is set */
1393 if (!buffer_string_is_empty(opts
->docroot
)) {
1394 buffer_copy_buffer(srv
->tmp_buf
, opts
->docroot
);
1396 buffer_copy_buffer(srv
->tmp_buf
, con
->physical
.basedir
);
1398 buffer_append_string_buffer(srv
->tmp_buf
, con
->request
.pathinfo
);
1399 rc
|= cb(vdata
, CONST_STR_LEN("PATH_TRANSLATED"),
1400 CONST_BUF_LEN(srv
->tmp_buf
));
1405 * SCRIPT_FILENAME and DOCUMENT_ROOT for php
1406 * The PHP manual http://www.php.net/manual/en/reserved.variables.php
1407 * treatment of PATH_TRANSLATED is different from the one of CGI specs.
1408 * (see php.ini cgi.fix_pathinfo = 1 config parameter)
1411 if (!buffer_string_is_empty(opts
->docroot
)) {
1412 /* alternate docroot, e.g. for remote FastCGI or SCGI server */
1413 buffer_copy_buffer(srv
->tmp_buf
, opts
->docroot
);
1414 buffer_append_string_buffer(srv
->tmp_buf
, con
->uri
.path
);
1415 rc
|= cb(vdata
, CONST_STR_LEN("SCRIPT_FILENAME"),
1416 CONST_BUF_LEN(srv
->tmp_buf
));
1417 rc
|= cb(vdata
, CONST_STR_LEN("DOCUMENT_ROOT"),
1418 CONST_BUF_LEN(opts
->docroot
));
1420 if (opts
->break_scriptfilename_for_php
) {
1421 /* php.ini config cgi.fix_pathinfo = 1 need a broken SCRIPT_FILENAME
1422 * to find out what PATH_INFO is itself
1424 * see src/sapi/cgi_main.c, init_request_info()
1426 buffer_copy_buffer(srv
->tmp_buf
, con
->physical
.path
);
1427 buffer_append_string_buffer(srv
->tmp_buf
, con
->request
.pathinfo
);
1428 rc
|= cb(vdata
, CONST_STR_LEN("SCRIPT_FILENAME"),
1429 CONST_BUF_LEN(srv
->tmp_buf
));
1431 rc
|= cb(vdata
, CONST_STR_LEN("SCRIPT_FILENAME"),
1432 CONST_BUF_LEN(con
->physical
.path
));
1434 rc
|= cb(vdata
, CONST_STR_LEN("DOCUMENT_ROOT"),
1435 CONST_BUF_LEN(con
->physical
.basedir
));
1438 s
= get_http_method_name(con
->request
.http_method
);
1440 rc
|= cb(vdata
, CONST_STR_LEN("REQUEST_METHOD"), s
, strlen(s
));
1442 s
= get_http_version_name(con
->request
.http_version
);
1444 rc
|= cb(vdata
, CONST_STR_LEN("SERVER_PROTOCOL"), s
, strlen(s
));
1446 rc
|= cb(vdata
, CONST_STR_LEN("SERVER_SOFTWARE"),
1447 CONST_BUF_LEN(con
->conf
.server_tag
));
1449 rc
|= cb(vdata
, CONST_STR_LEN("GATEWAY_INTERFACE"),
1450 CONST_STR_LEN("CGI/1.1"));
1452 rc
|= cb(vdata
, CONST_STR_LEN("REQUEST_SCHEME"),
1453 CONST_BUF_LEN(con
->uri
.scheme
));
1455 if (buffer_is_equal_caseless_string(con
->uri
.scheme
,
1456 CONST_STR_LEN("https"))) {
1457 rc
|= cb(vdata
, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
1460 addr
= &srv_sock
->addr
;
1461 li_utostrn(buf
, sizeof(buf
), sock_addr_get_port(addr
));
1462 rc
|= cb(vdata
, CONST_STR_LEN("SERVER_PORT"), buf
, strlen(buf
));
1464 switch (addr
->plain
.sa_family
) {
1467 if (sock_addr_is_addr_wildcard(addr
)) {
1468 socklen_t addrlen
= sizeof(addrbuf
);
1469 if (0 == getsockname(con
->fd
,(struct sockaddr
*)&addrbuf
,&addrlen
)){
1476 s
= sock_addr_inet_ntop(addr
, b2
, sizeof(b2
)-1);
1477 if (NULL
== s
) s
= "";
1484 rc
|= cb(vdata
, CONST_STR_LEN("SERVER_ADDR"), s
, strlen(s
));
1486 if (!buffer_string_is_empty(con
->server_name
)) {
1487 size_t len
= buffer_string_length(con
->server_name
);
1489 if (con
->server_name
->ptr
[0] == '[') {
1490 const char *colon
= strstr(con
->server_name
->ptr
, "]:");
1491 if (colon
) len
= (colon
+ 1) - con
->server_name
->ptr
;
1493 const char *colon
= strchr(con
->server_name
->ptr
, ':');
1494 if (colon
) len
= colon
- con
->server_name
->ptr
;
1497 rc
|= cb(vdata
, CONST_STR_LEN("SERVER_NAME"),
1498 con
->server_name
->ptr
, len
);
1500 /* set to be same as SERVER_ADDR (above) */
1501 rc
|= cb(vdata
, CONST_STR_LEN("SERVER_NAME"), s
, strlen(s
));
1504 rc
|= cb(vdata
, CONST_STR_LEN("REMOTE_ADDR"),
1505 CONST_BUF_LEN(con
->dst_addr_buf
));
1507 li_utostrn(buf
, sizeof(buf
), sock_addr_get_port(&con
->dst_addr
));
1508 rc
|= cb(vdata
, CONST_STR_LEN("REMOTE_PORT"), buf
, strlen(buf
));
1510 for (n
= 0; n
< con
->request
.headers
->used
; n
++) {
1511 data_string
*ds
= (data_string
*)con
->request
.headers
->data
[n
];
1512 if (!buffer_string_is_empty(ds
->value
) && !buffer_is_empty(ds
->key
)) {
1513 /* Security: Do not emit HTTP_PROXY in environment.
1514 * Some executables use HTTP_PROXY to configure
1515 * outgoing proxy. See also https://httpoxy.org/ */
1516 if (buffer_is_equal_caseless_string(ds
->key
,
1517 CONST_STR_LEN("Proxy"))) {
1520 buffer_copy_string_encoded_cgi_varnames(srv
->tmp_buf
,
1521 CONST_BUF_LEN(ds
->key
), 1);
1522 rc
|= cb(vdata
, CONST_BUF_LEN(srv
->tmp_buf
),
1523 CONST_BUF_LEN(ds
->value
));
1527 srv
->request_env(srv
, con
);
1529 for (n
= 0; n
< con
->environment
->used
; n
++) {
1530 data_string
*ds
= (data_string
*)con
->environment
->data
[n
];
1531 if (!buffer_is_empty(ds
->value
) && !buffer_is_empty(ds
->key
)) {
1532 buffer_copy_string_encoded_cgi_varnames(srv
->tmp_buf
,
1533 CONST_BUF_LEN(ds
->key
), 0);
1534 rc
|= cb(vdata
, CONST_BUF_LEN(srv
->tmp_buf
),
1535 CONST_BUF_LEN(ds
->value
));