4 # Copyright (c) 1999-2007 Yukihiro Matsumoto
5 # Copyright (c) 1999-2007 Minero Aoki
6 # Copyright (c) 2001 GOTOU Yuuzou
8 # Written and maintained by Minero Aoki <aamine@loveruby.net>.
9 # HTTPS support added by GOTOU Yuuzou <gotoyuzo@notwork.org>.
11 # This file is derived from "http-access.rb".
13 # Documented by Minero Aoki; converted to RDoc by William Webber.
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.
19 # See Net::HTTP for an overview and examples.
21 # NOTE: You can find Japanese version of this document here:
22 # http://www.ruby-lang.org/ja/man/html/net_http.html
28 require 'net/protocol'
34 class HTTPBadResponse < StandardError; end
35 class HTTPHeaderSyntaxError < StandardError; end
38 # == What Is This Library?
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).
47 # === Getting Document From WWW Server
49 # Example #1: Simple GET+print
52 # Net::HTTP.get_print 'www.example.com', '/index.html'
54 # Example #2: Simple GET+print by URL
58 # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
60 # Example #3: More generic GET+print
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')
71 # Example #4: More generic GET+print
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|
82 # === Posting Form Data
88 # res = Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'),
89 # {'q' => 'ruby', 'max' => '50'})
92 # #2: POST with basic authentication
93 # res = Net::HTTP.post_form(URI.parse('http://jack:pass@www.example.com/todo.cgi'),
94 # {'from' => '2005-01-01',
95 # 'to' => '2005-03-31'})
98 # #3: Detailed control
99 # url = URI.parse('http://www.example.com/todo.cgi')
100 # req = Net::HTTP::Post.new(url.path)
101 # req.basic_auth 'jack', 'pass'
102 # req.set_form_data({'from' => '2005-01-01', 'to' => '2005-03-31'}, ';')
103 # res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
105 # when Net::HTTPSuccess, Net::HTTPRedirection
111 # #4: Multiple values
112 # res = Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'),
113 # {'q' => ['ruby', 'perl'], 'max' => '50'})
116 # === Accessing via Proxy
118 # Net::HTTP.Proxy creates http proxy class. It has same
119 # methods of Net::HTTP but its instances always connect to
120 # proxy, instead of given host.
124 # proxy_addr = 'your.proxy.host'
127 # Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com') {|http|
128 # # always connect to your.proxy.addr:8080
132 # Since Net::HTTP.Proxy returns Net::HTTP itself when proxy_addr is nil,
133 # there's no need to change code if there's proxy or not.
135 # There are two additional parameters in Net::HTTP.Proxy which allow to
136 # specify proxy user name and password:
138 # Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user = nil, proxy_pass = nil)
140 # You may use them to work with authorization-enabled proxies:
145 # proxy_host = 'your.proxy.host'
147 # uri = URI.parse(ENV['http_proxy'])
148 # proxy_user, proxy_pass = uri.userinfo.split(/:/) if uri.userinfo
149 # Net::HTTP::Proxy(proxy_host, proxy_port,
150 # proxy_user, proxy_pass).start('www.example.com') {|http|
151 # # always connect to your.proxy.addr:8080 using specified username and password
155 # Note that net/http never rely on HTTP_PROXY environment variable.
156 # If you want to use proxy, set it explicitly.
158 # === Following Redirection
163 # def fetch(uri_str, limit = 10)
164 # # You should choose better exception.
165 # raise ArgumentError, 'HTTP redirect too deep' if limit == 0
167 # response = Net::HTTP.get_response(URI.parse(uri_str))
169 # when Net::HTTPSuccess then response
170 # when Net::HTTPRedirection then fetch(response['location'], limit - 1)
176 # print fetch('http://www.ruby-lang.org')
178 # Net::HTTPSuccess and Net::HTTPRedirection is a HTTPResponse class.
179 # All HTTPResponse objects belong to its own response class which
180 # indicate HTTP result status. For details of response classes,
181 # see section "HTTP Response Classes".
183 # === Basic Authentication
187 # Net::HTTP.start('www.example.com') {|http|
188 # req = Net::HTTP::Get.new('/secret-page.html')
189 # req.basic_auth 'account', 'password'
190 # response = http.request(req)
191 # print response.body
194 # === HTTP Request Classes
196 # Here is HTTP request class hierarchy.
203 # Net::HTTP::Proppatch
207 # Net::HTTP::Propfind
214 # === HTTP Response Classes
216 # Here is HTTP response class hierarchy.
217 # All classes are defined in Net module.
220 # HTTPUnknownResponse
221 # HTTPInformation # 1xx
223 # HTTPSwitchProtocl # 101
228 # HTTPNonAuthoritativeInformation # 203
229 # HTTPNoContent # 204
230 # HTTPResetContent # 205
231 # HTTPPartialContent # 206
232 # HTTPRedirection # 3xx
233 # HTTPMultipleChoice # 300
234 # HTTPMovedPermanently # 301
237 # HTTPNotModified # 304
239 # HTTPTemporaryRedirect # 307
240 # HTTPClientError # 4xx
241 # HTTPBadRequest # 400
242 # HTTPUnauthorized # 401
243 # HTTPPaymentRequired # 402
244 # HTTPForbidden # 403
246 # HTTPMethodNotAllowed # 405
247 # HTTPNotAcceptable # 406
248 # HTTPProxyAuthenticationRequired # 407
249 # HTTPRequestTimeOut # 408
252 # HTTPLengthRequired # 411
253 # HTTPPreconditionFailed # 412
254 # HTTPRequestEntityTooLarge # 413
255 # HTTPRequestURITooLong # 414
256 # HTTPUnsupportedMediaType # 415
257 # HTTPRequestedRangeNotSatisfiable # 416
258 # HTTPExpectationFailed # 417
259 # HTTPServerError # 5xx
260 # HTTPInternalServerError # 500
261 # HTTPNotImplemented # 501
262 # HTTPBadGateway # 502
263 # HTTPServiceUnavailable # 503
264 # HTTPGatewayTimeOut # 504
265 # HTTPVersionNotSupported # 505
267 # == Switching Net::HTTP versions
269 # You can use net/http.rb 1.1 features (bundled with Ruby 1.6)
270 # by calling HTTP.version_1_1. Calling Net::HTTP.version_1_2
271 # allows you to use 1.2 features again.
274 # Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
276 # Net::HTTP.version_1_1
277 # Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
279 # Net::HTTP.version_1_2
280 # Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
282 # This function is NOT thread-safe.
284 class HTTP < Protocol
287 Revision = %q$Revision$.split[1]
292 require 'stringio' #for our purposes (unpacking gzip) lump these together
299 # Turns on net/http 1.2 (ruby 1.8) features.
300 # Defaults to ON in ruby 1.8.
302 # I strongly recommend to call this method always.
305 # Net::HTTP.version_1_2
311 # Turns on net/http 1.1 (ruby 1.6) features.
312 # Defaults to OFF in ruby 1.8.
317 # true if net/http is in version 1.2 mode.
319 def HTTP.version_1_2?
323 # true if net/http is in version 1.1 compatible mode.
325 def HTTP.version_1_1?
330 alias is_version_1_1? version_1_1? #:nodoc:
331 alias is_version_1_2? version_1_2? #:nodoc:
339 # Get body from target and output it to +$stdout+. The
340 # target can either be specified as (+uri+), or as
341 # (+host+, +path+, +port+ = 80); so:
343 # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
347 # Net::HTTP.get_print 'www.example.com', '/index.html'
349 def HTTP.get_print(uri_or_host, path = nil, port = nil)
350 get_response(uri_or_host, path, port) {|res|
351 res.read_body do |chunk|
358 # Send a GET request to the target and return the response
359 # as a string. The target can either be specified as
360 # (+uri+), or as (+host+, +path+, +port+ = 80); so:
362 # print Net::HTTP.get(URI.parse('http://www.example.com/index.html'))
366 # print Net::HTTP.get('www.example.com', '/index.html')
368 def HTTP.get(uri_or_host, path = nil, port = nil)
369 get_response(uri_or_host, path, port).body
372 # Send a GET request to the target and return the response
373 # as a Net::HTTPResponse object. The target can either be specified as
374 # (+uri+), or as (+host+, +path+, +port+ = 80); so:
376 # res = Net::HTTP.get_response(URI.parse('http://www.example.com/index.html'))
381 # res = Net::HTTP.get_response('www.example.com', '/index.html')
384 def HTTP.get_response(uri_or_host, path = nil, port = nil, &block)
387 new(host, port || HTTP.default_port).start {|http|
388 return http.request_get(path, &block)
392 new(uri.host, uri.port).start {|http|
393 return http.request_get(uri.request_uri, &block)
398 # Posts HTML form data to the +URL+.
399 # Form data must be represented as a Hash of String to String, e.g:
401 # { "cmd" => "search", "q" => "ruby", "max" => "50" }
403 # This method also does Basic Authentication iff +URL+.user exists.
410 # HTTP.post_form URI.parse('http://www.example.com/search.cgi'),
411 # { "q" => "ruby", "max" => "50" }
413 def HTTP.post_form(url, params)
414 req = Post.new(url.path)
415 req.form_data = params
416 req.basic_auth url.user, url.password if url.user
417 new(url.host, url.port).start {|http|
423 # HTTP session management
426 # The default port to use for HTTP requests; defaults to 80.
427 def HTTP.default_port
431 # The default port to use for HTTP requests; defaults to 80.
432 def HTTP.http_default_port
436 # The default port to use for HTTPS requests; defaults to 443.
437 def HTTP.https_default_port
441 def HTTP.socket_type #:nodoc: obsolete
445 # creates a new Net::HTTP object and opens its TCP connection and
446 # HTTP session. If the optional block is given, the newly
447 # created Net::HTTP object is passed to it and closed when the
448 # block finishes. In this case, the return value of this method
449 # is the return value of the block. If no block is given, the
450 # return value of this method is the newly created Net::HTTP object
451 # itself, and the caller is responsible for closing it upon completion.
452 def HTTP.start(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil, &block) # :yield: +http+
453 new(address, port, p_addr, p_port, p_user, p_pass).start(&block)
460 # Creates a new Net::HTTP object.
461 # If +proxy_addr+ is given, creates an Net::HTTP object with proxy support.
462 # This method does not open the TCP connection.
463 def HTTP.new(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil)
464 h = Proxy(p_addr, p_port, p_user, p_pass).newobj(address, port)
466 @newimpl = ::Net::HTTP.version_1_2?
471 # Creates a new Net::HTTP object for the specified +address+.
472 # This method does not open the TCP connection.
473 def initialize(address, port = nil)
475 @port = (port || HTTP.default_port)
476 @curr_http_version = HTTPVersion
477 @no_keepalive_server = false
478 @close_on_empty_response = false
486 @enable_post_connection_check = true
488 @sspi_enabled = false
489 if defined?(SSL_ATTRIBUTES)
490 SSL_ATTRIBUTES.each do |name|
491 instance_variable_set "@#{name}", nil
497 "#<#{self.class} #{@address}:#{@port} open=#{started?}>"
500 # *WARNING* This method causes serious security hole.
501 # Never use this method in production code.
503 # Set an output stream for debugging.
505 # http = Net::HTTP.new
506 # http.set_debug_output $stderr
507 # http.start { .... }
509 def set_debug_output(output)
510 warn 'Net::HTTP#set_debug_output called after HTTP started' if started?
511 @debug_output = output
514 # The host name to connect to.
517 # The port number to connect to.
520 # Seconds to wait until connection is opened.
521 # If the HTTP object cannot open a connection in this many seconds,
522 # it raises a TimeoutError exception.
523 attr_accessor :open_timeout
525 # Seconds to wait until reading one block (by one read(2) call).
526 # If the HTTP object cannot open a connection in this many seconds,
527 # it raises a TimeoutError exception.
528 attr_reader :read_timeout
530 # Setter for the read_timeout attribute.
531 def read_timeout=(sec)
532 @socket.read_timeout = sec if @socket
536 # returns true if the HTTP session is started.
541 alias active? started? #:nodoc: obsolete
543 attr_accessor :close_on_empty_response
545 # returns true if use SSL/TLS with HTTP.
547 false # redefined in net/https
550 # Opens TCP connection and HTTP session.
552 # When this method is called with block, gives a HTTP object
553 # to the block and closes the TCP connection / HTTP session
554 # after the block executed.
556 # When called with a block, returns the return value of the
557 # block; otherwise, returns self.
559 def start # :yield: http
560 raise IOError, 'HTTP session already opened' if @started
580 D "opening connection to #{conn_address()}..."
581 s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
584 ssl_parameters = Hash.new
585 SSL_ATTRIBUTES.each do |name|
586 if value = instance_variable_get("@#{name}")
587 ssl_parameters[name] = value
590 @ssl_context = OpenSSL::SSL::SSLContext.new
591 @ssl_context.set_params(ssl_parameters)
592 s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
595 @socket = BufferedIO.new(s)
596 @socket.read_timeout = @read_timeout
597 @socket.debug_output = @debug_output
600 @socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
601 @address, @port, HTTPVersion)
602 @socket.writeline "Host: #{@address}:#{@port}"
604 credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
605 credential.delete!("\r\n")
606 @socket.writeline "Proxy-Authorization: Basic #{credential}"
609 HTTPResponse.read_new(@socket).value
612 if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
613 s.post_connection_check(@address)
624 # Finishes HTTP session and closes TCP connection.
625 # Raises IOError if not started.
627 raise IOError, 'HTTP session not yet started' unless started?
633 @socket.close if @socket and not @socket.closed?
645 @is_proxy_class = false
651 # Creates an HTTP proxy class.
652 # Arguments are address/port of proxy host and username/password
653 # if authorization on proxy server is required.
654 # You can replace the HTTP class with created proxy class.
656 # If ADDRESS is nil, this method returns self (Net::HTTP).
659 # proxy_class = Net::HTTP::Proxy('proxy.example.com', 8080)
661 # proxy_class.start('www.ruby-lang.org') {|http|
662 # # connecting proxy.foo.org:8080
666 def HTTP.Proxy(p_addr, p_port = nil, p_user = nil, p_pass = nil)
667 return self unless p_addr
669 proxyclass = Class.new(self)
670 proxyclass.module_eval {
673 @is_proxy_class = true
674 @proxy_address = p_addr
675 @proxy_port = p_port || default_port()
683 # returns true if self is a class which was created by HTTP::Proxy.
688 attr_reader :proxy_address
689 attr_reader :proxy_port
690 attr_reader :proxy_user
691 attr_reader :proxy_pass
694 # True if self is a HTTP proxy class.
696 self.class.proxy_class?
699 # Address of proxy host. If self does not use a proxy, nil.
701 self.class.proxy_address
704 # Port number of proxy host. If self does not use a proxy, nil.
706 self.class.proxy_port
709 # User name for accessing proxy. If self does not use a proxy, nil.
711 self.class.proxy_user
714 # User password for accessing proxy. If self does not use a proxy, nil.
716 self.class.proxy_pass
719 alias proxyaddr proxy_address #:nodoc: obsolete
720 alias proxyport proxy_port #:nodoc: obsolete
738 module ProxyDelta #:nodoc: internal use only
750 use_ssl? ? path : "http://#{addr_port()}#{path}"
760 # Gets data from +path+ on the connected-to host.
761 # +initheader+ must be a Hash like { 'Accept' => '*/*', ... },
762 # and it defaults to an empty hash.
763 # If +initheader+ doesn't have the key 'accept-encoding', then
764 # a value of "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" is used,
765 # so that gzip compression is used in preference to deflate
766 # compression, which is used in preference to no compression.
767 # Ruby doesn't have libraries to support the compress (Lempel-Ziv)
768 # compression, so that is not supported. The intent of this is
769 # to reduce bandwidth by default. If this routine sets up
770 # compression, then it does the decompression also, removing
771 # the header as well to prevent confusion. Otherwise
772 # it leaves the body as it found it.
774 # In version 1.1 (ruby 1.6), this method returns a pair of objects,
775 # a Net::HTTPResponse object and the entity body string.
776 # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse
779 # If called with a block, yields each fragment of the
780 # entity body in turn as a string as it is read from
781 # the socket. Note that in this case, the returned response
782 # object will *not* contain a (meaningful) body.
784 # +dest+ argument is obsolete.
785 # It still works but you must not use it.
787 # In version 1.1, this method might raise an exception for
788 # 3xx (redirect). In this case you can get a HTTPResponse object
789 # by "anException.response".
791 # In version 1.2, this method never raises exception.
793 # # version 1.1 (bundled with Ruby 1.6)
794 # response, body = http.get('/index.html')
796 # # version 1.2 (bundled with Ruby 1.8 or later)
797 # response = http.get('/index.html')
800 # File.open('result.txt', 'w') {|f|
801 # http.get('/~foo/') do |str|
806 def get(path, initheader = {}, dest = nil, &block) # :yield: +body_segment+
809 unless initheader.keys.any?{|k| k.downcase == "accept-encoding"}
810 initheader["accept-encoding"] = "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
814 request(Get.new(path, initheader)) {|r|
815 if r.key?("content-encoding") and @compression
816 @compression = nil # Clear it till next set.
817 the_body = r.read_body dest, &block
818 case r["content-encoding"]
820 r.body= Zlib::GzipReader.new(StringIO.new(the_body)).read
821 r.delete("content-encoding")
823 r.body= Zlib::Inflate.inflate(the_body);
824 r.delete("content-encoding")
828 ; # Don't do anything dramatic, unless we need to later
831 r.read_body dest, &block
843 # Gets only the header from +path+ on the connected-to host.
844 # +header+ is a Hash like { 'Accept' => '*/*', ... }.
846 # This method returns a Net::HTTPResponse object.
848 # In version 1.1, this method might raise an exception for
849 # 3xx (redirect). On the case you can get a HTTPResponse object
850 # by "anException.response".
851 # In version 1.2, this method never raises an exception.
854 # Net::HTTP.start('some.www.server', 80) {|http|
855 # response = http.head('/index.html')
857 # p response['content-type']
859 def head(path, initheader = nil)
860 res = request(Head.new(path, initheader))
861 res.value unless @newimpl
865 # Posts +data+ (must be a String) to +path+. +header+ must be a Hash
866 # like { 'Accept' => '*/*', ... }.
868 # In version 1.1 (ruby 1.6), this method returns a pair of objects, a
869 # Net::HTTPResponse object and an entity body string.
870 # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse object.
872 # If called with a block, yields each fragment of the
873 # entity body in turn as a string as it are read from
874 # the socket. Note that in this case, the returned response
875 # object will *not* contain a (meaningful) body.
877 # +dest+ argument is obsolete.
878 # It still works but you must not use it.
880 # In version 1.1, this method might raise an exception for
881 # 3xx (redirect). In this case you can get an HTTPResponse object
882 # by "anException.response".
883 # In version 1.2, this method never raises exception.
886 # response, body = http.post('/cgi-bin/search.rb', 'query=foo')
889 # response = http.post('/cgi-bin/search.rb', 'query=foo')
892 # File.open('result.txt', 'w') {|f|
893 # http.post('/cgi-bin/search.rb', 'query=foo') do |str|
898 # You should set Content-Type: header field for POST.
899 # If no Content-Type: field given, this method uses
900 # "application/x-www-form-urlencoded" by default.
902 def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
904 request(Post.new(path, initheader), data) {|r|
905 r.read_body dest, &block
915 def put(path, data, initheader = nil) #:nodoc:
916 res = request(Put.new(path, initheader), data)
917 res.value unless @newimpl
921 # Sends a PROPPATCH request to the +path+ and gets a response,
922 # as an HTTPResponse object.
923 def proppatch(path, body, initheader = nil)
924 request(Proppatch.new(path, initheader), body)
927 # Sends a LOCK request to the +path+ and gets a response,
928 # as an HTTPResponse object.
929 def lock(path, body, initheader = nil)
930 request(Lock.new(path, initheader), body)
933 # Sends a UNLOCK request to the +path+ and gets a response,
934 # as an HTTPResponse object.
935 def unlock(path, body, initheader = nil)
936 request(Unlock.new(path, initheader), body)
939 # Sends a OPTIONS request to the +path+ and gets a response,
940 # as an HTTPResponse object.
941 def options(path, initheader = nil)
942 request(Options.new(path, initheader))
945 # Sends a PROPFIND request to the +path+ and gets a response,
946 # as an HTTPResponse object.
947 def propfind(path, body = nil, initheader = {'Depth' => '0'})
948 request(Propfind.new(path, initheader), body)
951 # Sends a DELETE request to the +path+ and gets a response,
952 # as an HTTPResponse object.
953 def delete(path, initheader = {'Depth' => 'Infinity'})
954 request(Delete.new(path, initheader))
957 # Sends a MOVE request to the +path+ and gets a response,
958 # as an HTTPResponse object.
959 def move(path, initheader = nil)
960 request(Move.new(path, initheader))
963 # Sends a COPY request to the +path+ and gets a response,
964 # as an HTTPResponse object.
965 def copy(path, initheader = nil)
966 request(Copy.new(path, initheader))
969 # Sends a MKCOL request to the +path+ and gets a response,
970 # as an HTTPResponse object.
971 def mkcol(path, body = nil, initheader = nil)
972 request(Mkcol.new(path, initheader), body)
975 # Sends a TRACE request to the +path+ and gets a response,
976 # as an HTTPResponse object.
977 def trace(path, initheader = nil)
978 request(Trace.new(path, initheader))
981 # Sends a GET request to the +path+ and gets a response,
982 # as an HTTPResponse object.
984 # When called with a block, yields an HTTPResponse object.
985 # The body of this response will not have been read yet;
986 # the caller can process it using HTTPResponse#read_body,
989 # Returns the response.
991 # This method never raises Net::* exceptions.
993 # response = http.request_get('/index.html')
994 # # The entity body is already read here.
995 # p response['content-type']
999 # http.request_get('/index.html') {|response|
1000 # p response['content-type']
1001 # response.read_body do |str| # read body now
1006 def request_get(path, initheader = nil, &block) # :yield: +response+
1007 request(Get.new(path, initheader), &block)
1010 # Sends a HEAD request to the +path+ and gets a response,
1011 # as an HTTPResponse object.
1013 # Returns the response.
1015 # This method never raises Net::* exceptions.
1017 # response = http.request_head('/index.html')
1018 # p response['content-type']
1020 def request_head(path, initheader = nil, &block)
1021 request(Head.new(path, initheader), &block)
1024 # Sends a POST request to the +path+ and gets a response,
1025 # as an HTTPResponse object.
1027 # When called with a block, yields an HTTPResponse object.
1028 # The body of this response will not have been read yet;
1029 # the caller can process it using HTTPResponse#read_body,
1032 # Returns the response.
1034 # This method never raises Net::* exceptions.
1037 # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
1039 # puts response.body # body is already read
1042 # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
1044 # p response['content-type']
1045 # response.read_body do |str| # read body now
1050 def request_post(path, data, initheader = nil, &block) # :yield: +response+
1051 request Post.new(path, initheader), data, &block
1054 def request_put(path, data, initheader = nil, &block) #:nodoc:
1055 request Put.new(path, initheader), data, &block
1058 alias get2 request_get #:nodoc: obsolete
1059 alias head2 request_head #:nodoc: obsolete
1060 alias post2 request_post #:nodoc: obsolete
1061 alias put2 request_put #:nodoc: obsolete
1064 # Sends an HTTP request to the HTTP server.
1065 # This method also sends DATA string if DATA is given.
1067 # Returns a HTTPResponse object.
1069 # This method never raises Net::* exceptions.
1071 # response = http.send_request('GET', '/index.html')
1072 # puts response.body
1074 def send_request(name, path, data = nil, header = nil)
1075 r = HTTPGenericRequest.new(name,(data ? true : false),true,path,header)
1079 # Sends an HTTPRequest object REQUEST to the HTTP server.
1080 # This method also sends DATA string if REQUEST is a post/put request.
1081 # Giving DATA for get/head request causes ArgumentError.
1083 # When called with a block, yields an HTTPResponse object.
1084 # The body of this response will not have been read yet;
1085 # the caller can process it using HTTPResponse#read_body,
1088 # Returns a HTTPResponse object.
1090 # This method never raises Net::* exceptions.
1092 def request(req, body = nil, &block) # :yield: +response+
1095 req['connection'] ||= 'close'
1096 return request(req, body, &block)
1100 req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl?
1102 req.set_body_internal body
1103 res = transport_request(req, &block)
1106 res = transport_request(req, &block)
1113 def transport_request(req)
1115 req.exec @socket, @curr_http_version, edit_path(req.path)
1117 res = HTTPResponse.read_new(@socket)
1118 end while res.kind_of?(HTTPContinue)
1119 res.reading_body(@socket, req.response_body_permitted?) {
1120 yield res if block_given?
1122 end_transport req, res
1126 def begin_transport(req)
1127 connect if @socket.closed?
1128 if not req.response_body_permitted? and @close_on_empty_response
1129 req['connection'] ||= 'close'
1131 req['host'] ||= addr_port()
1134 def end_transport(req, res)
1135 @curr_http_version = res.http_version
1137 D 'Conn socket closed'
1138 elsif not res.body and @close_on_empty_response
1141 elsif keep_alive?(req, res)
1149 def keep_alive?(req, res)
1150 return false if req.connection_close?
1151 if @curr_http_version <= '1.0'
1152 res.connection_keep_alive?
1153 else # HTTP/1.1 or later
1154 not res.connection_close?
1159 return false unless @sspi_enabled
1160 if res.kind_of?(HTTPProxyAuthenticationRequired) and
1161 proxy? and res["Proxy-Authenticate"].include?("Negotiate")
1163 require 'win32/sspi'
1174 n = Win32::SSPI::NegotiateAuth.new
1175 req["Proxy-Authorization"] = "Negotiate #{n.get_initial_token}"
1176 # Some versions of ISA will close the connection if this isn't present.
1177 req["Connection"] = "Keep-Alive"
1178 req["Proxy-Connection"] = "Keep-Alive"
1179 res = transport_request(req)
1180 authphrase = res["Proxy-Authenticate"] or return res
1181 req["Proxy-Authorization"] = "Negotiate #{n.complete_authentication(authphrase)}"
1183 raise HTTPAuthenticationError.new('HTTP authentication failed', err)
1194 address() + (port == HTTP.https_default_port ? '' : ":#{port()}")
1196 address() + (port == HTTP.http_default_port ? '' : ":#{port()}")
1201 return unless @debug_output
1202 @debug_output << msg
1203 @debug_output << "\n"
1214 # Provides access to @header in the mixed-into class as a hash-like
1215 # object, except with case-insensitive keys. Also provides
1216 # methods for accessing commonly-used header values in a more
1217 # convenient format.
1221 def initialize_http_header(initheader)
1223 return unless initheader
1224 initheader.each do |key, value|
1225 warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
1226 @header[key.downcase] = [value.strip]
1230 def size #:nodoc: obsolete
1234 alias length size #:nodoc: obsolete
1236 # Returns the header field corresponding to the case-insensitive key.
1237 # For example, a key of "Content-Type" might return "text/html"
1239 a = @header[key.downcase] or return nil
1243 # Sets the header field corresponding to the case-insensitive key.
1246 @header.delete key.downcase
1249 @header[key.downcase] = [val]
1253 # Adds header field instead of replace.
1254 # Second argument +val+ must be a String.
1255 # See also #[]=, #[] and #get_fields.
1257 # request.add_field 'X-My-Header', 'a'
1258 # p request['X-My-Header'] #=> "a"
1259 # p request.get_fields('X-My-Header') #=> ["a"]
1260 # request.add_field 'X-My-Header', 'b'
1261 # p request['X-My-Header'] #=> "a, b"
1262 # p request.get_fields('X-My-Header') #=> ["a", "b"]
1263 # request.add_field 'X-My-Header', 'c'
1264 # p request['X-My-Header'] #=> "a, b, c"
1265 # p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
1267 def add_field(key, val)
1268 if @header.key?(key.downcase)
1269 @header[key.downcase].push val
1271 @header[key.downcase] = [val]
1276 # Returns an array of header field strings corresponding to the
1277 # case-insensitive +key+. This method allows you to get duplicated
1278 # header fields without any processing. See also #[].
1280 # p response.get_fields('Set-Cookie')
1281 # #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
1282 # "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
1283 # p response['Set-Cookie']
1284 # #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
1287 return nil unless @header[key.downcase]
1288 @header[key.downcase].dup
1291 # Returns the header field corresponding to the case-insensitive key.
1292 # Returns the default value +args+, or the result of the block, or nil,
1293 # if there's no header field named key. See Hash#fetch
1294 def fetch(key, *args, &block) #:yield: +key+
1295 a = @header.fetch(key.downcase, *args, &block)
1299 # Iterates for each header names and values.
1300 def each_header #:yield: +key+, +value+
1301 @header.each do |k,va|
1302 yield k, va.join(', ')
1306 alias each each_header
1308 # Iterates for each header names.
1309 def each_name(&block) #:yield: +key+
1310 @header.each_key(&block)
1313 alias each_key each_name
1315 # Iterates for each capitalized header names.
1316 def each_capitalized_name(&block) #:yield: +key+
1317 @header.each_key do |k|
1322 # Iterates for each header values.
1323 def each_value #:yield: +value+
1324 @header.each_value do |va|
1329 # Removes a header field.
1331 @header.delete(key.downcase)
1334 # true if +key+ header exists.
1336 @header.key?(key.downcase)
1339 # Returns a Hash consist of header names and values.
1344 # As for #each_header, except the keys are provided in capitalized form.
1345 def each_capitalized
1346 @header.each do |k,v|
1347 yield capitalize(k), v.join(', ')
1351 alias canonical_each each_capitalized
1353 def capitalize(name)
1354 name.split(/-/).map {|s| s.capitalize }.join('-')
1358 # Returns an Array of Range objects which represents Range: header field,
1359 # or +nil+ if there is no such header.
1361 return nil unless @header['range']
1362 self['Range'].split(/,/).map {|spec|
1363 m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
1364 raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
1367 if m[1] and m[2] then d1..d2
1368 elsif m[1] then d1..-1
1369 elsif m[2] then -d2..-1
1371 raise HTTPHeaderSyntaxError, 'range is not specified'
1376 # Set Range: header from Range (arg r) or beginning index and
1377 # length from it (arg idx&len).
1379 # req.range = (0..1023)
1380 # req.set_range 0, 1023
1382 def set_range(r, e = nil)
1384 @header.delete 'range'
1391 rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}")
1395 last -= 1 if r.exclude_end?
1397 rangestr = (first > 0 ? "#{first}-" : "-#{-first}")
1399 raise HTTPHeaderSyntaxError, 'range.first is negative' if first < 0
1400 raise HTTPHeaderSyntaxError, 'range.last is negative' if last < 0
1401 raise HTTPHeaderSyntaxError, 'must be .first < .last' if first > last
1402 rangestr = "#{first}-#{last}"
1405 raise TypeError, 'Range/Integer is required'
1407 @header['range'] = ["bytes=#{rangestr}"]
1411 alias range= set_range
1413 # Returns an Integer object which represents the Content-Length: header field
1414 # or +nil+ if that field is not provided.
1416 return nil unless key?('Content-Length')
1417 len = self['Content-Length'].slice(/\d+/) or
1418 raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
1422 def content_length=(len)
1424 @header.delete 'content-length'
1427 @header['content-length'] = [len.to_i.to_s]
1430 # Returns "true" if the "transfer-encoding" header is present and
1431 # set to "chunked". This is an HTTP/1.1 feature, allowing the
1432 # the content to be sent in "chunks" without at the outset
1433 # stating the entire content length.
1435 return false unless @header['transfer-encoding']
1436 field = self['Transfer-Encoding']
1437 (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
1440 # Returns a Range object which represents Content-Range: header field.
1441 # This indicates, for a partial entity body, where this fragment
1442 # fits inside the full entity body, as range of byte offsets.
1444 return nil unless @header['content-range']
1445 m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or
1446 raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
1447 m[1].to_i .. m[2].to_i + 1
1450 # The length of the range represented in Content-Range: header.
1452 r = content_range() or return nil
1456 # Returns a content type string such as "text/html".
1457 # This method returns nil if Content-Type: header field does not exist.
1459 return nil unless main_type()
1461 then "#{main_type()}/#{sub_type()}"
1466 # Returns a content type string such as "text".
1467 # This method returns nil if Content-Type: header field does not exist.
1469 return nil unless @header['content-type']
1470 self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
1473 # Returns a content type string such as "html".
1474 # This method returns nil if Content-Type: header field does not exist
1475 # or sub-type is not given (e.g. "Content-Type: text").
1477 return nil unless @header['content-type']
1478 main, sub = *self['Content-Type'].split(';').first.to_s.split('/')
1479 return nil unless sub
1483 # Returns content type parameters as a Hash as like
1484 # {"charset" => "iso-2022-jp"}.
1487 list = self['Content-Type'].to_s.split(';')
1489 list.each do |param|
1490 k, v = *param.split('=', 2)
1491 result[k.strip] = v.strip
1496 # Set Content-Type: header field by +type+ and +params+.
1497 # +type+ must be a String, +params+ must be a Hash.
1498 def set_content_type(type, params = {})
1499 @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
1502 alias content_type= set_content_type
1504 # Set header fields and a body from HTML form data.
1505 # +params+ should be a Hash containing HTML form data.
1506 # Optional argument +sep+ means data record separator.
1508 # This method also set Content-Type: header field to
1509 # application/x-www-form-urlencoded.
1512 # http.form_data = {"q" => "ruby", "lang" => "en"}
1513 # http.form_data = {"q" => ["ruby", "perl"], "lang" => "en"}
1514 # http.set_form_data({"q" => "ruby", "lang" => "en"}, ';')
1516 def set_form_data(params, sep = '&')
1517 self.body = params.map {|k, v| encode_kvpair(k, v) }.flatten.join(sep)
1518 self.content_type = 'application/x-www-form-urlencoded'
1521 alias form_data= set_form_data
1523 def encode_kvpair(k, vs)
1524 Array(vs).map {|v| "#{urlencode(k)}=#{urlencode(v.to_s)}" }
1526 private :encode_kvpair
1529 str.gsub(/[^a-zA-Z0-9_\.\-]/n) { sprintf('%%%02x', $&[0]) }
1533 # Set the Authorization: header for "Basic" authorization.
1534 def basic_auth(account, password)
1535 @header['authorization'] = [basic_encode(account, password)]
1538 # Set Proxy-Authorization: header for "Basic" authorization.
1539 def proxy_basic_auth(account, password)
1540 @header['proxy-authorization'] = [basic_encode(account, password)]
1543 def basic_encode(account, password)
1544 'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n")
1546 private :basic_encode
1548 def connection_close?
1549 tokens(@header['connection']).include?('close') or
1550 tokens(@header['proxy-connection']).include?('close')
1553 def connection_keep_alive?
1554 tokens(@header['connection']).include?('keep-alive') or
1555 tokens(@header['proxy-connection']).include?('keep-alive')
1559 return [] unless vals
1560 vals.map {|v| v.split(',') }.flatten\
1561 .reject {|str| str.strip.empty? }\
1562 .map {|tok| tok.strip.downcase }
1570 # Parent of HTTPRequest class. Do not use this directly; use
1571 # a subclass of HTTPRequest.
1573 # Mixes in the HTTPHeader module.
1575 class HTTPGenericRequest
1579 def initialize(m, reqbody, resbody, path, initheader = nil)
1581 @request_has_body = reqbody
1582 @response_has_body = resbody
1583 raise ArgumentError, "no HTTP request path given" unless path
1584 raise ArgumentError, "HTTP request path is empty" if path.empty?
1586 initialize_http_header initheader
1587 self['Accept'] ||= '*/*'
1588 self['User-Agent'] ||= 'Ruby'
1597 "\#<#{self.class} #{@method}>"
1600 def request_body_permitted?
1604 def response_body_permitted?
1609 warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE
1610 response_body_permitted?
1621 attr_reader :body_stream
1623 def body_stream=(input)
1625 @body_stream = input
1629 def set_body_internal(str) #:nodoc: internal use only
1630 raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream)
1631 self.body = str if str
1638 def exec(sock, ver, path) #:nodoc: internal use only
1640 send_request_with_body sock, ver, path, @body
1642 send_request_with_body_stream sock, ver, path, @body_stream
1644 write_header sock, ver, path
1650 def send_request_with_body(sock, ver, path, body)
1651 self.content_length = body.bytesize
1652 delete 'Transfer-Encoding'
1653 supply_default_content_type
1654 write_header sock, ver, path
1658 def send_request_with_body_stream(sock, ver, path, f)
1659 unless content_length() or chunked?
1660 raise ArgumentError,
1661 "Content-Length not given and Transfer-Encoding is not `chunked'"
1663 supply_default_content_type
1664 write_header sock, ver, path
1666 while s = f.read(1024)
1667 sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
1669 sock.write "0\r\n\r\n"
1671 while s = f.read(1024)
1677 def supply_default_content_type
1678 return if content_type()
1679 warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
1680 set_content_type 'application/x-www-form-urlencoded'
1683 def write_header(sock, ver, path)
1684 buf = "#{@method} #{path} HTTP/#{ver}\r\n"
1685 each_capitalized do |k,v|
1686 buf << "#{k}: #{v}\r\n"
1696 # HTTP request class. This class wraps request header and entity path.
1697 # You *must* use its subclass, Net::HTTP::Get, Post, Head.
1699 class HTTPRequest < HTTPGenericRequest
1701 # Creates HTTP request object.
1702 def initialize(path, initheader = nil)
1703 super self.class::METHOD,
1704 self.class::REQUEST_HAS_BODY,
1705 self.class::RESPONSE_HAS_BODY,
1713 # HTTP 1.1 methods --- RFC2616
1716 class Get < HTTPRequest
1718 REQUEST_HAS_BODY = false
1719 RESPONSE_HAS_BODY = true
1722 class Head < HTTPRequest
1724 REQUEST_HAS_BODY = false
1725 RESPONSE_HAS_BODY = false
1728 class Post < HTTPRequest
1730 REQUEST_HAS_BODY = true
1731 RESPONSE_HAS_BODY = true
1734 class Put < HTTPRequest
1736 REQUEST_HAS_BODY = true
1737 RESPONSE_HAS_BODY = true
1740 class Delete < HTTPRequest
1742 REQUEST_HAS_BODY = false
1743 RESPONSE_HAS_BODY = true
1746 class Options < HTTPRequest
1748 REQUEST_HAS_BODY = false
1749 RESPONSE_HAS_BODY = false
1752 class Trace < HTTPRequest
1754 REQUEST_HAS_BODY = false
1755 RESPONSE_HAS_BODY = true
1759 # WebDAV methods --- RFC2518
1762 class Propfind < HTTPRequest
1764 REQUEST_HAS_BODY = true
1765 RESPONSE_HAS_BODY = true
1768 class Proppatch < HTTPRequest
1769 METHOD = 'PROPPATCH'
1770 REQUEST_HAS_BODY = true
1771 RESPONSE_HAS_BODY = true
1774 class Mkcol < HTTPRequest
1776 REQUEST_HAS_BODY = true
1777 RESPONSE_HAS_BODY = true
1780 class Copy < HTTPRequest
1782 REQUEST_HAS_BODY = false
1783 RESPONSE_HAS_BODY = true
1786 class Move < HTTPRequest
1788 REQUEST_HAS_BODY = false
1789 RESPONSE_HAS_BODY = true
1792 class Lock < HTTPRequest
1794 REQUEST_HAS_BODY = true
1795 RESPONSE_HAS_BODY = true
1798 class Unlock < HTTPRequest
1800 REQUEST_HAS_BODY = true
1801 RESPONSE_HAS_BODY = true
1810 # HTTP exception class.
1811 # You must use its subclasses.
1812 module HTTPExceptions
1813 def initialize(msg, res) #:nodoc:
1817 attr_reader :response
1818 alias data response #:nodoc: obsolete
1820 class HTTPError < ProtocolError
1821 include HTTPExceptions
1823 class HTTPRetriableError < ProtoRetriableError
1824 include HTTPExceptions
1826 class HTTPServerException < ProtoServerError
1827 # We cannot use the name "HTTPServerError", it is the name of the response.
1828 include HTTPExceptions
1830 class HTTPFatalError < ProtoFatalError
1831 include HTTPExceptions
1835 # HTTP response class. This class wraps response header and entity.
1836 # Mixes in the HTTPHeader module, which provides access to response
1837 # header values both via hash-like methods and individual readers.
1838 # Note that each possible HTTP response code defines its own
1839 # HTTPResponse subclass. These are listed below.
1841 # defined under the Net module. Indentation indicates inheritance.
1845 # 1xx HTTPInformation
1847 # 101 HTTPSwitchProtocol
1853 # 203 HTTPNonAuthoritativeInformation
1855 # 205 HTTPResetContent
1856 # 206 HTTPPartialContent
1858 # 3xx HTTPRedirection
1859 # 300 HTTPMultipleChoice
1860 # 301 HTTPMovedPermanently
1863 # 304 HTTPNotModified
1865 # 307 HTTPTemporaryRedirect
1867 # 4xx HTTPClientError
1868 # 400 HTTPBadRequest
1869 # 401 HTTPUnauthorized
1870 # 402 HTTPPaymentRequired
1873 # 405 HTTPMethodNotAllowed
1874 # 406 HTTPNotAcceptable
1875 # 407 HTTPProxyAuthenticationRequired
1876 # 408 HTTPRequestTimeOut
1879 # 411 HTTPLengthRequired
1880 # 412 HTTPPreconditionFailed
1881 # 413 HTTPRequestEntityTooLarge
1882 # 414 HTTPRequestURITooLong
1883 # 415 HTTPUnsupportedMediaType
1884 # 416 HTTPRequestedRangeNotSatisfiable
1885 # 417 HTTPExpectationFailed
1887 # 5xx HTTPServerError
1888 # 500 HTTPInternalServerError
1889 # 501 HTTPNotImplemented
1890 # 502 HTTPBadGateway
1891 # 503 HTTPServiceUnavailable
1892 # 504 HTTPGatewayTimeOut
1893 # 505 HTTPVersionNotSupported
1895 # xxx HTTPUnknownResponse
1898 # true if the response has body.
1899 def HTTPResponse.body_permitted?
1903 def HTTPResponse.exception_type # :nodoc: internal use only
1904 self::EXCEPTION_TYPE
1906 end # reopened after
1910 class HTTPUnknownResponse < HTTPResponse
1912 EXCEPTION_TYPE = HTTPError
1914 class HTTPInformation < HTTPResponse # 1xx
1916 EXCEPTION_TYPE = HTTPError
1918 class HTTPSuccess < HTTPResponse # 2xx
1920 EXCEPTION_TYPE = HTTPError
1922 class HTTPRedirection < HTTPResponse # 3xx
1924 EXCEPTION_TYPE = HTTPRetriableError
1926 class HTTPClientError < HTTPResponse # 4xx
1928 EXCEPTION_TYPE = HTTPServerException # for backward compatibility
1930 class HTTPServerError < HTTPResponse # 5xx
1932 EXCEPTION_TYPE = HTTPFatalError # for backward compatibility
1935 class HTTPContinue < HTTPInformation # 100
1938 class HTTPSwitchProtocol < HTTPInformation # 101
1942 class HTTPOK < HTTPSuccess # 200
1945 class HTTPCreated < HTTPSuccess # 201
1948 class HTTPAccepted < HTTPSuccess # 202
1951 class HTTPNonAuthoritativeInformation < HTTPSuccess # 203
1954 class HTTPNoContent < HTTPSuccess # 204
1957 class HTTPResetContent < HTTPSuccess # 205
1960 class HTTPPartialContent < HTTPSuccess # 206
1964 class HTTPMultipleChoice < HTTPRedirection # 300
1967 class HTTPMovedPermanently < HTTPRedirection # 301
1970 class HTTPFound < HTTPRedirection # 302
1973 HTTPMovedTemporarily = HTTPFound
1974 class HTTPSeeOther < HTTPRedirection # 303
1977 class HTTPNotModified < HTTPRedirection # 304
1980 class HTTPUseProxy < HTTPRedirection # 305
1984 class HTTPTemporaryRedirect < HTTPRedirection # 307
1988 class HTTPBadRequest < HTTPClientError # 400
1991 class HTTPUnauthorized < HTTPClientError # 401
1994 class HTTPPaymentRequired < HTTPClientError # 402
1997 class HTTPForbidden < HTTPClientError # 403
2000 class HTTPNotFound < HTTPClientError # 404
2003 class HTTPMethodNotAllowed < HTTPClientError # 405
2006 class HTTPNotAcceptable < HTTPClientError # 406
2009 class HTTPProxyAuthenticationRequired < HTTPClientError # 407
2012 class HTTPRequestTimeOut < HTTPClientError # 408
2015 class HTTPConflict < HTTPClientError # 409
2018 class HTTPGone < HTTPClientError # 410
2021 class HTTPLengthRequired < HTTPClientError # 411
2024 class HTTPPreconditionFailed < HTTPClientError # 412
2027 class HTTPRequestEntityTooLarge < HTTPClientError # 413
2030 class HTTPRequestURITooLong < HTTPClientError # 414
2033 HTTPRequestURITooLarge = HTTPRequestURITooLong
2034 class HTTPUnsupportedMediaType < HTTPClientError # 415
2037 class HTTPRequestedRangeNotSatisfiable < HTTPClientError # 416
2040 class HTTPExpectationFailed < HTTPClientError # 417
2044 class HTTPInternalServerError < HTTPServerError # 500
2047 class HTTPNotImplemented < HTTPServerError # 501
2050 class HTTPBadGateway < HTTPServerError # 502
2053 class HTTPServiceUnavailable < HTTPServerError # 503
2056 class HTTPGatewayTimeOut < HTTPServerError # 504
2059 class HTTPVersionNotSupported < HTTPServerError # 505
2066 class HTTPResponse # reopen
2068 CODE_CLASS_TO_OBJ = {
2069 '1' => HTTPInformation,
2071 '3' => HTTPRedirection,
2072 '4' => HTTPClientError,
2073 '5' => HTTPServerError
2076 '100' => HTTPContinue,
2077 '101' => HTTPSwitchProtocol,
2080 '201' => HTTPCreated,
2081 '202' => HTTPAccepted,
2082 '203' => HTTPNonAuthoritativeInformation,
2083 '204' => HTTPNoContent,
2084 '205' => HTTPResetContent,
2085 '206' => HTTPPartialContent,
2087 '300' => HTTPMultipleChoice,
2088 '301' => HTTPMovedPermanently,
2090 '303' => HTTPSeeOther,
2091 '304' => HTTPNotModified,
2092 '305' => HTTPUseProxy,
2093 '307' => HTTPTemporaryRedirect,
2095 '400' => HTTPBadRequest,
2096 '401' => HTTPUnauthorized,
2097 '402' => HTTPPaymentRequired,
2098 '403' => HTTPForbidden,
2099 '404' => HTTPNotFound,
2100 '405' => HTTPMethodNotAllowed,
2101 '406' => HTTPNotAcceptable,
2102 '407' => HTTPProxyAuthenticationRequired,
2103 '408' => HTTPRequestTimeOut,
2104 '409' => HTTPConflict,
2106 '411' => HTTPLengthRequired,
2107 '412' => HTTPPreconditionFailed,
2108 '413' => HTTPRequestEntityTooLarge,
2109 '414' => HTTPRequestURITooLong,
2110 '415' => HTTPUnsupportedMediaType,
2111 '416' => HTTPRequestedRangeNotSatisfiable,
2112 '417' => HTTPExpectationFailed,
2114 '500' => HTTPInternalServerError,
2115 '501' => HTTPNotImplemented,
2116 '502' => HTTPBadGateway,
2117 '503' => HTTPServiceUnavailable,
2118 '504' => HTTPGatewayTimeOut,
2119 '505' => HTTPVersionNotSupported
2122 class << HTTPResponse
2123 def read_new(sock) #:nodoc: internal use only
2124 httpv, code, msg = read_status_line(sock)
2125 res = response_class(code).new(httpv, code, msg)
2126 each_response_header(sock) do |k,v|
2134 def read_status_line(sock)
2136 m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or
2137 raise HTTPBadResponse, "wrong status line: #{str.dump}"
2141 def response_class(code)
2142 CODE_TO_OBJ[code] or
2143 CODE_CLASS_TO_OBJ[code[0,1]] or
2147 def each_response_header(sock)
2149 line = sock.readuntil("\n", true).sub(/\s+\z/, '')
2150 break if line.empty?
2151 m = /\A([^:]+):\s*/.match(line) or
2152 raise HTTPBadResponse, 'wrong header line format'
2153 yield m[1], m.post_match
2158 # next is to fix bug in RDoc, where the private inside class << self
2164 def initialize(httpv, code, msg) #:nodoc: internal use only
2165 @http_version = httpv
2168 initialize_http_header nil
2173 # The HTTP version supported by the server.
2174 attr_reader :http_version
2176 # HTTP result code string. For example, '302'. You can also
2177 # determine the response type by which response subclass the
2178 # response object is an instance of.
2181 # HTTP result message. For example, 'Not Found'.
2182 attr_reader :message
2183 alias msg message # :nodoc: obsolete
2186 "#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
2189 # For backward compatibility.
2190 # To allow Net::HTTP 1.1 style assignment
2192 # response, body = Net::HTTP.get(....)
2195 warn "net/http.rb: warning: Net::HTTP v1.1 style assignment found at #{caller(1)[0]}; use `response = http.get(...)' instead." if $VERBOSE
2204 # response <-> exception relationship
2207 def code_type #:nodoc:
2212 raise error_type().new(@code + ' ' + @message.dump, self)
2215 def error_type #:nodoc:
2216 self.class::EXCEPTION_TYPE
2219 # Raises HTTP error if the response is not 2xx.
2221 error! unless self.kind_of?(HTTPSuccess)
2225 # header (for backward compatibility only; DO NOT USE)
2228 def response #:nodoc:
2229 warn "#{caller(1)[0]}: warning: HTTPResponse#response is obsolete" if $VERBOSE
2234 warn "#{caller(1)[0]}: warning: HTTPResponse#header is obsolete" if $VERBOSE
2238 def read_header #:nodoc:
2239 warn "#{caller(1)[0]}: warning: HTTPResponse#read_header is obsolete" if $VERBOSE
2247 def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only
2249 @body_exist = reqmethodallowbody && self.class.body_permitted?
2252 self.body # ensure to read body
2258 # Gets entity body. If the block given, yields it to +block+.
2259 # The body is provided in fragments, as it is read in from the socket.
2261 # Calling this method a second or subsequent time will return the
2262 # already read string.
2264 # http.request_get('/index.html') {|res|
2265 # puts res.read_body
2268 # http.request_get('/index.html') {|res|
2269 # p res.read_body.object_id # 538149362
2270 # p res.read_body.object_id # 538149362
2274 # http.request_get('/index.html') {|res|
2275 # res.read_body do |segment|
2280 def read_body(dest = nil, &block)
2282 raise IOError, "#{self.class}\#read_body called twice" if dest or block
2285 to = procdest(dest, block)
2298 # Returns the entity body.
2300 # Calling this method a second or subsequent time will return the
2301 # already read string.
2303 # http.request_get('/index.html') {|res|
2307 # http.request_get('/index.html') {|res|
2308 # p res.body.object_id # 538149362
2309 # p res.body.object_id # 538149362
2316 # Because it may be necessary to modify the body, Eg, decompression
2317 # this method facilitates that.
2322 alias entity body #:nodoc: obsolete
2326 def read_body_0(dest)
2331 clen = content_length()
2333 @socket.read clen, dest, true # ignore EOF
2336 clen = range_length()
2338 @socket.read clen, dest
2341 @socket.read_all dest
2344 def read_chunked(dest)
2348 line = @socket.readline
2349 hexlen = line.slice(/[0-9a-fA-F]+/) or
2350 raise HTTPBadResponse, "wrong chunk size line: #{line}"
2353 @socket.read len, dest; total += len
2354 @socket.read 2 # \r\n
2356 until @socket.readline.empty?
2362 raise IOError, 'attempt to read body out of block' if @socket.closed?
2365 def procdest(dest, block)
2366 raise ArgumentError, 'both arg and block given for HTTP method' \
2369 ReadAdapter.new(block)
2381 # for backward compatibility
2383 ProxyMod = ProxyDelta
2386 HTTPRequest = ::Net::HTTPRequest
2389 HTTPInformationCode = HTTPInformation
2390 HTTPSuccessCode = HTTPSuccess
2391 HTTPRedirectionCode = HTTPRedirection
2392 HTTPRetriableCode = HTTPRedirection
2393 HTTPClientErrorCode = HTTPClientError
2394 HTTPFatalErrorCode = HTTPClientError
2395 HTTPServerErrorCode = HTTPServerError
2396 HTTPResponceReceiver = HTTPResponse