6

I'm doing an http request in ruby:

  http = Net::HTTP.new(uri.host, uri.port)
  req = Net::HTTP::Post.new(uri.path)
  req.body = payload
  req['customeheader'] = 'xxxxxxxxx'
  http.set_debug_output $stdout

I have debug switched on and when the request is posted I can see the header is being posted as:

  Customheader: xxxxxxxxx

Is there anyway to stop this, the third party server I'm posting to is giving an error because the header name isn't correct - it's expecting customheader:

probably at the beach
  • 14,489
  • 16
  • 75
  • 116

4 Answers4

14

While it's possible to monkey patch Net::HTTPHeader and Net:HTTPGenericRequest to remove the downcase and capitalize, I found a different approach that allows selective case sensitivity rather than forcing everything.

The solution:

class CaseSensitiveString < String
    def downcase
        self
    end
    def capitalize
        self
    end
end

Then CaseSensitiveString.new('keyname') to create your keys when they are case sensitive. Use strings/symbols for other keys to maintain existing behaviour. This is much simpler than the monkey patching and works well with the rest-client library as well as Net::HTTP.

Corin
  • 2,317
  • 2
  • 32
  • 47
6

According to the HTTP spec (RFC 2616), header field names are case-insensitive. So the third-party server has a broken implementation.

If you really needed to, you could monkey-patch Net::HTTP to preserve case, because it downcases the field names when it stores them and then writes them with initial caps.

Here's the storage method you used (Net::HTTPHeader#[]=):

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

And here is where it writes the header (Net::HTTPGenericRequest#write_header):

# File lib/net/http.rb, line 2071
    def write_header(sock, ver, path)
      buf = "#{@method} #{path} HTTP/#{ver}\r\n"
      each_capitalized do |k,v|
        buf << "#{k}: #{v}\r\n"
      end
      buf << "\r\n"
      sock.write buf
    end

Those are likely the only methods you'd need to override, but I'm not 100% certain.

Mark Thomas
  • 37,131
  • 11
  • 74
  • 101
5

Building off of a previous answer, this will work for ruby 2.3:

class CaseSensitiveString < String
  def downcase
    self
  end

  def capitalize
    self
  end

  def to_s
    self
  end
end

The to_s method needs to be added because as of 2.3, HTTP::Header's capitalize method calls to_s on each header.

Stephen
  • 3,341
  • 1
  • 22
  • 21
Mike K
  • 81
  • 1
  • 2
  • you need to be careful, if you do req["Content-Type"] with this solution, you won't end up overriding "content-type" that ruby sets. – Stephen Apr 06 '19 at 01:24
0
require 'net/http'
require 'uri'

# monkey patching inorder to suppress header hijacking by net/http, RFC -> headers are case insensitive
# File lib/net/http/header.rb, line 220, ruby 3.1.2

# def capitalize(name)
#   name.to_s.split(/-/).map {|s| s.capitalize }.join('-')
# end

class CustomGet < Net::HTTP::Get
  def capitalize(name)
    name
  end
end

uri = URI.parse('https://example.com')
request = CustomGet.new(uri)

headers = { 'custom-header': 'value' }

headers.each do |header, value|
  request[header] = value
end

req_options = {
  use_ssl: uri.scheme == 'https'
}

response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
  http.request(request)
end

p response.code
p response.body
ravish
  • 61
  • 1
  • 3