Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
ioquatix authored Jun 10, 2024
2 parents 7ab4bfe + ee0a558 commit 791cb0d
Show file tree
Hide file tree
Showing 17 changed files with 126 additions and 41 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ root = true
[*]
indent_style = tab
indent_size = 2

[*.{yml,yaml}]
indent_style = space
indent_size = 2
10 changes: 5 additions & 5 deletions .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ jobs:
- macos

ruby:
- "3.2"
- "3.3"

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{matrix.ruby}}
Expand All @@ -34,7 +34,7 @@ jobs:
timeout-minutes: 5
run: bundle exec bake test

- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: coverage-${{matrix.os}}-${{matrix.ruby}}
path: .covered.db
Expand All @@ -44,10 +44,10 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
ruby-version: "3.3"
bundler-cache: true

- uses: actions/download-artifact@v3
Expand Down
11 changes: 4 additions & 7 deletions .github/workflows/documentation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ on:
branches:
- main

# Allows you to run this workflow manually from the Actions tab:
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages:
permissions:
contents: read
Expand All @@ -28,11 +25,11 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
ruby-version: "3.3"
bundler-cache: true

- name: Installing packages
Expand All @@ -43,7 +40,7 @@ jobs:
run: bundle exec bake utopia:project:static --force no

- name: Upload documentation artifact
uses: actions/upload-pages-artifact@v1
uses: actions/upload-pages-artifact@v3
with:
path: docs

Expand All @@ -58,4 +55,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
uses: actions/deploy-pages@v4
4 changes: 2 additions & 2 deletions .github/workflows/test-external.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ jobs:
- macos

ruby:
- "3.0"
- "3.1"
- "3.2"
- "3.3"

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{matrix.ruby}}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ jobs:
- macos

ruby:
- "3.0"
- "3.1"
- "3.2"
- "3.3"

experimental: [false]

Expand All @@ -39,7 +39,7 @@ jobs:
experimental: true

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{matrix.ruby}}
Expand Down
3 changes: 1 addition & 2 deletions gems.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2019-2023, by Samuel Williams.
# Copyright, 2019-2024, by Samuel Williams.

source "https://rubygems.org"

Expand All @@ -11,7 +11,6 @@
gem "bake-modernize"
gem "bake-gem"

gem "bake-github-pages"
gem "utopia-project"
end

Expand Down
31 changes: 26 additions & 5 deletions lib/protocol/http2/connection.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2019-2023, by Samuel Williams.
# Copyright, 2019-2024, by Samuel Williams.
# Copyright, 2023, by Marco Concetto Rudilosso.

require_relative 'framer'
Expand Down Expand Up @@ -62,8 +62,9 @@ def maximum_frame_size
@remote_settings.maximum_frame_size
end

# The maximum number of concurrent streams that this connection can initiate:
def maximum_concurrent_streams
@local_settings.maximum_concurrent_streams
@remote_settings.maximum_concurrent_streams
end

attr :framer
Expand Down Expand Up @@ -143,9 +144,14 @@ def ignore_frame?(frame)
end
end

def synchronize
yield
end

# Reads one frame from the network and processes. Processing the frame updates the state of the connection and related streams. If the frame triggers an error, e.g. a protocol error, the connection will typically emit a goaway frame and re-raise the exception. You should continue processing frames until the underlying connection is closed.
def read_frame
frame = @framer.read_frame(@local_settings.maximum_frame_size)

# puts "#{self.class} #{@state} read_frame: class=#{frame.class} stream_id=#{frame.stream_id} flags=#{frame.flags} length=#{frame.length} (remote_stream_id=#{@remote_stream_id})"
# puts "Windows: local_window=#{@local_window.inspect}; remote_window=#{@remote_window.inspect}"

Expand Down Expand Up @@ -207,11 +213,25 @@ def receive_goaway(frame)
end

def write_frame(frame)
@framer.write_frame(frame)
synchronize do
@framer.write_frame(frame)
end

# The IO is already synchronized, and we don't want additional contention.
@framer.flush
end

def write_frames
yield @framer
if @framer
synchronize do
yield @framer
end

# See above note.
@framer.flush
else
raise EOFError, "Connection closed!"
end
end

def update_local_settings(changes)
Expand Down Expand Up @@ -370,7 +390,8 @@ def receive_headers(frame)
raise ProtocolError, "Invalid stream id: #{stream_id} <= #{@remote_stream_id}!"
end

if @streams.size < self.maximum_concurrent_streams
# We need to validate that we have less streams than the specified maximum:
if @streams.size < @local_settings.maximum_concurrent_streams
stream = accept_stream(stream_id)
@remote_stream_id = stream_id

Expand Down
18 changes: 11 additions & 7 deletions lib/protocol/http2/framer.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2019-2023, by Samuel Williams.
# Copyright, 2019-2024, by Samuel Williams.


require "async/io/stream"
Expand Down Expand Up @@ -49,6 +49,10 @@ def initialize(stream, frames = FRAMES)
@frames = frames
end

def flush
@stream.flush
end

def close
@stream.close
end
Expand Down Expand Up @@ -77,7 +81,7 @@ def read_frame(maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE)
# Read the header:
length, type, flags, stream_id = read_header

