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

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

Video extension to Greenstone

File size: 63.7 KB
Line 
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
28require 'net/protocol'
29require 'uri'
30
31module 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
2274end # module Net
Note: See TracBrowser for help on using the repository browser.