protocol-http 0.45.0 → 0.47.0

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/protocol/http/accept_encoding.rb +15 -3
  4. data/lib/protocol/http/body/buffered.rb +29 -2
  5. data/lib/protocol/http/body/completable.rb +13 -0
  6. data/lib/protocol/http/body/deflate.rb +33 -0
  7. data/lib/protocol/http/body/digestable.rb +19 -4
  8. data/lib/protocol/http/body/file.rb +37 -1
  9. data/lib/protocol/http/body/head.rb +8 -0
  10. data/lib/protocol/http/body/inflate.rb +10 -2
  11. data/lib/protocol/http/body/readable.rb +32 -11
  12. data/lib/protocol/http/body/reader.rb +17 -0
  13. data/lib/protocol/http/body/rewindable.rb +19 -1
  14. data/lib/protocol/http/body/stream.rb +34 -6
  15. data/lib/protocol/http/body/streamable.rb +46 -5
  16. data/lib/protocol/http/body/wrapper.rb +25 -3
  17. data/lib/protocol/http/body/writable.rb +48 -7
  18. data/lib/protocol/http/body.rb +16 -0
  19. data/lib/protocol/http/content_encoding.rb +13 -3
  20. data/lib/protocol/http/cookie.rb +23 -0
  21. data/lib/protocol/http/header/authorization.rb +10 -2
  22. data/lib/protocol/http/header/cache_control.rb +42 -10
  23. data/lib/protocol/http/header/connection.rb +18 -1
  24. data/lib/protocol/http/header/cookie.rb +10 -3
  25. data/lib/protocol/http/header/date.rb +9 -0
  26. data/lib/protocol/http/header/etag.rb +11 -0
  27. data/lib/protocol/http/header/etags.rb +35 -4
  28. data/lib/protocol/http/header/multiple.rb +9 -1
  29. data/lib/protocol/http/header/priority.rb +55 -0
  30. data/lib/protocol/http/header/split.rb +17 -3
  31. data/lib/protocol/http/header/vary.rb +10 -1
  32. data/lib/protocol/http/headers.rb +82 -19
  33. data/lib/protocol/http/methods.rb +5 -0
  34. data/lib/protocol/http/middleware/builder.rb +17 -0
  35. data/lib/protocol/http/middleware.rb +28 -0
  36. data/lib/protocol/http/peer.rb +9 -0
  37. data/lib/protocol/http/reference.rb +33 -6
  38. data/lib/protocol/http/request.rb +25 -0
  39. data/lib/protocol/http/response.rb +12 -0
  40. data/lib/protocol/http/url.rb +34 -8
  41. data/lib/protocol/http/version.rb +1 -1
  42. data/readme.md +4 -0
  43. data/releases.md +4 -0
  44. data.tar.gz.sig +0 -0
  45. metadata +4 -2
  46. metadata.gz.sig +0 -0
@@ -13,9 +13,14 @@ require_relative "header/etags"
13
13
  require_relative "header/vary"
14
14
  require_relative "header/authorization"
15
15
  require_relative "header/date"
16
+ require_relative "header/priority"
16
17
 
17
18
  module Protocol
18
19
  module HTTP
20
+ # @namespace
21
+ module Header
22
+ end
23
+
19
24
  # Headers are an array of key-value pairs. Some header keys represent multiple values.
20
25
  class Headers
21
26
  Split = Header::Split
@@ -24,6 +29,7 @@ module Protocol
24
29
  TRAILER = "trailer"
25
30
 
26
31
  # Construct an instance from a headers Array or Hash. No-op if already an instance of `Headers`. If the underlying array is frozen, it will be duped.
32
+ #
27
33
  # @return [Headers] an instance of headers.
28
34
  def self.[] headers
29
35
  if headers.nil?
@@ -47,6 +53,10 @@ module Protocol
47
53
  return self.new(fields)
48
54
  end
49
55
 
56
+ # Initialize the headers with the specified fields.
57
+ #
58
+ # @parameter fields [Array] An array of `[key, value]` pairs.
59
+ # @parameter indexed [Hash] A hash table of normalized headers, if available.
50
60
  def initialize(fields = [], indexed = nil)
