Skip to content

Commit

Permalink
Add support for Typhoeus
Browse files Browse the repository at this point in the history
  • Loading branch information
liaden committed Jul 3, 2019
1 parent 05270e2 commit d48b349
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 0 deletions.
1 change: 1 addition & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Naming/AccessorMethodName:
- 'lib/api_auth/request_drivers/net_http.rb'
- 'lib/api_auth/request_drivers/rack.rb'
- 'lib/api_auth/request_drivers/rest_client.rb'
- 'lib/api_auth/request_drivers/typhoeus_request.rb'

# Offense count: 3
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
Expand Down
1 change: 1 addition & 0 deletions api_auth.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'pry'
s.add_development_dependency 'rake'
s.add_development_dependency 'rest-client', '~> 2.0'
s.add_development_dependency 'typhoeus'
s.add_development_dependency 'grape', '~> 1.1.0'
s.add_development_dependency 'rspec', '~> 3.4'

Expand Down
1 change: 1 addition & 0 deletions lib/api_auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
require 'api_auth/request_drivers/httpi'
require 'api_auth/request_drivers/faraday'
require 'api_auth/request_drivers/http'
require 'api_auth/request_drivers/typhoeus_request'

require 'api_auth/headers'
require 'api_auth/base'
Expand Down
2 changes: 2 additions & 0 deletions lib/api_auth/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def initialize_request_driver(request)
HttpiRequest.new(request)
when /Faraday::Request/
FaradayRequest.new(request)
when /Typhoeus::Request/
TyphoeusRequest.new(request)
when /HTTP::Request/
HttpRequest.new(request)
end
Expand Down
81 changes: 81 additions & 0 deletions lib/api_auth/request_drivers/typhoeus_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
module ApiAuth
module RequestDrivers # :nodoc:
class TyphoeusRequest # :nodoc:
include ApiAuth::Helpers

def initialize(request)
@request = request
fetch_headers
end

def set_auth_header(header)
@request.options[:headers]['Authorization'] = header
fetch_headers
@request
end

def calculated_md5
body = @request.options[:body] || ''
md5_base64digest(body.to_s)
end

def populate_content_md5
return unless %w[POST PUT].include?(http_method.to_s.upcase)

@request.options[:headers]['Content-MD5'] = calculated_md5
fetch_headers
end

def md5_mismatch?
if %w[POST PUT].include?(http_method.to_s.upcase)
calculated_md5 != content_md5
else
false
end
end

def fetch_headers
@headers = capitalize_keys(@request.options[:headers])
end

def http_method
@request.options[:method].to_s.upcase
end

def content_type
find_header(%w[CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE])
end

def content_md5
find_header(%w[CONTENT-MD5 CONTENT_MD5 HTTP-CONTENT-MD5 HTTP_CONTENT_MD5])
end

def original_uri
find_header(%w[X-ORIGINAL-URI X_ORIGINAL_URI HTTP_X_ORIGINAL_URI])
end

def request_uri
URI.parse(@request.url).request_uri
end

def set_date
@request.options[:headers]['Date'] = Time.now.utc.httpdate
fetch_headers
end

def timestamp
find_header(%w[DATE HTTP_DATE])
end

def authorization_header
find_header %w[Authorization AUTHORIZATION HTTP_AUTHORIZATION]
end

private

def find_header(keys)
keys.map { |key| @headers[key] }.compact.first
end
end
end
end
196 changes: 196 additions & 0 deletions spec/request_drivers/typhoeus_request_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
require 'spec_helper'

describe ApiAuth::RequestDrivers::TyphoeusRequest do
let(:timestamp) { Time.now.utc.httpdate }

let(:request_url) { 'https://example.com/resource.xml?foo=bar&bar=foo' }
let(:upload_file) { File.open('spec/fixtures/upload.png', 'r') }

let(:request_headers) do
{
'Authorization' => 'APIAuth 1044:12345',
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
'content-type' => 'text/plain',
'date' => timestamp
}
end

let(:upload_request) do
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: { file: upload_file })
end

let(:request) do
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: "hello\nworld")
end

subject(:driven_request) { ApiAuth::RequestDrivers::TyphoeusRequest.new(request) }

