类 Net::HTTP

类 Net::HTTP 提供了一个丰富的库,它在使用 HTTP 请求-响应协议的客户端-服务器模型中实现了客户端。有关 HTTP 的信息,请参阅

关于示例

这里的示例假设已经引入了 net/http(这也需要引入 uri)。

require 'net/http'

这里许多代码示例都使用了以下示例网站:

一些示例还假设了以下变量:

uri = URI('https://jsonplaceholder.typicode.com/')
uri.freeze # Examples may not modify.
hostname = uri.hostname # => "jsonplaceholder.typicode.com"
path = uri.path         # => "/"
port = uri.port         # => 443

这样示例请求就可以写成:

Net::HTTP.get(uri)
Net::HTTP.get(hostname, '/index.html')
Net::HTTP.start(hostname) do |http|
  http.get('/todos/1')
  http.get('/todos/2')
end

需要修改 URI 的示例首先会复制 uri,然后修改副本。

_uri = uri.dup
_uri.path = '/todos/1'

策略

上面提到的方法是便捷方法,它们通过少量参数允许对请求进行最少的控制。为了获得更大的控制,可以考虑使用 请求对象

URI

在互联网上,URI统一资源标识符)是一个标识特定资源的字符串。它包含一些或所有以下内容:方案、主机名、路径、查询和片段;请参阅 URI 语法

一个 Ruby URI::Generic 对象表示一个互联网 URI。它提供了 schemehostnamepathqueryfragment 等方法。

方案

一个互联网 URI 具有一个 方案

Net::HTTP 支持的两种方案是 'https''http'

uri.scheme                       # => "https"
URI('http://example.com').scheme # => "http"

主机名

主机名标识可以向其发送请求的服务器(主机)。

hostname = uri.hostname # => "jsonplaceholder.typicode.com"
Net::HTTP.start(hostname) do |http|
  # Some HTTP stuff.
end

路径

主机特定的路径标识主机上的资源。

_uri = uri.dup
_uri.path = '/todos/1'
hostname = _uri.hostname
path = _uri.path
Net::HTTP.get(hostname, path)

查询

主机特定的查询会在 URI 中添加名称/值对。

_uri = uri.dup
params = {userId: 1, completed: false}
_uri.query = URI.encode_www_form(params)
_uri # => #<URI::HTTPS https://jsonplaceholder.typicode.com?userId=1&completed=false>
Net::HTTP.get(_uri)

片段

在 Net::HTTP 中,URI 片段 没有任何作用;无论是否包含片段,返回的数据都是相同的。

请求头

请求头可用于向主机传递附加信息,类似于方法调用中传递的参数;每个头都是一个名称/值对。

每个向主机发送请求的 Net::HTTP 方法都有可选参数 headers,其中头以字段名/值对的哈希形式表示。

headers = {Accept: 'application/json', Connection: 'Keep-Alive'}
Net::HTTP.get(uri, headers)

请参阅 请求字段 中的标准请求字段和常见请求字段列表。主机也可以接受其他自定义字段。

HTTP 会话

会话 是服务器(主机)和客户端之间的连接,

请参阅 策略 中的示例会话。

使用 Net::HTTP.start 的会话

如果您需要向单个主机(和端口)发送多个请求,请考虑使用单例方法 Net::HTTP.start 和一个代码块;该方法会通过以下方式自动处理会话:

在代码块中,您可以使用以下实例方法,每个方法都会发送单个请求。

使用 Net::HTTP.start 和 Net::HTTP.finish 的会话

您可以使用方法 startfinish 手动管理会话。

http = Net::HTTP.new(hostname)
http.start
http.get('/todos/1')
http.get('/todos/2')
http.delete('/posts/1')
http.finish # Needed to free resources.

单请求会话

某些便捷方法会自动处理会话,方法是:

发送 GET 请求的此类方法

发送 POST 请求的此类方法

HTTP 请求和响应

上面列出的许多方法都是便捷方法,它们中的每一个都发送一个请求并返回一个字符串,而无需直接使用 Net::HTTPRequest 和 Net::HTTPResponse 对象。

但是,您可以直接创建一个请求对象,发送请求并检索响应对象;请参阅

跟随重定向

每个返回的响应都是 Net::HTTPResponse 子类的实例。请参阅 响应类层次结构

特别是,类 Net::HTTPRedirection 是所有重定向类的父类。这使您可以构建一个 case 语句来正确处理重定向。

def fetch(uri, limit = 10)
  # You should choose a better exception.
  raise ArgumentError, 'Too many HTTP redirects' if limit == 0

  res = Net::HTTP.get_response(URI(uri))
  case res
  when Net::HTTPSuccess     # Any success class.
    res
  when Net::HTTPRedirection # Any redirection class.
    location = res['Location']
    warn "Redirected to #{location}"
    fetch(location, limit - 1)
  else                      # Any other class.
    res.value
  end
end

fetch(uri)

基本身份验证

基本身份验证是根据 RFC2617 执行的。

req = Net::HTTP::Get.new(uri)
req.basic_auth('user', 'pass')
res = Net::HTTP.start(hostname) do |http|
  http.request(req)
end

流式响应主体

默认情况下,Net::HTTP 会将整个响应读入内存。如果您正在处理大型文件或希望实现进度条,则可以将主体直接流式传输到 IO

Net::HTTP.start(hostname) do |http|
  req = Net::HTTP::Get.new(uri)
  http.request(req) do |res|
    open('t.tmp', 'w') do |f|
      res.read_body do |chunk|
        f.write chunk
      end
    end
  end
end

HTTPS

通过 Net::HTTP#use_ssl= 为 HTTP 连接启用 HTTPS。

Net::HTTP.start(hostname, :use_ssl => true) do |http|
  req = Net::HTTP::Get.new(uri)
  res = http.request(req)
end

或者,如果您只想进行 GET 请求,可以传入一个具有 HTTPS URL 的 URI 对象。如果 URI 对象具有 'https' URI 方案,Net::HTTP 会自动开启 TLS 验证。

uri # => #<URI::HTTPS https://jsonplaceholder.typicode.com/>
Net::HTTP.get(uri)

代理服务器

HTTP 对象可以拥有一个 代理服务器

您可以使用方法 Net::HTTP.new 或方法 Net::HTTP.start 创建一个带有代理服务器的 HTTP 对象。

代理可以通过参数 p_addr 或环境变量 'http_proxy' 定义。

使用参数 p_addr 作为字符串的代理

当参数 p_addr 是一个字符串主机名时,返回的 http 将使用给定的主机作为其代理。