51
61
  @fields = fields
52
62
  @indexed = indexed
@@ -55,6 +65,9 @@ module Protocol
55
65
  @tail = nil
56
66
  end
57
67
 
68
+ # Initialize a copy of the headers.
69
+ #
70
+ # @parameter other [Headers] The headers to copy.
58
71
  def initialize_dup(other)
59
72
  super
60
73
 
@@ -62,13 +75,14 @@ module Protocol
62
75
  @indexed = @indexed.dup
63
76
  end
64
77
 
78
+ # Clear all headers.
65
79
  def clear
66
80
  @fields.clear
67
81
  @indexed = nil
68
82
  @tail = nil
69
83
  end
70
84
 
71
- # Flatten trailer into the headers.
85
+ # Flatten trailer into the headers, in-place.
72
86
  def flatten!
73
87
  if @tail
74
88
  self.delete(TRAILER)
@@ -78,29 +92,27 @@ module Protocol
78
92
  return self
79
93
  end
80
94
 
95
+ # Flatten trailer into the headers, returning a new instance of {Headers}.
81
96
  def flatten
82
97
  self.dup.flatten!
83
98
  end
84
99
 
85
- # An array of `[key, value]` pairs.
100
+ # @attribute [Array] An array of `[key, value]` pairs.
86
101
  attr :fields
87
102
 
88
- # @returns Whether there are any trailers.
103
+ # @returns [Boolean] Whether there are any trailers.
89
104
  def trailer?
90
105
  @tail != nil
91
106
  end
92
107
 
93
108
  # Record the current headers, and prepare to add trailers.
94
109
  #
95
- # This method is typically used after headers are sent to capture any
96
- # additional headers which should then be sent as trailers.
110
+ # This method is typically used after headers are sent to capture any additional headers which should then be sent as trailers.
97
111
  #
98
- # A sender that intends to generate one or more trailer fields in a
99
- # message should generate a trailer header field in the header section of
100
- # that message to indicate which fields might be present in the trailers.
112
+ # A sender that intends to generate one or more trailer fields in a message should generate a trailer header field in the header section of that message to indicate which fields might be present in the trailers.
101
113
  #
102
114
  # @parameter names [Array] The trailer header names which will be added later.
103
- # @yields block {|name, value| ...} The trailer headers if any.
115
+ # @yields {|name, value| ...} the trailing headers if a block is given.
104
116
  # @returns An enumerator which is suitable for iterating over trailers.
105
117
  def trailer!(&block)
106
118
  @tail ||= @fields.size
@@ -117,6 +129,7 @@ module Protocol
117
129
  end
118
130
  end
119
131
 
132
+ # Freeze the headers, and ensure the indexed hash is generated.
120
133
  def freeze
121
134
  return if frozen?
122
135
 
@@ -129,24 +142,35 @@ module Protocol
129
142
  super
130
143
  end
131
144
 
145
+ # @returns [Boolean] Whether the headers are empty.
132
146
  def empty?
133
147
  @fields.empty?
134
148
  end
135
149
 
150
+ # Enumerate all header keys and values.
151
+ #
152
+ # @yields {|key, value| ...}
153
+ # @parameter key [String] The header key.
154
+ # @parameter value [String] The header value.
136
155
  def each(&block)
137
156
  @fields.each(&block)
138
157
  end
139
158
 
159
+ # @returns [Boolean] Whether the headers include the specified key.
140
160
  def include? key
141
161
  self[key] != nil
142
162
  end
143
163
 
144
164
  alias key? include?
145
165
 
166
+ # @returns [Array] All the keys of the headers.
146
167
  def keys
147
168
  self.to_h.keys
148
169
  end
149
170
 
171
+ # Extract the specified keys from the headers.
172
+ #
173
+ # @parameter keys [Array] The keys to extract.
150
174
  def extract(keys)
151
175
  deleted, @fields = @fields.partition do |field|
152
176
  keys.include?(field.first.downcase)
@@ -163,21 +187,23 @@ module Protocol
163
187
 
164
188
  # Add the specified header key value pair.
165
189
  #
