[autobuild] allow sendfile() in cross-compile (fixes #2836)
[lighttpd.git] / src / connections-glue.c
blob7c6309b7e0a9f4c341fc1ed48118115f0675c6ac
1 #include "first.h"
3 #include "sys-socket.h"
4 #include "base.h"
5 #include "connections.h"
6 #include "log.h"
8 #include <errno.h>
10 const char *connection_get_state(connection_state_t state) {
11 switch (state) {
12 case CON_STATE_CONNECT: return "connect";
13 case CON_STATE_READ: return "read";
14 case CON_STATE_READ_POST: return "readpost";
15 case CON_STATE_WRITE: return "write";
16 case CON_STATE_CLOSE: return "close";
17 case CON_STATE_ERROR: return "error";
18 case CON_STATE_HANDLE_REQUEST: return "handle-req";
19 case CON_STATE_REQUEST_START: return "req-start";
20 case CON_STATE_REQUEST_END: return "req-end";
21 case CON_STATE_RESPONSE_START: return "resp-start";
22 case CON_STATE_RESPONSE_END: return "resp-end";
23 default: return "(unknown)";
27 const char *connection_get_short_state(connection_state_t state) {
28 switch (state) {
29 case CON_STATE_CONNECT: return ".";
30 case CON_STATE_READ: return "r";
31 case CON_STATE_READ_POST: return "R";
32 case CON_STATE_WRITE: return "W";
33 case CON_STATE_CLOSE: return "C";
34 case CON_STATE_ERROR: return "E";
35 case CON_STATE_HANDLE_REQUEST: return "h";
36 case CON_STATE_REQUEST_START: return "q";
37 case CON_STATE_REQUEST_END: return "Q";
38 case CON_STATE_RESPONSE_START: return "s";
39 case CON_STATE_RESPONSE_END: return "S";
40 default: return "x";
44 int connection_set_state(server *srv, connection *con, connection_state_t state) {
45 UNUSED(srv);
47 con->state = state;
49 return 0;
52 static int connection_handle_read_post_cq_compact(chunkqueue *cq) {
53 /* combine first mem chunk with next non-empty mem chunk
54 * (loop if next chunk is empty) */
55 chunk *c;
56 while (NULL != (c = cq->first) && NULL != c->next) {
57 buffer *mem = c->next->mem;
58 off_t offset = c->next->offset;
59 size_t blen = buffer_string_length(mem) - (size_t)offset;
60 force_assert(c->type == MEM_CHUNK);
61 force_assert(c->next->type == MEM_CHUNK);
62 buffer_append_string_len(c->mem, mem->ptr+offset, blen);
63 c->next->offset = c->offset;
64 c->next->mem = c->mem;
65 c->mem = mem;
66 c->offset = offset + (off_t)blen;
67 chunkqueue_remove_finished_chunks(cq);
68 if (0 != blen) return 1;
70 return 0;
73 static int connection_handle_read_post_chunked_crlf(chunkqueue *cq) {
74 /* caller might check chunkqueue_length(cq) >= 2 before calling here
75 * to limit return value to either 1 for good or -1 for error */
76 chunk *c;
77 buffer *b;
78 char *p;
79 size_t len;
81 /* caller must have called chunkqueue_remove_finished_chunks(cq), so if
82 * chunkqueue is not empty, it contains chunk with at least one char */
83 if (chunkqueue_is_empty(cq)) return 0;
85 c = cq->first;
86 b = c->mem;
87 p = b->ptr+c->offset;
88 if (p[0] != '\r') return -1; /* error */
89 if (p[1] == '\n') return 1;
90 len = buffer_string_length(b) - (size_t)c->offset;
91 if (1 != len) return -1; /* error */
93 while (NULL != (c = c->next)) {
94 b = c->mem;
95 len = buffer_string_length(b) - (size_t)c->offset;
96 if (0 == len) continue;
97 p = b->ptr+c->offset;
98 return (p[0] == '\n') ? 1 : -1; /* error if not '\n' */
100 return 0;
103 handler_t connection_handle_read_post_error(server *srv, connection *con, int http_status) {
104 UNUSED(srv);
106 con->keep_alive = 0;
108 /*(do not change status if response headers already set and possibly sent)*/
109 if (0 != con->bytes_header) return HANDLER_ERROR;
111 con->http_status = http_status;
112 con->mode = DIRECT;
113 chunkqueue_reset(con->write_queue);
114 return HANDLER_FINISHED;
117 static handler_t connection_handle_read_post_chunked(server *srv, connection *con, chunkqueue *cq, chunkqueue *dst_cq) {
119 /* con->conf.max_request_size is in kBytes */
120 const off_t max_request_size = (off_t)con->conf.max_request_size << 10;
121 off_t te_chunked = con->request.te_chunked;
122 do {
123 off_t len = cq->bytes_in - cq->bytes_out;
125 while (0 == te_chunked) {
126 char *p;
127 chunk *c = cq->first;
128 if (NULL == c) break;
129 force_assert(c->type == MEM_CHUNK);
130 p = strchr(c->mem->ptr+c->offset, '\n');
131 if (NULL != p) { /* found HTTP chunked header line */
132 off_t hsz = p + 1 - (c->mem->ptr+c->offset);
133 unsigned char *s = (unsigned char *)c->mem->ptr+c->offset;
134 for (unsigned char u;(u=(unsigned char)hex2int(*s))!=0xFF;++s) {
135 if (te_chunked > (~((off_t)-1) >> 4)) {
136 log_error_write(srv, __FILE__, __LINE__, "s",
137 "chunked data size too large -> 400");
138 /* 400 Bad Request */
139 return connection_handle_read_post_error(srv, con, 400);
141 te_chunked <<= 4;
142 te_chunked |= u;
144 while (*s == ' ' || *s == '\t') ++s;
145 if (*s != '\r' && *s != ';') {
146 log_error_write(srv, __FILE__, __LINE__, "s",
147 "chunked header invalid chars -> 400");
148 /* 400 Bad Request */
149 return connection_handle_read_post_error(srv, con, 400);
152 if (hsz >= 1024) {
153 /* prevent theoretical integer overflow
154 * casting to (size_t) and adding 2 (for "\r\n") */
155 log_error_write(srv, __FILE__, __LINE__, "s",
156 "chunked header line too long -> 400");
157 /* 400 Bad Request */
158 return connection_handle_read_post_error(srv, con, 400);
161 if (0 == te_chunked) {
162 /* do not consume final chunked header until
163 * (optional) trailers received along with
164 * request-ending blank line "\r\n" */
165 if (p[0] == '\r' && p[1] == '\n') {
166 /*(common case with no trailers; final \r\n received)*/
167 hsz += 2;
169 else {
170 /* trailers or final CRLF crosses into next cq chunk */
171 hsz -= 2;
172 do {
173 c = cq->first;
174 p = strstr(c->mem->ptr+c->offset+hsz, "\r\n\r\n");
175 } while (NULL == p
176 && connection_handle_read_post_cq_compact(cq));
177 if (NULL == p) {
178 /*(effectively doubles max request field size
179 * potentially received by backend, if in the future
180 * these trailers are added to request headers)*/
181 if ((off_t)buffer_string_length(c->mem) - c->offset
182 < srv->srvconf.max_request_field_size) {
183 break;
185 else {
186 /* ignore excessively long trailers;
187 * disable keep-alive on connection */
188 con->keep_alive = 0;
191 hsz = p + 4 - (c->mem->ptr+c->offset);
192 /* trailers currently ignored, but could be processed
193 * here if 0 == con->conf.stream_request_body, taking
194 * care to reject any fields forbidden in trailers,
195 * making trailers available to CGI and other backends*/
197 chunkqueue_mark_written(cq, (size_t)hsz);
198 con->request.content_length = dst_cq->bytes_in;
199 break; /* done reading HTTP chunked request body */
202 /* consume HTTP chunked header */
203 chunkqueue_mark_written(cq, (size_t)hsz);
204 len = cq->bytes_in - cq->bytes_out;
206 if (0 !=max_request_size
207 && (max_request_size < te_chunked
208 || max_request_size - te_chunked < dst_cq->bytes_in)) {
209 log_error_write(srv, __FILE__, __LINE__, "sos",
210 "request-size too long:",
211 dst_cq->bytes_in + te_chunked, "-> 413");
212 /* 413 Payload Too Large */
213 return connection_handle_read_post_error(srv, con, 413);
216 te_chunked += 2; /*(for trailing "\r\n" after chunked data)*/
218 break; /* read HTTP chunked header */
221 /*(likely better ways to handle chunked header crossing chunkqueue
222 * chunks, but this situation is not expected to occur frequently)*/
223 if ((off_t)buffer_string_length(c->mem) - c->offset >= 1024) {
224 log_error_write(srv, __FILE__, __LINE__, "s",
225 "chunked header line too long -> 400");
226 /* 400 Bad Request */
227 return connection_handle_read_post_error(srv, con, 400);
229 else if (!connection_handle_read_post_cq_compact(cq)) {
230 break;
233 if (0 == te_chunked) break;
235 if (te_chunked > 2) {
236 if (len > te_chunked-2) len = te_chunked-2;
237 if (dst_cq->bytes_in + te_chunked <= 64*1024) {
238 /* avoid buffering request bodies <= 64k on disk */
239 chunkqueue_steal(dst_cq, cq, len);
241 else if (0 != chunkqueue_steal_with_tempfiles(srv,dst_cq,cq,len)) {
242 /* 500 Internal Server Error */
243 return connection_handle_read_post_error(srv, con, 500);
245 te_chunked -= len;
246 len = cq->bytes_in - cq->bytes_out;
249 if (len < te_chunked) break;
251 if (2 == te_chunked) {
252 if (-1 == connection_handle_read_post_chunked_crlf(cq)) {
253 log_error_write(srv, __FILE__, __LINE__, "s",
254 "chunked data missing end CRLF -> 400");
255 /* 400 Bad Request */
256 return connection_handle_read_post_error(srv, con, 400);
258 chunkqueue_mark_written(cq, 2);/*consume \r\n at end of chunk data*/
259 te_chunked -= 2;
262 } while (!chunkqueue_is_empty(cq));
264 con->request.te_chunked = te_chunked;
265 return HANDLER_GO_ON;
268 static handler_t connection_handle_read_body_unknown(server *srv, connection *con, chunkqueue *cq, chunkqueue *dst_cq) {
269 /* con->conf.max_request_size is in kBytes */
270 const off_t max_request_size = (off_t)con->conf.max_request_size << 10;
271 chunkqueue_append_chunkqueue(dst_cq, cq);
272 if (0 != max_request_size && dst_cq->bytes_in > max_request_size) {
273 log_error_write(srv, __FILE__, __LINE__, "sos",
274 "request-size too long:", dst_cq->bytes_in, "-> 413");
275 /* 413 Payload Too Large */
276 return connection_handle_read_post_error(srv, con, 413);
278 return HANDLER_GO_ON;
281 static off_t connection_write_throttle(server *srv, connection *con, off_t max_bytes) {
282 UNUSED(srv);
283 if (con->conf.global_kbytes_per_second) {
284 off_t limit = con->conf.global_kbytes_per_second * 1024 - *(con->conf.global_bytes_per_second_cnt_ptr);
285 if (limit <= 0) {
286 /* we reached the global traffic limit */
287 con->traffic_limit_reached = 1;
289 return 0;
290 } else {
291 if (max_bytes > limit) max_bytes = limit;
295 if (con->conf.kbytes_per_second) {
296 off_t limit = con->conf.kbytes_per_second * 1024 - con->bytes_written_cur_second;
297 if (limit <= 0) {
298 /* we reached the traffic limit */
299 con->traffic_limit_reached = 1;
301 return 0;
302 } else {
303 if (max_bytes > limit) max_bytes = limit;
307 return max_bytes;
310 int connection_write_chunkqueue(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) {
311 int ret = -1;
312 off_t written = 0;
313 #ifdef TCP_CORK
314 int corked = 0;
315 #endif
317 max_bytes = connection_write_throttle(srv, con, max_bytes);
318 if (0 == max_bytes) return 1;
320 written = cq->bytes_out;
322 #ifdef TCP_CORK
323 /* Linux: put a cork into socket as we want to combine write() calls
324 * but only if we really have multiple chunks including non-MEM_CHUNK,
325 * and only if TCP socket
327 if (cq->first && cq->first->next) {
328 const int sa_family = sock_addr_get_family(&con->srv_socket->addr);
329 if (sa_family == AF_INET || sa_family == AF_INET6) {
330 chunk *c = cq->first;
331 while (c->type == MEM_CHUNK && NULL != (c = c->next)) ;
332 if (NULL != c) {
333 corked = 1;
334 (void)setsockopt(con->fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
338 #endif
340 ret = con->network_write(srv, con, cq, max_bytes);
341 if (ret >= 0) {
342 chunkqueue_remove_finished_chunks(cq);
343 ret = chunkqueue_is_empty(cq) ? 0 : 1;
346 #ifdef TCP_CORK
347 if (corked) {
348 corked = 0;
349 (void)setsockopt(con->fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
351 #endif
353 written = cq->bytes_out - written;
354 con->bytes_written += written;
355 con->bytes_written_cur_second += written;
356 *(con->conf.global_bytes_per_second_cnt_ptr) += written;
358 return ret;
361 static int connection_write_100_continue(server *srv, connection *con) {
362 /* Make best effort to send all or none of "HTTP/1.1 100 Continue" */
363 /* (Note: also choosing not to update con->write_request_ts
364 * which differs from connections.c:connection_handle_write()) */
365 static const char http_100_continue[] = "HTTP/1.1 100 Continue\r\n\r\n";
366 chunkqueue *cq;
367 off_t written;
368 int rc;
370 off_t max_bytes =
371 connection_write_throttle(srv, con, sizeof(http_100_continue)-1);
372 if (max_bytes < (off_t)sizeof(http_100_continue)-1) {
373 return 1; /* success; skip sending if throttled to partial */
376 cq = con->write_queue;
377 written = cq->bytes_out;
379 chunkqueue_append_mem(cq,http_100_continue,sizeof(http_100_continue)-1);
380 rc = con->network_write(srv, con, cq, sizeof(http_100_continue)-1);
382 written = cq->bytes_out - written;
383 con->bytes_written += written;
384 con->bytes_written_cur_second += written;
385 *(con->conf.global_bytes_per_second_cnt_ptr) += written;
387 if (rc < 0) {
388 connection_set_state(srv, con, CON_STATE_ERROR);
389 return 0; /* error */
392 if (written == sizeof(http_100_continue)-1) {
393 chunkqueue_remove_finished_chunks(cq);
394 } else if (0 == written) {
395 /* skip sending 100 Continue if send would block */
396 chunkqueue_mark_written(cq, sizeof(http_100_continue)-1);
397 con->is_writable = 0;
399 /* else partial write (unlikely), which can cause corrupt
400 * response if response is later cleared, e.g. sending errdoc.
401 * However, situation of partial write can occur here only on
402 * keep-alive request where client has sent pipelined request,
403 * and more than 0 chars were written, but fewer than 25 chars */
405 return 1; /* success; sent all or none of "HTTP/1.1 100 Continue" */
408 handler_t connection_handle_read_post_state(server *srv, connection *con) {
409 chunkqueue *cq = con->read_queue;
410 chunkqueue *dst_cq = con->request_content_queue;
412 int is_closed = 0;
414 if (con->is_readable) {
415 con->read_idle_ts = srv->cur_ts;
417 switch(con->network_read(srv, con, con->read_queue, MAX_READ_LIMIT)) {
418 case -1:
419 connection_set_state(srv, con, CON_STATE_ERROR);
420 return HANDLER_ERROR;
421 case -2:
422 is_closed = 1;
423 break;
424 default:
425 break;
429 chunkqueue_remove_finished_chunks(cq);
431 /* Check for Expect: 100-continue in request headers
432 * if no request body received yet */
433 if (chunkqueue_is_empty(cq) && 0 == dst_cq->bytes_in
434 && con->request.http_version != HTTP_VERSION_1_0
435 && chunkqueue_is_empty(con->write_queue) && con->is_writable) {
436 data_string *ds = (data_string *)array_get_element(con->request.headers, "Expect");
437 if (NULL != ds && 0 == buffer_caseless_compare(CONST_BUF_LEN(ds->value), CONST_STR_LEN("100-continue"))) {
438 buffer_reset(ds->value); /* unset value in request headers */
439 if (!connection_write_100_continue(srv, con)) {
440 return HANDLER_ERROR;
445 if (con->request.content_length < 0) {
446 /*(-1: Transfer-Encoding: chunked, -2: unspecified length)*/
447 handler_t rc = (-1 == con->request.content_length)
448 ? connection_handle_read_post_chunked(srv, con, cq, dst_cq)
449 : connection_handle_read_body_unknown(srv, con, cq, dst_cq);
450 if (HANDLER_GO_ON != rc) return rc;
452 else if (con->request.content_length <= 64*1024) {
453 /* don't buffer request bodies <= 64k on disk */
454 chunkqueue_steal(dst_cq, cq, (off_t)con->request.content_length - dst_cq->bytes_in);
456 else if (0 != chunkqueue_steal_with_tempfiles(srv, dst_cq, cq, (off_t)con->request.content_length - dst_cq->bytes_in)) {
457 /* writing to temp file failed */
458 return connection_handle_read_post_error(srv, con, 500); /* Internal Server Error */
461 chunkqueue_remove_finished_chunks(cq);
463 if (dst_cq->bytes_in == (off_t)con->request.content_length) {
464 /* Content is ready */
465 con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
466 if (con->state == CON_STATE_READ_POST) {
467 connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
469 return HANDLER_GO_ON;
470 } else if (is_closed) {
471 #if 0
472 return connection_handle_read_post_error(srv, con, 400); /* Bad Request */
473 #endif
474 return HANDLER_ERROR;
475 } else {
476 con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
477 return (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)
478 ? HANDLER_GO_ON
479 : HANDLER_WAIT_FOR_EVENT;
483 void connection_response_reset(server *srv, connection *con) {
484 UNUSED(srv);
486 con->mode = DIRECT;
487 con->http_status = 0;
488 con->is_writable = 1;
489 con->file_finished = 0;
490 con->file_started = 0;
491 con->parsed_response = 0;
492 con->response.keep_alive = 0;
493 con->response.content_length = -1;
494 con->response.transfer_encoding = 0;
495 if (con->physical.path) { /*(skip for mod_fastcgi authorizer)*/
496 buffer_reset(con->physical.doc_root);
497 buffer_reset(con->physical.path);
498 buffer_reset(con->physical.basedir);
499 buffer_reset(con->physical.rel_path);
500 buffer_reset(con->physical.etag);
502 array_reset(con->response.headers);
503 chunkqueue_reset(con->write_queue);