diff options
Diffstat (limited to 'ruby_2_2/lib/net/http.rb')
-rw-r--r-- | ruby_2_2/lib/net/http.rb | 1559 |
1 files changed, 0 insertions, 1559 deletions
diff --git a/ruby_2_2/lib/net/http.rb b/ruby_2_2/lib/net/http.rb deleted file mode 100644 index bceee1c069..0000000000 --- a/ruby_2_2/lib/net/http.rb +++ /dev/null @@ -1,1559 +0,0 @@ -# -# = net/http.rb -# -# Copyright (c) 1999-2007 Yukihiro Matsumoto -# Copyright (c) 1999-2007 Minero Aoki -# Copyright (c) 2001 GOTOU Yuuzou -# -# Written and maintained by Minero Aoki <aamine@loveruby.net>. -# HTTPS support added by GOTOU Yuuzou <gotoyuzo@notwork.org>. -# -# This file is derived from "http-access.rb". -# -# Documented by Minero Aoki; converted to RDoc by William Webber. -# -# This program is free software. You can re-distribute and/or -# modify this program under the same terms of ruby itself --- -# Ruby Distribution License or GNU General Public License. -# -# See Net::HTTP for an overview and examples. -# - -require 'net/protocol' -require 'uri' - -module Net #:nodoc: - autoload :OpenSSL, 'openssl' - - # :stopdoc: - class HTTPBadResponse < StandardError; end - class HTTPHeaderSyntaxError < StandardError; end - # :startdoc: - - # == An HTTP client API for Ruby. - # - # Net::HTTP provides a rich library which can be used to build HTTP - # user-agents. For more details about HTTP see - # [RFC2616](http://www.ietf.org/rfc/rfc2616.txt) - # - # Net::HTTP is designed to work closely with URI. URI::HTTP#host, - # URI::HTTP#port and URI::HTTP#request_uri are designed to work with - # Net::HTTP. - # - # If you are only performing a few GET requests you should try OpenURI. - # - # == Simple Examples - # - # All examples assume you have loaded Net::HTTP with: - # - # require 'net/http' - # - # This will also require 'uri' so you don't need to require it separately. - # - # The Net::HTTP methods in the following section do not persist - # connections. They are not recommended if you are performing many HTTP - # requests. - # - # === GET - # - # Net::HTTP.get('example.com', '/index.html') # => String - # - # === GET by URI - # - # uri = URI('http://example.com/index.html?count=10') - # Net::HTTP.get(uri) # => String - # - # === GET with Dynamic Parameters - # - # uri = URI('http://example.com/index.html') - # params = { :limit => 10, :page => 3 } - # uri.query = URI.encode_www_form(params) - # - # res = Net::HTTP.get_response(uri) - # puts res.body if res.is_a?(Net::HTTPSuccess) - # - # === POST - # - # uri = URI('http://www.example.com/search.cgi') - # res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50') - # puts res.body - # - # === POST with Multiple Values - # - # uri = URI('http://www.example.com/search.cgi') - # res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50') - # puts res.body - # - # == How to use Net::HTTP - # - # The following example code can be used as the basis of a HTTP user-agent - # which can perform a variety of request types using persistent - # connections. - # - # uri = URI('http://example.com/some_path?query=string') - # - # Net::HTTP.start(uri.host, uri.port) do |http| - # request = Net::HTTP::Get.new uri - # - # response = http.request request # Net::HTTPResponse object - # end - # - # Net::HTTP::start immediately creates a connection to an HTTP server which - # is kept open for the duration of the block. The connection will remain - # open for multiple requests in the block if the server indicates it - # supports persistent connections. - # - # The request types Net::HTTP supports are listed below in the section "HTTP - # Request Classes". - # - # If you wish to re-use a connection across multiple HTTP requests without - # automatically closing it you can use ::new instead of ::start. #request - # will automatically open a connection to the server if one is not currently - # open. You can manually close the connection with #finish. - # - # For all the Net::HTTP request objects and shortcut request methods you may - # supply either a String for the request path or a URI from which Net::HTTP - # will extract the request path. - # - # === Response Data - # - # uri = URI('http://example.com/index.html') - # res = Net::HTTP.get_response(uri) - # - # # Headers - # res['Set-Cookie'] # => String - # res.get_fields('set-cookie') # => Array - # res.to_hash['set-cookie'] # => Array - # puts "Headers: #{res.to_hash.inspect}" - # - # # Status - # puts res.code # => '200' - # puts res.message # => 'OK' - # puts res.class.name # => 'HTTPOK' - # - # # Body - # puts res.body if res.response_body_permitted? - # - # === Following Redirection - # - # Each Net::HTTPResponse object belongs to a class for its response code. - # - # For example, all 2XX responses are instances of a Net::HTTPSuccess - # subclass, a 3XX response is an instance of a Net::HTTPRedirection - # subclass and a 200 response is an instance of the Net::HTTPOK class. For - # details of response classes, see the section "HTTP Response Classes" - # below. - # - # Using a case statement you can handle various types of responses properly: - # - # def fetch(uri_str, limit = 10) - # # You should choose a better exception. - # raise ArgumentError, 'too many HTTP redirects' if limit == 0 - # - # response = Net::HTTP.get_response(URI(uri_str)) - # - # case response - # when Net::HTTPSuccess then - # response - # when Net::HTTPRedirection then - # location = response['location'] - # warn "redirected to #{location}" - # fetch(location, limit - 1) - # else - # response.value - # end - # end - # - # print fetch('http://www.ruby-lang.org') - # - # === POST - # - # A POST can be made using the Net::HTTP::Post request class. This example - # creates a urlencoded POST body: - # - # uri = URI('http://www.example.com/todo.cgi') - # req = Net::HTTP::Post.new(uri) - # req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31') - # - # res = Net::HTTP.start(uri.hostname, uri.port) do |http| - # http.request(req) - # end - # - # case res - # when Net::HTTPSuccess, Net::HTTPRedirection - # # OK - # else - # res.value - # end - # - # At this time Net::HTTP does not support multipart/form-data. To send - # multipart/form-data use Net::HTTPRequest#body= and - # Net::HTTPRequest#content_type=: - # - # req = Net::HTTP::Post.new(uri) - # req.body = multipart_data - # req.content_type = 'multipart/form-data' - # - # Other requests that can contain a body such as PUT can be created in the - # same way using the corresponding request class (Net::HTTP::Put). - # - # === Setting Headers - # - # The following example performs a conditional GET using the - # If-Modified-Since header. If the files has not been modified since the - # time in the header a Not Modified response will be returned. See RFC 2616 - # section 9.3 for further details. - # - # uri = URI('http://example.com/cached_response') - # file = File.stat 'cached_response' - # - # req = Net::HTTP::Get.new(uri) - # req['If-Modified-Since'] = file.mtime.rfc2822 - # - # res = Net::HTTP.start(uri.hostname, uri.port) {|http| - # http.request(req) - # } - # - # open 'cached_response', 'w' do |io| - # io.write res.body - # end if res.is_a?(Net::HTTPSuccess) - # - # === Basic Authentication - # - # Basic authentication is performed according to - # [RFC2617](http://www.ietf.org/rfc/rfc2617.txt) - # - # uri = URI('http://example.com/index.html?key=value') - # - # req = Net::HTTP::Get.new(uri) - # req.basic_auth 'user', 'pass' - # - # res = Net::HTTP.start(uri.hostname, uri.port) {|http| - # http.request(req) - # } - # puts res.body - # - # === Streaming Response Bodies - # - # By default Net::HTTP reads an entire response into memory. If you are - # handling large files or wish to implement a progress bar you can instead - # stream the body directly to an IO. - # - # uri = URI('http://example.com/large_file') - # - # Net::HTTP.start(uri.host, uri.port) do |http| - # request = Net::HTTP::Get.new uri - # - # http.request request do |response| - # open 'large_file', 'w' do |io| - # response.read_body do |chunk| - # io.write chunk - # end - # end - # end - # end - # - # === HTTPS - # - # HTTPS is enabled for an HTTP connection by Net::HTTP#use_ssl=. - # - # uri = URI('https://secure.example.com/some_path?query=string') - # - # Net::HTTP.start(uri.host, uri.port, - # :use_ssl => uri.scheme == 'https') do |http| - # request = Net::HTTP::Get.new uri - # - # response = http.request request # Net::HTTPResponse object - # end - # - # In previous versions of Ruby you would need to require 'net/https' to use - # HTTPS. This is no longer true. - # - # === Proxies - # - # Net::HTTP will automatically create a proxy from the +http_proxy+ - # environment variable if it is present. To disable use of +http_proxy+, - # pass +nil+ for the proxy address. - # - # You may also create a custom proxy: - # - # proxy_addr = 'your.proxy.host' - # proxy_port = 8080 - # - # Net::HTTP.new('example.com', nil, proxy_addr, proxy_port).start { |http| - # # always proxy via your.proxy.addr:8080 - # } - # - # See Net::HTTP.new for further details and examples such as proxies that - # require a username and password. - # - # === Compression - # - # Net::HTTP automatically adds Accept-Encoding for compression of response - # bodies and automatically decompresses gzip and deflate responses unless a - # Range header was sent. - # - # Compression can be disabled through the Accept-Encoding: identity header. - # - # == HTTP Request Classes - # - # Here is the HTTP request class hierarchy. - # - # * Net::HTTPRequest - # * Net::HTTP::Get - # * Net::HTTP::Head - # * Net::HTTP::Post - # * Net::HTTP::Patch - # * Net::HTTP::Put - # * Net::HTTP::Proppatch - # * Net::HTTP::Lock - # * Net::HTTP::Unlock - # * Net::HTTP::Options - # * Net::HTTP::Propfind - # * Net::HTTP::Delete - # * Net::HTTP::Move - # * Net::HTTP::Copy - # * Net::HTTP::Mkcol - # * Net::HTTP::Trace - # - # == HTTP Response Classes - # - # Here is HTTP response class hierarchy. All classes are defined in Net - # module and are subclasses of Net::HTTPResponse. - # - # HTTPUnknownResponse:: For unhandled HTTP extensions - # HTTPInformation:: 1xx - # HTTPContinue:: 100 - # HTTPSwitchProtocol:: 101 - # HTTPSuccess:: 2xx - # HTTPOK:: 200 - # HTTPCreated:: 201 - # HTTPAccepted:: 202 - # HTTPNonAuthoritativeInformation:: 203 - # HTTPNoContent:: 204 - # HTTPResetContent:: 205 - # HTTPPartialContent:: 206 - # HTTPMultiStatus:: 207 - # HTTPIMUsed:: 226 - # HTTPRedirection:: 3xx - # HTTPMultipleChoices:: 300 - # HTTPMovedPermanently:: 301 - # HTTPFound:: 302 - # HTTPSeeOther:: 303 - # HTTPNotModified:: 304 - # HTTPUseProxy:: 305 - # HTTPTemporaryRedirect:: 307 - # HTTPClientError:: 4xx - # HTTPBadRequest:: 400 - # HTTPUnauthorized:: 401 - # HTTPPaymentRequired:: 402 - # HTTPForbidden:: 403 - # HTTPNotFound:: 404 - # HTTPMethodNotAllowed:: 405 - # HTTPNotAcceptable:: 406 - # HTTPProxyAuthenticationRequired:: 407 - # HTTPRequestTimeOut:: 408 - # HTTPConflict:: 409 - # HTTPGone:: 410 - # HTTPLengthRequired:: 411 - # HTTPPreconditionFailed:: 412 - # HTTPRequestEntityTooLarge:: 413 - # HTTPRequestURITooLong:: 414 - # HTTPUnsupportedMediaType:: 415 - # HTTPRequestedRangeNotSatisfiable:: 416 - # HTTPExpectationFailed:: 417 - # HTTPUnprocessableEntity:: 422 - # HTTPLocked:: 423 - # HTTPFailedDependency:: 424 - # HTTPUpgradeRequired:: 426 - # HTTPPreconditionRequired:: 428 - # HTTPTooManyRequests:: 429 - # HTTPRequestHeaderFieldsTooLarge:: 431 - # HTTPServerError:: 5xx - # HTTPInternalServerError:: 500 - # HTTPNotImplemented:: 501 - # HTTPBadGateway:: 502 - # HTTPServiceUnavailable:: 503 - # HTTPGatewayTimeOut:: 504 - # HTTPVersionNotSupported:: 505 - # HTTPInsufficientStorage:: 507 - # HTTPNetworkAuthenticationRequired:: 511 - # - # There is also the Net::HTTPBadResponse exception which is raised when - # there is a protocol error. - # - class HTTP < Protocol - - # :stopdoc: - Revision = %q$Revision$.split[1] - HTTPVersion = '1.1' - begin - require 'zlib' - require 'stringio' #for our purposes (unpacking gzip) lump these together - HAVE_ZLIB=true - rescue LoadError - HAVE_ZLIB=false - end - # :startdoc: - - # Turns on net/http 1.2 (Ruby 1.8) features. - # Defaults to ON in Ruby 1.8 or later. - def HTTP.version_1_2 - true - end - - # Returns true if net/http is in version 1.2 mode. - # Defaults to true. - def HTTP.version_1_2? - true - end - - def HTTP.version_1_1? #:nodoc: - false - end - - class << HTTP - alias is_version_1_1? version_1_1? #:nodoc: - alias is_version_1_2? version_1_2? #:nodoc: - end - - # - # short cut methods - # - - # - # Gets the body text from the target and outputs it to $stdout. The - # target can either be specified as - # (+uri+), or as (+host+, +path+, +port+ = 80); so: - # - # Net::HTTP.get_print URI('http://www.example.com/index.html') - # - # or: - # - # Net::HTTP.get_print 'www.example.com', '/index.html' - # - def HTTP.get_print(uri_or_host, path = nil, port = nil) - get_response(uri_or_host, path, port) {|res| - res.read_body do |chunk| - $stdout.print chunk - end - } - nil - end - - # Sends a GET request to the target and returns the HTTP response - # as a string. The target can either be specified as - # (+uri+), or as (+host+, +path+, +port+ = 80); so: - # - # print Net::HTTP.get(URI('http://www.example.com/index.html')) - # - # or: - # - # print Net::HTTP.get('www.example.com', '/index.html') - # - def HTTP.get(uri_or_host, path = nil, port = nil) - get_response(uri_or_host, path, port).body - end - - # Sends a GET request to the target and returns the HTTP response - # as a Net::HTTPResponse object. The target can either be specified as - # (+uri+), or as (+host+, +path+, +port+ = 80); so: - # - # res = Net::HTTP.get_response(URI('http://www.example.com/index.html')) - # print res.body - # - # or: - # - # res = Net::HTTP.get_response('www.example.com', '/index.html') - # print res.body - # - def HTTP.get_response(uri_or_host, path = nil, port = nil, &block) - if path - host = uri_or_host - new(host, port || HTTP.default_port).start {|http| - return http.request_get(path, &block) - } - else - uri = uri_or_host - start(uri.hostname, uri.port, - :use_ssl => uri.scheme == 'https') {|http| - return http.request_get(uri, &block) - } - end - end - - # Posts HTML form data to the specified URI object. - # The form data must be provided as a Hash mapping from String to String. - # Example: - # - # { "cmd" => "search", "q" => "ruby", "max" => "50" } - # - # This method also does Basic Authentication iff +url+.user exists. - # But userinfo for authentication is deprecated (RFC3986). - # So this feature will be removed. - # - # Example: - # - # require 'net/http' - # require 'uri' - # - # Net::HTTP.post_form URI('http://www.example.com/search.cgi'), - # { "q" => "ruby", "max" => "50" } - # - def HTTP.post_form(url, params) - req = Post.new(url) - req.form_data = params - req.basic_auth url.user, url.password if url.user - start(url.hostname, url.port, - :use_ssl => url.scheme == 'https' ) {|http| - http.request(req) - } - end - - # - # HTTP session management - # - - # The default port to use for HTTP requests; defaults to 80. - def HTTP.default_port - http_default_port() - end - - # The default port to use for HTTP requests; defaults to 80. - def HTTP.http_default_port - 80 - end - - # The default port to use for HTTPS requests; defaults to 443. - def HTTP.https_default_port - 443 - end - - def HTTP.socket_type #:nodoc: obsolete - BufferedIO - end - - # :call-seq: - # HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block) - # HTTP.start(address, port=nil, p_addr=nil, p_port=nil, p_user=nil, p_pass=nil, opt, &block) - # - # Creates a new Net::HTTP object, then additionally opens the TCP - # connection and HTTP session. - # - # Arguments are the following: - # _address_ :: hostname or IP address of the server - # _port_ :: port of the server - # _p_addr_ :: address of proxy - # _p_port_ :: port of proxy - # _p_user_ :: user of proxy - # _p_pass_ :: pass of proxy - # _opt_ :: optional hash - # - # _opt_ sets following values by its accessor. - # The keys are ca_file, ca_path, cert, cert_store, ciphers, - # close_on_empty_response, key, open_timeout, read_timeout, ssl_timeout, - # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode. - # If you set :use_ssl as true, you can use https and default value of - # verify_mode is set as OpenSSL::SSL::VERIFY_PEER. - # - # If the optional block is given, the newly - # created Net::HTTP object is passed to it and closed when the - # block finishes. In this case, the return value of this method - # is the return value of the block. If no block is given, the - # return value of this method is the newly created Net::HTTP object - # itself, and the caller is responsible for closing it upon completion - # using the finish() method. - def HTTP.start(address, *arg, &block) # :yield: +http+ - arg.pop if opt = Hash.try_convert(arg[-1]) - port, p_addr, p_port, p_user, p_pass = *arg - port = https_default_port if !port && opt && opt[:use_ssl] - http = new(address, port, p_addr, p_port, p_user, p_pass) - - if opt - if opt[:use_ssl] - opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt) - end - http.methods.grep(/\A(\w+)=\z/) do |meth| - key = $1.to_sym - opt.key?(key) or next - http.__send__(meth, opt[key]) - end - end - - http.start(&block) - end - - class << HTTP - alias newobj new # :nodoc: - end - - # Creates a new Net::HTTP object without opening a TCP connection or - # HTTP session. - # - # The +address+ should be a DNS hostname or IP address, the +port+ is the - # port the server operates on. If no +port+ is given the default port for - # HTTP or HTTPS is used. - # - # If none of the +p_+ arguments are given, the proxy host and port are - # taken from the +http_proxy+ environment variable (or its uppercase - # equivalent) if present. If the proxy requires authentication you must - # supply it by hand. See URI::Generic#find_proxy for details of proxy - # detection from the environment. To disable proxy detection set +p_addr+ - # to nil. - # - # If you are connecting to a custom proxy, +p_addr+ the DNS name or IP - # address of the proxy host, +p_port+ the port to use to access the proxy, - # and +p_user+ and +p_pass+ the username and password if authorization is - # required to use the proxy. - # - def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) - http = super address, port - - if proxy_class? then # from Net::HTTP::Proxy() - http.proxy_from_env = @proxy_from_env - http.proxy_address = @proxy_address - http.proxy_port = @proxy_port - http.proxy_user = @proxy_user - http.proxy_pass = @proxy_pass - elsif p_addr == :ENV then - http.proxy_from_env = true - else - http.proxy_address = p_addr - http.proxy_port = p_port || default_port - http.proxy_user = p_user - http.proxy_pass = p_pass - end - - http - end - - # Creates a new Net::HTTP object for the specified server address, - # without opening the TCP connection or initializing the HTTP session. - # The +address+ should be a DNS hostname or IP address. - def initialize(address, port = nil) - @address = address - @port = (port || HTTP.default_port) - @local_host = nil - @local_port = nil - @curr_http_version = HTTPVersion - @keep_alive_timeout = 2 - @last_communicated = nil - @close_on_empty_response = false - @socket = nil - @started = false - @open_timeout = nil - @read_timeout = 60 - @continue_timeout = nil - @debug_output = nil - - @proxy_from_env = false - @proxy_uri = nil - @proxy_address = nil - @proxy_port = nil - @proxy_user = nil - @proxy_pass = nil - - @use_ssl = false - @ssl_context = nil - @ssl_session = nil - @enable_post_connection_check = true - @sspi_enabled = false - SSL_IVNAMES.each do |ivname| - instance_variable_set ivname, nil - end - end - - def inspect - "#<#{self.class} #{@address}:#{@port} open=#{started?}>" - end - - # *WARNING* This method opens a serious security hole. - # Never use this method in production code. - # - # Sets an output stream for debugging. - # - # http = Net::HTTP.new(hostname) - # http.set_debug_output $stderr - # http.start { .... } - # - def set_debug_output(output) - warn 'Net::HTTP#set_debug_output called after HTTP started' if started? - @debug_output = output - end - - # The DNS host name or IP address to connect to. - attr_reader :address - - # The port number to connect to. - attr_reader :port - - # The local host used to establish the connection. - attr_accessor :local_host - - # The local port used to establish the connection. - attr_accessor :local_port - - attr_writer :proxy_from_env - attr_writer :proxy_address - attr_writer :proxy_port - attr_writer :proxy_user - attr_writer :proxy_pass - - # Number of seconds to wait for the connection to open. Any number - # may be used, including Floats for fractional seconds. If the HTTP - # object cannot open a connection in this many seconds, it raises a - # Net::OpenTimeout exception. The default value is +nil+. - attr_accessor :open_timeout - - # Number of seconds to wait for one block to be read (via one read(2) - # call). Any number may be used, including Floats for fractional - # seconds. If the HTTP object cannot read data in this many seconds, - # it raises a Net::ReadTimeout exception. The default value is 60 seconds. - attr_reader :read_timeout - - # Setter for the read_timeout attribute. - def read_timeout=(sec) - @socket.read_timeout = sec if @socket - @read_timeout = sec - end - - # Seconds to wait for 100 Continue response. If the HTTP object does not - # receive a response in this many seconds it sends the request body. The - # default value is +nil+. - attr_reader :continue_timeout - - # Setter for the continue_timeout attribute. - def continue_timeout=(sec) - @socket.continue_timeout = sec if @socket - @continue_timeout = sec - end - - # Seconds to reuse the connection of the previous request. - # If the idle time is less than this Keep-Alive Timeout, - # Net::HTTP reuses the TCP/IP socket used by the previous communication. - # The default value is 2 seconds. - attr_accessor :keep_alive_timeout - - # Returns true if the HTTP session has been started. - def started? - @started - end - - alias active? started? #:nodoc: obsolete - - attr_accessor :close_on_empty_response - - # Returns true if SSL/TLS is being used with HTTP. - def use_ssl? - @use_ssl - end - - # Turn on/off SSL. - # This flag must be set before starting session. - # If you change use_ssl value after session started, - # a Net::HTTP object raises IOError. - def use_ssl=(flag) - flag = flag ? true : false - if started? and @use_ssl != flag - raise IOError, "use_ssl value changed, but session already started" - end - @use_ssl = flag - end - - SSL_IVNAMES = [ - :@ca_file, - :@ca_path, - :@cert, - :@cert_store, - :@ciphers, - :@key, - :@ssl_timeout, - :@ssl_version, - :@verify_callback, - :@verify_depth, - :@verify_mode, - ] - SSL_ATTRIBUTES = [ - :ca_file, - :ca_path, - :cert, - :cert_store, - :ciphers, - :key, - :ssl_timeout, - :ssl_version, - :verify_callback, - :verify_depth, - :verify_mode, - ] - - # Sets path of a CA certification file in PEM format. - # - # The file can contain several CA certificates. - attr_accessor :ca_file - - # Sets path of a CA certification directory containing certifications in - # PEM format. - attr_accessor :ca_path - - # Sets an OpenSSL::X509::Certificate object as client certificate. - # (This method is appeared in Michal Rokos's OpenSSL extension). - attr_accessor :cert - - # Sets the X509::Store to verify peer certificate. - attr_accessor :cert_store - - # Sets the available ciphers. See OpenSSL::SSL::SSLContext#ciphers= - attr_accessor :ciphers - - # Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. - # (This method is appeared in Michal Rokos's OpenSSL extension.) - attr_accessor :key - - # Sets the SSL timeout seconds. - attr_accessor :ssl_timeout - - # Sets the SSL version. See OpenSSL::SSL::SSLContext#ssl_version= - attr_accessor :ssl_version - - # Sets the verify callback for the server certification verification. - attr_accessor :verify_callback - - # Sets the maximum depth for the certificate chain verification. - attr_accessor :verify_depth - - # Sets the flags for server the certification verification at beginning of - # SSL/TLS session. - # - # OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER are acceptable. - attr_accessor :verify_mode - - # Returns the X.509 certificates the server presented. - def peer_cert - if not use_ssl? or not @socket - return nil - end - @socket.io.peer_cert - end - - # Opens a TCP connection and HTTP session. - # - # When this method is called with a block, it passes the Net::HTTP - # object to the block, and closes the TCP connection and HTTP session - # after the block has been executed. - # - # When called with a block, it returns the return value of the - # block; otherwise, it returns self. - # - def start # :yield: http - raise IOError, 'HTTP session already opened' if @started - if block_given? - begin - do_start - return yield(self) - ensure - do_finish - end - end - do_start - self - end - - def do_start - connect - @started = true - end - private :do_start - - def connect - if proxy? then - conn_address = proxy_address - conn_port = proxy_port - else - conn_address = address - conn_port = port - end - - D "opening connection to #{conn_address}:#{conn_port}..." - s = Timeout.timeout(@open_timeout, Net::OpenTimeout) { - TCPSocket.open(conn_address, conn_port, @local_host, @local_port) - } - s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) - D "opened" - if use_ssl? - ssl_parameters = Hash.new - iv_list = instance_variables - SSL_IVNAMES.each_with_index do |ivname, i| - if iv_list.include?(ivname) and - value = instance_variable_get(ivname) - ssl_parameters[SSL_ATTRIBUTES[i]] = value if value - end - end - @ssl_context = OpenSSL::SSL::SSLContext.new - @ssl_context.set_params(ssl_parameters) - D "starting SSL for #{conn_address}:#{conn_port}..." - s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) - s.sync_close = true - D "SSL established" - end - @socket = BufferedIO.new(s) - @socket.read_timeout = @read_timeout - @socket.continue_timeout = @continue_timeout - @socket.debug_output = @debug_output - if use_ssl? - begin - if proxy? - buf = "CONNECT #{@address}:#{@port} HTTP/#{HTTPVersion}\r\n" - buf << "Host: #{@address}:#{@port}\r\n" - if proxy_user - credential = ["#{proxy_user}:#{proxy_pass}"].pack('m') - credential.delete!("\r\n") - buf << "Proxy-Authorization: Basic #{credential}\r\n" - end - buf << "\r\n" - @socket.write(buf) - HTTPResponse.read_new(@socket).value - end - # Server Name Indication (SNI) RFC 3546 - s.hostname = @address if s.respond_to? :hostname= - if @ssl_session and - Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout - s.session = @ssl_session if @ssl_session - end - Timeout.timeout(@open_timeout, Net::OpenTimeout) { s.connect } - if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE - s.post_connection_check(@address) - end - @ssl_session = s.session - rescue => exception - D "Conn close because of connect error #{exception}" - @socket.close if @socket and not @socket.closed? - raise exception - end - end - on_connect - end - private :connect - - def on_connect - end - private :on_connect - - # Finishes the HTTP session and closes the TCP connection. - # Raises IOError if the session has not been started. - def finish - raise IOError, 'HTTP session not yet started' unless started? - do_finish - end - - def do_finish - @started = false - @socket.close if @socket and not @socket.closed? - @socket = nil - end - private :do_finish - - # - # proxy - # - - public - - # no proxy - @is_proxy_class = false - @proxy_from_env = false - @proxy_addr = nil - @proxy_port = nil - @proxy_user = nil - @proxy_pass = nil - - # Creates an HTTP proxy class which behaves like Net::HTTP, but - # performs all access via the specified proxy. - # - # This class is obsolete. You may pass these same parameters directly to - # Net::HTTP.new. See Net::HTTP.new for details of the arguments. - def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) - return self unless p_addr - - Class.new(self) { - @is_proxy_class = true - - if p_addr == :ENV then - @proxy_from_env = true - @proxy_address = nil - @proxy_port = nil - else - @proxy_from_env = false - @proxy_address = p_addr - @proxy_port = p_port || default_port - end - - @proxy_user = p_user - @proxy_pass = p_pass - } - end - - class << HTTP - # returns true if self is a class which was created by HTTP::Proxy. - def proxy_class? - defined?(@is_proxy_class) ? @is_proxy_class : false - end - - # Address of proxy host. If Net::HTTP does not use a proxy, nil. - attr_reader :proxy_address - - # Port number of proxy host. If Net::HTTP does not use a proxy, nil. - attr_reader :proxy_port - - # User name for accessing proxy. If Net::HTTP does not use a proxy, nil. - attr_reader :proxy_user - - # User password for accessing proxy. If Net::HTTP does not use a proxy, - # nil. - attr_reader :proxy_pass - end - - # True if requests for this connection will be proxied - def proxy? - !!if @proxy_from_env then - proxy_uri - else - @proxy_address - end - end - - # True if the proxy for this connection is determined from the environment - def proxy_from_env? - @proxy_from_env - end - - # The proxy URI determined from the environment for this connection. - def proxy_uri # :nodoc: - @proxy_uri ||= URI::HTTP.new( - "http".freeze, nil, address, port, nil, nil, nil, nil, nil - ).find_proxy - end - - # The address of the proxy server, if one is configured. - def proxy_address - if @proxy_from_env then - proxy_uri && proxy_uri.hostname - else - @proxy_address - end - end - - # The port of the proxy server, if one is configured. - def proxy_port - if @proxy_from_env then - proxy_uri && proxy_uri.port - else - @proxy_port - end - end - - # The proxy username, if one is configured - def proxy_user - @proxy_user - end - - # The proxy password, if one is configured - def proxy_pass - @proxy_pass - end - - alias proxyaddr proxy_address #:nodoc: obsolete - alias proxyport proxy_port #:nodoc: obsolete - - private - - # without proxy, obsolete - - def conn_address # :nodoc: - address() - end - - def conn_port # :nodoc: - port() - end - - def edit_path(path) - if proxy? and not use_ssl? then - "http://#{addr_port}#{path}" - else - path - end - end - - # - # HTTP operations - # - - public - - # Retrieves data from +path+ on the connected-to host which may be an - # absolute path String or a URI to extract the path from. - # - # +initheader+ must be a Hash like { 'Accept' => '*/*', ... }, - # and it defaults to an empty hash. - # If +initheader+ doesn't have the key 'accept-encoding', then - # a value of "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" is used, - # so that gzip compression is used in preference to deflate - # compression, which is used in preference to no compression. - # Ruby doesn't have libraries to support the compress (Lempel-Ziv) - # compression, so that is not supported. The intent of this is - # to reduce bandwidth by default. If this routine sets up - # compression, then it does the decompression also, removing - # the header as well to prevent confusion. Otherwise - # it leaves the body as it found it. - # - # This method returns a Net::HTTPResponse object. - # - # If called with a block, yields each fragment of the - # entity body in turn as a string as it is read from - # the socket. Note that in this case, the returned response - # object will *not* contain a (meaningful) body. - # - # +dest+ argument is obsolete. - # It still works but you must not use it. - # - # This method never raises an exception. - # - # response = http.get('/index.html') - # - # # using block - # File.open('result.txt', 'w') {|f| - # http.get('/~foo/') do |str| - # f.write str - # end - # } - # - def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+ - res = nil - request(Get.new(path, initheader)) {|r| - r.read_body dest, &block - res = r - } - res - end - - # Gets only the header from +path+ on the connected-to host. - # +header+ is a Hash like { 'Accept' => '*/*', ... }. - # - # This method returns a Net::HTTPResponse object. - # - # This method never raises an exception. - # - # response = nil - # Net::HTTP.start('some.www.server', 80) {|http| - # response = http.head('/index.html') - # } - # p response['content-type'] - # - def head(path, initheader = nil) - request(Head.new(path, initheader)) - end - - # Posts +data+ (must be a String) to +path+. +header+ must be a Hash - # like { 'Accept' => '*/*', ... }. - # - # This method returns a Net::HTTPResponse object. - # - # If called with a block, yields each fragment of the - # entity body in turn as a string as it is read from - # the socket. Note that in this case, the returned response - # object will *not* contain a (meaningful) body. - # - # +dest+ argument is obsolete. - # It still works but you must not use it. - # - # This method never raises exception. - # - # response = http.post('/cgi-bin/search.rb', 'query=foo') - # - # # using block - # File.open('result.txt', 'w') {|f| - # http.post('/cgi-bin/search.rb', 'query=foo') do |str| - # f.write str - # end - # } - # - # You should set Content-Type: header field for POST. - # If no Content-Type: field given, this method uses - # "application/x-www-form-urlencoded" by default. - # - def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ - send_entity(path, data, initheader, dest, Post, &block) - end - - # Sends a PATCH request to the +path+ and gets a response, - # as an HTTPResponse object. - def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ - send_entity(path, data, initheader, dest, Patch, &block) - end - - def put(path, data, initheader = nil) #:nodoc: - request(Put.new(path, initheader), data) - end - - # Sends a PROPPATCH request to the +path+ and gets a response, - # as an HTTPResponse object. - def proppatch(path, body, initheader = nil) - request(Proppatch.new(path, initheader), body) - end - - # Sends a LOCK request to the +path+ and gets a response, - # as an HTTPResponse object. - def lock(path, body, initheader = nil) - request(Lock.new(path, initheader), body) - end - - # Sends a UNLOCK request to the +path+ and gets a response, - # as an HTTPResponse object. - def unlock(path, body, initheader = nil) - request(Unlock.new(path, initheader), body) - end - - # Sends a OPTIONS request to the +path+ and gets a response, - # as an HTTPResponse object. - def options(path, initheader = nil) - request(Options.new(path, initheader)) - end - - # Sends a PROPFIND request to the +path+ and gets a response, - # as an HTTPResponse object. - def propfind(path, body = nil, initheader = {'Depth' => '0'}) - request(Propfind.new(path, initheader), body) - end - - # Sends a DELETE request to the +path+ and gets a response, - # as an HTTPResponse object. - def delete(path, initheader = {'Depth' => 'Infinity'}) - request(Delete.new(path, initheader)) - end - - # Sends a MOVE request to the +path+ and gets a response, - # as an HTTPResponse object. - def move(path, initheader = nil) - request(Move.new(path, initheader)) - end - - # Sends a COPY request to the +path+ and gets a response, - # as an HTTPResponse object. - def copy(path, initheader = nil) - request(Copy.new(path, initheader)) - end - - # Sends a MKCOL request to the +path+ and gets a response, - # as an HTTPResponse object. - def mkcol(path, body = nil, initheader = nil) - request(Mkcol.new(path, initheader), body) - end - - # Sends a TRACE request to the +path+ and gets a response, - # as an HTTPResponse object. - def trace(path, initheader = nil) - request(Trace.new(path, initheader)) - end - - # Sends a GET request to the +path+. - # Returns the response as a Net::HTTPResponse object. - # - # When called with a block, passes an HTTPResponse object to the block. - # The body of the response will not have been read yet; - # the block can process it using HTTPResponse#read_body, - # if desired. - # - # Returns the response. - # - # This method never raises Net::* exceptions. - # - # response = http.request_get('/index.html') - # # The entity body is already read in this case. - # p response['content-type'] - # puts response.body - # - # # Using a block - # http.request_get('/index.html') {|response| - # p response['content-type'] - # response.read_body do |str| # read body now - # print str - # end - # } - # - def request_get(path, initheader = nil, &block) # :yield: +response+ - request(Get.new(path, initheader), &block) - end - - # Sends a HEAD request to the +path+ and returns the response - # as a Net::HTTPResponse object. - # - # Returns the response. - # - # This method never raises Net::* exceptions. - # - # response = http.request_head('/index.html') - # p response['content-type'] - # - def request_head(path, initheader = nil, &block) - request(Head.new(path, initheader), &block) - end - - # Sends a POST request to the +path+. - # - # Returns the response as a Net::HTTPResponse object. - # - # When called with a block, the block is passed an HTTPResponse - # object. The body of that response will not have been read yet; - # the block can process it using HTTPResponse#read_body, if desired. - # - # Returns the response. - # - # This method never raises Net::* exceptions. - # - # # example - # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...') - # p response.status - # puts response.body # body is already read in this case - # - # # using block - # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response| - # p response.status - # p response['content-type'] - # response.read_body do |str| # read body now - # print str - # end - # } - # - def request_post(path, data, initheader = nil, &block) # :yield: +response+ - request Post.new(path, initheader), data, &block - end - - def request_put(path, data, initheader = nil, &block) #:nodoc: - request Put.new(path, initheader), data, &block - end - - alias get2 request_get #:nodoc: obsolete - alias head2 request_head #:nodoc: obsolete - alias post2 request_post #:nodoc: obsolete - alias put2 request_put #:nodoc: obsolete - - - # Sends an HTTP request to the HTTP server. - # Also sends a DATA string if +data+ is given. - # - # Returns a Net::HTTPResponse object. - # - # This method never raises Net::* exceptions. - # - # response = http.send_request('GET', '/index.html') - # puts response.body - # - def send_request(name, path, data = nil, header = nil) - has_response_body = name != 'HEAD' - r = HTTPGenericRequest.new(name,(data ? true : false),has_response_body,path,header) - request r, data - end - - # Sends an HTTPRequest object +req+ to the HTTP server. - # - # If +req+ is a Net::HTTP::Post or Net::HTTP::Put request containing - # data, the data is also sent. Providing data for a Net::HTTP::Head or - # Net::HTTP::Get request results in an ArgumentError. - # - # Returns an HTTPResponse object. - # - # When called with a block, passes an HTTPResponse object to the block. - # The body of the response will not have been read yet; - # the block can process it using HTTPResponse#read_body, - # if desired. - # - # This method never raises Net::* exceptions. - # - def request(req, body = nil, &block) # :yield: +response+ - unless started? - start { - req['connection'] ||= 'close' - return request(req, body, &block) - } - end - if proxy_user() - req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl? - end - req.set_body_internal body - res = transport_request(req, &block) - if sspi_auth?(res) - sspi_auth(req) - res = transport_request(req, &block) - end - res - end - - private - - # Executes a request which uses a representation - # and returns its body. - def send_entity(path, data, initheader, dest, type, &block) - res = nil - request(type.new(path, initheader), data) {|r| - r.read_body dest, &block - res = r - } - res - end - - IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc: - - def transport_request(req) - count = 0 - begin - begin_transport req - res = catch(:response) { - req.exec @socket, @curr_http_version, edit_path(req.path) - begin - res = HTTPResponse.read_new(@socket) - res.decode_content = req.decode_content - end while res.kind_of?(HTTPInformation) - - res.uri = req.uri - - res - } - res.reading_body(@socket, req.response_body_permitted?) { - yield res if block_given? - } - rescue Net::OpenTimeout - raise - rescue Net::ReadTimeout, IOError, EOFError, - Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE, - # avoid a dependency on OpenSSL - defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError, - Timeout::Error => exception - if count == 0 && IDEMPOTENT_METHODS_.include?(req.method) - count += 1 - @socket.close if @socket and not @socket.closed? - D "Conn close because of error #{exception}, and retry" - retry - end - D "Conn close because of error #{exception}" - @socket.close if @socket and not @socket.closed? - raise - end - - end_transport req, res - res - rescue => exception - D "Conn close because of error #{exception}" - @socket.close if @socket and not @socket.closed? - raise exception - end - - def begin_transport(req) - if @socket.closed? - connect - elsif @last_communicated && @last_communicated + @keep_alive_timeout < Time.now - D 'Conn close because of keep_alive_timeout' - @socket.close - connect - end - - if not req.response_body_permitted? and @close_on_empty_response - req['connection'] ||= 'close' - end - - req.update_uri address, port, use_ssl? - req['host'] ||= addr_port() - end - - def end_transport(req, res) - @curr_http_version = res.http_version - @last_communicated = nil - if @socket.closed? - D 'Conn socket closed' - elsif not res.body and @close_on_empty_response - D 'Conn close' - @socket.close - elsif keep_alive?(req, res) - D 'Conn keep-alive' - @last_communicated = Time.now - else - D 'Conn close' - @socket.close - end - end - - def keep_alive?(req, res) - return false if req.connection_close? - if @curr_http_version <= '1.0' - res.connection_keep_alive? - else # HTTP/1.1 or later - not res.connection_close? - end - end - - def sspi_auth?(res) - return false unless @sspi_enabled - if res.kind_of?(HTTPProxyAuthenticationRequired) and - proxy? and res["Proxy-Authenticate"].include?("Negotiate") - begin - require 'win32/sspi' - true - rescue LoadError - false - end - else - false - end - end - - def sspi_auth(req) - n = Win32::SSPI::NegotiateAuth.new - req["Proxy-Authorization"] = "Negotiate #{n.get_initial_token}" - # Some versions of ISA will close the connection if this isn't present. - req["Connection"] = "Keep-Alive" - req["Proxy-Connection"] = "Keep-Alive" - res = transport_request(req) - authphrase = res["Proxy-Authenticate"] or return res - req["Proxy-Authorization"] = "Negotiate #{n.complete_authentication(authphrase)}" - rescue => err - raise HTTPAuthenticationError.new('HTTP authentication failed', err) - end - - # - # utils - # - - private - - def addr_port - if use_ssl? - address() + (port == HTTP.https_default_port ? '' : ":#{port()}") - else - address() + (port == HTTP.http_default_port ? '' : ":#{port()}") - end - end - - def D(msg) - return unless @debug_output - @debug_output << msg - @debug_output << "\n" - end - end - -end - -require 'net/http/exceptions' - -require 'net/http/header' - -require 'net/http/generic_request' -require 'net/http/request' -require 'net/http/requests' - -require 'net/http/response' -require 'net/http/responses' - -require 'net/http/proxy_delta' - -require 'net/http/backward' - |