166
- # @param key [String] the header key.
167
- # @param value [String] the header value to assign.
190
+ # @parameter key [String] the header key.
191
+ # @parameter value [String] the header value to assign.
168
192
  def add(key, value)
169
193
  self[key] = value
170
194
  end
171
195
 
172
196
  # Set the specified header key to the specified value, replacing any existing header keys with the same name.
173
- # @param key [String] the header key to replace.
174
- # @param value [String] the header value to assign.
197
+ #
198
+ # @parameter key [String] the header key to replace.
199
+ # @parameter value [String] the header value to assign.
175
200
  def set(key, value)
176
201
  # TODO This could be a bit more efficient:
177
202
  self.delete(key)
178
203
  self.add(key, value)
179
204
  end
180
205
 
206
+ # Merge the headers into this instance.
181
207
  def merge!(headers)
182
208
  headers.each do |key, value|
183
209
  self[key] = value
@@ -186,13 +212,15 @@ module Protocol
186
212
  return self
187
213
  end
188
214
 
215
+ # Merge the headers into a new instance of {Headers}.
189
216
  def merge(headers)
190
217
  self.dup.merge!(headers)
191
218
  end
192
219
 
193
220
  # Append the value to the given key. Some values can be appended multiple times, others can only be set once.
194
- # @param key [String] The header key.
195
- # @param value The header value.
221
+ #
222
+ # @parameter key [String] The header key.
223
+ # @parameter value [String] The header value.
196
224
  def []= key, value
197
225
  if @indexed
198
226
  merge_into(@indexed, key.downcase, value)
@@ -201,8 +229,9 @@ module Protocol
201
229
  @fields << [key, value]
202
230
  end
203
231
 
232
+ # The policy for various headers, including how they are merged and normalized.
204
233
  POLICY = {
205
- # Headers which may only be specified once.
234
+ # Headers which may only be specified once:
206
235
  "content-type" => false,
207
236
  "content-disposition" => false,
208
237
  "content-length" => false,
@@ -218,6 +247,7 @@ module Protocol
218
247
  "connection" => Header::Connection,
219
248
  "cache-control" => Header::CacheControl,
220
249
  "vary" => Header::Vary,
250
+ "priority" => Header::Priority,
221
251
 
222
252
  # Headers specifically for proxies:
223
253
  "via" => Split,
@@ -249,7 +279,10 @@ module Protocol
249
279
  "if-unmodified-since" => Header::Date,
250
280
  }.tap{|hash| hash.default = Split}
251
281
 
252
- # Delete all headers with the given key, and return the merged value.
282
+ # Delete all header values for the given key, and return the merged value.
283
+ #
284
+ # @parameter key [String] The header key.
285
+ # @returns [String | Array | Object] The merged header value.
253
286
  def delete(key)
254
287
  deleted, @fields = @fields.partition do |field|
255
288
  field.first.downcase == key
@@ -274,6 +307,11 @@ module Protocol
274
307
  end
275
308
  end
276
309
 
310
+ # Merge the value into the hash according to the policy for the given key.
311
+ #
312
+ # @parameter hash [Hash] The hash to merge into.
313
+ # @parameter key [String] The header key.
314
+ # @parameter value [String] The raw header value.
277
315
  protected def merge_into(hash, key, value)
278
316
  if policy = POLICY[key]
279
317
  if current_value = hash[key]
@@ -287,11 +325,17 @@ module Protocol
287
325
  end
288
326
  end
289
327
 
328
+ # Get the value of the specified header key.
329
+ #
330
+ # @parameter key [String] The header key.
331
+ # @returns [String | Array | Object] The header value.
290
332
  def [] key
291
333
  to_h[key]
292
334
  end
293
335
 
294
- # A hash table of `{key, policy[key].map(values)}`
336
+ # Compute a hash table of headers, where the keys are normalized to lower case and the values are normalized according to the policy for that header.
337
+ #
338
+ # @returns [Hash] A hash table of `{key, value}` pairs.
295
339
  def to_h
296
340
  @indexed ||= @fields.inject({}) do |hash, (key, value)|
297
341
  merge_into(hash, key.downcase, value)
@@ -302,10 +346,16 @@ module Protocol
302
346
 
303
347
  alias as_json to_h
