@@ -458,12 +458,14 @@ function read_http_header(sock, state, keep_alive_timeout, max_body_size)
458458 var error_code = null
459459 var header_size = 0
460460 loop
461- async.read_until(sock, state, "\r\n")
461+ # Check the deadline before starting the read: breaking with a
462+ # just-started async op pending would leave it dangling past shutdown.
462463 var timeout = keep_alive_timeout - runtime.time()
463464 if timeout <= 0
464465 error_code = state_codes.code_408
465466 break
466467 end
468+ async.read_until(sock, state, "\r\n")
467469 if !state.wait_for(timeout)
468470 if state.has_done()
469471 if state.eof()
@@ -524,12 +526,13 @@ function read_http_header(sock, state, keep_alive_timeout, max_body_size)
524526 session.post_data = state.get_buffer(session.content_length)
525527 var remaining = session.content_length - session.post_data.size
526528 while remaining > 0
527- state = async.read(sock, remaining)
529+ # Same as above: check the deadline before starting the read.
528530 var timeout = keep_alive_timeout - runtime.time()
529531 if timeout <= 0
530532 error_code = state_codes.code_408
531533 break
532534 end
535+ state = async.read(sock, remaining)
533536 if !state.wait_for(timeout)
534537 if state.has_done()
535538 if state.eof()
@@ -635,7 +638,11 @@ function call_http_handler(session, server)
635638 if server->url_map.exist(error_code)
636639 server->url_map[error_code](*server, session)
637640 else
638- send_error_response(session.sock, error_code)
641+ # Route through the session so multi-process mode sends a framed
642+ # response to the master instead of raw HTTP bytes on the
643+ # slave<->master socket (which would desynchronize the framing).
644+ session.connection = "close"
645+ session.send_response(error_code, "", "text/html")
639646 end
640647 return false
641648 else
@@ -684,21 +691,27 @@ function simple_worker(self)
684691 end
685692 # Close cleanly once max_keep_alive requests are served: advertise
686693 # Connection: close on the final response
694+ var force_close = false
687695 if ++request_count >= self->server->max_keep_alive
688696 session.connection = "close"
697+ force_close = true
689698 end
690699 # Call handler
691700 session.sock = sock
692- if ! call_http_handler(session, self->server)
693- break
694- end
701+ var handler_ok = call_http_handler(session, self->server)
702+ # Wait for the response write (including error responses sent on
703+ # handler failure) before deciding the connection's fate.
695704 if session.response_state != null && !session.response_state.wait()
696705 log("Write response error: " + session.response_state.get_error())
697706 break
698707 end
708+ if !handler_ok
709+ break
710+ end
699711 last_request_time = runtime.time()
700- # Keep-Alive check
701- if !sock.is_open() || session.connection == "close"
712+ # Keep-Alive check — force_close guards against handlers that
713+ # overwrite session.connection after the limit was reached
714+ if !sock.is_open() || force_close || session.connection == "close"
702715 break
703716 end
704717 end
@@ -714,7 +727,6 @@ struct http_conn
714727 var read_state = null
715728 # -1 = close, 0 = established, 1 = busy
716729 var state = 0
717- var close = false
718730 var keep_alive = true
719731 var request_count = 0
720732 var last_request_time = 0
@@ -853,7 +865,6 @@ function master_request_worker(self)
853865 # Check connection type
854866 if session.connection == "close"
855867 conn->keep_alive = false
856- conn->close = true
857868 end
858869 conn->request_queue.push_back(move(session))
859870 conn->state = 0
@@ -890,21 +901,21 @@ function master_response_worker(self)
890901 if !response_state.wait()
891902 log("Write response error: " + response_state.get_error())
892903 conn->keep_alive = false
893- conn->close = true
894904 conn->request_queue = new array
895905 break
896906 end
897907 conn->request_queue.pop_front()
898908 --conn->request_idx
909+ # Restart the keep-alive idle window only once the response is
910+ # written, matching simple_worker: slave processing time must
911+ # not eat into the client's idle allowance.
912+ conn->last_request_time = runtime.time()
899913 else
900914 fiber.yield()
901915 break
902916 end
903917 end
904918 if conn->request_queue.empty() && !conn->keep_alive
905- if !conn->close
906- send_error_response(conn->sock, state_codes.code_408)
907- end
908919 if !conn->sock.safe_shutdown()
909920 log("safe_shutdown returned false — async jobs may still be pending")
910921 end
0 commit comments