http = Net::HTTP.new(hostname, nil, 'proxy.example')
http.proxy?          # => true
http.proxy_from_env? # => false
http.proxy_address   # => "proxy.example"
# These use default values.
http.proxy_port      # => 80
http.proxy_user      # => nil
http.proxy_pass      # => nil

还可以提供代理的端口、用户名和密码。

http = Net::HTTP.new(hostname, nil, 'proxy.example', 8000, 'pname', 'ppass')
# => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
http.proxy?          # => true
http.proxy_from_env? # => false
http.proxy_address   # => "proxy.example"
http.proxy_port      # => 8000
http.proxy_user      # => "pname"
http.proxy_pass      # => "ppass"

使用 'ENV['http_proxy']' 的代理

当环境变量 'http_proxy' 设置为一个 URI 字符串时,返回的 http 将使用该 URI 上的服务器作为其代理;请注意,URI 字符串必须具有协议,例如 'http''https'

ENV['http_proxy'] = 'http://example.com'
http = Net::HTTP.new(hostname)
http.proxy?          # => true
http.proxy_from_env? # => true
http.proxy_address   # => "example.com"
# These use default values.
http.proxy_port      # => 80
http.proxy_user      # => nil
http.proxy_pass      # => nil

URI 字符串可以包含代理用户名、密码和端口号。

ENV['http_proxy'] = 'http://pname:[email protected]:8000'
http = Net::HTTP.new(hostname)
http.proxy?          # => true
http.proxy_from_env? # => true
http.proxy_address   # => "example.com"
http.proxy_port      # => 8000
http.proxy_user      # => "pname"
http.proxy_pass      # => "ppass"

过滤代理

使用方法 Net::HTTP.new(但不是 Net::HTTP.start),您可以使用参数 p_no_proxy 过滤代理。

压缩和解压缩

Net::HTTP 不会在发送之前压缩请求主体。

默认情况下,Net::HTTP 会将标头 'Accept-Encoding' 添加到新的 请求对象

Net::HTTP::Get.new(uri)['Accept-Encoding']
# => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"

这会请求服务器在存在响应主体的情况下对响应主体进行 zip 编码;服务器不需要这样做。

如果响应具有标头 'Content-Range',则 Net::HTTP 不会自动解压缩响应主体。

否则,解压缩(或不解压缩)取决于标头 Content-Encoding 的值。

这里有什么

这是方法和属性的分类摘要。

Net::HTTP 对象

会话

连接

请求

响应

代理

安全

地址和端口

HTTP 版本

调试

属性

proxy_address[R]

返回代理主机地址,如果不存在则返回 nil;参见 Net::HTTP 中的代理服务器

proxy_pass[R]

返回访问代理的密码,如果不存在则返回 nil;参见 Net::HTTP 中的代理服务器

proxy_port[R]

返回代理主机的端口号,如果不存在则返回 nil;参见 Net::HTTP 中的代理服务器

proxy_user[R]

返回访问代理的用户名,如果不存在则返回 nil;参见 Net::HTTP 中的代理服务器

address[R]

返回在 ::new 中作为参数 address 给出的字符串主机名或主机 IP。

ca_file[RW]

设置或返回 PEM 格式的 CA 证书文件路径。

ca_path[RW]

设置或返回包含 PEM 格式证书文件的 CA 目录路径。

cert[RW]

设置或返回用于客户端证书的 OpenSSL::X509::Certificate 对象。

cert_store[RW]

设置或返回用于验证对等证书的 X509::Store。

ciphers[RW]

设置或返回可用的 SSL 密码。参见 OpenSSL::SSL::SSLContext#ciphers=

close_on_empty_response[RW]

设置或返回响应为空时是否关闭连接;初始值为 false

continue_timeout[R]

返回继续超时值;请参阅 continue_timeout=。

extra_chain_cert[RW]

设置或返回要添加到证书链中的额外 X509 证书。请参阅 OpenSSL::SSL::SSLContext#add_certificate

ignore_eof[RW]

设置或返回是否在使用 Content-Length 标头读取响应主体时忽略文件结尾;最初为 true

keep_alive_timeout[RW]

设置或返回发送请求后保持连接打开的秒数(整数或浮点数);最初为 2。如果在给定时间间隔内发出新请求,则将使用仍处于打开状态的连接;否则,连接将已关闭,并将打开新连接。

key[RW]

设置或返回 OpenSSL::PKey::RSAOpenSSL::PKey::DSA 对象。

local_host[RW]

设置或返回用于建立连接的字符串本地主机;最初为 nil

local_port[RW]

设置或返回用于建立连接的整数本地端口;最初为 nil

max_retries[R]

返回重试幂等请求的最大次数;请参阅 max_retries=

max_version[RW]

设置或返回最大 SSL 版本。请参阅 OpenSSL::SSL::SSLContext#max_version=。

min_version[RW]

设置或返回最小 SSL 版本。请参阅 OpenSSL::SSL::SSLContext#min_version=。

open_timeout[RW]

设置或返回连接打开等待的秒数(整数或浮点数);初始值为 60。如果在给定时间间隔内未建立连接,则会引发异常。

port[R]

返回在 ::new 中作为参数 port 给出的整数端口号。

proxy_address[W]

设置代理地址;参见 代理服务器

proxy_from_env[W]

设置是否从环境变量 ‘ENV['http_proxy']’ 中确定代理;参见 使用 ENV[‘http_proxy’] 的代理

proxy_pass[W]

设置代理密码;参见 代理服务器

proxy_port[W]

设置代理端口;参见 代理服务器

proxy_user[W]

设置代理用户;参见 代理服务器

read_timeout[R]

返回等待读取一个块(通过一个 read(2) 调用)的秒数(整数或浮点数);参见 read_timeout=

response_body_encoding[R]

返回用于响应主体编码的编码;参见 response_body_encoding=

ssl_timeout[RW]

设置或返回 SSL 超时秒数。

ssl_version[RW]

设置或返回 SSL 版本。参见 OpenSSL::SSL::SSLContext#ssl_version=。

verify_callback[RW]

设置或返回服务器证书验证的回调函数。

verify_depth[RW]

设置或返回证书链验证的最大深度。

verify_hostname[RW]

设置或返回是否验证服务器证书对主机名的有效性。请参阅 OpenSSL::SSL::SSLContext#verify_hostname=。

verify_mode[RW]

设置或返回在 SSL/TLS 会话开始时进行服务器证书验证的标志。OpenSSL::SSL::VERIFY_NONE 或 OpenSSL::SSL::VERIFY_PEER 是可接受的。

write_timeout[R]