304
348
 
349
+ # Inspect the headers.
350
+ #
351
+ # @returns [String] A string representation of the headers.
305
352
  def inspect
306
353
  "#<#{self.class} #{@fields.inspect}>"
307
354
  end
308
355
 
356
+ # Compare this object to another object. May depend on the order of the fields.
357
+ #
358
+ # @returns [Boolean] Whether the other object is equal to this one.
309
359
  def == other
310
360
  case other
311
361
  when Hash
@@ -321,29 +371,42 @@ module Protocol
321
371
  class Merged
322
372
  include Enumerable
323
373
 
374
+ # Construct a merged list of headers.
375
+ #
376
+ # @parameter *all [Array] An array of all headers to merge.
324
377
  def initialize(*all)
325
378
  @all = all
326
379
  end
327
380
 
381
+ # @returns [Array] A list of all headers, in the order they were added, as `[key, value]` pairs.
328
382
  def fields
329
383
  each.to_a
330
384
  end
331
385
 
386
+ # @returns [Headers] A new instance of {Headers} containing all the merged headers.
332
387
  def flatten
333
388
  Headers.new(fields)
334
389
  end
335
390
 
391
+ # Clear the references to all headers.
336
392
  def clear
337
393
  @all.clear
338
394
  end
339
395
 
396
+ # Add a new set of headers to the merged list.
397
+ #
398
+ # @parameter headers [Headers | Array | Hash] A list of headers to add.
340
399
  def << headers
341
400
  @all << headers
342
401
 
343
402
  return self
344
403
  end
345
404
 
346
- # @yields [String, String] header key (lower case string) and value (as string).
405
+ # Enumerate all headers in the merged list.
406
+ #
407
+ # @yields {|key, value| ...} The header key and value.
408
+ # @parameter key [String] The header key (lower case).
409
+ # @parameter value [String] The header value.
347
410
  def each(&block)
348
411
  return to_enum unless block_given?
349
412
 
@@ -50,6 +50,11 @@ module Protocol
50
50
  # The PATCH method applies partial modifications to a resource.
51
51
  PATCH = "PATCH"
52
52
 
53
+ # Check if the given name is a valid HTTP method, according to this module.
54
+ #
55
+ # Note that this method only knows about the methods defined in this module, however there are many other methods defined in different specifications.
56
+ #
57
+ # @returns [Boolean] True if the name is a valid HTTP method.
53
58
  def self.valid?(name)
54
59
  const_defined?(name)
55
60
  rescue NameError
@@ -8,25 +8,42 @@ require_relative "../middleware"
8
8
  module Protocol
9
9
  module HTTP
10
10
  class Middleware
11
+ # A convenient interface for constructing middleware stacks.
11
12
  class Builder
13
+ # Initialize the builder with the given default application.
14
+ #
15
+ # @parameter default_app [Object] The default application to use if no middleware is specified.
12
16
  def initialize(default_app = NotFound)
13
17
  @use = []
14
18
  @app = default_app
15
19
  end
16
20
 
21
+ # Use the given middleware with the given arguments and options.
22
+ #
23
+ # @parameter middleware [Class | Object] The middleware class to use.
24
+ # @parameter arguments [Array] The arguments to pass to the middleware constructor.
25
+ # @parameter options [Hash] The options to pass to the middleware constructor.
26
+ # @parameter block [Proc] The block to pass to the middleware constructor.
17
27
  def use(middleware, *arguments, **options, &block)
18
28
  @use << proc {|app| middleware.new(app, *arguments, **options, &block)}
19
29
  end
20
30
 
31
+ # Specify the (default) middleware application to use.
32
+ #
33
+ # @parameter app [Middleware] The application to use if no middleware is able to handle the request.
21
34
  def run(app)
22
35
  @app = app
23
36
  end
24
37
 
38
+ # Convert the builder to an application by chaining the middleware together.
39
+ #
40
+ # @returns [Middleware] The application.
25
41
  def to_app
26
42
  @use.reverse.inject(@app) {|app, use| use.call(app)}
27
43
  end
28
44
  end
29
45
 
46
+ # Build a middleware application using the given block.
30
47
  def self.build(&block)
