Class: Collavre::LinkPreviewFetcher::PinnedHttpClient
- Inherits:
-
Object
- Object
- Collavre::LinkPreviewFetcher::PinnedHttpClient
- Defined in:
- app/services/collavre/link_preview_fetcher.rb
Overview
Performs a single (non-redirect-following) GET against a pre-resolved IP
while keeping the original hostname for the Host header and TLS SNI/cert
verification. Net::HTTP#ipaddr= pins the socket to ip, so no second DNS
lookup happens between validation and connect. Enforces the caller's open/
read timeouts and byte cap.
Instance Method Summary collapse
Instance Method Details
#get(uri, ip:, headers:, open_timeout:, read_timeout:, max_bytes:) ⇒ Object
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'app/services/collavre/link_preview_fetcher.rb', line 276 def get(uri, ip:, headers:, open_timeout:, read_timeout:, max_bytes:) # Pass an explicit nil proxy: Net::HTTP.new defaults p_addr to :ENV, so a # set http_proxy/HTTP_PROXY would route through the proxy, which resolves # the hostname itself and defeats `http.ipaddr = ip` pinning — reopening # the DNS-rebinding/SSRF gap. nil forces a direct, pinned connection. http = Net::HTTP.new(uri.hostname, uri.port, nil) http.use_ssl = uri.scheme == "https" http.ipaddr = ip http.open_timeout = open_timeout http.read_timeout = read_timeout request = Net::HTTP::Get.new(uri, headers) response_struct = nil http.start do |conn| conn.request(request) do |response| body = read_capped_body(response, max_bytes) response_struct = Response.new( code: response.code.to_i, content_type: response.content_type, body: body, location: response["location"] ) end end response_struct ensure http&.finish if http&.started? end |