返回等待写入一个块(通过一次 write(2) 调用)的数值(整数或浮点数)秒数;请参阅 write_timeout=

公共类方法

default_port() 点击切换源代码

返回整数 80,这是用于 HTTP 请求的默认端口。

Net::HTTP.default_port # => 80
# File lib/net/http.rb, line 900
def HTTP.default_port
  http_default_port()
end
Net::HTTP.get(hostname, path, port = 80) → body 点击切换源代码
Net::HTTP:get(uri, headers = {}, port = uri.port) → body

发送 GET 请求并返回 HTTP 响应正文作为字符串。

使用字符串参数 hostnamepath

hostname = 'jsonplaceholder.typicode.com'
path = '/todos/1'
puts Net::HTTP.get(hostname, path)

输出

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

使用 URI 对象 uri 和可选的哈希参数 headers

uri = URI('https://jsonplaceholder.typicode.com/todos/1')
headers = {'Content-type' => 'application/json; charset=UTF-8'}
Net::HTTP.get(uri, headers)

相关

# File lib/net/http.rb, line 802
def HTTP.get(uri_or_host, path_or_headers = nil, port = nil)
  get_response(uri_or_host, path_or_headers, port).body
end
Net::HTTP.get_print(hostname, path, port = 80) → nil 点击切换源代码
Net::HTTP:get_print(uri, headers = {}, port = uri.port) → nil

Net::HTTP.get 相似,但将返回的正文写入 $stdout;返回 nil

# File lib/net/http.rb, line 761
def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil)
  get_response(uri_or_host, path_or_headers, port) {|res|
    res.read_body do |chunk|
      $stdout.print chunk
    end
  }
  nil
end
Net::HTTP.get_response(hostname, path, port = 80) → http_response 点击切换源代码
Net::HTTP:get_response(uri, headers = {}, port = uri.port) → http_response

Net::HTTP.get 相似,但返回一个 Net::HTTPResponse 对象,而不是正文字符串。

# File lib/net/http.rb, line 812
def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block)
  if path_or_headers && !path_or_headers.is_a?(Hash)
    host = uri_or_host
    path = path_or_headers
    new(host, port || HTTP.default_port).start {|http|
      return http.request_get(path, &block)
    }
  else
    uri = uri_or_host
    headers = path_or_headers
    start(uri.hostname, uri.port,
          :use_ssl => uri.scheme == 'https') {|http|
      return http.request_get(uri, headers, &block)
    }
  end
end
http_default_port() 点击切换源代码

返回整数 80,这是用于 HTTP 请求的默认端口。

Net::HTTP.http_default_port # => 80
# File lib/net/http.rb, line 908
def HTTP.http_default_port
  80
end
https_default_port() 点击切换源代码

返回整数 443,这是用于 HTTPS 请求的默认端口。

Net::HTTP.https_default_port # => 443
# File lib/net/http.rb, line 916
def HTTP.https_default_port
  443
end
is_version_1_2?()
别名:version_1_2?
new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil) 点击切换源代码

返回一个新的 Net::HTTP 对象 http(但不打开 TCP 连接或 HTTP 会话)。

仅给出字符串参数 address(且 ENV['http_proxy'] 未定义或为 nil),返回的 http

示例

http = Net::HTTP.new(hostname)
# => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
http.address # => "jsonplaceholder.typicode.com"
http.port    # => 80
http.proxy?  # => false

也给出整数参数 port,返回的 http 具有给定的端口

http = Net::HTTP.new(hostname, 8000)
# => #<Net::HTTP jsonplaceholder.typicode.com:8000 open=false>
http.port # => 8000

对于代理定义参数 p_addrp_no_proxy,请参阅 代理服务器

调用超类方法
# File lib/net/http.rb, line 1065
def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = 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
    if p_addr && p_no_proxy && !URI::Generic.use_proxy?(address, address, port, p_no_proxy)
      p_addr = nil
      p_port = nil
    end
    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
也别名为:newobj
newobj(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil)
别名为:new
post(url, data, header = nil) 点击切换源代码

将数据发布到主机;返回一个 Net::HTTPResponse 对象。

参数 url 必须是 URL;参数 data 必须是字符串

_uri = uri.dup
_uri.path = '/posts'
data = '{"title": "foo", "body": "bar", "userId": 1}'
headers = {'content-type': 'application/json'}
res = Net::HTTP.post(_uri, data, headers) # => #<Net::HTTPCreated 201 Created readbody=true>
puts res.body

输出

{
  "title": "foo",
  "body": "bar",
  "userId": 1,
  "id": 101
}

相关

# File lib/net/http.rb, line 855
def HTTP.post(url, data, header = nil)
  start(url.hostname, url.port,
        :use_ssl => url.scheme == 'https' ) {|http|
    http.post(url, data, header)
  }
end
post_form(url, params) 点击切换源代码

将数据发布到主机;返回一个 Net::HTTPResponse 对象。

参数 url 必须是 URI;参数 data 必须是哈希表

_uri = uri.dup
_uri.path = '/posts'
data = {title: 'foo', body: 'bar', userId: 1}
res = Net::HTTP.post_form(_uri, data) # => #<Net::HTTPCreated 201 Created readbody=true>
puts res.body

输出

{
  "title": "foo",
  "body": "bar",
  "userId": "1",
  "id": 101
}
# File lib/net/http.rb, line 882
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
proxy_class?() 点击切换源代码

如果 self 是由 HTTP::Proxy 创建的类,则返回 true。

# File lib/net/http.rb, line 1762
def proxy_class?
  defined?(@is_proxy_class) ? @is_proxy_class : false
end
start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) → http 点击切换源代码
start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) {|http| ... } → object

通过 Net::HTTP.new 创建一个新的 Net::HTTP 对象 http

  • 对于参数 addressport,请参阅 Net::HTTP.new

  • 对于代理定义参数 p_addrp_pass,请参阅 代理服务器

  • 对于参数 opts,请参阅下文。

没有给出代码块

  • 调用 `http.start` 而不带块(参见 start),这将打开一个 TCP 连接和 HTTP 会话。

  • 返回 `http`。

  • 调用者应调用 finish 来关闭会话。

    http = Net::HTTP.start(hostname)
    http.started? # => true
    http.finish
    http.started? # => false
    

如果给定块

  • 调用 `http.start` 带有块(参见 start),这将

    • 打开一个 TCP 连接和 HTTP 会话。

    • 调用块,该块可能对主机发出任意数量的请求。

    • 在块退出时关闭 HTTP 会话和 TCP 连接。

    • 返回块的值 `object`。

  • 返回 `object`。

