escher 0.4.3 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dbb2c924f14cb51125c9a2248c3363c44db153f6
4
- data.tar.gz: 01990347371ae73f6e51b9f82f1c8912fd99d656
2
+ SHA256:
3
+ metadata.gz: b18ad0e7492749391a2d41baab5ba09033def7a7c1399ce8f9725cd9786eab98
4
+ data.tar.gz: 6ac12253dbf9cab09ffb66b60539edb33f0648ebc0c0b4c9c2b746157236f9af
5
5
  SHA512:
6
- metadata.gz: 8627e0c6108b62f1ab72136bcd0f53362ec50af7cbffdbed37560b2d9ee55cd449e1c953445447e6d1c3774e09f46534b8e14e7a6efc386e1887965e3abe2e88
7
- data.tar.gz: 8e7705c5395920085e166b1953db9433ad42db825acf1a733a4f70a6dfd640513f0f71034b115b1f1c8f6af5676a5293cdbccafefec4af40748bef7bf11e0097
6
+ metadata.gz: d1d465e795dab3645b727748ff4a2b0f00e448a214986c0c36c5abc9671d64f5aec2c7ad7275f38a8cca7729ca01a38fde3421697be5d2c91bfbc55365777e05
7
+ data.tar.gz: 5601a61b35a0700c80bbab5a42cc2e524643ae7aced341ad7073d17cb3cb51f3f333b811326aa519c19bee6f91bda7550881dfda53a84ee3689fddc41b694146
@@ -0,0 +1,33 @@
1
+ name: Ruby
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby-versions: ['2.6', '2.7', '3.0']
11
+
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ - uses: ruby/setup-ruby@v1
15
+ with:
16
+ ruby-version: ${{ matrix.ruby-versions }}
17
+ - name: Checkout testsuite
18
+ run: ./scripts/checkout_test_suite.sh
19
+ - name: Install dependencies
20
+ run: bundle install
21
+ - name: Run tests
22
+ run: bundle exec rake
23
+ - name: Deploy
24
+ if : github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
25
+ run: |
26
+ mkdir -p $HOME/.gem
27
+ touch $HOME/.gem/credentials
28
+ chmod 0600 $HOME/.gem/credentials
29
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
30
+ gem build *.gemspec
31
+ gem push *.gem
32
+ env:
33
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
data/escher.gemspec CHANGED
@@ -20,14 +20,14 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.required_ruby_version = '>= 1.9'
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.6"
23
+ spec.add_development_dependency "bundler", "~> 2.2.20"
24
24
  spec.add_development_dependency "rails"
25
25
 
26
- spec.add_development_dependency "rake", "~> 10"
27
- spec.add_development_dependency "rspec", "~> 2"
26
+ spec.add_development_dependency "rake", "~> 13.0.6"
27
+ spec.add_development_dependency "rspec", "~> 3"
28
28
 
29
29
  spec.add_development_dependency "rack"
30
30
  spec.add_development_dependency "plissken"
31
31
 
32
- spec.add_runtime_dependency "addressable", "~> 2.3"
32
+ spec.add_runtime_dependency "addressable", "~> 2.8.0"
33
33
  end
data/lib/escher/auth.rb CHANGED
@@ -6,7 +6,7 @@ module Escher
6
6
  @algo_prefix = options[:algo_prefix] || 'ESR'
7
7
  @vendor_key = options[:vendor_key] || 'Escher'
8
8
  @hash_algo = options[:hash_algo] || 'SHA256'
9
- @current_time = options[:current_time] || Time.now
9
+ @current_time = options[:current_time]
10
10
  @auth_header_name = options[:auth_header_name] || 'X-Escher-Auth'
11
11
  @date_header_name = options[:date_header_name] || 'X-Escher-Date'
12
12
  @clock_skew = options[:clock_skew] || 300
@@ -17,15 +17,17 @@ module Escher
17
17
 
18
18
 
19
19
  def sign!(req, client, headers_to_sign = [])
20
+ current_time = @current_time || Time.now
21
+
20
22
  headers_to_sign |= [@date_header_name.downcase, 'host']
21
23
 
22
24
  request = wrap_request req
