protocol-http 0.28.2 → 0.30.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/protocol/http/body/buffered.rb +21 -11
- data/lib/protocol/http/body/completable.rb +8 -0
- data/lib/protocol/http/body/readable.rb +9 -1
- data/lib/protocol/http/body/rewindable.rb +17 -1
- data/lib/protocol/http/body/wrapper.rb +12 -0
- data/lib/protocol/http/methods.rb +2 -2
- data/lib/protocol/http/request.rb +24 -4
- data/lib/protocol/http/response.rb +2 -2
- data/lib/protocol/http/version.rb +1 -1
- data/readme.md +19 -10
- data/releases.md +40 -0
- data.tar.gz.sig +0 -0
- metadata +3 -2
- 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: 5365fcaf109db92e1db6bc6389e881881f7ec19c4f1bcd747b16d72698e89a7c
|
4
|
+
data.tar.gz: 11f68df5f5b9370579764e475a0fe1579c3b4d2f06aaed80efb0cb5fa5c6e392
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05f867a38d373f1491630bc5a9bf30401b8ac0fa41cec5392787a1fb916d42fa0f14ff8b80078b89246ab5bbdaae40b2dd67b77302571e3cb0803477e48ead4f
|
7
|
+
data.tar.gz: dbb7ac119a2d46c5150b8a274c89280582d69f64718849a7537d0a4b876f40c0b9a36c47fc3e2d6f27a244e12465d4ba372a3e4c87c7d0e1c738c0757cd672d5
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -11,25 +11,29 @@ module Protocol
|
|
11
11
|
module Body
|
12
12
|
# A body which buffers all it's contents.
|
13
13
|
class Buffered < Readable
|
14
|
-
#
|
14
|
+
# Tries to wrap an object in a {Buffered} instance.
|
15
15
|
#
|
16
16
|
# For compatibility, also accepts anything that behaves like an `Array(String)`.
|
17
17
|
#
|
18
18
|
# @parameter body [String | Array(String) | Readable | nil] the body to wrap.
|
19
19
|
# @returns [Readable | nil] the wrapped body or nil if nil was given.
|
20
|
-
def self.wrap(
|
21
|
-
if
|
22
|
-
return
|
23
|
-
elsif
|
24
|
-
return self.new(
|
25
|
-
elsif
|
26
|
-
return self.new([
|
27
|
-
elsif
|
28
|
-
return self.
|
20
|
+
def self.wrap(object)
|
21
|
+
if object.is_a?(Readable)
|
22
|
+
return object
|
23
|
+
elsif object.is_a?(Array)
|
24
|
+
return self.new(object)
|
25
|
+
elsif object.is_a?(String)
|
26
|
+
return self.new([object])
|
27
|
+
elsif object
|
28
|
+
return self.read(object)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
|
32
|
+
# Read the entire body into a buffered representation.
|
33
|
+
#
|
34
|
+
# @parameter body [Readable] the body to read.
|
35
|
+
# @returns [Buffered] the buffered body.
|
36
|
+
def self.read(body)
|
33
37
|
chunks = []
|
34
38
|
|
35
39
|
body.each do |chunk|
|
@@ -77,8 +81,14 @@ module Protocol
|
|
77
81
|
@chunks << chunk
|
78
82
|
end
|
79
83
|
|
84
|
+
def rewindable?
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
80
88
|
def rewind
|
81
89
|
@index = 0
|
90
|
+
|
91
|
+
return true
|
82
92
|
end
|
83
93
|
|
84
94
|
def inspect
|
@@ -29,6 +29,14 @@ module Protocol
|
|
29
29
|
false
|
30
30
|
end
|
31
31
|
|
32
|
+
def rewindable?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def rewind
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
32
40
|
def length
|
33
41
|
nil
|
34
42
|
end
|
@@ -58,7 +66,7 @@ module Protocol
|
|
58
66
|
# @returns [Buffered] The buffered body.
|
59
67
|
def finish
|
60
68
|
# Internally, this invokes `self.each` which then invokes `self.close`.
|
61
|
-
Buffered.
|
69
|
+
Buffered.read(self)
|
62
70
|
end
|
63
71
|
|
64
72
|
# Enumerate all chunks until finished, then invoke `#close`.
|
@@ -11,6 +11,16 @@ module Protocol
|
|
11
11
|
module Body
|
12
12
|
# A body which buffers all it's contents as it is `#read`.
|
13
13
|
class Rewindable < Wrapper
|
14
|
+
def self.wrap(message)
|
15
|
+
if body = message.body
|
16
|
+
if body.rewindable?
|
17
|
+
body
|
18
|
+
else
|
19
|
+
message.body = self.new(body)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
14
24
|
def initialize(body)
|
15
25
|
super(body)
|
16
26
|
|
@@ -26,7 +36,9 @@ module Protocol
|
|
26
36
|
(@index < @chunks.size) || super
|
27
37
|
end
|
28
38
|
|
29
|
-
# A rewindable body wraps some other body. Convert it to a buffered body
|
39
|
+
# A rewindable body wraps some other body. Convert it to a buffered body. The buffered body will share the same chunks as the rewindable body.
|
40
|
+
#
|
41
|
+
# @returns [Buffered] the buffered body.
|
30
42
|
def buffered
|
31
43
|
Buffered.new(@chunks)
|
32
44
|
end
|
@@ -54,6 +66,10 @@ module Protocol
|
|
54
66
|
@index = 0
|
55
67
|
end
|
56
68
|
|
69
|
+
def rewindable?
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
57
73
|
def inspect
|
58
74
|
"\#<#{self.class} #{@index}/#{@chunks.size} chunks read>"
|
59
75
|
end
|
@@ -10,6 +10,10 @@ module Protocol
|
|
10
10
|
module Body
|
11
11
|
# Wrapping body instance. Typically you'd override `#read`.
|
12
12
|
class Wrapper < Readable
|
13
|
+
# Wrap the body of the given message in a new instance of this class.
|
14
|
+
#
|
15
|
+
# @parameter message [Request | Response] the message to wrap.
|
16
|
+
# @returns [Wrapper | nil] the wrapped body or nil if the body was nil.
|
13
17
|
def self.wrap(message)
|
14
18
|
if body = message.body
|
15
19
|
message.body = self.new(body)
|
@@ -42,6 +46,14 @@ module Protocol
|
|
42
46
|
@body.ready?
|
43
47
|
end
|
44
48
|
|
49
|
+
def rewind
|
50
|
+
@body.rewind
|
51
|
+
end
|
52
|
+
|
53
|
+
def rewindable?
|
54
|
+
@body.rewindable?
|
55
|
+
end
|
56
|
+
|
45
57
|
def length
|
46
58
|
@body.length
|
47
59
|
end
|
@@ -70,9 +70,9 @@ module Protocol
|
|
70
70
|
end
|
71
71
|
|
72
72
|
self.each do |name, value|
|
73
|
-
define_method(name) do |location,
|
73
|
+
define_method(name) do |location, *arguments, **options|
|
74
74
|
self.call(
|
75
|
-
Request[value, location.to_s,
|
75
|
+
Request[value, location.to_s, *arguments, **options]
|
76
76
|
)
|
77
77
|
end
|
78
78
|
end
|
@@ -25,7 +25,7 @@ module Protocol
|
|
25
25
|
class Request
|
26
26
|
prepend Body::Reader
|
27
27
|
|
28
|
-
def initialize(scheme = nil, authority = nil, method = nil, path = nil, version = nil, headers = Headers.new, body = nil, protocol = nil)
|
28
|
+
def initialize(scheme = nil, authority = nil, method = nil, path = nil, version = nil, headers = Headers.new, body = nil, protocol = nil, interim_response = nil)
|
29
29
|
@scheme = scheme
|
30
30
|
@authority = authority
|
31
31
|
@method = method
|
@@ -34,6 +34,7 @@ module Protocol
|
|
34
34
|
@headers = headers
|
35
35
|
@body = body
|
36
36
|
@protocol = protocol
|
37
|
+
@interim_response = interim_response
|
37
38
|
end
|
38
39
|
|
39
40
|
# @attribute [String] the request scheme, usually `"http"` or `"https"`.
|
@@ -60,11 +61,30 @@ module Protocol
|
|
60
61
|
# @attribute [String | Array(String) | Nil] the request protocol, usually empty, but occasionally `"websocket"` or `"webtransport"`. In HTTP/1, it is used to request a connection upgrade, and in HTTP/2 it is used to indicate a specfic protocol for the stream.
|
61
62
|
attr_accessor :protocol
|
62
63
|
|
64
|
+
# @attribute [Proc] a callback which is called when an interim response is received.
|
65
|
+
attr_accessor :interim_response
|
66
|
+
|
63
67
|
# Send the request to the given connection.
|
64
68
|
def call(connection)
|
65
69
|
connection.call(self)
|
66
70
|
end
|
67
71
|
|
72
|
+
# Send an interim response back to the origin of this request, if possible.
|
73
|
+
def send_interim_response(status, headers)
|
74
|
+
@interim_response&.call(status, headers)
|
75
|
+
end
|
76
|
+
|
77
|
+
def on_interim_response(&block)
|
78
|
+
if interim_response = @interim_response
|
79
|
+
@interim_response = ->(status, headers) do
|
80
|
+
block.call(status, headers)
|
81
|
+
interim_response.call(status, headers)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
@interim_response = block
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
68
88
|
# Whether this is a HEAD request: no body is expected in the response.
|
69
89
|
def head?
|
70
90
|
@method == Methods::HEAD
|
@@ -81,11 +101,11 @@ module Protocol
|
|
81
101
|
# @parameter path [String] The path, e.g. `"/index.html"`, `"/search?q=hello"`, etc.
|
82
102
|
# @parameter headers [Hash] The headers, e.g. `{"accept" => "text/html"}`, etc.
|
83
103
|
# @parameter body [String | Array(String) | Body::Readable] The body, e.g. `"Hello, World!"`, etc. See {Body::Buffered.wrap} for more information about .
|
84
|
-
def self.[](method, path,
|
104
|
+
def self.[](method, path, _headers = nil, _body = nil, scheme: nil, authority: nil, headers: _headers, body: _body, protocol: nil, interim_response: nil)
|
85
105
|
body = Body::Buffered.wrap(body)
|
86
|
-
headers =
|
106
|
+
headers = Headers[headers]
|
87
107
|
|
88
|
-
self.new(
|
108
|
+
self.new(scheme, authority, method, path, nil, headers, body, protocol, interim_response)
|
89
109
|
end
|
90
110
|
|
91
111
|
# Whether the request can be replayed without side-effects.
|
@@ -130,9 +130,9 @@ module Protocol
|
|
130
130
|
# @parameter status [Integer] The HTTP status code, e.g. `200`, `404`, etc.
|
131
131
|
# @parameter headers [Hash] The headers, e.g. `{"content-type" => "text/html"}`, etc.
|
132
132
|
# @parameter body [String | Array(String) | Body::Readable] The body, e.g. `"Hello, World!"`, etc. See {Body::Buffered.wrap} for more information about .
|
133
|
-
def self.[](status,
|
133
|
+
def self.[](status, _headers = nil, _body = nil, headers: _headers, body: _body, protocol: nil)
|
134
134
|
body = Body::Buffered.wrap(body)
|
135
|
-
headers =
|
135
|
+
headers = Headers[headers]
|
136
136
|
|
137
137
|
self.new(nil, status, headers, body, protocol)
|
138
138
|
end
|
data/readme.md
CHANGED
@@ -18,6 +18,25 @@ Please see the [project documentation](https://socketry.github.io/protocol-http/
|
|
18
18
|
|
19
19
|
- [Design Overview](https://socketry.github.io/protocol-http/guides/design-overview/index) - This guide explains the high level design of `protocol-http` in the context of wider design patterns that can be used to implement HTTP clients and servers.
|
20
20
|
|
21
|
+
## Releases
|
22
|
+
|
23
|
+
Please see the [project releases](https://socketry.github.io/protocol-http/releases/index) for all releases.
|
24
|
+
|
25
|
+
### Unreleased
|
26
|
+
|
27
|
+
- [`Request[]` and `Response[]` Keyword Arguments](https://socketry.github.io/protocol-http/releases/index#request[]-and-response[]-keyword-arguments)
|
28
|
+
- [Interim Response Handling](https://socketry.github.io/protocol-http/releases/index#interim-response-handling)
|
29
|
+
|
30
|
+
## See Also
|
31
|
+
|
32
|
+
- [protocol-http1](https://github.com/socketry/protocol-http1) — HTTP/1 client/server implementation using this
|
33
|
+
interface.
|
34
|
+
- [protocol-http2](https://github.com/socketry/protocol-http2) — HTTP/2 client/server implementation using this
|
35
|
+
interface.
|
36
|
+
- [async-http](https://github.com/socketry/async-http) — Asynchronous HTTP client and server, supporting multiple HTTP
|
37
|
+
protocols & TLS.
|
38
|
+
- [async-websocket](https://github.com/socketry/async-websocket) — Asynchronous client and server WebSockets.
|
39
|
+
|
21
40
|
## Contributing
|
22
41
|
|
23
42
|
We welcome contributions to this project.
|
@@ -35,13 +54,3 @@ In order to protect users of this project, we require all contributors to comply
|
|
35
54
|
### Community Guidelines
|
36
55
|
|
37
56
|
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
38
|
-
|
39
|
-
## See Also
|
40
|
-
|
41
|
-
- [protocol-http1](https://github.com/socketry/protocol-http1) — HTTP/1 client/server implementation using this
|
42
|
-
interface.
|
43
|
-
- [protocol-http2](https://github.com/socketry/protocol-http2) — HTTP/2 client/server implementation using this
|
44
|
-
interface.
|
45
|
-
- [async-http](https://github.com/socketry/async-http) — Asynchronous HTTP client and server, supporting multiple HTTP
|
46
|
-
protocols & TLS.
|
47
|
-
- [async-websocket](https://github.com/socketry/async-websocket) — Asynchronous client and server WebSockets.
|
data/releases.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Releases
|
2
|
+
|
3
|
+
## Unreleased
|
4
|
+
|
5
|
+
### `Request[]` and `Response[]` Keyword Arguments
|
6
|
+
|
7
|
+
The `Request[]` and `Response[]` methods now support keyword arguments as a convenient way to set various positional arguments.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# Request keyword arguments:
|
11
|
+
client.get("/", headers: {"accept" => "text/html"}, authority: "example.com")
|
12
|
+
|
13
|
+
# Response keyword arguments:
|
14
|
+
def call(request)
|
15
|
+
return Response[200, headers: {"content-Type" => "text/html"}, body: "Hello, World!"]
|
16
|
+
```
|
17
|
+
|
18
|
+
### Interim Response Handling
|
19
|
+
|
20
|
+
The `Request` class now exposes a `#interim_response` attribute which can be used to handle interim responses both on the client side and server side.
|
21
|
+
|
22
|
+
On the client side, you can pass a callback using the `interim_response` keyword argument which will be invoked whenever an interim response is received:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
client = ...
|
26
|
+
response = client.get("/index", interim_response: proc{|status, headers| ...})
|
27
|
+
```
|
28
|
+
|
29
|
+
On the server side, you can send an interim response using the `#send_interim_response` method:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
def call(request)
|
33
|
+
if request.headers["expect"] == "100-continue"
|
34
|
+
# Send an interim response:
|
35
|
+
request.send_interim_response(100)
|
36
|
+
end
|
37
|
+
|
38
|
+
# ...
|
39
|
+
end
|
40
|
+
```
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protocol-http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.30.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -47,7 +47,7 @@ cert_chain:
|
|
47
47
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
48
48
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
49
49
|
-----END CERTIFICATE-----
|
50
|
-
date: 2024-08-
|
50
|
+
date: 2024-08-30 00:00:00.000000000 Z
|
51
51
|
dependencies: []
|
52
52
|
description:
|
53
53
|
email:
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- lib/protocol/http/version.rb
|
94
94
|
- license.md
|
95
95
|
- readme.md
|
96
|
+
- releases.md
|
96
97
|
homepage: https://github.com/socketry/protocol-http
|
97
98
|
licenses:
|
98
99
|
- MIT
|
metadata.gz.sig
CHANGED
Binary file
|