使用 Ruby 发送自定义 HTTP 标头

Posted

技术标签:

【中文标题】使用 Ruby 发送自定义 HTTP 标头【英文标题】:Sending custom HTTP headers with Ruby 【发布时间】:2018-12-22 17:11:06 【问题描述】:

我想随 HTTP 请求一起发送自定义标头。 我根据ruby-doc, Net::HTTP, Setting Headers 的示例创建了以下内容,但我的版本因“对等方重置连接”而失败:

#!/usr/bin/env ruby -w
require 'fileutils'
require 'net/http'
require 'time'

cached_response = 'index.html'                                       # Added
FileUtils.touch(cached_response) unless File.exist?(cached_response) # Added
uri = URI("https://www.apple.com/#cached_response")                # Changed
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)

但是,不带自定义标头的发送工作正常:

#!/usr/bin/env ruby -w
require 'fileutils'
require 'net/http'
require 'time'

cached_response = 'index.html'
uri = URI("https://www.apple.com/#cached_response")
FileUtils.touch(cached_response) unless File.exist?(cached_response)
file = File.stat cached_response

req = Net::HTTP::Get.new(uri)                                        # Ignored
req['If-Modified-Since'] = file.mtime.rfc2822                        # Ignored

res = Net::HTTP.get_response(uri)                                    # Changed

open cached_response, 'w' do |io|
  io.write res.body
end if res.is_a?(Net::HTTPSuccess)

谁能解释为什么我的示例 Ruby 代码的实现不起作用?

我在几个 Sinatra 路由中添加了类似的代码:

get '/test1' do
  uri = URI('https://www.apple.com/index.html')
  req = Net::HTTP::Get.new(uri)
  req['Accept'] = request.env['HTTP_ACCEPT']
  res = Net::HTTP.start(uri.hostname, uri.port)  |h| h.request(req) 
  headers = res.to_hash
  headers.delete('transfer-encoding')
  [res.code.to_i, headers, res.body]
end

然后

get '/test2' do
  uri = URI('https://www.apple.com/index.html')
  res = Net::HTTP.get_response(uri)
  headers = res.to_hash
  headers.delete('transfer-encoding')
  [res.code.to_i, headers, res.body]
end

使用 /test1,我得到了

curl -iv 'http://localhost:9292/test1'
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 9292 (#0)
> GET /test1 HTTP/1.1
> Host: localhost:9292
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 500 Internal Server Error 
HTTP/1.1 500 Internal Server Error 
< Content-Type: text/plain
Content-Type: text/plain
< Content-Length: 5582
Content-Length: 5582
< Server: WEBrick/1.4.2 (Ruby/2.5.0/2018-12-06)
Server: WEBrick/1.4.2 (Ruby/2.5.0/2018-12-06)
< Date: Sat, 22 Dec 2018 16:51:03 GMT
Date: Sat, 22 Dec 2018 16:51:03 GMT
< Connection: Keep-Alive
Connection: Keep-Alive

< 
EOFError: end of file reached
    ...
* Connection #0 to host localhost left intact

使用 /test2,我得到了

curl -iv 'http://localhost:9292/test2'
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 9292 (#0)
> GET /test2 HTTP/1.1
> Host: localhost:9292
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK 
HTTP/1.1 200 OK 
...
<etc. HTML written to console>

看起来请求标头是相同的。同样的四行。

如果我将 Accept 标头添加到 curl 命令,请求 Accept 标头会按预期更改,并且都返回与以前相同的结果 (/test1: 500; /test2: 200)

curl -iv -H 'Accept: application/json' 'http://localhost:9292/test2'
...
> GET /test2 HTTP/1.1
> Host: localhost:9292
> User-Agent: curl/7.54.0
> Accept: application/json

【问题讨论】:

嗯...结果最后一点是红鲱鱼。没有人对 curl 正确设置 Accept 标头感到惊讶。在Net::HTTP.start 块中查看来自.request(req) 命令的(不正确的?)请求标头并将它们与来自Net::HTTP.get_response(uri) 的(正确的)请求标头进行比较会很有帮助。 【参考方案1】:

终于想通了。设置 HTTP 请求时,使用“https”方案不会自动启用 TLS/SSL。您必须在请求开始之前明确执行此操作。这是我的更新版本:

#!/usr/bin/env ruby -w
# frozen_string_literal: true

require 'fileutils'
require 'net/http'
require 'time'

cached_response = 'index.html'                                     # Added
FileUtils.touch cached_response unless File.exist? cached_response # Added
uri = URI("https://www.apple.com/#cached_response")              # Changed
file = File.stat cached_response

req = Net::HTTP::Get.new(uri)
req['If-Modified-Since'] = file.mtime.rfc2822

http = Net::HTTP.new(uri.hostname, uri.port)                       # Added
http.use_ssl = uri.scheme == 'https'                               # Added
res = http.start  |h| h.request(req)                             # Changed

if res.is_a?(Net::HTTPSuccess)
  File.open cached_response, 'w' do |io|
    io.write res.body
  end
end

【讨论】:

以上是关于使用 Ruby 发送自定义 HTTP 标头的主要内容,如果未能解决你的问题,请参考以下文章

自定义 Authorization HTTP 标头

与 qtwebkit 请求一起发送自定义标头

通过 RSpec 发送自定义标头

发送带有自定义 HTTP 标头的跨域请求时禁用预检 OPTION 请求

使用 CURL 发送自定义标头

为BlazeDS和AMF发送的请求添加自定义HTTP标头