23
25
  raise EscherError, 'The host header is missing' unless request.has_header? 'host'
24
26
 
25
- request.set_header(@date_header_name.downcase, format_date_for_header) unless request.has_header? @date_header_name
27
+ request.set_header(@date_header_name.downcase, format_date_for_header(current_time)) unless request.has_header? @date_header_name
26
28
 
27
- signature = generate_signature(client[:api_secret], request.body, request.headers, request.method, headers_to_sign, request.path, request.query_values)
28
- request.set_header(@auth_header_name, "#{@algo_id} Credential=#{client[:api_key_id]}/#{short_date(@current_time)}/#{@credential_scope}, SignedHeaders=#{prepare_headers_to_sign headers_to_sign}, Signature=#{signature}")
29
+ signature = generate_signature(client[:api_secret], request.body, request.headers, request.method, headers_to_sign, request.path, request.query_values, current_time)
30
+ request.set_header(@auth_header_name, "#{@algo_id} Credential=#{client[:api_key_id]}/#{short_date(current_time)}/#{@credential_scope}, SignedHeaders=#{prepare_headers_to_sign headers_to_sign}, Signature=#{signature}")
29
31
 
30
32
  request.request
31
33
  end
@@ -44,6 +46,7 @@ module Escher
44
46
 
45
47
 
46
48
  def authenticate(req, key_db, mandatory_signed_headers = nil)
49
+ current_time = @current_time || Time.now
47
50
  request = wrap_request req
48
51
  method = request.method
49
52
  body = request.body
@@ -78,10 +81,9 @@ module Escher
78
81
  raise EscherError, 'Invalid Escher key' unless api_secret
79
82
  raise EscherError, 'Invalid hash algorithm, only SHA256 and SHA512 are allowed' unless %w(SHA256 SHA512).include?(algorithm)
80
83
  raise EscherError, 'The request method is invalid' unless valid_request_method?(method)
81
- raise EscherError, "The request body shouldn't be empty if the request method is POST" if (method.upcase == 'POST' && body.empty?)
82
84
  raise EscherError, "The request url shouldn't contains http or https" if path.match /^https?:\/\//
83
85
  raise EscherError, 'Invalid date in authorization header, it should equal with date header' unless short_date(date) == short_date
84
- raise EscherError, 'The request date is not within the accepted time range' unless is_date_within_range?(date, expires)
86
+ raise EscherError, 'The request date is not within the accepted time range' unless is_date_within_range?(date, expires, current_time)
85
87
  raise EscherError, 'Invalid Credential Scope' unless credential_scope == @credential_scope
86
88
  raise EscherError, 'The mandatorySignedHeaders parameter must be undefined or array of strings' unless mandatory_signed_headers_valid?(mandatory_signed_headers)
87
89
  raise EscherError, 'The host header is not signed' unless signed_headers.include? 'host'
@@ -94,7 +96,7 @@ module Escher
94
96
  raise EscherError, 'The date header is not signed' if !signature_from_query && !signed_headers.include?(@date_header_name.downcase)
95
97
 
96
98
  escher = reconfig(algorithm, credential_scope, date)
97
- expected_signature = escher.generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts)
99
+ expected_signature = escher.generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts, date)
98
100
  raise EscherError, 'The signatures do not match' unless signature == expected_signature
99
101
  api_key_id
100
102
  end
@@ -116,6 +118,7 @@ module Escher
116
118
 
117
119
 
118
120
  def generate_signed_url(url_to_sign, client, expires = 86400)
121
+ current_time = @current_time || Time.now
119
122
  uri = Addressable::URI.parse(url_to_sign)
120
123
 
121
124
  if (not uri.port.nil?) && (uri.port != uri.default_port)
@@ -137,13 +140,13 @@ module Escher
137
140
  body = 'UNSIGNED-PAYLOAD'
138
141
  query_parts += [
139
142
  ['Algorithm', @algo_id],
140
- ['Credentials', "#{client[:api_key_id]}/#{short_date(@current_time)}/#{@credential_scope}"],
141
- ['Date', long_date(@current_time)],
143
+ ['Credentials', "#{client[:api_key_id]}/#{short_date(current_time)}/#{@credential_scope}"],
144
+ ['Date', long_date(current_time)],
142
145
  ['Expires', expires.to_s],
143
146
  ['SignedHeaders', headers_to_sign.join(';')],
144
147
  ].map { |k, v| query_pair(k, v) }
