Class: Thin::Request

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

Overview

A request sent by the client to the server.

Constant Summary collapse

MAX_BODY =

Maximum request body size before it is moved out of memory and into a tempfile for reading.

1024 * (80 + 32)
BODY_TMPFILE =
'thin-body'.freeze
MAX_HEADER =
1024 * (80 + 32)
INITIAL_BODY =
String.new
SERVER_SOFTWARE =

Freeze some HTTP header names & values

'SERVER_SOFTWARE'.freeze
SERVER_NAME =
'SERVER_NAME'.freeze
REQUEST_METHOD =
'REQUEST_METHOD'.freeze
LOCALHOST =
'localhost'.freeze
HTTP_VERSION =
'HTTP_VERSION'.freeze
HTTP_1_0 =
'HTTP/1.0'.freeze
REMOTE_ADDR =
'REMOTE_ADDR'.freeze
CONTENT_LENGTH =
'CONTENT_LENGTH'.freeze
CONNECTION =
'HTTP_CONNECTION'.freeze
KEEP_ALIVE_REGEXP =
/\bkeep-alive\b/i.freeze
CLOSE_REGEXP =
/\bclose\b/i.freeze
HEAD =
'HEAD'.freeze
RACK_INPUT =

Freeze some Rack header names

'rack.input'.freeze
RACK_VERSION =
'rack.version'.freeze
RACK_ERRORS =
'rack.errors'.freeze
RACK_MULTITHREAD =
'rack.multithread'.freeze
RACK_MULTIPROCESS =
'rack.multiprocess'.freeze
RACK_RUN_ONCE =
'rack.run_once'.freeze
ASYNC_CALLBACK =
'async.callback'.freeze
ASYNC_CLOSE =
'async.close'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRequest

Returns a new instance of Request.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/thin/request.rb', line 53

def initialize
  @parser   = Thin::HttpParser.new
  @data     = String.new
  @nparsed  = 0
  @body     = StringIO.new(INITIAL_BODY.dup)
  @env      = {
    SERVER_SOFTWARE   => SERVER,
    SERVER_NAME       => LOCALHOST,

    # Rack stuff
    RACK_INPUT        => @body,

    RACK_VERSION      => VERSION::RACK,
    RACK_ERRORS       => STDERR,

    RACK_MULTITHREAD  => false,
    RACK_MULTIPROCESS => false,
    RACK_RUN_ONCE     => false
  }
end

Instance Attribute Details

#bodyObject (readonly)

Request body



51
52
53
# File 'lib/thin/request.rb', line 51

def body
  @body
end

#dataObject (readonly)

Unparsed data of the request



48
49
50
# File 'lib/thin/request.rb', line 48

def data
  @data
end

#envObject (readonly)

CGI-like request environment variables



45
46
47
# File 'lib/thin/request.rb', line 45

def env
  @env
end

Instance Method Details

#async_callback=(callback) ⇒ Object



136
137
138
139
# File 'lib/thin/request.rb', line 136

def async_callback=(callback)
  @env[ASYNC_CALLBACK] = callback
  @env[ASYNC_CLOSE] = EventMachine::DefaultDeferrable.new
end

#async_closeObject



141
142
143
# File 'lib/thin/request.rb', line 141

def async_close
  @async_close ||= @env[ASYNC_CLOSE]
end

#closeObject

Close any resource used by the request



150
151
152
# File 'lib/thin/request.rb', line 150

def close
  @body.close! if @body.class == Tempfile
end

#content_lengthObject

Expected size of the body



108
109
110
# File 'lib/thin/request.rb', line 108

def content_length
  @env[CONTENT_LENGTH].to_i
end

#finished?Boolean

true if headers and body are finished parsing

Returns:

  • (Boolean)


103
104
105
# File 'lib/thin/request.rb', line 103

def finished?
  @parser.finished? && @body.size >= content_length
end

#head?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'lib/thin/request.rb', line 145

def head?
  @env[REQUEST_METHOD] == HEAD
end

#parse(data) ⇒ Object

Parse a chunk of data into the request environment Raises an InvalidRequest if invalid. Returns true if the parsing is complete.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/thin/request.rb', line 77

def parse(data)
  if data.size > 0 && finished? # headers and body already fully satisfied. more data is erroneous.
    raise InvalidRequest, 'Content longer than specified'
  elsif @parser.finished?  # Header finished, can only be some more body
    @body << data
  else                  # Parse more header using the super parser
    @data << data
    raise InvalidRequest, 'Header longer than allowed' if @data.size > MAX_HEADER

    @nparsed = @parser.execute(@env, @data, @nparsed)

    # Transfer to a tempfile if body is very big
    move_body_to_tempfile if @parser.finished? && content_length > MAX_BODY
  end


  if finished?   # Check if header and body are complete
    @data = nil
    @body.rewind
    true         # Request is fully parsed
  else
    false        # Not finished, need more data
  end
end

#persistent?Boolean

Returns true if the client expects the connection to be persistent.

Returns:

  • (Boolean)


113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/thin/request.rb', line 113

def persistent?
  # Clients and servers SHOULD NOT assume that a persistent connection
  # is maintained for HTTP versions less than 1.1 unless it is explicitly
  # signaled. (http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html)
  if @env[HTTP_VERSION] == HTTP_1_0
    @env[CONNECTION] =~ KEEP_ALIVE_REGEXP

  # HTTP/1.1 client intends to maintain a persistent connection unless
  # a Connection header including the connection-token "close" was sent
  # in the request
  else
    @env[CONNECTION].nil? || @env[CONNECTION] !~ CLOSE_REGEXP
  end
end

#remote_address=(address) ⇒ Object



128
129
130
# File 'lib/thin/request.rb', line 128

def remote_address=(address)
  @env[REMOTE_ADDR] = address
end

#threaded=(value) ⇒ Object



132
133
134
# File 'lib/thin/request.rb', line 132

def threaded=(value)
  @env[RACK_MULTITHREAD] = value
end