1 | #
|
---|
2 | # = net/http.rb
|
---|
3 | #
|
---|
4 | # Copyright (c) 1999-2006 Yukihiro Matsumoto
|
---|
5 | # Copyright (c) 1999-2006 Minero Aoki
|
---|
6 | # Copyright (c) 2001 GOTOU Yuuzou
|
---|
7 | #
|
---|
8 | # Written and maintained by Minero Aoki <[email protected]>.
|
---|
9 | # HTTPS support added by GOTOU Yuuzou <[email protected]>.
|
---|
10 | #
|
---|
11 | # This file is derived from "http-access.rb".
|
---|
12 | #
|
---|
13 | # Documented by Minero Aoki; converted to RDoc by William Webber.
|
---|
14 | #
|
---|
15 | # This program is free software. You can re-distribute and/or
|
---|
16 | # modify this program under the same terms of ruby itself ---
|
---|
17 | # Ruby Distribution License or GNU General Public License.
|
---|
18 | #
|
---|
19 | # See Net::HTTP for an overview and examples.
|
---|
20 | #
|
---|
21 | # NOTE: You can find Japanese version of this document here:
|
---|
22 | # http://www.ruby-lang.org/ja/man/?cmd=view;name=net%2Fhttp.rb
|
---|
23 | #
|
---|
24 | #--
|
---|
25 | # $Id: http.rb 11708 2007-02-12 23:01:19Z shyouhei $
|
---|
26 | #++
|
---|
27 |
|
---|
28 | require 'net/protocol'
|
---|
29 | require 'uri'
|
---|
30 |
|
---|
31 | module Net #:nodoc:
|
---|
32 |
|
---|
33 | # :stopdoc:
|
---|
34 | class HTTPBadResponse < StandardError; end
|
---|
35 | class HTTPHeaderSyntaxError < StandardError; end
|
---|
36 | # :startdoc:
|
---|
37 |
|
---|
38 | # == What Is This Library?
|
---|
39 | #
|
---|
40 | # This library provides your program functions to access WWW
|
---|
41 | # documents via HTTP, Hyper Text Transfer Protocol version 1.1.
|
---|
42 | # For details of HTTP, refer [RFC2616]
|
---|
43 | # (http://www.ietf.org/rfc/rfc2616.txt).
|
---|
44 | #
|
---|
45 | # == Examples
|
---|
46 | #
|
---|
47 | # === Getting Document From WWW Server
|
---|
48 | #
|
---|
49 | # Example #1: Simple GET+print
|
---|
50 | #
|
---|
51 | # require 'net/http'
|
---|
52 | # Net::HTTP.get_print 'www.example.com', '/index.html'
|
---|
53 | #
|
---|
54 | # Example #2: Simple GET+print by URL
|
---|
55 | #
|
---|
56 | # require 'net/http'
|
---|
57 | # require 'uri'
|
---|
58 | # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
|
---|
59 | #
|
---|
60 | # Example #3: More generic GET+print
|
---|
61 | #
|
---|
62 | # require 'net/http'
|
---|
63 | # require 'uri'
|
---|
64 | #
|
---|
65 | # url = URI.parse('http://www.example.com/index.html')
|
---|
66 | # res = Net::HTTP.start(url.host, url.port) {|http|
|
---|
67 | # http.get('/index.html')
|
---|
68 | # }
|
---|
69 | # puts res.body
|
---|
70 | #
|
---|
71 | # Example #4: More generic GET+print
|
---|
72 | #
|
---|
73 | # require 'net/http'
|
---|
74 | #
|
---|
75 | # url = URI.parse('http://www.example.com/index.html')
|
---|
76 | # req = Net::HTTP::Get.new(url.path)
|
---|
77 | # res = Net::HTTP.start(url.host, url.port) {|http|
|
---|
78 | # http.request(req)
|
---|
79 | # }
|
---|
80 | # puts res.body
|
---|
81 | #
|
---|
82 | # === Posting Form Data
|
---|
83 | #
|
---|
84 | # require 'net/http'
|
---|
85 | # require 'uri'
|
---|
86 | #
|
---|
87 | # #1: Simple POST
|
---|
88 | # res = Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'),
|
---|
89 | # {'q'=>'ruby', 'max'=>'50'})
|
---|
90 | # puts res.body
|
---|
91 | #
|
---|
92 | # #2: POST with basic authentication
|
---|
93 | # res = Net::HTTP.post_form(URI.parse('http://jack:[email protected]/todo.cgi'),
|
---|
94 | # {'from'=>'2005-01-01', 'to'=>'2005-03-31'})
|
---|
95 | # puts res.body
|
---|
96 | #
|
---|
97 | # #3: Detailed control
|
---|
98 | # url = URI.parse('http://www.example.com/todo.cgi')
|
---|
99 | # req = Net::HTTP::Post.new(url.path)
|
---|
100 | # req.basic_auth 'jack', 'pass'
|
---|
101 | # req.set_form_data({'from'=>'2005-01-01', 'to'=>'2005-03-31'}, ';')
|
---|
102 | # res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
|
---|
103 | # case res
|
---|
104 | # when Net::HTTPSuccess, Net::HTTPRedirection
|
---|
105 | # # OK
|
---|
106 | # else
|
---|
107 | # res.error!
|
---|
108 | # end
|
---|
109 | #
|
---|
110 | # === Accessing via Proxy
|
---|
111 | #
|
---|
112 | # Net::HTTP.Proxy creates http proxy class. It has same
|
---|
113 | # methods of Net::HTTP but its instances always connect to
|
---|
114 | # proxy, instead of given host.
|
---|
115 | #
|
---|
116 | # require 'net/http'
|
---|
117 | #
|
---|
118 | # proxy_addr = 'your.proxy.host'
|
---|
119 | # proxy_port = 8080
|
---|
120 | # :
|
---|
121 | # Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com') {|http|
|
---|
122 | # # always connect to your.proxy.addr:8080
|
---|
123 | # :
|
---|
124 | # }
|
---|
125 | #
|
---|
126 | # Since Net::HTTP.Proxy returns Net::HTTP itself when proxy_addr is nil,
|
---|
127 | # there's no need to change code if there's proxy or not.
|
---|
128 | #
|
---|
129 | # There are two additional parameters in Net::HTTP.Proxy which allow to
|
---|
130 | # specify proxy user name and password:
|
---|
131 | #
|
---|
132 | # Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user = nil, proxy_pass = nil)
|
---|
133 | #
|
---|
134 | # You may use them to work with authorization-enabled proxies:
|
---|
135 | #
|
---|
136 | # require 'net/http'
|
---|
137 | # require 'uri'
|
---|
138 | #
|
---|
139 | # proxy_host = 'your.proxy.host'
|
---|
140 | # proxy_port = 8080
|
---|
141 | # uri = URI.parse(ENV['http_proxy'])
|
---|
142 | # proxy_user, proxy_pass = uri.userinfo.split(/:/) if uri.userinfo
|
---|
143 | # Net::HTTP::Proxy(proxy_host, proxy_port,
|
---|
144 | # proxy_user, proxy_pass).start('www.example.com') {|http|
|
---|
145 | # # always connect to your.proxy.addr:8080 using specified username and password
|
---|
146 | # :
|
---|
147 | # }
|
---|
148 | #
|
---|
149 | # Note that net/http never rely on HTTP_PROXY environment variable.
|
---|
150 | # If you want to use proxy, set it explicitly.
|
---|
151 | #
|
---|
152 | # === Following Redirection
|
---|
153 | #
|
---|
154 | # require 'net/http'
|
---|
155 | # require 'uri'
|
---|
156 | #
|
---|
157 | # def fetch(uri_str, limit = 10)
|
---|
158 | # # You should choose better exception.
|
---|
159 | # raise ArgumentError, 'HTTP redirect too deep' if limit == 0
|
---|
160 | #
|
---|
161 | # response = Net::HTTP.get_response(URI.parse(uri_str))
|
---|
162 | # case response
|
---|
163 | # when Net::HTTPSuccess then response
|
---|
164 | # when Net::HTTPRedirection then fetch(response['location'], limit - 1)
|
---|
165 | # else
|
---|
166 | # response.error!
|
---|
167 | # end
|
---|
168 | # end
|
---|
169 | #
|
---|
170 | # print fetch('http://www.ruby-lang.org')
|
---|
171 | #
|
---|
172 | # Net::HTTPSuccess and Net::HTTPRedirection is a HTTPResponse class.
|
---|
173 | # All HTTPResponse objects belong to its own response class which
|
---|
174 | # indicate HTTP result status. For details of response classes,
|
---|
175 | # see section "HTTP Response Classes".
|
---|
176 | #
|
---|
177 | # === Basic Authentication
|
---|
178 | #
|
---|
179 | # require 'net/http'
|
---|
180 | #
|
---|
181 | # Net::HTTP.start('www.example.com') {|http|
|
---|
182 | # req = Net::HTTP::Get.new('/secret-page.html')
|
---|
183 | # req.basic_auth 'account', 'password'
|
---|
184 | # response = http.request(req)
|
---|
185 | # print response.body
|
---|
186 | # }
|
---|
187 | #
|
---|
188 | # === HTTP Request Classes
|
---|
189 | #
|
---|
190 | # Here is HTTP request class hierarchy.
|
---|
191 | #
|
---|
192 | # Net::HTTPRequest
|
---|
193 | # Net::HTTP::Get
|
---|
194 | # Net::HTTP::Head
|
---|
195 | # Net::HTTP::Post
|
---|
196 | # Net::HTTP::Put
|
---|
197 | # Net::HTTP::Proppatch
|
---|
198 | # Net::HTTP::Lock
|
---|
199 | # Net::HTTP::Unlock
|
---|
200 | # Net::HTTP::Options
|
---|
201 | # Net::HTTP::Propfind
|
---|
202 | # Net::HTTP::Delete
|
---|
203 | # Net::HTTP::Move
|
---|
204 | # Net::HTTP::Copy
|
---|
205 | # Net::HTTP::Mkcol
|
---|
206 | # Net::HTTP::Trace
|
---|
207 | #
|
---|
208 | # === HTTP Response Classes
|
---|
209 | #
|
---|
210 | # Here is HTTP response class hierarchy.
|
---|
211 | # All classes are defined in Net module.
|
---|
212 | #
|
---|
213 | # HTTPResponse
|
---|
214 | # HTTPUnknownResponse
|
---|
215 | # HTTPInformation # 1xx
|
---|
216 | # HTTPContinue # 100
|
---|
217 | # HTTPSwitchProtocl # 101
|
---|
218 | # HTTPSuccess # 2xx
|
---|
219 | # HTTPOK # 200
|
---|
220 | # HTTPCreated # 201
|
---|
221 | # HTTPAccepted # 202
|
---|
222 | # HTTPNonAuthoritativeInformation # 203
|
---|
223 | # HTTPNoContent # 204
|
---|
224 | # HTTPResetContent # 205
|
---|
225 | # HTTPPartialContent # 206
|
---|
226 | # HTTPRedirection # 3xx
|
---|
227 | # HTTPMultipleChoice # 300
|
---|
228 | # HTTPMovedPermanently # 301
|
---|
229 | # HTTPFound # 302
|
---|
230 | # HTTPSeeOther # 303
|
---|
231 | # HTTPNotModified # 304
|
---|
232 | # HTTPUseProxy # 305
|
---|
233 | # HTTPTemporaryRedirect # 307
|
---|
234 | # HTTPClientError # 4xx
|
---|
235 | # HTTPBadRequest # 400
|
---|
236 | # HTTPUnauthorized # 401
|
---|
237 | # HTTPPaymentRequired # 402
|
---|
238 | # HTTPForbidden # 403
|
---|
239 | # HTTPNotFound # 404
|
---|
240 | # HTTPMethodNotAllowed # 405
|
---|
241 | # HTTPNotAcceptable # 406
|
---|
242 | # HTTPProxyAuthenticationRequired # 407
|
---|
243 | # HTTPRequestTimeOut # 408
|
---|
244 | # HTTPConflict # 409
|
---|
245 | # HTTPGone # 410
|
---|
246 | # HTTPLengthRequired # 411
|
---|
247 | # HTTPPreconditionFailed # 412
|
---|
248 | # HTTPRequestEntityTooLarge # 413
|
---|
249 | # HTTPRequestURITooLong # 414
|
---|
250 | # HTTPUnsupportedMediaType # 415
|
---|
251 | # HTTPRequestedRangeNotSatisfiable # 416
|
---|
252 | # HTTPExpectationFailed # 417
|
---|
253 | # HTTPServerError # 5xx
|
---|
254 | # HTTPInternalServerError # 500
|
---|
255 | # HTTPNotImplemented # 501
|
---|
256 | # HTTPBadGateway # 502
|
---|
257 | # HTTPServiceUnavailable # 503
|
---|
258 | # HTTPGatewayTimeOut # 504
|
---|
259 | # HTTPVersionNotSupported # 505
|
---|
260 | #
|
---|
261 | # == Switching Net::HTTP versions
|
---|
262 | #
|
---|
263 | # You can use net/http.rb 1.1 features (bundled with Ruby 1.6)
|
---|
264 | # by calling HTTP.version_1_1. Calling Net::HTTP.version_1_2
|
---|
265 | # allows you to use 1.2 features again.
|
---|
266 | #
|
---|
267 | # # example
|
---|
268 | # Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
|
---|
269 | #
|
---|
270 | # Net::HTTP.version_1_1
|
---|
271 | # Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
|
---|
272 | #
|
---|
273 | # Net::HTTP.version_1_2
|
---|
274 | # Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
|
---|
275 | #
|
---|
276 | # This function is NOT thread-safe.
|
---|
277 | #
|
---|
278 | class HTTP < Protocol
|
---|
279 |
|
---|
280 | # :stopdoc:
|
---|
281 | Revision = %q$Revision: 11708 $.split[1]
|
---|
282 | HTTPVersion = '1.1'
|
---|
283 | @newimpl = true
|
---|
284 | # :startdoc:
|
---|
285 |
|
---|
286 | # Turns on net/http 1.2 (ruby 1.8) features.
|
---|
287 | # Defaults to ON in ruby 1.8.
|
---|
288 | #
|
---|
289 | # I strongly recommend to call this method always.
|
---|
290 | #
|
---|
291 | # require 'net/http'
|
---|
292 | # Net::HTTP.version_1_2
|
---|
293 | #
|
---|
294 | def HTTP.version_1_2
|
---|
295 | @newimpl = true
|
---|
296 | end
|
---|
297 |
|
---|
298 | # Turns on net/http 1.1 (ruby 1.6) features.
|
---|
299 | # Defaults to OFF in ruby 1.8.
|
---|
300 | def HTTP.version_1_1
|
---|
301 | @newimpl = false
|
---|
302 | end
|
---|
303 |
|
---|
304 | # true if net/http is in version 1.2 mode.
|
---|
305 | # Defaults to true.
|
---|
306 | def HTTP.version_1_2?
|
---|
307 | @newimpl
|
---|
308 | end
|
---|
309 |
|
---|
310 | # true if net/http is in version 1.1 compatible mode.
|
---|
311 | # Defaults to true.
|
---|
312 | def HTTP.version_1_1?
|
---|
313 | not @newimpl
|
---|
314 | end
|
---|
315 |
|
---|
316 | class << HTTP
|
---|
317 | alias is_version_1_1? version_1_1? #:nodoc:
|
---|
318 | alias is_version_1_2? version_1_2? #:nodoc:
|
---|
319 | end
|
---|
320 |
|
---|
321 | #
|
---|
322 | # short cut methods
|
---|
323 | #
|
---|
324 |
|
---|
325 | #
|
---|
326 | # Get body from target and output it to +$stdout+. The
|
---|
327 | # target can either be specified as (+uri+), or as
|
---|
328 | # (+host+, +path+, +port+ = 80); so:
|
---|
329 | #
|
---|
330 | # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
|
---|
331 | #
|
---|
332 | # or:
|
---|
333 | #
|
---|
334 | # Net::HTTP.get_print 'www.example.com', '/index.html'
|
---|
335 | #
|
---|
336 | def HTTP.get_print(uri_or_host, path = nil, port = nil)
|
---|
337 | get_response(uri_or_host, path, port) {|res|
|
---|
338 | res.read_body do |chunk|
|
---|
339 | $stdout.print chunk
|
---|
340 | end
|
---|
341 | }
|
---|
342 | nil
|
---|
343 | end
|
---|
344 |
|
---|
345 | # Send a GET request to the target and return the response
|
---|
346 | # as a string. The target can either be specified as
|
---|
347 | # (+uri+), or as (+host+, +path+, +port+ = 80); so:
|
---|
348 | #
|
---|
349 | # print Net::HTTP.get(URI.parse('http://www.example.com/index.html'))
|
---|
350 | #
|
---|
351 | # or:
|
---|
352 | #
|
---|
353 | # print Net::HTTP.get('www.example.com', '/index.html')
|
---|
354 | #
|
---|
355 | def HTTP.get(uri_or_host, path = nil, port = nil)
|
---|
356 | get_response(uri_or_host, path, port).body
|
---|
357 | end
|
---|
358 |
|
---|
359 | # Send a GET request to the target and return the response
|
---|
360 | # as a Net::HTTPResponse object. The target can either be specified as
|
---|
361 | # (+uri+), or as (+host+, +path+, +port+ = 80); so:
|
---|
362 | #
|
---|
363 | # res = Net::HTTP.get_response(URI.parse('http://www.example.com/index.html'))
|
---|
364 | # print res.body
|
---|
365 | #
|
---|
366 | # or:
|
---|
367 | #
|
---|
368 | # res = Net::HTTP.get_response('www.example.com', '/index.html')
|
---|
369 | # print res.body
|
---|
370 | #
|
---|
371 | def HTTP.get_response(uri_or_host, path = nil, port = nil, &block)
|
---|
372 | if path
|
---|
373 | host = uri_or_host
|
---|
374 | new(host, port || HTTP.default_port).start {|http|
|
---|
375 | return http.request_get(path, &block)
|
---|
376 | }
|
---|
377 | else
|
---|
378 | uri = uri_or_host
|
---|
379 | new(uri.host, uri.port).start {|http|
|
---|
380 | return http.request_get(uri.request_uri, &block)
|
---|
381 | }
|
---|
382 | end
|
---|
383 | end
|
---|
384 |
|
---|
385 | # Posts HTML form data to the +URL+.
|
---|
386 | # Form data must be represented as a Hash of String to String, e.g:
|
---|
387 | #
|
---|
388 | # { "cmd" => "search", "q" => "ruby", "max" => "50" }
|
---|
389 | #
|
---|
390 | # This method also does Basic Authentication iff +URL+.user exists.
|
---|
391 | #
|
---|
392 | # Example:
|
---|
393 | #
|
---|
394 | # require 'net/http'
|
---|
395 | # require 'uri'
|
---|
396 | #
|
---|
397 | # HTTP.post_form URI.parse('http://www.example.com/search.cgi'),
|
---|
398 | # { "q" => "ruby", "max" => "50" }
|
---|
399 | #
|
---|
400 | def HTTP.post_form(url, params)
|
---|
401 | req = Post.new(url.path)
|
---|
402 | req.form_data = params
|
---|
403 | req.basic_auth url.user, url.password if url.user
|
---|
404 | new(url.host, url.port).start {|http|
|
---|
405 | http.request(req)
|
---|
406 | }
|
---|
407 | end
|
---|
408 |
|
---|
409 | #
|
---|
410 | # HTTP session management
|
---|
411 | #
|
---|
412 |
|
---|
413 | # The default port to use for HTTP requests; defaults to 80.
|
---|
414 | def HTTP.default_port
|
---|
415 | http_default_port()
|
---|
416 | end
|
---|
417 |
|
---|
418 | # The default port to use for HTTP requests; defaults to 80.
|
---|
419 | def HTTP.http_default_port
|
---|
420 | 80
|
---|
421 | end
|
---|
422 |
|
---|
423 | # The default port to use for HTTPS requests; defaults to 443.
|
---|
424 | def HTTP.https_default_port
|
---|
425 | 443
|
---|
426 | end
|
---|
427 |
|
---|
428 | def HTTP.socket_type #:nodoc: obsolete
|
---|
429 | BufferedIO
|
---|
430 | end
|
---|
431 |
|
---|
432 | # creates a new Net::HTTP object and opens its TCP connection and
|
---|
433 | # HTTP session. If the optional block is given, the newly
|
---|
434 | # created Net::HTTP object is passed to it and closed when the
|
---|
435 | # block finishes. In this case, the return value of this method
|
---|
436 | # is the return value of the block. If no block is given, the
|
---|
437 | # return value of this method is the newly created Net::HTTP object
|
---|
438 | # itself, and the caller is responsible for closing it upon completion.
|
---|
439 | def HTTP.start(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil, &block) # :yield: +http+
|
---|
440 | new(address, port, p_addr, p_port, p_user, p_pass).start(&block)
|
---|
441 | end
|
---|
442 |
|
---|
443 | class << HTTP
|
---|
444 | alias newobj new
|
---|
445 | end
|
---|
446 |
|
---|
447 | # Creates a new Net::HTTP object.
|
---|
448 | # If +proxy_addr+ is given, creates an Net::HTTP object with proxy support.
|
---|
449 | # This method does not open the TCP connection.
|
---|
450 | def HTTP.new(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil)
|
---|
451 | h = Proxy(p_addr, p_port, p_user, p_pass).newobj(address, port)
|
---|
452 | h.instance_eval {
|
---|
453 | @newimpl = ::Net::HTTP.version_1_2?
|
---|
454 | }
|
---|
455 | h
|
---|
456 | end
|
---|
457 |
|
---|
458 | # Creates a new Net::HTTP object for the specified +address+.
|
---|
459 | # This method does not open the TCP connection.
|
---|
460 | def initialize(address, port = nil)
|
---|
461 | @address = address
|
---|
462 | @port = (port || HTTP.default_port)
|
---|
463 | @curr_http_version = HTTPVersion
|
---|
464 | @seems_1_0_server = false
|
---|
465 | @close_on_empty_response = false
|
---|
466 | @socket = nil
|
---|
467 | @started = false
|
---|
468 | @open_timeout = nil
|
---|
469 | @read_timeout = 60
|
---|
470 | @debug_output = nil
|
---|
471 | @use_ssl = false
|
---|
472 | @ssl_context = nil
|
---|
473 | end
|
---|
474 |
|
---|
475 | def inspect
|
---|
476 | "#<#{self.class} #{@address}:#{@port} open=#{started?}>"
|
---|
477 | end
|
---|
478 |
|
---|
479 | # *WARNING* This method causes serious security hole.
|
---|
480 | # Never use this method in production code.
|
---|
481 | #
|
---|
482 | # Set an output stream for debugging.
|
---|
483 | #
|
---|
484 | # http = Net::HTTP.new
|
---|
485 | # http.set_debug_output $stderr
|
---|
486 | # http.start { .... }
|
---|
487 | #
|
---|
488 | def set_debug_output(output)
|
---|
489 | warn 'Net::HTTP#set_debug_output called after HTTP started' if started?
|
---|
490 | @debug_output = output
|
---|
491 | end
|
---|
492 |
|
---|
493 | # The host name to connect to.
|
---|
494 | attr_reader :address
|
---|
495 |
|
---|
496 | # The port number to connect to.
|
---|
497 | attr_reader :port
|
---|
498 |
|
---|
499 | # Seconds to wait until connection is opened.
|
---|
500 | # If the HTTP object cannot open a connection in this many seconds,
|
---|
501 | # it raises a TimeoutError exception.
|
---|
502 | attr_accessor :open_timeout
|
---|
503 |
|
---|
504 | # Seconds to wait until reading one block (by one read(2) call).
|
---|
505 | # If the HTTP object cannot open a connection in this many seconds,
|
---|
506 | # it raises a TimeoutError exception.
|
---|
507 | attr_reader :read_timeout
|
---|
508 |
|
---|
509 | # Setter for the read_timeout attribute.
|
---|
510 | def read_timeout=(sec)
|
---|
511 | @socket.read_timeout = sec if @socket
|
---|
512 | @read_timeout = sec
|
---|
513 | end
|
---|
514 |
|
---|
515 | # returns true if the HTTP session is started.
|
---|
516 | def started?
|
---|
517 | @started
|
---|
518 | end
|
---|
519 |
|
---|
520 | alias active? started? #:nodoc: obsolete
|
---|
521 |
|
---|
522 | attr_accessor :close_on_empty_response
|
---|
523 |
|
---|
524 | # returns true if use SSL/TLS with HTTP.
|
---|
525 | def use_ssl?
|
---|
526 | false # redefined in net/https
|
---|
527 | end
|
---|
528 |
|
---|
529 | # Opens TCP connection and HTTP session.
|
---|
530 | #
|
---|
531 | # When this method is called with block, gives a HTTP object
|
---|
532 | # to the block and closes the TCP connection / HTTP session
|
---|
533 | # after the block executed.
|
---|
534 | #
|
---|
535 | # When called with a block, returns the return value of the
|
---|
536 | # block; otherwise, returns self.
|
---|
537 | #
|
---|
538 | def start # :yield: http
|
---|
539 | raise IOError, 'HTTP session already opened' if @started
|
---|
540 | if block_given?
|
---|
541 | begin
|
---|
542 | do_start
|
---|
543 | return yield(self)
|
---|
544 | ensure
|
---|
545 | do_finish
|
---|
546 | end
|
---|
547 | end
|
---|
548 | do_start
|
---|
549 | self
|
---|
550 | end
|
---|
551 |
|
---|
552 | def do_start
|
---|
553 | connect
|
---|
554 | @started = true
|
---|
555 | end
|
---|
556 | private :do_start
|
---|
557 |
|
---|
558 | def connect
|
---|
559 | D "opening connection to #{conn_address()}..."
|
---|
560 | s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
|
---|
561 | D "opened"
|
---|
562 | if use_ssl?
|
---|
563 | unless @ssl_context.verify_mode
|
---|
564 | warn "warning: peer certificate won't be verified in this SSL session"
|
---|
565 | @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
---|
566 | end
|
---|
567 | s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
|
---|
568 | s.sync_close = true
|
---|
569 | end
|
---|
570 | @socket = BufferedIO.new(s)
|
---|
571 | @socket.read_timeout = @read_timeout
|
---|
572 | @socket.debug_output = @debug_output
|
---|
573 | if use_ssl?
|
---|
574 | if proxy?
|
---|
575 | @socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
|
---|
576 | @address, @port, HTTPVersion)
|
---|
577 | @socket.writeline "Host: #{@address}:#{@port}"
|
---|
578 | if proxy_user
|
---|
579 | credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
|
---|
580 | credential.delete!("\r\n")
|
---|
581 | @socket.writeline "Proxy-Authorization: Basic #{credential}"
|
---|
582 | end
|
---|
583 | @socket.writeline ''
|
---|
584 | HTTPResponse.read_new(@socket).value
|
---|
585 | end
|
---|
586 | s.connect
|
---|
587 | end
|
---|
588 | on_connect
|
---|
589 | end
|
---|
590 | private :connect
|
---|
591 |
|
---|
592 | def on_connect
|
---|
593 | end
|
---|
594 | private :on_connect
|
---|
595 |
|
---|
596 | # Finishes HTTP session and closes TCP connection.
|
---|
597 | # Raises IOError if not started.
|
---|
598 | def finish
|
---|
599 | raise IOError, 'HTTP session not yet started' unless started?
|
---|
600 | do_finish
|
---|
601 | end
|
---|
602 |
|
---|
603 | def do_finish
|
---|
604 | @started = false
|
---|
605 | @socket.close if @socket and not @socket.closed?
|
---|
606 | @socket = nil
|
---|
607 | end
|
---|
608 | private :do_finish
|
---|
609 |
|
---|
610 | #
|
---|
611 | # proxy
|
---|
612 | #
|
---|
613 |
|
---|
614 | public
|
---|
615 |
|
---|
616 | # no proxy
|
---|
617 | @is_proxy_class = false
|
---|
618 | @proxy_addr = nil
|
---|
619 | @proxy_port = nil
|
---|
620 | @proxy_user = nil
|
---|
621 | @proxy_pass = nil
|
---|
622 |
|
---|
623 | # Creates an HTTP proxy class.
|
---|
624 | # Arguments are address/port of proxy host and username/password
|
---|
625 | # if authorization on proxy server is required.
|
---|
626 | # You can replace the HTTP class with created proxy class.
|
---|
627 | #
|
---|
628 | # If ADDRESS is nil, this method returns self (Net::HTTP).
|
---|
629 | #
|
---|
630 | # # Example
|
---|
631 | # proxy_class = Net::HTTP::Proxy('proxy.example.com', 8080)
|
---|
632 | # :
|
---|
633 | # proxy_class.start('www.ruby-lang.org') {|http|
|
---|
634 | # # connecting proxy.foo.org:8080
|
---|
635 | # :
|
---|
636 | # }
|
---|
637 | #
|
---|
638 | def HTTP.Proxy(p_addr, p_port = nil, p_user = nil, p_pass = nil)
|
---|
639 | return self unless p_addr
|
---|
640 | delta = ProxyDelta
|
---|
641 | proxyclass = Class.new(self)
|
---|
642 | proxyclass.module_eval {
|
---|
643 | include delta
|
---|
644 | # with proxy
|
---|
645 | @is_proxy_class = true
|
---|
646 | @proxy_address = p_addr
|
---|
647 | @proxy_port = p_port || default_port()
|
---|
648 | @proxy_user = p_user
|
---|
649 | @proxy_pass = p_pass
|
---|
650 | }
|
---|
651 | proxyclass
|
---|
652 | end
|
---|
653 |
|
---|
654 | class << HTTP
|
---|
655 | # returns true if self is a class which was created by HTTP::Proxy.
|
---|
656 | def proxy_class?
|
---|
657 | @is_proxy_class
|
---|
658 | end
|
---|
659 |
|
---|
660 | attr_reader :proxy_address
|
---|
661 | attr_reader :proxy_port
|
---|
662 | attr_reader :proxy_user
|
---|
663 | attr_reader :proxy_pass
|
---|
664 | end
|
---|
665 |
|
---|
666 | # True if self is a HTTP proxy class.
|
---|
667 | def proxy?
|
---|
668 | self.class.proxy_class?
|
---|
669 | end
|
---|
670 |
|
---|
671 | # Address of proxy host. If self does not use a proxy, nil.
|
---|
672 | def proxy_address
|
---|
673 | self.class.proxy_address
|
---|
674 | end
|
---|
675 |
|
---|
676 | # Port number of proxy host. If self does not use a proxy, nil.
|
---|
677 | def proxy_port
|
---|
678 | self.class.proxy_port
|
---|
679 | end
|
---|
680 |
|
---|
681 | # User name for accessing proxy. If self does not use a proxy, nil.
|
---|
682 | def proxy_user
|
---|
683 | self.class.proxy_user
|
---|
684 | end
|
---|
685 |
|
---|
686 | # User password for accessing proxy. If self does not use a proxy, nil.
|
---|
687 | def proxy_pass
|
---|
688 | self.class.proxy_pass
|
---|
689 | end
|
---|
690 |
|
---|
691 | alias proxyaddr proxy_address #:nodoc: obsolete
|
---|
692 | alias proxyport proxy_port #:nodoc: obsolete
|
---|
693 |
|
---|
694 | private
|
---|
695 |
|
---|
696 | # without proxy
|
---|
697 |
|
---|
698 | def conn_address
|
---|
699 | address()
|
---|
700 | end
|
---|
701 |
|
---|
702 | def conn_port
|
---|
703 | port()
|
---|
704 | end
|
---|
705 |
|
---|
706 | def edit_path(path)
|
---|
707 | path
|
---|
708 | end
|
---|
709 |
|
---|
710 | module ProxyDelta #:nodoc: internal use only
|
---|
711 | private
|
---|
712 |
|
---|
713 | def conn_address
|
---|
714 | proxy_address()
|
---|
715 | end
|
---|
716 |
|
---|
717 | def conn_port
|
---|
718 | proxy_port()
|
---|
719 | end
|
---|
720 |
|
---|
721 | def edit_path(path)
|
---|
722 | use_ssl? ? path : "http://#{addr_port()}#{path}"
|
---|
723 | end
|
---|
724 | end
|
---|
725 |
|
---|
726 | #
|
---|
727 | # HTTP operations
|
---|
728 | #
|
---|
729 |
|
---|
730 | public
|
---|
731 |
|
---|
732 | # Gets data from +path+ on the connected-to host.
|
---|
733 | # +header+ must be a Hash like { 'Accept' => '*/*', ... }.
|
---|
734 | #
|
---|
735 | # In version 1.1 (ruby 1.6), this method returns a pair of objects,
|
---|
736 | # a Net::HTTPResponse object and the entity body string.
|
---|
737 | # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse
|
---|
738 | # object.
|
---|
739 | #
|
---|
740 | # If called with a block, yields each fragment of the
|
---|
741 | # entity body in turn as a string as it is read from
|
---|
742 | # the socket. Note that in this case, the returned response
|
---|
743 | # object will *not* contain a (meaningful) body.
|
---|
744 | #
|
---|
745 | # +dest+ argument is obsolete.
|
---|
746 | # It still works but you must not use it.
|
---|
747 | #
|
---|
748 | # In version 1.1, this method might raise an exception for
|
---|
749 | # 3xx (redirect). In this case you can get a HTTPResponse object
|
---|
750 | # by "anException.response".
|
---|
751 | #
|
---|
752 | # In version 1.2, this method never raises exception.
|
---|
753 | #
|
---|
754 | # # version 1.1 (bundled with Ruby 1.6)
|
---|
755 | # response, body = http.get('/index.html')
|
---|
756 | #
|
---|
757 | # # version 1.2 (bundled with Ruby 1.8 or later)
|
---|
758 | # response = http.get('/index.html')
|
---|
759 | #
|
---|
760 | # # using block
|
---|
761 | # File.open('result.txt', 'w') {|f|
|
---|
762 | # http.get('/~foo/') do |str|
|
---|
763 | # f.write str
|
---|
764 | # end
|
---|
765 | # }
|
---|
766 | #
|
---|
767 | def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+
|
---|
768 | res = nil
|
---|
769 | request(Get.new(path, initheader)) {|r|
|
---|
770 | r.read_body dest, &block
|
---|
771 | res = r
|
---|
772 | }
|
---|
773 | unless @newimpl
|
---|
774 | res.value
|
---|
775 | return res, res.body
|
---|
776 | end
|
---|
777 |
|
---|
778 | res
|
---|
779 | end
|
---|
780 |
|
---|
781 | # Gets only the header from +path+ on the connected-to host.
|
---|
782 | # +header+ is a Hash like { 'Accept' => '*/*', ... }.
|
---|
783 | #
|
---|
784 | # This method returns a Net::HTTPResponse object.
|
---|
785 | #
|
---|
786 | # In version 1.1, this method might raise an exception for
|
---|
787 | # 3xx (redirect). On the case you can get a HTTPResponse object
|
---|
788 | # by "anException.response".
|
---|
789 | # In version 1.2, this method never raises an exception.
|
---|
790 | #
|
---|
791 | # response = nil
|
---|
792 | # Net::HTTP.start('some.www.server', 80) {|http|
|
---|
793 | # response = http.head('/index.html')
|
---|
794 | # }
|
---|
795 | # p response['content-type']
|
---|
796 | #
|
---|
797 | def head(path, initheader = nil)
|
---|
798 | res = request(Head.new(path, initheader))
|
---|
799 | res.value unless @newimpl
|
---|
800 | res
|
---|
801 | end
|
---|
802 |
|
---|
803 | # Posts +data+ (must be a String) to +path+. +header+ must be a Hash
|
---|
804 | # like { 'Accept' => '*/*', ... }.
|
---|
805 | #
|
---|
806 | # In version 1.1 (ruby 1.6), this method returns a pair of objects, a
|
---|
807 | # Net::HTTPResponse object and an entity body string.
|
---|
808 | # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse object.
|
---|
809 | #
|
---|
810 | # If called with a block, yields each fragment of the
|
---|
811 | # entity body in turn as a string as it are read from
|
---|
812 | # the socket. Note that in this case, the returned response
|
---|
813 | # object will *not* contain a (meaningful) body.
|
---|
814 | #
|
---|
815 | # +dest+ argument is obsolete.
|
---|
816 | # It still works but you must not use it.
|
---|
817 | #
|
---|
818 | # In version 1.1, this method might raise an exception for
|
---|
819 | # 3xx (redirect). In this case you can get an HTTPResponse object
|
---|
820 | # by "anException.response".
|
---|
821 | # In version 1.2, this method never raises exception.
|
---|
822 | #
|
---|
823 | # # version 1.1
|
---|
824 | # response, body = http.post('/cgi-bin/search.rb', 'query=foo')
|
---|
825 | #
|
---|
826 | # # version 1.2
|
---|
827 | # response = http.post('/cgi-bin/search.rb', 'query=foo')
|
---|
828 | #
|
---|
829 | # # using block
|
---|
830 | # File.open('result.txt', 'w') {|f|
|
---|
831 | # http.post('/cgi-bin/search.rb', 'query=foo') do |str|
|
---|
832 | # f.write str
|
---|
833 | # end
|
---|
834 | # }
|
---|
835 | #
|
---|
836 | # You should set Content-Type: header field for POST.
|
---|
837 | # If no Content-Type: field given, this method uses
|
---|
838 | # "application/x-www-form-urlencoded" by default.
|
---|
839 | #
|
---|
840 | def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
|
---|
841 | res = nil
|
---|
842 | request(Post.new(path, initheader), data) {|r|
|
---|
843 | r.read_body dest, &block
|
---|
844 | res = r
|
---|
845 | }
|
---|
846 | unless @newimpl
|
---|
847 | res.value
|
---|
848 | return res, res.body
|
---|
849 | end
|
---|
850 | res
|
---|
851 | end
|
---|
852 |
|
---|
853 | def put(path, data, initheader = nil) #:nodoc:
|
---|
854 | res = request(Put.new(path, initheader), data)
|
---|
855 | res.value unless @newimpl
|
---|
856 | res
|
---|
857 | end
|
---|
858 |
|
---|
859 | # Sends a PROPPATCH request to the +path+ and gets a response,
|
---|
860 | # as an HTTPResponse object.
|
---|
861 | def proppatch(path, body, initheader = nil)
|
---|
862 | request(Proppatch.new(path, initheader), body)
|
---|
863 | end
|
---|
864 |
|
---|
865 | # Sends a LOCK request to the +path+ and gets a response,
|
---|
866 | # as an HTTPResponse object.
|
---|
867 | def lock(path, body, initheader = nil)
|
---|
868 | request(Lock.new(path, initheader), body)
|
---|
869 | end
|
---|
870 |
|
---|
871 | # Sends a UNLOCK request to the +path+ and gets a response,
|
---|
872 | # as an HTTPResponse object.
|
---|
873 | def unlock(path, body, initheader = nil)
|
---|
874 | request(Unlock.new(path, initheader), body)
|
---|
875 | end
|
---|
876 |
|
---|
877 | # Sends a OPTIONS request to the +path+ and gets a response,
|
---|
878 | # as an HTTPResponse object.
|
---|
879 | def options(path, initheader = nil)
|
---|
880 | request(Options.new(path, initheader))
|
---|
881 | end
|
---|
882 |
|
---|
883 | # Sends a PROPFIND request to the +path+ and gets a response,
|
---|
884 | # as an HTTPResponse object.
|
---|
885 | def propfind(path, body = nil, initheader = {'Depth' => '0'})
|
---|
886 | request(Propfind.new(path, initheader), body)
|
---|
887 | end
|
---|
888 |
|
---|
889 | # Sends a DELETE request to the +path+ and gets a response,
|
---|
890 | # as an HTTPResponse object.
|
---|
891 | def delete(path, initheader = {'Depth' => 'Infinity'})
|
---|
892 | request(Delete.new(path, initheader))
|
---|
893 | end
|
---|
894 |
|
---|
895 | # Sends a MOVE request to the +path+ and gets a response,
|
---|
896 | # as an HTTPResponse object.
|
---|
897 | def move(path, initheader = nil)
|
---|
898 | request(Move.new(path, initheader))
|
---|
899 | end
|
---|
900 |
|
---|
901 | # Sends a COPY request to the +path+ and gets a response,
|
---|
902 | # as an HTTPResponse object.
|
---|
903 | def copy(path, initheader = nil)
|
---|
904 | request(Copy.new(path, initheader))
|
---|
905 | end
|
---|
906 |
|
---|
907 | # Sends a MKCOL request to the +path+ and gets a response,
|
---|
908 | # as an HTTPResponse object.
|
---|
909 | def mkcol(path, body = nil, initheader = nil)
|
---|
910 | request(Mkcol.new(path, initheader), body)
|
---|
911 | end
|
---|
912 |
|
---|
913 | # Sends a TRACE request to the +path+ and gets a response,
|
---|
914 | # as an HTTPResponse object.
|
---|
915 | def trace(path, initheader = nil)
|
---|
916 | request(Trace.new(path, initheader))
|
---|
917 | end
|
---|
918 |
|
---|
919 | # Sends a GET request to the +path+ and gets a response,
|
---|
920 | # as an HTTPResponse object.
|
---|
921 | #
|
---|
922 | # When called with a block, yields an HTTPResponse object.
|
---|
923 | # The body of this response will not have been read yet;
|
---|
924 | # the caller can process it using HTTPResponse#read_body,
|
---|
925 | # if desired.
|
---|
926 | #
|
---|
927 | # Returns the response.
|
---|
928 | #
|
---|
929 | # This method never raises Net::* exceptions.
|
---|
930 | #
|
---|
931 | # response = http.request_get('/index.html')
|
---|
932 | # # The entity body is already read here.
|
---|
933 | # p response['content-type']
|
---|
934 | # puts response.body
|
---|
935 | #
|
---|
936 | # # using block
|
---|
937 | # http.request_get('/index.html') {|response|
|
---|
938 | # p response['content-type']
|
---|
939 | # response.read_body do |str| # read body now
|
---|
940 | # print str
|
---|
941 | # end
|
---|
942 | # }
|
---|
943 | #
|
---|
944 | def request_get(path, initheader = nil, &block) # :yield: +response+
|
---|
945 | request(Get.new(path, initheader), &block)
|
---|
946 | end
|
---|
947 |
|
---|
948 | # Sends a HEAD request to the +path+ and gets a response,
|
---|
949 | # as an HTTPResponse object.
|
---|
950 | #
|
---|
951 | # Returns the response.
|
---|
952 | #
|
---|
953 | # This method never raises Net::* exceptions.
|
---|
954 | #
|
---|
955 | # response = http.request_head('/index.html')
|
---|
956 | # p response['content-type']
|
---|
957 | #
|
---|
958 | def request_head(path, initheader = nil, &block)
|
---|
959 | request(Head.new(path, initheader), &block)
|
---|
960 | end
|
---|
961 |
|
---|
962 | # Sends a POST request to the +path+ and gets a response,
|
---|
963 | # as an HTTPResponse object.
|
---|
964 | #
|
---|
965 | # When called with a block, yields an HTTPResponse object.
|
---|
966 | # The body of this response will not have been read yet;
|
---|
967 | # the caller can process it using HTTPResponse#read_body,
|
---|
968 | # if desired.
|
---|
969 | #
|
---|
970 | # Returns the response.
|
---|
971 | #
|
---|
972 | # This method never raises Net::* exceptions.
|
---|
973 | #
|
---|
974 | # # example
|
---|
975 | # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
|
---|
976 | # p response.status
|
---|
977 | # puts response.body # body is already read
|
---|
978 | #
|
---|
979 | # # using block
|
---|
980 | # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
|
---|
981 | # p response.status
|
---|
982 | # p response['content-type']
|
---|
983 | # response.read_body do |str| # read body now
|
---|
984 | # print str
|
---|
985 | # end
|
---|
986 | # }
|
---|
987 | #
|
---|
988 | def request_post(path, data, initheader = nil, &block) # :yield: +response+
|
---|
989 | request Post.new(path, initheader), data, &block
|
---|
990 | end
|
---|
991 |
|
---|
992 | def request_put(path, data, initheader = nil, &block) #:nodoc:
|
---|
993 | request Put.new(path, initheader), data, &block
|
---|
994 | end
|
---|
995 |
|
---|
996 | alias get2 request_get #:nodoc: obsolete
|
---|
997 | alias head2 request_head #:nodoc: obsolete
|
---|
998 | alias post2 request_post #:nodoc: obsolete
|
---|
999 | alias put2 request_put #:nodoc: obsolete
|
---|
1000 |
|
---|
1001 |
|
---|
1002 | # Sends an HTTP request to the HTTP server.
|
---|
1003 | # This method also sends DATA string if DATA is given.
|
---|
1004 | #
|
---|
1005 | # Returns a HTTPResponse object.
|
---|
1006 | #
|
---|
1007 | # This method never raises Net::* exceptions.
|
---|
1008 | #
|
---|
1009 | # response = http.send_request('GET', '/index.html')
|
---|
1010 | # puts response.body
|
---|
1011 | #
|
---|
1012 | def send_request(name, path, data = nil, header = nil)
|
---|
1013 | r = HTTPGenericRequest.new(name,(data ? true : false),true,path,header)
|
---|
1014 | request r, data
|
---|
1015 | end
|
---|
1016 |
|
---|
1017 | # Sends an HTTPRequest object REQUEST to the HTTP server.
|
---|
1018 | # This method also sends DATA string if REQUEST is a post/put request.
|
---|
1019 | # Giving DATA for get/head request causes ArgumentError.
|
---|
1020 | #
|
---|
1021 | # When called with a block, yields an HTTPResponse object.
|
---|
1022 | # The body of this response will not have been read yet;
|
---|
1023 | # the caller can process it using HTTPResponse#read_body,
|
---|
1024 | # if desired.
|
---|
1025 | #
|
---|
1026 | # Returns a HTTPResponse object.
|
---|
1027 | #
|
---|
1028 | # This method never raises Net::* exceptions.
|
---|
1029 | #
|
---|
1030 | def request(req, body = nil, &block) # :yield: +response+
|
---|
1031 | unless started?
|
---|
1032 | start {
|
---|
1033 | req['connection'] ||= 'close'
|
---|
1034 | return request(req, body, &block)
|
---|
1035 | }
|
---|
1036 | end
|
---|
1037 | if proxy_user()
|
---|
1038 | unless use_ssl?
|
---|
1039 | req.proxy_basic_auth proxy_user(), proxy_pass()
|
---|
1040 | end
|
---|
1041 | end
|
---|
1042 |
|
---|
1043 | req.set_body_internal body
|
---|
1044 | begin_transport req
|
---|
1045 | req.exec @socket, @curr_http_version, edit_path(req.path)
|
---|
1046 | begin
|
---|
1047 | res = HTTPResponse.read_new(@socket)
|
---|
1048 | end while res.kind_of?(HTTPContinue)
|
---|
1049 | res.reading_body(@socket, req.response_body_permitted?) {
|
---|
1050 | yield res if block_given?
|
---|
1051 | }
|
---|
1052 | end_transport req, res
|
---|
1053 |
|
---|
1054 | res
|
---|
1055 | end
|
---|
1056 |
|
---|
1057 | private
|
---|
1058 |
|
---|
1059 | def begin_transport(req)
|
---|
1060 | if @socket.closed?
|
---|
1061 | connect
|
---|
1062 | end
|
---|
1063 | if @seems_1_0_server
|
---|
1064 | req['connection'] ||= 'close'
|
---|
1065 | end
|
---|
1066 | if not req.response_body_permitted? and @close_on_empty_response
|
---|
1067 | req['connection'] ||= 'close'
|
---|
1068 | end
|
---|
1069 | req['host'] ||= addr_port()
|
---|
1070 | end
|
---|
1071 |
|
---|
1072 | def end_transport(req, res)
|
---|
1073 | @curr_http_version = res.http_version
|
---|
1074 | if not res.body and @close_on_empty_response
|
---|
1075 | D 'Conn close'
|
---|
1076 | @socket.close
|
---|
1077 | elsif keep_alive?(req, res)
|
---|
1078 | D 'Conn keep-alive'
|
---|
1079 | if @socket.closed?
|
---|
1080 | D 'Conn (but seems 1.0 server)'
|
---|
1081 | @seems_1_0_server = true
|
---|
1082 | end
|
---|
1083 | else
|
---|
1084 | D 'Conn close'
|
---|
1085 | @socket.close
|
---|
1086 | end
|
---|
1087 | end
|
---|
1088 |
|
---|
1089 | def keep_alive?(req, res)
|
---|
1090 | return false if /close/i =~ req['connection'].to_s
|
---|
1091 | return false if @seems_1_0_server
|
---|
1092 | return true if /keep-alive/i =~ res['connection'].to_s
|
---|
1093 | return false if /close/i =~ res['connection'].to_s
|
---|
1094 | return true if /keep-alive/i =~ res['proxy-connection'].to_s
|
---|
1095 | return false if /close/i =~ res['proxy-connection'].to_s
|
---|
1096 | (@curr_http_version == '1.1')
|
---|
1097 | end
|
---|
1098 |
|
---|
1099 | #
|
---|
1100 | # utils
|
---|
1101 | #
|
---|
1102 |
|
---|
1103 | private
|
---|
1104 |
|
---|
1105 | def addr_port
|
---|
1106 | if use_ssl?
|
---|
1107 | address() + (port == HTTP.https_default_port ? '' : ":#{port()}")
|
---|
1108 | else
|
---|
1109 | address() + (port == HTTP.http_default_port ? '' : ":#{port()}")
|
---|
1110 | end
|
---|
1111 | end
|
---|
1112 |
|
---|
1113 | def D(msg)
|
---|
1114 | return unless @debug_output
|
---|
1115 | @debug_output << msg
|
---|
1116 | @debug_output << "\n"
|
---|
1117 | end
|
---|
1118 |
|
---|
1119 | end
|
---|
1120 |
|
---|
1121 | HTTPSession = HTTP
|
---|
1122 |
|
---|
1123 |
|
---|
1124 | #
|
---|
1125 | # Header module.
|
---|
1126 | #
|
---|
1127 | # Provides access to @header in the mixed-into class as a hash-like
|
---|
1128 | # object, except with case-insensitive keys. Also provides
|
---|
1129 | # methods for accessing commonly-used header values in a more
|
---|
1130 | # convenient format.
|
---|
1131 | #
|
---|
1132 | module HTTPHeader
|
---|
1133 |
|
---|
1134 | def initialize_http_header(initheader)
|
---|
1135 | @header = {}
|
---|
1136 | return unless initheader
|
---|
1137 | initheader.each do |key, value|
|
---|
1138 | warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
|
---|
1139 | @header[key.downcase] = [value.strip]
|
---|
1140 | end
|
---|
1141 | end
|
---|
1142 |
|
---|
1143 | def size #:nodoc: obsolete
|
---|
1144 | @header.size
|
---|
1145 | end
|
---|
1146 |
|
---|
1147 | alias length size #:nodoc: obsolete
|
---|
1148 |
|
---|
1149 | # Returns the header field corresponding to the case-insensitive key.
|
---|
1150 | # For example, a key of "Content-Type" might return "text/html"
|
---|
1151 | def [](key)
|
---|
1152 | a = @header[key.downcase] or return nil
|
---|
1153 | a.join(', ')
|
---|
1154 | end
|
---|
1155 |
|
---|
1156 | # Sets the header field corresponding to the case-insensitive key.
|
---|
1157 | def []=(key, val)
|
---|
1158 | unless val
|
---|
1159 | @header.delete key.downcase
|
---|
1160 | return val
|
---|
1161 | end
|
---|
1162 | @header[key.downcase] = [val]
|
---|
1163 | end
|
---|
1164 |
|
---|
1165 | # [Ruby 1.8.3]
|
---|
1166 | # Adds header field instead of replace.
|
---|
1167 | # Second argument +val+ must be a String.
|
---|
1168 | # See also #[]=, #[] and #get_fields.
|
---|
1169 | #
|
---|
1170 | # request.add_field 'X-My-Header', 'a'
|
---|
1171 | # p request['X-My-Header'] #=> "a"
|
---|
1172 | # p request.get_fields('X-My-Header') #=> ["a"]
|
---|
1173 | # request.add_field 'X-My-Header', 'b'
|
---|
1174 | # p request['X-My-Header'] #=> "a, b"
|
---|
1175 | # p request.get_fields('X-My-Header') #=> ["a", "b"]
|
---|
1176 | # request.add_field 'X-My-Header', 'c'
|
---|
1177 | # p request['X-My-Header'] #=> "a, b, c"
|
---|
1178 | # p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
|
---|
1179 | #
|
---|
1180 | def add_field(key, val)
|
---|
1181 | if @header.key?(key.downcase)
|
---|
1182 | @header[key.downcase].push val
|
---|
1183 | else
|
---|
1184 | @header[key.downcase] = [val]
|
---|
1185 | end
|
---|
1186 | end
|
---|
1187 |
|
---|
1188 | # [Ruby 1.8.3]
|
---|
1189 | # Returns an array of header field strings corresponding to the
|
---|
1190 | # case-insensitive +key+. This method allows you to get duplicated
|
---|
1191 | # header fields without any processing. See also #[].
|
---|
1192 | #
|
---|
1193 | # p response.get_fields('Set-Cookie')
|
---|
1194 | # #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
|
---|
1195 | # "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
|
---|
1196 | # p response['Set-Cookie']
|
---|
1197 | # #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
|
---|
1198 | #
|
---|
1199 | def get_fields(key)
|
---|
1200 | return nil unless @header[key.downcase]
|
---|
1201 | @header[key.downcase].dup
|
---|
1202 | end
|
---|
1203 |
|
---|
1204 | # Returns the header field corresponding to the case-insensitive key.
|
---|
1205 | # Returns the default value +args+, or the result of the block, or nil,
|
---|
1206 | # if there's no header field named key. See Hash#fetch
|
---|
1207 | def fetch(key, *args, &block) #:yield: +key+
|
---|
1208 | a = @header.fetch(key.downcase, *args, &block)
|
---|
1209 | a.join(', ')
|
---|
1210 | end
|
---|
1211 |
|
---|
1212 | # Iterates for each header names and values.
|
---|
1213 | def each_header #:yield: +key+, +value+
|
---|
1214 | @header.each do |k,va|
|
---|
1215 | yield k, va.join(', ')
|
---|
1216 | end
|
---|
1217 | end
|
---|
1218 |
|
---|
1219 | alias each each_header
|
---|
1220 |
|
---|
1221 | # Iterates for each header names.
|
---|
1222 | def each_name(&block) #:yield: +key+
|
---|
1223 | @header.each_key(&block)
|
---|
1224 | end
|
---|
1225 |
|
---|
1226 | alias each_key each_name
|
---|
1227 |
|
---|
1228 | # Iterates for each capitalized header names.
|
---|
1229 | def each_capitalized_name(&block) #:yield: +key+
|
---|
1230 | @header.each_key do |k|
|
---|
1231 | yield capitalize(k)
|
---|
1232 | end
|
---|
1233 | end
|
---|
1234 |
|
---|
1235 | # Iterates for each header values.
|
---|
1236 | def each_value #:yield: +value+
|
---|
1237 | @header.each_value do |va|
|
---|
1238 | yield va.join(', ')
|
---|
1239 | end
|
---|
1240 | end
|
---|
1241 |
|
---|
1242 | # Removes a header field.
|
---|
1243 | def delete(key)
|
---|
1244 | @header.delete(key.downcase)
|
---|
1245 | end
|
---|
1246 |
|
---|
1247 | # true if +key+ header exists.
|
---|
1248 | def key?(key)
|
---|
1249 | @header.key?(key.downcase)
|
---|
1250 | end
|
---|
1251 |
|
---|
1252 | # Returns a Hash consist of header names and values.
|
---|
1253 | def to_hash
|
---|
1254 | @header.dup
|
---|
1255 | end
|
---|
1256 |
|
---|
1257 | # As for #each_header, except the keys are provided in capitalized form.
|
---|
1258 | def each_capitalized
|
---|
1259 | @header.each do |k,v|
|
---|
1260 | yield capitalize(k), v.join(', ')
|
---|
1261 | end
|
---|
1262 | end
|
---|
1263 |
|
---|
1264 | alias canonical_each each_capitalized
|
---|
1265 |
|
---|
1266 | def capitalize(name)
|
---|
1267 | name.split(/-/).map {|s| s.capitalize }.join('-')
|
---|
1268 | end
|
---|
1269 | private :capitalize
|
---|
1270 |
|
---|
1271 | # Returns an Array of Range objects which represents Range: header field,
|
---|
1272 | # or +nil+ if there is no such header.
|
---|
1273 | def range
|
---|
1274 | return nil unless @header['range']
|
---|
1275 | self['Range'].split(/,/).map {|spec|
|
---|
1276 | m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
|
---|
1277 | raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
|
---|
1278 | d1 = m[1].to_i
|
---|
1279 | d2 = m[2].to_i
|
---|
1280 | if m[1] and m[2] then d1..d2
|
---|
1281 | elsif m[1] then d1..-1
|
---|
1282 | elsif m[2] then -d2..-1
|
---|
1283 | else
|
---|
1284 | raise HTTPHeaderSyntaxError, 'range is not specified'
|
---|
1285 | end
|
---|
1286 | }
|
---|
1287 | end
|
---|
1288 |
|
---|
1289 | # Set Range: header from Range (arg r) or beginning index and
|
---|
1290 | # length from it (arg idx&len).
|
---|
1291 | #
|
---|
1292 | # req.range = (0..1023)
|
---|
1293 | # req.set_range 0, 1023
|
---|
1294 | #
|
---|
1295 | def set_range(r, e = nil)
|
---|
1296 | unless r
|
---|
1297 | @header.delete 'range'
|
---|
1298 | return r
|
---|
1299 | end
|
---|
1300 | r = (r...r+e) if e
|
---|
1301 | case r
|
---|
1302 | when Numeric
|
---|
1303 | n = r.to_i
|
---|
1304 | rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}")
|
---|
1305 | when Range
|
---|
1306 | first = r.first
|
---|
1307 | last = r.last
|
---|
1308 | last -= 1 if r.exclude_end?
|
---|
1309 | if last == -1
|
---|
1310 | rangestr = (first > 0 ? "#{first}-" : "-#{-first}")
|
---|
1311 | else
|
---|
1312 | raise HTTPHeaderSyntaxError, 'range.first is negative' if first < 0
|
---|
1313 | raise HTTPHeaderSyntaxError, 'range.last is negative' if last < 0
|
---|
1314 | raise HTTPHeaderSyntaxError, 'must be .first < .last' if first > last
|
---|
1315 | rangestr = "#{first}-#{last}"
|
---|
1316 | end
|
---|
1317 | else
|
---|
1318 | raise TypeError, 'Range/Integer is required'
|
---|
1319 | end
|
---|
1320 | @header['range'] = ["bytes=#{rangestr}"]
|
---|
1321 | r
|
---|
1322 | end
|
---|
1323 |
|
---|
1324 | alias range= set_range
|
---|
1325 |
|
---|
1326 | # Returns an Integer object which represents the Content-Length: header field
|
---|
1327 | # or +nil+ if that field is not provided.
|
---|
1328 | def content_length
|
---|
1329 | return nil unless key?('Content-Length')
|
---|
1330 | len = self['Content-Length'].slice(/\d+/) or
|
---|
1331 | raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
|
---|
1332 | len.to_i
|
---|
1333 | end
|
---|
1334 |
|
---|
1335 | def content_length=(len)
|
---|
1336 | unless len
|
---|
1337 | @header.delete 'content-length'
|
---|
1338 | return nil
|
---|
1339 | end
|
---|
1340 | @header['content-length'] = [len.to_i.to_s]
|
---|
1341 | end
|
---|
1342 |
|
---|
1343 | # Returns "true" if the "transfer-encoding" header is present and
|
---|
1344 | # set to "chunked". This is an HTTP/1.1 feature, allowing the
|
---|
1345 | # the content to be sent in "chunks" without at the outset
|
---|
1346 | # stating the entire content length.
|
---|
1347 | def chunked?
|
---|
1348 | return false unless @header['transfer-encoding']
|
---|
1349 | field = self['Transfer-Encoding']
|
---|
1350 | (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
|
---|
1351 | end
|
---|
1352 |
|
---|
1353 | # Returns a Range object which represents Content-Range: header field.
|
---|
1354 | # This indicates, for a partial entity body, where this fragment
|
---|
1355 | # fits inside the full entity body, as range of byte offsets.
|
---|
1356 | def content_range
|
---|
1357 | return nil unless @header['content-range']
|
---|
1358 | m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or
|
---|
1359 | raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
|
---|
1360 | m[1].to_i .. m[2].to_i + 1
|
---|
1361 | end
|
---|
1362 |
|
---|
1363 | # The length of the range represented in Content-Range: header.
|
---|
1364 | def range_length
|
---|
1365 | r = content_range() or return nil
|
---|
1366 | r.end - r.begin
|
---|
1367 | end
|
---|
1368 |
|
---|
1369 | # Returns a content type string such as "text/html".
|
---|
1370 | # This method returns nil if Content-Type: header field does not exist.
|
---|
1371 | def content_type
|
---|
1372 | return nil unless main_type()
|
---|
1373 | if sub_type()
|
---|
1374 | then "#{main_type()}/#{sub_type()}"
|
---|
1375 | else main_type()
|
---|
1376 | end
|
---|
1377 | end
|
---|
1378 |
|
---|
1379 | # Returns a content type string such as "text".
|
---|
1380 | # This method returns nil if Content-Type: header field does not exist.
|
---|
1381 | def main_type
|
---|
1382 | return nil unless @header['content-type']
|
---|
1383 | self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
|
---|
1384 | end
|
---|
1385 |
|
---|
1386 | # Returns a content type string such as "html".
|
---|
1387 | # This method returns nil if Content-Type: header field does not exist
|
---|
1388 | # or sub-type is not given (e.g. "Content-Type: text").
|
---|
1389 | def sub_type
|
---|
1390 | return nil unless @header['content-type']
|
---|
1391 | main, sub = *self['Content-Type'].split(';').first.to_s.split('/')
|
---|
1392 | return nil unless sub
|
---|
1393 | sub.strip
|
---|
1394 | end
|
---|
1395 |
|
---|
1396 | # Returns content type parameters as a Hash as like
|
---|
1397 | # {"charset" => "iso-2022-jp"}.
|
---|
1398 | def type_params
|
---|
1399 | result = {}
|
---|
1400 | list = self['Content-Type'].to_s.split(';')
|
---|
1401 | list.shift
|
---|
1402 | list.each do |param|
|
---|
1403 | k, v = *param.split('=', 2)
|
---|
1404 | result[k.strip] = v.strip
|
---|
1405 | end
|
---|
1406 | result
|
---|
1407 | end
|
---|
1408 |
|
---|
1409 | # Set Content-Type: header field by +type+ and +params+.
|
---|
1410 | # +type+ must be a String, +params+ must be a Hash.
|
---|
1411 | def set_content_type(type, params = {})
|
---|
1412 | @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
|
---|
1413 | end
|
---|
1414 |
|
---|
1415 | alias content_type= set_content_type
|
---|
1416 |
|
---|
1417 | # Set header fields and a body from HTML form data.
|
---|
1418 | # +params+ should be a Hash containing HTML form data.
|
---|
1419 | # Optional argument +sep+ means data record separator.
|
---|
1420 | #
|
---|
1421 | # This method also set Content-Type: header field to
|
---|
1422 | # application/x-www-form-urlencoded.
|
---|
1423 | def set_form_data(params, sep = '&')
|
---|
1424 | self.body = params.map {|k,v| "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" }.join(sep)
|
---|
1425 | self.content_type = 'application/x-www-form-urlencoded'
|
---|
1426 | end
|
---|
1427 |
|
---|
1428 | alias form_data= set_form_data
|
---|
1429 |
|
---|
1430 | def urlencode(str)
|
---|
1431 | str.gsub(/[^a-zA-Z0-9_\.\-]/n) {|s| sprintf('%%%02x', s[0]) }
|
---|
1432 | end
|
---|
1433 | private :urlencode
|
---|
1434 |
|
---|
1435 | # Set the Authorization: header for "Basic" authorization.
|
---|
1436 | def basic_auth(account, password)
|
---|
1437 | @header['authorization'] = [basic_encode(account, password)]
|
---|
1438 | end
|
---|
1439 |
|
---|
1440 | # Set Proxy-Authorization: header for "Basic" authorization.
|
---|
1441 | def proxy_basic_auth(account, password)
|
---|
1442 | @header['proxy-authorization'] = [basic_encode(account, password)]
|
---|
1443 | end
|
---|
1444 |
|
---|
1445 | def basic_encode(account, password)
|
---|
1446 | 'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n")
|
---|
1447 | end
|
---|
1448 | private :basic_encode
|
---|
1449 |
|
---|
1450 | end
|
---|
1451 |
|
---|
1452 |
|
---|
1453 | #
|
---|
1454 | # Parent of HTTPRequest class. Do not use this directly; use
|
---|
1455 | # a subclass of HTTPRequest.
|
---|
1456 | #
|
---|
1457 | # Mixes in the HTTPHeader module.
|
---|
1458 | #
|
---|
1459 | class HTTPGenericRequest
|
---|
1460 |
|
---|
1461 | include HTTPHeader
|
---|
1462 |
|
---|
1463 | def initialize(m, reqbody, resbody, path, initheader = nil)
|
---|
1464 | @method = m
|
---|
1465 | @request_has_body = reqbody
|
---|
1466 | @response_has_body = resbody
|
---|
1467 | raise ArgumentError, "HTTP request path is empty" if path.empty?
|
---|
1468 | @path = path
|
---|
1469 | initialize_http_header initheader
|
---|
1470 | self['Accept'] ||= '*/*'
|
---|
1471 | @body = nil
|
---|
1472 | @body_stream = nil
|
---|
1473 | end
|
---|
1474 |
|
---|
1475 | attr_reader :method
|
---|
1476 | attr_reader :path
|
---|
1477 |
|
---|
1478 | def inspect
|
---|
1479 | "\#<#{self.class} #{@method}>"
|
---|
1480 | end
|
---|
1481 |
|
---|
1482 | def request_body_permitted?
|
---|
1483 | @request_has_body
|
---|
1484 | end
|
---|
1485 |
|
---|
1486 | def response_body_permitted?
|
---|
1487 | @response_has_body
|
---|
1488 | end
|
---|
1489 |
|
---|
1490 | def body_exist?
|
---|
1491 | warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE
|
---|
1492 | response_body_permitted?
|
---|
1493 | end
|
---|
1494 |
|
---|
1495 | attr_reader :body
|
---|
1496 |
|
---|
1497 | def body=(str)
|
---|
1498 | @body = str
|
---|
1499 | @body_stream = nil
|
---|
1500 | str
|
---|
1501 | end
|
---|
1502 |
|
---|
1503 | attr_reader :body_stream
|
---|
1504 |
|
---|
1505 | def body_stream=(input)
|
---|
1506 | @body = nil
|
---|
1507 | @body_stream = input
|
---|
1508 | input
|
---|
1509 | end
|
---|
1510 |
|
---|
1511 | def set_body_internal(str) #:nodoc: internal use only
|
---|
1512 | raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream)
|
---|
1513 | self.body = str if str
|
---|
1514 | end
|
---|
1515 |
|
---|
1516 | #
|
---|
1517 | # write
|
---|
1518 | #
|
---|
1519 |
|
---|
1520 | def exec(sock, ver, path) #:nodoc: internal use only
|
---|
1521 | if @body
|
---|
1522 | send_request_with_body sock, ver, path, @body
|
---|
1523 | elsif @body_stream
|
---|
1524 | send_request_with_body_stream sock, ver, path, @body_stream
|
---|
1525 | else
|
---|
1526 | write_header sock, ver, path
|
---|
1527 | end
|
---|
1528 | end
|
---|
1529 |
|
---|
1530 | private
|
---|
1531 |
|
---|
1532 | def send_request_with_body(sock, ver, path, body)
|
---|
1533 | self.content_length = body.length
|
---|
1534 | delete 'Transfer-Encoding'
|
---|
1535 | supply_default_content_type
|
---|
1536 | write_header sock, ver, path
|
---|
1537 | sock.write body
|
---|
1538 | end
|
---|
1539 |
|
---|
1540 | def send_request_with_body_stream(sock, ver, path, f)
|
---|
1541 | unless content_length() or chunked?
|
---|
1542 | raise ArgumentError,
|
---|
1543 | "Content-Length not given and Transfer-Encoding is not `chunked'"
|
---|
1544 | end
|
---|
1545 | supply_default_content_type
|
---|
1546 | write_header sock, ver, path
|
---|
1547 | if chunked?
|
---|
1548 | while s = f.read(1024)
|
---|
1549 | sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
|
---|
1550 | end
|
---|
1551 | sock.write "0\r\n\r\n"
|
---|
1552 | else
|
---|
1553 | while s = f.read(1024)
|
---|
1554 | sock.write s
|
---|
1555 | end
|
---|
1556 | end
|
---|
1557 | end
|
---|
1558 |
|
---|
1559 | def supply_default_content_type
|
---|
1560 | return if content_type()
|
---|
1561 | warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
|
---|
1562 | set_content_type 'application/x-www-form-urlencoded'
|
---|
1563 | end
|
---|
1564 |
|
---|
1565 | def write_header(sock, ver, path)
|
---|
1566 | buf = "#{@method} #{path} HTTP/#{ver}\r\n"
|
---|
1567 | each_capitalized do |k,v|
|
---|
1568 | buf << "#{k}: #{v}\r\n"
|
---|
1569 | end
|
---|
1570 | buf << "\r\n"
|
---|
1571 | sock.write buf
|
---|
1572 | end
|
---|
1573 |
|
---|
1574 | end
|
---|
1575 |
|
---|
1576 |
|
---|
1577 | #
|
---|
1578 | # HTTP request class. This class wraps request header and entity path.
|
---|
1579 | # You *must* use its subclass, Net::HTTP::Get, Post, Head.
|
---|
1580 | #
|
---|
1581 | class HTTPRequest < HTTPGenericRequest
|
---|
1582 |
|
---|
1583 | # Creates HTTP request object.
|
---|
1584 | def initialize(path, initheader = nil)
|
---|
1585 | super self.class::METHOD,
|
---|
1586 | self.class::REQUEST_HAS_BODY,
|
---|
1587 | self.class::RESPONSE_HAS_BODY,
|
---|
1588 | path, initheader
|
---|
1589 | end
|
---|
1590 | end
|
---|
1591 |
|
---|
1592 |
|
---|
1593 | class HTTP # reopen
|
---|
1594 | #
|
---|
1595 | # HTTP 1.1 methods --- RFC2616
|
---|
1596 | #
|
---|
1597 |
|
---|
1598 | class Get < HTTPRequest
|
---|
1599 | METHOD = 'GET'
|
---|
1600 | REQUEST_HAS_BODY = false
|
---|
1601 | RESPONSE_HAS_BODY = true
|
---|
1602 | end
|
---|
1603 |
|
---|
1604 | class Head < HTTPRequest
|
---|
1605 | METHOD = 'HEAD'
|
---|
1606 | REQUEST_HAS_BODY = false
|
---|
1607 | RESPONSE_HAS_BODY = false
|
---|
1608 | end
|
---|
1609 |
|
---|
1610 | class Post < HTTPRequest
|
---|
1611 | METHOD = 'POST'
|
---|
1612 | REQUEST_HAS_BODY = true
|
---|
1613 | RESPONSE_HAS_BODY = true
|
---|
1614 | end
|
---|
1615 |
|
---|
1616 | class Put < HTTPRequest
|
---|
1617 | METHOD = 'PUT'
|
---|
1618 | REQUEST_HAS_BODY = true
|
---|
1619 | RESPONSE_HAS_BODY = true
|
---|
1620 | end
|
---|
1621 |
|
---|
1622 | class Delete < HTTPRequest
|
---|
1623 | METHOD = 'DELETE'
|
---|
1624 | REQUEST_HAS_BODY = false
|
---|
1625 | RESPONSE_HAS_BODY = true
|
---|
1626 | end
|
---|
1627 |
|
---|
1628 | class Options < HTTPRequest
|
---|
1629 | METHOD = 'OPTIONS'
|
---|
1630 | REQUEST_HAS_BODY = false
|
---|
1631 | RESPONSE_HAS_BODY = false
|
---|
1632 | end
|
---|
1633 |
|
---|
1634 | class Trace < HTTPRequest
|
---|
1635 | METHOD = 'TRACE'
|
---|
1636 | REQUEST_HAS_BODY = false
|
---|
1637 | RESPONSE_HAS_BODY = true
|
---|
1638 | end
|
---|
1639 |
|
---|
1640 | #
|
---|
1641 | # WebDAV methods --- RFC2518
|
---|
1642 | #
|
---|
1643 |
|
---|
1644 | class Propfind < HTTPRequest
|
---|
1645 | METHOD = 'PROPFIND'
|
---|
1646 | REQUEST_HAS_BODY = true
|
---|
1647 | RESPONSE_HAS_BODY = true
|
---|
1648 | end
|
---|
1649 |
|
---|
1650 | class Proppatch < HTTPRequest
|
---|
1651 | METHOD = 'PROPPATCH'
|
---|
1652 | REQUEST_HAS_BODY = true
|
---|
1653 | RESPONSE_HAS_BODY = true
|
---|
1654 | end
|
---|
1655 |
|
---|
1656 | class Mkcol < HTTPRequest
|
---|
1657 | METHOD = 'MKCOL'
|
---|
1658 | REQUEST_HAS_BODY = true
|
---|
1659 | RESPONSE_HAS_BODY = true
|
---|
1660 | end
|
---|
1661 |
|
---|
1662 | class Copy < HTTPRequest
|
---|
1663 | METHOD = 'COPY'
|
---|
1664 | REQUEST_HAS_BODY = false
|
---|
1665 | RESPONSE_HAS_BODY = true
|
---|
1666 | end
|
---|
1667 |
|
---|
1668 | class Move < HTTPRequest
|
---|
1669 | METHOD = 'MOVE'
|
---|
1670 | REQUEST_HAS_BODY = false
|
---|
1671 | RESPONSE_HAS_BODY = true
|
---|
1672 | end
|
---|
1673 |
|
---|
1674 | class Lock < HTTPRequest
|
---|
1675 | METHOD = 'LOCK'
|
---|
1676 | REQUEST_HAS_BODY = true
|
---|
1677 | RESPONSE_HAS_BODY = true
|
---|
1678 | end
|
---|
1679 |
|
---|
1680 | class Unlock < HTTPRequest
|
---|
1681 | METHOD = 'UNLOCK'
|
---|
1682 | REQUEST_HAS_BODY = true
|
---|
1683 | RESPONSE_HAS_BODY = true
|
---|
1684 | end
|
---|
1685 | end
|
---|
1686 |
|
---|
1687 |
|
---|
1688 | ###
|
---|
1689 | ### Response
|
---|
1690 | ###
|
---|
1691 |
|
---|
1692 | # HTTP exception class.
|
---|
1693 | # You must use its subclasses.
|
---|
1694 | module HTTPExceptions
|
---|
1695 | def initialize(msg, res) #:nodoc:
|
---|
1696 | super msg
|
---|
1697 | @response = res
|
---|
1698 | end
|
---|
1699 | attr_reader :response
|
---|
1700 | alias data response #:nodoc: obsolete
|
---|
1701 | end
|
---|
1702 | class HTTPError < ProtocolError
|
---|
1703 | include HTTPExceptions
|
---|
1704 | end
|
---|
1705 | class HTTPRetriableError < ProtoRetriableError
|
---|
1706 | include HTTPExceptions
|
---|
1707 | end
|
---|
1708 | class HTTPServerException < ProtoServerError
|
---|
1709 | # We cannot use the name "HTTPServerError", it is the name of the response.
|
---|
1710 | include HTTPExceptions
|
---|
1711 | end
|
---|
1712 | class HTTPFatalError < ProtoFatalError
|
---|
1713 | include HTTPExceptions
|
---|
1714 | end
|
---|
1715 |
|
---|
1716 |
|
---|
1717 | # HTTP response class. This class wraps response header and entity.
|
---|
1718 | # Mixes in the HTTPHeader module, which provides access to response
|
---|
1719 | # header values both via hash-like methods and individual readers.
|
---|
1720 | # Note that each possible HTTP response code defines its own
|
---|
1721 | # HTTPResponse subclass. These are listed below.
|
---|
1722 | # All classes are
|
---|
1723 | # defined under the Net module. Indentation indicates inheritance.
|
---|
1724 | #
|
---|
1725 | # xxx HTTPResponse
|
---|
1726 | #
|
---|
1727 | # 1xx HTTPInformation
|
---|
1728 | # 100 HTTPContinue
|
---|
1729 | # 101 HTTPSwitchProtocol
|
---|
1730 | #
|
---|
1731 | # 2xx HTTPSuccess
|
---|
1732 | # 200 HTTPOK
|
---|
1733 | # 201 HTTPCreated
|
---|
1734 | # 202 HTTPAccepted
|
---|
1735 | # 203 HTTPNonAuthoritativeInformation
|
---|
1736 | # 204 HTTPNoContent
|
---|
1737 | # 205 HTTPResetContent
|
---|
1738 | # 206 HTTPPartialContent
|
---|
1739 | #
|
---|
1740 | # 3xx HTTPRedirection
|
---|
1741 | # 300 HTTPMultipleChoice
|
---|
1742 | # 301 HTTPMovedPermanently
|
---|
1743 | # 302 HTTPFound
|
---|
1744 | # 303 HTTPSeeOther
|
---|
1745 | # 304 HTTPNotModified
|
---|
1746 | # 305 HTTPUseProxy
|
---|
1747 | # 307 HTTPTemporaryRedirect
|
---|
1748 | #
|
---|
1749 | # 4xx HTTPClientError
|
---|
1750 | # 400 HTTPBadRequest
|
---|
1751 | # 401 HTTPUnauthorized
|
---|
1752 | # 402 HTTPPaymentRequired
|
---|
1753 | # 403 HTTPForbidden
|
---|
1754 | # 404 HTTPNotFound
|
---|
1755 | # 405 HTTPMethodNotAllowed
|
---|
1756 | # 406 HTTPNotAcceptable
|
---|
1757 | # 407 HTTPProxyAuthenticationRequired
|
---|
1758 | # 408 HTTPRequestTimeOut
|
---|
1759 | # 409 HTTPConflict
|
---|
1760 | # 410 HTTPGone
|
---|
1761 | # 411 HTTPLengthRequired
|
---|
1762 | # 412 HTTPPreconditionFailed
|
---|
1763 | # 413 HTTPRequestEntityTooLarge
|
---|
1764 | # 414 HTTPRequestURITooLong
|
---|
1765 | # 415 HTTPUnsupportedMediaType
|
---|
1766 | # 416 HTTPRequestedRangeNotSatisfiable
|
---|
1767 | # 417 HTTPExpectationFailed
|
---|
1768 | #
|
---|
1769 | # 5xx HTTPServerError
|
---|
1770 | # 500 HTTPInternalServerError
|
---|
1771 | # 501 HTTPNotImplemented
|
---|
1772 | # 502 HTTPBadGateway
|
---|
1773 | # 503 HTTPServiceUnavailable
|
---|
1774 | # 504 HTTPGatewayTimeOut
|
---|
1775 | # 505 HTTPVersionNotSupported
|
---|
1776 | #
|
---|
1777 | # xxx HTTPUnknownResponse
|
---|
1778 | #
|
---|
1779 | class HTTPResponse
|
---|
1780 | # true if the response has body.
|
---|
1781 | def HTTPResponse.body_permitted?
|
---|
1782 | self::HAS_BODY
|
---|
1783 | end
|
---|
1784 |
|
---|
1785 | def HTTPResponse.exception_type # :nodoc: internal use only
|
---|
1786 | self::EXCEPTION_TYPE
|
---|
1787 | end
|
---|
1788 | end # reopened after
|
---|
1789 |
|
---|
1790 | # :stopdoc:
|
---|
1791 |
|
---|
1792 | class HTTPUnknownResponse < HTTPResponse
|
---|
1793 | HAS_BODY = true
|
---|
1794 | EXCEPTION_TYPE = HTTPError
|
---|
1795 | end
|
---|
1796 | class HTTPInformation < HTTPResponse # 1xx
|
---|
1797 | HAS_BODY = false
|
---|
1798 | EXCEPTION_TYPE = HTTPError
|
---|
1799 | end
|
---|
1800 | class HTTPSuccess < HTTPResponse # 2xx
|
---|
1801 | HAS_BODY = true
|
---|
1802 | EXCEPTION_TYPE = HTTPError
|
---|
1803 | end
|
---|
1804 | class HTTPRedirection < HTTPResponse # 3xx
|
---|
1805 | HAS_BODY = true
|
---|
1806 | EXCEPTION_TYPE = HTTPRetriableError
|
---|
1807 | end
|
---|
1808 | class HTTPClientError < HTTPResponse # 4xx
|
---|
1809 | HAS_BODY = true
|
---|
1810 | EXCEPTION_TYPE = HTTPServerException # for backward compatibility
|
---|
1811 | end
|
---|
1812 | class HTTPServerError < HTTPResponse # 5xx
|
---|
1813 | HAS_BODY = true
|
---|
1814 | EXCEPTION_TYPE = HTTPFatalError # for backward compatibility
|
---|
1815 | end
|
---|
1816 |
|
---|
1817 | class HTTPContinue < HTTPInformation # 100
|
---|
1818 | HAS_BODY = false
|
---|
1819 | end
|
---|
1820 | class HTTPSwitchProtocol < HTTPInformation # 101
|
---|
1821 | HAS_BODY = false
|
---|
1822 | end
|
---|
1823 |
|
---|
1824 | class HTTPOK < HTTPSuccess # 200
|
---|
1825 | HAS_BODY = true
|
---|
1826 | end
|
---|
1827 | class HTTPCreated < HTTPSuccess # 201
|
---|
1828 | HAS_BODY = true
|
---|
1829 | end
|
---|
1830 | class HTTPAccepted < HTTPSuccess # 202
|
---|
1831 | HAS_BODY = true
|
---|
1832 | end
|
---|
1833 | class HTTPNonAuthoritativeInformation < HTTPSuccess # 203
|
---|
1834 | HAS_BODY = true
|
---|
1835 | end
|
---|
1836 | class HTTPNoContent < HTTPSuccess # 204
|
---|
1837 | HAS_BODY = false
|
---|
1838 | end
|
---|
1839 | class HTTPResetContent < HTTPSuccess # 205
|
---|
1840 | HAS_BODY = false
|
---|
1841 | end
|
---|
1842 | class HTTPPartialContent < HTTPSuccess # 206
|
---|
1843 | HAS_BODY = true
|
---|
1844 | end
|
---|
1845 |
|
---|
1846 | class HTTPMultipleChoice < HTTPRedirection # 300
|
---|
1847 | HAS_BODY = true
|
---|
1848 | end
|
---|
1849 | class HTTPMovedPermanently < HTTPRedirection # 301
|
---|
1850 | HAS_BODY = true
|
---|
1851 | end
|
---|
1852 | class HTTPFound < HTTPRedirection # 302
|
---|
1853 | HAS_BODY = true
|
---|
1854 | end
|
---|
1855 | HTTPMovedTemporarily = HTTPFound
|
---|
1856 | class HTTPSeeOther < HTTPRedirection # 303
|
---|
1857 | HAS_BODY = true
|
---|
1858 | end
|
---|
1859 | class HTTPNotModified < HTTPRedirection # 304
|
---|
1860 | HAS_BODY = false
|
---|
1861 | end
|
---|
1862 | class HTTPUseProxy < HTTPRedirection # 305
|
---|
1863 | HAS_BODY = false
|
---|
1864 | end
|
---|
1865 | # 306 unused
|
---|
1866 | class HTTPTemporaryRedirect < HTTPRedirection # 307
|
---|
1867 | HAS_BODY = true
|
---|
1868 | end
|
---|
1869 |
|
---|
1870 | class HTTPBadRequest < HTTPClientError # 400
|
---|
1871 | HAS_BODY = true
|
---|
1872 | end
|
---|
1873 | class HTTPUnauthorized < HTTPClientError # 401
|
---|
1874 | HAS_BODY = true
|
---|
1875 | end
|
---|
1876 | class HTTPPaymentRequired < HTTPClientError # 402
|
---|
1877 | HAS_BODY = true
|
---|
1878 | end
|
---|
1879 | class HTTPForbidden < HTTPClientError # 403
|
---|
1880 | HAS_BODY = true
|
---|
1881 | end
|
---|
1882 | class HTTPNotFound < HTTPClientError # 404
|
---|
1883 | HAS_BODY = true
|
---|
1884 | end
|
---|
1885 | class HTTPMethodNotAllowed < HTTPClientError # 405
|
---|
1886 | HAS_BODY = true
|
---|
1887 | end
|
---|
1888 | class HTTPNotAcceptable < HTTPClientError # 406
|
---|
1889 | HAS_BODY = true
|
---|
1890 | end
|
---|
1891 | class HTTPProxyAuthenticationRequired < HTTPClientError # 407
|
---|
1892 | HAS_BODY = true
|
---|
1893 | end
|
---|
1894 | class HTTPRequestTimeOut < HTTPClientError # 408
|
---|
1895 | HAS_BODY = true
|
---|
1896 | end
|
---|
1897 | class HTTPConflict < HTTPClientError # 409
|
---|
1898 | HAS_BODY = true
|
---|
1899 | end
|
---|
1900 | class HTTPGone < HTTPClientError # 410
|
---|
1901 | HAS_BODY = true
|
---|
1902 | end
|
---|
1903 | class HTTPLengthRequired < HTTPClientError # 411
|
---|
1904 | HAS_BODY = true
|
---|
1905 | end
|
---|
1906 | class HTTPPreconditionFailed < HTTPClientError # 412
|
---|
1907 | HAS_BODY = true
|
---|
1908 | end
|
---|
1909 | class HTTPRequestEntityTooLarge < HTTPClientError # 413
|
---|
1910 | HAS_BODY = true
|
---|
1911 | end
|
---|
1912 | class HTTPRequestURITooLong < HTTPClientError # 414
|
---|
1913 | HAS_BODY = true
|
---|
1914 | end
|
---|
1915 | HTTPRequestURITooLarge = HTTPRequestURITooLong
|
---|
1916 | class HTTPUnsupportedMediaType < HTTPClientError # 415
|
---|
1917 | HAS_BODY = true
|
---|
1918 | end
|
---|
1919 | class HTTPRequestedRangeNotSatisfiable < HTTPClientError # 416
|
---|
1920 | HAS_BODY = true
|
---|
1921 | end
|
---|
1922 | class HTTPExpectationFailed < HTTPClientError # 417
|
---|
1923 | HAS_BODY = true
|
---|
1924 | end
|
---|
1925 |
|
---|
1926 | class HTTPInternalServerError < HTTPServerError # 500
|
---|
1927 | HAS_BODY = true
|
---|
1928 | end
|
---|
1929 | class HTTPNotImplemented < HTTPServerError # 501
|
---|
1930 | HAS_BODY = true
|
---|
1931 | end
|
---|
1932 | class HTTPBadGateway < HTTPServerError # 502
|
---|
1933 | HAS_BODY = true
|
---|
1934 | end
|
---|
1935 | class HTTPServiceUnavailable < HTTPServerError # 503
|
---|
1936 | HAS_BODY = true
|
---|
1937 | end
|
---|
1938 | class HTTPGatewayTimeOut < HTTPServerError # 504
|
---|
1939 | HAS_BODY = true
|
---|
1940 | end
|
---|
1941 | class HTTPVersionNotSupported < HTTPServerError # 505
|
---|
1942 | HAS_BODY = true
|
---|
1943 | end
|
---|
1944 |
|
---|
1945 | # :startdoc:
|
---|
1946 |
|
---|
1947 |
|
---|
1948 | class HTTPResponse # reopen
|
---|
1949 |
|
---|
1950 | CODE_CLASS_TO_OBJ = {
|
---|
1951 | '1' => HTTPInformation,
|
---|
1952 | '2' => HTTPSuccess,
|
---|
1953 | '3' => HTTPRedirection,
|
---|
1954 | '4' => HTTPClientError,
|
---|
1955 | '5' => HTTPServerError
|
---|
1956 | }
|
---|
1957 | CODE_TO_OBJ = {
|
---|
1958 | '100' => HTTPContinue,
|
---|
1959 | '101' => HTTPSwitchProtocol,
|
---|
1960 |
|
---|
1961 | '200' => HTTPOK,
|
---|
1962 | '201' => HTTPCreated,
|
---|
1963 | '202' => HTTPAccepted,
|
---|
1964 | '203' => HTTPNonAuthoritativeInformation,
|
---|
1965 | '204' => HTTPNoContent,
|
---|
1966 | '205' => HTTPResetContent,
|
---|
1967 | '206' => HTTPPartialContent,
|
---|
1968 |
|
---|
1969 | '300' => HTTPMultipleChoice,
|
---|
1970 | '301' => HTTPMovedPermanently,
|
---|
1971 | '302' => HTTPFound,
|
---|
1972 | '303' => HTTPSeeOther,
|
---|
1973 | '304' => HTTPNotModified,
|
---|
1974 | '305' => HTTPUseProxy,
|
---|
1975 | '307' => HTTPTemporaryRedirect,
|
---|
1976 |
|
---|
1977 | '400' => HTTPBadRequest,
|
---|
1978 | '401' => HTTPUnauthorized,
|
---|
1979 | '402' => HTTPPaymentRequired,
|
---|
1980 | '403' => HTTPForbidden,
|
---|
1981 | '404' => HTTPNotFound,
|
---|
1982 | '405' => HTTPMethodNotAllowed,
|
---|
1983 | '406' => HTTPNotAcceptable,
|
---|
1984 | '407' => HTTPProxyAuthenticationRequired,
|
---|
1985 | '408' => HTTPRequestTimeOut,
|
---|
1986 | '409' => HTTPConflict,
|
---|
1987 | '410' => HTTPGone,
|
---|
1988 | '411' => HTTPLengthRequired,
|
---|
1989 | '412' => HTTPPreconditionFailed,
|
---|
1990 | '413' => HTTPRequestEntityTooLarge,
|
---|
1991 | '414' => HTTPRequestURITooLong,
|
---|
1992 | '415' => HTTPUnsupportedMediaType,
|
---|
1993 | '416' => HTTPRequestedRangeNotSatisfiable,
|
---|
1994 | '417' => HTTPExpectationFailed,
|
---|
1995 |
|
---|
1996 | '500' => HTTPInternalServerError,
|
---|
1997 | '501' => HTTPNotImplemented,
|
---|
1998 | '502' => HTTPBadGateway,
|
---|
1999 | '503' => HTTPServiceUnavailable,
|
---|
2000 | '504' => HTTPGatewayTimeOut,
|
---|
2001 | '505' => HTTPVersionNotSupported
|
---|
2002 | }
|
---|
2003 |
|
---|
2004 | class << HTTPResponse
|
---|
2005 | def read_new(sock) #:nodoc: internal use only
|
---|
2006 | httpv, code, msg = read_status_line(sock)
|
---|
2007 | res = response_class(code).new(httpv, code, msg)
|
---|
2008 | each_response_header(sock) do |k,v|
|
---|
2009 | res.add_field k, v
|
---|
2010 | end
|
---|
2011 | res
|
---|
2012 | end
|
---|
2013 |
|
---|
2014 | private
|
---|
2015 |
|
---|
2016 | def read_status_line(sock)
|
---|
2017 | str = sock.readline
|
---|
2018 | m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or
|
---|
2019 | raise HTTPBadResponse, "wrong status line: #{str.dump}"
|
---|
2020 | m.captures
|
---|
2021 | end
|
---|
2022 |
|
---|
2023 | def response_class(code)
|
---|
2024 | CODE_TO_OBJ[code] or
|
---|
2025 | CODE_CLASS_TO_OBJ[code[0,1]] or
|
---|
2026 | HTTPUnknownResponse
|
---|
2027 | end
|
---|
2028 |
|
---|
2029 | def each_response_header(sock)
|
---|
2030 | while true
|
---|
2031 | line = sock.readuntil("\n", true).sub(/\s+\z/, '')
|
---|
2032 | break if line.empty?
|
---|
2033 | m = /\A([^:]+):\s*/.match(line) or
|
---|
2034 | raise HTTPBadResponse, 'wrong header line format'
|
---|
2035 | yield m[1], m.post_match
|
---|
2036 | end
|
---|
2037 | end
|
---|
2038 | end
|
---|
2039 |
|
---|
2040 | # next is to fix bug in RDoc, where the private inside class << self
|
---|
2041 | # spills out.
|
---|
2042 | public
|
---|
2043 |
|
---|
2044 | include HTTPHeader
|
---|
2045 |
|
---|
2046 | def initialize(httpv, code, msg) #:nodoc: internal use only
|
---|
2047 | @http_version = httpv
|
---|
2048 | @code = code
|
---|
2049 | @message = msg
|
---|
2050 | initialize_http_header nil
|
---|
2051 | @body = nil
|
---|
2052 | @read = false
|
---|
2053 | end
|
---|
2054 |
|
---|
2055 | # The HTTP version supported by the server.
|
---|
2056 | attr_reader :http_version
|
---|
2057 |
|
---|
2058 | # HTTP result code string. For example, '302'. You can also
|
---|
2059 | # determine the response type by which response subclass the
|
---|
2060 | # response object is an instance of.
|
---|
2061 | attr_reader :code
|
---|
2062 |
|
---|
2063 | # HTTP result message. For example, 'Not Found'.
|
---|
2064 | attr_reader :message
|
---|
2065 | alias msg message # :nodoc: obsolete
|
---|
2066 |
|
---|
2067 | def inspect
|
---|
2068 | "#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
|
---|
2069 | end
|
---|
2070 |
|
---|
2071 | # For backward compatibility.
|
---|
2072 | # To allow Net::HTTP 1.1 style assignment
|
---|
2073 | # e.g.
|
---|
2074 | # response, body = Net::HTTP.get(....)
|
---|
2075 | #
|
---|
2076 | def to_ary
|
---|
2077 | warn "net/http.rb: warning: Net::HTTP v1.1 style assignment found at #{caller(1)[0]}; use `response = http.get(...)' instead." if $VERBOSE
|
---|
2078 | res = self.dup
|
---|
2079 | class << res
|
---|
2080 | undef to_ary
|
---|
2081 | end
|
---|
2082 | [res, res.body]
|
---|
2083 | end
|
---|
2084 |
|
---|
2085 | #
|
---|
2086 | # response <-> exception relationship
|
---|
2087 | #
|
---|
2088 |
|
---|
2089 | def code_type #:nodoc:
|
---|
2090 | self.class
|
---|
2091 | end
|
---|
2092 |
|
---|
2093 | def error! #:nodoc:
|
---|
2094 | raise error_type().new(@code + ' ' + @message.dump, self)
|
---|
2095 | end
|
---|
2096 |
|
---|
2097 | def error_type #:nodoc:
|
---|
2098 | self.class::EXCEPTION_TYPE
|
---|
2099 | end
|
---|
2100 |
|
---|
2101 | # Raises HTTP error if the response is not 2xx.
|
---|
2102 | def value
|
---|
2103 | error! unless self.kind_of?(HTTPSuccess)
|
---|
2104 | end
|
---|
2105 |
|
---|
2106 | #
|
---|
2107 | # header (for backward compatibility only; DO NOT USE)
|
---|
2108 | #
|
---|
2109 |
|
---|
2110 | def response #:nodoc:
|
---|
2111 | warn "#{caller(1)[0]}: warning: HTTPResponse#response is obsolete" if $VERBOSE
|
---|
2112 | self
|
---|
2113 | end
|
---|
2114 |
|
---|
2115 | def header #:nodoc:
|
---|
2116 | warn "#{caller(1)[0]}: warning: HTTPResponse#header is obsolete" if $VERBOSE
|
---|
2117 | self
|
---|
2118 | end
|
---|
2119 |
|
---|
2120 | def read_header #:nodoc:
|
---|
2121 | warn "#{caller(1)[0]}: warning: HTTPResponse#read_header is obsolete" if $VERBOSE
|
---|
2122 | self
|
---|
2123 | end
|
---|
2124 |
|
---|
2125 | #
|
---|
2126 | # body
|
---|
2127 | #
|
---|
2128 |
|
---|
2129 | def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only
|
---|
2130 | @socket = sock
|
---|
2131 | @body_exist = reqmethodallowbody && self.class.body_permitted?
|
---|
2132 | begin
|
---|
2133 | yield
|
---|
2134 | self.body # ensure to read body
|
---|
2135 | ensure
|
---|
2136 | @socket = nil
|
---|
2137 | end
|
---|
2138 | end
|
---|
2139 |
|
---|
2140 | # Gets entity body. If the block given, yields it to +block+.
|
---|
2141 | # The body is provided in fragments, as it is read in from the socket.
|
---|
2142 | #
|
---|
2143 | # Calling this method a second or subsequent time will return the
|
---|
2144 | # already read string.
|
---|
2145 | #
|
---|
2146 | # http.request_get('/index.html') {|res|
|
---|
2147 | # puts res.read_body
|
---|
2148 | # }
|
---|
2149 | #
|
---|
2150 | # http.request_get('/index.html') {|res|
|
---|
2151 | # p res.read_body.object_id # 538149362
|
---|
2152 | # p res.read_body.object_id # 538149362
|
---|
2153 | # }
|
---|
2154 | #
|
---|
2155 | # # using iterator
|
---|
2156 | # http.request_get('/index.html') {|res|
|
---|
2157 | # res.read_body do |segment|
|
---|
2158 | # print segment
|
---|
2159 | # end
|
---|
2160 | # }
|
---|
2161 | #
|
---|
2162 | def read_body(dest = nil, &block)
|
---|
2163 | if @read
|
---|
2164 | raise IOError, "#{self.class}\#read_body called twice" if dest or block
|
---|
2165 | return @body
|
---|
2166 | end
|
---|
2167 | to = procdest(dest, block)
|
---|
2168 | stream_check
|
---|
2169 | if @body_exist
|
---|
2170 | read_body_0 to
|
---|
2171 | @body = to
|
---|
2172 | else
|
---|
2173 | @body = nil
|
---|
2174 | end
|
---|
2175 | @read = true
|
---|
2176 |
|
---|
2177 | @body
|
---|
2178 | end
|
---|
2179 |
|
---|
2180 | # Returns the entity body.
|
---|
2181 | #
|
---|
2182 | # Calling this method a second or subsequent time will return the
|
---|
2183 | # already read string.
|
---|
2184 | #
|
---|
2185 | # http.request_get('/index.html') {|res|
|
---|
2186 | # puts res.body
|
---|
2187 | # }
|
---|
2188 | #
|
---|
2189 | # http.request_get('/index.html') {|res|
|
---|
2190 | # p res.body.object_id # 538149362
|
---|
2191 | # p res.body.object_id # 538149362
|
---|
2192 | # }
|
---|
2193 | #
|
---|
2194 | def body
|
---|
2195 | read_body()
|
---|
2196 | end
|
---|
2197 |
|
---|
2198 | alias entity body #:nodoc: obsolete
|
---|
2199 |
|
---|
2200 | private
|
---|
2201 |
|
---|
2202 | def read_body_0(dest)
|
---|
2203 | if chunked?
|
---|
2204 | read_chunked dest
|
---|
2205 | return
|
---|
2206 | end
|
---|
2207 | clen = content_length()
|
---|
2208 | if clen
|
---|
2209 | @socket.read clen, dest, true # ignore EOF
|
---|
2210 | return
|
---|
2211 | end
|
---|
2212 | clen = range_length()
|
---|
2213 | if clen
|
---|
2214 | @socket.read clen, dest
|
---|
2215 | return
|
---|
2216 | end
|
---|
2217 | @socket.read_all dest
|
---|
2218 | end
|
---|
2219 |
|
---|
2220 | def read_chunked(dest)
|
---|
2221 | len = nil
|
---|
2222 | total = 0
|
---|
2223 | while true
|
---|
2224 | line = @socket.readline
|
---|
2225 | hexlen = line.slice(/[0-9a-fA-F]+/) or
|
---|
2226 | raise HTTPBadResponse, "wrong chunk size line: #{line}"
|
---|
2227 | len = hexlen.hex
|
---|
2228 | break if len == 0
|
---|
2229 | @socket.read len, dest; total += len
|
---|
2230 | @socket.read 2 # \r\n
|
---|
2231 | end
|
---|
2232 | until @socket.readline.empty?
|
---|
2233 | # none
|
---|
2234 | end
|
---|
2235 | end
|
---|
2236 |
|
---|
2237 | def stream_check
|
---|
2238 | raise IOError, 'attempt to read body out of block' if @socket.closed?
|
---|
2239 | end
|
---|
2240 |
|
---|
2241 | def procdest(dest, block)
|
---|
2242 | raise ArgumentError, 'both arg and block given for HTTP method' \
|
---|
2243 | if dest and block
|
---|
2244 | if block
|
---|
2245 | ReadAdapter.new(block)
|
---|
2246 | else
|
---|
2247 | dest || ''
|
---|
2248 | end
|
---|
2249 | end
|
---|
2250 |
|
---|
2251 | end
|
---|
2252 |
|
---|
2253 |
|
---|
2254 | # :enddoc:
|
---|
2255 |
|
---|
2256 | #--
|
---|
2257 | # for backward compatibility
|
---|
2258 | class HTTP
|
---|
2259 | ProxyMod = ProxyDelta
|
---|
2260 | end
|
---|
2261 | module NetPrivate
|
---|
2262 | HTTPRequest = ::Net::HTTPRequest
|
---|
2263 | end
|
---|
2264 |
|
---|
2265 | HTTPInformationCode = HTTPInformation
|
---|
2266 | HTTPSuccessCode = HTTPSuccess
|
---|
2267 | HTTPRedirectionCode = HTTPRedirection
|
---|
2268 | HTTPRetriableCode = HTTPRedirection
|
---|
2269 | HTTPClientErrorCode = HTTPClientError
|
---|
2270 | HTTPFatalErrorCode = HTTPClientError
|
---|
2271 | HTTPServerErrorCode = HTTPServerError
|
---|
2272 | HTTPResponceReceiver = HTTPResponse
|
---|
2273 |
|
---|
2274 | end # module Net
|
---|