示例

hostname = 'jsonplaceholder.typicode.com'
Net::HTTP.start(hostname) do |http|
  puts http.get('/todos/1').body
  puts http.get('/todos/2').body
end

输出

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
{
  "userId": 1,
  "id": 2,
  "title": "quis ut nam facilis et officia qui",
  "completed": false
}

如果给定的最后一个参数是哈希,则它是 `opts` 哈希,其中每个键都是要调用的方法或访问器,其值是要设置的值。

键可能包括

注意:如果 `port` 为 `nil` 且 `opts[:use_ssl]` 为真值,则传递给 `new` 的值为 Net::HTTP.https_default_port,而不是 `port`。

# File lib/net/http.rb, line 1010
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
  p_addr = :ENV if arg.size < 2
  port = https_default_port if !port && opt && opt[:use_ssl]
  http = new(address, port, p_addr, p_port, p_user, p_pass)
  http.ipaddr = opt[:ipaddr] if opt && opt[:ipaddr]

  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
version_1_2() 点击切换源代码

返回 `true`;保留以保持兼容性。

# File lib/net/http.rb, line 736
def HTTP.version_1_2
  true
end
version_1_2?() 点击切换源代码

返回 `true`;保留以保持兼容性。

# File lib/net/http.rb, line 741
def HTTP.version_1_2?
  true
end
也称为:is_version_1_2?

公共实例方法

active?()
别名:started?
continue_timeout=(sec) 点击切换源代码

设置继续超时值,即等待预期 100 Continue 响应的秒数。如果 HTTP 对象在这么多秒内未收到响应,它将发送请求主体。

# File lib/net/http.rb, line 1380
def continue_timeout=(sec)
  @socket.continue_timeout = sec if @socket
  @continue_timeout = sec
end
copy(path, initheader = nil) 点击切换源代码

向服务器发送 COPY 请求;返回一个 Net::HTTPResponse 子类的实例。

该请求基于从字符串 path 和初始头信息哈希 initheader 创建的 Net::HTTP::Copy 对象。

http = Net::HTTP.new(hostname)
http.copy('/todos/1')
# File lib/net/http.rb, line 2123
def copy(path, initheader = nil)
  request(Copy.new(path, initheader))
end
delete(path, initheader = {'Depth' => 'Infinity'}) 点击切换源代码

向服务器发送 DELETE 请求;返回一个 Net::HTTPResponse 子类的实例。

该请求基于从字符串 path 和初始头信息哈希 initheader 创建的 Net::HTTP::Delete 对象。

http = Net::HTTP.new(hostname)
http.delete('/todos/1')
# File lib/net/http.rb, line 2097
def delete(path, initheader = {'Depth' => 'Infinity'})
  request(Delete.new(path, initheader))
end
finish() 点击切换源代码

结束 HTTP 会话

http = Net::HTTP.new(hostname)
http.start
http.started? # => true
http.finish   # => nil
http.started? # => false

如果不在会话中,则引发 IOError

# File lib/net/http.rb, line 1708
def finish
  raise IOError, 'HTTP session not yet started' unless started?
  do_finish
end
get(path, initheader = nil) {|res| ... } 点击切换源代码

向服务器发送 GET 请求;返回一个 Net::HTTPResponse 子类的实例。

该请求基于从字符串 path 和初始头信息哈希 initheader 创建的 Net::HTTP::Get 对象。

如果给定代码块,则使用响应主体调用代码块

http = Net::HTTP.new(hostname)
http.get('/todos/1') do |res|
  p res
end # => #<Net::HTTPOK 200 OK readbody=true>

输出

"{\n  \"userId\": 1,\n  \"id\": 1,\n  \"title\": \"delectus aut autem\",\n  \"completed\": false\n}"

如果没有给定代码块,则只返回响应对象

http.get('/') # => #<Net::HTTPOK 200 OK readbody=true>

相关

# File lib/net/http.rb, line 1914
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
get2(path, initheader = nil)
别名:request_get
head(path, initheader = nil) 点击切换源代码

向服务器发送 HEAD 请求;返回一个 Net::HTTPResponse 子类的实例。

该请求基于从字符串 path 和初始头信息哈希 initheader 创建的 Net::HTTP::Head 对象

