6 #include "http_header.h"
15 static int request_check_hostname(buffer
*host
) {
16 enum { DOMAINLABEL
, TOPLABEL
} stage
= TOPLABEL
;
19 size_t host_len
, hostport_len
;
21 int is_ip
= -1; /* -1 don't know yet, 0 no, 1 yes */
25 * hostport = host [ ":" port ]
26 * host = hostname | IPv4address | IPv6address
27 * hostname = *( domainlabel "." ) toplabel [ "." ]
28 * domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
29 * toplabel = alpha | alpha *( alphanum | "-" ) alphanum
30 * IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
31 * IPv6address = "[" ... "]"
36 if (host
->ptr
[0] == '[') {
37 char *c
= host
->ptr
+ 1;
40 /* check the address inside [...] */
41 for (; *c
&& *c
!= ']'; c
++) {
43 if (++colon_cnt
> 7) {
46 } else if (!light_isxdigit(*c
) && '.' != *c
) {
58 for (c
+= 2; *c
; c
++) {
59 if (!light_isdigit(*c
)) {
64 else if ('\0' != *(c
+1)) {
65 /* only a port is allowed to follow [...] */
71 hostport_len
= host_len
= buffer_string_length(host
);
73 if (NULL
!= (colon
= memchr(host
->ptr
, ':', host_len
))) {
76 /* check portnumber */
78 if (!light_isdigit(*c
)) return -1;
81 /* remove the port from the host-len */
82 host_len
= colon
- host
->ptr
;
86 if (host_len
== 0) return -1;
88 /* if the hostname ends in a "." strip it */
89 if (host
->ptr
[host_len
-1] == '.') {
90 /* shift port info one left */
91 if (NULL
!= colon
) memmove(colon
-1, colon
, hostport_len
- host_len
);
92 buffer_string_set_length(host
, --hostport_len
);
93 if (--host_len
== 0) return -1;
97 /* scan from the right and skip the \0 */
98 for (i
= host_len
; i
-- > 0; ) {
99 const char c
= host
->ptr
[i
];
104 /* only switch stage, if this is not the last character */
105 if (i
!= host_len
- 1) {
106 if (label_len
== 0) {
110 /* check the first character at right of the dot */
112 if (!light_isalnum(host
->ptr
[i
+1])) {
115 } else if (!light_isdigit(host
->ptr
[i
+1])) {
117 } else if ('-' == host
->ptr
[i
+1]) {
129 /* just a dot and nothing else is evil */
133 /* the first character of the hostname */
134 if (!light_isalnum(c
)) {
139 if (c
!= '-' && !light_isalnum(c
)) {
143 if (!light_isdigit(c
)) is_ip
= 0;
152 if (label_len
== 0) {
158 } else if (!light_isdigit(c
)) {
165 if (label_len
== 0) {
169 /* c is either - or alphanum here */
170 if ('-' == host
->ptr
[i
+1]) {
177 if (!light_isalnum(c
)) {
182 if (c
!= '-' && !light_isalnum(c
)) {
193 /* a IP has to consist of 4 parts */
194 if (is_ip
== 1 && level
!= 3) {
198 if (label_len
== 0) {
205 int http_request_host_normalize(buffer
*b
, int scheme_port
) {
207 * check for and canonicalize numeric IP address and portnum (optional)
208 * (IP address may be followed by ":portnum" (optional))
211 * - IPv4: 12345678 (32-bit decimal number)
212 * - IPv4: 012345678 (32-bit octal number)
213 * - IPv4: 0x12345678 (32-bit hex number)
215 * allow any chars (except ':' and '\0' and stray '[' or ']')
216 * (other code may check chars more strictly or more pedantically)
217 * ':' delimits (optional) port at end of string
218 * "[]" wraps IPv6 address literal
219 * '\0' should have been rejected earlier were it present
221 * any chars includes, but is not limited to:
222 * - allow '-' any where, even at beginning of word
223 * (security caution: might be confused for cmd flag if passed to shell)
224 * - allow all-digit TLDs
225 * (might be mistaken for IPv4 addr by inet_aton()
226 * unless non-digits appear in subdomain)
229 /* Note: not using getaddrinfo() since it does not support "[]" around IPv6
230 * and is not as lenient as inet_aton() and inet_addr() for IPv4 strings.
231 * Not using inet_pton() (when available) on IPv4 for similar reasons. */
233 const char * const p
= b
->ptr
;
234 const size_t blen
= buffer_string_length(b
);
238 char * const colon
= (char *)memchr(p
, ':', blen
);
240 if (*p
== ':') return -1; /*(empty host then port, or naked IPv6)*/
241 if (colon
[1] != '\0') {
243 port
= strtol(colon
+1, &e
, 0); /*(allow decimal, octal, hex)*/
244 if (0 < port
&& port
<= USHRT_MAX
&& *e
== '\0') {
249 } /*(else ignore stray colon at string end)*/
250 buffer_string_set_length(b
, (size_t)(colon
- p
)); /*(remove port str)*/
253 if (light_isdigit(*p
)) do {
254 /* (IPv4 address literal or domain starting w/ digit (e.g. 3com))*/
255 /* (check one-element cache of normalized IPv4 address string) */
256 static struct { char s
[INET_ADDRSTRLEN
]; size_t n
; } laddr
;
257 size_t n
= colon
? (size_t)(colon
- p
) : blen
;
259 if (n
== laddr
.n
&& 0 == memcmp(p
, laddr
.s
, n
)) break;
260 if (1 == sock_addr_inet_pton(&addr
, p
, AF_INET
, 0)) {
261 sock_addr_inet_ntop_copy_buffer(b
, &addr
);
262 n
= buffer_string_length(b
);
263 if (n
< sizeof(laddr
.s
)) memcpy(laddr
.s
, b
->ptr
, (laddr
.n
= n
));
266 } else do { /* IPv6 addr */
267 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
269 /* (check one-element cache of normalized IPv4 address string) */
270 static struct { char s
[INET6_ADDRSTRLEN
]; size_t n
; } laddr
;
272 char *bracket
= b
->ptr
+blen
-1;
273 char *percent
= strchr(b
->ptr
+1, '%');
276 char buf
[INET6_ADDRSTRLEN
+16]; /*(+16 for potential %interface name)*/
277 if (blen
<= 2) return -1; /*(invalid "[]")*/
278 if (*bracket
!= ']') {
279 bracket
= (char *)memchr(b
->ptr
+1, ']', blen
-1);
280 if (NULL
== bracket
|| bracket
[1] != ':' || bracket
- b
->ptr
== 1){
283 if (bracket
[2] != '\0') { /*(ignore stray colon at string end)*/
285 port
= strtol(bracket
+2, &e
, 0); /*(allow decimal, octal, hex)*/
286 if (0 < port
&& port
<= USHRT_MAX
&& *e
== '\0') {
294 len
= (size_t)((percent
? percent
: bracket
) - (b
->ptr
+1));
295 if (laddr
.n
== len
&& 0 == memcmp(laddr
.s
, b
->ptr
+1, len
)) {
296 /* truncate after ']' and re-add normalized port, if needed */
297 buffer_string_set_length(b
, (size_t)(bracket
- b
->ptr
+ 1));
301 *bracket
= '\0';/*(terminate IPv6 string)*/
302 if (percent
) *percent
= '\0'; /*(remove %interface from address)*/
303 rc
= sock_addr_inet_pton(&addr
, b
->ptr
+1, AF_INET6
, 0);
304 if (percent
) *percent
= '%'; /*(restore %interface)*/
305 *bracket
= ']'; /*(restore bracket)*/
306 if (1 != rc
) return -1;
308 sock_addr_inet_ntop(&addr
, buf
, sizeof(buf
));
311 if (percent
> bracket
) return -1;
312 if (len
+ (size_t)(bracket
- percent
) >= sizeof(buf
)) return -1;
313 if (len
< sizeof(laddr
.s
)) memcpy(laddr
.s
, buf
, (laddr
.n
= len
));
314 memcpy(buf
+len
, percent
, (size_t)(bracket
- percent
));
315 len
+= (size_t)(bracket
- percent
);
317 buffer_string_set_length(b
, 1); /* truncate after '[' */
318 buffer_append_string_len(b
, buf
, len
);
319 buffer_append_string_len(b
, CONST_STR_LEN("]"));
328 if (0 != port
&& port
!= scheme_port
) {
329 buffer_append_string_len(b
, CONST_STR_LEN(":"));
330 buffer_append_int(b
, (int)port
);
336 static int scheme_port (const buffer
*scheme
)
338 return buffer_is_equal_string(scheme
, CONST_STR_LEN("https")) ? 443 : 80;
341 int http_request_host_policy (connection
*con
, buffer
*b
, const buffer
*scheme
) {
342 return (((con
->conf
.http_parseopts
& HTTP_PARSEOPT_HOST_STRICT
)
343 && 0 != request_check_hostname(b
))
344 || ((con
->conf
.http_parseopts
& HTTP_PARSEOPT_HOST_NORMALIZE
)
345 && 0 != http_request_host_normalize(b
, scheme_port(scheme
))));
348 static int http_request_split_value(array
*vals
, const char *current
, size_t len
) {
350 const char *token_start
= NULL
, *token_end
= NULL
;
354 * val1, val2, val3, val4
356 * into a array (more or less a explode() incl. stripping of whitespaces
359 for (size_t i
= 0; i
<= len
; ++i
, ++current
) {
361 case 0: /* find start of a token */
364 case '\t': /* skip white space */
365 case ',': /* skip empty token */
367 case '\0': /* end of string */
370 /* found real data, switch to state 1 to find the end of the token */
371 token_start
= token_end
= current
;
376 case 1: /* find end of token and last non white space character */
380 /* space - don't update token_end */
383 case '\0': /* end of string also marks the end of a token */
384 array_insert_value(vals
, token_start
, token_end
-token_start
+1);
388 /* no white space, update token_end to include current character */
399 static int request_uri_is_valid_char(unsigned char c
) {
400 return (c
> 32 && c
!= 127 && c
!= 255);
404 __attribute_noinline__
405 static int http_request_header_line_invalid(server
*srv
, int status
, const char *msg
) {
406 if (srv
->srvconf
.log_request_header_on_error
) {
407 if (msg
) log_error_write(srv
, __FILE__
, __LINE__
, "s", msg
);
413 __attribute_noinline__
414 static int http_request_header_char_invalid(server
*srv
, char ch
, const char *msg
) {
415 if (srv
->srvconf
.log_request_header_on_error
) {
416 if ((unsigned char)ch
> 32 && ch
!= 127) {
417 char buf
[2] = { ch
, '\0' };
418 log_error_write(srv
,__FILE__
,__LINE__
,"sSSS",msg
,"('",buf
,"')");
421 log_error_write(srv
,__FILE__
,__LINE__
,"sSXS",msg
,"(",ch
,")");
427 enum keep_alive_set
{
428 HTTP_CONNECTION_UNSET
,
429 HTTP_CONNECTION_KEEPALIVE
,
430 HTTP_CONNECTION_CLOSE
,
434 enum keep_alive_set keep_alive_set
;
439 } parse_header_state
;
441 static void init_parse_header_state(parse_header_state
* state
) {
442 state
->keep_alive_set
= HTTP_CONNECTION_UNSET
;
443 state
->con_length_set
= 0;
444 state
->reqline_host
= NULL
;
445 state
->reqline_hostlen
= 0;
446 state
->reqline_len
= 0;
449 /* add header to list of headers
450 * certain headers are also parsed
451 * might drop a header if deemed unnecessary/broken
453 * returns 0 on success, HTTP status on error
455 static int parse_single_header(server
*srv
, connection
*con
, parse_header_state
*state
, char *k
, size_t klen
, char *v
, size_t vlen
) {
456 const enum http_header_e id
= http_header_hkey_get(k
, klen
);
457 buffer
**saveb
= NULL
;
459 /* strip leading whitespace */
460 for (; vlen
> 0 && (v
[0] == ' ' || v
[0] == '\t'); ++v
, --vlen
) ;
462 /* strip trailing whitespace */
463 while (vlen
> 0 && (v
[vlen
- 1] == ' ' || v
[vlen
- 1] == '\t')) --vlen
;
465 /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
466 if (0 == vlen
) return 0; /* ignore header */
469 * Note: k might not be '\0'-terminated
473 /*case HTTP_HEADER_OTHER:*/
476 case HTTP_HEADER_HOST
:
477 if (!(con
->request
.htags
& HTTP_HEADER_HOST
)) {
478 saveb
= &con
->request
.http_host
;
479 if (vlen
>= 1024) { /*(expecting < 256)*/
480 return http_request_header_line_invalid(srv
, 400, "uri-authority too long -> 400");
483 else if (state
->reqline_host
) {
484 /* ignore all Host: headers as we got Host in request line */
485 return 0; /* ignore header */
488 return http_request_header_line_invalid(srv
, 400, "duplicate Host header -> 400");
491 case HTTP_HEADER_CONNECTION
:
493 array
* const vals
= srv
->split_vals
;
494 array_reset_data_strings(vals
);
495 http_request_split_value(vals
, v
, vlen
); /* split on , */
496 for (size_t vi
= 0; vi
< vals
->used
; ++vi
) {
497 data_string
*dsv
= (data_string
*)vals
->data
[vi
];
498 if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv
->value
),
499 CONST_STR_LEN("keep-alive"))) {
500 state
->keep_alive_set
= HTTP_CONNECTION_KEEPALIVE
;
503 else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv
->value
),
504 CONST_STR_LEN("close"))) {
505 state
->keep_alive_set
= HTTP_CONNECTION_CLOSE
;
511 case HTTP_HEADER_CONTENT_TYPE
:
512 if (con
->request
.htags
& HTTP_HEADER_CONTENT_TYPE
) {
513 return http_request_header_line_invalid(srv
, 400, "duplicate Content-Type header -> 400");
516 case HTTP_HEADER_IF_NONE_MATCH
:
517 /* if dup, only the first one will survive */
518 if (con
->request
.htags
& HTTP_HEADER_IF_NONE_MATCH
) {
519 return 0; /* ignore header */
522 case HTTP_HEADER_CONTENT_LENGTH
:
523 if (!(con
->request
.htags
& HTTP_HEADER_CONTENT_LENGTH
)) {
525 off_t r
= strtoll(v
, &err
, 10);
527 if (*err
== '\0' && r
>= 0) {
528 con
->request
.content_length
= r
;
531 return http_request_header_line_invalid(srv
, 400, "invalid Content-Length header -> 400");
535 return http_request_header_line_invalid(srv
, 400, "duplicate Content-Length header -> 400");
538 case HTTP_HEADER_IF_MODIFIED_SINCE
:
539 if (con
->request
.htags
& HTTP_HEADER_IF_MODIFIED_SINCE
) {
540 /* Proxies sometimes send dup headers
541 * if they are the same we ignore the second
542 * if not, we raise an error */
544 http_header_request_get(con
, HTTP_HEADER_IF_MODIFIED_SINCE
,
545 CONST_STR_LEN("If-Modified-Since"));
546 if (vb
&& buffer_is_equal_caseless_string(vb
, v
, vlen
)) {
547 /* ignore it if they are the same */
548 return 0; /* ignore header */
551 return http_request_header_line_invalid(srv
, 400, "duplicate If-Modified-Since header -> 400");
557 con
->request
.htags
|= id
;
558 http_header_request_append(con
, id
, k
, klen
, v
, vlen
);
561 *saveb
= http_header_request_get(con
, id
, k
, klen
);
567 static size_t http_request_parse_reqline(server
*srv
, connection
*con
, buffer
*hdrs
, parse_header_state
*state
) {
568 char * const ptr
= hdrs
->ptr
;
569 char *uri
= NULL
, *proto
= NULL
;
572 const unsigned int http_header_strict
= (con
->conf
.http_parseopts
& HTTP_PARSEOPT_HEADER_STRICT
);
574 /* hdrs must end with '\n' (already checked before parsing headers) */
576 if (NULL
== strchr(ptr
, '\n')) return 400;
580 * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$"
581 * Option : "^([-a-zA-Z]+): (.+)$"
585 /* parse the first line of the request
589 * <method> <uri> <protocol>\r\n
591 for (i
= 0; ptr
[i
] != '\n'; ++i
) {
593 if (NULL
== uri
) uri
= ptr
+ i
+ 1;
594 else if (NULL
== proto
) proto
= ptr
+ i
+ 1;
595 else return http_request_header_line_invalid(srv
, 400, "overlong request line; extra space -> 400"); /* ERROR, one space to much */
599 state
->reqline_len
= i
+1;
607 if (0 == i
) return 400;
609 if (ptr
[i
-1] == '\r') {
611 } else if (http_header_strict
) { /* '\n' */
612 return http_request_header_line_invalid(srv
, 400, "missing CR before LF in header -> 400");
616 return http_request_header_line_invalid(srv
, 400, "incomplete request line -> 400");
619 con
->request
.http_method
= get_http_method_key(ptr
, uri
- 1 - ptr
);
620 if (HTTP_METHOD_UNSET
== con
->request
.http_method
) {
621 return http_request_header_line_invalid(srv
, 501, "unknown http-method -> 501");
626 * HTTP-version = HTTP-name "/" DIGIT "." DIGIT
627 * HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive
629 if (proto
[0]=='H' && proto
[1]=='T' && proto
[2]=='T' && proto
[3]=='P' && proto
[4] == '/') {
630 if (proto
[5] == '1' && proto
[6] == '.' && (proto
[7] == '1' || proto
[7] == '0')) {
631 con
->request
.http_version
= (proto
[7] == '1') ? HTTP_VERSION_1_1
: HTTP_VERSION_1_0
;
633 return http_request_header_line_invalid(srv
, 505, "unknown HTTP version -> 505");
636 return http_request_header_line_invalid(srv
, 400, "unknown protocol -> 400");
639 jlen
= (size_t)(proto
- uri
- 1);
643 buffer_copy_string_len(con
->request
.uri
, uri
, jlen
);
644 } else if (0 == buffer_caseless_compare(uri
, 7, "http://", 7) &&
645 NULL
!= (nuri
= memchr(uri
+ 7, '/', jlen
-7))) {
646 state
->reqline_host
= uri
+ 7;
647 state
->reqline_hostlen
= nuri
- state
->reqline_host
;
649 buffer_copy_string_len(con
->request
.uri
, nuri
, proto
- nuri
- 1);
650 } else if (0 == buffer_caseless_compare(uri
, 8, "https://", 8) &&
651 NULL
!= (nuri
= memchr(uri
+ 8, '/', jlen
-8))) {
652 state
->reqline_host
= uri
+ 8;
653 state
->reqline_hostlen
= nuri
- state
->reqline_host
;
655 buffer_copy_string_len(con
->request
.uri
, nuri
, proto
- nuri
- 1);
656 } else if (!http_header_strict
657 || (HTTP_METHOD_CONNECT
== con
->request
.http_method
&& (uri
[0] == ':' || light_isdigit(uri
[0])))
658 || (HTTP_METHOD_OPTIONS
== con
->request
.http_method
&& uri
[0] == '*' && 1 == jlen
)) {
659 buffer_copy_string_len(con
->request
.uri
, uri
, jlen
);
661 return http_request_header_line_invalid(srv
, 400, "request-URI parse error -> 400");
664 /* check uri for invalid characters */
665 jlen
= buffer_string_length(con
->request
.uri
);
666 if (0 == jlen
) return http_request_header_line_invalid(srv
, 400, "no uri specified -> 400");
667 if ((con
->conf
.http_parseopts
& HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT
)) {
668 j
= jlen
; /* URI will be checked in http_response_prepare() */
669 } else if (http_header_strict
) {
670 for (j
= 0; j
< jlen
&& request_uri_is_valid_char(con
->request
.uri
->ptr
[j
]); j
++) ;
672 char *z
= memchr(con
->request
.uri
->ptr
, '\0', jlen
);
673 j
= (NULL
== z
) ? jlen
: (size_t)(z
- con
->request
.uri
->ptr
);
676 return http_request_header_char_invalid(srv
, con
->request
.uri
->ptr
[j
], "invalid character in URI -> 400");
679 buffer_copy_buffer(con
->request
.orig_uri
, con
->request
.uri
);
682 if (state
->reqline_host
) {
683 /* Insert as host header */
684 if (state
->reqline_hostlen
>= 1024) { /*(expecting < 256)*/
685 return http_request_header_line_invalid(srv
, 400, "uri-authority too long -> 400");
687 http_header_request_set(con
, HTTP_HEADER_HOST
, CONST_STR_LEN("Host"), state
->reqline_host
, state
->reqline_hostlen
);
688 con
->request
.http_host
= http_header_request_get(con
, HTTP_HEADER_HOST
, CONST_STR_LEN("Host"));
694 int http_request_parse(server
*srv
, connection
*con
, buffer
*hdrs
) {
695 char * const ptr
= hdrs
->ptr
;
697 size_t i
, first
, ilen
;
698 const unsigned int http_header_strict
= (con
->conf
.http_parseopts
& HTTP_PARSEOPT_HEADER_STRICT
);
701 parse_header_state state
;
702 init_parse_header_state(&state
);
704 status
= http_request_parse_reqline(srv
, con
, hdrs
, &state
);
705 if (0 != status
) return status
;
707 i
= first
= state
.reqline_len
;
709 if (ptr
[i
] == ' ' || ptr
[i
] == '\t') {
710 return http_request_header_line_invalid(srv
, 400, "WS at the start of first line -> 400");
713 ilen
= buffer_string_length(hdrs
);
714 for (int is_key
= 1, key_len
= 0; i
< ilen
; ++i
) {
719 * 1*<any CHAR except CTLs or separators>
720 * CTLs == 0-31 + 127, CHAR = 7-bit ascii (0..127)
726 /* skip every thing up to the : */
727 do { ++cur
; } while (*cur
== ' ' || *cur
== '\t');
729 return http_request_header_line_invalid(srv
, 400, "WS character in key -> 400");
754 return http_request_header_char_invalid(srv
, *cur
, "invalid character in header key -> 400");
756 if (ptr
[i
+1] == '\n' && i
== first
) {
760 return http_request_header_line_invalid(srv
, 400, "CR without LF -> 400");
764 if (http_header_strict
) {
765 return http_request_header_line_invalid(srv
, 400, "missing CR before LF in header -> 400");
766 } else if (i
== first
) {
772 if (http_header_strict
? (*cur
< 32 || ((unsigned char)*cur
) >= 127) : *cur
== '\0') {
773 return http_request_header_char_invalid(srv
, *cur
, "invalid character in header key -> 400");
781 if (cur
[1] != '\n') {
782 return http_request_header_line_invalid(srv
, 400, "CR without LF -> 400");
784 if (cur
[2] == ' ' || cur
[2] == '\t') { /* header line folding */
794 if (http_header_strict
) {
795 return http_request_header_line_invalid(srv
, 400, "missing CR before LF in header -> 400");
797 if (cur
[1] == ' ' || cur
[1] == '\t') { /* header line folding */
804 /* End of Headerline */
805 *cur
= '\0'; /*(for if value is further parsed and '\0' is expected at end of string)*/
807 status
= parse_single_header(srv
, con
, &state
, ptr
+ first
, key_len
, value
, cur
- value
);
808 if (0 != status
) return status
;
818 if (http_header_strict
? (*cur
>= 0 && *cur
< 32) : *cur
== '\0') {
819 return http_request_header_char_invalid(srv
, *cur
, "invalid character in header -> 400");
826 /* do some post-processing */
828 if (con
->request
.http_version
== HTTP_VERSION_1_1
) {
829 if (state
.keep_alive_set
!= HTTP_CONNECTION_CLOSE
) {
830 /* no Connection-Header sent */
832 /* HTTP/1.1 -> keep-alive default TRUE */
838 /* RFC 2616, 14.23 */
839 if (con
->request
.http_host
== NULL
||
840 buffer_string_is_empty(con
->request
.http_host
)) {
841 return http_request_header_line_invalid(srv
, 400, "HTTP/1.1 but Host missing -> 400");
844 if (state
.keep_alive_set
== HTTP_CONNECTION_KEEPALIVE
) {
845 /* no Connection-Header sent */
847 /* HTTP/1.0 -> keep-alive default FALSE */
854 /* check hostname field if it is set */
855 if (!buffer_is_empty(con
->request
.http_host
) &&
856 0 != http_request_host_policy(con
, con
->request
.http_host
, con
->proto
)) {
857 return http_request_header_line_invalid(srv
, 400, "Invalid Hostname -> 400");
860 if (con
->request
.htags
& HTTP_HEADER_TRANSFER_ENCODING
) {
861 buffer
*vb
= http_header_request_get(con
, HTTP_HEADER_TRANSFER_ENCODING
, CONST_STR_LEN("Transfer-Encoding"));
863 if (con
->request
.http_version
== HTTP_VERSION_1_0
) {
864 return http_request_header_line_invalid(srv
, 400, "HTTP/1.0 with Transfer-Encoding (bad HTTP/1.0 proxy?) -> 400");
867 if (0 != buffer_caseless_compare(CONST_BUF_LEN(vb
), CONST_STR_LEN("chunked"))) {
868 /* Transfer-Encoding might contain additional encodings,
869 * which are not currently supported by lighttpd */
870 return http_request_header_line_invalid(srv
, 501, NULL
); /* Not Implemented */
873 /* reset value for Transfer-Encoding, a hop-by-hop header,
874 * which must not be blindly forwarded to backends */
875 http_header_request_unset(con
, HTTP_HEADER_TRANSFER_ENCODING
, CONST_STR_LEN("Transfer-Encoding"));
877 /*(note: ignore whether or not Content-Length was provided)*/
878 if (con
->request
.htags
& HTTP_HEADER_CONTENT_LENGTH
) {
879 http_header_request_unset(con
, HTTP_HEADER_CONTENT_LENGTH
, CONST_STR_LEN("Content-Length"));
882 state
.con_length_set
= 1;
883 con
->request
.content_length
= -1;
886 else if (con
->request
.htags
& HTTP_HEADER_CONTENT_LENGTH
) {
887 state
.con_length_set
= 1;
890 switch(con
->request
.http_method
) {
891 case HTTP_METHOD_GET
:
892 case HTTP_METHOD_HEAD
:
893 /* content-length is forbidden for those */
894 if (state
.con_length_set
&& 0 != con
->request
.content_length
895 && !(con
->conf
.http_parseopts
& HTTP_PARSEOPT_METHOD_GET_BODY
)) {
896 return http_request_header_line_invalid(srv
, 400, "GET/HEAD with content-length -> 400");
899 case HTTP_METHOD_POST
:
900 /* content-length is required for them */
901 if (!state
.con_length_set
) {
902 return http_request_header_line_invalid(srv
, 411, "POST-request, but content-length missing -> 411");