145
148
 
146
- signature = generate_signature(client[:api_secret], body, headers, 'GET', headers_to_sign, path, query_parts)
149
+ signature = generate_signature(client[:api_secret], body, headers, 'GET', headers_to_sign, path, query_parts, current_time)
147
150
  query_parts_with_signature = (query_parts.map { |k, v| [uri_encode(k), uri_encode(v)] } << query_pair('Signature', signature))
148
151
  "#{uri.scheme}://#{host}#{path}?#{query_parts_with_signature.map { |k, v| k + '=' + v }.join('&')}#{(fragment === nil ? '' : '#' + fragment)}"
149
152
  end
@@ -189,11 +192,11 @@ module Escher
189
192
 
190
193
 
191
194
 
192
- def generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts)
195
+ def generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts, current_time)
193
196
  canonicalized_request = canonicalize(method, path, query_parts, body, headers, signed_headers.uniq)
194
- string_to_sign = get_string_to_sign(canonicalized_request)
197
+ string_to_sign = get_string_to_sign(canonicalized_request, current_time)
195
198
 
196
- signing_key = OpenSSL::HMAC.digest(@algo, @algo_prefix + api_secret, short_date(@current_time))
199
+ signing_key = OpenSSL::HMAC.digest(@algo, @algo_prefix + api_secret, short_date(current_time))
197
200
  @credential_scope.split('/').each { |data|
198
201
  signing_key = OpenSSL::HMAC.digest(@algo, signing_key, data)
199
202
  }
@@ -203,8 +206,8 @@ module Escher
203
206
 
204
207
 
205
208
 
206
- def format_date_for_header
207
- @date_header_name.downcase == 'date' ? @current_time.utc.rfc2822.sub('-0000', 'GMT') : long_date(@current_time)
209
+ def format_date_for_header(current_time)
210
+ @date_header_name.downcase == 'date' ? current_time.utc.rfc2822.sub('-0000', 'GMT') : long_date(current_time)
208
211
  end
209
212
 
210
213
 
@@ -239,11 +242,11 @@ module Escher
239
242
 
240
243
 
241
244
 
242
- def get_string_to_sign(canonicalized_request)
245
+ def get_string_to_sign(canonicalized_request, current_time)
243
246
  [
244
247
  @algo_id,
245
- long_date(@current_time),
246
- short_date(@current_time) + '/' + @credential_scope,
248
+ long_date(current_time),
249
+ short_date(current_time) + '/' + @credential_scope,
247
250
  @algo.new.hexdigest(canonicalized_request)
248
251
  ].join("\n")
249
252
  end
@@ -255,7 +258,7 @@ module Escher
255
258
  when 'SHA256'
256
259
  @algo = OpenSSL::Digest::SHA256.new
257
260
  when 'SHA512'
258
- @algo = OpenSSL::Digest::SHA521.new
261
+ @algo = OpenSSL::Digest::SHA512.new
259
262
  else
260
263
  raise EscherError, 'Unidentified hash algorithm'
261
264
  end
@@ -275,8 +278,8 @@ module Escher
275
278
 
276
279
 
277
280
 
278
- def is_date_within_range?(request_date, expires)
279
- (request_date - @clock_skew .. request_date + expires + @clock_skew).cover? @current_time
281
+ def is_date_within_range?(request_date, expires, current_time)
282
+ (request_date - @clock_skew .. request_date + expires + @clock_skew).cover? current_time
280
283
  end
281
284
 
282
285
 
@@ -6,7 +6,7 @@ module Escher
6
6
  case request
7
7
 
8
8
  when defined?(ActionDispatch::Request) && ActionDispatch::Request
9
- ActionDispatchRequest.new(request)
9
+ RackRequest.new(Rack::Request.new(request.env))
10
10
 
11
11
  when defined?(Rack::Request) && Rack::Request
12
12
  RackRequest.new(request)
