Class: WEBrickNIO::HTTPRequest

Inherits:
Object
  • Object
show all
Defined in:
lib/webricknio/httprequest.rb

Overview

An HTTP request.

Constant Summary collapse

BODY_CONTAINABLE_METHODS =
[ "POST", "PUT" ]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ HTTPRequest

Returns a new instance of HTTPRequest.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/webricknio/httprequest.rb', line 50

def initialize(config)
  @config = config
  @in_progress = false
  @buffer_size = @config[:InputBufferSize]
  @logger = config[:Logger]

  @request_line = @request_method =
    @unparsed_uri = @http_version = nil

  @request_uri = @host = @port = @path = nil
  @script_name = @path_info = nil
  @query_string = nil
  @query = nil
  @form_data = nil

  @raw_header = Array.new
  @header = nil
  @cookies = []
  @accept = []
  @accept_charset = []
  @accept_encoding = []
  @accept_language = []
  @body = nil

  @addr = @peeraddr = nil
  @attributes = {}
  @user = nil
  @keep_alive = false
  @request_time = nil

  @remaining_size = nil
  @socket_channel = nil
  @socket = nil

  @forwarded_proto = @forwarded_host = @forwarded_port =
    @forwarded_server = @forwarded_for = nil

  @byte_buffer = ByteBuffer.allocate(8192)

end

Instance Attribute Details

#acceptObject (readonly)

Returns the value of attribute accept.



40
41
42
# File 'lib/webricknio/httprequest.rb', line 40

def accept
  @accept
end

#accept_charsetObject (readonly)

Returns the value of attribute accept_charset.



40
41
42
# File 'lib/webricknio/httprequest.rb', line 40

def accept_charset
  @accept_charset
end

#accept_encodingObject (readonly)

Returns the value of attribute accept_encoding.



41
42
43
# File 'lib/webricknio/httprequest.rb', line 41

def accept_encoding
  @accept_encoding
end

#accept_languageObject (readonly)

Returns the value of attribute accept_language.



41
42
43
# File 'lib/webricknio/httprequest.rb', line 41

def accept_language
  @accept_language
end

#addrObject (readonly)

Returns the value of attribute addr.



45
46
47
# File 'lib/webricknio/httprequest.rb', line 45

def addr
  @addr
end

#attributesObject (readonly)

Returns the value of attribute attributes.



46
47
48
# File 'lib/webricknio/httprequest.rb', line 46

def attributes
  @attributes
end

#cookiesObject (readonly)

:section: Header and entity body



39
40
41
# File 'lib/webricknio/httprequest.rb', line 39

def cookies
  @cookies
end

#headerObject (readonly)

:section: Header and entity body



39
40
41
# File 'lib/webricknio/httprequest.rb', line 39

def header
  @header
end

#http_versionObject (readonly)

Returns the value of attribute http_version.



32
33
34
# File 'lib/webricknio/httprequest.rb', line 32

def http_version
  @http_version
end

#keep_aliveObject (readonly)

Returns the value of attribute keep_alive.



47
48
49
# File 'lib/webricknio/httprequest.rb', line 47

def keep_alive
  @keep_alive
end

#pathObject (readonly)

:section: Request-URI



35
36
37
# File 'lib/webricknio/httprequest.rb', line 35

def path
  @path
end

#path_infoObject

Returns the value of attribute path_info.



36
37
38
# File 'lib/webricknio/httprequest.rb', line 36

def path_info
  @path_info
end

#peeraddrObject (readonly)

Returns the value of attribute peeraddr.



45
46
47
# File 'lib/webricknio/httprequest.rb', line 45

def peeraddr
  @peeraddr
end

#query_stringObject

Returns the value of attribute query_string.



36
37
38
# File 'lib/webricknio/httprequest.rb', line 36

def query_string
  @query_string
end

#raw_headerObject (readonly)

:section: Header and entity body



39
40
41
# File 'lib/webricknio/httprequest.rb', line 39

def raw_header
  @raw_header
end

#request_lineObject (readonly)

:section: Request line



31
32
33
# File 'lib/webricknio/httprequest.rb', line 31

def request_line
  @request_line
end

#request_methodObject (readonly)

Returns the value of attribute request_method.



