source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/webrick/httprequest.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 10.0 KB
Line 
1#
2# httprequest.rb -- HTTPRequest Class
3#
4# Author: IPR -- Internet Programming with Ruby -- writers
5# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7# reserved.
8#
9# $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $
10
11require 'timeout'
12require 'uri'
13
14require 'webrick/httpversion'
15require 'webrick/httpstatus'
16require 'webrick/httputils'
17require 'webrick/cookie'
18
19module WEBrick
20
21 class HTTPRequest
22 BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ]
23 BUFSIZE = 1024*4
24
25 # Request line
26 attr_reader :request_line
27 attr_reader :request_method, :unparsed_uri, :http_version
28
29 # Request-URI
30 attr_reader :request_uri, :host, :port, :path
31 attr_accessor :script_name, :path_info, :query_string
32
33 # Header and entity body
34 attr_reader :raw_header, :header, :cookies
35 attr_reader :accept, :accept_charset
36 attr_reader :accept_encoding, :accept_language
37
38 # Misc
39 attr_accessor :user
40 attr_reader :addr, :peeraddr
41 attr_reader :attributes
42 attr_reader :keep_alive
43 attr_reader :request_time
44
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
76
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
123
124 def body(&block)
125 block ||= Proc.new{|chunk| @body << chunk }
126 read_body(@socket, block)
127 @body.empty? ? nil : @body
128 end
129
130 def query
131 unless @query
132 parse_query()
133 end
134 @query
135 end
136
137 def content_length
138 return Integer(self['content-length'])
139 end
140
141 def content_type
142 return self['content-type']
143 end
144
145 def [](header_name)
146 if @header
147 value = @header[header_name.downcase]
148 value.empty? ? nil : value.join(", ")
149 end
150 end
151
152 def each
153 @header.each{|k, v|
154 value = @header[k]
155 yield(k, value.empty? ? nil : value.join(", "))
156 }
157 end
158
159 def keep_alive?
160 @keep_alive
161 end
162
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
170
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
182
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
221
222 private
223
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
237
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 begin
246 @header = HTTPUtils::parse_header(@raw_header)
247 rescue => ex
248 raise HTTPStatus::BadRequest, ex.message
249 end
250 end
251
252 def parse_uri(str, scheme="http")
253 if @config[:Escape8bitURI]
254 str = HTTPUtils::escape8bit(str)
255 end
256 uri = URI::parse(str)
257 return uri if uri.absolute?
258 if self["host"]
259 pattern = /\A(#{URI::REGEXP::PATTERN::HOST})(?::(\d+))?\z/n
260 host, port = *self['host'].scan(pattern)[0]
261 elsif @addr.size > 0
262 host, port = @addr[2], @addr[1]
263 else
264 host, port = @config[:ServerName], @config[:Port]
265 end
266 uri.scheme = scheme
267 uri.host = host
268 uri.port = port ? port.to_i : nil
269 return URI::parse(uri.to_s)
270 end
271
272 def read_body(socket, block)
273 return unless socket
274 if tc = self['transfer-encoding']
275 case tc
276 when /chunked/io then read_chunked(socket, block)
277 else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
278 end
279 elsif self['content-length'] || @remaining_size
280 @remaining_size ||= self['content-length'].to_i
281 while @remaining_size > 0
282 sz = BUFSIZE < @remaining_size ? BUFSIZE : @remaining_size
283 break unless buf = read_data(socket, sz)
284 @remaining_size -= buf.size
285 block.call(buf)
286 end
287 if @remaining_size > 0 && @socket.eof?
288 raise HTTPStatus::BadRequest, "invalid body size."
289 end
290 elsif BODY_CONTAINABLE_METHODS.member?(@request_method)
291 raise HTTPStatus::LengthRequired
292 end
293 return @body
294 end
295
296 def read_chunk_size(socket)
297 line = read_line(socket)
298 if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line
299 chunk_size = $1.hex
300 chunk_ext = $2
301 [ chunk_size, chunk_ext ]
302 else
303 raise HTTPStatus::BadRequest, "bad chunk `#{line}'."
304 end
305 end
306
307 def read_chunked(socket, block)
308 chunk_size, = read_chunk_size(socket)
309 while chunk_size > 0
310 data = ""
311 while data.size < chunk_size
312 tmp = read_data(socket, chunk_size-data.size) # read chunk-data
313 break unless tmp
314 data << tmp
315 end
316 if data.nil? || data.size != chunk_size
317 raise BadRequest, "bad chunk data size."
318 end
319 read_line(socket) # skip CRLF
320 block.call(data)
321 chunk_size, = read_chunk_size(socket)
322 end
323 read_header(socket) # trailer + CRLF
324 @header.delete("transfer-encoding")
325 @remaining_size = 0
326 end
327
328 def _read_data(io, method, arg)
329 begin
330 timeout(@config[:RequestTimeout]){
331 return io.__send__(method, arg)
332 }
333 rescue Errno::ECONNRESET
334 return nil
335 rescue TimeoutError
336 raise HTTPStatus::RequestTimeout
337 end
338 end
339
340 def read_line(io)
341 _read_data(io, :gets, LF)
342 end
343
344 def read_data(io, size)
345 _read_data(io, :read, size)
346 end
347
348 def parse_query()
349 begin
350 if @request_method == "GET" || @request_method == "HEAD"
351 @query = HTTPUtils::parse_query(@query_string)
352 elsif self['content-type'] =~ /^application\/x-www-form-urlencoded/
353 @query = HTTPUtils::parse_query(body)
354 elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/
355 boundary = HTTPUtils::dequote($1)
356 @query = HTTPUtils::parse_form_data(body, boundary)
357 else
358 @query = Hash.new
359 end
360 rescue => ex
361 raise HTTPStatus::BadRequest, ex.message
362 end
363 end
364 end
365end
Note: See TracBrowser for help on using the repository browser.