使用Ruby的Net:HTTP保留HTTP头中的大小写

时间:2010-04-25 23:26:55

标签: ruby http networking paypal

虽然HTTP规范说标题不区分大小写; Paypal及其新的自适应支付API要求其标题区分大小写。

对ActiveMerchant(http://github.com/lamp/paypal_adaptive_gateway)使用paypal自适应支付扩​​展程序似乎虽然标题是全部大写的,但它们是以大小写形式发送的。

以下是发送HTTP请求的代码:

headers = {
  "X-PAYPAL-REQUEST-DATA-FORMAT" => "XML",
  "X-PAYPAL-RESPONSE-DATA-FORMAT" => "JSON",
  "X-PAYPAL-SECURITY-USERID" => @config[:login],
  "X-PAYPAL-SECURITY-PASSWORD" => @config[:password],
  "X-PAYPAL-SECURITY-SIGNATURE" => @config[:signature],
  "X-PAYPAL-APPLICATION-ID" => @config[:appid]
}
build_url action

request = Net::HTTP::Post.new(@url.path)

request.body = @xml
headers.each_pair { |k,v| request[k] = v }
request.content_type = 'text/xml'

proxy = Net::HTTP::Proxy("127.0.0.1", "60723")

server = proxy.new(@url.host, 443)
server.use_ssl = true

server.start { |http| http.request(request) }.body

(我添加了代理行,所以我可以看到Charles发生了什么 - http://www.charlesproxy.com/

当我查看charles中的请求标头时,这就是我所看到的:

X-Paypal-Application-Id ...
X-Paypal-Security-Password...
X-Paypal-Security-Signature ...
X-Paypal-Security-Userid ...
X-Paypal-Request-Data-Format XML
X-Paypal-Response-Data-Format JSON
Accept */*
Content-Type text/xml
Content-Length 522
Host svcs.sandbox.paypal.com

我通过使用curl运行类似请求来验证Charles不进行大小写转换。在那次测试中,案件得以保留。

3 个答案:

答案 0 :(得分:2)

RFC确实指定标题密钥为case-insensitive,所以不幸的是,您似乎已经遇到了PayPal API的烦人需求。

Net :: HTTP正在改变这种情况,虽然我很惊讶他们并没有全都下挫:

# File net/http.rb, line 1160
    def []=(key, val)
      unless val
        @header.delete key.downcase
        return val
      end
      @header[key.downcase] = [val]
    end

“设置与不区分大小写的键对应的标题字段。”

由于以上是一个简单的类,它可以进行猴子修补。我会进一步思考一个更好的解决方案。

答案 1 :(得分:1)

使用以下代码强制区分大小写的标头。

class CaseSensitivePost < Net::HTTP::Post
  def initialize_http_header(headers)
    @header = {}
    headers.each{|k,v| @header[k.to_s] = [v] }
  end

  def [](name)
    @header[name.to_s]
  end

  def []=(name, val)
    if val
      @header[name.to_s] = [val]
    else
      @header.delete(name.to_s)
    end
  end

  def capitalize(name)
    name
  end
end

用法示例:

post = CaseSensitivePost.new(url, {myCasedHeader: '1'})
post.body = body
http = Net::HTTP.new(host, port)
http.request(post)

答案 2 :(得分:0)

我遇到了@ kaplan-ilya提出的代码的几个问题,因为Net :: HTTP库试图检测帖子的内容类型,而我最终遇到了2个内容类型,并且其他字段重复了不同的情况。

因此,下面的代码应确保选择了特定案例后,它将保持不变。

  class Post < Net::HTTP::Post
    def initialize_http_header(headers)
      @header = {}
      headers.each { |k, v| @header[k.to_s] = [v] }
    end

    def [](name)
      _k, val = header_insensitive_match name
      val
    end

    def []=(name, val)
      key, _val = header_insensitive_match name
      key = name if key.nil?
      if val
        @header[key] = [val]
      else
        @header.delete(key)
      end
    end

    def capitalize(name)
      name
    end

    def header_insensitive_match(name)
      @header.find { |key, _value| key.match Regexp.new(name.to_s, Regexp::IGNORECASE) }
    end
  end