@@ -6,7 +6,6 @@ module Escher::Request
6
6
  require 'escher/request/hash_request'
7
7
  require 'escher/request/rack_request'
8
8
  require 'escher/request/legacy_request'
9
- require 'escher/request/action_dispatch_request'
10
9
 
11
10
  require 'escher/request/factory'
12
11
 
@@ -1,3 +1,3 @@
1
1
  module Escher
2
- VERSION = '0.4.3'
2
+ VERSION = '2.0.2'
3
3
  end
data/repo-info.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "is_in_production": false,
3
+ "is_scannable": true,
4
+ "is_critical": false,
5
+ "contact": "G-GSUITE-Security@emarsys.com",
6
+ "hosted": null
7
+ }
@@ -35,11 +35,5 @@ module Escher
35
35
  expect(request).to eq(test_case.expected_request)
36
36
  end
37
37
  end
38
-
39
-
40
- xspecify "every case in the test suite is being used" do
41
- expect(::EmarsysTestSuiteHelpers::TestSuite.in_use_size).to eq ::EmarsysTestSuiteHelpers::TestSuite.size
42
- end
43
-
44
38
  end
45
39
  end
@@ -101,7 +101,7 @@ module Escher
101
101
  headers_to_sign = headers.map { |k| k[0].downcase }
102
102
  path, query_parts = escher.parse_uri(request_uri)
103
103
  canonicalized_request = escher.canonicalize(method, path, query_parts, body, headers, headers_to_sign)
104
- string_to_sign = escher.get_string_to_sign(canonicalized_request)
104
+ string_to_sign = escher.get_string_to_sign(canonicalized_request, Time.parse(date))
105
105
  expect(string_to_sign).to eq(fixture(suite, test, 'sts'))
106
106
  end
107
107
  end
@@ -484,7 +484,7 @@ module Escher
484
484
 
485
485
  it 'should convert dates' do
486
486
  date_str = 'Fri, 09 Sep 2011 23:36:00 GMT'
487
- expect(described_class.new('irrelevant', date_header_name: 'date', current_time: Time.parse(date_str)).format_date_for_header).to eq date_str
487
+ expect(described_class.new('irrelevant', date_header_name: 'date', current_time: Time.parse(date_str)).format_date_for_header(Time.parse(date_str))).to eq date_str
488
488
  end
489
489
 
490
490
 
@@ -7,19 +7,31 @@ require 'action_dispatch'
7
7
  describe Escher::Request::Factory do
8
8
 
9
9
  describe ".from_request" do
10
+ request_env = {Rack::PATH_INFO.to_s => "request-path"}
11
+
10
12
  {
11
13
 
12
- {uri: "request uri"} => Escher::Request::HashRequest,
13
- Struct.new(:uri) => Escher::Request::LegacyRequest,
14
- Rack::Request.new({}) => Escher::Request::RackRequest,
15
- ActionDispatch::Request.new({}) => Escher::Request::ActionDispatchRequest
14
+ {uri: "request-path"} => Escher::Request::HashRequest,
15
+ Struct.new("Request", :uri).new("request-path") => Escher::Request::LegacyRequest,
16
+ Rack::Request.new(request_env) => Escher::Request::RackRequest,
17
+ ActionDispatch::Request.new(request_env) => Escher::Request::RackRequest
16
18
 
17
19
  }.each do |request, expected_class|
18
20
 
19
- it "should return a #{expected_class.name} when the request to be wrapped is a #{request.class.name}" do
20
- expect(expected_class).to receive(:new).with(request).and_return "#{expected_class.name} wrapping request"
21
+ context "the request to be wrapped is a #{request.class.name}" do
22
+
23
+ it "returns a #{expected_class.name}" do
24
+ wrapped_request = described_class.from_request request
25
+
26
+ expect(wrapped_request).to be_an_instance_of expected_class
27
+ end
28
+
29
+ it "wraps the path from the original request" do
30
+ wrapped_request = described_class.from_request request
31
+
32
+ expect(wrapped_request.path).to eq "request-path"
33
+ end
21
34
 
22
- expect(described_class.from_request request).to eq "#{expected_class.name} wrapping request"
23
35
  end
