Open pooled push connections as a Net::HTTP subclass that includes Stages

WebPush::Connections mixed Stages into each new connection with
`http.extend Stages` just before starting it. The connection is built in
one place, WebPush::PersistentRequest, so it can be built from a class
that already includes Stages: the same method lookup, without giving
every pooled connection its own singleton class. Connections#request
now also refuses a connection that isn't one, so a caller that builds a
plain Net::HTTP gets the ArgumentError rather than a connection whose
failures can't be told apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sam RubyandClaude Opus 5.5 authored and Rosa Gutierrez committed 2026-10-09 23:41:45 +02:00
1 parent 2d4b05a74a
commit 2daa01e26b
3 files changed
+9 -4

No files matched your search

+1 -1
View File
@@ -29,7 +29,7 @@ module WebPush::PersistentRequest
# itself and re-resolve the endpoint host, so http.ipaddr would no longer
# pin the destination and the DNS-rebinding guarantee would be lost.
# Push delivery to public vendor endpoints goes direct.
http = Net::HTTP.new(uri.host, uri.port, nil)
http = (@options[:connection] ? WebPush::Connections::HTTP : Net::HTTP).new(uri.host, uri.port, nil)
http.ipaddr = endpoint_ip
http.use_ssl = true
http.ssl_timeout = @options[:ssl_timeout] unless @options[:ssl_timeout].nil?
+7 -2
View File
@@ -26,6 +26,12 @@ class WebPush::Connections
end
end
# The Net::HTTP a pooled delivery is opened with: Stages is part of the class, so a connection carries its stage
# from the start instead of having the module mixed into each one as it's opened.
class HTTP < Net::HTTP
include Stages
end
def initialize(keep_alive_timeout: 30, max_idle: 150)
@keep_alive_timeout = keep_alive_timeout
@max_idle = max_idle
@@ -35,7 +41,7 @@ class WebPush::Connections
end
def request(http, request)
unless http.ipaddr && !http.proxy? && http.use_ssl? && !http.started?
unless http.is_a?(HTTP) && http.ipaddr && !http.proxy? && http.use_ssl? && !http.started?
raise ArgumentError, "Only new, direct TLS connections pinned to an address are pooled"
end
address = [ http.address, http.port, http.ipaddr ]
@@ -48,7 +54,6 @@ class WebPush::Connections
end
end
http.extend Stages
http.keep_alive_timeout = @keep_alive_timeout
http.start
send_over(http, request, reused: false).tap { checkin(address, http) }
@@ -109,7 +109,7 @@ module PushServiceTestHelper
# Built as WebPush::PersistentRequest builds it for a delivery.
def pinned_connection(server, ip = IP)
Net::HTTP.new(HOST, server.port, nil).tap do |http|
WebPush::Connections::HTTP.new(HOST, server.port, nil).tap do |http|
http.ipaddr = ip
http.use_ssl = true
end