escher 0.4.3 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
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