32
33
34
# File 'lib/webricknio/httprequest.rb', line 32

def request_method
  @request_method
end

#request_timeObject (readonly)

Returns the value of attribute request_time.



48
49
50
# File 'lib/webricknio/httprequest.rb', line 48

def request_time
  @request_time
end

#request_uriObject (readonly)

:section: Request-URI



35
36
37
# File 'lib/webricknio/httprequest.rb', line 35

def request_uri
  @request_uri
end

#script_nameObject

Returns the value of attribute script_name.



36
37
38
# File 'lib/webricknio/httprequest.rb', line 36

def script_name
  @script_name
end

#unparsed_uriObject (readonly)

Returns the value of attribute unparsed_uri.



32
33
34
# File 'lib/webricknio/httprequest.rb', line 32

def unparsed_uri
  @unparsed_uri
end

#userObject

:section:



44
45
46
# File 'lib/webricknio/httprequest.rb', line 44

def user
  @user
end

Instance Method Details

#[](header_name) ⇒ Object

Retrieves header_name



295
296
297
298
299
300
# File 'lib/webricknio/httprequest.rb', line 295

def [](header_name)
  if @header
    value = @header[header_name.downcase]
    value.empty? ? nil : value.join(", ")
  end
end

#body(&block) ⇒ Object



259
260
261
# File 'lib/webricknio/httprequest.rb', line 259

def body(&block)
  @body
end

#content_lengthObject

The content-length header



277
278
279
280
281
282
283
# File 'lib/webricknio/httprequest.rb', line 277

def content_length
  begin
    return Integer(self['content-length'])
  rescue Exception
    return 0
  end
end

#content_typeObject

The content-type header



288
289
290
# File 'lib/webricknio/httprequest.rb', line 288

def content_type
  return self['content-type']
end

#continueObject

Generate HTTP/1.1 100 continue response if the client expects it, otherwise does nothing.



252
253
254
255
256
257
# File 'lib/webricknio/httprequest.rb', line 252

def continue
  if self['expect'] == '100-continue' && @config[:HTTPVersion] >= "1.1"
    @socket << "HTTP/#{@config[:HTTPVersion]} 100 continue#{CRLF}#{CRLF}"
    @header.delete('expect')
  end
end

#eachObject

Iterates over the request headers



305
306
307
308
309
310
311
312
# File 'lib/webricknio/httprequest.rb', line 305

def each
  if @header
    @header.each{|k, v|
      value = @header[k]
      yield(k, value.empty? ? nil : value.join(", "))
    }
  end
end

#fixupObject



356
357
358
359
360
361
362
363
364
365
366
# File 'lib/webricknio/httprequest.rb', line 356

def fixup()
  begin
    body{|chunk| }   # read remaining body
  rescue ::WEBrick::HTTPStatus::Error => ex
    @logger.error("HTTPRequest#fixup: #{ex.class} occured.")
    @keep_alive = false
  rescue => ex
    @logger.error(ex)
    @keep_alive = false
  end
end

#hostObject

The host this request is for



317
318
319
# File 'lib/webricknio/httprequest.rb', line 317

def host
  return @forwarded_host || @host
end

#in_progress?Boolean

Returns:

  • (Boolean)


246
247
248
# File 'lib/webricknio/httprequest.rb', line 246

def in_progress?
  @in_progress
end

#keep_alive?Boolean

Should the connection this request was made on be kept alive?

Returns:

  • (Boolean)


352
353
354
# File 'lib/webricknio/httprequest.rb', line 352

def keep_alive?
  @keep_alive
end

#meta_varsObject

This method provides the metavariables defined by the revision 3 of “The WWW Common Gateway Interface Version 1.1” Web.Golux.Com/coar/cgi/



372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/webricknio/httprequest.rb', line 372

