webricknio 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|