# Async.logger.debug(self) {"read_frame: length=#{length} type=#{type} flags=#{flags} stream_id=#{stream_id} -> klass=#{@frames[type].inspect}"}
# Console.debug(self) {"read_frame: length=#{length} type=#{type} flags=#{flags} stream_id=#{stream_id} -> klass=#{@frames[type].inspect}"}

# Allocate the frame:
klass = @frames[type] || Frame
Expand All @@ -86,19 +90,19 @@ def read_frame(maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE)
# Read the payload:
frame.read(@stream, maximum_frame_size)

# Async.logger.debug(self, name: "read") {frame.inspect}
# Console.debug(self, name: "read") {frame.inspect}

return frame
end

# Write a frame to the underlying IO.
# After writing one or more frames, you should call flush to ensure the frames are sent to the remote peer.
# @parameter frame [Frame] the frame to write.
def write_frame(frame)
# Async.logger.debug(self, name: "write") {frame.inspect}
# Console.debug(self, name: "write") {frame.inspect}

frame.write(@stream)

# Don't call @stream.flush here because it can cause significant contention if there is a semaphore around this method.
# @stream.flush

return frame
end

Expand Down
6 changes: 4 additions & 2 deletions lib/protocol/http2/server.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2019-2023, by Samuel Williams.
# Copyright, 2019-2024, by Samuel Williams.

require_relative 'connection'

Expand Down Expand Up @@ -31,7 +31,9 @@ def read_connection_preface(settings = [])
send_settings(settings)

read_frame do |frame|
raise ProtocolError, "First frame must be #{SettingsFrame}, but got #{frame.class}" unless frame.is_a? SettingsFrame
unless frame.is_a? SettingsFrame
raise ProtocolError, "First frame must be #{SettingsFrame}, but got #{frame.class}"
end
end
else
raise ProtocolError, "Cannot read connection preface in state #{@state}"
Expand Down
4 changes: 2 additions & 2 deletions lib/protocol/http2/version.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2019-2023, by Samuel Williams.
# Copyright, 2019-2024, by Samuel Williams.

module Protocol
module HTTP2
VERSION = "0.15.1"
VERSION = "0.18.0"
end
end
2 changes: 1 addition & 1 deletion license.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MIT License

Copyright, 2019-2023, by Samuel Williams.
Copyright, 2019-2024, by Samuel Williams.
Copyright, 2019, by Yuta Iwama.
Copyright, 2020, by Olle Jonsson.
Copyright, 2023, by Marco Concetto Rudilosso.
Expand Down
7 changes: 6 additions & 1 deletion protocol-http2.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ Gem::Specification.new do |spec|

spec.homepage = "https://github.com/socketry/protocol-http2"

spec.metadata = {
"documentation_uri" => "https://socketry.github.io/protocol-http2/",
"source_code_uri" => "https://github.com/socketry/protocol-http2.git",
}

spec.files = Dir.glob(['{lib}/**/*', '*.md'], File::FNM_DOTMATCH, base: __dir__)

spec.required_ruby_version = ">= 2.7.6"
spec.required_ruby_version = ">= 3.1"

spec.add_dependency "async-io", "~> 1.37"
spec.add_dependency "protocol-hpack", "~> 1.4"
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,4 @@ This project uses the [Developer Certificate of Origin](https://developercertifi

### Contributor Covenant

This project is governed by [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
34 changes: 33 additions & 1 deletion test/protocol/http2/connection.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2019-2023, by Samuel Williams.
# Copyright, 2019-2024, by Samuel Williams.
# Copyright, 2023, by Marco Concetto Rudilosso.

require 'connection_context'
Expand All @@ -11,6 +11,29 @@
let(:framer) {Protocol::HTTP2::Framer.new(stream)}
let(:connection) {subject.new(framer, 1)}

with "#maximum_concurrent_streams" do
it "is the remote peer's maximum concurrent streams" do
connection.remote_settings.maximum_concurrent_streams = 10
connection.local_settings.current.maximum_concurrent_streams = 5

expect(connection.maximum_concurrent_streams).to be == 10
end
end

with '#receive_headers' do
it "fails with protocol error if exceeding the maximum concurrent connections" do
connection.local_settings.current.maximum_concurrent_streams = 1
# Create a stream to
connection.streams[1] = Protocol::HTTP2::Stream.new(connection, 1)

frame = Protocol::HTTP2::HeadersFrame.new(3)

expect do
connection.receive_headers(frame)
end.to raise_exception(Protocol::HTTP2::ProtocolError, message: be =~ /Exceeded maximum concurrent streams/)
end
end

it "reports the connection id 0 is not closed" do
expect(connection).not.to be(:closed_stream_id?, 0)
end
Expand Down Expand Up @@ -65,6 +88,15 @@
connection.read_frame
end.to raise_exception(Protocol::HPACK::Error)
end

it "can't write frames to a closed connection" do
connection.close

expect do
connection.write_frames do |framer|
end
end.to raise_exception(EOFError, message: be =~ /Connection closed/)
end
end

with 'client and server' do
Expand Down
Loading

0 comments on commit 791cb0d

Please sign in to comment.