31
48
  builder = Builder.new
32
49
 
@@ -22,49 +22,77 @@ module Protocol
22
22
  # You do not need to use the Middleware class to implement middleware. You can implement the interface directly.
23
23
  class Middleware < Methods
24
24
  # Convert a block to a middleware delegate.
25
+ #
26
+ # @parameter block [Proc] The block to convert to a middleware delegate.
27
+ # @returns [Middleware] The middleware delegate.
25
28
  def self.for(&block)
29
+ # Add a close method to the block.
26
30
  def block.close
27
31
  end
28
32
 
29
33
  return self.new(block)
30
34
  end
31
35
 
36
+ # Initialize the middleware with the given delegate.
37
+ #
38
+ # @parameter delegate [Object] The delegate object. A delegate is used for passing along requests that are not handled by *this* middleware.
32
39
  def initialize(delegate)
33
40
  @delegate = delegate
34
41
  end
35
42
 
43
+ # @attribute [Object] The delegate object that is used for passing along requests that are not handled by *this* middleware.
36
44
  attr :delegate
37
45
 
46
+ # Close the middleware. Invokes the close method on the delegate.
38
47
  def close
39
48
  @delegate.close
40
49
  end
41
50
 
51
+ # Call the middleware with the given request. Invokes the call method on the delegate.
42
52
  def call(request)
43
53
  @delegate.call(request)
44
54
  end
45
55
 
56
+ # A simple middleware that always returns a 200 response.
46
57
  module Okay
58
+ # Close the middleware - idempotent no-op.
47
59
  def self.close
48
60
  end
49
61
 
62
+ # Call the middleware with the given request, always returning a 200 response.
63
+ #
64
+ # @parameter request [Request] The request object.
65
+ # @returns [Response] The response object, which always contains a 200 status code.
50
66
  def self.call(request)
51
67
  Response[200]
52
68
  end
53
69
  end
54
70
 
71
+ # A simple middleware that always returns a 404 response.
55
72
  module NotFound
73
+ # Close the middleware - idempotent no-op.
56
74
  def self.close
57
75
  end
58
76
 
77
+ # Call the middleware with the given request, always returning a 404 response. This middleware is useful as a default.
78
+ #
79
+ # @parameter request [Request] The request object.
80
+ # @returns [Response] The response object, which always contains a 404 status code.
59
81
  def self.call(request)
60
82
  Response[404]
61
83
  end
62
84
  end
63
85
 
86
+ # A simple middleware that always returns "Hello World!".
64
87
  module HelloWorld
88
+ # Close the middleware - idempotent no-op.
65
89
  def self.close
66
90
  end
67
91
 
92
+ # Call the middleware with the given request.
93
+ #
94
+ # @parameter request [Request] The request object.
95
+ # @returns [Response] The response object, whihc always contains "Hello World!".
68
96
  def self.call(request)
69
97
  Response[200, Headers["content-type" => "text/plain"], ["Hello World!"]]
70
98
  end
@@ -7,12 +7,18 @@ module Protocol
7
7
  module HTTP
8
8
  # Provide a well defined, cached representation of a peer (address).
9
9
  class Peer
10
+ # Create a new peer object for the given IO object, using the remote address if available.
11
+ #
12
+ # @returns [Peer | Nil] The peer object, or nil if the remote address is not available.
10
13
  def self.for(io)
11
14
  if address = io.remote_address
12
15
  return new(address)
13
16
  end
14
17
  end
15
18
 
19
+ # Initialize the peer with the given address.
20
+ #
21
+ # @parameter address [Addrinfo] The remote address of the peer.
16
22
  def initialize(address)
17
23
  @address = address
18
24
 
@@ -21,7 +27,10 @@ module Protocol
21
27
  end
22
28
  end
23
29
 
30
+ # @attribute [Addrinfo] The remote address of the peer.
24
31
  attr :address
32
+
33
+ # @attribute [String] The IP address of the peer, if available.
25
34
  attr :ip_address
26
35
 
27
36
  alias remote_address address
@@ -19,6 +19,12 @@ module Protocol
19
19
  self.new(path, query, fragment, parameters)
20
20
  end
21
21
 