24
36
 
25
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: escher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andras Barthazi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-25 00:00:00.000000000 Z
11
+ date: 2021-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
19
+ version: 2.2.20
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.6'
26
+ version: 2.2.20
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rails
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10'
47
+ version: 13.0.6
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10'
54
+ version: 13.0.6
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '2'
61
+ version: '3'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '2'
68
+ version: '3'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rack
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '2.3'
103
+ version: 2.8.0
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '2.3'
110
+ version: 2.8.0
111
111
  description: Escher helps you creating secure HTTP requests (for APIs) by signing
112
112
  HTTP(s) requests.
113
113
  email:
@@ -116,8 +116,8 @@ executables: []
116
116
  extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
+ - ".github/workflows/ruby.yml"
119
120
  - ".gitignore"
120
- - ".travis.yml"
121
121
  - Gemfile
122
122
  - LICENSE
123
123
  - README.md
@@ -127,7 +127,6 @@ files:
127
127
  - lib/escher/auth.rb
128
128
  - lib/escher/escher_error.rb
129
129
  - lib/escher/request.rb
130
- - lib/escher/request/action_dispatch_request.rb
131
130
  - lib/escher/request/base.rb
132
131
  - lib/escher/request/dci.rb
133
132
  - lib/escher/request/dci/rack_env.rb
@@ -136,6 +135,7 @@ files:
136
135
  - lib/escher/request/legacy_request.rb
137
136
  - lib/escher/request/rack_request.rb
138
137
  - lib/escher/version.rb
138
+ - repo-info.json
139
139
  - scripts/checkout_test_suite.sh
140
140
  - spec/aws4_testsuite/get-header-key-duplicate.authz
141
141
  - spec/aws4_testsuite/get-header-key-duplicate.creq
@@ -315,7 +315,6 @@ files:
315
315
  - spec/emarsys_testsuite/post-header-value-spaces.sreq
316
316
  - spec/emarsys_testsuite/post-header-value-spaces.sts
317
317
  - spec/escher/auth_spec.rb
318
- - spec/escher/request/action_dispatch_request_spec.rb
319
318
  - spec/escher/request/factory_spec.rb
320
319
  - spec/escher/request/hash_request_spec.rb
321
320
  - spec/escher/request/rack_request_spec.rb
@@ -342,8 +341,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
342
341
  - !ruby/object:Gem::Version
343
342
  version: '0'
344
343
  requirements: []
345
- rubyforge_project:
346
- rubygems_version: 2.5.1
344
+ rubygems_version: 3.2.22
347
345
  signing_key:
348
346
  specification_version: 4
349
347
  summary: Library for HTTP request signing (Ruby implementation)
@@ -526,7 +524,6 @@ test_files:
526
524
  - spec/emarsys_testsuite/post-header-value-spaces.sreq
527
525
  - spec/emarsys_testsuite/post-header-value-spaces.sts
528
526
  - spec/escher/auth_spec.rb
529
- - spec/escher/request/action_dispatch_request_spec.rb
530
527
  - spec/escher/request/factory_spec.rb
531
528
  - spec/escher/request/hash_request_spec.rb
