webricknio 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +26 -0
- data/README.md +58 -0
- data/lib/generator/USAGE +8 -0
- data/lib/generator/webricknio.rb +57 -0
- data/lib/generator/webricknio_generator.rb +15 -0
- data/lib/rack/handler/webricknio.rb +88 -0
- data/lib/webricknio.rb +237 -0
- data/lib/webricknio/accesslog.rb +157 -0
- data/lib/webricknio/block.rb +271 -0
- data/lib/webricknio/block.yaml +7 -0
- data/lib/webricknio/config.rb +125 -0
- data/lib/webricknio/httprequest.rb +559 -0
- data/lib/webricknio/httpresponse.rb +469 -0
- data/lib/webricknio/httpserver.rb +499 -0
- data/lib/webricknio/log.rb +30 -0
- data/lib/webricknio/version.rb +14 -0
- data/webricknio.gemspec +15 -0
- metadata +79 -0
@@ -0,0 +1,559 @@
|
|
1
|
+
#
|
2
|
+
# httprequest.rb -- HTTPRequest Class
|
3
|
+
#
|
4
|
+
# Author: IPR -- Internet Programming with Ruby -- writers, Pradeep Singh
|
5
|
+
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
|
6
|
+
# Copyright (c) 2002 Internet Programming with Ruby writers
|
7
|
+
# Copyright (c) 2012 Pradeep Singh
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $
|
11
|
+
|
12
|
+
require 'uri'
|
13
|
+
require 'webrick/httpversion'
|
14
|
+
require 'webrick/httpstatus'
|
15
|
+
require 'webrick/httputils'
|
16
|
+
require 'webrick/cookie'
|
17
|
+
|
18
|
+
require 'java'
|
19
|
+
|
20
|
+
java_import 'java.nio.ByteBuffer'
|
21
|
+
|
22
|
+
module WEBrickNIO
|
23
|
+
|
24
|
+
##
|
25
|
+
# An HTTP request.
|
26
|
+
class HTTPRequest
|
27
|
+
|
28
|
+
BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ]
|
29
|
+
|
30
|
+
# :section: Request line
|
31
|
+
attr_reader :request_line
|
32
|
+
attr_reader :request_method, :unparsed_uri, :http_version
|
33
|
+
|
34
|
+
# :section: Request-URI
|
35
|
+
attr_reader :request_uri, :path
|
36
|
+
attr_accessor :script_name, :path_info, :query_string
|
37
|
+
|
38
|
+
# :section: Header and entity body
|
39
|
+
attr_reader :raw_header, :header, :cookies
|
40
|
+
attr_reader :accept, :accept_charset
|
41
|
+
attr_reader :accept_encoding, :accept_language
|
42
|
+
|
43
|
+
# :section:
|
44
|
+
attr_accessor :user
|
45
|
+
attr_reader :addr, :peeraddr
|
46
|
+
attr_reader :attributes
|
47
|
+
attr_reader :keep_alive
|
48
|
+
attr_reader :request_time
|
49
|
+
|
50
|
+
def initialize(config)
|
51
|
+
@config = config
|
52
|
+
@in_progress = false
|
53
|
+
@buffer_size = @config[:InputBufferSize]
|
54
|
+
@logger = config[:Logger]
|
55
|
+
|
56
|
+
@request_line = @request_method =
|
57
|
+
@unparsed_uri = @http_version = nil
|
58
|
+
|
59
|
+
@request_uri = @host = @port = @path = nil
|
60
|
+
@script_name = @path_info = nil
|
61
|
+
@query_string = nil
|
62
|
+
@query = nil
|
63
|
+
@form_data = nil
|
64
|
+
|
65
|
+
@raw_header = Array.new
|
66
|
+
@header = nil
|
67
|
+
@cookies = []
|
68
|
+
@accept = []
|
69
|
+
@accept_charset = []
|
70
|
+
@accept_encoding = []
|
71
|
+
@accept_language = []
|
72
|
+
@body = nil
|
73
|
+
|
74
|
+
@addr = @peeraddr = nil
|
75
|
+
@attributes = {}
|
76
|
+
@user = nil
|
77
|
+
@keep_alive = false
|
78
|
+
@request_time = nil
|
79
|
+
|
80
|
+
@remaining_size = nil
|
81
|
+
@socket_channel = nil
|
82
|
+
@socket = nil
|
83
|
+
|
84
|
+
@forwarded_proto = @forwarded_host = @forwarded_port =
|
85
|
+
@forwarded_server = @forwarded_for = nil
|
86
|
+
|
87
|
+
@byte_buffer = ByteBuffer.allocate(8192)
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
def parse(socket_channel)
|
92
|
+
@socket_channel = socket_channel
|
93
|
+
@socket = @socket_channel.socket
|
94
|
+
|
95
|
+
begin
|
96
|
+
@peeraddr = @socket.respond_to?(:get_remote_socket_address) ? @socket.get_remote_socket_address : []
|
97
|
+
@addr = @socket.respond_to?(:get_local_socket_address) ? @socket.get_local_socket_address : []
|
98
|
+
rescue Errno::ENOTCONN => ex
|
99
|
+
@logger.error "socket id: #{@socket.object_id}"
|
100
|
+
@logger.error(ex.backtrace)
|
101
|
+
raise ::WEBrick::HTTPStatus::EOFError
|
102
|
+
end
|
103
|
+
|
104
|
+
begin
|
105
|
+
time = Time.now
|
106
|
+
@num_read = 0
|
107
|
+
@anything_read = false
|
108
|
+
while ((@num_read = @socket_channel.java_send :read, [Java::JavaNio::ByteBuffer], @byte_buffer) > 0) || (!@anything_read && Time.now - time < 2)
|
109
|
+
@anything_read = true if @num_read > 0
|
110
|
+
#@logger.debug "num_read = #{@num_read}, socket id: #{@socket.object_id}"
|
111
|
+
raise "socket was closed" if @num_read == -1
|
112
|
+
end
|
113
|
+
rescue Exception => ex
|
114
|
+
ex.respond_to?(:java_class) ? @logger.debug("socket was closed: #{ex.java_class.name}") : @logger.debug("socket was closed")
|
115
|
+
raise ::WEBrick::HTTPStatus::EOFError
|
116
|
+
end
|
117
|
+
|
118
|
+
raise ::WEBrick::HTTPStatus::EOFError if @byte_buffer.array.length == 0 #close this socket
|
119
|
+
|
120
|
+
req_string = String.from_java_bytes(@byte_buffer.array)
|
121
|
+
index_header_begin = req_string.index("\r\n\r\n")
|
122
|
+
unless index_header_begin
|
123
|
+
# ruby gives nil if index is not found
|
124
|
+
@logger.error "double new line not found within first received block of request, null index_header_begin, request: #{req_string}, size: #{req_string.length}"
|
125
|
+
raise ::WEBrick::HTTPStatus::BadRequest
|
126
|
+
end
|
127
|
+
|
128
|
+
index_uri_end = req_string.index("\r\n")
|
129
|
+
unless index_uri_end
|
130
|
+
# ruby gives nil if index is not found
|
131
|
+
@logger.error "single new line not found within first received block of request, null index_uri_end, request: #{req_string}, size: #{req_string.length}"
|
132
|
+
raise ::WEBrick::HTTPStatus::BadRequest
|
133
|
+
end
|
134
|
+
|
135
|
+
@request_line = req_string[0..index_uri_end]
|
136
|
+
|
137
|
+
process_request_line(@request_line)
|
138
|
+
|
139
|
+
if @http_version.major > 0
|
140
|
+
process_header(req_string[index_uri_end+2..index_header_begin])
|
141
|
+
@header['cookie'].each{|cookie|
|
142
|
+
@cookies += ::WEBrick::Cookie::parse(cookie)
|
143
|
+
}
|
144
|
+
@accept = ::WEBrick::HTTPUtils.parse_qvalues(self['accept'])
|
145
|
+
@accept_charset = ::WEBrick::HTTPUtils.parse_qvalues(self['accept-charset'])
|
146
|
+
@accept_encoding = ::WEBrick::HTTPUtils.parse_qvalues(self['accept-encoding'])
|
147
|
+
@accept_language = ::WEBrick::HTTPUtils.parse_qvalues(self['accept-language'])
|
148
|
+
end
|
149
|
+
return if @request_method == "CONNECT"
|
150
|
+
return if @unparsed_uri == "*"
|
151
|
+
|
152
|
+
@logger.debug "User Agent: #{self["User-Agent"]}"
|
153
|
+
|
154
|
+
step_two
|
155
|
+
|
156
|
+
begin
|
157
|
+
if content_length > 0
|
158
|
+
|
159
|
+
index_header_end = index_header_begin + 4
|
160
|
+
index_body_end = req_string.index(/\u0000+\Z/) || req_string.length
|
161
|
+
|
162
|
+
if index_header_end < index_body_end
|
163
|
+
body_bytes_read = index_body_end - index_header_end
|
164
|
+
body_remaining = content_length - body_bytes_read
|
165
|
+
@body = req_string[index_header_end..index_body_end]
|
166
|
+
@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}"
|
167
|
+
if body_remaining > 0
|
168
|
+
#need to read more body
|
169
|
+
@byte_buffer = ByteBuffer.allocate(body_remaining)
|
170
|
+
@in_progress = true
|
171
|
+
resume
|
172
|
+
end
|
173
|
+
else
|
174
|
+
@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}"
|
175
|
+
raise ::WEBrick::HTTPStatus::RequestEntityTooLarge
|
176
|
+
end
|
177
|
+
end
|
178
|
+
rescue Exception => ex
|
179
|
+
if ex.respond_to?(:java_class)
|
180
|
+
@logger.error "#{ex.java_class.name}"
|
181
|
+
else
|
182
|
+
@logger.error(ex)
|
183
|
+
end
|
184
|
+
raise ::WEBrick::HTTPStatus::EOFError
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
def resume
|
190
|
+
begin
|
191
|
+
# 2 seconds for maximum interruption between bytes, where 0 bytes are received
|
192
|
+
# bytes should be constantly arriving, otherwise the request will be postponed
|
193
|
+
time = Time.now
|
194
|
+
while @byte_buffer.has_remaining && Time.now - time < 2
|
195
|
+
num_read = @socket_channel.java_send :read, [Java::JavaNio::ByteBuffer], @byte_buffer
|
196
|
+
if num_read > 0
|
197
|
+
time = Time.now
|
198
|
+
elsif num_read == -1
|
199
|
+
@logger.debug "socket closed"
|
200
|
+
raise ::WEBrick::HTTPStatus::EOFError
|
201
|
+
end
|
202
|
+
end
|
203
|
+
@logger.debug "buffer position :#{@byte_buffer.position}"
|
204
|
+
if !@byte_buffer.has_remaining
|
205
|
+
@body += String.from_java_bytes(@byte_buffer.array)
|
206
|
+
@in_progress = false
|
207
|
+
elsif @http_version < "1.1"
|
208
|
+
raise "http_version < 1.1 client did not send data for more than 2 seconds"
|
209
|
+
end
|
210
|
+
rescue Exception => ex
|
211
|
+
if ex.respond_to?(:java_class)
|
212
|
+
@logger.debug "error: #{ex.java_class.name}"
|
213
|
+
else
|
214
|
+
@logger.debug(ex)
|
215
|
+
end
|
216
|
+
raise ::WEBrick::HTTPStatus::EOFError
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def step_two
|
221
|
+
begin
|
222
|
+
setup_forwarded_info
|
223
|
+
@request_uri = parse_uri(@unparsed_uri)
|
224
|
+
@path = ::WEBrick::HTTPUtils::unescape(@request_uri.path)
|
225
|
+
@path = ::WEBrick::HTTPUtils::normalize_path(@path)
|
226
|
+
@host = @request_uri.host
|
227
|
+
@port = @request_uri.port
|
228
|
+
@query_string = @request_uri.query
|
229
|
+
@script_name = ""
|
230
|
+
@path_info = @path.dup
|
231
|
+
rescue
|
232
|
+
raise ::WEBrick::HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'."
|
233
|
+
end
|
234
|
+
|
235
|
+
if /close/io =~ self["connection"]
|
236
|
+
@keep_alive = false
|
237
|
+
elsif /keep-alive/io =~ self["connection"]
|
238
|
+
@keep_alive = true
|
239
|
+
elsif @http_version < "1.1"
|
240
|
+
@keep_alive = false
|
241
|
+
else
|
242
|
+
@keep_alive = true
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def in_progress?
|
247
|
+
@in_progress
|
248
|
+
end
|
249
|
+
|
250
|
+
# Generate HTTP/1.1 100 continue response if the client expects it,
|
251
|
+
# otherwise does nothing.
|
252
|
+
def continue
|
253
|
+
if self['expect'] == '100-continue' && @config[:HTTPVersion] >= "1.1"
|
254
|
+
@socket << "HTTP/#{@config[:HTTPVersion]} 100 continue#{CRLF}#{CRLF}"
|
255
|
+
@header.delete('expect')
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def body(&block)
|
260
|
+
@body
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
##
|
265
|
+
# Request query as a Hash
|
266
|
+
|
267
|
+
def query
|
268
|
+
unless @query
|
269
|
+
parse_query()
|
270
|
+
end
|
271
|
+
@query
|
272
|
+
end
|
273
|
+
|
274
|
+
##
|
275
|
+
# The content-length header
|
276
|
+
|
277
|
+
def content_length
|
278
|
+
begin
|
279
|
+
return Integer(self['content-length'])
|
280
|
+
rescue Exception
|
281
|
+
return 0
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
##
|
286
|
+
# The content-type header
|
287
|
+
|
288
|
+
def content_type
|
289
|
+
return self['content-type']
|
290
|
+
end
|
291
|
+
|
292
|
+
##
|
293
|
+
# Retrieves +header_name+
|
294
|
+
|
295
|
+
def [](header_name)
|
296
|
+
if @header
|
297
|
+
value = @header[header_name.downcase]
|
298
|
+
value.empty? ? nil : value.join(", ")
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
##
|
303
|
+
# Iterates over the request headers
|
304
|
+
|
305
|
+
def each
|
306
|
+
if @header
|
307
|
+
@header.each{|k, v|
|
308
|
+
value = @header[k]
|
309
|
+
yield(k, value.empty? ? nil : value.join(", "))
|
310
|
+
}
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
##
|
315
|
+
# The host this request is for
|
316
|
+
|
317
|
+
def host
|
318
|
+
return @forwarded_host || @host
|
319
|
+
end
|
320
|
+
|
321
|
+
##
|
322
|
+
# The port this request is for
|
323
|
+
|
324
|
+
def port
|
325
|
+
return @forwarded_port || @port
|
326
|
+
end
|
327
|
+
|
328
|
+
##
|
329
|
+
# The server name this request is for
|
330
|
+
|
331
|
+
def server_name
|
332
|
+
return @forwarded_server || @config[:ServerName]
|
333
|
+
end
|
334
|
+
|
335
|
+
##
|
336
|
+
# The client's IP address
|
337
|
+
|
338
|
+
def remote_ip
|
339
|
+
return self["client-ip"] || @forwarded_for || @peeraddr[3]
|
340
|
+
end
|
341
|
+
|
342
|
+
##
|
343
|
+
# Is this an SSL request?
|
344
|
+
|
345
|
+
def ssl?
|
346
|
+
return @request_uri.scheme == "https"
|
347
|
+
end
|
348
|
+
|
349
|
+
##
|
350
|
+
# Should the connection this request was made on be kept alive?
|
351
|
+
|
352
|
+
def keep_alive?
|
353
|
+
@keep_alive
|
354
|
+
end
|
355
|
+
|
356
|
+
def fixup()
|
357
|
+
begin
|
358
|
+
body{|chunk| } # read remaining body
|
359
|
+
rescue ::WEBrick::HTTPStatus::Error => ex
|
360
|
+
@logger.error("HTTPRequest#fixup: #{ex.class} occured.")
|
361
|
+
@keep_alive = false
|
362
|
+
rescue => ex
|
363
|
+
@logger.error(ex)
|
364
|
+
@keep_alive = false
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
# This method provides the metavariables defined by the revision 3
|
369
|
+
# of "The WWW Common Gateway Interface Version 1.1"
|
370
|
+
# http://Web.Golux.Com/coar/cgi/
|
371
|
+
|
372
|
+
def meta_vars
|
373
|
+
meta = Hash.new
|
374
|
+
|
375
|
+
cl = self["Content-Length"]
|
376
|
+
ct = self["Content-Type"]
|
377
|
+
meta["CONTENT_LENGTH"] = cl if cl.to_i > 0
|
378
|
+
meta["CONTENT_TYPE"] = ct.dup if ct
|
379
|
+
meta["GATEWAY_INTERFACE"] = "CGI/1.1"
|
380
|
+
meta["PATH_INFO"] = @path_info ? @path_info.dup : ""
|
381
|
+
#meta["PATH_TRANSLATED"] = nil # no plan to be provided
|
382
|
+
meta["QUERY_STRING"] = @query_string ? @query_string.dup : ""
|
383
|
+
meta["REMOTE_ADDR"] = @peeraddr.get_address.get_host_address
|
384
|
+
meta["REMOTE_HOST"] = @peeraddr.get_host_name
|
385
|
+
#meta["REMOTE_IDENT"] = nil # no plan to be provided
|
386
|
+
meta["REMOTE_USER"] = @user
|
387
|
+
meta["REQUEST_METHOD"] = @request_method.dup
|
388
|
+
meta["REQUEST_URI"] = @request_uri.to_s
|
389
|
+
meta["SCRIPT_NAME"] = @script_name ? @script_name.dup : ""
|
390
|
+
meta["SERVER_NAME"] = @host
|
391
|
+
meta["SERVER_PORT"] = @port.to_s
|
392
|
+
meta["SERVER_PROTOCOL"] = "HTTP/" + @config[:HTTPVersion].to_s
|
393
|
+
meta["SERVER_SOFTWARE"] = @config[:ServerSoftware].dup
|
394
|
+
|
395
|
+
self.each{|key, val|
|
396
|
+
next if /^content-type$/i =~ key
|
397
|
+
next if /^content-length$/i =~ key
|
398
|
+
name = "HTTP_" + key
|
399
|
+
name.gsub!(/-/o, "_")
|
400
|
+
name.upcase!
|
401
|
+
meta[name] = val
|
402
|
+
}
|
403
|
+
|
404
|
+
meta
|
405
|
+
end
|
406
|
+
|
407
|
+
private
|
408
|
+
|
409
|
+
MAX_URI_LENGTH = 2083 # :nodoc:
|
410
|
+
|
411
|
+
def process_request_line(req_line)
|
412
|
+
@logger.debug "request line: #{@request_line.strip}"
|
413
|
+
if @request_line.bytesize >= MAX_URI_LENGTH
|
414
|
+
raise ::WEBrick::HTTPStatus::RequestURITooLarge
|
415
|
+
end
|
416
|
+
@request_time = Time.now
|
417
|
+
raise ::WEBrick::HTTPStatus::EOFError unless @request_line
|
418
|
+
if /^(\S+)\s+(\S++)(?:\s+HTTP\/(\d+\.\d+))?\r?\n?/mo =~ @request_line
|
419
|
+
@request_method = $1
|
420
|
+
@unparsed_uri = $2
|
421
|
+
@http_version = ::WEBrick::HTTPVersion.new($3 ? $3 : "0.9")
|
422
|
+
#@logger.debug "request method: #{@request_method}, unparsed uri: #{@unparsed_uri}, http version: #{@http_version}"
|
423
|
+
else
|
424
|
+
rl = @request_line.sub(/\r?\n\z/o, '')
|
425
|
+
raise ::WEBrick::HTTPStatus::BadRequest, "bad Request-Line `#{rl}'."
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
def process_header(req_lines)
|
430
|
+
@header = ::WEBrick::HTTPUtils::parse_header(req_lines)
|
431
|
+
end
|
432
|
+
|
433
|
+
# Not used anymore but being preserved as an indication of how to handle chunked encoding
|
434
|
+
def read_body(socket, block)
|
435
|
+
return unless socket
|
436
|
+
if tc = self['transfer-encoding']
|
437
|
+
case tc
|
438
|
+
when /chunked/io then read_chunked(socket, block)
|
439
|
+
else raise ::WEBrick::HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
|
440
|
+
end
|
441
|
+
elsif self['content-length'] || @remaining_size
|
442
|
+
@remaining_size ||= self['content-length'].to_i
|
443
|
+
while @remaining_size > 0
|
444
|
+
sz = [@buffer_size, @remaining_size].min
|
445
|
+
break unless buf = read_data(socket, sz)
|
446
|
+
@remaining_size -= buf.bytesize
|
447
|
+
block.call(buf)
|
448
|
+
end
|
449
|
+
if @remaining_size > 0 && @socket.eof?
|
450
|
+
raise ::WEBrick::HTTPStatus::BadRequest, "invalid body size."
|
451
|
+
end
|
452
|
+
elsif BODY_CONTAINABLE_METHODS.member?(@request_method)
|
453
|
+
raise ::WEBrick::HTTPStatus::LengthRequired
|
454
|
+
end
|
455
|
+
return @body
|
456
|
+
end
|
457
|
+
|
458
|
+
def parse_uri(str, scheme="http")
|
459
|
+
if @config[:Escape8bitURI]
|
460
|
+
str = ::WEBrick::HTTPUtils::escape8bit(str)
|
461
|
+
end
|
462
|
+
str.sub!(%r{\A/+}o, '/')
|
463
|
+
uri = URI::parse(str)
|
464
|
+
return uri if uri.absolute?
|
465
|
+
if @forwarded_host
|
466
|
+
host, port = @forwarded_host, @forwarded_port
|
467
|
+
#elsif self["host"]
|
468
|
+
# @logger.debug "5, #{self['host']}"
|
469
|
+
# pattern = /\A(#{URI::REGEXP::PATTERN::HOST})(?::(\d+))?\z/n
|
470
|
+
# host, port = *self['host'].scan(pattern)[0]
|
471
|
+
elsif !@addr.nil?
|
472
|
+
host = @addr.get_address.isAnyLocalAddress || @addr.get_address.isLoopbackAddress ?
|
473
|
+
"localhost" :
|
474
|
+
@addr.get_address.getHostAddress #IP address string in textual presentation
|
475
|
+
port = @addr.get_port
|
476
|
+
#host, port = @addr[2], @addr[1]
|
477
|
+
else
|
478
|
+
host, port = @config[:ServerName], @config[:Port]
|
479
|
+
end
|
480
|
+
uri.scheme = @forwarded_proto || scheme
|
481
|
+
uri.host = host
|
482
|
+
uri.port = port ? port : nil
|
483
|
+
return URI::parse(uri.to_s)
|
484
|
+
end
|
485
|
+
|
486
|
+
def read_chunk_size(socket)
|
487
|
+
line = read_line(socket)
|
488
|
+
if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line
|
489
|
+
chunk_size = $1.hex
|
490
|
+
chunk_ext = $2
|
491
|
+
[ chunk_size, chunk_ext ]
|
492
|
+
else
|
493
|
+
raise ::WEBrick::HTTPStatus::BadRequest, "bad chunk `#{line}'."
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
def read_chunked(socket, block)
|
498
|
+
chunk_size, = read_chunk_size(socket)
|
499
|
+
while chunk_size > 0
|
500
|
+
data = read_data(socket, chunk_size) # read chunk-data
|
501
|
+
if data.nil? || data.bytesize != chunk_size
|
502
|
+
raise BadRequest, "bad chunk data size."
|
503
|
+
end
|
504
|
+
read_line(socket) # skip CRLF
|
505
|
+
block.call(data)
|
506
|
+
chunk_size, = read_chunk_size(socket)
|
507
|
+
end
|
508
|
+
read_header(socket) # trailer + CRLF
|
509
|
+
@header.delete("transfer-encoding")
|
510
|
+
@remaining_size = 0
|
511
|
+
end
|
512
|
+
|
513
|
+
def parse_query()
|
514
|
+
begin
|
515
|
+
if @request_method == "GET" || @request_method == "HEAD"
|
516
|
+
@query = ::WEBrick::HTTPUtils::parse_query(@query_string)
|
517
|
+
elsif self['content-type'] =~ /^application\/x-www-form-urlencoded/
|
518
|
+
@query = ::WEBrick::HTTPUtils::parse_query(body)
|
519
|
+
elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/
|
520
|
+
boundary = ::WEBrick::HTTPUtils::dequote($1)
|
521
|
+
@query = ::WEBrick::HTTPUtils::parse_form_data(body, boundary)
|
522
|
+
else
|
523
|
+
@query = Hash.new
|
524
|
+
end
|
525
|
+
rescue => ex
|
526
|
+
raise ::WEBrick::HTTPStatus::BadRequest, ex.message
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
PrivateNetworkRegexp = /
|
531
|
+
^unknown$|
|
532
|
+
^((::ffff:)?127.0.0.1|::1)$|
|
533
|
+
^(::ffff:)?(10|172\.(1[6-9]|2[0-9]|3[01])|192\.168)\.
|
534
|
+
/ixo
|
535
|
+
|
536
|
+
# It's said that all X-Forwarded-* headers will contain more than one
|
537
|
+
# (comma-separated) value if the original request already contained one of
|
538
|
+
# these headers. Since we could use these values as Host header, we choose
|
539
|
+
# the initial(first) value. (apr_table_mergen() adds new value after the
|
540
|
+
# existing value with ", " prefix)
|
541
|
+
def setup_forwarded_info
|
542
|
+
if @forwarded_server = self["x-forwarded-server"]
|
543
|
+
@forwarded_server = @forwarded_server.split(",", 2).first
|
544
|
+
end
|
545
|
+
@forwarded_proto = self["x-forwarded-proto"]
|
546
|
+
if host_port = self["x-forwarded-host"]
|
547
|
+
host_port = host_port.split(",", 2).first
|
548
|
+
@forwarded_host, tmp = host_port.split(":", 2)
|
549
|
+
@forwarded_port = (tmp || (@forwarded_proto == "https" ? 443 : 80)).to_i
|
550
|
+
end
|
551
|
+
if addrs = self["x-forwarded-for"]
|
552
|
+
addrs = addrs.split(",").collect(&:strip)
|
553
|
+
addrs.reject!{|ip| PrivateNetworkRegexp =~ ip }
|
554
|
+
@forwarded_for = addrs.first
|
555
|
+
end
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
end
|