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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/protocol/http/accept_encoding.rb +15 -3
- data/lib/protocol/http/body/buffered.rb +29 -2
- data/lib/protocol/http/body/completable.rb +13 -0
- data/lib/protocol/http/body/deflate.rb +33 -0
- data/lib/protocol/http/body/digestable.rb +19 -4
- data/lib/protocol/http/body/file.rb +37 -1
- data/lib/protocol/http/body/head.rb +8 -0
- data/lib/protocol/http/body/inflate.rb +10 -2
- data/lib/protocol/http/body/readable.rb +32 -11
- data/lib/protocol/http/body/reader.rb +17 -0
- data/lib/protocol/http/body/rewindable.rb +19 -1
- data/lib/protocol/http/body/stream.rb +34 -6
- data/lib/protocol/http/body/streamable.rb +46 -5
- data/lib/protocol/http/body/wrapper.rb +25 -3
- data/lib/protocol/http/body/writable.rb +48 -7
- data/lib/protocol/http/body.rb +16 -0
- data/lib/protocol/http/content_encoding.rb +13 -3
- data/lib/protocol/http/cookie.rb +23 -0
- data/lib/protocol/http/header/authorization.rb +10 -2
- data/lib/protocol/http/header/cache_control.rb +42 -10
- data/lib/protocol/http/header/connection.rb +18 -1
- data/lib/protocol/http/header/cookie.rb +10 -3
- data/lib/protocol/http/header/date.rb +9 -0
- data/lib/protocol/http/header/etag.rb +11 -0
- data/lib/protocol/http/header/etags.rb +35 -4
- data/lib/protocol/http/header/multiple.rb +9 -1
- data/lib/protocol/http/header/priority.rb +55 -0
- data/lib/protocol/http/header/split.rb +17 -3
- data/lib/protocol/http/header/vary.rb +10 -1
- data/lib/protocol/http/headers.rb +82 -19
- data/lib/protocol/http/methods.rb +5 -0
- data/lib/protocol/http/middleware/builder.rb +17 -0
- data/lib/protocol/http/middleware.rb +28 -0
- data/lib/protocol/http/peer.rb +9 -0
- data/lib/protocol/http/reference.rb +33 -6
- data/lib/protocol/http/request.rb +25 -0
- data/lib/protocol/http/response.rb +12 -0
- data/lib/protocol/http/url.rb +34 -8
- data/lib/protocol/http/version.rb +1 -1
- data/readme.md +4 -0
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +4 -2
- 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
|
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
|
-
# @
|
167
|
-
# @
|
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
|
-
#
|
174
|
-
# @
|
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
|
-
#
|
195
|
-
# @
|
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
|
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
|
-
#
|
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
|
-
#
|
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
|
data/lib/protocol/http/peer.rb
CHANGED
@@ -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.
|
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
|
-
#
|
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
|
-
|
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
|
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
|