Skip to content

Commit

Permalink
add request + response hooks w/example
Browse files Browse the repository at this point in the history
  • Loading branch information
zarqman committed Aug 25, 2023
1 parent e265f3f commit 6356867
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 0 deletions.
74 changes: 74 additions & 0 deletions examples/timeouts/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2023, by Thomas Morgan.

$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
# $:.unshift File.expand_path '../../../protocol-http1/lib', __dir__

require 'async'
require 'async/http/client'
require 'async/http/endpoint'

CONNECT_TIMEOUT = 3
IDLE_TIMEOUT = 90
READ_WRITE_TIMEOUT = 5
RESPONSE_WAIT_TIMEOUT = 55

METRICS = {
requests_sent: 0,
responses_received: 0,
}

module HTTP1Timeouts

def sending_request
stream.io.timeout = READ_WRITE_TIMEOUT

# To count after the request has been fully written, move this into waiting_for_response.
METRICS[:requests_sent] += 1
end

def waiting_for_response
# Upstreams sometimes take a while to process a request and begin the response.
# This allows extra time at that stage.
stream.io.timeout = RESPONSE_WAIT_TIMEOUT
end

def received_response
# Return to a shorter timeout for reading the body, if any.
stream.io.timeout = READ_WRITE_TIMEOUT

METRICS[:responses_received] += 1
end

end

module HTTP2Timeouts

def connection_ready
# To facilitate keepalive, use IDLE_TIMEOUT instead of READ_WRITE_TIMEOUT
stream.io.timeout = IDLE_TIMEOUT
end

end

Protocol::HTTP1::Connection.include HTTP1Timeouts
Async::HTTP::Protocol::HTTP2::Client.include HTTP2Timeouts

endpoint = Async::HTTP::Endpoint.parse('http://127.0.0.1:8080', reuse_port: true, timeout: CONNECT_TIMEOUT)


protocol = Async::HTTP::Protocol::HTTP1
# protocol = Async::HTTP::Protocol::HTTP2

puts "Making request with #{protocol}..."
Async do |task|
client = Async::HTTP::Client.new(endpoint, protocol: protocol)
response = client.get(endpoint.path)
puts response.read
ensure
client.close
end

puts METRICS
77 changes: 77 additions & 0 deletions examples/timeouts/server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2023, by Thomas Morgan.

$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
# $:.unshift File.expand_path '../../../protocol-http1/lib', __dir__

require 'async'
require 'async/http/server'
require 'async/http/endpoint'

IDLE_TIMEOUT = 90
READ_WRITE_TIMEOUT = 5

METRICS = {
requests_received: 0,
responses_sent: 0,
}

module HTTP1Timeouts

def waiting_for_request
if count == 0
# First request for this connection
stream.io.timeout = READ_WRITE_TIMEOUT
else
# Additional requests; aka keep-alive
stream.io.timeout = IDLE_TIMEOUT
end
end

def receiving_request
# Client must send the request headers and body, if any, in a timely manner.
stream.io.timeout = READ_WRITE_TIMEOUT

METRICS[:requests_received] += 1
end

def sent_response
# alternate location for:
# stream.io.timeout = IDLE_TIMEOUT

METRICS[:responses_sent] += 1
end

end

module HTTP2Timeouts

def connection_ready
stream.io.timeout = IDLE_TIMEOUT
end

end

Protocol::HTTP1::Connection.include HTTP1Timeouts
Async::HTTP::Protocol::HTTP2::Server.include HTTP2Timeouts

endpoint = Async::HTTP::Endpoint.parse('http://127.0.0.1:8080', reuse_port: true, timeout: READ_WRITE_TIMEOUT)


protocol = Async::HTTP::Protocol::HTTP1
# protocol = Async::HTTP::Protocol::HTTP2

puts "Accepting #{protocol}..."
begin
Async do
server = Async::HTTP::Server.for(endpoint, protocol: protocol) do |request|
Protocol::HTTP::Response[200, {}, 'response from server']
end
server.run
end
rescue Interrupt
end

puts METRICS
2 changes: 2 additions & 0 deletions lib/async/http/protocol/http1/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ def each(task: Task.current)
# Gracefully finish reading the request body if it was not already done so.
request&.finish

sent_response if respond_to?(:sent_response)

# This ensures we yield at least once every iteration of the loop and allow other fibers to execute.
task.yield
rescue => error
Expand Down
2 changes: 2 additions & 0 deletions lib/async/http/protocol/http2/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ def http2?
end

def start_connection
connection_ready if respond_to?(:connection_ready)

@reader || read_in_background
end

Expand Down

0 comments on commit 6356867

Please sign in to comment.