Skip to content

persistent connection#3

Open
Piyush-Goenka wants to merge 1 commit intolow-rb:mainfrom
Piyush-Goenka:persistent-connection
Open

persistent connection#3
Piyush-Goenka wants to merge 1 commit intolow-rb:mainfrom
Piyush-Goenka:persistent-connection

Conversation

@Piyush-Goenka
Copy link
Copy Markdown

Summary

  • Add Content-Length and Connection header negotiation as prerequisites for connection reuse.
  • Add configurable idle timeout (default 30s) and per-request parsing timeout (default 10s) to protect against slow/malicious clients.

Changes

request_parser.rb

  • Added create_stream(socket) so the IO stream is created once per connection and reused across requests.
  • parse now accepts stream: and optional version: (cached after the first request) instead of creating a new stream from socket: each time.
  • Returns nil on EOF instead of raising, letting the connection loop exit cleanly.
  • parse_body reads exactly Content-Length bytes instead of stream.read (which would block forever on a persistent connection).
  • Guards against nil from stream.gets during header parsing (client disconnect mid-request).

response_builder.rb

  • Adds Content-Length header (file size for file bodies, bytesize for buffered bodies).
  • Adds Connection: keep-alive or Connection: close based on negotiation.
  • Removed socket.close — connection lifecycle is now managed by LowLoop#handle_connection.
  • Uses socket.write for the body instead of socket.puts (avoids trailing newline that would break Content-Length).
  • Filters Content-Length and Connection from the response headers loop so we don't emit duplicates when a handler sets them.

low_loop.rb

  • Extracted handle_connection(socket) that owns the full connection lifecycle: create stream → loop (wait → parse → process → respond → check keep-alive) → close.
  • keep_alive? negotiates per RFC 9112: HTTP/1.1 defaults keep-alive, HTTP/1.0 defaults close. Handles multi-token Connection headers (e.g. Connection: Upgrade, close) by splitting on commas.
  • socket.wait_readable(keep_alive_timeout) for idle timeout (Fiber-scheduler-aware, unlike IO.select).
  • socket.timeout = request_timeout during parsing to bound each read — prevents slowloris-style attacks where a client drip-feeds bytes to hold the connection open. IO::TimeoutError exits the loop cleanly.
  • Configurable via config.keep_alive_timeout (default 30s) and config.request_timeout (default 10s).

response_factory.rb

  • Fixed response version string from http/1.1 to HTTP/1.1 so clients correctly identify the protocol (curl was falling back to HTTP/1.0 parsing otherwise).

spec/units/low_loop_spec.rb

  • Bumped concurrent-request timing threshold from 1.1s to 1.2s to account for the small keep-alive connection lifecycle overhead (one extra loop iteration per connection to detect client disconnect).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant