diff --git a/config/initializers/web_push.rb b/config/initializers/web_push.rb index 6914dae22..4487268f5 100644 --- a/config/initializers/web_push.rb +++ b/config/initializers/web_push.rb @@ -20,7 +20,8 @@ module WebPush::PersistentRequest endpoint_ip = @options[:endpoint_ip] if endpoint_ip - http = Net::HTTP.new(uri.host, uri.port, ipaddr: endpoint_ip) + http = Net::HTTP.new(uri.host, uri.port) + http.ipaddr = endpoint_ip http.use_ssl = true http.ssl_timeout = @options[:ssl_timeout] unless @options[:ssl_timeout].nil? http.open_timeout = @options[:open_timeout] unless @options[:open_timeout].nil? diff --git a/test/lib/web_push/persistent_request_test.rb b/test/lib/web_push/persistent_request_test.rb new file mode 100644 index 000000000..2cf8a6c7c --- /dev/null +++ b/test/lib/web_push/persistent_request_test.rb @@ -0,0 +1,26 @@ +require "test_helper" + +class WebPush::PersistentRequestTest < ActiveSupport::TestCase + PUBLIC_TEST_IP = "142.250.185.206" + ENDPOINT = "https://fcm.googleapis.com/fcm/send/test123" + + test "pins connection to endpoint_ip" do + request = stub_request(:post, ENDPOINT) + .with(ipaddr: PUBLIC_TEST_IP) + .to_return(status: 201) + + notification = WebPush::Notification.new( + title: "Test", + body: "Test notification", + path: "/test", + badge: 0, + endpoint: ENDPOINT, + endpoint_ip: PUBLIC_TEST_IP, + p256dh_key: "BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA_0QTpQtUbVlUls0VJXg7A8u-Ts1XbjhazAkj7I99e8QcYP7DkM", + auth_key: "tBHItJI5svbpez7KI4CCXg" + ) + notification.deliver + + assert_requested request + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 14893b030..b1f813761 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -3,6 +3,7 @@ require_relative "../config/environment" require "rails/test_help" require "webmock/minitest" +require_relative "webmock_ipaddr_extension" require "vcr" require "mocha/minitest" require "turbo/broadcastable/test_helper" diff --git a/test/webmock_ipaddr_extension.rb b/test/webmock_ipaddr_extension.rb new file mode 100644 index 000000000..874654f87 --- /dev/null +++ b/test/webmock_ipaddr_extension.rb @@ -0,0 +1,44 @@ +# Extends WebMock to support ipaddr matching for testing IP pinning. +# +# Usage: +# stub_request(:post, "https://example.com/push") +# .with(ipaddr: "93.184.216.34") +# .to_return(status: 201) +# +# If the HTTP connection's ipaddr doesn't match, the stub won't match and +# WebMock will raise an error about an unregistered request. + +module WebMock + class RequestSignature + attr_accessor :ipaddr + end + + module RequestPatternIpaddrExtension + attr_accessor :ipaddr_pattern + + def assign_options(options) + options = options.dup + @ipaddr_pattern = options.delete(:ipaddr) || options.delete("ipaddr") + super(options) + end + + def matches?(request_signature) + super && ipaddr_matches?(request_signature) + end + + private + def ipaddr_matches?(request_signature) + @ipaddr_pattern.nil? || @ipaddr_pattern == request_signature.ipaddr + end + end + + RequestPattern.prepend RequestPatternIpaddrExtension + + module NetHTTPUtilityIpaddrExtension + def request_signature_from_request(net_http, request, body = nil) + super.tap { |signature| signature.ipaddr = net_http.ipaddr } + end + end + + NetHTTPUtility.singleton_class.prepend NetHTTPUtilityIpaddrExtension +end