protocol-http 0.16.2 → 0.16.3

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
2
  SHA256:
3
- metadata.gz: 8780963e3d4d2047c5eaaa772ad17b06552e16a7d552163e2547e2efc135ba95
4
- data.tar.gz: 8b84a92f87b1aa6d548bb5c55833935b860b762db547b741177681de951998dd
3
+ metadata.gz: 8f3dfaca48112b9b8d52096c8cab996403736391102e44a2f9ed6f5e71c2ce7d
4
+ data.tar.gz: 611c1ba35c482cba1e4fbeb98daadaf7872b8d2198717c795dfa94a4343e7b38
5
5
  SHA512:
6
- metadata.gz: 70a2f05d40dfd70d7b27f8d1c0f9b52cd7bdec132c4dd45b56581827ee9cf1e6bb3edfc50c937a23bd72ae712eeb8f9a0502594adecef1b3d2cf07dcac6ea6f9
7
- data.tar.gz: dd5d9697b59d893fd9c5cb94a7c84dc3f9e75a49754262aada2a4b0bb54b235165a7012215641728904247d23224d9a0bddab4e4f5eff86fc884d073a95df527
6
+ metadata.gz: 8d6988b4b2f2c1218c441d05788b88db0aa8890ac70877acc6f87c8ce2f18e32bae5d20505532ece25576c792c1935d42f69bb11ea3e34a8908211ed3fadc88f
7
+ data.tar.gz: 111ec27937755f580826ff37e2cdfcce11b8fcc571434e31c12822e355694b5d4431fc378c3e91476368006bf657bcb0be164392302274221586074e0193b03a
@@ -62,7 +62,12 @@ module Protocol
62
62
  end
63
63
 
64
64
  def digest
65
- @digest ||= compute_digest
65
+ if @digest.nil?
66
+ @digest = Digest::SHA256.new
67
+ @chunks.each{|chunk| @digest.update(chunk)}
68
+ end
69
+
70
+ @digest.hexdigest
66
71
  end
67
72
 
68
73
  def finish
@@ -86,7 +91,7 @@ module Protocol
86
91
  end
87
92
 
88
93
  def write(chunk)
89
- @digest = nil
94
+ @digest&.update(chunk)
90
95
  @chunks << chunk
91
96
  end
92
97
 
@@ -97,14 +102,6 @@ module Protocol
97
102
  def inspect
98
103
  "\#<#{self.class} #{@chunks.size} chunks, #{self.length} bytes>"
99
104
  end
100
-
101
- private
102
-
103
- def compute_digest
104
- algorithm = Digest::SHA256.new
105
- @chunks.each{|chunk| algorithm.update(chunk)}
106
- return algorithm.hexdigest
107
- end
108
105
  end
109
106
  end
110
107
  end
@@ -33,6 +33,16 @@ module Protocol
33
33
 
34
34
  @chunks = []
35
35
  @index = 0
36
+ @digest = nil
37
+ end
38
+
39
+ def digest
40
+ if @digest.nil?
41
+ @digest = Digest::SHA256.new
42
+ @chunks.each{|chunk| @digest.update(chunk)}
43
+ end
44
+
45
+ @digest.hexdigest
36
46
  end
37
47
 
38
48
  def empty?
@@ -51,6 +61,7 @@ module Protocol
51
61
  else
52
62
  if chunk = super
53
63
  @chunks << chunk
64
+ @digest&.update(chunk)
54
65
  @index += 1
55
66
  end
56
67
  end
@@ -26,7 +26,7 @@ module Protocol
26
26
  module HTTP
27
27
  module Header
28
28
  # Used for basic authorization.
29
- # @example headers.add(*Authorization.new("samuel", "password"))
29
+ # @example headers.add('authorization', Authorization.new("samuel", "password"))
30
30
  class Authorization
31
31
  KEY = "Authorization"
32
32
 
@@ -35,10 +35,6 @@ module Protocol
35
35
  @password = password
36
36
  end
37
37
 
38
- def to_ary
39
- return KEY, self.to_str
40
- end
41
-
42
38
  def encoded
43
39
  "#{@username}:#{@password}"
44
40
  end
@@ -25,7 +25,6 @@ require_relative 'split'
25
25
  module Protocol
26
26
  module HTTP
27
27
  module Header
28
- # Header value which is split by newline charaters (e.g. cookies).
29
28
  class CacheControl < Split
30
29
  PRIVATE = 'private'
31
30
  PUBLIC = 'public'
@@ -33,6 +32,10 @@ module Protocol
33
32
  NO_STORE = 'no-store'
34
33
  MAX_AGE = 'max-age'
35
34
 
35
+ STATIC = 'static'
36
+ DYNAMIC = 'dynamic'
37
+ STREAMING = 'streaming'
38
+
36
39
  def initialize(value)
37
40
  super(value.downcase)
38
41
  end
@@ -41,6 +44,18 @@ module Protocol
41
44
  super(value.downcase)
42
45
  end
43
46
 
47
+ def static?
48
+ self.include?(STATIC)
49
+ end
50
+
51
+ def dynamic?
52
+ self.include?(DYNAMIC)
53
+ end
54
+
55
+ def streaming?
56
+ self.include?(STREAMING)
57
+ end
58
+
44
59
  def private?
45
60
  self.include?(PRIVATE)
46
61
  end
@@ -25,7 +25,6 @@ require_relative 'split'
25
25
  module Protocol
26
26
  module HTTP
27
27
  module Header
28
- # Header value which is split by newline charaters (e.g. cookies).
29
28
  class Connection < Split
30
29
  KEEP_ALIVE = 'keep-alive'
31
30
  CLOSE = 'close'
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative 'split'
24
+
25
+ module Protocol
26
+ module HTTP
27
+ module Header
28
+ class ETag < String
29
+ def << value
30
+ replace(value)
31
+ end
32
+
33
+ def weak?
34
+ self.start_with('\W')
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -25,16 +25,8 @@ require_relative 'split'
25
25
  module Protocol
26
26
  module HTTP
27
27
  module Header
28
- # Header value which is split by newline charaters (e.g. cookies).
28
+ # This implementation is not strictly correct according to the RFC-specified format.
29
29
  class ETags < Split
30
- def initialize(value)
31
- super(value.downcase)
32
- end
33
-
34
- def << value
35
- super(value.downcase)
36
- end
37
-
38
30
  def wildcard?
39
31
  self.include?('*')
40
32
  end
@@ -25,7 +25,6 @@ require_relative 'split'
25
25
  module Protocol
26
26
  module HTTP
27
27
  module Header
28
- # Header value which is split by newline charaters (e.g. cookies).
29
28
  class Vary < Split
30
29
  def initialize(value)
31
30
  super(value.downcase)
@@ -25,6 +25,7 @@ require_relative 'header/multiple'
25
25
  require_relative 'header/cookie'
26
26
  require_relative 'header/connection'
27
27
  require_relative 'header/cache_control'
28
+ require_relative 'header/etag'
28
29
  require_relative 'header/etags'
29
30
  require_relative 'header/vary'
30
31
 
@@ -34,43 +35,93 @@ module Protocol
34
35
  class Headers
35
36
  Split = Header::Split
36
37
  Multiple = Header::Multiple
38
+ TRAILERS = 'trailers'
37
39
 
38
- def self.[] hash
39
- self.new(hash.to_a)
40
- end
41
-
42
- def initialize(fields = nil, indexed = nil)
43
- if fields
44
- @fields = fields.dup
40
+ # Construct an instance from a headers Array or Hash. No-op if already an instance of `Headers`.
41
+ # @return [Headers] an instance of headers.
42
+ def self.[] headers
43
+ if headers.is_a?(self)
44
+ headers
45
45
  else
46
- @fields = []
46
+ self.new(headers.to_a)
47
47
  end
48
+ end
49
+
50
+ def initialize(fields = [], indexed = nil)
51
+ @fields = fields
52
+ @indexed = indexed
48
53
 
49
- if indexed
50
- @indexed = indexed.dup
51
- else
52
- @indexed = nil
53
- end
54
+ # Marks where trailers start in the @fields array.
55
+ @tail = nil
56
+ @deferred = []
54
57
  end
55
58
 
56
- def dup
57
- self.class.new(@fields, @indexed)
59
+ def initialize_dup(other)
60
+ super
61
+
62
+ @fields = @fields.dup
63
+ @indexed = @indexed.dup
64
+ @deferred = @deferred.dup
58
65
  end
59
66
 
60
67
  def clear
61
68
  @fields.clear
62
69
  @indexed = nil
70
+ @tail = nil
71
+ @deferred.clear
63
72
  end
64
73
 
65
74
  # An array of `[key, value]` pairs.
66
75
  attr :fields
67
76
 
77
+ # Mark the subsequent headers as trailers.
78
+ def trailers!
79
+ @tail ||= @fields.size
80
+ end
81
+
82
+ # @return the trailers if there are any.
83
+ def trailers?
84
+ @tail != nil
85
+ end
86
+
87
+ def flatten!
88
+ unless @deferred.empty?
89
+ @tail ||= @fields.size
90
+
91
+ @deferred.each do |key, value|
92
+ self.add(key, value.call)
93
+ end
94
+ end
95
+ end
96
+
97
+ # Enumerate all trailers, including evaluating all deferred headers.
98
+ def trailers(&block)
99
+ return nil unless self.include?(TRAILERS)
100
+
101
+ return to_enum(:trailers) unless block_given?
102
+
103
+ flatten!
104
+
105
+ if @tail
106
+ @fields.drop(@tail).each(&block)
107
+ end
108
+ end
109
+
68
110
  def freeze