res = http.head('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
res.body                    # => nil
res.to_hash.take(3)
# =>
[["date", ["Wed, 15 Feb 2023 15:25:42 GMT"]],
 ["content-type", ["application/json; charset=utf-8"]],
 ["connection", ["close"]]]
# File lib/net/http.rb, line 1938
def head(path, initheader = nil)
  request(Head.new(path, initheader))
end
head2(path, initheader = nil, &block)
别名:request_head
inspect() 点击切换源代码

返回 self 的字符串表示形式

Net::HTTP.new(hostname).inspect
# => "#<Net::HTTP jsonplaceholder.typicode.com:80 open=false>"
# File lib/net/http.rb, line 1135
def inspect
  "#<#{self.class} #{@address}:#{@port} open=#{started?}>"
end
ipaddr() 点击切换源代码

返回连接的 IP 地址。

如果会话尚未开始,则返回由 ipaddr= 设置的值,如果未设置,则返回 nil

http = Net::HTTP.new(hostname)
http.ipaddr # => nil
http.ipaddr = '172.67.155.76'
http.ipaddr # => "172.67.155.76"

如果会话已开始,则返回套接字的 IP 地址。

http = Net::HTTP.new(hostname)
http.start
http.ipaddr # => "172.67.155.76"
http.finish
# File lib/net/http.rb, line 1274
def ipaddr
  started? ?  @socket.io.peeraddr[3] : @ipaddr
end
ipaddr=(addr) 点击切换源代码

设置连接的 IP 地址。

http = Net::HTTP.new(hostname)
http.ipaddr # => nil
http.ipaddr = '172.67.155.76'
http.ipaddr # => "172.67.155.76"

如果会话已开始,则可能无法设置 IP 地址。

# File lib/net/http.rb, line 1286
def ipaddr=(addr)
  raise IOError, "ipaddr value changed, but session already started" if started?
  @ipaddr = addr
end
lock(path, body, initheader = nil) 点击切换源代码

向服务器发送 LOCK 请求;返回 Net::HTTPResponse 子类的实例。

该请求基于从字符串 path、字符串 body 和初始标头哈希 initheader 创建的 Net::HTTP::Lock 对象。

data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
http = Net::HTTP.new(hostname)
http.lock('/todos/1', data)
# File lib/net/http.rb, line 2043
def lock(path, body, initheader = nil)
  request(Lock.new(path, initheader), body)
end
max_retries=(retries) 点击切换源代码

设置在发生 Net::ReadTimeout、IOErrorEOFError、Errno::ECONNRESET、Errno::ECONNABORTED、Errno::EPIPE、OpenSSL::SSL::SSLErrorTimeout::Error 时重试幂等请求的最大次数。初始值为 1。

参数 retries 必须是非负数值。

http = Net::HTTP.new(hostname)
http.max_retries = 2   # => 2
http.max_retries       # => 2
# File lib/net/http.rb, line 1320
def max_retries=(retries)
  retries = retries.to_int
  if retries < 0
    raise ArgumentError, 'max_retries should be non-negative integer number'
  end
  @max_retries = retries
end
mkcol(path, body = nil, initheader = nil) 点击切换源代码

向服务器发送 MKCOL 请求;返回 Net::HTTPResponse 子类的实例。

该请求基于从字符串 path、字符串 body 和初始标头哈希 initheader 创建的 Net::HTTP::Mkcol 对象。

data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
http.mkcol('/todos/1', data)
http = Net::HTTP.new(hostname)
# File lib/net/http.rb, line 2137
def mkcol(path, body = nil, initheader = nil)
  request(Mkcol.new(path, initheader), body)
end
move(path, initheader = nil) 点击切换源代码

向服务器发送 MOVE 请求;返回 Net::HTTPResponse 子类的实例。

该请求基于从字符串 path 和初始标头哈希 initheader 创建的 Net::HTTP::Move 对象。

http = Net::HTTP.new(hostname)
http.move('/todos/1')
# File lib/net/http.rb, line 2110
def move(path, initheader = nil)
  request(Move.new(path, initheader))
end
options(path, initheader = nil) 点击切换源代码

向服务器发送 Options 请求;返回 Net::HTTPResponse 子类的实例。

请求基于从字符串path和初始头信息哈希initheader创建的Net::HTTP::Options对象。

http = Net::HTTP.new(hostname)
http.options('/')
# File lib/net/http.rb, line 2070
def options(path, initheader = nil)
  request(Options.new(path, initheader))
end
patch(path, data, initheader = nil) {|res| ... } 点击切换源代码

向服务器发送PATCH请求;返回Net::HTTPResponse子类的实例。

请求基于从字符串path、字符串data和初始头信息哈希initheader创建的Net::HTTP::Patch对象。

如果给定代码块,则使用响应主体调用代码块

data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
http = Net::HTTP.new(hostname)
http.patch('/todos/1', data) do |res|
  p res
end # => #<Net::HTTPOK 200 OK readbody=true>

输出

"{\n  \"userId\": 1,\n  \"id\": 1,\n  \"title\": \"delectus aut autem\",\n  \"completed\": false,\n  \"{\\\"userId\\\": 1, \\\"id\\\": 1, \\\"title\\\": \\\"delectus aut autem\\\", \\\"completed\\\": false}\": \"\"\n}"

如果没有给定代码块,则只返回响应对象

http.patch('/todos/1', data) # => #<Net::HTTPCreated 201 Created readbody=true>
# File lib/net/http.rb, line 2001
def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
  send_entity(path, data, initheader, dest, Patch, &block)
end
peer_cert() 点击切换源代码

返回会话套接字对端的X509证书链(字符串数组),如果不存在则返回nil

# File lib/net/http.rb, line 1537
def peer_cert
  if not use_ssl? or not @socket
    return nil
  end
  @socket.io.peer_cert
end
post(path, data, initheader = nil) {|res| ... } 点击切换源代码

向服务器发送POST请求;返回Net::HTTPResponse子类的实例。

请求基于从字符串path、字符串data和初始头信息哈希initheader创建的Net::HTTP::Post对象。

如果给定代码块,则使用响应主体调用代码块

data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
http = Net::HTTP.new(hostname)
http.post('/todos', data) do |res|
  p res
end # => #<Net::HTTPCreated 201 Created readbody=true>

输出

"{\n  \"{\\\"userId\\\": 1, \\\"id\\\": 1, \\\"title\\\": \\\"delectus aut autem\\\", \\\"completed\\\": false}\": \"\",\n  \"id\": 201\n}"

如果没有给定代码块,则只返回响应对象

http.post('/todos', data) # => #<Net::HTTPCreated 201 Created readbody=true>

相关

# File lib/net/http.rb, line 1972
def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
  send_entity(path, data, initheader, dest, Post, &block)
end
post2(path, data, initheader = nil)
别名:request_post
propfind(path, body = nil, initheader = {'Depth' => '0'}) 点击切换源代码

向服务器发送PROPFIND请求;返回Net::HTTPResponse子类的实例。

请求基于从字符串path、字符串body和初始头信息哈希initheader创建的Net::HTTP::Propfind对象。

data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
http = Net::HTTP.new(hostname)
http.propfind('/todos/1', data)
# File lib/net/http.rb, line 2084
def propfind(path, body = nil, initheader = {'Depth' => '0'})
  request(Propfind.new(path, initheader), body)
end
proppatch(path, body, initheader = nil) 点击切换源代码

向服务器发送PROPPATCH请求;返回Net::HTTPResponse子类的实例。

该请求基于从字符串 `path`、字符串 `body` 和初始标头哈希 `initheader` 创建的 Net::HTTP::Proppatch 对象。

data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
http = Net::HTTP.new(hostname)
http.proppatch('/todos/1', data)
# File lib/net/http.rb, line 2029
def proppatch(path, body, initheader = nil)
  request(Proppatch.new(path, initheader), body)
end
proxy?() 点击切换源代码

如果定义了代理服务器,则返回 `true`,否则返回 `false`;请参阅 代理服务器

# File lib/net/http.rb, line 1785
def proxy?
  !!(@proxy_from_env ? proxy_uri : @proxy_address)
end
proxy_address() 点击切换源代码

如果定义了代理服务器,则返回代理服务器的地址,否则返回 `nil`;请参阅 代理服务器

# File lib/net/http.rb, line 1807
def proxy_address
  if @proxy_from_env then
    proxy_uri&.hostname
  else
    @proxy_address
  end
end
别名:proxyaddr
proxy_from_env?() 点击切换源代码

如果在环境中定义了代理服务器,则返回 `true`,否则返回 `false`;请参阅 代理服务器

# File lib/net/http.rb, line 1792
def proxy_from_env?
  @proxy_from_env
end
proxy_pass() 点击切换源代码

如果定义了代理服务器,则返回代理服务器的密码,否则返回 `nil`;请参阅 代理服务器

# File lib/net/http.rb, line 1838
def proxy_pass
  if @proxy_from_env
    pass = proxy_uri&.password
    unescape(pass) if pass
  else
    @proxy_pass
  end
end
proxy_port() 点击切换源代码

如果定义了代理服务器,则返回代理服务器的端口号,否则返回 `nil`;请参阅 代理服务器

# File lib/net/http.rb, line 1817
def proxy_port
  if @proxy_from_env then
    proxy_uri&.port
  else
    @proxy_port
  end
end
别名:proxyport
proxy_user() 点击切换源代码

如果定义了代理服务器,则返回代理服务器的用户名,否则返回 `nil`;请参阅 代理服务器

# File lib/net/http.rb, line 1827
def proxy_user
  if @proxy_from_env
    user = proxy_uri&.user
    unescape(user) if user
  else
    @proxy_user
  end
end
proxyaddr()
别名:proxy_address
proxyport()
别名:proxy_port
put(path, data, initheader = nil) 点击切换源代码

向服务器发送 PUT 请求;返回 Net::HTTPResponse 的子类的实例。

该请求基于从字符串 `path`、字符串 `data` 和初始标头哈希 `initheader` 创建的 Net::HTTP::Put 对象。

data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
http = Net::HTTP.new(hostname)
http.put('/todos/1', data) # => #<Net::HTTPOK 200 OK readbody=true>
# File lib/net/http.rb, line 2015
def put(path, data, initheader = nil)
  request(Put.new(path, initheader), data)
end
read_timeout=(sec) 点击切换源代码

设置 self 的读取超时时间(以秒为单位)为整数 sec;初始值为 60。

参数 sec 必须是非负数值。

http = Net::HTTP.new(hostname)
http.read_timeout # => 60
http.get('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
http.read_timeout = 0
http.get('/todos/1') # Raises Net::ReadTimeout.
# File lib/net/http.rb, line 1343
def read_timeout=(sec)
  @socket.read_timeout = sec if @socket
  @read_timeout = sec
end
request(req, body = nil) { |response| ... } 点击切换源代码

将给定的请求 req 发送到服务器;将响应格式化为 Net::HTTPResponse 对象。

给定的 req 必须是 Net::HTTPRequest 子类 的实例。仅在请求需要时才应提供参数 body

如果没有给出代码块,则返回响应对象。

http = Net::HTTP.new(hostname)

req = Net::HTTP::Get.new('/todos/1')
http.request(req)
# => #<Net::HTTPOK 200 OK readbody=true>

req = Net::HTTP::Post.new('/todos')
http.request(req, 'xyzzy')
# => #<Net::HTTPCreated 201 Created readbody=true>

如果给出代码块,则使用响应调用代码块并返回响应。

req = Net::HTTP::Get.new('/todos/1')
http.request(req) do |res|
  p res
end # => #<Net::HTTPOK 200 OK readbody=true>

输出

#<Net::HTTPOK 200 OK readbody=false>
# File lib/net/http.rb, line 2295
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
request_get(path, initheader = nil) { |response| ... } 点击切换源代码

向服务器发送 GET 请求;将响应格式化为 Net::HTTPResponse 对象。

该请求基于从字符串 path 和初始头信息哈希 initheader 创建的 Net::HTTP::Get 对象。

如果没有给出代码块,则返回响应对象。

http = Net::HTTP.new(hostname)
http.request_get('/todos') # => #<Net::HTTPOK 200 OK readbody=true>

如果给出代码块,则使用响应对象调用代码块并返回响应对象。

http.request_get('/todos') do |res|
  p res
end # => #<Net::HTTPOK 200 OK readbody=true>

输出

#<Net::HTTPOK 200 OK readbody=false>
# File lib/net/http.rb, line 2176
def request_get(path, initheader = nil, &block) # :yield: +response+
  request(Get.new(path, initheader), &block)
end
也称为:get2
request_head(path, initheader = nil, &block) 点击切换源代码

向服务器发送 HEAD 请求;返回一个 Net::HTTPResponse 子类的实例。

请求基于从字符串 path 和初始标头哈希 initheader 创建的 Net::HTTP::Head 对象。

http = Net::HTTP.new(hostname)
http.head('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
# File lib/net/http.rb, line 2189
def request_head(path, initheader = nil, &block)
  request(Head.new(path, initheader), &block)
end
也称为:head2
request_post(path, data, initheader = nil) { |response| ... } 点击切换源代码

向服务器发送 POST 请求;将响应格式化为 Net::HTTPResponse 对象。

请求基于从字符串path、字符串data和初始头信息哈希initheader创建的Net::HTTP::Post对象。

如果没有给出代码块,则返回响应对象。

http = Net::HTTP.new(hostname)
http.post('/todos', 'xyzzy')
# => #<Net::HTTPCreated 201 Created readbody=true>

如果给出代码块,则使用响应主体调用代码块并返回响应对象。

http.post('/todos', 'xyzzy') do |res|
  p res
end # => #<Net::HTTPCreated 201 Created readbody=true>

输出

"{\n  \"xyzzy\": \"\",\n  \"id\": 201\n}"
# File lib/net/http.rb, line 2216
def request_post(path, data, initheader = nil, &block) # :yield: +response+
  request Post.new(path, initheader), data, &block
end
也称为:post2
response_body_encoding=(value) 点击切换源代码

设置用于响应主体的编码;返回编码。

给定的 value 可以是

  • Encoding 对象。

  • 编码的名称。

  • 编码名称的别名。

参见 Encoding

示例

http = Net::HTTP.new(hostname)
http.response_body_encoding = Encoding::US_ASCII # => #<Encoding:US-ASCII>
http.response_body_encoding = 'US-ASCII'         # => "US-ASCII"
http.response_body_encoding = 'ASCII'            # => "ASCII"
# File lib/net/http.rb, line 1229
def response_body_encoding=(value)
  value = Encoding.find(value) if value.is_a?(String)
  @response_body_encoding = value
end
send_request(name, path, data = nil, header = nil) 点击切换源代码

向服务器发送 HTTP 请求;返回 Net::HTTPResponse 子类的实例。

该请求基于从字符串 path、字符串 data 和初始标头哈希 header 创建的 Net::HTTPRequest 对象。该对象是 Net::HTTPRequest 子类 的实例,对应于给定的大写字符串 name,该字符串必须是 HTTP 请求方法WebDAV 请求方法

示例

http = Net::HTTP.new(hostname)
http.send_request('GET', '/todos/1')
# => #<Net::HTTPOK 200 OK readbody=true>
http.send_request('POST', '/todos', 'xyzzy')
# => #<Net::HTTPCreated 201 Created readbody=true>
# File lib/net/http.rb, line 2259
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
set_debug_output(output) 点击切换源代码

警告 此方法会打开一个严重的安全性漏洞。切勿在生产代码中使用此方法。

设置调试的输出流

http = Net::HTTP.new(hostname)
File.open('t.tmp', 'w') do |file|
  http.set_debug_output(file)
  http.start
  http.get('/nosuch/1')
  http.finish
end
puts File.read('t.tmp')

输出

opening connection to jsonplaceholder.typicode.com:80...
opened
<- "GET /nosuch/1 HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nHost: jsonplaceholder.typicode.com\r\n\r\n"
-> "HTTP/1.1 404 Not Found\r\n"
-> "Date: Mon, 12 Dec 2022 21:14:11 GMT\r\n"
-> "Content-Type: application/json; charset=utf-8\r\n"
-> "Content-Length: 2\r\n"
-> "Connection: keep-alive\r\n"
-> "X-Powered-By: Express\r\n"
-> "X-Ratelimit-Limit: 1000\r\n"
-> "X-Ratelimit-Remaining: 999\r\n"
-> "X-Ratelimit-Reset: 1670879660\r\n"
-> "Vary: Origin, Accept-Encoding\r\n"
-> "Access-Control-Allow-Credentials: true\r\n"
-> "Cache-Control: max-age=43200\r\n"
-> "Pragma: no-cache\r\n"
-> "Expires: -1\r\n"
-> "X-Content-Type-Options: nosniff\r\n"
-> "Etag: W/\"2-vyGp6PvFo4RvsFtPoIWeCReyIC8\"\r\n"
-> "Via: 1.1 vegur\r\n"
-> "CF-Cache-Status: MISS\r\n"
-> "Server-Timing: cf-q-config;dur=1.3000000762986e-05\r\n"
-> "Report-To: {\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=yOr40jo%2BwS1KHzhTlVpl54beJ5Wx2FcG4gGV0XVrh3X9OlR5q4drUn2dkt5DGO4GDcE%2BVXT7CNgJvGs%2BZleIyMu8CLieFiDIvOviOY3EhHg94m0ZNZgrEdpKD0S85S507l1vsEwEHkoTm%2Ff19SiO\"}],\"group\":\"cf-nel\",\"max_age\":604800}\r\n"
-> "NEL: {\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}\r\n"
-> "Server: cloudflare\r\n"
-> "CF-RAY: 778977dc484ce591-DFW\r\n"
-> "alt-svc: h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400\r\n"
-> "\r\n"
reading 2 bytes...
-> "{}"
read 2 bytes
Conn keep-alive
# File lib/net/http.rb, line 1188
def set_debug_output(output)
  warn 'Net::HTTP#set_debug_output called after HTTP started', uplevel: 1 if started?
  @debug_output = output
end
start() { |http| ... } 点击切换源代码

启动 HTTP 会话。

没有块,返回 self

http = Net::HTTP.new(hostname)
# => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
http.start
# => #<Net::HTTP jsonplaceholder.typicode.com:80 open=true>
http.started? # => true
http.finish

有块,用 self 调用块,在块退出时结束会话,并返回块的值

http.start do |http|
  http
end
# => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
http.started? # => false
# File lib/net/http.rb, line 1565
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
started?() 点击切换源代码

如果 HTTP 会话已启动,则返回 true

http = Net::HTTP.new(hostname)
http.started? # => false
http.start
http.started? # => true
http.finish # => nil
http.started? # => false

Net::HTTP.start(hostname) do |http|
  http.started?
end # => true
http.started? # => false
# File lib/net/http.rb, line 1413
def started?
  @started
end
也称为:active?
trace(path, initheader = nil) 点击切换源代码

向服务器发送 TRACE 请求;返回 Net::HTTPResponse 子类的实例。

该请求基于从字符串 path 和初始标头哈希 initheader 创建的 Net::HTTP::Trace 对象。

http = Net::HTTP.new(hostname)
http.trace('/todos/1')
# File lib/net/http.rb, line 2150
def trace(path, initheader = nil)
  request(Trace.new(path, initheader))
end
unlock(path, body, initheader = nil) 点击切换源代码

向服务器发送 UNLOCK 请求;返回 Net::HTTPResponse 子类的实例。

该请求基于从字符串path、字符串body和初始头哈希initheader创建的Net::HTTP::Unlock对象。

data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
http = Net::HTTP.new(hostname)
http.unlock('/todos/1', data)
# File lib/net/http.rb, line 2057
def unlock(path, body, initheader = nil)
  request(Unlock.new(path, initheader), body)
end
use_ssl=(flag) 点击切换源代码

设置新的会话是否使用传输层安全

如果尝试在会话期间更改,则会引发IOError

如果端口不是 HTTPS 端口,则会引发OpenSSL::SSL::SSLError

# File lib/net/http.rb, line 1435
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
use_ssl?() 点击切换源代码

如果self使用 SSL,则返回true,否则返回false。请参阅Net::HTTP#use_ssl=

# File lib/net/http.rb, line 1425
def use_ssl?
  @use_ssl
end
write_timeout=(sec) 点击切换源代码

self的写入超时时间(以秒为单位)设置为整数sec;初始值为 60。

参数 sec 必须是非负数值。

_uri = uri.dup
_uri.path = '/posts'
body = 'bar' * 200000
data = <<EOF
{"title": "foo", "body": "#{body}", "userId": "1"}
EOF
headers = {'content-type': 'application/json'}
http = Net::HTTP.new(hostname)
http.write_timeout # => 60
http.post(_uri.path, data, headers)
# => #<Net::HTTPCreated 201 Created readbody=true>
http.write_timeout = 0
http.post(_uri.path, data, headers) # Raises Net::WriteTimeout.
# File lib/net/http.rb, line 1367
def write_timeout=(sec)
  @socket.write_timeout = sec if @socket
  @write_timeout = sec
end

私有实例方法

D(msg)
别名:debug
addr_port() 点击切换源代码

实用程序

# File lib/net/http.rb, line 2464
def addr_port
  addr = address
  addr = "[#{addr}]" if addr.include?(":")
  default_port = use_ssl? ? HTTP.https_default_port : HTTP.http_default_port
  default_port == port ? addr : "#{addr}:#{port}"
end
begin_transport(req) 点击切换源代码
# File lib/net/http.rb, line 2381
def begin_transport(req)
  if @socket.closed?
    connect
  elsif @last_communicated
    if @last_communicated + @keep_alive_timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC)
      debug 'Conn close because of keep_alive_timeout'
      @socket.close
      connect
    elsif @socket.io.to_io.wait_readable(0) && @socket.eof?
      debug "Conn close because of EOF"
      @socket.close
      connect
    end
  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
connect() 点击切换源代码
# File lib/net/http.rb, line 1585
def connect
  if use_ssl?
    # reference early to load OpenSSL before connecting,
    # as OpenSSL may take time to load.
    @ssl_context = OpenSSL::SSL::SSLContext.new
  end

  if proxy? then
    conn_addr = proxy_address
    conn_port = proxy_port
  else
    conn_addr = conn_address
    conn_port = port
  end

  debug "opening connection to #{conn_addr}:#{conn_port}..."
  s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
    begin
      TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
    rescue => e
      raise e, "Failed to open TCP connection to " +
        "#{conn_addr}:#{conn_port} (#{e.message})"
    end
  }
  s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
  debug "opened"
  if use_ssl?
    if proxy?
      plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
                                  write_timeout: @write_timeout,
                                  continue_timeout: @continue_timeout,
                                  debug_output: @debug_output)
      buf = +"CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n" \
        "Host: #{@address}:#{@port}\r\n"
      if proxy_user
        credential = ["#{proxy_user}:#{proxy_pass}"].pack('m0')
        buf << "Proxy-Authorization: Basic #{credential}\r\n"
      end
      buf << "\r\n"
      plain_sock.write(buf)
      HTTPResponse.read_new(plain_sock).value
      # assuming nothing left in buffers after successful CONNECT response
    end

    ssl_parameters = Hash.new
    iv_list = instance_variables
    SSL_IVNAMES.each_with_index do |ivname, i|
      if iv_list.include?(ivname)
        value = instance_variable_get(ivname)
        unless value.nil?
          ssl_parameters[SSL_ATTRIBUTES[i]] = value
        end
      end
    end
    @ssl_context.set_params(ssl_parameters)
    unless @ssl_context.session_cache_mode.nil? # a dummy method on JRuby
      @ssl_context.session_cache_mode =
          OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
              OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
    end
    if @ssl_context.respond_to?(:session_new_cb) # not implemented under JRuby
      @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
    end

    # Still do the post_connection_check below even if connecting
    # to IP address
    verify_hostname = @ssl_context.verify_hostname

    # Server Name Indication (SNI) RFC 3546/6066
    case @address
    when Resolv::IPv4::Regex, Resolv::IPv6::Regex
      # don't set SNI, as IP addresses in SNI is not valid
      # per RFC 6066, section 3.

      # Avoid openssl warning
      @ssl_context.verify_hostname = false
    else
      ssl_host_address = @address
    end

    debug "starting SSL for #{conn_addr}:#{conn_port}..."
    s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
    s.sync_close = true
    s.hostname = ssl_host_address if s.respond_to?(:hostname=) && ssl_host_address

    if @ssl_session and
       Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
      s.session = @ssl_session
    end
    ssl_socket_connect(s, @open_timeout)
    if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && verify_hostname
      s.post_connection_check(@address)
    end
    debug "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}"
  end
  @socket = BufferedIO.new(s, read_timeout: @read_timeout,
                           write_timeout: @write_timeout,
                           continue_timeout: @continue_timeout,
                           debug_output: @debug_output)
  @last_communicated = nil
  on_connect
