protocol-http 0.71.0 → 0.72.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/context/message-body.md +17 -0
- data/lib/protocol/http/body/readable.rb +6 -2
- data/lib/protocol/http/body/stream.rb +17 -3
- data/lib/protocol/http/body/streamable.rb +2 -2
- data/lib/protocol/http/error.rb +8 -8
- data/lib/protocol/http/header/accept.rb +1 -1
- data/lib/protocol/http/header/accept_charset.rb +1 -1
- data/lib/protocol/http/header/accept_encoding.rb +1 -1
- data/lib/protocol/http/header/accept_language.rb +1 -1
- data/lib/protocol/http/header/digest.rb +1 -1
- data/lib/protocol/http/header/range.rb +1 -1
- data/lib/protocol/http/header/server_timing.rb +1 -1
- data/lib/protocol/http/header/te.rb +1 -1
- data/lib/protocol/http/version.rb +1 -1
- data/readme.md +10 -8
- data/releases.md +10 -0
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65ff75ab6759c365978c021f9f35150b4d5fa7a7b2bba8783041afda704607bf
|
|
4
|
+
data.tar.gz: b234c9cce9f99d7c4313fde4d2b4b15bf10a074875efc358d4b5fb2bad19e27d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16529f44e3cfde0d49afa69d08d64570fe165af759ea2d0d74aef74363768569d8af7ec2008c1a67ac8ceadae655b719d4d6aa9a28c628633d31a1197b186cdd
|
|
7
|
+
data.tar.gz: a7858b7d471e34772eb722d03af12fa7ec077deff39c9ce94bd9fff3146e0453fe1cab0823479a0ff56dfd662c10fdf856906e37ce5892bc2843ee002a79f27c
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/message-body.md
CHANGED
|
@@ -259,6 +259,23 @@ content = uppercase.join # => "HELLO WORLD"
|
|
|
259
259
|
|
|
260
260
|
## Life-cycle
|
|
261
261
|
|
|
262
|
+
Bodies model application-facing streams. Their close operations describe what the application will do next; protocol implementations are responsible for mapping those operations to the wire protocol safely.
|
|
263
|
+
|
|
264
|
+
### Directional Closure
|
|
265
|
+
|
|
266
|
+
Request and response bodies are independent, so a bidirectional {ruby Protocol::HTTP::Body::Stream} can close either direction without implicitly closing the other:
|
|
267
|
+
|
|
268
|
+
| Operation | Application-level meaning | Typical protocol consequence |
|
|
269
|
+
| --- | --- | --- |
|
|
270
|
+
| `read` returns `nil` | The peer or producer completed the input normally. | The inbound body has reached end-of-stream. |
|
|
271
|
+
| `close_read` before end-of-stream | The application will not consume the remaining input, but may continue writing. | Discard unread data, terminate the exchange, or make the connection non-reusable. |
|
|
272
|
+
| `close_write` without an error | The application has finished producing output, but may continue reading. | Preserve previously written data and send a normal end-of-stream. |
|
|
273
|
+
| `close` without an error | The application has finished with both directions. | Complete or terminate the exchange without reporting an application error. |
|
|
274
|
+
| `close` with an error | The application cannot continue the exchange successfully. | Propagate the error or terminate the exchange using an appropriate protocol error. |
|
|
275
|
+
| `discard` | Consume input without processing it. | Prefer preserving the exchange or connection for reuse. |
|
|
276
|
+
|
|
277
|
+
These operations update local application-facing state. Returning from a close operation does not guarantee that the peer has observed it or that the underlying transport has been closed synchronously. Those details depend on the protocol and may be completed later.
|
|
278
|
+
|
|
262
279
|
### Initialization
|
|
263
280
|
|
|
264
281
|
Bodies are typically initialized with the data they need to process. For example:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2019-
|
|
4
|
+
# Copyright, 2019-2026, by Samuel Williams.
|
|
5
5
|
# Copyright, 2023, by Bruno Sutic.
|
|
6
6
|
|
|
7
7
|
require_relative "stream"
|
|
@@ -22,7 +22,11 @@ module Protocol
|
|
|
22
22
|
class Readable
|
|
23
23
|
# Close the stream immediately. After invoking this method, the stream should be considered closed, and all internal resources should be released.
|
|
24
24
|
#
|
|
25
|
-
#
|
|
25
|
+
# Closing the stream before it reaches end-of-file abandons any remaining input. The protocol implementation must account for that unread data before the associated exchange or connection can be reused. Depending on the protocol, it may discard the remaining data, terminate the exchange, or make the connection non-reusable. Use {discard} when preserving the exchange or connection is preferred.
|
|
26
|
+
#
|
|
27
|
+
# When closing before end-of-file, omitting the error represents deliberate application-level abandonment, not a protocol failure.
|
|
28
|
+
#
|
|
29
|
+
# If an error occurred while handling the output, it can be passed as an argument. This may be propagated to the client, for example the client may be informed that the stream was not fully read correctly.
|
|
26
30
|
#
|
|
27
31
|
# Invoking {read} after {close} will return `nil`.
|
|
28
32
|
#
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2019-
|
|
4
|
+
# Copyright, 2019-2026, by Samuel Williams.
|
|
5
5
|
# Copyright, 2023, by Genki Takiuchi.
|
|
6
6
|
# Copyright, 2025, by William T. Nelson.
|
|
7
7
|
|
|
@@ -347,7 +347,13 @@ module Protocol
|
|
|
347
347
|
def flush
|
|
348
348
|
end
|
|
349
349
|
|
|
350
|
-
# Close the input body.
|
|
350
|
+
# Close the application-facing input body. This does not close the output body, which may continue to be written independently.
|
|
351
|
+
#
|
|
352
|
+
# If the input has not reached end-of-file, any remaining data is abandoned. The protocol implementation must ensure that unread data cannot interfere with subsequent exchanges. Depending on the protocol, it may discard the remaining data, terminate the current exchange, or make the connection non-reusable.
|
|
353
|
+
#
|
|
354
|
+
# Closing without an error represents orderly application-level abandonment, not a protocol failure.
|
|
355
|
+
#
|
|
356
|
+
# This method is idempotent. After the first call, subsequent calls have no effect.
|
|
351
357
|
#
|
|
352
358
|
# If, while processing the data that was read from this stream, an error is encountered, it should be passed to this method.
|
|
353
359
|
#
|
|
@@ -362,7 +368,11 @@ module Protocol
|
|
|
362
368
|
end
|
|
363
369
|
end
|
|
364
370
|
|
|
365
|
-
# Close the output body.
|
|
371
|
+
# Close the application-facing output body. This does not close the input body, which may continue to be read independently.
|
|
372
|
+
#
|
|
373
|
+
# Closing without an error indicates that no more output will be produced. Previously written data remains part of the output and should be followed by a normal end-of-stream from the protocol implementation. If an error is provided, the protocol implementation may terminate the exchange instead.
|
|
374
|
+
#
|
|
375
|
+
# This method is idempotent. After the first call, subsequent calls have no effect.
|
|
366
376
|
#
|
|
367
377
|
# If, while generating the data that is written to this stream, an error is encountered, it should be passed to this method.
|
|
368
378
|
#
|
|
@@ -377,6 +387,10 @@ module Protocol
|
|
|
377
387
|
|
|
378
388
|
# Close the input and output bodies.
|
|
379
389
|
#
|
|
390
|
+
# Closing without an error represents orderly completion or abandonment of both application-facing directions, not cancellation. If the peer has not completed the exchange, the protocol implementation may need to terminate it without reporting an application error.
|
|
391
|
+
#
|
|
392
|
+
# Repeated calls are safe; each underlying direction will be closed at most once.
|
|
393
|
+
#
|
|
380
394
|
# @parameter error [Exception | Nil] The error that caused this stream to be closed, if any.
|
|
381
395
|
def close(error = nil)
|
|
382
396
|
self.close_read(error)
|
|
@@ -15,7 +15,7 @@ module Protocol
|
|
|
15
15
|
#
|
|
16
16
|
# In some cases, it's advantageous to directly read and write to the underlying stream if possible. For example, HTTP/1 upgrade requests, WebSockets, and similar. To handle that case, response bodies can implement {stream?} and return `true`. When {stream?} returns true, the body **should** be consumed by calling `call(stream)`. Server implementations may choose to always invoke `call(stream)` if it's efficient to do so. Bodies that don't support it will fall back to using {each}.
|
|
17
17
|
#
|
|
18
|
-
#
|
|
18
|
+
# Invoking `call(stream)` transfers ownership of the stream to the called body. The body may read from and write to the stream, and **must** close it before returning. The caller must consider the stream closed and unusable after `call(stream)` returns.
|
|
19
19
|
module Streamable
|
|
20
20
|
# Generate a new streaming request body using the given block to generate the body.
|
|
21
21
|
#
|
|
@@ -113,7 +113,7 @@ module Protocol
|
|
|
113
113
|
@output.read
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
-
# Invoke the block with the given stream. The block
|
|
116
|
+
# Invoke the block with the given stream. The block owns the stream for the duration of the call and must close it before returning.
|
|
117
117
|
#
|
|
118
118
|
# @parameter stream [Stream] The stream to read and write to.
|
|
119
119
|
def call(stream)
|
data/lib/protocol/http/error.rb
CHANGED
|
@@ -22,15 +22,17 @@ module Protocol
|
|
|
22
22
|
# @deprecated Use {RefusedError} instead.
|
|
23
23
|
RequestRefusedError = RefusedError
|
|
24
24
|
|
|
25
|
-
#
|
|
26
|
-
# This is used to indicate that the request was malformed or invalid.
|
|
25
|
+
# Marks errors which may indicate a malformed or invalid request when raised while processing an incoming request.
|
|
27
26
|
module BadRequest
|
|
28
27
|
end
|
|
29
28
|
|
|
30
|
-
# Raised when
|
|
31
|
-
class
|
|
29
|
+
# Raised when an HTTP header is malformed or invalid. When raised while processing an incoming request, it may be treated as a bad request.
|
|
30
|
+
class InvalidHeaderError < Error
|
|
32
31
|
include BadRequest
|
|
33
|
-
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Raised when a singleton (e.g. `content-length`) header is duplicated in a request or response.
|
|
35
|
+
class DuplicateHeaderError < InvalidHeaderError
|
|
34
36
|
# @parameter key [String] The header key that was duplicated.
|
|
35
37
|
def initialize(key, existing_value, new_value)
|
|
36
38
|
super("Duplicate singleton header key: #{key.inspect}")
|
|
@@ -62,9 +64,7 @@ module Protocol
|
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
# Raised when an invalid trailer header is encountered in headers.
|
|
65
|
-
class InvalidTrailerError <
|
|
66
|
-
include BadRequest
|
|
67
|
-
|
|
67
|
+
class InvalidTrailerError < InvalidHeaderError
|
|
68
68
|
# @parameter key [String] The trailer key that is invalid.
|
|
69
69
|
def initialize(key)
|
|
70
70
|
super("Invalid trailer key: #{key.inspect}")
|
|
@@ -12,7 +12,7 @@ module Protocol
|
|
|
12
12
|
module Header
|
|
13
13
|
# The `accept-charset` header represents a list of character sets that the client can accept.
|
|
14
14
|
class AcceptCharset < Split
|
|
15
|
-
ParseError = Class.new(
|
|
15
|
+
ParseError = Class.new(InvalidHeaderError)
|
|
16
16
|
|
|
17
17
|
# https://tools.ietf.org/html/rfc7231#section-5.3.3
|
|
18
18
|
CHARSET = /\A(?<name>#{TOKEN})(;q=(?<q>#{QVALUE}))?\z/
|
|
@@ -12,7 +12,7 @@ module Protocol
|
|
|
12
12
|
module Header
|
|
13
13
|
# The `accept-encoding` header represents a list of encodings that the client can accept.
|
|
14
14
|
class AcceptEncoding < Split
|
|
15
|
-
ParseError = Class.new(
|
|
15
|
+
ParseError = Class.new(InvalidHeaderError)
|
|
16
16
|
|
|
17
17
|
# https://tools.ietf.org/html/rfc7231#section-5.3.1
|
|
18
18
|
QVALUE = /0(\.[0-9]{0,3})?|1(\.[0]{0,3})?/
|
|
@@ -12,7 +12,7 @@ module Protocol
|
|
|
12
12
|
module Header
|
|
13
13
|
# The `accept-language` header represents a list of languages that the client can accept.
|
|
14
14
|
class AcceptLanguage < Split
|
|
15
|
-
ParseError = Class.new(
|
|
15
|
+
ParseError = Class.new(InvalidHeaderError)
|
|
16
16
|
|
|
17
17
|
# https://tools.ietf.org/html/rfc3066#section-2.1
|
|
18
18
|
NAME = /\*|[A-Z]{1,8}(-[A-Z0-9]{1,8})*/i
|
|
@@ -23,7 +23,7 @@ module Protocol
|
|
|
23
23
|
# # => "sha-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=, md5=9bb58f26192e4ba00f01e2e7b136bbd8"
|
|
24
24
|
# ```
|
|
25
25
|
class Digest < Split
|
|
26
|
-
ParseError = Class.new(
|
|
26
|
+
ParseError = Class.new(InvalidHeaderError)
|
|
27
27
|
|
|
28
28
|
# https://tools.ietf.org/html/rfc3230#section-4.3.2
|
|
29
29
|
ENTRY = /\A(?<algorithm>[a-zA-Z0-9][a-zA-Z0-9\-]*)\s*=\s*(?<value>.*)\z/
|
|
@@ -10,7 +10,7 @@ module Protocol
|
|
|
10
10
|
module Header
|
|
11
11
|
# Represents a `range` request header.
|
|
12
12
|
class Range
|
|
13
|
-
ParseError = Class.new(
|
|
13
|
+
ParseError = Class.new(InvalidHeaderError)
|
|
14
14
|
|
|
15
15
|
TOKEN = /[!#$%&'*+\-.0-9A-Z^_`a-z|~]+/
|
|
16
16
|
HEADER = /\A(?<unit>#{TOKEN})=(?<ranges>.*)\z/
|
|
@@ -23,7 +23,7 @@ module Protocol
|
|
|
23
23
|
# # => "db;dur=53.2, cache;dur=12.1;desc=\"Redis lookup\""
|
|
24
24
|
# ```
|
|
25
25
|
class ServerTiming < Split
|
|
26
|
-
ParseError = Class.new(
|
|
26
|
+
ParseError = Class.new(InvalidHeaderError)
|
|
27
27
|
|
|
28
28
|
# https://www.w3.org/TR/server-timing/
|
|
29
29
|
METRIC = /\A(?<name>[a-zA-Z0-9][a-zA-Z0-9_\-]*)(;(?<parameters>.*))?\z/
|
|
@@ -14,7 +14,7 @@ module Protocol
|
|
|
14
14
|
#
|
|
15
15
|
# The `te` header allows a client to indicate which transfer encodings it can handle, and in what order of preference using quality factors.
|
|
16
16
|
class TE < Split
|
|
17
|
-
ParseError = Class.new(
|
|
17
|
+
ParseError = Class.new(InvalidHeaderError)
|
|
18
18
|
|
|
19
19
|
# Transfer encoding token pattern
|
|
20
20
|
TOKEN = /[!#$%&'*+\-.0-9A-Z^_`a-z|~]+/
|
data/readme.md
CHANGED
|
@@ -30,6 +30,16 @@ Please see the [project documentation](https://socketry.github.io/protocol-http/
|
|
|
30
30
|
|
|
31
31
|
Please see the [project releases](https://socketry.github.io/protocol-http/releases/index) for all releases.
|
|
32
32
|
|
|
33
|
+
### v0.72.0
|
|
34
|
+
|
|
35
|
+
- Clarified body stream lifecycle and ownership, including the directional semantics of `Protocol::HTTP::Body::Stream#close_read`, `#close_write`, and `#close`, how premature input closure affects the associated HTTP exchange, and ownership of streams passed to `Streamable#call`.
|
|
36
|
+
- Introduce `Protocol::HTTP::InvalidHeaderError` for malformed or invalid headers, which can be treated as bad requests.
|
|
37
|
+
|
|
38
|
+
### v0.71.0
|
|
39
|
+
|
|
40
|
+
- Parse all cookie pairs from `Cookie` header fields, including multiple semicolon-separated pairs within each field.
|
|
41
|
+
- Preserve `Set-Cookie` parsing as one cookie plus attributes/directives per header field.
|
|
42
|
+
|
|
33
43
|
### v0.70.0
|
|
34
44
|
|
|
35
45
|
- Add stable preference ordering for weighted `Accept`, `Accept-Charset`, `Accept-Encoding`, `Accept-Language`, and `TE` values.
|
|
@@ -64,14 +74,6 @@ Please see the [project releases](https://socketry.github.io/protocol-http/relea
|
|
|
64
74
|
|
|
65
75
|
- Add support for the HTTP `QUERY` method.
|
|
66
76
|
|
|
67
|
-
### v0.62.1
|
|
68
|
-
|
|
69
|
-
- Fix handling of `Stream#read(0)`, it must return a mutable string (or clear the given buffer).
|
|
70
|
-
|
|
71
|
-
### v0.61.0
|
|
72
|
-
|
|
73
|
-
- Introduce `Protocol::HTTP::RefusedError` for indicating a stream or request was refused before processing and can be safely retried. `RequestRefusedError` is provided as an alias for backwards compatibility.
|
|
74
|
-
|
|
75
77
|
## See Also
|
|
76
78
|
|
|
77
79
|
- [protocol-http1](https://github.com/socketry/protocol-http1) — HTTP/1 client/server implementation using this
|
data/releases.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.72.0
|
|
4
|
+
|
|
5
|
+
- Clarified body stream lifecycle and ownership, including the directional semantics of `Protocol::HTTP::Body::Stream#close_read`, `#close_write`, and `#close`, how premature input closure affects the associated HTTP exchange, and ownership of streams passed to `Streamable#call`.
|
|
6
|
+
- Introduce `Protocol::HTTP::InvalidHeaderError` for malformed or invalid headers, which can be treated as bad requests.
|
|
7
|
+
|
|
8
|
+
## v0.71.0
|
|
9
|
+
|
|
10
|
+
- Parse all cookie pairs from `Cookie` header fields, including multiple semicolon-separated pairs within each field.
|
|
11
|
+
- Preserve `Set-Cookie` parsing as one cookie plus attributes/directives per header field.
|
|
12
|
+
|
|
3
13
|
## v0.70.0
|
|
4
14
|
|
|
5
15
|
- Add stable preference ordering for weighted `Accept`, `Accept-Charset`, `Accept-Encoding`, `Accept-Language`, and `TE` values.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|