Skip to content

Commit e5380fd

Browse files
committed
add Async::HTTP::Protocol::HTTP to auto-detect h1,h2 for inbound http:// connections
1 parent 586aa14 commit e5380fd

File tree

8 files changed

+100
-6
lines changed

8 files changed

+100
-6
lines changed

lib/async/http/endpoint.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def protocol
8484
if secure?
8585
Protocol::HTTPS
8686
else
87-
Protocol::HTTP1
87+
Protocol::HTTP
8888
end
8989
end
9090
end

lib/async/http/protocol/http.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# frozen_string_literal: true
2+
3+
# Released under the MIT License.
4+
# Copyright, 2023, by Thomas Morgan.
5+
6+
require_relative 'http1'
7+
require_relative 'http2'
8+
9+
module Async
10+
module HTTP
11+
module Protocol
12+
# HTTP is an http:// server that auto-selects HTTP/1.1 or HTTP/2 by detecting the HTTP/2
13+
# connection preface.
14+
module HTTP
15+
HTTP2_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
16+
HTTP2_PREFACE_SIZE = HTTP2_PREFACE.bytesize
17+
18+
def self.protocol_for(stream)
19+
# Detect HTTP/2 connection preface
20+
# https://www.rfc-editor.org/rfc/rfc9113.html#section-3.4
21+
preface = stream.peek do |read_buffer|
22+
if read_buffer.bytesize >= HTTP2_PREFACE_SIZE
23+
break read_buffer[0, HTTP2_PREFACE_SIZE]
24+
elsif read_buffer.bytesize > 0
25+
# If partial read_buffer already doesn't match, no need to wait for more bytes.
26+
break read_buffer unless HTTP2_PREFACE[read_buffer]
27+
end
28+
end
29+
if preface == HTTP2_PREFACE
30+
HTTP2
31+
else
32+
HTTP1
33+
end
34+
end
35+
36+
# Only inbound connections can detect HTTP1 vs HTTP2 for http://.
37+
# Outbound connections default to HTTP1.
38+
def self.client(peer, **options)
39+
HTTP1.client(peer, **options)
40+
end
41+
42+
def self.server(peer, **options)
43+
stream = IO::Stream.new(peer, sync: true)
44+
protocol_for(stream).server(stream, **options)
45+
end
46+
47+
def self.names
48+
["h2", "http/1.1", "http/1.0"]
49+
end
50+
end
51+
end
52+
end
53+
end

lib/async/http/protocol/http1.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# Released under the MIT License.
44
# Copyright, 2017-2023, by Samuel Williams.
5+
# Copyright, 2023, by Thomas Morgan.
56

67
require_relative 'http1/client'
78
require_relative 'http1/server'
@@ -27,7 +28,7 @@ def self.client(peer)
2728
end
2829

2930
def self.server(peer)
30-
stream = IO::Stream.new(peer, sync: true)
31+
stream = peer.is_a?(IO::Stream) ? peer : IO::Stream.new(peer, sync: true)
3132

3233
return HTTP1::Server.new(stream, VERSION)
3334
end

lib/async/http/protocol/http10.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# Released under the MIT License.
44
# Copyright, 2017-2023, by Samuel Williams.
5+
# Copyright, 2023, by Thomas Morgan.
56

67
require_relative 'http1'
78

@@ -26,7 +27,7 @@ def self.client(peer)
2627
end
2728

2829
def self.server(peer)
29-
stream = IO::Stream.new(peer, sync: true)
30+
stream = peer.is_a?(IO::Stream) ? peer : IO::Stream.new(peer, sync: true)
3031

3132
return HTTP1::Server.new(stream, VERSION)
3233
end

lib/async/http/protocol/http11.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Released under the MIT License.
44
# Copyright, 2017-2023, by Samuel Williams.
55
# Copyright, 2018, by Janko Marohnić.
6+
# Copyright, 2023, by Thomas Morgan.
67

78
require_relative 'http1'
89

@@ -27,7 +28,7 @@ def self.client(peer)
2728
end
2829

2930
def self.server(peer)
30-
stream = IO::Stream.new(peer, sync: true)
31+
stream = peer.is_a?(IO::Stream) ? peer : IO::Stream.new(peer, sync: true)
3132

3233
return HTTP1::Server.new(stream, VERSION)
3334
end

lib/async/http/protocol/http2.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# Released under the MIT License.
44
# Copyright, 2018-2023, by Samuel Williams.
5+
# Copyright, 2023, by Thomas Morgan.
56

67
require_relative 'http2/client'
78
require_relative 'http2/server'
@@ -46,7 +47,7 @@ def self.client(peer, settings = CLIENT_SETTINGS)
4647
end
4748

4849
def self.server(peer, settings = SERVER_SETTINGS)
49-
stream = IO::Stream.new(peer, sync: true)
50+
stream = peer.is_a?(IO::Stream) ? peer : IO::Stream.new(peer, sync: true)
5051

5152
server = Server.new(stream)
5253

test/async/http/endpoint.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@
155155

156156
describe Async::HTTP::Endpoint.parse("http://www.google.com/search") do
157157
it "should select the correct protocol" do
158-
expect(subject.protocol).to be == Async::HTTP::Protocol::HTTP1
158+
expect(subject.protocol).to be == Async::HTTP::Protocol::HTTP
159159
end
160160

161161
it "should parse the correct hostname" do

test/async/http/protocol/http.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
3+
# Released under the MIT License.
4+
# Copyright, 2023, by Thomas Morgan.
5+
6+
require 'async/http/protocol/http'
7+
require 'async/http/a_protocol'
8+
9+
describe Async::HTTP::Protocol::HTTP do
10+
with 'server' do
11+
include Sus::Fixtures::Async::HTTP::ServerContext
12+
let(:protocol) {subject}
13+
14+
with 'http11 client' do
15+
it 'should make a successful request' do
16+
response = client.get('/')
17+
expect(response).to be(:success?)
18+
expect(response.version).to be == 'HTTP/1.1'
19+
response.read
20+
end
21+
end
22+
23+
with 'http2 client' do
24+
def make_client(endpoint, **options)
25+
options[:protocol] = Async::HTTP::Protocol::HTTP2
26+
super
27+
end
28+
29+
it 'should make a successful request' do
30+
response = client.get('/')
31+
expect(response).to be(:success?)
32+
expect(response.version).to be == 'HTTP/2'
33+
response.read
34+
end
35+
end
36+
end
37+
end

0 commit comments

Comments
 (0)