1# 2# = net/http.rb 3# 4# Copyright (c) 1999-2007 Yukihiro Matsumoto 5# Copyright (c) 1999-2007 Minero Aoki 6# Copyright (c) 2001 GOTOU Yuuzou 7# 8# Written and maintained by Minero Aoki <aamine@loveruby.net>. 9# HTTPS support added by GOTOU Yuuzou <gotoyuzo@notwork.org>. 10# 11# This file is derived from "http-access.rb". 12# 13# Documented by Minero Aoki; converted to RDoc by William Webber. 14# 15# This program is free software. You can re-distribute and/or 16# modify this program under the same terms of ruby itself --- 17# Ruby Distribution License or GNU General Public License. 18# 19# See Net::HTTP for an overview and examples. 20# 21 22require 'net/protocol' 23require 'uri' 24 25module Net #:nodoc: 26 autoload :OpenSSL, 'openssl' 27 28 # :stopdoc: 29 class HTTPBadResponse < StandardError; end 30 class HTTPHeaderSyntaxError < StandardError; end 31 # :startdoc: 32 33 # == An HTTP client API for Ruby. 34 # 35 # Net::HTTP provides a rich library which can be used to build HTTP 36 # user-agents. For more details about HTTP see 37 # [RFC2616](http://www.ietf.org/rfc/rfc2616.txt) 38 # 39 # Net::HTTP is designed to work closely with URI. URI::HTTP#host, 40 # URI::HTTP#port and URI::HTTP#request_uri are designed to work with 41 # Net::HTTP. 42 # 43 # If you are only performing a few GET requests you should try OpenURI. 44 # 45 # == Simple Examples 46 # 47 # All examples assume you have loaded Net::HTTP with: 48 # 49 # require 'net/http' 50 # 51 # This will also require 'uri' so you don't need to require it separately. 52 # 53 # The Net::HTTP methods in the following section do not persist 54 # connections. They are not recommended if you are performing many HTTP 55 # requests. 56 # 57 # === GET 58 # 59 # Net::HTTP.get('example.com', '/index.html') # => String 60 # 61 # === GET by URI 62 # 63 # uri = URI('http://example.com/index.html?count=10') 64 # Net::HTTP.get(uri) # => String 65 # 66 # === GET with Dynamic Parameters 67 # 68 # uri = URI('http://example.com/index.html') 69 # params = { :limit => 10, :page => 3 } 70 # uri.query = URI.encode_www_form(params) 71 # 72 # res = Net::HTTP.get_response(uri) 73 # puts res.body if res.is_a?(Net::HTTPSuccess) 74 # 75 # === POST 76 # 77 # uri = URI('http://www.example.com/search.cgi') 78 # res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50') 79 # puts res.body 80 # 81 # === POST with Multiple Values 82 # 83 # uri = URI('http://www.example.com/search.cgi') 84 # res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50') 85 # puts res.body 86 # 87 # == How to use Net::HTTP 88 # 89 # The following example code can be used as the basis of a HTTP user-agent 90 # which can perform a variety of request types using persistent 91 # connections. 92 # 93 # uri = URI('http://example.com/some_path?query=string') 94 # 95 # Net::HTTP.start(uri.host, uri.port) do |http| 96 # request = Net::HTTP::Get.new uri 97 # 98 # response = http.request request # Net::HTTPResponse object 99 # end 100 # 101 # Net::HTTP::start immediately creates a connection to an HTTP server which 102 # is kept open for the duration of the block. The connection will remain 103 # open for multiple requests in the block if the server indicates it 104 # supports persistent connections. 105 # 106 # The request types Net::HTTP supports are listed below in the section "HTTP 107 # Request Classes". 108 # 109 # If you wish to re-use a connection across multiple HTTP requests without 110 # automatically closing it you can use ::new instead of ::start. #request 111 # will automatically open a connection to the server if one is not currently 112 # open. You can manually close the connection with #finish. 113 # 114 # For all the Net::HTTP request objects and shortcut request methods you may 115 # supply either a String for the request path or a URI from which Net::HTTP 116 # will extract the request path. 117 # 118 # === Response Data 119 # 120 # uri = URI('http://example.com/index.html') 121 # res = Net::HTTP.get_response(uri) 122 # 123 # # Headers 124 # res['Set-Cookie'] # => String 125 # res.get_fields('set-cookie') # => Array 126 # res.to_hash['set-cookie'] # => Array 127 # puts "Headers: #{res.to_hash.inspect}" 128 # 129 # # Status 130 # puts res.code # => '200' 131 # puts res.message # => 'OK' 132 # puts res.class.name # => 'HTTPOK' 133 # 134 # # Body 135 # puts res.body if res.response_body_permitted? 136 # 137 # === Following Redirection 138 # 139 # Each Net::HTTPResponse object belongs to a class for its response code. 140 # 141 # For example, all 2XX responses are instances of a Net::HTTPSuccess 142 # subclass, a 3XX response is an instance of a Net::HTTPRedirection 143 # subclass and a 200 response is an instance of the Net::HTTPOK class. For 144 # details of response classes, see the section "HTTP Response Classes" 145 # below. 146 # 147 # Using a case statement you can handle various types of responses properly: 148 # 149 # def fetch(uri_str, limit = 10) 150 # # You should choose a better exception. 151 # raise ArgumentError, 'too many HTTP redirects' if limit == 0 152 # 153 # response = Net::HTTP.get_response(URI(uri_str)) 154 # 155 # case response 156 # when Net::HTTPSuccess then 157 # response 158 # when Net::HTTPRedirection then 159 # location = response['location'] 160 # warn "redirected to #{location}" 161 # fetch(location, limit - 1) 162 # else 163 # response.value 164 # end 165 # end 166 # 167 # print fetch('http://www.ruby-lang.org') 168 # 169 # === POST 170 # 171 # A POST can be made using the Net::HTTP::Post request class. This example 172 # creates a urlencoded POST body: 173 # 174 # uri = URI('http://www.example.com/todo.cgi') 175 # req = Net::HTTP::Post.new(uri) 176 # req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31') 177 # 178 # res = Net::HTTP.start(uri.hostname, uri.port) do |http| 179 # http.request(req) 180 # end 181 # 182 # case res 183 # when Net::HTTPSuccess, Net::HTTPRedirection 184 # # OK 185 # else 186 # res.value 187 # end 188 # 189 # At this time Net::HTTP does not support multipart/form-data. To send 190 # multipart/form-data use Net::HTTPRequest#body= and 191 # Net::HTTPRequest#content_type=: 192 # 193 # req = Net::HTTP::Post.new(uri) 194 # req.body = multipart_data 195 # req.content_type = 'multipart/form-data' 196 # 197 # Other requests that can contain a body such as PUT can be created in the 198 # same way using the corresponding request class (Net::HTTP::Put). 199 # 200 # === Setting Headers 201 # 202 # The following example performs a conditional GET using the 203 # If-Modified-Since header. If the files has not been modified since the 204 # time in the header a Not Modified response will be returned. See RFC 2616 205 # section 9.3 for further details. 206 # 207 # uri = URI('http://example.com/cached_response') 208 # file = File.stat 'cached_response' 209 # 210 # req = Net::HTTP::Get.new(uri) 211 # req['If-Modified-Since'] = file.mtime.rfc2822 212 # 213 # res = Net::HTTP.start(uri.hostname, uri.port) {|http| 214 # http.request(req) 215 # } 216 # 217 # open 'cached_response', 'w' do |io| 218 # io.write res.body 219 # end if res.is_a?(Net::HTTPSuccess) 220 # 221 # === Basic Authentication 222 # 223 # Basic authentication is performed according to 224 # [RFC2617](http://www.ietf.org/rfc/rfc2617.txt) 225 # 226 # uri = URI('http://example.com/index.html?key=value') 227 # 228 # req = Net::HTTP::Get.new(uri) 229 # req.basic_auth 'user', 'pass' 230 # 231 # res = Net::HTTP.start(uri.hostname, uri.port) {|http| 232 # http.request(req) 233 # } 234 # puts res.body 235 # 236 # === Streaming Response Bodies 237 # 238 # By default Net::HTTP reads an entire response into memory. If you are 239 # handling large files or wish to implement a progress bar you can instead 240 # stream the body directly to an IO. 241 # 242 # uri = URI('http://example.com/large_file') 243 # 244 # Net::HTTP.start(uri.host, uri.port) do |http| 245 # request = Net::HTTP::Get.new uri 246 # 247 # http.request request do |response| 248 # open 'large_file', 'w' do |io| 249 # response.read_body do |chunk| 250 # io.write chunk 251 # end 252 # end 253 # end 254 # end 255 # 256 # === HTTPS 257 # 258 # HTTPS is enabled for an HTTP connection by Net::HTTP#use_ssl=. 259 # 260 # uri = URI('https://secure.example.com/some_path?query=string') 261 # 262 # Net::HTTP.start(uri.host, uri.port, 263 # :use_ssl => uri.scheme == 'https') do |http| 264 # request = Net::HTTP::Get.new uri 265 # 266 # response = http.request request # Net::HTTPResponse object 267 # end 268 # 269 # In previous versions of ruby you would need to require 'net/https' to use 270 # HTTPS. This is no longer true. 271 # 272 # === Proxies 273 # 274 # Net::HTTP will automatically create a proxy from the +http_proxy+ 275 # environment variable if it is present. To disable use of +http_proxy+, 276 # pass +nil+ for the proxy address. 277 # 278 # You may also create a custom proxy: 279 # 280 # proxy_addr = 'your.proxy.host' 281 # proxy_port = 8080 282 # 283 # Net::HTTP.new('example.com', nil, proxy_addr, proxy_port).start { |http| 284 # # always proxy via your.proxy.addr:8080 285 # } 286 # 287 # See Net::HTTP.new for further details and examples such as proxies that 288 # require a username and password. 289 # 290 # === Compression 291 # 292 # Net::HTTP automatically adds Accept-Encoding for compression of response 293 # bodies and automatically decompresses gzip and deflate responses unless a 294 # Range header was sent. 295 # 296 # Compression can be disabled through the Accept-Encoding: identity header. 297 # 298 # == HTTP Request Classes 299 # 300 # Here is the HTTP request class hierarchy. 301 # 302 # * Net::HTTPRequest 303 # * Net::HTTP::Get 304 # * Net::HTTP::Head 305 # * Net::HTTP::Post 306 # * Net::HTTP::Patch 307 # * Net::HTTP::Put 308 # * Net::HTTP::Proppatch 309 # * Net::HTTP::Lock 310 # * Net::HTTP::Unlock 311 # * Net::HTTP::Options 312 # * Net::HTTP::Propfind 313 # * Net::HTTP::Delete 314 # * Net::HTTP::Move 315 # * Net::HTTP::Copy 316 # * Net::HTTP::Mkcol 317 # * Net::HTTP::Trace 318 # 319 # == HTTP Response Classes 320 # 321 # Here is HTTP response class hierarchy. All classes are defined in Net 322 # module and are subclasses of Net::HTTPResponse. 323 # 324 # HTTPUnknownResponse:: For unhandled HTTP extensions 325 # HTTPInformation:: 1xx 326 # HTTPContinue:: 100 327 # HTTPSwitchProtocol:: 101 328 # HTTPSuccess:: 2xx 329 # HTTPOK:: 200 330 # HTTPCreated:: 201 331 # HTTPAccepted:: 202 332 # HTTPNonAuthoritativeInformation:: 203 333 # HTTPNoContent:: 204 334 # HTTPResetContent:: 205 335 # HTTPPartialContent:: 206 336 # HTTPMultiStatus:: 207 337 # HTTPRedirection:: 3xx 338 # HTTPMultipleChoices:: 300 339 # HTTPMovedPermanently:: 301 340 # HTTPFound:: 302 341 # HTTPSeeOther:: 303 342 # HTTPNotModified:: 304 343 # HTTPUseProxy:: 305 344 # HTTPTemporaryRedirect:: 307 345 # HTTPClientError:: 4xx 346 # HTTPBadRequest:: 400 347 # HTTPUnauthorized:: 401 348 # HTTPPaymentRequired:: 402 349 # HTTPForbidden:: 403 350 # HTTPNotFound:: 404 351 # HTTPMethodNotAllowed:: 405 352 # HTTPNotAcceptable:: 406 353 # HTTPProxyAuthenticationRequired:: 407 354 # HTTPRequestTimeOut:: 408 355 # HTTPConflict:: 409 356 # HTTPGone:: 410 357 # HTTPLengthRequired:: 411 358 # HTTPPreconditionFailed:: 412 359 # HTTPRequestEntityTooLarge:: 413 360 # HTTPRequestURITooLong:: 414 361 # HTTPUnsupportedMediaType:: 415 362 # HTTPRequestedRangeNotSatisfiable:: 416 363 # HTTPExpectationFailed:: 417 364 # HTTPUnprocessableEntity:: 422 365 # HTTPLocked:: 423 366 # HTTPFailedDependency:: 424 367 # HTTPUpgradeRequired:: 426 368 # HTTPPreconditionRequired:: 428 369 # HTTPTooManyRequests:: 429 370 # HTTPRequestHeaderFieldsTooLarge:: 431 371 # HTTPServerError:: 5xx 372 # HTTPInternalServerError:: 500 373 # HTTPNotImplemented:: 501 374 # HTTPBadGateway:: 502 375 # HTTPServiceUnavailable:: 503 376 # HTTPGatewayTimeOut:: 504 377 # HTTPVersionNotSupported:: 505 378 # HTTPInsufficientStorage:: 507 379 # HTTPNetworkAuthenticationRequired:: 511 380 # 381 # There is also the Net::HTTPBadResponse exception which is raised when 382 # there is a protocol error. 383 # 384 class HTTP < Protocol 385 386 # :stopdoc: 387 Revision = %q$Revision: 40295 $.split[1] 388 HTTPVersion = '1.1' 389 begin 390 require 'zlib' 391 require 'stringio' #for our purposes (unpacking gzip) lump these together 392 HAVE_ZLIB=true 393 rescue LoadError 394 HAVE_ZLIB=false 395 end 396 # :startdoc: 397 398 # Turns on net/http 1.2 (ruby 1.8) features. 399 # Defaults to ON in ruby 1.8 or later. 400 def HTTP.version_1_2 401 true 402 end 403 404 # Returns true if net/http is in version 1.2 mode. 405 # Defaults to true. 406 def HTTP.version_1_2? 407 true 408 end 409 410 def HTTP.version_1_1? #:nodoc: 411 false 412 end 413 414 class << HTTP 415 alias is_version_1_1? version_1_1? #:nodoc: 416 alias is_version_1_2? version_1_2? #:nodoc: 417 end 418 419 # 420 # short cut methods 421 # 422 423 # 424 # Gets the body text from the target and outputs it to $stdout. The 425 # target can either be specified as 426 # (+uri+), or as (+host+, +path+, +port+ = 80); so: 427 # 428 # Net::HTTP.get_print URI('http://www.example.com/index.html') 429 # 430 # or: 431 # 432 # Net::HTTP.get_print 'www.example.com', '/index.html' 433 # 434 def HTTP.get_print(uri_or_host, path = nil, port = nil) 435 get_response(uri_or_host, path, port) {|res| 436 res.read_body do |chunk| 437 $stdout.print chunk 438 end 439 } 440 nil 441 end 442 443 # Sends a GET request to the target and returns the HTTP response 444 # as a string. The target can either be specified as 445 # (+uri+), or as (+host+, +path+, +port+ = 80); so: 446 # 447 # print Net::HTTP.get(URI('http://www.example.com/index.html')) 448 # 449 # or: 450 # 451 # print Net::HTTP.get('www.example.com', '/index.html') 452 # 453 def HTTP.get(uri_or_host, path = nil, port = nil) 454 get_response(uri_or_host, path, port).body 455 end 456 457 # Sends a GET request to the target and returns the HTTP response 458 # as a Net::HTTPResponse object. The target can either be specified as 459 # (+uri+), or as (+host+, +path+, +port+ = 80); so: 460 # 461 # res = Net::HTTP.get_response(URI('http://www.example.com/index.html')) 462 # print res.body 463 # 464 # or: 465 # 466 # res = Net::HTTP.get_response('www.example.com', '/index.html') 467 # print res.body 468 # 469 def HTTP.get_response(uri_or_host, path = nil, port = nil, &block) 470 if path 471 host = uri_or_host 472 new(host, port || HTTP.default_port).start {|http| 473 return http.request_get(path, &block) 474 } 475 else 476 uri = uri_or_host 477 start(uri.hostname, uri.port, 478 :use_ssl => uri.scheme == 'https') {|http| 479 return http.request_get(uri, &block) 480 } 481 end 482 end 483 484 # Posts HTML form data to the specified URI object. 485 # The form data must be provided as a Hash mapping from String to String. 486 # Example: 487 # 488 # { "cmd" => "search", "q" => "ruby", "max" => "50" } 489 # 490 # This method also does Basic Authentication iff +url+.user exists. 491 # But userinfo for authentication is deprecated (RFC3986). 492 # So this feature will be removed. 493 # 494 # Example: 495 # 496 # require 'net/http' 497 # require 'uri' 498 # 499 # Net::HTTP.post_form URI('http://www.example.com/search.cgi'), 500 # { "q" => "ruby", "max" => "50" } 501 # 502 def HTTP.post_form(url, params) 503 req = Post.new(url) 504 req.form_data = params 505 req.basic_auth url.user, url.password if url.user 506 start(url.hostname, url.port, 507 :use_ssl => url.scheme == 'https' ) {|http| 508 http.request(req) 509 } 510 end 511 512 # 513 # HTTP session management 514 # 515 516 # The default port to use for HTTP requests; defaults to 80. 517 def HTTP.default_port 518 http_default_port() 519 end 520 521 # The default port to use for HTTP requests; defaults to 80. 522 def HTTP.http_default_port 523 80 524 end 525 526 # The default port to use for HTTPS requests; defaults to 443. 527 def HTTP.https_default_port 528 443 529 end 530 531 def HTTP.socket_type #:nodoc: obsolete 532 BufferedIO 533 end 534 535 # :call-seq: 536 # HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block) 537 # HTTP.start(address, port=nil, p_addr=nil, p_port=nil, p_user=nil, p_pass=nil, opt, &block) 538 # 539 # Creates a new Net::HTTP object, then additionally opens the TCP 540 # connection and HTTP session. 541 # 542 # Arguments are the following: 543 # _address_ :: hostname or IP address of the server 544 # _port_ :: port of the server 545 # _p_addr_ :: address of proxy 546 # _p_port_ :: port of proxy 547 # _p_user_ :: user of proxy 548 # _p_pass_ :: pass of proxy 549 # _opt_ :: optional hash 550 # 551 # _opt_ sets following values by its accessor. 552 # The keys are ca_file, ca_path, cert, cert_store, ciphers, 553 # close_on_empty_response, key, open_timeout, read_timeout, ssl_timeout, 554 # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode. 555 # If you set :use_ssl as true, you can use https and default value of 556 # verify_mode is set as OpenSSL::SSL::VERIFY_PEER. 557 # 558 # If the optional block is given, the newly 559 # created Net::HTTP object is passed to it and closed when the 560 # block finishes. In this case, the return value of this method 561 # is the return value of the block. If no block is given, the 562 # return value of this method is the newly created Net::HTTP object 563 # itself, and the caller is responsible for closing it upon completion 564 # using the finish() method. 565 def HTTP.start(address, *arg, &block) # :yield: +http+ 566 arg.pop if opt = Hash.try_convert(arg[-1]) 567 port, p_addr, p_port, p_user, p_pass = *arg 568 port = https_default_port if !port && opt && opt[:use_ssl] 569 http = new(address, port, p_addr, p_port, p_user, p_pass) 570 571 if opt 572 if opt[:use_ssl] 573 opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt) 574 end 575 http.methods.grep(/\A(\w+)=\z/) do |meth| 576 key = $1.to_sym 577 opt.key?(key) or next 578 http.__send__(meth, opt[key]) 579 end 580 end 581 582 http.start(&block) 583 end 584 585 class << HTTP 586 alias newobj new # :nodoc: 587 end 588 589 # Creates a new Net::HTTP object without opening a TCP connection or 590 # HTTP session. 591 # 592 # The +address+ should be a DNS hostname or IP address, the +port+ is the 593 # port the server operates on. If no +port+ is given the default port for 594 # HTTP or HTTPS is used. 595 # 596 # If none of the +p_+ arguments are given, the proxy host and port are 597 # taken from the +http_proxy+ environment variable (or its uppercase 598 # equivalent) if present. If the proxy requires authentication you must 599 # supply it by hand. See URI::Generic#find_proxy for details of proxy 600 # detection from the environment. To disable proxy detection set +p_addr+ 601 # to nil. 602 # 603 # If you are connecting to a custom proxy, +p_addr+ the DNS name or IP 604 # address of the proxy host, +p_port+ the port to use to access the proxy, 605 # and +p_user+ and +p_pass+ the username and password if authorization is 606 # required to use the proxy. 607 # 608 def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) 609 http = super address, port 610 611 if proxy_class? then # from Net::HTTP::Proxy() 612 http.proxy_from_env = @proxy_from_env 613 http.proxy_address = @proxy_address 614 http.proxy_port = @proxy_port 615 http.proxy_user = @proxy_user 616 http.proxy_pass = @proxy_pass 617 elsif p_addr == :ENV then 618 http.proxy_from_env = true 619 else 620 http.proxy_address = p_addr 621 http.proxy_port = p_port || default_port 622 http.proxy_user = p_user 623 http.proxy_pass = p_pass 624 end 625 626 http 627 end 628 629 # Creates a new Net::HTTP object for the specified server address, 630 # without opening the TCP connection or initializing the HTTP session. 631 # The +address+ should be a DNS hostname or IP address. 632 def initialize(address, port = nil) 633 @address = address 634 @port = (port || HTTP.default_port) 635 @local_host = nil 636 @local_port = nil 637 @curr_http_version = HTTPVersion 638 @keep_alive_timeout = 2 639 @last_communicated = nil 640 @close_on_empty_response = false 641 @socket = nil 642 @started = false 643 @open_timeout = nil 644 @read_timeout = 60 645 @continue_timeout = nil 646 @debug_output = nil 647 648 @proxy_from_env = false 649 @proxy_uri = nil 650 @proxy_address = nil 651 @proxy_port = nil 652 @proxy_user = nil 653 @proxy_pass = nil 654 655 @use_ssl = false 656 @ssl_context = nil 657 @ssl_session = nil 658 @enable_post_connection_check = true 659 @sspi_enabled = false 660 SSL_IVNAMES.each do |ivname| 661 instance_variable_set ivname, nil 662 end 663 end 664 665 def inspect 666 "#<#{self.class} #{@address}:#{@port} open=#{started?}>" 667 end 668 669 # *WARNING* This method opens a serious security hole. 670 # Never use this method in production code. 671 # 672 # Sets an output stream for debugging. 673 # 674 # http = Net::HTTP.new 675 # http.set_debug_output $stderr 676 # http.start { .... } 677 # 678 def set_debug_output(output) 679 warn 'Net::HTTP#set_debug_output called after HTTP started' if started? 680 @debug_output = output 681 end 682 683 # The DNS host name or IP address to connect to. 684 attr_reader :address 685 686 # The port number to connect to. 687 attr_reader :port 688 689 # The local host used to estabilish the connection. 690 attr_accessor :local_host 691 692 # The local port used to estabilish the connection. 693 attr_accessor :local_port 694 695 attr_writer :proxy_from_env 696 attr_writer :proxy_address 697 attr_writer :proxy_port 698 attr_writer :proxy_user 699 attr_writer :proxy_pass 700 701 # Number of seconds to wait for the connection to open. Any number 702 # may be used, including Floats for fractional seconds. If the HTTP 703 # object cannot open a connection in this many seconds, it raises a 704 # Net::OpenTimeout exception. The default value is +nil+. 705 attr_accessor :open_timeout 706 707 # Number of seconds to wait for one block to be read (via one read(2) 708 # call). Any number may be used, including Floats for fractional 709 # seconds. If the HTTP object cannot read data in this many seconds, 710 # it raises a Net::ReadTimeout exception. The default value is 60 seconds. 711 attr_reader :read_timeout 712 713 # Setter for the read_timeout attribute. 714 def read_timeout=(sec) 715 @socket.read_timeout = sec if @socket 716 @read_timeout = sec 717 end 718 719 # Seconds to wait for 100 Continue response. If the HTTP object does not 720 # receive a response in this many seconds it sends the request body. The 721 # default value is +nil+. 722 attr_reader :continue_timeout 723 724 # Setter for the continue_timeout attribute. 725 def continue_timeout=(sec) 726 @socket.continue_timeout = sec if @socket 727 @continue_timeout = sec 728 end 729 730 # Seconds to reuse the connection of the previous request. 731 # If the idle time is less than this Keep-Alive Timeout, 732 # Net::HTTP reuses the TCP/IP socket used by the previous communication. 733 # The default value is 2 seconds. 734 attr_accessor :keep_alive_timeout 735 736 # Returns true if the HTTP session has been started. 737 def started? 738 @started 739 end 740 741 alias active? started? #:nodoc: obsolete 742 743 attr_accessor :close_on_empty_response 744 745 # Returns true if SSL/TLS is being used with HTTP. 746 def use_ssl? 747 @use_ssl 748 end 749 750 # Turn on/off SSL. 751 # This flag must be set before starting session. 752 # If you change use_ssl value after session started, 753 # a Net::HTTP object raises IOError. 754 def use_ssl=(flag) 755 flag = flag ? true : false 756 if started? and @use_ssl != flag 757 raise IOError, "use_ssl value changed, but session already started" 758 end 759 @use_ssl = flag 760 end 761 762 SSL_IVNAMES = [ 763 :@ca_file, 764 :@ca_path, 765 :@cert, 766 :@cert_store, 767 :@ciphers, 768 :@key, 769 :@ssl_timeout, 770 :@ssl_version, 771 :@verify_callback, 772 :@verify_depth, 773 :@verify_mode, 774 ] 775 SSL_ATTRIBUTES = [ 776 :ca_file, 777 :ca_path, 778 :cert, 779 :cert_store, 780 :ciphers, 781 :key, 782 :ssl_timeout, 783 :ssl_version, 784 :verify_callback, 785 :verify_depth, 786 :verify_mode, 787 ] 788 789 # Sets path of a CA certification file in PEM format. 790 # 791 # The file can contain several CA certificates. 792 attr_accessor :ca_file 793 794 # Sets path of a CA certification directory containing certifications in 795 # PEM format. 796 attr_accessor :ca_path 797 798 # Sets an OpenSSL::X509::Certificate object as client certificate. 799 # (This method is appeared in Michal Rokos's OpenSSL extension). 800 attr_accessor :cert 801 802 # Sets the X509::Store to verify peer certificate. 803 attr_accessor :cert_store 804 805 # Sets the available ciphers. See OpenSSL::SSL::SSLContext#ciphers= 806 attr_accessor :ciphers 807 808 # Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. 809 # (This method is appeared in Michal Rokos's OpenSSL extension.) 810 attr_accessor :key 811 812 # Sets the SSL timeout seconds. 813 attr_accessor :ssl_timeout 814 815 # Sets the SSL version. See OpenSSL::SSL::SSLContext#ssl_version= 816 attr_accessor :ssl_version 817 818 # Sets the verify callback for the server certification verification. 819 attr_accessor :verify_callback 820 821 # Sets the maximum depth for the certificate chain verification. 822 attr_accessor :verify_depth 823 824 # Sets the flags for server the certification verification at beginning of 825 # SSL/TLS session. 826 # 827 # OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER are acceptable. 828 attr_accessor :verify_mode 829 830 # Returns the X.509 certificates the server presented. 831 def peer_cert 832 if not use_ssl? or not @socket 833 return nil 834 end 835 @socket.io.peer_cert 836 end 837 838 # Opens a TCP connection and HTTP session. 839 # 840 # When this method is called with a block, it passes the Net::HTTP 841 # object to the block, and closes the TCP connection and HTTP session 842 # after the block has been executed. 843 # 844 # When called with a block, it returns the return value of the 845 # block; otherwise, it returns self. 846 # 847 def start # :yield: http 848 raise IOError, 'HTTP session already opened' if @started 849 if block_given? 850 begin 851 do_start 852 return yield(self) 853 ensure 854 do_finish 855 end 856 end 857 do_start 858 self 859 end 860 861 def do_start 862 connect 863 @started = true 864 end 865 private :do_start 866 867 def connect 868 if proxy? then 869 conn_address = proxy_address 870 conn_port = proxy_port 871 else 872 conn_address = address 873 conn_port = port 874 end 875 876 D "opening connection to #{conn_address}:#{conn_port}..." 877 s = Timeout.timeout(@open_timeout, Net::OpenTimeout) { 878 TCPSocket.open(conn_address, conn_port, @local_host, @local_port) 879 } 880 D "opened" 881 if use_ssl? 882 ssl_parameters = Hash.new 883 iv_list = instance_variables 884 SSL_IVNAMES.each_with_index do |ivname, i| 885 if iv_list.include?(ivname) and 886 value = instance_variable_get(ivname) 887 ssl_parameters[SSL_ATTRIBUTES[i]] = value if value 888 end 889 end 890 @ssl_context = OpenSSL::SSL::SSLContext.new 891 @ssl_context.set_params(ssl_parameters) 892 D "starting SSL for #{conn_address}:#{conn_port}..." 893 s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) 894 s.sync_close = true 895 D "SSL established" 896 end 897 @socket = BufferedIO.new(s) 898 @socket.read_timeout = @read_timeout 899 @socket.continue_timeout = @continue_timeout 900 @socket.debug_output = @debug_output 901 if use_ssl? 902 begin 903 if proxy? 904 buf = "CONNECT #{@address}:#{@port} HTTP/#{HTTPVersion}\r\n" 905 buf << "Host: #{@address}:#{@port}\r\n" 906 if proxy_user 907 credential = ["#{proxy_user}:#{proxy_pass}"].pack('m') 908 credential.delete!("\r\n") 909 buf << "Proxy-Authorization: Basic #{credential}\r\n" 910 end 911 buf << "\r\n" 912 @socket.write(buf) 913 HTTPResponse.read_new(@socket).value 914 end 915 s.session = @ssl_session if @ssl_session 916 # Server Name Indication (SNI) RFC 3546 917 s.hostname = @address if s.respond_to? :hostname= 918 Timeout.timeout(@open_timeout, Net::OpenTimeout) { s.connect } 919 if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE 920 s.post_connection_check(@address) 921 end 922 @ssl_session = s.session 923 rescue => exception 924 D "Conn close because of connect error #{exception}" 925 @socket.close if @socket and not @socket.closed? 926 raise exception 927 end 928 end 929 on_connect 930 end 931 private :connect 932 933 def on_connect 934 end 935 private :on_connect 936 937 # Finishes the HTTP session and closes the TCP connection. 938 # Raises IOError if the session has not been started. 939 def finish 940 raise IOError, 'HTTP session not yet started' unless started? 941 do_finish 942 end 943 944 def do_finish 945 @started = false 946 @socket.close if @socket and not @socket.closed? 947 @socket = nil 948 end 949 private :do_finish 950 951 # 952 # proxy 953 # 954 955 public 956 957 # no proxy 958 @is_proxy_class = false 959 @proxy_from_env = false 960 @proxy_addr = nil 961 @proxy_port = nil 962 @proxy_user = nil 963 @proxy_pass = nil 964 965 # Creates an HTTP proxy class which behaves like Net::HTTP, but 966 # performs all access via the specified proxy. 967 # 968 # This class is obsolete. You may pass these same parameters directly to 969 # Net::HTTP.new. See Net::HTTP.new for details of the arguments. 970 def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) 971 return self unless p_addr 972 973 Class.new(self) { 974 @is_proxy_class = true 975 976 if p_addr == :ENV then 977 @proxy_from_env = true 978 @proxy_address = nil 979 @proxy_port = nil 980 else 981 @proxy_from_env = false 982 @proxy_address = p_addr 983 @proxy_port = p_port || default_port 984 end 985 986 @proxy_user = p_user 987 @proxy_pass = p_pass 988 } 989 end 990 991 class << HTTP 992 # returns true if self is a class which was created by HTTP::Proxy. 993 def proxy_class? 994 defined?(@is_proxy_class) ? @is_proxy_class : false 995 end 996 997 # Address of proxy host. If Net::HTTP does not use a proxy, nil. 998 attr_reader :proxy_address 999 1000 # Port number of proxy host. If Net::HTTP does not use a proxy, nil. 1001 attr_reader :proxy_port 1002 1003 # User name for accessing proxy. If Net::HTTP does not use a proxy, nil. 1004 attr_reader :proxy_user 1005 1006 # User password for accessing proxy. If Net::HTTP does not use a proxy, 1007 # nil. 1008 attr_reader :proxy_pass 1009 end 1010 1011 # True if requests for this connection will be proxied 1012 def proxy? 1013 !!if @proxy_from_env then 1014 proxy_uri 1015 else 1016 @proxy_address 1017 end 1018 end 1019 1020 # True if the proxy for this connection is determined from the environment 1021 def proxy_from_env? 1022 @proxy_from_env 1023 end 1024 1025 # The proxy URI determined from the environment for this connection. 1026 def proxy_uri # :nodoc: 1027 @proxy_uri ||= URI("http://#{address}:#{port}").find_proxy 1028 end 1029 1030 # The address of the proxy server, if one is configured. 1031 def proxy_address 1032 if @proxy_from_env then 1033 proxy_uri && proxy_uri.hostname 1034 else 1035 @proxy_address 1036 end 1037 end 1038 1039 # The port of the proxy server, if one is configured. 1040 def proxy_port 1041 if @proxy_from_env then 1042 proxy_uri && proxy_uri.port 1043 else 1044 @proxy_port 1045 end 1046 end 1047 1048 # The proxy username, if one is configured 1049 def proxy_user 1050 @proxy_user 1051 end 1052 1053 # The proxy password, if one is configured 1054 def proxy_pass 1055 @proxy_pass 1056 end 1057 1058 alias proxyaddr proxy_address #:nodoc: obsolete 1059 alias proxyport proxy_port #:nodoc: obsolete 1060 1061 private 1062 1063 # without proxy, obsolete 1064 1065 def conn_address # :nodoc: 1066 address() 1067 end 1068 1069 def conn_port # :nodoc: 1070 port() 1071 end 1072 1073 def edit_path(path) 1074 if proxy? and not use_ssl? then 1075 "http://#{addr_port}#{path}" 1076 else 1077 path 1078 end 1079 end 1080 1081 # 1082 # HTTP operations 1083 # 1084 1085 public 1086 1087 # Retrieves data from +path+ on the connected-to host which may be an 1088 # absolute path String or a URI to extract the path from. 1089 # 1090 # +initheader+ must be a Hash like { 'Accept' => '*/*', ... }, 1091 # and it defaults to an empty hash. 1092 # If +initheader+ doesn't have the key 'accept-encoding', then 1093 # a value of "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" is used, 1094 # so that gzip compression is used in preference to deflate 1095 # compression, which is used in preference to no compression. 1096 # Ruby doesn't have libraries to support the compress (Lempel-Ziv) 1097 # compression, so that is not supported. The intent of this is 1098 # to reduce bandwidth by default. If this routine sets up 1099 # compression, then it does the decompression also, removing 1100 # the header as well to prevent confusion. Otherwise 1101 # it leaves the body as it found it. 1102 # 1103 # This method returns a Net::HTTPResponse object. 1104 # 1105 # If called with a block, yields each fragment of the 1106 # entity body in turn as a string as it is read from 1107 # the socket. Note that in this case, the returned response 1108 # object will *not* contain a (meaningful) body. 1109 # 1110 # +dest+ argument is obsolete. 1111 # It still works but you must not use it. 1112 # 1113 # This method never raises an exception. 1114 # 1115 # response = http.get('/index.html') 1116 # 1117 # # using block 1118 # File.open('result.txt', 'w') {|f| 1119 # http.get('/~foo/') do |str| 1120 # f.write str 1121 # end 1122 # } 1123 # 1124 def get(path, initheader = {}, dest = nil, &block) # :yield: +body_segment+ 1125 res = nil 1126 request(Get.new(path, initheader)) {|r| 1127 r.read_body dest, &block 1128 res = r 1129 } 1130 res 1131 end 1132 1133 # Gets only the header from +path+ on the connected-to host. 1134 # +header+ is a Hash like { 'Accept' => '*/*', ... }. 1135 # 1136 # This method returns a Net::HTTPResponse object. 1137 # 1138 # This method never raises an exception. 1139 # 1140 # response = nil 1141 # Net::HTTP.start('some.www.server', 80) {|http| 1142 # response = http.head('/index.html') 1143 # } 1144 # p response['content-type'] 1145 # 1146 def head(path, initheader = nil) 1147 request(Head.new(path, initheader)) 1148 end 1149 1150 # Posts +data+ (must be a String) to +path+. +header+ must be a Hash 1151 # like { 'Accept' => '*/*', ... }. 1152 # 1153 # This method returns a Net::HTTPResponse object. 1154 # 1155 # If called with a block, yields each fragment of the 1156 # entity body in turn as a string as it is read from 1157 # the socket. Note that in this case, the returned response 1158 # object will *not* contain a (meaningful) body. 1159 # 1160 # +dest+ argument is obsolete. 1161 # It still works but you must not use it. 1162 # 1163 # This method never raises exception. 1164 # 1165 # response = http.post('/cgi-bin/search.rb', 'query=foo') 1166 # 1167 # # using block 1168 # File.open('result.txt', 'w') {|f| 1169 # http.post('/cgi-bin/search.rb', 'query=foo') do |str| 1170 # f.write str 1171 # end 1172 # } 1173 # 1174 # You should set Content-Type: header field for POST. 1175 # If no Content-Type: field given, this method uses 1176 # "application/x-www-form-urlencoded" by default. 1177 # 1178 def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ 1179 send_entity(path, data, initheader, dest, Post, &block) 1180 end 1181 1182 # Sends a PATCH request to the +path+ and gets a response, 1183 # as an HTTPResponse object. 1184 def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ 1185 send_entity(path, data, initheader, dest, Patch, &block) 1186 end 1187 1188 def put(path, data, initheader = nil) #:nodoc: 1189 request(Put.new(path, initheader), data) 1190 end 1191 1192 # Sends a PROPPATCH request to the +path+ and gets a response, 1193 # as an HTTPResponse object. 1194 def proppatch(path, body, initheader = nil) 1195 request(Proppatch.new(path, initheader), body) 1196 end 1197 1198 # Sends a LOCK request to the +path+ and gets a response, 1199 # as an HTTPResponse object. 1200 def lock(path, body, initheader = nil) 1201 request(Lock.new(path, initheader), body) 1202 end 1203 1204 # Sends a UNLOCK request to the +path+ and gets a response, 1205 # as an HTTPResponse object. 1206 def unlock(path, body, initheader = nil) 1207 request(Unlock.new(path, initheader), body) 1208 end 1209 1210 # Sends a OPTIONS request to the +path+ and gets a response, 1211 # as an HTTPResponse object. 1212 def options(path, initheader = nil) 1213 request(Options.new(path, initheader)) 1214 end 1215 1216 # Sends a PROPFIND request to the +path+ and gets a response, 1217 # as an HTTPResponse object. 1218 def propfind(path, body = nil, initheader = {'Depth' => '0'}) 1219 request(Propfind.new(path, initheader), body) 1220 end 1221 1222 # Sends a DELETE request to the +path+ and gets a response, 1223 # as an HTTPResponse object. 1224 def delete(path, initheader = {'Depth' => 'Infinity'}) 1225 request(Delete.new(path, initheader)) 1226 end 1227 1228 # Sends a MOVE request to the +path+ and gets a response, 1229 # as an HTTPResponse object. 1230 def move(path, initheader = nil) 1231 request(Move.new(path, initheader)) 1232 end 1233 1234 # Sends a COPY request to the +path+ and gets a response, 1235 # as an HTTPResponse object. 1236 def copy(path, initheader = nil) 1237 request(Copy.new(path, initheader)) 1238 end 1239 1240 # Sends a MKCOL request to the +path+ and gets a response, 1241 # as an HTTPResponse object. 1242 def mkcol(path, body = nil, initheader = nil) 1243 request(Mkcol.new(path, initheader), body) 1244 end 1245 1246 # Sends a TRACE request to the +path+ and gets a response, 1247 # as an HTTPResponse object. 1248 def trace(path, initheader = nil) 1249 request(Trace.new(path, initheader)) 1250 end 1251 1252 # Sends a GET request to the +path+. 1253 # Returns the response as a Net::HTTPResponse object. 1254 # 1255 # When called with a block, passes an HTTPResponse object to the block. 1256 # The body of the response will not have been read yet; 1257 # the block can process it using HTTPResponse#read_body, 1258 # if desired. 1259 # 1260 # Returns the response. 1261 # 1262 # This method never raises Net::* exceptions. 1263 # 1264 # response = http.request_get('/index.html') 1265 # # The entity body is already read in this case. 1266 # p response['content-type'] 1267 # puts response.body 1268 # 1269 # # Using a block 1270 # http.request_get('/index.html') {|response| 1271 # p response['content-type'] 1272 # response.read_body do |str| # read body now 1273 # print str 1274 # end 1275 # } 1276 # 1277 def request_get(path, initheader = nil, &block) # :yield: +response+ 1278 request(Get.new(path, initheader), &block) 1279 end 1280 1281 # Sends a HEAD request to the +path+ and returns the response 1282 # as a Net::HTTPResponse object. 1283 # 1284 # Returns the response. 1285 # 1286 # This method never raises Net::* exceptions. 1287 # 1288 # response = http.request_head('/index.html') 1289 # p response['content-type'] 1290 # 1291 def request_head(path, initheader = nil, &block) 1292 request(Head.new(path, initheader), &block) 1293 end 1294 1295 # Sends a POST request to the +path+. 1296 # 1297 # Returns the response as a Net::HTTPResponse object. 1298 # 1299 # When called with a block, the block is passed an HTTPResponse 1300 # object. The body of that response will not have been read yet; 1301 # the block can process it using HTTPResponse#read_body, if desired. 1302 # 1303 # Returns the response. 1304 # 1305 # This method never raises Net::* exceptions. 1306 # 1307 # # example 1308 # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...') 1309 # p response.status 1310 # puts response.body # body is already read in this case 1311 # 1312 # # using block 1313 # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response| 1314 # p response.status 1315 # p response['content-type'] 1316 # response.read_body do |str| # read body now 1317 # print str 1318 # end 1319 # } 1320 # 1321 def request_post(path, data, initheader = nil, &block) # :yield: +response+ 1322 request Post.new(path, initheader), data, &block 1323 end 1324 1325 def request_put(path, data, initheader = nil, &block) #:nodoc: 1326 request Put.new(path, initheader), data, &block 1327 end 1328 1329 alias get2 request_get #:nodoc: obsolete 1330 alias head2 request_head #:nodoc: obsolete 1331 alias post2 request_post #:nodoc: obsolete 1332 alias put2 request_put #:nodoc: obsolete 1333 1334 1335 # Sends an HTTP request to the HTTP server. 1336 # Also sends a DATA string if +data+ is given. 1337 # 1338 # Returns a Net::HTTPResponse object. 1339 # 1340 # This method never raises Net::* exceptions. 1341 # 1342 # response = http.send_request('GET', '/index.html') 1343 # puts response.body 1344 # 1345 def send_request(name, path, data = nil, header = nil) 1346 r = HTTPGenericRequest.new(name,(data ? true : false),true,path,header) 1347 request r, data 1348 end 1349 1350 # Sends an HTTPRequest object +req+ to the HTTP server. 1351 # 1352 # If +req+ is a Net::HTTP::Post or Net::HTTP::Put request containing 1353 # data, the data is also sent. Providing data for a Net::HTTP::Head or 1354 # Net::HTTP::Get request results in an ArgumentError. 1355 # 1356 # Returns an HTTPResponse object. 1357 # 1358 # When called with a block, passes an HTTPResponse object to the block. 1359 # The body of the response will not have been read yet; 1360 # the block can process it using HTTPResponse#read_body, 1361 # if desired. 1362 # 1363 # This method never raises Net::* exceptions. 1364 # 1365 def request(req, body = nil, &block) # :yield: +response+ 1366 unless started? 1367 start { 1368 req['connection'] ||= 'close' 1369 return request(req, body, &block) 1370 } 1371 end 1372 if proxy_user() 1373 req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl? 1374 end 1375 req.set_body_internal body 1376 res = transport_request(req, &block) 1377 if sspi_auth?(res) 1378 sspi_auth(req) 1379 res = transport_request(req, &block) 1380 end 1381 res 1382 end 1383 1384 private 1385 1386 # Executes a request which uses a representation 1387 # and returns its body. 1388 def send_entity(path, data, initheader, dest, type, &block) 1389 res = nil 1390 request(type.new(path, initheader), data) {|r| 1391 r.read_body dest, &block 1392 res = r 1393 } 1394 res 1395 end 1396 1397 IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc: 1398 1399 def transport_request(req) 1400 count = 0 1401 begin 1402 begin_transport req 1403 res = catch(:response) { 1404 req.exec @socket, @curr_http_version, edit_path(req.path) 1405 begin 1406 res = HTTPResponse.read_new(@socket) 1407 res.decode_content = req.decode_content 1408 end while res.kind_of?(HTTPContinue) 1409 1410 res.uri = req.uri 1411 1412 res.reading_body(@socket, req.response_body_permitted?) { 1413 yield res if block_given? 1414 } 1415 res 1416 } 1417 rescue Net::OpenTimeout 1418 raise 1419 rescue Net::ReadTimeout, IOError, EOFError, 1420 Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE, 1421 # avoid a dependency on OpenSSL 1422 defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError, 1423 Timeout::Error => exception 1424 if count == 0 && IDEMPOTENT_METHODS_.include?(req.method) 1425 count += 1 1426 @socket.close if @socket and not @socket.closed? 1427 D "Conn close because of error #{exception}, and retry" 1428 retry 1429 end 1430 D "Conn close because of error #{exception}" 1431 @socket.close if @socket and not @socket.closed? 1432 raise 1433 end 1434 1435 end_transport req, res 1436 res 1437 rescue => exception 1438 D "Conn close because of error #{exception}" 1439 @socket.close if @socket and not @socket.closed? 1440 raise exception 1441 end 1442 1443 def begin_transport(req) 1444 if @socket.closed? 1445 connect 1446 elsif @last_communicated && @last_communicated + @keep_alive_timeout < Time.now 1447 D 'Conn close because of keep_alive_timeout' 1448 @socket.close 1449 connect 1450 end 1451 1452 if not req.response_body_permitted? and @close_on_empty_response 1453 req['connection'] ||= 'close' 1454 end 1455 1456 host = req['host'] || address 1457 host = $1 if host =~ /(.*):\d+$/ 1458 req.update_uri host, port, use_ssl? 1459 1460 req['host'] ||= addr_port() 1461 end 1462 1463 def end_transport(req, res) 1464 @curr_http_version = res.http_version 1465 @last_communicated = nil 1466 if @socket.closed? 1467 D 'Conn socket closed' 1468 elsif not res.body and @close_on_empty_response 1469 D 'Conn close' 1470 @socket.close 1471 elsif keep_alive?(req, res) 1472 D 'Conn keep-alive' 1473 @last_communicated = Time.now 1474 else 1475 D 'Conn close' 1476 @socket.close 1477 end 1478 end 1479 1480 def keep_alive?(req, res) 1481 return false if req.connection_close? 1482 if @curr_http_version <= '1.0' 1483 res.connection_keep_alive? 1484 else # HTTP/1.1 or later 1485 not res.connection_close? 1486 end 1487 end 1488 1489 def sspi_auth?(res) 1490 return false unless @sspi_enabled 1491 if res.kind_of?(HTTPProxyAuthenticationRequired) and 1492 proxy? and res["Proxy-Authenticate"].include?("Negotiate") 1493 begin 1494 require 'win32/sspi' 1495 true 1496 rescue LoadError 1497 false 1498 end 1499 else 1500 false 1501 end 1502 end 1503 1504 def sspi_auth(req) 1505 n = Win32::SSPI::NegotiateAuth.new 1506 req["Proxy-Authorization"] = "Negotiate #{n.get_initial_token}" 1507 # Some versions of ISA will close the connection if this isn't present. 1508 req["Connection"] = "Keep-Alive" 1509 req["Proxy-Connection"] = "Keep-Alive" 1510 res = transport_request(req) 1511 authphrase = res["Proxy-Authenticate"] or return res 1512 req["Proxy-Authorization"] = "Negotiate #{n.complete_authentication(authphrase)}" 1513 rescue => err 1514 raise HTTPAuthenticationError.new('HTTP authentication failed', err) 1515 end 1516 1517 # 1518 # utils 1519 # 1520 1521 private 1522 1523 def addr_port 1524 if use_ssl? 1525 address() + (port == HTTP.https_default_port ? '' : ":#{port()}") 1526 else 1527 address() + (port == HTTP.http_default_port ? '' : ":#{port()}") 1528 end 1529 end 1530 1531 def D(msg) 1532 return unless @debug_output 1533 @debug_output << msg 1534 @debug_output << "\n" 1535 end 1536 end 1537 1538end 1539 1540require 'net/http/exceptions' 1541 1542require 'net/http/header' 1543 1544require 'net/http/generic_request' 1545require 'net/http/request' 1546require 'net/http/requests' 1547 1548require 'net/http/response' 1549require 'net/http/responses' 1550 1551require 'net/http/proxy_delta' 1552 1553require 'net/http/backward' 1554 1555