Class: Stipa::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/stipa/request.rb

Overview

Parses a raw HTTP/1.1 header block (already read by Connection) and reads the body from the socket using Content-Length.

Design notes:

- Headers are stored with lower-cased keys for O(1) case-insensitive
  lookup (RFC 7230 ยง3.2: header names are case-insensitive).
- Body is read with IO.select + read_nonblock rather than SO_RCVTIMEO
  because SO_RCVTIMEO behaves inconsistently across Ruby versions and
  platforms. IO.select releases the GVL while waiting.
- `id` and `params` are writable: RequestId middleware sets `id`,
  the router sets `params` after matching.
- We reject header folding (obsolete since RFC 7230) with a 400.
- Chunked Transfer-Encoding is not supported in this version.

Constant Summary collapse

MAX_HEADER_SIZE =

8 KB โ€” slow-loris defence

8 * 1024
MAX_BODY_SIZE =

1 MB default; configurable per-server

1 * 1024 * 1024
VALID_METHODS =
%w[GET POST PUT PATCH DELETE HEAD OPTIONS TRACE CONNECT].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(raw_headers, socket:, peer:, body_timeout:, socket_buffer: '', config: {}) ⇒ Request

Returns a new instance of Request.



45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/stipa/request.rb', line 45

def initialize(raw_headers, socket:, peer:, body_timeout:,
               socket_buffer: '', config: {})
  @socket        = socket
  @peer          = peer
  @body_timeout  = body_timeout
  @socket_buffer = socket_buffer.b   # binary copy of pre-read body bytes
  @max_body      = config.fetch(:max_body_size, MAX_BODY_SIZE)
  @bytes_in      = raw_headers.bytesize
  @id            = nil   # set by RequestId middleware
  @params        = {}    # set by App#dispatch after route match
  parse_headers(raw_headers)
  read_body
end

Instance Attribute Details

#bodyObject (readonly)

Returns the value of attribute body.



27
28
29
# File 'lib/stipa/request.rb', line 27

def body
  @body
end

#bytes_inObject (readonly)

Returns the value of attribute bytes_in.



27
28
29
# File 'lib/stipa/request.rb', line 27

def bytes_in
  @bytes_in
end

#headersObject (readonly)

Returns the value of attribute headers.



27
28
29
# File 'lib/stipa/request.rb', line 27

def headers
  @headers
end

#http_versionObject (readonly)

Returns the value of attribute http_version.



27
28
29
# File 'lib/stipa/request.rb', line 27

def http_version
  @http_version
end

#idObject

Returns the value of attribute id.



26
27
28
# File 'lib/stipa/request.rb', line 26

def id
  @id
end

#methodObject

Returns the value of attribute method.



26
27
28
# File 'lib/stipa/request.rb', line 26

def method
  @method
end

#paramsObject

Returns the value of attribute params.



26
27
28
# File 'lib/stipa/request.rb', line 26

def params
  @params
end

#pathObject (readonly)

Returns the value of attribute path.



27
28
29
# File 'lib/stipa/request.rb', line 27

def path
  @path
end

#query_stringObject (readonly)

Returns the value of attribute query_string.



27
28
29
# File 'lib/stipa/request.rb', line 27

def query_string
  @query_string
end

Class Method Details

.parse(raw_headers, socket:, peer:, body_timeout:, socket_buffer: '', config: {}) ⇒ Object

Factory โ€” called by Connection after reading the header block.

Parameters:

  • raw_headers (String)

    everything up to (not including) rnrn

  • socket (Socket)

    live socket for reading remaining body bytes

  • peer (String)

    remote address string (for error messages)

  • body_timeout (Numeric)

    seconds allowed for body read

  • socket_buffer (String) (defaults to: '')

    body bytes already read by Connection when it over-read past the header boundary

  • config (Hash) (defaults to: {})

    server-level config overrides



39
40
41
42
43
# File 'lib/stipa/request.rb', line 39

def self.parse(raw_headers, socket:, peer:, body_timeout:,
               socket_buffer: '', config: {})
  new(raw_headers, socket:, peer:, body_timeout:,
      socket_buffer:, config:)
end

Instance Method Details

#[](name) ⇒ Object

Case-insensitive header lookup: req or req



60
61
62
# File 'lib/stipa/request.rb', line 60

def [](name)
  @headers[name.to_s.downcase]
end