532
529
  - spec/escher/request/rack_request_spec.rb
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.3
4
- - 2.3.0
5
- before_script: ./scripts/checkout_test_suite.sh
@@ -1,47 +0,0 @@
1
- module Escher
2
- module Request
3
- class ActionDispatchRequest < Base
4
-
5
- include Escher::Request::DCI::RackEnv
6
-
7
- def headers
8
- get_headers_by_rack_env(request.env)
9
- end
10
-
11
-
12
-
13
- def method
14
- request.request_method
15
- end
16
-
17
-
18
-
19
- def body
20
- case request.body
21
- when StringIO
22
- request.body.string
23
- else
24
- request.body.to_s
25
- end
26
- end
27
-
28
-
29
-
30
- def path
31
- request.env['REQUEST_PATH'] || request.path
32
- end
33
-
34
-
35
-
36
- def query_values
37
- Addressable::URI.new(:query => request.env['QUERY_STRING']).query_values(Array) or []
38
- end
39
-
40
-
41
-
42
- def set_header(header_name, value)
43
- end
44
-
45
- end
46
- end
47
- end
@@ -1,120 +0,0 @@
1
- require 'spec_helper'
2
-
3
- require 'action_dispatch'
4
-
5
- describe Escher::Request::ActionDispatchRequest do
6
-
7
- let(:request_params) { {"PATH_INFO" => "/", } }
8
- let(:request) { ActionDispatch::Request.new(request_params) }
9
-
10
- subject { described_class.new request }
11
-
12
- describe "#request" do
13
- it "should return the underlying request object" do
14
- expect(subject.request).to eq request
15
- end
16
- end
17
-
18
-
19
- describe "#headers" do
20
- it "should return only the HTTP request headers" do
21
- request_params.merge! 'HTTP_HOST' => 'some host',
22
- 'SOME_HEADER' => 'some header'
23
-
24
- expect(subject.headers).to eq [['HOST', 'some host']]
25
- end
26
-
27
- it "should replace underscores with dashes in the header name" do
28
- request_params.merge! 'HTTP_HOST_NAME' => 'some host'
29
-
30
- expect(subject.headers).to eq [['HOST-NAME', 'some host']]
31
- end
32
-
33
- it 'should add the content-type and content-length to the headers' do
34
- request_params.merge!( 'CONTENT_LENGTH' => '123', 'CONTENT_TYPE' => 'text/plain' )
35
-
36
- expect(subject.headers).to eq [%w(CONTENT-LENGTH 123), %w(CONTENT-TYPE text/plain)]
37
- end
38
- end
39
-
40
-
41
- describe "#has_header?" do
42
- it "should return true if request has specified header, false otherwise" do
43
- request_params.merge! 'HTTP_HOST_NAME' => 'some host'
44
-
45
- expect(subject.has_header? 'host-name').to be_truthy
46
- expect(subject.has_header? 'no-such-header').to be_falsey
47
- end
48
- end
49
-
50
-
51
- describe "#header" do
52
- it "should return the value for the requested header" do
53
- request_params.merge! 'HTTP_HOST' => 'some host'
54
-
55
- expect(subject.header 'host').to eq 'some host'
56
- end
57
-
58
- it "should return nil if no such header exists" do
59
- expect(subject.header 'host').to be_nil
60
- end
61
- end
62
-
63
-
64
- describe "#method" do
65
- it "should return the request method" do
66
- request_params.merge! 'REQUEST_METHOD' => 'GET'
67
-
68
- expect(subject.method).to eq 'GET'
69
- end
70
- end
71
-
72
-
73
- describe "#body" do
74
- it "should return the request body" do
75
- request_params.merge! 'rack.input' => 'request body'
76
-
77
- expect(subject.body).to eq 'request body'
78
- end
79
-
80
- it "should return empty string for no body" do
81
- expect(subject.body).to eq ''
82
- end
83
- end
84
-
85
-
86
- describe "#path" do
87
- it "should return the request path" do
88
- request_params.merge! 'REQUEST_PATH' => '/resources/id///'
89
-
90
- expect(subject.path).to eq '/resources/id///'
91
- end
92
- end
93
-
94
-
95
- describe "#query_values" do
96
- it "should return the request query parameters as an array of key-value pairs" do
97
- request_params.merge! 'QUERY_STRING' => 'search=query&param=value'
98
-
99
- expect(subject.query_values).to eq [['search', 'query'], ['param', 'value']]
100
- end
101
-
102
- it "should return the query parameters regardless of fragments" do
103
- request_params.merge! 'QUERY_STRING' => "@\#$%^&+=/,?><`\";:\\|][{}"
104
-
105
- expect(subject.query_values).to eq [["@\#$%^"], ["+", "/,?><`\";:\\|][{}"]]
106
- end
107
-
108
- it "should return an empty array if the request has no query parameters" do
109
- expect(subject.query_values).to eq []
110
- end
111
- end
112
-
113
-
114
- describe "#set_header" do
115
- it "should ignore calls" do
116
- expect { subject.set_header 'test-header', 'test value' }.not_to raise_error
117
- end
118
- end
119
-
120
- end