如何使用http.get保留案例?

时间:2012-01-14 19:07:08

标签: ruby-on-rails ruby

我需要在特定字符大小写中发送HTTP标头。我知道这是针对RFC的,但我有一个要求。

http.get似乎改变了我提供的头字典的情况。我该如何保留角色?

5 个答案:

答案 0 :(得分:13)

基于Tin Man的答案,Net::HTTP库在您的自定义标题键(以及所有标题键)上调用#downcase,这里有一些其他选项不会修补整个Net::HTTP

你可以试试这个:

custom_header_key = "X-miXEd-cASe"
def custom_header_key.downcase
  self
end

要避免清除方法缓存,请将上述结果存储在类级别常量中:

custom_header_key = "X-miXEd-cASe"
def custom_header_key.downcase
  self
end
CUSTOM_HEADER_KEY = custom_header_key

或子类String以覆盖该特定行为:

class StringWithIdentityDowncase < String
  def downcase
    self
  end
end

custom_header_key = StringWithIdentityDowncase.new("X-miXEd-cASe")

答案 1 :(得分:5)

接受的答案不起作用。坦率地说,我怀疑它曾经做过,因为看起来它也必须覆盖split and capitalize,我跟着那个方法做了几次提交,至少自2004年以来就是这样。

以下是我的解决方案,回答this封闭式问题:

require 'net/http'

class Net::HTTP::ImmutableHeaderKey
  attr_reader :key

  def initialize(key)
    @key = key
  end

  def downcase
    self
  end

  def capitalize
    self
  end

  def split(*)
    [self]
  end

  def hash
    key.hash
  end

  def eql?(other)
    key.eql? other.key.eql?
  end

  def to_s
    key
  end
end

现在您需要确保始终使用此类的实例作为密钥。

request           = Net::HTTP::Get.new('/')
user_key          = Net::HTTP::ImmutableHeaderKey.new("user")
request[user_key] = "James"

require 'stringio'
StringIO.new.tap do |output|
  request.exec output, 'ver', 'path'
  puts output.string
end

# >> GET path HTTP/ver
# >> Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
# >> Accept: */*
# >> User-Agent: Ruby
# >> user: James
# >> 

答案 2 :(得分:2)

Mine是这样做的一种方法,但我建议按照@yfeldblum的建议进行操作,只需将downcase短路用于需要单独保留其案例的标题键。


在Net :: HTTP :: HTTPHeader的多个位置,使用downcase将标题折叠为小写。

我认为改变这种行为非常激烈,但这样做会有所改变。将其添加到您的源代码中,它将重新定义HTTPHeader模块中包含downcase的方法。

module HTTPHeader

  def initialize_http_header(initheader)
    @header = {}
    return unless initheader
    initheader.each do |key, value|
      warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
      @header[key] = [value.strip]
    end
  end

  def [](key)
    a = @header[key] or return nil
    a.join(', ')
  end

  def []=(key, val)
    unless val
      @header.delete key
      return val
    end
    @header[key] = [val]
  end

  def add_field(key, val)
    if @header.key?(key)
      @header[key].push val
    else
      @header[key] = [val]
    end
  end

  def get_fields(key)
    return nil unless @header[key]
    @header[key].dup
  end

  def fetch(key, *args, &block)   #:yield: +key+
    a = @header.fetch(key, *args, &block)
    a.kind_of?(Array) ? a.join(', ') : a
  end

  # Removes a header field.
  def delete(key)
    @header.delete(key)
  end

  # true if +key+ header exists.
  def key?(key)
    @header.key?(key)
  end

  def tokens(vals)
    return [] unless vals
    vals.map {|v| v.split(',') }.flatten\
    .reject {|str| str.strip.empty? }\
    .map {|tok| tok.strip }
  end

end

我认为这是一种蛮力的方式,但没有其他更优雅的东西跳到脑海。

虽然这应该可以解决使用Net :: HTTP的任何Ruby库的问题,但对于使用Curl或libcurl的任何gem来说,它可能会失败。

答案 3 :(得分:1)

Joshua Cheek的答案很棒,但它在Ruby 2.3中已经完成了工作

此修改修复了它:

class Net::HTTP::ImmutableHeaderKey
  ...

  def to_s
    caller.first.match(/capitalize/) ? self : @key
  end
end

答案 4 :(得分:0)

这一切都落入了net / generic_request#write_header。 你可以修补代码

# 'net/generic_request' line 319
def write_header(sock, ver, path)
  customheaders = {
    "My-Custom-Header" => "MY-CUSTOM-HEADER",
    "Another-Custom-Header" => "aNoThErCuStOmHeAdEr"
  }
  buf = "#{@method} #{path} HTTP/#{ver}\r\n"
  each_capitalized do |k,v|
    customheaders.key?(k) ? kk = customheaders[k] : kk = k
    buf << "#{kk}: #{v}\r\n"
  end
  buf << "\r\n"
  sock.write buf
end

并且您不需要重写整个net / http / header,net / generic_request和net / http链。 它不是最好的解决方案,但它是我猜的最简单的解决方案,而且猴子补丁的数量最少。

希望它有所帮助。