Skip to content

Commit 94baae9

Browse files
committed
Add support for multiplexing protocol configurations.
1 parent 5624288 commit 94baae9

File tree

6 files changed

+87
-8
lines changed

6 files changed

+87
-8
lines changed

lib/async/http/protocol/http.rb

+17-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ module HTTP
1616
HTTP2_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
1717
HTTP2_PREFACE_SIZE = HTTP2_PREFACE.bytesize
1818

19+
# Determine if the inbound connection is HTTP/1 or HTTP/2.
20+
#
21+
# @parameter stream [IO::Stream] The stream to detect the protocol for.
22+
# @returns [Class] The protocol class to use.
1923
def self.protocol_for(stream)
2024
# Detect HTTP/2 connection preface
2125
# https://www.rfc-editor.org/rfc/rfc9113.html#section-3.4
@@ -35,18 +39,28 @@ def self.protocol_for(stream)
3539
end
3640
end
3741

38-
# Only inbound connections can detect HTTP1 vs HTTP2 for http://.
39-
# Outbound connections default to HTTP1.
42+
# Create a client for an outbound connection. Defaults to HTTP/1 for plaintext connections.
43+
#
44+
# @parameter peer [IO] The peer to communicate with.
45+
# @parameter options [Hash] Options to pass to the protocol, keyed by protocol class.
4046
def self.client(peer, **options)
47+
options = options[protocol]
48+
4149
HTTP1.client(peer, **options)
4250
end
4351

52+
# Create a server for an inbound connection. Able to detect HTTP1 vs HTTP2.
53+
#
54+
# @parameter peer [IO] The peer to communicate with.
55+
# @parameter options [Hash] Options to pass to the protocol, keyed by protocol class.
4456
def self.server(peer, **options)
4557
stream = ::IO::Stream(peer)
58+
protocol = protocol_for(stream)
4659

47-
return protocol_for(stream).server(stream, **options)
60+
return protocol.server(stream, options[protocol])
4861
end
4962

63+
# @returns [Array] The names of the supported protocols.
5064
def self.names
5165
["h2", "http/1.1", "http/1.0"]
5266
end

lib/async/http/protocol/http1.rb

+11
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,37 @@ module Protocol
1515
module HTTP1
1616
VERSION = "HTTP/1.1"
1717

18+
# @returns [Boolean] Whether the protocol supports bidirectional communication.
1819
def self.bidirectional?
1920
true
2021
end
2122

23+
# @returns [Boolean] Whether the protocol supports trailers.
2224
def self.trailer?
2325
true
2426
end
2527

28+
# Create a client for an outbound connection.
29+
#
30+
# @parameter peer [IO] The peer to communicate with.
31+
# @parameter options [Hash] Options to pass to the client instance.
2632
def self.client(peer, **options)
2733
stream = ::IO::Stream(peer)
2834

2935
return HTTP1::Client.new(stream, VERSION, **options)
3036
end
3137

38+
# Create a server for an inbound connection.
39+
#
40+
# @parameter peer [IO] The peer to communicate with.
41+
# @parameter options [Hash] Options to pass to the server instance.
3242
def self.server(peer, **options)
3343
stream = ::IO::Stream(peer)
3444

3545
return HTTP1::Server.new(stream, VERSION, **options)
3646
end
3747

48+
# @returns [Array] The names of the supported protocol.
3849
def self.names
3950
["http/1.1", "http/1.0"]
4051
end

lib/async/http/protocol/http10.rb

+11
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,37 @@ module Protocol
1212
module HTTP10
1313
VERSION = "HTTP/1.0"
1414

15+
# @returns [Boolean] Whether the protocol supports bidirectional communication.
1516
def self.bidirectional?
1617
false
1718
end
1819

20+
# @returns [Boolean] Whether the protocol supports trailers.
1921
def self.trailer?
2022
false
2123
end
2224

25+
# Create a client for an outbound connection.
26+
#
27+
# @parameter peer [IO] The peer to communicate with.
28+
# @parameter options [Hash] Options to pass to the client instance.
2329
def self.client(peer, **options)
2430
stream = ::IO::Stream(peer)
2531

2632
return HTTP1::Client.new(stream, VERSION, **options)
2733
end
2834

35+
# Create a server for an inbound connection.
36+
#
37+
# @parameter peer [IO] The peer to communicate with.
38+
# @parameter options [Hash] Options to pass to the server instance.
2939
def self.server(peer, **options)
3040
stream = ::IO::Stream(peer)
3141

3242
return HTTP1::Server.new(stream, VERSION, **options)
3343
end
3444

45+
# @returns [Array] The names of the supported protocol.
3546
def self.names
3647
["http/1.0"]
3748
end

lib/async/http/protocol/http11.rb

+11
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,37 @@ module Protocol
1313
module HTTP11
1414
VERSION = "HTTP/1.1"
1515

