| 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 45
45: def meta_vars
46: meta = orig_meta_vars
47: if @server_cert
48: meta["HTTPS"] = "on"
49: meta["SSL_SERVER_CERT"] = @server_cert.to_pem
50: meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : ""
51: if @client_cert_chain
52: @client_cert_chain.each_with_index{|cert, i|
53: meta["SSL_CLIENT_CERT_CHAIN_#{i}"] = cert.to_pem
54: }
55: end
56: meta["SSL_CIPHER"] = @cipher[0]
57: meta["SSL_PROTOCOL"] = @cipher[1]
58: meta["SSL_CIPHER_USEKEYSIZE"] = @cipher[2].to_s
59: meta["SSL_CIPHER_ALGKEYSIZE"] = @cipher[3].to_s
60: end
61: meta
62: 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: @cipher = @server_cert = @client_cert = nil
25: if socket.respond_to?(:cert)
26: @server_cert = socket.cert || @config[:SSLCertificate]
27: @client_cert = socket.peer_cert
28: @client_cert_chain = socket.peer_cert_chain
29: @cipher = socket.cipher
30: end
31: orig_parse(socket)
32: end
# File lib/webrick/https.rb, line 36
36: def parse_uri(str, scheme="https")
37: if @server_cert
38: return orig_parse_uri(str, scheme)
39: end
40: return orig_parse_uri(str)
41: 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 325
325: def _read_data(io, method, arg)
326: begin
327: timeout(@config[:RequestTimeout]){
328: return io.__send__(method, arg)
329: }
330: rescue Errno::ECONNRESET
331: return nil
332: rescue TimeoutError
333: raise HTTPStatus::RequestTimeout
334: end
335: end
# File lib/webrick/httprequest.rb, line 345
345: def parse_query()
346: begin
347: if @request_method == "GET" || @request_method == "HEAD"
348: @query = HTTPUtils::parse_query(@query_string)
349: elsif self['content-type'] =~ /^application\/x-www-form-urlencoded/
350: @query = HTTPUtils::parse_query(body)
351: elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/
352: boundary = HTTPUtils::dequote($1)
353: @query = HTTPUtils::parse_form_data(body, boundary)
354: else
355: @query = Hash.new
356: end
357: rescue => ex
358: raise HTTPStatus::BadRequest, ex.message
359: end
360: 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: str.sub!(%r{\A/+}o, '/')
253: uri = URI::parse(str)
254: return uri if uri.absolute?
255: if self["host"]
256: pattern = /\A(#{URI::REGEXP::PATTERN::HOST})(?::(\d+))?\z/n
257: host, port = *self['host'].scan(pattern)[0]
258: elsif @addr.size > 0
259: host, port = @addr[2], @addr[1]
260: else
261: host, port = @config[:ServerName], @config[:Port]
262: end
263: uri.scheme = scheme
264: uri.host = host
265: uri.port = port ? port.to_i : nil
266: return URI::parse(uri.to_s)
267: end
# File lib/webrick/httprequest.rb, line 269
269: def read_body(socket, block)
270: return unless socket
271: if tc = self['transfer-encoding']
272: case tc
273: when /chunked/io then read_chunked(socket, block)
274: else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
275: end
276: elsif self['content-length'] || @remaining_size
277: @remaining_size ||= self['content-length'].to_i
278: while @remaining_size > 0
279: sz = BUFSIZE < @remaining_size ? BUFSIZE : @remaining_size
280: break unless buf = read_data(socket, sz)
281: @remaining_size -= buf.size
282: block.call(buf)
283: end
284: if @remaining_size > 0 && @socket.eof?
285: raise HTTPStatus::BadRequest, "invalid body size."
286: end
287: elsif BODY_CONTAINABLE_METHODS.member?(@request_method)
288: raise HTTPStatus::LengthRequired
289: end
290: return @body
291: end
# File lib/webrick/httprequest.rb, line 293
293: def read_chunk_size(socket)
294: line = read_line(socket)
295: if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line
296: chunk_size = $1.hex
297: chunk_ext = $2
298: [ chunk_size, chunk_ext ]
299: else
300: raise HTTPStatus::BadRequest, "bad chunk `#{line}'."
301: end
302: end
# File lib/webrick/httprequest.rb, line 304
304: def read_chunked(socket, block)
305: chunk_size, = read_chunk_size(socket)
306: while chunk_size > 0
307: data = ""
308: while data.size < chunk_size
309: tmp = read_data(socket, chunk_size-data.size) # read chunk-data
310: break unless tmp
311: data << tmp
312: end
313: if data.nil? || data.size != chunk_size
314: raise BadRequest, "bad chunk data size."
315: end
316: read_line(socket) # skip CRLF
317: block.call(data)
318: chunk_size, = read_chunk_size(socket)
319: end
320: read_header(socket) # trailer + CRLF
321: @header.delete("transfer-encoding")
322: @remaining_size = 0
323: end
# File lib/webrick/httprequest.rb, line 341
341: def read_data(io, size)
342: _read_data(io, :read, size)
343: 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 337
337: def read_line(io)
338: _read_data(io, :gets, LF)
339: 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