Skip to content

Invalid request is swallowed and times out #3552

@utay

Description

@utay

Describe the bug

It seems like there's a bug in this optimization where after handling a request it tries to read the next request from the socket:
https://github.com/puma/puma/blob/v6.4.3/lib/puma/server.rb#L475-L486.

In this code specifically:

        begin
          if fast_check && @to_io.wait_readable(FAST_TRACK_KA_TIMEOUT)
            return try_to_finish
          end
        rescue IOError
          # swallow it
        end

If the request parsing fails, try_to_finish raises a Puma::HttpParserError which inherits IOError and it causes the invalid request to be swallowed and to time out after 20 seconds.

To Reproduce

Copy this into a file called hello.ru:

run lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Hello World"]] }

Run it with:

bundle exec puma hello.ru

Run this curl command:

curl -v --keepalive-time 30 "http://localhost:9292" --next "http://localhost:9292/$(printf 'a%.0s' {1..9000})"

This will send 2 requests sequentially in the same TCP connection:

  • The first request succeeds with status code 200
  • The second request is invalid - its path is longer than the allowed length (8192) - it hangs for 20 seconds and then the connection dies 💥

Expected behavior

The second request should return 400 immediately with the following error:

HTTP parse error, malformed request ("GET " - (-)): #<Puma::HttpParserError: HTTP element REQUEST_PATH is longer than the (8192) allowed length (was 9001)>

Desktop (please complete the following information):

  • Puma Version 6.4.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions