| Class | WEBrick::HTTPRequest |
| In: |
lib/webrick/https.rb
lib/webrick/httprequest.rb |
| Parent: | Object |
| BODY_CONTAINABLE_METHODS | = | [ "POST", "PUT" ] |
| BUFSIZE | = | 1024*4 |
| parse | -> | orig_parse |
| parse_uri | -> | orig_parse_uri |
| meta_vars | -> | orig_meta_vars |
| accept | [R] | |
| accept_charset | [R] | |
| accept_encoding | [R] | |
| accept_language | [R] | |
| addr | [R] | |
| attributes | [R] | |
| cipher | [R] | |
| client_cert | [R] | |
| cookies | [R] | Header and entity body |
| header | [R] | Header and entity body |
| host | [R] | Request-URI |
| http_version | [R] | |
| keep_alive | [R] | |
| path | [R] | Request-URI |
| path_info | [RW] | |
| peeraddr | [R] | |
| port | [R] | Request-URI |
| query_string | [RW] | |
| raw_header | [R] | Header and entity body |
| request_line | [R] | Request line |
| request_method | [R] | |
| request_time | [R] | |
| request_uri | [R] | Request-URI |
| script_name | [RW] | |
| server_cert | [R] | |
| unparsed_uri | [R] | |
| user | [RW] | Misc |
# File lib/webrick/httprequest.rb, line 45
45: def initialize(config)
46: @config = config
47: @logger = config[:Logger]
48:
49: @request_line = @request_method =
50: @unparsed_uri = @http_version = nil
51:
52: @request_uri = @host = @port = @path = nil
53: @script_name = @path_info = nil
54: @query_string = nil
55: @query = nil
56: @form_data = nil
57:
58: @raw_header = Array.new
59: @header = nil
60: @cookies = []
61: @accept = []
62: @accept_charset = []
63: @accept_encoding = []
64: @accept_language = []
65: @body = ""
66:
67: @addr = @peeraddr = nil
68: @attributes = {}
69: @user = nil
70: @keep_alive = false
71: @request_time = nil
72:
73: @remaining_size = nil
74: @socket = nil
75: end
# File lib/webrick/httprequest.rb, line 145
145: def [](header_name)
146: if @header
147: value = @header[header_name.downcase]
148: value.empty? ? nil : value.join(", ")
149: end
150: end
# File lib/webrick/httprequest.rb, line 124
124: def body(&block)
125: block ||= Proc.new{|chunk| @body << chunk }
126: read_body(@socket, block)
127: @body.empty? ? nil : @body
128: end
# File lib/webrick/httprequest.rb, line 137
137: def content_length
138: return Integer(self['content-length'])
139: end
# File lib/webrick/httprequest.rb, line 141
141: def content_type
142: return self['content-type']
143: end
# File lib/webrick/httprequest.rb, line 152
152: def each
153: @header.each{|k, v|
154: value = @header[k]
155: yield(k, value.empty? ? nil : value.join(", "))
156: }
157: end
# File lib/webrick/httprequest.rb, line 171
171: def fixup()
172: begin
173: body{|chunk| } # read remaining body
174: rescue HTTPStatus::Error => ex
175: @logger.error("HTTPRequest#fixup: #{ex.class} occured.")
176: @keep_alive = false
177: rescue => ex
178: @logger.error(ex)
179: @keep_alive = false
180: end
181: end
# File lib/webrick/https.rb, line 44
44: def meta_vars
45: meta = orig_meta_vars
46: if @server_cert
47: meta["HTTPS"] = "on"
48: meta["SSL_SERVER_CERT"] = @server_cert.to_pem
49: meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : ""
50: if @client_cert_chain
51: @client_cert_chain.each_with_index{|cert, i|
52: meta["SSL_CLIENT_CERT_CHAIN_#{i}"] = cert.to_pem
53: }
54: end
55: meta["SSL_CIPHER"] = @cipher[0]
56: meta["SSL_PROTOCOL"] = @cipher[1]
57: meta["SSL_CIPHER_USEKEYSIZE"] = @cipher[2].to_s
58: meta["SSL_CIPHER_ALGKEYSIZE"] = @cipher[3].to_s
59: end
60: meta
61: end
# File lib/webrick/httprequest.rb, line 183
183: def meta_vars
184: # This method provides the metavariables defined by the revision 3
185: # of ``The WWW Common Gateway Interface Version 1.1''.
186: # (http://Web.Golux.Com/coar/cgi/)
187:
188: meta = Hash.new
189:
190: cl = self["Content-Length"]
191: ct = self["Content-Type"]
192: meta["CONTENT_LENGTH"] = cl if cl.to_i > 0
193: meta["CONTENT_TYPE"] = ct.dup if ct
194: meta["GATEWAY_INTERFACE"] = "CGI/1.1"
195: meta["PATH_INFO"] = @path_info ? @path_info.dup : ""
196: #meta["PATH_TRANSLATED"] = nil # no plan to be provided
197: meta["QUERY_STRING"] = @query_string ? @query_string.dup : ""
198: meta["REMOTE_ADDR"] = @peeraddr[3]
199: meta["REMOTE_HOST"] = @peeraddr[2]
200: #meta["REMOTE_IDENT"] = nil # no plan to be provided
201: meta["REMOTE_USER"] = @user
202: meta["REQUEST_METHOD"] = @request_method.dup
203: meta["REQUEST_URI"] = @request_uri.to_s
204: meta["SCRIPT_NAME"] = @script_name.dup
205: meta["SERVER_NAME"] = @host
206: meta["SERVER_PORT"] = @port.to_s
207: meta["SERVER_PROTOCOL"] = "HTTP/" + @config[:HTTPVersion].to_s
208: meta["SERVER_SOFTWARE"] = @config[:ServerSoftware].dup
209:
210: self.each{|key, val|
211: next if /^content-type$/i =~ key
212: next if /^content-length$/i =~ key
213: name = "HTTP_" + key
214: name.gsub!(/-/o, "_")
215: name.upcase!
216: meta[name] = val
217: }
218:
219: meta
220: end
# File lib/webrick/httprequest.rb, line 77
77: def parse(socket=nil)
78: @socket = socket
79: begin
80: @peeraddr = socket.respond_to?(:peeraddr) ? socket.peeraddr : []
81: @addr = socket.respond_to?(:addr) ? socket.addr : []
82: rescue Errno::ENOTCONN
83: raise HTTPStatus::EOFError
84: end
85:
86: read_request_line(socket)
87: if @http_version.major > 0
88: read_header(socket)
89: @header['cookie'].each{|cookie|
90: @cookies += Cookie::parse(cookie)
91: }
92: @accept = HTTPUtils.parse_qvalues(self['accept'])
93: @accept_charset = HTTPUtils.parse_qvalues(self['accept-charset'])
94: @accept_encoding = HTTPUtils.parse_qvalues(self['accept-encoding'])
95: @accept_language = HTTPUtils.parse_qvalues(self['accept-language'])
96: end
97: return if @request_method == "CONNECT"
98: return if @unparsed_uri == "*"
99:
100: begin
101: @request_uri = parse_uri(@unparsed_uri)
102: @path = HTTPUtils::unescape(@request_uri.path)
103: @path = HTTPUtils::normalize_path(@path)
104: @host = @request_uri.host
105: @port = @request_uri.port
106: @query_string = @request_uri.query
107: @script_name = ""
108: @path_info = @path.dup
109: rescue
110: raise HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'."
111: end
112:
113: if /close/io =~ self["connection"]
114: @keep_alive = false
115: elsif /keep-alive/io =~ self["connection"]
116: @keep_alive = true
117: elsif @http_version < "1.1"
118: @keep_alive = false
119: else
120: @keep_alive = true
121: end
122: end
# File lib/webrick/https.rb, line 23
23: def parse(socket=nil)
24: if socket.respond_to?(:cert)
25: @server_cert = socket.cert || @config[:SSLCertificate]
26: @client_cert = socket.peer_cert
27: @client_cert_chain = socket.peer_cert_chain
28: @cipher = socket.cipher
29: end
30: orig_parse(socket)
31: end
# File lib/webrick/https.rb, line 35
35: def parse_uri(str, scheme="https")
36: if @server_cert
37: return orig_parse_uri(str, scheme)
38: end
39: return orig_parse_uri(str)
40: end
# File lib/webrick/httprequest.rb, line 130
130: def query
131: unless @query
132: parse_query()
133: end
134: @query
135: end
# File lib/webrick/httprequest.rb, line 163
163: def to_s
164: ret = @request_line.dup
165: @raw_header.each{|line| ret << line }
166: ret << CRLF
167: ret << body if body
168: ret
169: end
# File lib/webrick/httprequest.rb, line 324
324: def _read_data(io, method, arg)
325: begin
326: timeout(@config[:RequestTimeout]){
327: return io.__send__(method, arg)
328: }
329: rescue Errno::ECONNRESET
330: return nil
331: rescue TimeoutError
332: raise HTTPStatus::RequestTimeout
333: end
334: end
# File lib/webrick/httprequest.rb, line 344
344: def parse_query()
345: begin
346: if @request_method == "GET" || @request_method == "HEAD"
347: @query = HTTPUtils::parse_query(@query_string)
348: elsif self['content-type'] =~ /^application\/x-www-form-urlencoded/
349: @query = HTTPUtils::parse_query(body)
350: elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/
351: boundary = HTTPUtils::dequote($1)
352: @query = HTTPUtils::parse_form_data(body, boundary)
353: else
354: @query = Hash.new
355: end
356: rescue => ex
357: raise HTTPStatus::BadRequest, ex.message
358: end
359: end
# File lib/webrick/httprequest.rb, line 248
248: def parse_uri(str, scheme="http")
249: if @config[:Escape8bitURI]
250: str = HTTPUtils::escape8bit(str)
251: end
252: uri = URI::parse(str)
253: return uri if uri.absolute?
254: if self["host"]
255: pattern = /\A(#{URI::REGEXP::PATTERN::HOST})(?::(\d+))?\z/n
256: host, port = *self['host'].scan(pattern)[0]
257: elsif @addr.size > 0
258: host, port = @addr[2], @addr[1]
259: else
260: host, port = @config[:ServerName], @config[:Port]
261: end
262: uri.scheme = scheme
263: uri.host = host
264: uri.port = port ? port.to_i : nil
265: return URI::parse(uri.to_s)
266: end
# File lib/webrick/httprequest.rb, line 268
268: def read_body(socket, block)
269: return unless socket
270: if tc = self['transfer-encoding']
271: case tc
272: when /chunked/io then read_chunked(socket, block)
273: else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
274: end
275: elsif self['content-length'] || @remaining_size
276: @remaining_size ||= self['content-length'].to_i
277: while @remaining_size > 0
278: sz = BUFSIZE < @remaining_size ? BUFSIZE : @remaining_size
279: break unless buf = read_data(socket, sz)
280: @remaining_size -= buf.size
281: block.call(buf)
282: end
283: if @remaining_size > 0 && @socket.eof?
284: raise HTTPStatus::BadRequest, "invalid body size."
285: end
286: elsif BODY_CONTAINABLE_METHODS.member?(@request_method)
287: raise HTTPStatus::LengthRequired
288: end
289: return @body
290: end
# File lib/webrick/httprequest.rb, line 292
292: def read_chunk_size(socket)
293: line = read_line(socket)
294: if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line
295: chunk_size = $1.hex
296: chunk_ext = $2
297: [ chunk_size, chunk_ext ]
298: else
299: raise HTTPStatus::BadRequest, "bad chunk `#{line}'."
300: end
301: end
# File lib/webrick/httprequest.rb, line 303
303: def read_chunked(socket, block)
304: chunk_size, = read_chunk_size(socket)
305: while chunk_size > 0
306: data = ""
307: while data.size < chunk_size
308: tmp = read_data(socket, chunk_size-data.size) # read chunk-data
309: break unless tmp
310: data << tmp
311: end
312: if data.nil? || data.size != chunk_size
313: raise BadRequest, "bad chunk data size."
314: end
315: read_line(socket) # skip CRLF
316: block.call(data)
317: chunk_size, = read_chunk_size(socket)
318: end
319: read_header(socket) # trailer + CRLF
320: @header.delete("transfer-encoding")
321: @remaining_size = 0
322: end
# File lib/webrick/httprequest.rb, line 340
340: def read_data(io, size)
341: _read_data(io, :read, size)
342: end
# File lib/webrick/httprequest.rb, line 238
238: def read_header(socket)
239: if socket
240: while line = read_line(socket)
241: break if /\A(#{CRLF}|#{LF})\z/om =~ line
242: @raw_header << line
243: end
244: end
245: @header = HTTPUtils::parse_header(@raw_header.join)
246: end
# File lib/webrick/httprequest.rb, line 336
336: def read_line(io)
337: _read_data(io, :gets, LF)
338: end
# File lib/webrick/httprequest.rb, line 224
224: def read_request_line(socket)
225: @request_line = read_line(socket) if socket
226: @request_time = Time.now
227: raise HTTPStatus::EOFError unless @request_line
228: if /^(\S+)\s+(\S+?)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
229: @request_method = $1
230: @unparsed_uri = $2
231: @http_version = HTTPVersion.new($3 ? $3 : "0.9")
232: else
233: rl = @request_line.sub(/\x0d?\x0a\z/o, '')
234: raise HTTPStatus::BadRequest, "bad Request-Line `#{rl}'."
235: end
236: end