def meta_vars
  meta = Hash.new

  cl = self["Content-Length"]
  ct = self["Content-Type"]
  meta["CONTENT_LENGTH"]    = cl if cl.to_i > 0
  meta["CONTENT_TYPE"]      = ct.dup if ct
  meta["GATEWAY_INTERFACE"] = "CGI/1.1"
  meta["PATH_INFO"]         = @path_info ? @path_info.dup : ""
 #meta["PATH_TRANSLATED"]   = nil      # no plan to be provided
  meta["QUERY_STRING"]      = @query_string ? @query_string.dup : ""
  meta["REMOTE_ADDR"]       = @peeraddr.get_address.get_host_address
  meta["REMOTE_HOST"]       = @peeraddr.get_host_name
 #meta["REMOTE_IDENT"]      = nil      # no plan to be provided
  meta["REMOTE_USER"]       = @user
  meta["REQUEST_METHOD"]    = @request_method.dup
  meta["REQUEST_URI"]       = @request_uri.to_s
  meta["SCRIPT_NAME"]       = @script_name ? @script_name.dup : ""
  meta["SERVER_NAME"]       = @host
  meta["SERVER_PORT"]       = @port.to_s
  meta["SERVER_PROTOCOL"]   = "HTTP/" + @config[:HTTPVersion].to_s
  meta["SERVER_SOFTWARE"]   = @config[:ServerSoftware].dup

  self.each{|key, val|
    next if /^content-type$/i =~ key
    next if /^content-length$/i =~ key
    name = "HTTP_" + key
    name.gsub!(/-/o, "_")
    name.upcase!
    meta[name] = val
  }

  meta
end

#parse(socket_channel) ⇒ Object

Raises:

  • (::WEBrick::HTTPStatus::EOFError)


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/webricknio/httprequest.rb', line 91

def parse(socket_channel)
  @socket_channel = socket_channel
  @socket = @socket_channel.socket

  begin
    @peeraddr = @socket.respond_to?(:get_remote_socket_address) ? @socket.get_remote_socket_address : []
    @addr = @socket.respond_to?(:get_local_socket_address) ? @socket.get_local_socket_address : []
  rescue Errno::ENOTCONN => ex
    @logger.error "socket id: #{@socket.object_id}"
    @logger.error(ex.backtrace)
    raise  ::WEBrick::HTTPStatus::EOFError
  end

  begin
    time = Time.now
    @num_read = 0
    @anything_read = false
    while ((@num_read = @socket_channel.java_send :read, [Java::JavaNio::ByteBuffer], @byte_buffer)  > 0) || (!@anything_read && Time.now - time < 2)
      @anything_read = true if @num_read > 0
      #@logger.debug "num_read = #{@num_read}, socket id: #{@socket.object_id}"
      raise "socket was closed" if @num_read == -1
    end
  rescue Exception => ex
    ex.respond_to?(:java_class) ? @logger.debug("socket was closed: #{ex.java_class.name}") : @logger.debug("socket was closed")
    raise ::WEBrick::HTTPStatus::EOFError
  end

  raise ::WEBrick::HTTPStatus::EOFError if @byte_buffer.array.length == 0 #close this socket

  req_string = String.from_java_bytes(@byte_buffer.array)
  index_header_begin = req_string.index("\r\n\r\n")
  unless index_header_begin
    # ruby gives nil if index is not found
    @logger.error "double new line not found within first received block of request, null index_header_begin, request: #{req_string}, size: #{req_string.length}"
    raise ::WEBrick::HTTPStatus::BadRequest
  end

  index_uri_end = req_string.index("\r\n")
  unless index_uri_end
    # ruby gives nil if index is not found
    @logger.error "single new line not found within first received block of request, null index_uri_end, request: #{req_string}, size: #{req_string.length}"
    raise ::WEBrick::HTTPStatus::BadRequest
  end

  @request_line = req_string[0..index_uri_end]

  process_request_line(@request_line)

  if @http_version.major > 0
    process_header(req_string[index_uri_end+2..index_header_begin])
    @header['cookie'].each{|cookie|
      @cookies += ::WEBrick::Cookie::parse(cookie)
    }
    @accept =  ::WEBrick::HTTPUtils.parse_qvalues(self['accept'])
    @accept_charset =  ::WEBrick::HTTPUtils.parse_qvalues(self['accept-charset'])
    @accept_encoding =  ::WEBrick::HTTPUtils.parse_qvalues(self['accept-encoding'])
    @accept_language =  ::WEBrick::HTTPUtils.parse_qvalues(self['accept-language'])
  end
  return if @request_method == "CONNECT"
  return if @unparsed_uri == "*"

  @logger.debug "User Agent: #{self["User-Agent"]}"

  step_two

  begin
    if content_length > 0

      index_header_end = index_header_begin + 4
      index_body_end = req_string.index(/\u0000+\Z/) || req_string.length

      if index_header_end < index_body_end
        body_bytes_read = index_body_end - index_header_end
        body_remaining = content_length - body_bytes_read
        @body = req_string[index_header_end..index_body_end]
        @logger.debug "body read so far: #{body_bytes_read}, remaining: #{body_remaining}, index_header_end: #{index_header_end}, index_body_end: #{index_body_end}, content length: #{content_length}"
        if body_remaining > 0
          #need to read more body
          @byte_buffer = ByteBuffer.allocate(body_remaining)
          @in_progress = true
          resume
        end
      else
        @logger.error "header did not end within first received block of request. index_header_end: #{index_header_end}, index_body_end: #{index_body_end}, req length: #{req_string.length}, req_string: #{req_string}"
        raise ::WEBrick::HTTPStatus::RequestEntityTooLarge
      end
    end
  rescue Exception => ex
    if ex.respond_to?(:java_class)
      @logger.error "#{ex.java_class.name}"
    else
      @logger.error(ex)
    end
    raise ::WEBrick::HTTPStatus::EOFError
  end