rescue => exception
  if s
    debug "Conn close because of connect error #{exception}"
    s.close
  end
  raise
end
debug(msg) 点击切换源代码

向调试输出添加消息

# File lib/net/http.rb, line 2472
def debug(msg)
  return unless @debug_output
  @debug_output << msg
  @debug_output << "\n"
end
也称为:D
do_finish() 点击切换源代码
# File lib/net/http.rb, line 1713
def do_finish
  @started = false
  @socket.close if @socket
  @socket = nil
end
do_start() 点击切换源代码
# File lib/net/http.rb, line 1579
def do_start
  connect
  @started = true
end
edit_path(path) 点击切换源代码
# File lib/net/http.rb, line 1867
def edit_path(path)
  if proxy?
    if path.start_with?("ftp://") || use_ssl?
      path
    else
      "http://#{addr_port}#{path}"
    end
  else
    path
  end
end
end_transport(req, res) 点击切换源代码
# File lib/net/http.rb, line 2404
def end_transport(req, res)
  @curr_http_version = res.http_version
  @last_communicated = nil
  if @socket.closed?
    debug 'Conn socket closed'
  elsif not res.body and @close_on_empty_response
    debug 'Conn close'
    @socket.close
  elsif keep_alive?(req, res)
    debug 'Conn keep-alive'
    @last_communicated = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  else
    debug 'Conn close'
    @socket.close
  end
