diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml new file mode 100644 index 00000000..4b73eaaa --- /dev/null +++ b/.github/workflows/development.yml @@ -0,0 +1,52 @@ +name: Development + +on: [push, pull_request] + +jobs: + test: + runs-on: ${{matrix.os}}-latest + continue-on-error: ${{matrix.experimental}} + + strategy: + matrix: + experimental: [false] + + os: + - ubuntu + - macos + + ruby: + - 2.5 + - 2.6 + - 2.7 + + include: + - experimental: true + os: ubuntu + ruby: truffleruby + - experimental: true + os: ubuntu + ruby: jruby + - experimental: true + os: ubuntu + ruby: head + - experimental: true + os: ubuntu + ruby: 2.6 + env: COVERAGE=PartialSummary,Coveralls + + steps: + - uses: actions/checkout@v1 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + + - name: Installing packages (ubuntu) + if: matrix.os == 'ubuntu' + run: sudo apt-get install apache2-utils + + - name: Install dependencies + run: ${{matrix.env}} bundle install + + - name: Run tests + run: ${{matrix.env}} bundle exec rspec diff --git a/async-http.gemspec b/async-http.gemspec index e4f2b3b8..2200a718 100644 --- a/async-http.gemspec +++ b/async-http.gemspec @@ -17,13 +17,13 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f)} spec.require_paths = ["lib"] - spec.add_dependency("async", "~> 1.23") + spec.add_dependency("async", "~> 1.25") spec.add_dependency("async-io", "~> 1.28") spec.add_dependency("async-pool", "~> 0.2") spec.add_dependency("protocol-http", "~> 0.18.0") spec.add_dependency("protocol-http1", "~> 0.12.0") - spec.add_dependency("protocol-http2", "~> 0.13.0") + spec.add_dependency("protocol-http2", "~> 0.14.0") # spec.add_dependency("openssl") diff --git a/lib/async/http/body/pipe.rb b/lib/async/http/body/pipe.rb index 4ac02229..266d9da3 100644 --- a/lib/async/http/body/pipe.rb +++ b/lib/async/http/body/pipe.rb @@ -60,7 +60,7 @@ def close def reader(task) @reader = task - task.annotate "pipe reader" + task.annotate "#{self.class} reader." while chunk = @input.read @head.write(chunk) @@ -80,7 +80,7 @@ def reader(task) def writer(task) @writer = task - task.annotate "pipe writer" + task.annotate "#{self.class} writer." while chunk = @head.read_partial @output.write(chunk) diff --git a/lib/async/http/client.rb b/lib/async/http/client.rb index 17e4dc1f..837f9c2b 100755 --- a/lib/async/http/client.rb +++ b/lib/async/http/client.rb @@ -83,7 +83,7 @@ def self.open(*arguments, **options, &block) def close while @pool.busy? - Async.logger.warn(self) {"Waiting for pool to drain: #{@pool}"} + Async.logger.warn(self) {"Waiting for #{@protocol} pool to drain: #{@pool}"} @pool.wait end diff --git a/lib/async/http/protocol/http1/server.rb b/lib/async/http/protocol/http1/server.rb index 902d4cbd..ada8484a 100644 --- a/lib/async/http/protocol/http1/server.rb +++ b/lib/async/http/protocol/http1/server.rb @@ -45,7 +45,9 @@ def next_request return request rescue Async::TimeoutError - fail_request(408) + # For an interesting discussion about this behaviour, see https://trac.nginx.org/nginx/ticket/1005 + # If you enable this, you will see some spec failures... + # fail_request(408) raise rescue fail_request(400) @@ -54,6 +56,8 @@ def next_request # Server loop. def each(task: Task.current) + task.annotate("Reading #{version} requests for #{self.class}.") + while request = next_request response = yield(request, self) diff --git a/lib/async/http/protocol/http2/connection.rb b/lib/async/http/protocol/http2/connection.rb index 0d381635..9f126d85 100644 --- a/lib/async/http/protocol/http2/connection.rb +++ b/lib/async/http/protocol/http2/connection.rb @@ -66,7 +66,7 @@ def http2? end def start_connection - @reader ||= read_in_background + @reader || read_in_background end def close(error = nil) @@ -93,7 +93,11 @@ def write_frames(&block) end def read_in_background(parent: Task.current) - parent.async do |task| + raise RuntimeError, "Connection is closed!" if closed? + + parent.async(transient: true) do |task| + @reader = task + task.annotate("#{version} reading data for #{self.class}.") begin @@ -130,7 +134,7 @@ def viable? end def reusable? - !(self.closed? || @stream.closed?) + !self.closed? end def version diff --git a/lib/async/http/protocol/http2/server.rb b/lib/async/http/protocol/http2/server.rb index f3b8ea7c..3db051fe 100644 --- a/lib/async/http/protocol/http2/server.rb +++ b/lib/async/http/protocol/http2/server.rb @@ -52,17 +52,18 @@ def accept_stream(stream_id) end def close(error = nil) - # This invokes Framer#close which closes the stream: - super - if @requests # Stop the request loop: - @requests.enqueue nil + @requests.enqueue(nil) @requests = nil end + + super end - def each + def each(task: Task.current) + task.annotate("Reading #{version} requests for #{self.class}.") + # It's possible the connection has died before we get here... @requests&.async do |task, request| task.annotate("Incoming request: #{request.method} #{request.path.inspect}.") @@ -80,6 +81,8 @@ def each request.send_response(response) end end + + # Maybe we should add some synchronisation here - i.e. only exit once all requests are finished. end end end diff --git a/lib/async/http/version.rb b/lib/async/http/version.rb index 3d208fb8..a8a6f162 100644 --- a/lib/async/http/version.rb +++ b/lib/async/http/version.rb @@ -22,6 +22,6 @@ module Async module HTTP - VERSION = "0.51.2" + VERSION = "0.52.0" end end diff --git a/spec/async/http/protocol/shared_examples.rb b/spec/async/http/protocol/shared_examples.rb index 8db26a0a..6c7326dd 100644 --- a/spec/async/http/protocol/shared_examples.rb +++ b/spec/async/http/protocol/shared_examples.rb @@ -167,6 +167,25 @@ expect(server.scheme).to be == "http" end + it "disconnects slow clients" do + response = client.get("/") + response.read + + # We expect this connection to be closed: + connection = response.connection + + reactor.sleep(1.0) + + response = client.get("/") + response.read + + expect(connection).to_not be_reusable + + # client.close + # reactor.sleep(0.1) + # reactor.print_hierarchy + end + context 'using GET method' do let(:expected) {"GET #{protocol::VERSION}"} diff --git a/spec/async/http/proxy_spec.rb b/spec/async/http/proxy_spec.rb index 41ea475e..e7e3c09d 100644 --- a/spec/async/http/proxy_spec.rb +++ b/spec/async/http/proxy_spec.rb @@ -142,23 +142,25 @@ upstream = Async::IO::Stream.new(endpoint.connect) Async.logger.debug(self) {"Connected to #{upstream}..."} - reader = Async do + reader = Async do |task| + task.annotate "Upstream reader." + while chunk = upstream.read_partial stream.write(chunk) stream.flush end - ensure Async.logger.debug(self) {"Finished reading from upstream..."} stream.close_write end - writer = Async do + writer = Async do |task| + task.annotate "Upstream writer." + while chunk = stream.read_partial upstream.write(chunk) upstream.flush end - rescue Async::Wrapper::Cancelled #ignore ensure diff --git a/spec/async/http/server_context.rb b/spec/async/http/server_context.rb index 12e9e5d9..80251bd4 100644 --- a/spec/async/http/server_context.rb +++ b/spec/async/http/server_context.rb @@ -26,7 +26,7 @@ include_context Async::RSpec::Reactor let(:protocol) {described_class} - let(:endpoint) {Async::HTTP::Endpoint.parse('http://127.0.0.1:9294', reuse_port: true)} + let(:endpoint) {Async::HTTP::Endpoint.parse('http://127.0.0.1:9294', timeout: 0.8, reuse_port: true)} let(:retries) {1} let!(:client) {Async::HTTP::Client.new(endpoint, protocol, retries: retries)} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy