protocol-http 0.16.2 → 0.16.3

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
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