13

I intend to make a call from a Ruby on Rails application:

c = Curl::Easy.http_post("https://example.com", json_string_goes_here) do |curl|
  curl.headers['Accept'] = 'application/json'
  curl.headers['Content-Type'] = 'application/json'
  curl.headers['Api-Version'] = '2.2'
end

The response should have custom headers:

X-Custom1 : "some value"
X-Custom2 : "another value"

How do I iterate over the response headers to compare the values to what I was expecting?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Mike Malloy
  • 1,520
  • 1
  • 15
  • 19

1 Answers1

36

Using Curl::Easy's header_str you can access the returned headers as a string. From the documentation:

Return the response header from the previous call to perform. This is populated by the default on_header handler - if you supply your own header handler, this string will be empty.

To test this I turned on the built-in Gem server using:

gem server

Here's some code to test this:

curl = Curl::Easy.http_get('http://0.0.0.0:8808')
curl.header_str
=> "HTTP/1.1 200 OK \r\nDate: 2013-01-10 09:07:42 -0700\r\nContent-Type: text/html\r\nServer: WEBrick/1.3.1 (Ruby/1.9.3/2012-11-10)\r\nContent-Length: 62164\r\nConnection: Keep-Alive\r\n\r\n"

Capturing the response, and breaking the remaining string into a hash making it easier to use, is simple:

http_response, *http_headers = curl.header_str.split(/[\r\n]+/).map(&:strip)
http_headers = Hash[http_headers.flat_map{ |s| s.scan(/^(\S+): (.+)/) }]

http_response # => "HTTP/1.1 200 OK"

http_headers 
=> {
                  "Date" => "2013-01-10 09:07:42 -0700",
          "Content-Type" => "text/html",
                "Server" => "WEBrick/1.3.1 (Ruby/1.9.3/2012-11-10)",
        "Content-Length" => "62164",
            "Connection" => "Keep-Alive"
    }

Testing again, in Pry:

[27] (pry) main: 0> curl = Curl::Easy.http_get('http://www.example.com')
#<Curl::Easy http://www.example.com>
[28] (pry) main: 0> curl.header_str
"HTTP/1.0 302 Found\r\nLocation: http://www.iana.org/domains/example/\r\nServer: BigIP\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\n\r\n"
[29] (pry) main: 0> http_response, *http_headers = curl.header_str.split(/[\r\n]+/).map(&:strip)
[
    [0] "HTTP/1.0 302 Found",
    [1] "Location: http://www.iana.org/domains/example/",
    [2] "Server: BigIP",
    [3] "Connection: Keep-Alive",
    [4] "Content-Length: 0"
]
[30] (pry) main: 0> http_headers = Hash[http_headers.flat_map{ |s| s.scan(/^(\S+): (.+)/) }]
{
          "Location" => "http://www.iana.org/domains/example/",
            "Server" => "BigIP",
        "Connection" => "Keep-Alive",
    "Content-Length" => "0"
}
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • 4
    This is a great answer, you deserve for this one to be accepted. On a side note, what a terrible user experience curb is giving you by not parsing the headers for you. – Greg Guida Mar 05 '13 at 09:11
  • 1
    I don't understand your comment. You agree with him and think my answer should be taken down and replaced? He says this is a great answer. As for following redirects, we had to do it at a lot lower level for years, so, after having figured out how to unravel the data returned, it isn't that big a deal. For people who've never seen this level of HTTP communication it might be confusing or daunting, but it's just one more part of programming. – the Tin Man Mar 15 '13 at 22:14
  • 3
    @theTinMan - I think he is saying that it'd be nice if this was part of curb, so that everyone arriving here doesn't end up writing the same code over and over again... Your answer is a good one ;) – Louis Sayers May 26 '14 at 16:36