0

I'm using Faraday to create an SDK that will interact with an API, and I need to send two headers API_SIGNATURE and API_REQUEST_TIME, so that's what I've created:

class APIClient
  def initialize(api_key)
    @api_key = api_key
  end

  def get_users
    request.post('/users')
  end

  private

  def request
    Faraday.new(@@BASE_API_URL, headers: headers)
  end

  def headers
    timestamp = Time.now.to_i.to_s
    return {
      'API_SIGNATURE': Digest::MD5.hexdigest(@api_key + timestamp),
      'API_REQUEST_TIME': timestamp
    }
  end
end

And for some reason Faraday is changing API_SIGNATURE to Api-Signature and API_REQUEST_TIME to Api-Request-Time.

I would like to prevent that from happening. Someone suggested to use patron:

  def request
    Faraday.new(@@BASE_API_URL, headers: headers) do |faraday|
      faraday.adapter :patron
    end
  end

But that doesn't work. The following error is raised:

/Users/me/.rvm/gems/ruby-2.4.9/gems/patron-0.13.3/lib/patron/session.rb:330:in `handle_request': Operation timed out after 1004 milliseconds with 0 out of 0 bytes received (Faraday::TimeoutError)
        from /Users/me/.rvm/gems/ruby-2.4.9/gems/patron-0.13.3/lib/patron/session.rb:330:in `request'
        from /Users/me/.rvm/gems/ruby-2.4.9/gems/faraday-0.17.0/lib/faraday/adapter/patron.rb:29:in `call'
        from /Users/me/.rvm/gems/ruby-2.4.9/gems/faraday-0.17.0/lib/faraday/rack_builder.rb:143:in `build_response'
        from /Users/me/.rvm/gems/ruby-2.4.9/gems/faraday-0.17.0/lib/faraday/connection.rb:387:in `run_request'
        from /Users/me/.rvm/gems/ruby-2.4.9/gems/faraday-0.17.0/lib/faraday/connection.rb:175:in `post'
        from api.rb:32:in `analyze_text'
        from api.rb:38:in `full_analysis'
        from api.rb:65:in `<main>'

Thank you.

======= Update ==========

The problem reduced:

headers = { API_SIGNATURE: '', API_REQUEST_TIME: '' }

conn = Faraday.new('https://api.test.io', headers: headers) do |f|
  f.adapter :patron
end

puts conn.headers

2 Answers2

2

This error occurs because Faraday uses Net::HTTP by default and Net::HTTP changes the case of your header keys. You can read more about this issue at this related question.

You can get around this by using one of the other available adapters listed at https://lostisland.github.io/faraday/adapters/:

Or external adapters that require another gem:

Your specific implementation of Patron looks correct, so try using one of the other adapters to see if you have any better luck.

Update

I loaded your updated example and tested it myself. The solution is to use stringified keys. You're using symbolized keys.

# symbolized
headers = { API_SIGNATURE: '', API_REQUEST_TIME: '' }
=> {
       :API_SIGNATURE => "",
    :API_REQUEST_TIME => ""
}

This returns:

puts conn.headers
{"Api-Signature"=>"", "Api-Request-Time"=>"", "User-Agent"=>"Faraday v0.17.0"}

vs:

# stringified
headers = { 'API_SIGNATURE' => '', 'API_REQUEST_TIME' => '' }
=> {
       "API_SIGNATURE" => "",
    "API_REQUEST_TIME" => ""
}

Now you get the proper values:

puts conn.headers
{"API_SIGNATURE"=>"", "API_REQUEST_TIME"=>"", "User-Agent"=>"Faraday v0.17.0"}

At a glance, your original example appears stringified but is not (which is why I didn't catch it):

{
  'API_SIGNATURE': '',
  'API_REQUEST_TIME': ''
}
=> {
       :API_SIGNATURE => "",
    :API_REQUEST_TIME => ""
}

Using a colon for your hash key in Ruby automatically makes it a symbol, even if it's a quoted string. You need to use the hash rocket syntax for stringified keys.

anothermh
  • 9,815
  • 3
  • 33
  • 52
  • That's so weird, none of them are working. Do you think it could be the Ruby version I'm using? (2.5.3 installed with rvm). –  Oct 18 '19 at 23:15
  • Reduce your example so you're not using a class, just a simple `Faraday.new(...) do |f|` block with all your data hard-coded into it, and try again. – anothermh Oct 18 '19 at 23:34
  • 1
    It's pretty simple in Ruby. Do you want a symbolized string for the key? Use a colon. (`{ foo: 'bar' }`) Do you want to use any object at all for the key and not have it changed? Use a hash rocket. (`{ 1 => 'bar' }` or `{ [] => 'bar' }` or `{ Faraday.new => 'bar' }` or `{ 'foo' => 'bar' }` or even `{ :foo => 'bar' }`) – anothermh Oct 19 '19 at 00:00
0

The solution would be to use rocket assignation instead of colons. Like this:

headers = {
      'Content-Type': 'application/json',
      'x-api-key': '0000'
    }
...
conn.headers
=> {"Content-type"=>"application/json", "X-api-key"=>"0000", "User-Agent"=>"Faraday v0.17.3"}

VS

headers = {
      'Content-Type' => 'application/json',
      'x-api-key' => '0000'
    }
conn.headers
=> {"Content-Type"=>"application/json", "x-api-key"=>"0000", "User-Agent"=>"Faraday v0.17.3"} 

Note how the x was capitalized with colons vs how it was NOT using rockets.

damuz91
  • 1,461
  • 2
  • 21
  • 37