16+
# @returns [Boolean] Whether the protocol supports bidirectional communication.
1617
def self.bidirectional?
1718
true
1819
end
1920

21+
# @returns [Boolean] Whether the protocol supports trailers.
2022
def self.trailer?
2123
true
2224
end
2325

26+
# Create a client for an outbound connection.
27+
#
28+
# @parameter peer [IO] The peer to communicate with.
29+
# @parameter options [Hash] Options to pass to the client instance.
2430
def self.client(peer, **options)
2531
stream = ::IO::Stream(peer)
2632

2733
return HTTP1::Client.new(stream, VERSION, **options)
2834
end
2935

36+
# Create a server for an inbound connection.
37+
#
38+
# @parameter peer [IO] The peer to communicate with.
39+
# @parameter options [Hash] Options to pass to the server instance.
3040
def self.server(peer, **options)
3141
stream = ::IO::Stream(peer)
3242

3343
return HTTP1::Server.new(stream, VERSION, **options)
3444
end
3545

46+
# @returns [Array] The names of the supported protocol.
3647
def self.names
3748
["http/1.1"]
3849
end

lib/async/http/protocol/http2.rb

+11
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ module Protocol
1515
module HTTP2
1616
VERSION = "HTTP/2"
1717

18+
# @returns [Boolean] Whether the protocol supports bidirectional communication.
1819
def self.bidirectional?
1920
true
2021
end
2122

23+
# @returns [Boolean] Whether the protocol supports trailers.
2224
def self.trailer?
2325
true
2426
end
@@ -39,6 +41,10 @@ def self.trailer?
3941
::Protocol::HTTP2::Settings::NO_RFC7540_PRIORITIES => 1,
4042
}
4143

44+
# Create a client for an outbound connection.
45+
#
46+
# @parameter peer [IO] The peer to communicate with.
47+
# @parameter options [Hash] Options to pass to the client instance.
4248
def self.client(peer, settings: CLIENT_SETTINGS)
4349
stream = ::IO::Stream(peer)
4450
client = Client.new(stream)
@@ -49,6 +55,10 @@ def self.client(peer, settings: CLIENT_SETTINGS)
4955
return client
5056
end
5157

58+
# Create a server for an inbound connection.
59+
#
60+
# @parameter peer [IO] The peer to communicate with.
61+
# @parameter options [Hash] Options to pass to the server instance.
5262
def self.server(peer, settings: SERVER_SETTINGS)
5363
stream = ::IO::Stream(peer)
5464
server = Server.new(stream)
@@ -59,6 +69,7 @@ def self.server(peer, settings: SERVER_SETTINGS)
5969
return server
6070
end
6171

72+
# @returns [Array] The names of the supported protocol.
6273
def self.names
6374
["h2"]
6475
end

lib/async/http/protocol/https.rb

+26-5
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@ module HTTP
1414
module Protocol
1515
# A server that supports both HTTP1.0 and HTTP1.1 semantics by detecting the version of the request.
1616
module HTTPS
17+
# The protocol classes for each supported protocol.
1718
HANDLERS = {
1819
"h2" => HTTP2,
1920
"http/1.1" => HTTP11,
2021
"http/1.0" => HTTP10,
2122
nil => HTTP11,
2223
}
2324

25+
# Determine the protocol of the peer and return the appropriate protocol class.
26+
#
27+
# Use TLS Application Layer Protocol Negotiation (ALPN) to determine the protocol.
28+
#
29+
# @parameter peer [IO] The peer to communicate with.
30+
# @returns [Class] The protocol class to use.
2431
def self.protocol_for(peer)
2532
# alpn_protocol is only available if openssl v1.0.2+
2633
name = peer.alpn_protocol
@@ -34,15 +41,29 @@ def self.protocol_for(peer)
3441
end
3542
end
3643

37-
def self.client(peer)
38-
protocol_for(peer).client(peer)
44+
# Create a client for an outbound connection.
45+
#
46+
# @parameter peer [IO] The peer to communicate with.
47+
# @parameter options [Hash] Options to pass to the client instance.
48+
def self.client(peer, **options)
49+
protocol = protocol_for(peer)
50+
options = options[protocol]
51+
52+
protocol.client(peer, **options)
3953
end
4054

41-
def self.server(peer)
42-
protocol_for(peer).server(peer)
55+
# Create a server for an inbound connection.
56+
#
57+
# @parameter peer [IO] The peer to communicate with.
58+
# @parameter options [Hash] Options to pass to the server instance.
59+
def self.server(peer, **options)
60+
protocol = protocol_for(peer)
61+
options = options[protocol]
62+
63+
protocol.server(peer, **options)
4364
end
4465

45-
# Supported Application Layer Protocol Negotiation names:
66+
# @returns [Array] The names of the supported protocol, used for Application Layer Protocol Negotiation (ALPN).
4667
def self.names
4768
HANDLERS.keys.compact
4869
end

0 commit comments

Comments
 (0)