describe 'getting headers correctly' do
describe '#content_type' do
it 'gets the content_type' do
expect(driven_request.content_type).to eq('text/plain')
end
end

it 'gets the content_md5' do
expect(driven_request.content_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
end

it 'gets the request_uri' do
expect(driven_request.request_uri).to eq('/resource.xml?foo=bar&bar=foo')
end

it 'gets the timestamp' do
expect(driven_request.timestamp).to eq(timestamp)
end

it 'gets the authorization_header' do
expect(driven_request.authorization_header).to eq('APIAuth 1044:12345')
end

describe '#calculated_md5' do
it 'calculates md5 from the body' do
expect(driven_request.calculated_md5).to eq('kZXQvrKoieG+Be1rsZVINw==')
end

it 'treats no body as empty string' do
request.options[:body] = nil
expect(driven_request.calculated_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
end

context 'file upload' do
let(:request) { upload_request }

it 'calculates correctly for multipart content' do
expect(driven_request.calculated_md5).to eq('3wNtEzQ9ZdXZnkyg/glH1g==')
end
end
end

describe 'http_method' do
context 'when put request' do
let(:request) { Typhoeus::Request.new(request_url, method: :put, headers: request_headers) }

it 'returns upcased put' do
expect(driven_request.http_method).to eq('PUT')
end
end

context 'when get request' do
let(:request) { Typhoeus::Request.new(request_url, method: :get, headers: request_headers) }

it 'returns upcased get' do
expect(driven_request.http_method).to eq('GET')
end
end
end
end

describe 'setting headers correctly' do
let(:request_headers) do
{
'content-type' => 'text/plain'
}
end

let(:request) do
Typhoeus::Request.new(request_url, method: :put, headers: request_headers)
end

describe '#populate_content_md5' do
context 'when request type has no body' do
let(:request) do
Typhoeus::Request.new(request_url, method: :get, headers: request_headers)
end

it "doesn't populate content-md5" do
driven_request.populate_content_md5
expect(request.options[:headers]['Content-MD5']).to be_nil
end
end

context 'when request type has a body' do
let(:request) do
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: "hello\nworld")
end

it 'populates content-md5' do
driven_request.populate_content_md5
expect(request.options[:headers]['Content-MD5']).to eq('kZXQvrKoieG+Be1rsZVINw==')
end

it 'refreshes the cached headers' do
driven_request.populate_content_md5
expect(driven_request.content_md5).to eq('kZXQvrKoieG+Be1rsZVINw==')
end
end
end

describe '#set_date' do
before do
allow(Time).to receive_message_chain(:now, :utc, :httpdate).and_return(timestamp)
end

it 'sets the date header of the request' do
driven_request.set_date
expect(request.options[:headers]['Date']).to eq(timestamp)
end

it 'refreshes the cached headers' do
driven_request.set_date
expect(driven_request.timestamp).to eq(timestamp)
end
end

describe '#set_auth_header' do
it 'sets the auth header' do
driven_request.set_auth_header('APIAuth 1044:54321')
expect(request.options[:headers]['Authorization']).to eq('APIAuth 1044:54321')
end
end
end

describe 'md5_mismatch?' do
context 'when request type has no body' do
let(:request) do
Typhoeus::Request.new(request_url, method: :get, headers: request_headers)
end

it 'is false' do
expect(driven_request.md5_mismatch?).to be false
end
end

context 'when request type has a body' do
let(:request) do
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: "hello\world")
end

context 'when calculated matches sent' do
before do
request.options[:headers]['Content-MD5'] = '/F4DjTilcDIIVEHn/nAQsA=='
end

it 'is false' do
expect(driven_request.md5_mismatch?).to be false
end
end

context "when calculated doesn't match sent" do
before do
request.options[:headers]['Content-MD5'] = '3'
end

it 'is true' do
expect(driven_request.md5_mismatch?).to be true
end
end
end
end

describe 'fetch_headers' do
it 'returns request headers' do
expect(driven_request.fetch_headers).to include('CONTENT-TYPE' => 'text/plain')
end
end
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
require 'httpi'
require 'faraday'
require 'grape'
require 'typhoeus'
require 'net/http/post/multipart'

# Requires supporting files with custom matchers and macros, etc,
Expand Down

0 comments on commit d48b349

Please sign in to comment.