3 #include "sys-socket.h"
5 #include "connections.h"
10 const char *connection_get_state(connection_state_t 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
) {
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";
44 int connection_set_state(server
*srv
, connection
*con
, connection_state_t state
) {
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) */
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
;
66 c
->offset
= offset
+ (off_t
)blen
;
67 chunkqueue_remove_finished_chunks(cq
);
68 if (0 != blen
) return 1;
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 */
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;
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
)) {
95 len
= buffer_string_length(b
) - (size_t)c
->offset
;
96 if (0 == len
) continue;
98 return (p
[0] == '\n') ? 1 : -1; /* error if not '\n' */
103 handler_t
connection_handle_read_post_error(server
*srv
, connection
*con
, int http_status
) {
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
;
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
;
123 off_t len
= cq
->bytes_in
- cq
->bytes_out
;
125 while (0 == te_chunked
) {
127 chunk
*c
= cq
->first
;
128 force_assert(c
->type
== MEM_CHUNK
);
129 p
= strchr(c
->mem
->ptr
+c
->offset
, '\n');
130 if (NULL
!= p
) { /* found HTTP chunked header line */
131 off_t hsz
= p
+ 1 - (c
->mem
->ptr
+c
->offset
);
132 unsigned char *s
= (unsigned char *)c
->mem
->ptr
+c
->offset
;
133 for (unsigned char u
;(u
=(unsigned char)hex2int(*s
))!=0xFF;++s
) {
134 if (te_chunked
> (~((off_t
)-1) >> 4)) {
135 log_error_write(srv
, __FILE__
, __LINE__
, "s",
136 "chunked data size too large -> 400");
137 /* 400 Bad Request */
138 return connection_handle_read_post_error(srv
, con
, 400);
143 while (*s
== ' ' || *s
== '\t') ++s
;
144 if (*s
!= '\r' && *s
!= ';') {
145 log_error_write(srv
, __FILE__
, __LINE__
, "s",
146 "chunked header invalid chars -> 400");
147 /* 400 Bad Request */
148 return connection_handle_read_post_error(srv
, con
, 400);
152 /* prevent theoretical integer overflow
153 * casting to (size_t) and adding 2 (for "\r\n") */
154 log_error_write(srv
, __FILE__
, __LINE__
, "s",
155 "chunked header line too long -> 400");
156 /* 400 Bad Request */
157 return connection_handle_read_post_error(srv
, con
, 400);
160 if (0 == te_chunked
) {
161 /* do not consume final chunked header until
162 * (optional) trailers received along with
163 * request-ending blank line "\r\n" */
164 if (p
[0] == '\r' && p
[1] == '\n') {
165 /*(common case with no trailers; final \r\n received)*/
169 /* trailers or final CRLF crosses into next cq chunk */
173 p
= strstr(c
->mem
->ptr
+c
->offset
+hsz
, "\r\n\r\n");
175 && connection_handle_read_post_cq_compact(cq
));
177 /*(effectively doubles max request field size
178 * potentially received by backend, if in the future
179 * these trailers are added to request headers)*/
180 if ((off_t
)buffer_string_length(c
->mem
) - c
->offset
181 < srv
->srvconf
.max_request_field_size
) {
185 /* ignore excessively long trailers;
186 * disable keep-alive on connection */
190 hsz
= p
+ 4 - (c
->mem
->ptr
+c
->offset
);
191 /* trailers currently ignored, but could be processed
192 * here if 0 == con->conf.stream_request_body, taking
193 * care to reject any fields forbidden in trailers,
194 * making trailers available to CGI and other backends*/
196 chunkqueue_mark_written(cq
, (size_t)hsz
);
197 con
->request
.content_length
= dst_cq
->bytes_in
;
198 break; /* done reading HTTP chunked request body */
201 /* consume HTTP chunked header */
202 chunkqueue_mark_written(cq
, (size_t)hsz
);
203 len
= cq
->bytes_in
- cq
->bytes_out
;
205 if (0 !=max_request_size
206 && (max_request_size
< te_chunked
207 || max_request_size
- te_chunked
< dst_cq
->bytes_in
)) {
208 log_error_write(srv
, __FILE__
, __LINE__
, "sos",
209 "request-size too long:",
210 dst_cq
->bytes_in
+ te_chunked
, "-> 413");
211 /* 413 Payload Too Large */
212 return connection_handle_read_post_error(srv
, con
, 413);
215 te_chunked
+= 2; /*(for trailing "\r\n" after chunked data)*/
217 break; /* read HTTP chunked header */
220 /*(likely better ways to handle chunked header crossing chunkqueue
221 * chunks, but this situation is not expected to occur frequently)*/
222 if ((off_t
)buffer_string_length(c
->mem
) - c
->offset
>= 1024) {
223 log_error_write(srv
, __FILE__
, __LINE__
, "s",
224 "chunked header line too long -> 400");
225 /* 400 Bad Request */
226 return connection_handle_read_post_error(srv
, con
, 400);
228 else if (!connection_handle_read_post_cq_compact(cq
)) {
232 if (0 == te_chunked
) break;
234 if (te_chunked
> 2) {
235 if (len
> te_chunked
-2) len
= te_chunked
-2;
236 if (dst_cq
->bytes_in
+ te_chunked
<= 64*1024) {
237 /* avoid buffering request bodies <= 64k on disk */
238 chunkqueue_steal(dst_cq
, cq
, len
);
240 else if (0 != chunkqueue_steal_with_tempfiles(srv
,dst_cq
,cq
,len
)) {
241 /* 500 Internal Server Error */
242 return connection_handle_read_post_error(srv
, con
, 500);
245 len
= cq
->bytes_in
- cq
->bytes_out
;
248 if (len
< te_chunked
) break;
250 if (2 == te_chunked
) {
251 if (-1 == connection_handle_read_post_chunked_crlf(cq
)) {
252 log_error_write(srv
, __FILE__
, __LINE__
, "s",
253 "chunked data missing end CRLF -> 400");
254 /* 400 Bad Request */
255 return connection_handle_read_post_error(srv
, con
, 400);
257 chunkqueue_mark_written(cq
, 2);/*consume \r\n at end of chunk data*/
261 } while (!chunkqueue_is_empty(cq
));
263 con
->request
.te_chunked
= te_chunked
;
264 return HANDLER_GO_ON
;
267 static handler_t
connection_handle_read_body_unknown(server
*srv
, connection
*con
, chunkqueue
*cq
, chunkqueue
*dst_cq
) {
268 /* con->conf.max_request_size is in kBytes */
269 const off_t max_request_size
= (off_t
)con
->conf
.max_request_size
<< 10;
270 chunkqueue_append_chunkqueue(dst_cq
, cq
);
271 if (0 != max_request_size
&& dst_cq
->bytes_in
> max_request_size
) {
272 log_error_write(srv
, __FILE__
, __LINE__
, "sos",
273 "request-size too long:", dst_cq
->bytes_in
, "-> 413");
274 /* 413 Payload Too Large */
275 return connection_handle_read_post_error(srv
, con
, 413);
277 return HANDLER_GO_ON
;
280 static off_t
connection_write_throttle(server
*srv
, connection
*con
, off_t max_bytes
) {
282 if (con
->conf
.global_kbytes_per_second
) {
283 off_t limit
= con
->conf
.global_kbytes_per_second
* 1024 - *(con
->conf
.global_bytes_per_second_cnt_ptr
);
285 /* we reached the global traffic limit */
286 con
->traffic_limit_reached
= 1;
290 if (max_bytes
> limit
) max_bytes
= limit
;
294 if (con
->conf
.kbytes_per_second
) {
295 off_t limit
= con
->conf
.kbytes_per_second
* 1024 - con
->bytes_written_cur_second
;
297 /* we reached the traffic limit */
298 con
->traffic_limit_reached
= 1;
302 if (max_bytes
> limit
) max_bytes
= limit
;
309 int connection_write_chunkqueue(server
*srv
, connection
*con
, chunkqueue
*cq
, off_t max_bytes
) {
316 max_bytes
= connection_write_throttle(srv
, con
, max_bytes
);
317 if (0 == max_bytes
) return 1;
319 written
= cq
->bytes_out
;
322 /* Linux: put a cork into the socket as we want to combine the write() calls
323 * but only if we really have multiple chunks, and only if TCP socket
325 if (cq
->first
&& cq
->first
->next
) {
326 const int sa_family
= con
->srv_socket
->addr
.plain
.sa_family
;
327 if (sa_family
== AF_INET
|| sa_family
== AF_INET6
) {
329 (void)setsockopt(con
->fd
, IPPROTO_TCP
, TCP_CORK
, &corked
, sizeof(corked
));
334 ret
= con
->network_write(srv
, con
, cq
, max_bytes
);
336 chunkqueue_remove_finished_chunks(cq
);
337 ret
= chunkqueue_is_empty(cq
) ? 0 : 1;
343 (void)setsockopt(con
->fd
, IPPROTO_TCP
, TCP_CORK
, &corked
, sizeof(corked
));
347 written
= cq
->bytes_out
- written
;
348 con
->bytes_written
+= written
;
349 con
->bytes_written_cur_second
+= written
;
350 *(con
->conf
.global_bytes_per_second_cnt_ptr
) += written
;
355 static int connection_write_100_continue(server
*srv
, connection
*con
) {
356 /* Make best effort to send all or none of "HTTP/1.1 100 Continue" */
357 /* (Note: also choosing not to update con->write_request_ts
358 * which differs from connections.c:connection_handle_write()) */
359 static const char http_100_continue
[] = "HTTP/1.1 100 Continue\r\n\r\n";
365 connection_write_throttle(srv
, con
, sizeof(http_100_continue
)-1);
366 if (max_bytes
< (off_t
)sizeof(http_100_continue
)-1) {
367 return 1; /* success; skip sending if throttled to partial */
370 cq
= con
->write_queue
;
371 written
= cq
->bytes_out
;
373 chunkqueue_append_mem(cq
,http_100_continue
,sizeof(http_100_continue
)-1);
374 rc
= con
->network_write(srv
, con
, cq
, sizeof(http_100_continue
)-1);
376 written
= cq
->bytes_out
- written
;
377 con
->bytes_written
+= written
;
378 con
->bytes_written_cur_second
+= written
;
379 *(con
->conf
.global_bytes_per_second_cnt_ptr
) += written
;
382 connection_set_state(srv
, con
, CON_STATE_ERROR
);
383 return 0; /* error */
386 if (written
== sizeof(http_100_continue
)-1) {
387 chunkqueue_remove_finished_chunks(cq
);
388 } else if (0 == written
) {
389 /* skip sending 100 Continue if send would block */
390 chunkqueue_mark_written(cq
, sizeof(http_100_continue
)-1);
391 con
->is_writable
= 0;
393 /* else partial write (unlikely), which can cause corrupt
394 * response if response is later cleared, e.g. sending errdoc.
395 * However, situation of partial write can occur here only on
396 * keep-alive request where client has sent pipelined request,
397 * and more than 0 chars were written, but fewer than 25 chars */
399 return 1; /* success; sent all or none of "HTTP/1.1 100 Continue" */
402 handler_t
connection_handle_read_post_state(server
*srv
, connection
*con
) {
403 chunkqueue
*cq
= con
->read_queue
;
404 chunkqueue
*dst_cq
= con
->request_content_queue
;
408 if (con
->is_readable
) {
409 con
->read_idle_ts
= srv
->cur_ts
;
411 switch(con
->network_read(srv
, con
, con
->read_queue
, MAX_READ_LIMIT
)) {
413 connection_set_state(srv
, con
, CON_STATE_ERROR
);
414 return HANDLER_ERROR
;
423 chunkqueue_remove_finished_chunks(cq
);
425 /* Check for Expect: 100-continue in request headers
426 * if no request body received yet */
427 if (chunkqueue_is_empty(cq
) && 0 == dst_cq
->bytes_in
428 && con
->request
.http_version
!= HTTP_VERSION_1_0
429 && chunkqueue_is_empty(con
->write_queue
) && con
->is_writable
) {
430 data_string
*ds
= (data_string
*)array_get_element(con
->request
.headers
, "Expect");
431 if (NULL
!= ds
&& 0 == buffer_caseless_compare(CONST_BUF_LEN(ds
->value
), CONST_STR_LEN("100-continue"))) {
432 buffer_reset(ds
->value
); /* unset value in request headers */
433 if (!connection_write_100_continue(srv
, con
)) {
434 return HANDLER_ERROR
;
439 if (con
->request
.content_length
< 0) {
440 /*(-1: Transfer-Encoding: chunked, -2: unspecified length)*/
441 handler_t rc
= (-1 == con
->request
.content_length
)
442 ? connection_handle_read_post_chunked(srv
, con
, cq
, dst_cq
)
443 : connection_handle_read_body_unknown(srv
, con
, cq
, dst_cq
);
444 if (HANDLER_GO_ON
!= rc
) return rc
;
446 else if (con
->request
.content_length
<= 64*1024) {
447 /* don't buffer request bodies <= 64k on disk */
448 chunkqueue_steal(dst_cq
, cq
, (off_t
)con
->request
.content_length
- dst_cq
->bytes_in
);
450 else if (0 != chunkqueue_steal_with_tempfiles(srv
, dst_cq
, cq
, (off_t
)con
->request
.content_length
- dst_cq
->bytes_in
)) {
451 /* writing to temp file failed */
452 return connection_handle_read_post_error(srv
, con
, 500); /* Internal Server Error */
455 chunkqueue_remove_finished_chunks(cq
);
457 if (dst_cq
->bytes_in
== (off_t
)con
->request
.content_length
) {
458 /* Content is ready */
459 con
->conf
.stream_request_body
&= ~FDEVENT_STREAM_REQUEST_POLLIN
;
460 if (con
->state
== CON_STATE_READ_POST
) {
461 connection_set_state(srv
, con
, CON_STATE_HANDLE_REQUEST
);
463 return HANDLER_GO_ON
;
464 } else if (is_closed
) {
466 return connection_handle_read_post_error(srv
, con
, 400); /* Bad Request */
468 return HANDLER_ERROR
;
470 con
->conf
.stream_request_body
|= FDEVENT_STREAM_REQUEST_POLLIN
;
471 return (con
->conf
.stream_request_body
& FDEVENT_STREAM_REQUEST
)
473 : HANDLER_WAIT_FOR_EVENT
;
477 void connection_response_reset(server
*srv
, connection
*con
) {
481 con
->http_status
= 0;
482 con
->is_writable
= 1;
483 con
->file_finished
= 0;
484 con
->file_started
= 0;
485 con
->parsed_response
= 0;
486 con
->response
.keep_alive
= 0;
487 con
->response
.content_length
= -1;
488 con
->response
.transfer_encoding
= 0;
489 if (con
->physical
.path
) { /*(skip for mod_fastcgi authorizer)*/
490 buffer_reset(con
->physical
.doc_root
);
491 buffer_reset(con
->physical
.path
);
492 buffer_reset(con
->physical
.basedir
);
493 buffer_reset(con
->physical
.rel_path
);
494 buffer_reset(con
->physical
.etag
);
496 array_reset(con
->response
.headers
);
497 chunkqueue_reset(con
->write_queue
);