end

#portObject

The port this request is for



324
325
326
# File 'lib/webricknio/httprequest.rb', line 324

def port
  return @forwarded_port || @port
end

#queryObject

Request query as a Hash



267
268
269
270
271
272
# File 'lib/webricknio/httprequest.rb', line 267

def query
  unless @query
    parse_query()
  end
  @query
end

#remote_ipObject

The client’s IP address



338
339
340
# File 'lib/webricknio/httprequest.rb', line 338

def remote_ip
  return self["client-ip"] || @forwarded_for || @peeraddr[3]
end

#resumeObject



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/webricknio/httprequest.rb', line 189

def resume
  begin
    # 2 seconds for maximum interruption between bytes, where 0 bytes are received
    # bytes should be constantly arriving, otherwise the request will be postponed
    time = Time.now
    while @byte_buffer.has_remaining && Time.now - time < 2
      num_read = @socket_channel.java_send :read, [Java::JavaNio::ByteBuffer], @byte_buffer
      if num_read > 0
        time = Time.now
      elsif num_read == -1
        @logger.debug "socket closed"
        raise ::WEBrick::HTTPStatus::EOFError
      end
    end
    @logger.debug "buffer position :#{@byte_buffer.position}"
    if !@byte_buffer.has_remaining
      @body += String.from_java_bytes(@byte_buffer.array)
      @in_progress = false
    elsif @http_version < "1.1"
      raise "http_version < 1.1 client did not send data for more than 2 seconds"
    end
  rescue Exception => ex
    if ex.respond_to?(:java_class)
      @logger.debug "error: #{ex.java_class.name}"
    else
      @logger.debug(ex)
    end
    raise ::WEBrick::HTTPStatus::EOFError
  end
end

#server_nameObject

The server name this request is for



331
332
333
# File 'lib/webricknio/httprequest.rb', line 331

def server_name
  return @forwarded_server || @config[:ServerName]
end

#ssl?Boolean

Is this an SSL request?

Returns:

  • (Boolean)


345
346
347
# File 'lib/webricknio/httprequest.rb', line 345

def ssl?
  return @request_uri.scheme == "https"
end

#step_twoObject



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/webricknio/httprequest.rb', line 220

def step_two
  begin
    setup_forwarded_info
    @request_uri = parse_uri(@unparsed_uri)
    @path =  ::WEBrick::HTTPUtils::unescape(@request_uri.path)
    @path =  ::WEBrick::HTTPUtils::normalize_path(@path)
    @host = @request_uri.host
    @port = @request_uri.port
    @query_string = @request_uri.query
    @script_name = ""
    @path_info = @path.dup
  rescue
    raise ::WEBrick::HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'."
  end

  if /close/io =~ self["connection"]
    @keep_alive = false
  elsif /keep-alive/io =~ self["connection"]
    @keep_alive = true
  elsif @http_version < "1.1"
    @keep_alive = false
  else
    @keep_alive = true
  end
end