Skip to content

Commit d48b349

Browse files
committed
Add support for Typhoeus
1 parent 05270e2 commit d48b349

File tree

7 files changed

+283
-0
lines changed

7 files changed

+283
-0
lines changed

.rubocop_todo.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Naming/AccessorMethodName:
6161
- 'lib/api_auth/request_drivers/net_http.rb'
6262
- 'lib/api_auth/request_drivers/rack.rb'
6363
- 'lib/api_auth/request_drivers/rest_client.rb'
64+
- 'lib/api_auth/request_drivers/typhoeus_request.rb'
6465

6566
# Offense count: 3
6667
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.

api_auth.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
2424
s.add_development_dependency 'pry'
2525
s.add_development_dependency 'rake'
2626
s.add_development_dependency 'rest-client', '~> 2.0'
27+
s.add_development_dependency 'typhoeus'
2728
s.add_development_dependency 'grape', '~> 1.1.0'
2829
s.add_development_dependency 'rspec', '~> 3.4'
2930

lib/api_auth.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
require 'api_auth/request_drivers/httpi'
1616
require 'api_auth/request_drivers/faraday'
1717
require 'api_auth/request_drivers/http'
18+
require 'api_auth/request_drivers/typhoeus_request'
1819

1920
require 'api_auth/headers'
2021
require 'api_auth/base'

