require "net/http" require "uri" require_relative "result" module Relay # The single place outbound HTTP happens. Timeouts, TLS policy, and # instrumentation live here so call sites cannot drift. class HttpClient OPEN_TIMEOUT = 5 READ_TIMEOUT = 5 def post(url, body, headers = {}) uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = uri.scheme == "https" http.open_timeout = OPEN_TIMEOUT http.read_timeout = READ_TIMEOUT request = Net::HTTP::Post.new(uri.request_uri, default_headers.merge(headers)) request.body = body response = http.request(request) if response.is_a?(Net::HTTPSuccess) Result.ok(response) else Result.err("HTTP #{response.code} from #{uri.host}") end rescue SystemCallError, Net::OpenTimeout, Net::ReadTimeout => e Result.err("#{e.class}: #{e.message}") end private def default_headers { "Content-Type" => "application/json", "User-Agent" => "relay/1.0" } end end end