protocol-http 0.45.0 → 0.46.0

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