lib/api_auth/headers.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ def initialize_request_driver(request)
3636
HttpiRequest.new(request)
3737
when /Faraday::Request/
3838
FaradayRequest.new(request)
39+
when /Typhoeus::Request/
40+
TyphoeusRequest.new(request)
3941
when /HTTP::Request/
4042
HttpRequest.new(request)
4143
end
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
module ApiAuth
2+
module RequestDrivers # :nodoc:
3+
class TyphoeusRequest # :nodoc:
4+
include ApiAuth::Helpers
5+
6+
def initialize(request)
7+
@request = request
8+
fetch_headers
9+
end
10+
11+
def set_auth_header(header)
12+
@request.options[:headers]['Authorization'] = header
13+
fetch_headers
14+
@request
15+
end
16+
17+
def calculated_md5
18+
body = @request.options[:body] || ''
19+
md5_base64digest(body.to_s)
20+
end
21+
22+
def populate_content_md5
23+
return unless %w[POST PUT].include?(http_method.to_s.upcase)
24+
25+
@request.options[:headers]['Content-MD5'] = calculated_md5
26+
fetch_headers
27+
end
28+
29+
def md5_mismatch?
30+
if %w[POST PUT].include?(http_method.to_s.upcase)
31+
calculated_md5 != content_md5
32+
else
33+
false
34+
end
35+
end
36+
37+
def fetch_headers
38+
@headers = capitalize_keys(@request.options[:headers])
39+
end
40+
41+
def http_method
42+
@request.options[:method].to_s.upcase
43+
end
44+
45+
def content_type
46+
find_header(%w[CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE])
47+
end
48+
49+
def content_md5
50+
find_header(%w[CONTENT-MD5 CONTENT_MD5 HTTP-CONTENT-MD5 HTTP_CONTENT_MD5])
51+
end
52+
53+
def original_uri
54+
find_header(%w[X-ORIGINAL-URI X_ORIGINAL_URI HTTP_X_ORIGINAL_URI])
55+
end
56+
57+
def request_uri
58+
URI.parse(@request.url).request_uri
59+
end
60+
61+
def set_date
62+
@request.options[:headers]['Date'] = Time.now.utc.httpdate
63+
fetch_headers
64+
end
65+
66+
def timestamp
67+
find_header(%w[DATE HTTP_DATE])
68+
end
69+
70+
def authorization_header
71+
find_header %w[Authorization AUTHORIZATION HTTP_AUTHORIZATION]
72+
end
73+
74+
private
75+
76+
def find_header(keys)
77+
keys.map { |key| @headers[key] }.compact.first
78+
end
79+
end
80+
end
81+
end
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
require 'spec_helper'
2+
3+
describe ApiAuth::RequestDrivers::TyphoeusRequest do
4+
let(:timestamp) { Time.now.utc.httpdate }
5+
6+
let(:request_url) { 'https://example.com/resource.xml?foo=bar&bar=foo' }
7+
let(:upload_file) { File.open('spec/fixtures/upload.png', 'r') }
8+
9+
let(:request_headers) do
10+
{
11+
'Authorization' => 'APIAuth 1044:12345',
12+
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
13+
'content-type' => 'text/plain',
14+
'date' => timestamp
15+
}
16+
end
17+
18+
let(:upload_request) do
19+
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: { file: upload_file })
20+
end
21+
22+
let(:request) do
23+
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: "hello\nworld")
24+
end
25+
26+
subject(:driven_request) { ApiAuth::RequestDrivers::TyphoeusRequest.new(request) }
27+
28+
describe 'getting headers correctly' do
29+
describe '#content_type' do
30+
it 'gets the content_type' do
31+
expect(driven_request.content_type).to eq('text/plain')
32+
end
33+
end
34+
35+
it 'gets the content_md5' do
36+
expect(driven_request.content_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
37+
end
38+
39+
it 'gets the request_uri' do
40+
expect(driven_request.request_uri).to eq('/resource.xml?foo=bar&bar=foo')
41+
end
42+
43+
it 'gets the timestamp' do
44+
expect(driven_request.timestamp).to eq(timestamp)
45+
end
46+
47+
it 'gets the authorization_header' do
48+
expect(driven_request.authorization_header).to eq('APIAuth 1044:12345')
49+
end
50+
51+
describe '#calculated_md5' do
52+
it 'calculates md5 from the body' do
53+
expect(driven_request.calculated_md5).to eq('kZXQvrKoieG+Be1rsZVINw==')
54+
end
55+
56+
it 'treats no body as empty string' do
57+
request.options[:body] = nil
58+
expect(driven_request.calculated_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
59+
end
60+
61+
context 'file upload' do
62+
let(:request) { upload_request }
63+
64+
it 'calculates correctly for multipart content' do
65+
expect(driven_request.calculated_md5).to eq('3wNtEzQ9ZdXZnkyg/glH1g==')
66+
end
67+
end
68+
end
69+
70+
describe 'http_method' do
71+
context 'when put request' do
72+
let(:request) { Typhoeus::Request.new(request_url, method: :put, headers: request_headers) }
73+
74+
it 'returns upcased put' do
75+
expect(driven_request.http_method).to eq('PUT')
76+
end
77+
end
78+
79+
context 'when get request' do
80+
let(:request) { Typhoeus::Request.new(request_url, method: :get, headers: request_headers) }
81+
82+
it 'returns upcased get' do
83+
expect(driven_request.http_method).to eq('GET')
84+
end
85+
end
86+
end
87+
end
88+
89+
describe 'setting headers correctly' do
90+
let(:request_headers) do
91+
{
92+
'content-type' => 'text/plain'
93+
}
94+
end
95+
96+
let(:request) do
97+
Typhoeus::Request.new(request_url, method: :put, headers: request_headers)
98+
end
99+
100+
describe '#populate_content_md5' do
101+
context 'when request type has no body' do
102+
let(:request) do
103+
Typhoeus::Request.new(request_url, method: :get, headers: request_headers)
104+
end
105+
106+
it "doesn't populate content-md5" do
107+
driven_request.populate_content_md5
108+
expect(request.options[:headers]['Content-MD5']).to be_nil
109+
end
110+
end
111+
112+
context 'when request type has a body' do
113+
let(:request) do
114+
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: "hello\nworld")
115+
end
116+
117+
it 'populates content-md5' do
118+
driven_request.populate_content_md5
119+
expect(request.options[:headers]['Content-MD5']).to eq('kZXQvrKoieG+Be1rsZVINw==')
120+
end
121+
122+
it 'refreshes the cached headers' do
123+
driven_request.populate_content_md5
124+
expect(driven_request.content_md5).to eq('kZXQvrKoieG+Be1rsZVINw==')
125+
end
126+
end
127+
end
128+
129+
describe '#set_date' do
130+
before do
131+
allow(Time).to receive_message_chain(:now, :utc, :httpdate).and_return(timestamp)
132+
end
133+
134+
it 'sets the date header of the request' do
135+
driven_request.set_date
136+
expect(request.options[:headers]['Date']).to eq(timestamp)
137+
end
138+
139+
it 'refreshes the cached headers' do
140+
driven_request.set_date
141+
expect(driven_request.timestamp).to eq(timestamp)
142+
end
143+
end
144+
145+
describe '#set_auth_header' do
146+
it 'sets the auth header' do
147+
driven_request.set_auth_header('APIAuth 1044:54321')
148+
expect(request.options[:headers]['Authorization']).to eq('APIAuth 1044:54321')
149+
end
150+
end
151+
end
152+
153+
describe 'md5_mismatch?' do
154+
context 'when request type has no body' do
155+
let(:request) do
156+
Typhoeus::Request.new(request_url, method: :get, headers: request_headers)
157+
end
158+
159+
it 'is false' do
160+
expect(driven_request.md5_mismatch?).to be false
161+
end
162+
end
163+
164+
context 'when request type has a body' do
165+
let(:request) do
166+
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: "hello\world")
167+
end
168+
169+
context 'when calculated matches sent' do
170+
before do
171+
request.options[:headers]['Content-MD5'] = '/F4DjTilcDIIVEHn/nAQsA=='
172+
end
173+
174+
it 'is false' do
175+
expect(driven_request.md5_mismatch?).to be false
176+
end
177+
end
178+
179+
context "when calculated doesn't match sent" do
180+
before do
181+
request.options[:headers]['Content-MD5'] = '3'
182+
end
183+
184+
it 'is true' do
185+
expect(driven_request.md5_mismatch?).to be true
186+
end
187+
end
188+
end
189+
end
190+
191+
describe 'fetch_headers' do
192+
it 'returns request headers' do
193+
expect(driven_request.fetch_headers).to include('CONTENT-TYPE' => 'text/plain')
194+
end
195+
end
196+
end

spec/spec_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
require 'httpi'
1818
require 'faraday'
1919
require 'grape'
20+
require 'typhoeus'
2021
require 'net/http/post/multipart'
2122

2223
# Requires supporting files with custom matchers and macros, etc,

0 commit comments

Comments
 (0)