22
+ # Initialize the reference.
23
+ #
24
+ # @parameter path [String] The path component, e.g. `/foo/bar/index.html`.
25
+ # @parameter query [String | Nil] The un-parsed query string, e.g. 'x=10&y=20'.
26
+ # @parameter fragment [String | Nil] The fragment, the part after the '#'.
27
+ # @parameter parameters [Hash | Nil] User supplied parameters that will be appended to the query part.
22
28
  def initialize(path = "/", query = nil, fragment = nil, parameters = nil)
23
29
  @path = path
24
30
  @query = query
@@ -26,18 +32,21 @@ module Protocol
26
32
  @parameters = parameters
27
33
  end
28
34
 
29
- # The path component, e.g. /foo/bar/index.html
35
+ # @attribute [String] The path component, e.g. `/foo/bar/index.html`.
30
36
  attr_accessor :path
31
37
 
32
- # The un-parsed query string, e.g. 'x=10&y=20'
38
+ # @attribute [String] The un-parsed query string, e.g. 'x=10&y=20'.
33
39
  attr_accessor :query
34
40
 
35
- # A fragment, the part after the '#'
41
+ # @attribute [String] The fragment, the part after the '#'.
36
42
  attr_accessor :fragment
37
43
 
38
- # User supplied parameters that will be appended to the query part.
44
+ # @attribute [Hash] User supplied parameters that will be appended to the query part.
39
45
  attr_accessor :parameters
40
46
 
47
+ # Freeze the reference.
48
+ #
49
+ # @returns [Reference] The frozen reference.
41
50
  def freeze
42
51
  return self if frozen?
43
52
 
@@ -49,14 +58,25 @@ module Protocol
49
58
  super
50
59
  end
51
60
 
61
+ # Implicit conversion to an array.
62
+ #
63
+ # @returns [Array] The reference as an array, `[path, query, fragment, parameters]`.
52
64
  def to_ary
53
65
  [@path, @query, @fragment, @parameters]
54
66
  end
55
67
 
68
+ # Compare two references.
69
+ #
70
+ # @parameter other [Reference] The other reference to compare.
71
+ # @returns [Integer] -1, 0, 1 if the reference is less than, equal to, or greater than the other reference.
56
72
  def <=> other
57
73
  to_ary <=> other.to_ary
58
74
  end
59
75
 
76
+ # Type-cast a reference.
77
+ #
78
+ # @parameter reference [Reference | String] The reference to type-cast.
79
+ # @returns [Reference] The type-casted reference.
60
80
  def self.[] reference
61
81
  if reference.is_a? self
62
82
  return reference
@@ -65,19 +85,23 @@ module Protocol
65
85
  end
66
86
  end
67
87
 
88
+ # @returns [Boolean] Whether the reference has parameters.
68
89
  def parameters?
69
90
  @parameters and !@parameters.empty?
70
91
  end
71
92
 
93
+ # @returns [Boolean] Whether the reference has a query string.
72
94
  def query?
73
95
  @query and !@query.empty?
74
96
  end
75
97
 
98
+ # @returns [Boolean] Whether the reference has a fragment.
76
99
  def fragment?
77
100
  @fragment and !@fragment.empty?
78
101
  end
79
102
 
80
- def append(buffer)
103
+ # Append the reference to the given buffer.
104
+ def append(buffer = String.new)
81
105
  if query?
82
106
  buffer << URL.escape_path(@path) << "?" << @query
83
107
  buffer << "&" << URL.encode(@parameters) if parameters?
@@ -93,8 +117,11 @@ module Protocol
93
117
  return buffer
94
118
  end
95
119
 
120
+ # Convert the reference to a string, e.g. `/foo/bar/index.html?x=10&y=20#section`
121
+ #
122
+ # @returns [String] The reference as a string.
96
123
  def to_s
97
- append(String.new)
124
+ append
98
125
  end
99
126
 
100
127
  # Merges two references as specified by RFC2396, similar to `URI.join`.
@@ -25,6 +25,17 @@ module Protocol
25
25
  class Request
26
26
  prepend Body::Reader
27
27
 
