1*76404edcSAsim Jamshed #include "request.h"
2*76404edcSAsim Jamshed #include "keyvalue.h"
3*76404edcSAsim Jamshed #include "log.h"
4*76404edcSAsim Jamshed
5*76404edcSAsim Jamshed #include <sys/stat.h>
6*76404edcSAsim Jamshed
7*76404edcSAsim Jamshed #include <limits.h>
8*76404edcSAsim Jamshed #include <stdlib.h>
9*76404edcSAsim Jamshed #include <string.h>
10*76404edcSAsim Jamshed #include <stdio.h>
11*76404edcSAsim Jamshed #include <ctype.h>
12*76404edcSAsim Jamshed
request_check_hostname(server * srv,connection * con,buffer * host)13*76404edcSAsim Jamshed static int request_check_hostname(server *srv, connection *con, buffer *host) {
14*76404edcSAsim Jamshed enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL;
15*76404edcSAsim Jamshed size_t i;
16*76404edcSAsim Jamshed int label_len = 0;
17*76404edcSAsim Jamshed size_t host_len;
18*76404edcSAsim Jamshed char *colon;
19*76404edcSAsim Jamshed int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */
20*76404edcSAsim Jamshed int level = 0;
21*76404edcSAsim Jamshed
22*76404edcSAsim Jamshed UNUSED(srv);
23*76404edcSAsim Jamshed UNUSED(con);
24*76404edcSAsim Jamshed
25*76404edcSAsim Jamshed /*
26*76404edcSAsim Jamshed * hostport = host [ ":" port ]
27*76404edcSAsim Jamshed * host = hostname | IPv4address | IPv6address
28*76404edcSAsim Jamshed * hostname = *( domainlabel "." ) toplabel [ "." ]
29*76404edcSAsim Jamshed * domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
30*76404edcSAsim Jamshed * toplabel = alpha | alpha *( alphanum | "-" ) alphanum
31*76404edcSAsim Jamshed * IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
32*76404edcSAsim Jamshed * IPv6address = "[" ... "]"
33*76404edcSAsim Jamshed * port = *digit
34*76404edcSAsim Jamshed */
35*76404edcSAsim Jamshed
36*76404edcSAsim Jamshed /* no Host: */
37*76404edcSAsim Jamshed if (!host || host->used == 0) return 0;
38*76404edcSAsim Jamshed
39*76404edcSAsim Jamshed host_len = host->used - 1;
40*76404edcSAsim Jamshed
41*76404edcSAsim Jamshed /* IPv6 adress */
42*76404edcSAsim Jamshed if (host->ptr[0] == '[') {
43*76404edcSAsim Jamshed char *c = host->ptr + 1;
44*76404edcSAsim Jamshed int colon_cnt = 0;
45*76404edcSAsim Jamshed
46*76404edcSAsim Jamshed /* check portnumber */
47*76404edcSAsim Jamshed for (; *c && *c != ']'; c++) {
48*76404edcSAsim Jamshed if (*c == ':') {
49*76404edcSAsim Jamshed if (++colon_cnt > 7) {
50*76404edcSAsim Jamshed return -1;
51*76404edcSAsim Jamshed }
52*76404edcSAsim Jamshed } else if (!light_isxdigit(*c) && '.' != *c) {
53*76404edcSAsim Jamshed return -1;
54*76404edcSAsim Jamshed }
55*76404edcSAsim Jamshed }
56*76404edcSAsim Jamshed
57*76404edcSAsim Jamshed /* missing ] */
58*76404edcSAsim Jamshed if (!*c) {
59*76404edcSAsim Jamshed return -1;
60*76404edcSAsim Jamshed }
61*76404edcSAsim Jamshed
62*76404edcSAsim Jamshed /* check port */
63*76404edcSAsim Jamshed if (*(c+1) == ':') {
64*76404edcSAsim Jamshed for (c += 2; *c; c++) {
65*76404edcSAsim Jamshed if (!light_isdigit(*c)) {
66*76404edcSAsim Jamshed return -1;
67*76404edcSAsim Jamshed }
68*76404edcSAsim Jamshed }
69*76404edcSAsim Jamshed }
70*76404edcSAsim Jamshed return 0;
71*76404edcSAsim Jamshed }
72*76404edcSAsim Jamshed
73*76404edcSAsim Jamshed if (NULL != (colon = memchr(host->ptr, ':', host_len))) {
74*76404edcSAsim Jamshed char *c = colon + 1;
75*76404edcSAsim Jamshed
76*76404edcSAsim Jamshed /* check portnumber */
77*76404edcSAsim Jamshed for (; *c; c++) {
78*76404edcSAsim Jamshed if (!light_isdigit(*c)) return -1;
79*76404edcSAsim Jamshed }
80*76404edcSAsim Jamshed
81*76404edcSAsim Jamshed /* remove the port from the host-len */
82*76404edcSAsim Jamshed host_len = colon - host->ptr;
83*76404edcSAsim Jamshed }
84*76404edcSAsim Jamshed
85*76404edcSAsim Jamshed /* Host is empty */
86*76404edcSAsim Jamshed if (host_len == 0) return -1;
87*76404edcSAsim Jamshed
88*76404edcSAsim Jamshed /* if the hostname ends in a "." strip it */
89*76404edcSAsim Jamshed if (host->ptr[host_len-1] == '.') {
90*76404edcSAsim Jamshed /* shift port info one left */
91*76404edcSAsim Jamshed if (NULL != colon) memmove(colon-1, colon, host->used - host_len);
92*76404edcSAsim Jamshed else host->ptr[host_len-1] = '\0';
93*76404edcSAsim Jamshed host_len -= 1;
94*76404edcSAsim Jamshed host->used -= 1;
95*76404edcSAsim Jamshed }
96*76404edcSAsim Jamshed
97*76404edcSAsim Jamshed if (host_len == 0) return -1;
98*76404edcSAsim Jamshed
99*76404edcSAsim Jamshed /* scan from the right and skip the \0 */
100*76404edcSAsim Jamshed for (i = host_len; i-- > 0; ) {
101*76404edcSAsim Jamshed const char c = host->ptr[i];
102*76404edcSAsim Jamshed
103*76404edcSAsim Jamshed switch (stage) {
104*76404edcSAsim Jamshed case TOPLABEL:
105*76404edcSAsim Jamshed if (c == '.') {
106*76404edcSAsim Jamshed /* only switch stage, if this is not the last character */
107*76404edcSAsim Jamshed if (i != host_len - 1) {
108*76404edcSAsim Jamshed if (label_len == 0) {
109*76404edcSAsim Jamshed return -1;
110*76404edcSAsim Jamshed }
111*76404edcSAsim Jamshed
112*76404edcSAsim Jamshed /* check the first character at right of the dot */
113*76404edcSAsim Jamshed if (is_ip == 0) {
114*76404edcSAsim Jamshed if (!light_isalnum(host->ptr[i+1])) {
115*76404edcSAsim Jamshed return -1;
116*76404edcSAsim Jamshed }
117*76404edcSAsim Jamshed } else if (!light_isdigit(host->ptr[i+1])) {
118*76404edcSAsim Jamshed is_ip = 0;
119*76404edcSAsim Jamshed } else if ('-' == host->ptr[i+1]) {
120*76404edcSAsim Jamshed return -1;
121*76404edcSAsim Jamshed } else {
122*76404edcSAsim Jamshed /* just digits */
123*76404edcSAsim Jamshed is_ip = 1;
124*76404edcSAsim Jamshed }
125*76404edcSAsim Jamshed
126*76404edcSAsim Jamshed stage = DOMAINLABEL;
127*76404edcSAsim Jamshed
128*76404edcSAsim Jamshed label_len = 0;
129*76404edcSAsim Jamshed level++;
130*76404edcSAsim Jamshed } else if (i == 0) {
131*76404edcSAsim Jamshed /* just a dot and nothing else is evil */
132*76404edcSAsim Jamshed return -1;
133*76404edcSAsim Jamshed }
134*76404edcSAsim Jamshed } else if (i == 0) {
135*76404edcSAsim Jamshed /* the first character of the hostname */
136*76404edcSAsim Jamshed if (!light_isalnum(c)) {
137*76404edcSAsim Jamshed return -1;
138*76404edcSAsim Jamshed }
139*76404edcSAsim Jamshed label_len++;
140*76404edcSAsim Jamshed } else {
141*76404edcSAsim Jamshed if (c != '-' && !light_isalnum(c)) {
142*76404edcSAsim Jamshed return -1;
143*76404edcSAsim Jamshed }
144*76404edcSAsim Jamshed if (is_ip == -1) {
145*76404edcSAsim Jamshed if (!light_isdigit(c)) is_ip = 0;
146*76404edcSAsim Jamshed }
147*76404edcSAsim Jamshed label_len++;
148*76404edcSAsim Jamshed }
149*76404edcSAsim Jamshed
150*76404edcSAsim Jamshed break;
151*76404edcSAsim Jamshed case DOMAINLABEL:
152*76404edcSAsim Jamshed if (is_ip == 1) {
153*76404edcSAsim Jamshed if (c == '.') {
154*76404edcSAsim Jamshed if (label_len == 0) {
155*76404edcSAsim Jamshed return -1;
156*76404edcSAsim Jamshed }
157*76404edcSAsim Jamshed
158*76404edcSAsim Jamshed label_len = 0;
159*76404edcSAsim Jamshed level++;
160*76404edcSAsim Jamshed } else if (!light_isdigit(c)) {
161*76404edcSAsim Jamshed return -1;
162*76404edcSAsim Jamshed } else {
163*76404edcSAsim Jamshed label_len++;
164*76404edcSAsim Jamshed }
165*76404edcSAsim Jamshed } else {
166*76404edcSAsim Jamshed if (c == '.') {
167*76404edcSAsim Jamshed if (label_len == 0) {
168*76404edcSAsim Jamshed return -1;
169*76404edcSAsim Jamshed }
170*76404edcSAsim Jamshed
171*76404edcSAsim Jamshed /* c is either - or alphanum here */
172*76404edcSAsim Jamshed if ('-' == host->ptr[i+1]) {
173*76404edcSAsim Jamshed return -1;
174*76404edcSAsim Jamshed }
175*76404edcSAsim Jamshed
176*76404edcSAsim Jamshed label_len = 0;
177*76404edcSAsim Jamshed level++;
178*76404edcSAsim Jamshed } else if (i == 0) {
179*76404edcSAsim Jamshed if (!light_isalnum(c)) {
180*76404edcSAsim Jamshed return -1;
181*76404edcSAsim Jamshed }
182*76404edcSAsim Jamshed label_len++;
183*76404edcSAsim Jamshed } else {
184*76404edcSAsim Jamshed if (c != '-' && !light_isalnum(c)) {
185*76404edcSAsim Jamshed return -1;
186*76404edcSAsim Jamshed }
187*76404edcSAsim Jamshed label_len++;
188*76404edcSAsim Jamshed }
189*76404edcSAsim Jamshed }
190*76404edcSAsim Jamshed
191*76404edcSAsim Jamshed break;
192*76404edcSAsim Jamshed }
193*76404edcSAsim Jamshed }
194*76404edcSAsim Jamshed
195*76404edcSAsim Jamshed /* a IP has to consist of 4 parts */
196*76404edcSAsim Jamshed if (is_ip == 1 && level != 3) {
197*76404edcSAsim Jamshed return -1;
198*76404edcSAsim Jamshed }
199*76404edcSAsim Jamshed
200*76404edcSAsim Jamshed if (label_len == 0) {
201*76404edcSAsim Jamshed return -1;
202*76404edcSAsim Jamshed }
203*76404edcSAsim Jamshed
204*76404edcSAsim Jamshed return 0;
205*76404edcSAsim Jamshed }
206*76404edcSAsim Jamshed
207*76404edcSAsim Jamshed #if 0
208*76404edcSAsim Jamshed #define DUMP_HEADER
209*76404edcSAsim Jamshed #endif
210*76404edcSAsim Jamshed
http_request_split_value(array * vals,buffer * b)211*76404edcSAsim Jamshed static int http_request_split_value(array *vals, buffer *b) {
212*76404edcSAsim Jamshed size_t i;
213*76404edcSAsim Jamshed int state = 0;
214*76404edcSAsim Jamshed
215*76404edcSAsim Jamshed const char *current;
216*76404edcSAsim Jamshed const char *token_start = NULL, *token_end = NULL;
217*76404edcSAsim Jamshed /*
218*76404edcSAsim Jamshed * parse
219*76404edcSAsim Jamshed *
220*76404edcSAsim Jamshed * val1, val2, val3, val4
221*76404edcSAsim Jamshed *
222*76404edcSAsim Jamshed * into a array (more or less a explode() incl. striping of whitespaces
223*76404edcSAsim Jamshed */
224*76404edcSAsim Jamshed
225*76404edcSAsim Jamshed if (b->used == 0) return 0;
226*76404edcSAsim Jamshed
227*76404edcSAsim Jamshed current = b->ptr;
228*76404edcSAsim Jamshed for (i = 0; i < b->used; ++i, ++current) {
229*76404edcSAsim Jamshed data_string *ds;
230*76404edcSAsim Jamshed
231*76404edcSAsim Jamshed switch (state) {
232*76404edcSAsim Jamshed case 0: /* find start of a token */
233*76404edcSAsim Jamshed switch (*current) {
234*76404edcSAsim Jamshed case ' ':
235*76404edcSAsim Jamshed case '\t': /* skip white space */
236*76404edcSAsim Jamshed case ',': /* skip empty token */
237*76404edcSAsim Jamshed break;
238*76404edcSAsim Jamshed case '\0': /* end of string */
239*76404edcSAsim Jamshed return 0;
240*76404edcSAsim Jamshed default:
241*76404edcSAsim Jamshed /* found real data, switch to state 1 to find the end of the token */
242*76404edcSAsim Jamshed token_start = token_end = current;
243*76404edcSAsim Jamshed state = 1;
244*76404edcSAsim Jamshed break;
245*76404edcSAsim Jamshed }
246*76404edcSAsim Jamshed break;
247*76404edcSAsim Jamshed case 1: /* find end of token and last non white space character */
248*76404edcSAsim Jamshed switch (*current) {
249*76404edcSAsim Jamshed case ' ':
250*76404edcSAsim Jamshed case '\t':
251*76404edcSAsim Jamshed /* space - don't update token_end */
252*76404edcSAsim Jamshed break;
253*76404edcSAsim Jamshed case ',':
254*76404edcSAsim Jamshed case '\0': /* end of string also marks the end of a token */
255*76404edcSAsim Jamshed if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) {
256*76404edcSAsim Jamshed ds = data_string_init();
257*76404edcSAsim Jamshed }
258*76404edcSAsim Jamshed
259*76404edcSAsim Jamshed buffer_copy_string_len(ds->value, token_start, token_end-token_start+1);
260*76404edcSAsim Jamshed array_insert_unique(vals, (data_unset *)ds);
261*76404edcSAsim Jamshed
262*76404edcSAsim Jamshed state = 0;
263*76404edcSAsim Jamshed break;
264*76404edcSAsim Jamshed default:
265*76404edcSAsim Jamshed /* no white space, update token_end to include current character */
266*76404edcSAsim Jamshed token_end = current;
267*76404edcSAsim Jamshed break;
268*76404edcSAsim Jamshed }
269*76404edcSAsim Jamshed break;
270*76404edcSAsim Jamshed }
271*76404edcSAsim Jamshed }
272*76404edcSAsim Jamshed
273*76404edcSAsim Jamshed return 0;
274*76404edcSAsim Jamshed }
275*76404edcSAsim Jamshed
request_uri_is_valid_char(unsigned char c)276*76404edcSAsim Jamshed static int request_uri_is_valid_char(unsigned char c) {
277*76404edcSAsim Jamshed if (c <= 32) return 0;
278*76404edcSAsim Jamshed if (c == 127) return 0;
279*76404edcSAsim Jamshed if (c == 255) return 0;
280*76404edcSAsim Jamshed
281*76404edcSAsim Jamshed return 1;
282*76404edcSAsim Jamshed }
283*76404edcSAsim Jamshed
http_request_parse(server * srv,connection * con)284*76404edcSAsim Jamshed int http_request_parse(server *srv, connection *con) {
285*76404edcSAsim Jamshed char *uri = NULL, *proto = NULL, *method = NULL, con_length_set;
286*76404edcSAsim Jamshed int is_key = 1, key_len = 0, is_ws_after_key = 0, in_folding;
287*76404edcSAsim Jamshed char *value = NULL, *key = NULL;
288*76404edcSAsim Jamshed char *reqline_host = NULL;
289*76404edcSAsim Jamshed int reqline_hostlen = 0;
290*76404edcSAsim Jamshed
291*76404edcSAsim Jamshed enum { HTTP_CONNECTION_UNSET, HTTP_CONNECTION_KEEPALIVE, HTTP_CONNECTION_CLOSE } keep_alive_set = HTTP_CONNECTION_UNSET;
292*76404edcSAsim Jamshed
293*76404edcSAsim Jamshed int line = 0;
294*76404edcSAsim Jamshed
295*76404edcSAsim Jamshed int request_line_stage = 0;
296*76404edcSAsim Jamshed size_t i, first;
297*76404edcSAsim Jamshed
298*76404edcSAsim Jamshed int done = 0;
299*76404edcSAsim Jamshed
300*76404edcSAsim Jamshed /*
301*76404edcSAsim Jamshed * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$"
302*76404edcSAsim Jamshed * Option : "^([-a-zA-Z]+): (.+)$"
303*76404edcSAsim Jamshed * End : "^$"
304*76404edcSAsim Jamshed */
305*76404edcSAsim Jamshed
306*76404edcSAsim Jamshed if (con->conf.log_request_header) {
307*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sdsdSb",
308*76404edcSAsim Jamshed "fd:", con->fd,
309*76404edcSAsim Jamshed "request-len:", con->request.request->used,
310*76404edcSAsim Jamshed "\n", con->request.request);
311*76404edcSAsim Jamshed }
312*76404edcSAsim Jamshed
313*76404edcSAsim Jamshed if (con->request_count > 1 &&
314*76404edcSAsim Jamshed con->request.request->ptr[0] == '\r' &&
315*76404edcSAsim Jamshed con->request.request->ptr[1] == '\n') {
316*76404edcSAsim Jamshed /* we are in keep-alive and might get \r\n after a previous POST request.*/
317*76404edcSAsim Jamshed
318*76404edcSAsim Jamshed buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, con->request.request->used - 1 - 2);
319*76404edcSAsim Jamshed } else {
320*76404edcSAsim Jamshed /* fill the local request buffer */
321*76404edcSAsim Jamshed buffer_copy_string_buffer(con->parse_request, con->request.request);
322*76404edcSAsim Jamshed }
323*76404edcSAsim Jamshed
324*76404edcSAsim Jamshed keep_alive_set = 0;
325*76404edcSAsim Jamshed con_length_set = 0;
326*76404edcSAsim Jamshed
327*76404edcSAsim Jamshed /* parse the first line of the request
328*76404edcSAsim Jamshed *
329*76404edcSAsim Jamshed * should be:
330*76404edcSAsim Jamshed *
331*76404edcSAsim Jamshed * <method> <uri> <protocol>\r\n
332*76404edcSAsim Jamshed * */
333*76404edcSAsim Jamshed for (i = 0, first = 0; i < con->parse_request->used && line == 0; i++) {
334*76404edcSAsim Jamshed char *cur = con->parse_request->ptr + i;
335*76404edcSAsim Jamshed
336*76404edcSAsim Jamshed switch(*cur) {
337*76404edcSAsim Jamshed case '\r':
338*76404edcSAsim Jamshed if (con->parse_request->ptr[i+1] == '\n') {
339*76404edcSAsim Jamshed http_method_t r;
340*76404edcSAsim Jamshed char *nuri = NULL;
341*76404edcSAsim Jamshed size_t j;
342*76404edcSAsim Jamshed
343*76404edcSAsim Jamshed /* \r\n -> \0\0 */
344*76404edcSAsim Jamshed con->parse_request->ptr[i] = '\0';
345*76404edcSAsim Jamshed con->parse_request->ptr[i+1] = '\0';
346*76404edcSAsim Jamshed
347*76404edcSAsim Jamshed buffer_copy_string_len(con->request.request_line, con->parse_request->ptr, i);
348*76404edcSAsim Jamshed
349*76404edcSAsim Jamshed if (request_line_stage != 2) {
350*76404edcSAsim Jamshed con->http_status = 400;
351*76404edcSAsim Jamshed con->response.keep_alive = 0;
352*76404edcSAsim Jamshed con->keep_alive = 0;
353*76404edcSAsim Jamshed
354*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
355*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400");
356*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
357*76404edcSAsim Jamshed "request-header:\n",
358*76404edcSAsim Jamshed con->request.request);
359*76404edcSAsim Jamshed }
360*76404edcSAsim Jamshed return 0;
361*76404edcSAsim Jamshed }
362*76404edcSAsim Jamshed
363*76404edcSAsim Jamshed proto = con->parse_request->ptr + first;
364*76404edcSAsim Jamshed
365*76404edcSAsim Jamshed *(uri - 1) = '\0';
366*76404edcSAsim Jamshed *(proto - 1) = '\0';
367*76404edcSAsim Jamshed
368*76404edcSAsim Jamshed /* we got the first one :) */
369*76404edcSAsim Jamshed if (-1 == (r = get_http_method_key(method))) {
370*76404edcSAsim Jamshed con->http_status = 501;
371*76404edcSAsim Jamshed con->response.keep_alive = 0;
372*76404edcSAsim Jamshed con->keep_alive = 0;
373*76404edcSAsim Jamshed
374*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
375*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501");
376*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
377*76404edcSAsim Jamshed "request-header:\n",
378*76404edcSAsim Jamshed con->request.request);
379*76404edcSAsim Jamshed }
380*76404edcSAsim Jamshed
381*76404edcSAsim Jamshed return 0;
382*76404edcSAsim Jamshed }
383*76404edcSAsim Jamshed
384*76404edcSAsim Jamshed con->request.http_method = r;
385*76404edcSAsim Jamshed
386*76404edcSAsim Jamshed /*
387*76404edcSAsim Jamshed * RFC2616 says:
388*76404edcSAsim Jamshed *
389*76404edcSAsim Jamshed * HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
390*76404edcSAsim Jamshed *
391*76404edcSAsim Jamshed * */
392*76404edcSAsim Jamshed if (0 == strncmp(proto, "HTTP/", sizeof("HTTP/") - 1)) {
393*76404edcSAsim Jamshed char * major = proto + sizeof("HTTP/") - 1;
394*76404edcSAsim Jamshed char * minor = strchr(major, '.');
395*76404edcSAsim Jamshed char *err = NULL;
396*76404edcSAsim Jamshed int major_num = 0, minor_num = 0;
397*76404edcSAsim Jamshed
398*76404edcSAsim Jamshed int invalid_version = 0;
399*76404edcSAsim Jamshed
400*76404edcSAsim Jamshed if (NULL == minor || /* no dot */
401*76404edcSAsim Jamshed minor == major || /* no major */
402*76404edcSAsim Jamshed *(minor + 1) == '\0' /* no minor */) {
403*76404edcSAsim Jamshed invalid_version = 1;
404*76404edcSAsim Jamshed } else {
405*76404edcSAsim Jamshed *minor = '\0';
406*76404edcSAsim Jamshed major_num = strtol(major, &err, 10);
407*76404edcSAsim Jamshed
408*76404edcSAsim Jamshed if (*err != '\0') invalid_version = 1;
409*76404edcSAsim Jamshed
410*76404edcSAsim Jamshed *minor++ = '.';
411*76404edcSAsim Jamshed minor_num = strtol(minor, &err, 10);
412*76404edcSAsim Jamshed
413*76404edcSAsim Jamshed if (*err != '\0') invalid_version = 1;
414*76404edcSAsim Jamshed }
415*76404edcSAsim Jamshed
416*76404edcSAsim Jamshed if (invalid_version) {
417*76404edcSAsim Jamshed con->http_status = 400;
418*76404edcSAsim Jamshed con->keep_alive = 0;
419*76404edcSAsim Jamshed
420*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
421*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
422*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
423*76404edcSAsim Jamshed "request-header:\n",
424*76404edcSAsim Jamshed con->request.request);
425*76404edcSAsim Jamshed }
426*76404edcSAsim Jamshed return 0;
427*76404edcSAsim Jamshed }
428*76404edcSAsim Jamshed
429*76404edcSAsim Jamshed if (major_num == 1 && minor_num == 1) {
430*76404edcSAsim Jamshed con->request.http_version = con->conf.allow_http11 ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0;
431*76404edcSAsim Jamshed } else if (major_num == 1 && minor_num == 0) {
432*76404edcSAsim Jamshed con->request.http_version = HTTP_VERSION_1_0;
433*76404edcSAsim Jamshed } else {
434*76404edcSAsim Jamshed con->http_status = 505;
435*76404edcSAsim Jamshed
436*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
437*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "unknown HTTP version -> 505");
438*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
439*76404edcSAsim Jamshed "request-header:\n",
440*76404edcSAsim Jamshed con->request.request);
441*76404edcSAsim Jamshed }
442*76404edcSAsim Jamshed return 0;
443*76404edcSAsim Jamshed }
444*76404edcSAsim Jamshed } else {
445*76404edcSAsim Jamshed con->http_status = 400;
446*76404edcSAsim Jamshed con->keep_alive = 0;
447*76404edcSAsim Jamshed
448*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
449*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
450*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
451*76404edcSAsim Jamshed "request-header:\n",
452*76404edcSAsim Jamshed con->request.request);
453*76404edcSAsim Jamshed }
454*76404edcSAsim Jamshed return 0;
455*76404edcSAsim Jamshed }
456*76404edcSAsim Jamshed
457*76404edcSAsim Jamshed if (0 == strncmp(uri, "http://", 7) &&
458*76404edcSAsim Jamshed NULL != (nuri = strchr(uri + 7, '/'))) {
459*76404edcSAsim Jamshed reqline_host = uri + 7;
460*76404edcSAsim Jamshed reqline_hostlen = nuri - reqline_host;
461*76404edcSAsim Jamshed
462*76404edcSAsim Jamshed buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
463*76404edcSAsim Jamshed } else if (0 == strncmp(uri, "https://", 8) &&
464*76404edcSAsim Jamshed NULL != (nuri = strchr(uri + 8, '/'))) {
465*76404edcSAsim Jamshed reqline_host = uri + 8;
466*76404edcSAsim Jamshed reqline_hostlen = nuri - reqline_host;
467*76404edcSAsim Jamshed
468*76404edcSAsim Jamshed buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
469*76404edcSAsim Jamshed } else {
470*76404edcSAsim Jamshed /* everything looks good so far */
471*76404edcSAsim Jamshed buffer_copy_string_len(con->request.uri, uri, proto - uri - 1);
472*76404edcSAsim Jamshed }
473*76404edcSAsim Jamshed
474*76404edcSAsim Jamshed /* check uri for invalid characters */
475*76404edcSAsim Jamshed for (j = 0; j < con->request.uri->used - 1; j++) {
476*76404edcSAsim Jamshed if (!request_uri_is_valid_char(con->request.uri->ptr[j])) {
477*76404edcSAsim Jamshed unsigned char buf[2];
478*76404edcSAsim Jamshed con->http_status = 400;
479*76404edcSAsim Jamshed con->keep_alive = 0;
480*76404edcSAsim Jamshed
481*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
482*76404edcSAsim Jamshed buf[0] = con->request.uri->ptr[j];
483*76404edcSAsim Jamshed buf[1] = '\0';
484*76404edcSAsim Jamshed
485*76404edcSAsim Jamshed if (con->request.uri->ptr[j] > 32 &&
486*76404edcSAsim Jamshed con->request.uri->ptr[j] != 127) {
487*76404edcSAsim Jamshed /* the character is printable -> print it */
488*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss",
489*76404edcSAsim Jamshed "invalid character in URI -> 400",
490*76404edcSAsim Jamshed buf);
491*76404edcSAsim Jamshed } else {
492*76404edcSAsim Jamshed /* a control-character, print ascii-code */
493*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sd",
494*76404edcSAsim Jamshed "invalid character in URI -> 400",
495*76404edcSAsim Jamshed con->request.uri->ptr[j]);
496*76404edcSAsim Jamshed }
497*76404edcSAsim Jamshed
498*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
499*76404edcSAsim Jamshed "request-header:\n",
500*76404edcSAsim Jamshed con->request.request);
501*76404edcSAsim Jamshed }
502*76404edcSAsim Jamshed
503*76404edcSAsim Jamshed return 0;
504*76404edcSAsim Jamshed }
505*76404edcSAsim Jamshed }
506*76404edcSAsim Jamshed
507*76404edcSAsim Jamshed buffer_copy_string_buffer(con->request.orig_uri, con->request.uri);
508*76404edcSAsim Jamshed
509*76404edcSAsim Jamshed con->http_status = 0;
510*76404edcSAsim Jamshed
511*76404edcSAsim Jamshed i++;
512*76404edcSAsim Jamshed line++;
513*76404edcSAsim Jamshed first = i+1;
514*76404edcSAsim Jamshed }
515*76404edcSAsim Jamshed break;
516*76404edcSAsim Jamshed case ' ':
517*76404edcSAsim Jamshed switch(request_line_stage) {
518*76404edcSAsim Jamshed case 0:
519*76404edcSAsim Jamshed /* GET|POST|... */
520*76404edcSAsim Jamshed method = con->parse_request->ptr + first;
521*76404edcSAsim Jamshed first = i + 1;
522*76404edcSAsim Jamshed break;
523*76404edcSAsim Jamshed case 1:
524*76404edcSAsim Jamshed /* /foobar/... */
525*76404edcSAsim Jamshed uri = con->parse_request->ptr + first;
526*76404edcSAsim Jamshed first = i + 1;
527*76404edcSAsim Jamshed break;
528*76404edcSAsim Jamshed default:
529*76404edcSAsim Jamshed /* ERROR, one space to much */
530*76404edcSAsim Jamshed con->http_status = 400;
531*76404edcSAsim Jamshed con->response.keep_alive = 0;
532*76404edcSAsim Jamshed con->keep_alive = 0;
533*76404edcSAsim Jamshed
534*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
535*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "overlong request line -> 400");
536*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
537*76404edcSAsim Jamshed "request-header:\n",
538*76404edcSAsim Jamshed con->request.request);
539*76404edcSAsim Jamshed }
540*76404edcSAsim Jamshed return 0;
541*76404edcSAsim Jamshed }
542*76404edcSAsim Jamshed
543*76404edcSAsim Jamshed request_line_stage++;
544*76404edcSAsim Jamshed break;
545*76404edcSAsim Jamshed }
546*76404edcSAsim Jamshed }
547*76404edcSAsim Jamshed
548*76404edcSAsim Jamshed in_folding = 0;
549*76404edcSAsim Jamshed
550*76404edcSAsim Jamshed if (con->request.uri->used == 1) {
551*76404edcSAsim Jamshed con->http_status = 400;
552*76404edcSAsim Jamshed con->response.keep_alive = 0;
553*76404edcSAsim Jamshed con->keep_alive = 0;
554*76404edcSAsim Jamshed
555*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
556*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "no uri specified -> 400");
557*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
558*76404edcSAsim Jamshed "request-header:\n",
559*76404edcSAsim Jamshed con->request.request);
560*76404edcSAsim Jamshed }
561*76404edcSAsim Jamshed return 0;
562*76404edcSAsim Jamshed }
563*76404edcSAsim Jamshed
564*76404edcSAsim Jamshed if (reqline_host) {
565*76404edcSAsim Jamshed /* Insert as host header */
566*76404edcSAsim Jamshed data_string *ds;
567*76404edcSAsim Jamshed
568*76404edcSAsim Jamshed if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
569*76404edcSAsim Jamshed ds = data_string_init();
570*76404edcSAsim Jamshed }
571*76404edcSAsim Jamshed
572*76404edcSAsim Jamshed buffer_copy_string_len(ds->key, CONST_STR_LEN("Host"));
573*76404edcSAsim Jamshed buffer_copy_string_len(ds->value, reqline_host, reqline_hostlen);
574*76404edcSAsim Jamshed array_insert_unique(con->request.headers, (data_unset *)ds);
575*76404edcSAsim Jamshed con->request.http_host = ds->value;
576*76404edcSAsim Jamshed }
577*76404edcSAsim Jamshed
578*76404edcSAsim Jamshed for (; i < con->parse_request->used && !done; i++) {
579*76404edcSAsim Jamshed char *cur = con->parse_request->ptr + i;
580*76404edcSAsim Jamshed
581*76404edcSAsim Jamshed if (is_key) {
582*76404edcSAsim Jamshed size_t j;
583*76404edcSAsim Jamshed int got_colon = 0;
584*76404edcSAsim Jamshed
585*76404edcSAsim Jamshed /**
586*76404edcSAsim Jamshed * 1*<any CHAR except CTLs or separators>
587*76404edcSAsim Jamshed * CTLs == 0-31 + 127
588*76404edcSAsim Jamshed *
589*76404edcSAsim Jamshed */
590*76404edcSAsim Jamshed switch(*cur) {
591*76404edcSAsim Jamshed case ':':
592*76404edcSAsim Jamshed is_key = 0;
593*76404edcSAsim Jamshed
594*76404edcSAsim Jamshed value = cur + 1;
595*76404edcSAsim Jamshed
596*76404edcSAsim Jamshed if (is_ws_after_key == 0) {
597*76404edcSAsim Jamshed key_len = i - first;
598*76404edcSAsim Jamshed }
599*76404edcSAsim Jamshed is_ws_after_key = 0;
600*76404edcSAsim Jamshed
601*76404edcSAsim Jamshed break;
602*76404edcSAsim Jamshed case '(':
603*76404edcSAsim Jamshed case ')':
604*76404edcSAsim Jamshed case '<':
605*76404edcSAsim Jamshed case '>':
606*76404edcSAsim Jamshed case '@':
607*76404edcSAsim Jamshed case ',':
608*76404edcSAsim Jamshed case ';':
609*76404edcSAsim Jamshed case '\\':
610*76404edcSAsim Jamshed case '\"':
611*76404edcSAsim Jamshed case '/':
612*76404edcSAsim Jamshed case '[':
613*76404edcSAsim Jamshed case ']':
614*76404edcSAsim Jamshed case '?':
615*76404edcSAsim Jamshed case '=':
616*76404edcSAsim Jamshed case '{':
617*76404edcSAsim Jamshed case '}':
618*76404edcSAsim Jamshed con->http_status = 400;
619*76404edcSAsim Jamshed con->keep_alive = 0;
620*76404edcSAsim Jamshed con->response.keep_alive = 0;
621*76404edcSAsim Jamshed
622*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sbsds",
623*76404edcSAsim Jamshed "invalid character in key", con->request.request, cur, *cur, "-> 400");
624*76404edcSAsim Jamshed return 0;
625*76404edcSAsim Jamshed case ' ':
626*76404edcSAsim Jamshed case '\t':
627*76404edcSAsim Jamshed if (i == first) {
628*76404edcSAsim Jamshed is_key = 0;
629*76404edcSAsim Jamshed in_folding = 1;
630*76404edcSAsim Jamshed value = cur;
631*76404edcSAsim Jamshed
632*76404edcSAsim Jamshed break;
633*76404edcSAsim Jamshed }
634*76404edcSAsim Jamshed
635*76404edcSAsim Jamshed
636*76404edcSAsim Jamshed key_len = i - first;
637*76404edcSAsim Jamshed
638*76404edcSAsim Jamshed /* skip every thing up to the : */
639*76404edcSAsim Jamshed for (j = 1; !got_colon; j++) {
640*76404edcSAsim Jamshed switch(con->parse_request->ptr[j + i]) {
641*76404edcSAsim Jamshed case ' ':
642*76404edcSAsim Jamshed case '\t':
643*76404edcSAsim Jamshed /* skip WS */
644*76404edcSAsim Jamshed continue;
645*76404edcSAsim Jamshed case ':':
646*76404edcSAsim Jamshed /* ok, done; handle the colon the usual way */
647*76404edcSAsim Jamshed
648*76404edcSAsim Jamshed i += j - 1;
649*76404edcSAsim Jamshed got_colon = 1;
650*76404edcSAsim Jamshed is_ws_after_key = 1; /* we already know the key length */
651*76404edcSAsim Jamshed
652*76404edcSAsim Jamshed break;
653*76404edcSAsim Jamshed default:
654*76404edcSAsim Jamshed /* error */
655*76404edcSAsim Jamshed
656*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
657*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "WS character in key -> 400");
658*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
659*76404edcSAsim Jamshed "request-header:\n",
660*76404edcSAsim Jamshed con->request.request);
661*76404edcSAsim Jamshed }
662*76404edcSAsim Jamshed
663*76404edcSAsim Jamshed con->http_status = 400;
664*76404edcSAsim Jamshed con->response.keep_alive = 0;
665*76404edcSAsim Jamshed con->keep_alive = 0;
666*76404edcSAsim Jamshed
667*76404edcSAsim Jamshed return 0;
668*76404edcSAsim Jamshed }
669*76404edcSAsim Jamshed }
670*76404edcSAsim Jamshed
671*76404edcSAsim Jamshed break;
672*76404edcSAsim Jamshed case '\r':
673*76404edcSAsim Jamshed if (con->parse_request->ptr[i+1] == '\n' && i == first) {
674*76404edcSAsim Jamshed /* End of Header */
675*76404edcSAsim Jamshed con->parse_request->ptr[i] = '\0';
676*76404edcSAsim Jamshed con->parse_request->ptr[i+1] = '\0';
677*76404edcSAsim Jamshed
678*76404edcSAsim Jamshed i++;
679*76404edcSAsim Jamshed
680*76404edcSAsim Jamshed done = 1;
681*76404edcSAsim Jamshed
682*76404edcSAsim Jamshed break;
683*76404edcSAsim Jamshed } else {
684*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
685*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "CR without LF -> 400");
686*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
687*76404edcSAsim Jamshed "request-header:\n",
688*76404edcSAsim Jamshed con->request.request);
689*76404edcSAsim Jamshed }
690*76404edcSAsim Jamshed
691*76404edcSAsim Jamshed con->http_status = 400;
692*76404edcSAsim Jamshed con->keep_alive = 0;
693*76404edcSAsim Jamshed con->response.keep_alive = 0;
694*76404edcSAsim Jamshed return 0;
695*76404edcSAsim Jamshed }
696*76404edcSAsim Jamshed /* fall thru */
697*76404edcSAsim Jamshed case 0: /* illegal characters (faster than a if () :) */
698*76404edcSAsim Jamshed case 1:
699*76404edcSAsim Jamshed case 2:
700*76404edcSAsim Jamshed case 3:
701*76404edcSAsim Jamshed case 4:
702*76404edcSAsim Jamshed case 5:
703*76404edcSAsim Jamshed case 6:
704*76404edcSAsim Jamshed case 7:
705*76404edcSAsim Jamshed case 8:
706*76404edcSAsim Jamshed case 10:
707*76404edcSAsim Jamshed case 11:
708*76404edcSAsim Jamshed case 12:
709*76404edcSAsim Jamshed case 14:
710*76404edcSAsim Jamshed case 15:
711*76404edcSAsim Jamshed case 16:
712*76404edcSAsim Jamshed case 17:
713*76404edcSAsim Jamshed case 18:
714*76404edcSAsim Jamshed case 19:
715*76404edcSAsim Jamshed case 20:
716*76404edcSAsim Jamshed case 21:
717*76404edcSAsim Jamshed case 22:
718*76404edcSAsim Jamshed case 23:
719*76404edcSAsim Jamshed case 24:
720*76404edcSAsim Jamshed case 25:
721*76404edcSAsim Jamshed case 26:
722*76404edcSAsim Jamshed case 27:
723*76404edcSAsim Jamshed case 28:
724*76404edcSAsim Jamshed case 29:
725*76404edcSAsim Jamshed case 30:
726*76404edcSAsim Jamshed case 31:
727*76404edcSAsim Jamshed case 127:
728*76404edcSAsim Jamshed con->http_status = 400;
729*76404edcSAsim Jamshed con->keep_alive = 0;
730*76404edcSAsim Jamshed con->response.keep_alive = 0;
731*76404edcSAsim Jamshed
732*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
733*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sbsds",
734*76404edcSAsim Jamshed "CTL character in key", con->request.request, cur, *cur, "-> 400");
735*76404edcSAsim Jamshed
736*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
737*76404edcSAsim Jamshed "request-header:\n",
738*76404edcSAsim Jamshed con->request.request);
739*76404edcSAsim Jamshed }
740*76404edcSAsim Jamshed
741*76404edcSAsim Jamshed return 0;
742*76404edcSAsim Jamshed default:
743*76404edcSAsim Jamshed /* ok */
744*76404edcSAsim Jamshed break;
745*76404edcSAsim Jamshed }
746*76404edcSAsim Jamshed } else {
747*76404edcSAsim Jamshed switch(*cur) {
748*76404edcSAsim Jamshed case '\r':
749*76404edcSAsim Jamshed if (con->parse_request->ptr[i+1] == '\n') {
750*76404edcSAsim Jamshed data_string *ds = NULL;
751*76404edcSAsim Jamshed
752*76404edcSAsim Jamshed /* End of Headerline */
753*76404edcSAsim Jamshed con->parse_request->ptr[i] = '\0';
754*76404edcSAsim Jamshed con->parse_request->ptr[i+1] = '\0';
755*76404edcSAsim Jamshed
756*76404edcSAsim Jamshed if (in_folding) {
757*76404edcSAsim Jamshed buffer *key_b;
758*76404edcSAsim Jamshed /**
759*76404edcSAsim Jamshed * we use a evil hack to handle the line-folding
760*76404edcSAsim Jamshed *
761*76404edcSAsim Jamshed * As array_insert_unique() deletes 'ds' in the case of a duplicate
762*76404edcSAsim Jamshed * ds points somewhere and we get a evil crash. As a solution we keep the old
763*76404edcSAsim Jamshed * "key" and get the current value from the hash and append us
764*76404edcSAsim Jamshed *
765*76404edcSAsim Jamshed * */
766*76404edcSAsim Jamshed
767*76404edcSAsim Jamshed if (!key || !key_len) {
768*76404edcSAsim Jamshed /* 400 */
769*76404edcSAsim Jamshed
770*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
771*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "WS at the start of first line -> 400");
772*76404edcSAsim Jamshed
773*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
774*76404edcSAsim Jamshed "request-header:\n",
775*76404edcSAsim Jamshed con->request.request);
776*76404edcSAsim Jamshed }
777*76404edcSAsim Jamshed
778*76404edcSAsim Jamshed
779*76404edcSAsim Jamshed con->http_status = 400;
780*76404edcSAsim Jamshed con->keep_alive = 0;
781*76404edcSAsim Jamshed con->response.keep_alive = 0;
782*76404edcSAsim Jamshed return 0;
783*76404edcSAsim Jamshed }
784*76404edcSAsim Jamshed
785*76404edcSAsim Jamshed key_b = buffer_init();
786*76404edcSAsim Jamshed buffer_copy_string_len(key_b, key, key_len);
787*76404edcSAsim Jamshed
788*76404edcSAsim Jamshed if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key_b->ptr))) {
789*76404edcSAsim Jamshed buffer_append_string(ds->value, value);
790*76404edcSAsim Jamshed }
791*76404edcSAsim Jamshed
792*76404edcSAsim Jamshed buffer_free(key_b);
793*76404edcSAsim Jamshed } else {
794*76404edcSAsim Jamshed int s_len;
795*76404edcSAsim Jamshed key = con->parse_request->ptr + first;
796*76404edcSAsim Jamshed
797*76404edcSAsim Jamshed s_len = cur - value;
798*76404edcSAsim Jamshed
799*76404edcSAsim Jamshed /* strip trailing white-spaces */
800*76404edcSAsim Jamshed for (; s_len > 0 &&
801*76404edcSAsim Jamshed (value[s_len - 1] == ' ' ||
802*76404edcSAsim Jamshed value[s_len - 1] == '\t'); s_len--);
803*76404edcSAsim Jamshed
804*76404edcSAsim Jamshed value[s_len] = '\0';
805*76404edcSAsim Jamshed
806*76404edcSAsim Jamshed if (s_len > 0) {
807*76404edcSAsim Jamshed int cmp = 0;
808*76404edcSAsim Jamshed if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
809*76404edcSAsim Jamshed ds = data_string_init();
810*76404edcSAsim Jamshed }
811*76404edcSAsim Jamshed buffer_copy_string_len(ds->key, key, key_len);
812*76404edcSAsim Jamshed buffer_copy_string_len(ds->value, value, s_len);
813*76404edcSAsim Jamshed
814*76404edcSAsim Jamshed /* retreive values
815*76404edcSAsim Jamshed *
816*76404edcSAsim Jamshed *
817*76404edcSAsim Jamshed * the list of options is sorted to simplify the search
818*76404edcSAsim Jamshed */
819*76404edcSAsim Jamshed
820*76404edcSAsim Jamshed if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) {
821*76404edcSAsim Jamshed array *vals;
822*76404edcSAsim Jamshed size_t vi;
823*76404edcSAsim Jamshed
824*76404edcSAsim Jamshed /* split on , */
825*76404edcSAsim Jamshed
826*76404edcSAsim Jamshed vals = srv->split_vals;
827*76404edcSAsim Jamshed
828*76404edcSAsim Jamshed array_reset(vals);
829*76404edcSAsim Jamshed
830*76404edcSAsim Jamshed http_request_split_value(vals, ds->value);
831*76404edcSAsim Jamshed
832*76404edcSAsim Jamshed for (vi = 0; vi < vals->used; vi++) {
833*76404edcSAsim Jamshed data_string *dsv = (data_string *)vals->data[vi];
834*76404edcSAsim Jamshed
835*76404edcSAsim Jamshed if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("keep-alive"))) {
836*76404edcSAsim Jamshed keep_alive_set = HTTP_CONNECTION_KEEPALIVE;
837*76404edcSAsim Jamshed
838*76404edcSAsim Jamshed break;
839*76404edcSAsim Jamshed } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) {
840*76404edcSAsim Jamshed keep_alive_set = HTTP_CONNECTION_CLOSE;
841*76404edcSAsim Jamshed
842*76404edcSAsim Jamshed break;
843*76404edcSAsim Jamshed }
844*76404edcSAsim Jamshed }
845*76404edcSAsim Jamshed
846*76404edcSAsim Jamshed } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) {
847*76404edcSAsim Jamshed char *err;
848*76404edcSAsim Jamshed unsigned long int r;
849*76404edcSAsim Jamshed size_t j;
850*76404edcSAsim Jamshed
851*76404edcSAsim Jamshed if (con_length_set) {
852*76404edcSAsim Jamshed con->http_status = 400;
853*76404edcSAsim Jamshed con->keep_alive = 0;
854*76404edcSAsim Jamshed
855*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
856*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s",
857*76404edcSAsim Jamshed "duplicate Content-Length-header -> 400");
858*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
859*76404edcSAsim Jamshed "request-header:\n",
860*76404edcSAsim Jamshed con->request.request);
861*76404edcSAsim Jamshed }
862*76404edcSAsim Jamshed array_insert_unique(con->request.headers, (data_unset *)ds);
863*76404edcSAsim Jamshed return 0;
864*76404edcSAsim Jamshed }
865*76404edcSAsim Jamshed
866*76404edcSAsim Jamshed if (ds->value->used == 0) SEGFAULT();
867*76404edcSAsim Jamshed
868*76404edcSAsim Jamshed for (j = 0; j < ds->value->used - 1; j++) {
869*76404edcSAsim Jamshed char c = ds->value->ptr[j];
870*76404edcSAsim Jamshed if (!isdigit((unsigned char)c)) {
871*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sbs",
872*76404edcSAsim Jamshed "content-length broken:", ds->value, "-> 400");
873*76404edcSAsim Jamshed
874*76404edcSAsim Jamshed con->http_status = 400;
875*76404edcSAsim Jamshed con->keep_alive = 0;
876*76404edcSAsim Jamshed
877*76404edcSAsim Jamshed array_insert_unique(con->request.headers, (data_unset *)ds);
878*76404edcSAsim Jamshed return 0;
879*76404edcSAsim Jamshed }
880*76404edcSAsim Jamshed }
881*76404edcSAsim Jamshed
882*76404edcSAsim Jamshed r = strtoul(ds->value->ptr, &err, 10);
883*76404edcSAsim Jamshed
884*76404edcSAsim Jamshed if (*err == '\0') {
885*76404edcSAsim Jamshed con_length_set = 1;
886*76404edcSAsim Jamshed con->request.content_length = r;
887*76404edcSAsim Jamshed } else {
888*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sbs",
889*76404edcSAsim Jamshed "content-length broken:", ds->value, "-> 400");
890*76404edcSAsim Jamshed
891*76404edcSAsim Jamshed con->http_status = 400;
892*76404edcSAsim Jamshed con->keep_alive = 0;
893*76404edcSAsim Jamshed
894*76404edcSAsim Jamshed array_insert_unique(con->request.headers, (data_unset *)ds);
895*76404edcSAsim Jamshed return 0;
896*76404edcSAsim Jamshed }
897*76404edcSAsim Jamshed } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Type")))) {
898*76404edcSAsim Jamshed /* if dup, only the first one will survive */
899*76404edcSAsim Jamshed if (!con->request.http_content_type) {
900*76404edcSAsim Jamshed con->request.http_content_type = ds->value->ptr;
901*76404edcSAsim Jamshed } else {
902*76404edcSAsim Jamshed con->http_status = 400;
903*76404edcSAsim Jamshed con->keep_alive = 0;
904*76404edcSAsim Jamshed
905*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
906*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s",
907*76404edcSAsim Jamshed "duplicate Content-Type-header -> 400");
908*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
909*76404edcSAsim Jamshed "request-header:\n",
910*76404edcSAsim Jamshed con->request.request);
911*76404edcSAsim Jamshed }
912*76404edcSAsim Jamshed array_insert_unique(con->request.headers, (data_unset *)ds);
913*76404edcSAsim Jamshed return 0;
914*76404edcSAsim Jamshed }
915*76404edcSAsim Jamshed } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Expect")))) {
916*76404edcSAsim Jamshed /* HTTP 2616 8.2.3
917*76404edcSAsim Jamshed * Expect: 100-continue
918*76404edcSAsim Jamshed *
919*76404edcSAsim Jamshed * -> (10.1.1) 100 (read content, process request, send final status-code)
920*76404edcSAsim Jamshed * -> (10.4.18) 417 (close)
921*76404edcSAsim Jamshed *
922*76404edcSAsim Jamshed * (not handled at all yet, we always send 417 here)
923*76404edcSAsim Jamshed *
924*76404edcSAsim Jamshed * What has to be added ?
925*76404edcSAsim Jamshed * 1. handling of chunked request body
926*76404edcSAsim Jamshed * 2. out-of-order sending from the HTTP/1.1 100 Continue
927*76404edcSAsim Jamshed * header
928*76404edcSAsim Jamshed *
929*76404edcSAsim Jamshed */
930*76404edcSAsim Jamshed
931*76404edcSAsim Jamshed if (srv->srvconf.reject_expect_100_with_417 && 0 == buffer_caseless_compare(CONST_BUF_LEN(ds->value), CONST_STR_LEN("100-continue"))) {
932*76404edcSAsim Jamshed con->http_status = 417;
933*76404edcSAsim Jamshed con->keep_alive = 0;
934*76404edcSAsim Jamshed array_insert_unique(con->request.headers, (data_unset *)ds);
935*76404edcSAsim Jamshed return 0;
936*76404edcSAsim Jamshed }
937*76404edcSAsim Jamshed } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) {
938*76404edcSAsim Jamshed if (reqline_host) {
939*76404edcSAsim Jamshed /* ignore all host: headers as we got the host in the request line */
940*76404edcSAsim Jamshed ds->free((data_unset*) ds);
941*76404edcSAsim Jamshed ds = NULL;
942*76404edcSAsim Jamshed } else if (!con->request.http_host) {
943*76404edcSAsim Jamshed con->request.http_host = ds->value;
944*76404edcSAsim Jamshed } else {
945*76404edcSAsim Jamshed con->http_status = 400;
946*76404edcSAsim Jamshed con->keep_alive = 0;
947*76404edcSAsim Jamshed
948*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
949*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s",
950*76404edcSAsim Jamshed "duplicate Host-header -> 400");
951*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
952*76404edcSAsim Jamshed "request-header:\n",
953*76404edcSAsim Jamshed con->request.request);
954*76404edcSAsim Jamshed }
955*76404edcSAsim Jamshed array_insert_unique(con->request.headers, (data_unset *)ds);
956*76404edcSAsim Jamshed return 0;
957*76404edcSAsim Jamshed }
958*76404edcSAsim Jamshed } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) {
959*76404edcSAsim Jamshed /* Proxies sometimes send dup headers
960*76404edcSAsim Jamshed * if they are the same we ignore the second
961*76404edcSAsim Jamshed * if not, we raise an error */
962*76404edcSAsim Jamshed if (!con->request.http_if_modified_since) {
963*76404edcSAsim Jamshed con->request.http_if_modified_since = ds->value->ptr;
964*76404edcSAsim Jamshed } else if (0 == strcasecmp(con->request.http_if_modified_since,
965*76404edcSAsim Jamshed ds->value->ptr)) {
966*76404edcSAsim Jamshed /* ignore it if they are the same */
967*76404edcSAsim Jamshed
968*76404edcSAsim Jamshed ds->free((data_unset *)ds);
969*76404edcSAsim Jamshed ds = NULL;
970*76404edcSAsim Jamshed } else {
971*76404edcSAsim Jamshed con->http_status = 400;
972*76404edcSAsim Jamshed con->keep_alive = 0;
973*76404edcSAsim Jamshed
974*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
975*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s",
976*76404edcSAsim Jamshed "duplicate If-Modified-Since header -> 400");
977*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
978*76404edcSAsim Jamshed "request-header:\n",
979*76404edcSAsim Jamshed con->request.request);
980*76404edcSAsim Jamshed }
981*76404edcSAsim Jamshed array_insert_unique(con->request.headers, (data_unset *)ds);
982*76404edcSAsim Jamshed return 0;
983*76404edcSAsim Jamshed }
984*76404edcSAsim Jamshed } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) {
985*76404edcSAsim Jamshed /* if dup, only the first one will survive */
986*76404edcSAsim Jamshed if (!con->request.http_if_none_match) {
987*76404edcSAsim Jamshed con->request.http_if_none_match = ds->value->ptr;
988*76404edcSAsim Jamshed } else {
989*76404edcSAsim Jamshed ds->free((data_unset*) ds);
990*76404edcSAsim Jamshed ds = NULL;
991*76404edcSAsim Jamshed }
992*76404edcSAsim Jamshed } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) {
993*76404edcSAsim Jamshed if (!con->request.http_range) {
994*76404edcSAsim Jamshed /* bytes=.*-.* */
995*76404edcSAsim Jamshed
996*76404edcSAsim Jamshed if (0 == strncasecmp(ds->value->ptr, "bytes=", 6) &&
997*76404edcSAsim Jamshed NULL != strchr(ds->value->ptr+6, '-')) {
998*76404edcSAsim Jamshed
999*76404edcSAsim Jamshed /* if dup, only the first one will survive */
1000*76404edcSAsim Jamshed con->request.http_range = ds->value->ptr + 6;
1001*76404edcSAsim Jamshed }
1002*76404edcSAsim Jamshed } else {
1003*76404edcSAsim Jamshed con->http_status = 400;
1004*76404edcSAsim Jamshed con->keep_alive = 0;
1005*76404edcSAsim Jamshed
1006*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
1007*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s",
1008*76404edcSAsim Jamshed "duplicate Range-header -> 400");
1009*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
1010*76404edcSAsim Jamshed "request-header:\n",
1011*76404edcSAsim Jamshed con->request.request);
1012*76404edcSAsim Jamshed }
1013*76404edcSAsim Jamshed array_insert_unique(con->request.headers, (data_unset *)ds);
1014*76404edcSAsim Jamshed return 0;
1015*76404edcSAsim Jamshed }
1016*76404edcSAsim Jamshed }
1017*76404edcSAsim Jamshed
1018*76404edcSAsim Jamshed if (ds) array_insert_unique(con->request.headers, (data_unset *)ds);
1019*76404edcSAsim Jamshed } else {
1020*76404edcSAsim Jamshed /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
1021*76404edcSAsim Jamshed }
1022*76404edcSAsim Jamshed }
1023*76404edcSAsim Jamshed
1024*76404edcSAsim Jamshed i++;
1025*76404edcSAsim Jamshed first = i+1;
1026*76404edcSAsim Jamshed is_key = 1;
1027*76404edcSAsim Jamshed value = NULL;
1028*76404edcSAsim Jamshed #if 0
1029*76404edcSAsim Jamshed /**
1030*76404edcSAsim Jamshed * for Bug 1230 keep the key_len a live
1031*76404edcSAsim Jamshed */
1032*76404edcSAsim Jamshed key_len = 0;
1033*76404edcSAsim Jamshed #endif
1034*76404edcSAsim Jamshed in_folding = 0;
1035*76404edcSAsim Jamshed } else {
1036*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
1037*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sbs",
1038*76404edcSAsim Jamshed "CR without LF", con->request.request, "-> 400");
1039*76404edcSAsim Jamshed }
1040*76404edcSAsim Jamshed
1041*76404edcSAsim Jamshed con->http_status = 400;
1042*76404edcSAsim Jamshed con->keep_alive = 0;
1043*76404edcSAsim Jamshed con->response.keep_alive = 0;
1044*76404edcSAsim Jamshed return 0;
1045*76404edcSAsim Jamshed }
1046*76404edcSAsim Jamshed break;
1047*76404edcSAsim Jamshed case ' ':
1048*76404edcSAsim Jamshed case '\t':
1049*76404edcSAsim Jamshed /* strip leading WS */
1050*76404edcSAsim Jamshed if (value == cur) value = cur+1;
1051*76404edcSAsim Jamshed default:
1052*76404edcSAsim Jamshed if (*cur >= 0 && *cur < 32 && *cur != '\t') {
1053*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
1054*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sds",
1055*76404edcSAsim Jamshed "invalid char in header", (int)*cur, "-> 400");
1056*76404edcSAsim Jamshed }
1057*76404edcSAsim Jamshed
1058*76404edcSAsim Jamshed con->http_status = 400;
1059*76404edcSAsim Jamshed con->keep_alive = 0;
1060*76404edcSAsim Jamshed
1061*76404edcSAsim Jamshed return 0;
1062*76404edcSAsim Jamshed }
1063*76404edcSAsim Jamshed break;
1064*76404edcSAsim Jamshed }
1065*76404edcSAsim Jamshed }
1066*76404edcSAsim Jamshed }
1067*76404edcSAsim Jamshed
1068*76404edcSAsim Jamshed con->header_len = i;
1069*76404edcSAsim Jamshed
1070*76404edcSAsim Jamshed /* do some post-processing */
1071*76404edcSAsim Jamshed
1072*76404edcSAsim Jamshed if (con->request.http_version == HTTP_VERSION_1_1) {
1073*76404edcSAsim Jamshed if (keep_alive_set != HTTP_CONNECTION_CLOSE) {
1074*76404edcSAsim Jamshed /* no Connection-Header sent */
1075*76404edcSAsim Jamshed
1076*76404edcSAsim Jamshed /* HTTP/1.1 -> keep-alive default TRUE */
1077*76404edcSAsim Jamshed con->keep_alive = 1;
1078*76404edcSAsim Jamshed } else {
1079*76404edcSAsim Jamshed con->keep_alive = 0;
1080*76404edcSAsim Jamshed }
1081*76404edcSAsim Jamshed
1082*76404edcSAsim Jamshed /* RFC 2616, 14.23 */
1083*76404edcSAsim Jamshed if (con->request.http_host == NULL ||
1084*76404edcSAsim Jamshed buffer_is_empty(con->request.http_host)) {
1085*76404edcSAsim Jamshed con->http_status = 400;
1086*76404edcSAsim Jamshed con->response.keep_alive = 0;
1087*76404edcSAsim Jamshed con->keep_alive = 0;
1088*76404edcSAsim Jamshed
1089*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
1090*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400");
1091*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
1092*76404edcSAsim Jamshed "request-header:\n",
1093*76404edcSAsim Jamshed con->request.request);
1094*76404edcSAsim Jamshed }
1095*76404edcSAsim Jamshed return 0;
1096*76404edcSAsim Jamshed }
1097*76404edcSAsim Jamshed } else {
1098*76404edcSAsim Jamshed if (keep_alive_set == HTTP_CONNECTION_KEEPALIVE) {
1099*76404edcSAsim Jamshed /* no Connection-Header sent */
1100*76404edcSAsim Jamshed
1101*76404edcSAsim Jamshed /* HTTP/1.0 -> keep-alive default FALSE */
1102*76404edcSAsim Jamshed con->keep_alive = 1;
1103*76404edcSAsim Jamshed } else {
1104*76404edcSAsim Jamshed con->keep_alive = 0;
1105*76404edcSAsim Jamshed }
1106*76404edcSAsim Jamshed }
1107*76404edcSAsim Jamshed
1108*76404edcSAsim Jamshed /* check hostname field if it is set */
1109*76404edcSAsim Jamshed if (NULL != con->request.http_host &&
1110*76404edcSAsim Jamshed 0 != request_check_hostname(srv, con, con->request.http_host)) {
1111*76404edcSAsim Jamshed
1112*76404edcSAsim Jamshed if (srv->srvconf.log_request_header_on_error) {
1113*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s",
1114*76404edcSAsim Jamshed "Invalid Hostname -> 400");
1115*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "Sb",
1116*76404edcSAsim Jamshed "request-header:\n",
1117*76404edcSAsim Jamshed con->request.request);
1118*76404edcSAsim Jamshed }
1119*76404edcSAsim Jamshed
1120*76404edcSAsim Jamshed con->http_status = 400;
1121*76404edcSAsim Jamshed con->response.keep_alive = 0;
1122*76404edcSAsim Jamshed con->keep_alive = 0;
1123*76404edcSAsim Jamshed
1124*76404edcSAsim Jamshed return 0;
1125*76404edcSAsim Jamshed }
1126*76404edcSAsim Jamshed
1127*76404edcSAsim Jamshed switch(con->request.http_method) {
1128*76404edcSAsim Jamshed case HTTP_METHOD_GET:
1129*76404edcSAsim Jamshed case HTTP_METHOD_HEAD:
1130*76404edcSAsim Jamshed /* content-length is forbidden for those */
1131*76404edcSAsim Jamshed if (con_length_set && con->request.content_length != 0) {
1132*76404edcSAsim Jamshed /* content-length is missing */
1133*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s",
1134*76404edcSAsim Jamshed "GET/HEAD with content-length -> 400");
1135*76404edcSAsim Jamshed
1136*76404edcSAsim Jamshed con->keep_alive = 0;
1137*76404edcSAsim Jamshed con->http_status = 400;
1138*76404edcSAsim Jamshed return 0;
1139*76404edcSAsim Jamshed }
1140*76404edcSAsim Jamshed break;
1141*76404edcSAsim Jamshed case HTTP_METHOD_POST:
1142*76404edcSAsim Jamshed /* content-length is required for them */
1143*76404edcSAsim Jamshed if (!con_length_set) {
1144*76404edcSAsim Jamshed /* content-length is missing */
1145*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s",
1146*76404edcSAsim Jamshed "POST-request, but content-length missing -> 411");
1147*76404edcSAsim Jamshed
1148*76404edcSAsim Jamshed con->keep_alive = 0;
1149*76404edcSAsim Jamshed con->http_status = 411;
1150*76404edcSAsim Jamshed return 0;
1151*76404edcSAsim Jamshed
1152*76404edcSAsim Jamshed }
1153*76404edcSAsim Jamshed break;
1154*76404edcSAsim Jamshed default:
1155*76404edcSAsim Jamshed /* the may have a content-length */
1156*76404edcSAsim Jamshed break;
1157*76404edcSAsim Jamshed }
1158*76404edcSAsim Jamshed
1159*76404edcSAsim Jamshed
1160*76404edcSAsim Jamshed /* check if we have read post data */
1161*76404edcSAsim Jamshed if (con_length_set) {
1162*76404edcSAsim Jamshed /* don't handle more the SSIZE_MAX bytes in content-length */
1163*76404edcSAsim Jamshed if (con->request.content_length > SSIZE_MAX) {
1164*76404edcSAsim Jamshed con->http_status = 413;
1165*76404edcSAsim Jamshed con->keep_alive = 0;
1166*76404edcSAsim Jamshed
1167*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sos",
1168*76404edcSAsim Jamshed "request-size too long:", (off_t) con->request.content_length, "-> 413");
1169*76404edcSAsim Jamshed return 0;
1170*76404edcSAsim Jamshed }
1171*76404edcSAsim Jamshed
1172*76404edcSAsim Jamshed /* divide by 1024 as srvconf.max_request_size is in kBytes */
1173*76404edcSAsim Jamshed if (srv->srvconf.max_request_size != 0 &&
1174*76404edcSAsim Jamshed (con->request.content_length >> 10) > srv->srvconf.max_request_size) {
1175*76404edcSAsim Jamshed /* the request body itself is larger then
1176*76404edcSAsim Jamshed * our our max_request_size
1177*76404edcSAsim Jamshed */
1178*76404edcSAsim Jamshed
1179*76404edcSAsim Jamshed con->http_status = 413;
1180*76404edcSAsim Jamshed con->keep_alive = 0;
1181*76404edcSAsim Jamshed
1182*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sos",
1183*76404edcSAsim Jamshed "request-size too long:", (off_t) con->request.content_length, "-> 413");
1184*76404edcSAsim Jamshed return 0;
1185*76404edcSAsim Jamshed }
1186*76404edcSAsim Jamshed
1187*76404edcSAsim Jamshed
1188*76404edcSAsim Jamshed /* we have content */
1189*76404edcSAsim Jamshed if (con->request.content_length != 0) {
1190*76404edcSAsim Jamshed return 1;
1191*76404edcSAsim Jamshed }
1192*76404edcSAsim Jamshed }
1193*76404edcSAsim Jamshed
1194*76404edcSAsim Jamshed return 0;
1195*76404edcSAsim Jamshed }
1196*76404edcSAsim Jamshed
http_request_header_finished(server * srv,connection * con)1197*76404edcSAsim Jamshed int http_request_header_finished(server *srv, connection *con) {
1198*76404edcSAsim Jamshed UNUSED(srv);
1199*76404edcSAsim Jamshed
1200*76404edcSAsim Jamshed if (con->request.request->used < 5) return 0;
1201*76404edcSAsim Jamshed
1202*76404edcSAsim Jamshed if (0 == memcmp(con->request.request->ptr + con->request.request->used - 5, "\r\n\r\n", 4)) return 1;
1203*76404edcSAsim Jamshed if (NULL != strstr(con->request.request->ptr, "\r\n\r\n")) return 1;
1204*76404edcSAsim Jamshed
1205*76404edcSAsim Jamshed return 0;
1206*76404edcSAsim Jamshed }
1207