end
keep_alive?(req, res) 点击切换源代码
# File lib/net/http.rb, line 2421
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
on_connect() 点击切换源代码
# File lib/net/http.rb, line 1695
def on_connect
end
send_entity(path, data, initheader, dest, type, &block) 点击切换源代码

执行使用表示形式的请求并返回其主体。

# File lib/net/http.rb, line 2318
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
sspi_auth(req) 点击切换源代码
# File lib/net/http.rb, line 2445
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
sspi_auth?(res) 点击切换源代码
# File lib/net/http.rb, line 2430
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
transport_request(req) { |res| ... } 点击切换源代码
# File lib/net/http.rb, line 2329
def transport_request(req)
  count = 0
  begin
    begin_transport req
    res = catch(:response) {
      begin
        req.exec @socket, @curr_http_version, edit_path(req.path)
      rescue Errno::EPIPE
        # Failure when writing full request, but we can probably
        # still read the received response.
      end

      begin
        res = HTTPResponse.read_new(@socket)
        res.decode_content = req.decode_content
        res.body_encoding = @response_body_encoding
        res.ignore_eof = @ignore_eof
      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, Errno::ETIMEDOUT,
         # avoid a dependency on OpenSSL
         defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError,
         Timeout::Error => exception
    if count < max_retries && IDEMPOTENT_METHODS_.include?(req.method)
      count += 1
      @socket.close if @socket
      debug "Conn close because of error #{exception}, and retry"
      retry
    end
    debug "Conn close because of error #{exception}"
    @socket.close if @socket
    raise
  end

  end_transport req, res
  res
rescue => exception
  debug "Conn close because of error #{exception}"
  @socket.close if @socket
  raise exception
end
unescape(value) 点击切换源代码
# File lib/net/http.rb, line 1852
def unescape(value)
  require 'cgi/util'
  CGI.unescape(value)
end