69
111
  return if frozen?
70
112
 
113
+ # Ensure all deferred headers are evaluated:
114
+ self.flatten!
115
+
71
116
  # Ensure @indexed is generated:
72
117
  self.to_h
73
118
 
119
+ # Remove all trailers:
120
+ self.delete(TRAILERS)
121
+
122
+ # No longer has stateful trailers:
123
+ @tail = nil
124
+
74
125
  @fields.freeze
75
126
  @indexed.freeze
76
127
 
@@ -89,6 +140,10 @@ module Protocol
89
140
  self[key] != nil
90
141
  end
91
142
 
143
+ def keys
144
+ self.to_h.keys
145
+ end
146
+
92
147
  def extract(keys)
93
148
  deleted, @fields = @fields.partition do |field|
94
149
  keys.include?(field.first.downcase)
@@ -103,14 +158,17 @@ module Protocol
103
158
  return deleted
104
159
  end
105
160
 
106
- # This is deprecated.
107
- alias slice! extract
108
-
109
161
  # Add the specified header key value pair.
110
162
  # @param key [String] the header key.
111
163
  # @param value [String] the header value to assign.
112
- def add(key, value)
113
- self[key] = value
164
+ # @yield dynamically generate the value when used as a trailer.
165
+ def add(key, value = nil, &block)
166
+ if block_given?
167
+ @deferred << [key, block]
168
+ self[TRAILERS] = key
169
+ else
170
+ self[key] = value
171
+ end
114
172
  end
115
173
 
116
174
  # Set the specified header key to the specified value, replacing any existing header keys with the same name.
@@ -145,7 +203,7 @@ module Protocol
145
203
  @fields << [key, value]
146
204
  end
147
205
 
148
- MERGE_POLICY = {
206
+ POLICY = {
149
207
  # Headers which may only be specified once.
150
208
  'content-type' => false,
151
209
  'content-disposition' => false,
@@ -161,6 +219,7 @@ module Protocol
161
219
  'location' => false,
162
220
  'max-forwards' => false,
163
221
 
222
+ # Custom headers:
164
223
  'connection' => Header::Connection,
165
224
  'cache-control' => Header::CacheControl,
166
225
  'vary' => Header::Vary,
@@ -170,6 +229,7 @@ module Protocol
170
229
  'x-forwarded-for' => Split,
171
230
 
172
231
  # Cache validations:
232
+ 'etag' => Header::ETag,
173
233
  'if-match' => Header::ETags,
174
234
  'if-none-match' => Header::ETags,
175
235
 
@@ -194,7 +254,7 @@ module Protocol
194
254
 
195
255
  if @indexed
196
256
  return @indexed.delete(key)
197
- elsif policy = MERGE_POLICY[key]
257
+ elsif policy = POLICY[key]
198
258
  (key, value), *tail = deleted
199
259
  merged = policy.new(value)
200
260
 
@@ -208,7 +268,7 @@ module Protocol
208
268
  end
209
269
 
210
270
  protected def merge_into(hash, key, value)
211
- if policy = MERGE_POLICY[key]
271
+ if policy = POLICY[key]
212
272
  if current_value = hash[key]
213
273
  current_value << value
214
274
  else
@@ -48,6 +48,12 @@ module Protocol
48
48
  attr_accessor :body
49
49
  attr_accessor :protocol
50
50
 
51
+ def trailers
52
+ if @headers.respond_to?(:trailers)
53
+ @headers.trailers
54
+ end
55
+ end
56
+
51
57
  # Send the request to the given connection.
52
58
  def call(connection)
53
59
  connection.call(self)
@@ -42,6 +42,12 @@ module Protocol
42
42
  attr_accessor :body
43
43
  attr_accessor :protocol
44
44
 
45
+ def trailers
46
+ if @headers.respond_to?(:trailers)
47
+ @headers.trailers
48
+ end
49
+ end
50
+
45
51
  def hijack?
46
52
  false
47
53
  end
@@ -22,6 +22,6 @@
22
22
 
23
23
  module Protocol
24
24
  module HTTP
25
- VERSION = "0.16.2"
25
+ VERSION = "0.16.3"
26
26
  end
27
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protocol-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.2
4
+ version: 0.16.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-05 00:00:00.000000000 Z
11
+ date: 2020-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: covered
@@ -99,6 +99,7 @@ files:
99
99
  - lib/protocol/http/header/cache_control.rb
100
100
  - lib/protocol/http/header/connection.rb
101
101
  - lib/protocol/http/header/cookie.rb
102
+ - lib/protocol/http/header/etag.rb
102
103
  - lib/protocol/http/header/etags.rb
103
104
  - lib/protocol/http/header/multiple.rb
104
105
  - lib/protocol/http/header/split.rb