28
+ # Initialize the request.
29
+ #
30
+ # @parameter scheme [String | Nil] The request scheme, usually `"http"` or `"https"`.
31
+ # @parameter authority [String | Nil] The request authority, usually a hostname and port number, e.g. `"example.com:80"`.
32
+ # @parameter method [String | Nil] The request method, usually one of `"GET"`, `"HEAD"`, `"POST"`, `"PUT"`, `"DELETE"`, `"CONNECT"` or `"OPTIONS"`, etc.
33
+ # @parameter path [String | Nil] The request path, usually a path and query string, e.g. `"/index.html"`, `"/search?q=hello"`, etc.
34
+ # @parameter version [String | Nil] The request version, usually `"http/1.0"`, `"http/1.1"`, `"h2"`, or `"h3"`.
35
+ # @parameter headers [Headers] The request headers, usually containing metadata associated with the request such as the `"user-agent"`, `"accept"` (content type), `"accept-language"`, etc.
36
+ # @parameter body [Body::Readable] The request body.
37
+ # @parameter protocol [String | Array(String) | Nil] The request protocol, usually empty, but occasionally `"websocket"` or `"webtransport"`.
38
+ # @parameter interim_response [Proc] A callback which is called when an interim response is received.
28
39
  def initialize(scheme = nil, authority = nil, method = nil, path = nil, version = nil, headers = Headers.new, body = nil, protocol = nil, interim_response = nil)
29
40
  @scheme = scheme
30
41
  @authority = authority
@@ -81,6 +92,11 @@ module Protocol
81
92
  @interim_response&.call(status, headers)
82
93
  end
83
94
 
95
+ # Register a callback to be called when an interim response is received.
96
+ #
97
+ # @yields {|status, headers| ...} The callback to be called when an interim response is received.
98
+ # @parameter status [Integer] The HTTP status code, e.g. `100`, `101`, etc.
99
+ # @parameter headers [Hash] The headers, e.g. `{"link" => "</style.css>; rel=stylesheet"}`, etc.
84
100
  def on_interim_response(&block)
85
101
  if interim_response = @interim_response
86
102
  @interim_response = ->(status, headers) do
@@ -120,6 +136,9 @@ module Protocol
120
136
  @method != Methods::POST && (@body.nil? || @body.empty?)
121
137
  end
122
138
 
139
+ # Convert the request to a hash, suitable for serialization.
140
+ #
141
+ # @returns [Hash] The request as a hash.
123
142
  def as_json(...)
124
143
  {
125
144
  scheme: @scheme,
@@ -133,10 +152,16 @@ module Protocol
133
152
  }
134
153
  end
135
154
 
155
+ # Convert the request to JSON.
156
+ #
157
+ # @returns [String] The request as JSON.
136
158
  def to_json(...)
137
159
  as_json.to_json(...)
138
160
  end
139
161
 
162
+ # Summarize the request as a string.
163
+ #
164
+ # @returns [String] The request as a string.
140
165
  def to_s
141
166
  "#{@scheme}://#{@authority}: #{@method} #{@path} #{@version}"
142
167
  end
@@ -151,6 +151,9 @@ module Protocol
151
151
  Response[500, Headers["content-type" => "text/plain"], ["#{exception.class}: #{exception.message}"]]
152
152
  end
153
153
 
154
+ # Convert the response to a hash suitable for serialization.
155
+ #
156
+ # @returns [Hash] The response as a hash.
154
157
  def as_json(...)
155
158
  {
156
159
  version: @version,
@@ -161,14 +164,23 @@ module Protocol
161
164
  }
162
165
  end
163
166
 
167
+ # Convert the response to JSON.
168
+ #
169
+ # @returns [String] The response as JSON.
164
170
  def to_json(...)
165
171
  as_json.to_json(...)
166
172
  end
167
173
 
174
+ # Summarise the response as a string.
175
+ #
176
+ # @returns [String] The response as a string.
168
177
  def to_s
169
178
  "#{@status} #{@version}"
170
179
  end
171
180
 
181
+ # Implicit conversion to an array.
182
+ #
183
+ # @returns [Array] The response as an array, e.g. `[status, headers, body]`.
172
184
  def to_ary
173
185
  return @status, @headers, @body
174
186
  end