Skip to content

Expose optional peer TLS certificate information on responses #125

Description

@ZilvinasKucinskas

Priority: P2

Describe the issue

Rust wreq can attach wreq::tls::TlsInfo to a response when TLS information is enabled on the client. The structure contains the DER-encoded peer certificate and peer certificate chain. wreq-ruby does not expose the builder option or response extension.

This is useful for diagnosing TLS interception, proxy certificate substitution, trust-chain failures, and origin-specific certificate behavior without enabling verbose wire logs. It is also a direct binding feature: unlike DNS/connect/TLS phase timings or a connection_reused? flag, the required data already exists in Rust wreq.

Proposed Ruby API

client = Wreq::Client.new(tls_info: true)
response = client.get("https://example.com")

info = response.tls_info
info.peer_certificate
# => DER-encoded binary String or nil

info.peer_certificate_chain
# => Array of DER-encoded binary Strings, or nil

response.tls_info should return nil when collection was not enabled, the response did not use TLS, or the native transport has no TLS information. When present, return a small immutable Wreq::TlsInfo value object, matching the gem's existing Wreq::TlsError constant style.

Data and ownership semantics

  • peer_certificate is the DER-encoded leaf certificate as a binary Ruby String (Encoding::BINARY) or nil.
  • peer_certificate_chain is a frozen Array of binary DER strings or nil. It includes the leaf certificate when Rust wreq supplies it that way.
  • Returned data must remain valid independently of response-body consumption and connection-pool reuse.
  • Repeated calls may return the same object or equivalent immutable values; object identity is not part of the API.
  • Certificate parsing, subject/issuer helpers, fingerprint formatting, and OpenSSL object construction are outside the binding's first scope. Callers can pass DER bytes to OpenSSL::X509::Certificate when needed.
  • Collection stays opt-in because retaining certificate-chain bytes has a cost.

Deliberately out of scope

This issue does not request:

  • DNS/connect/TLS/TTFB/download phase timings;
  • a connection-reuse flag;
  • copies of emitted request headers or browser-profile state;
  • client certificates or mTLS configuration; or
  • TLS key logging.

Those need separate upstream support, security review, or a concrete use case. Total request duration can already be measured accurately by the Ruby caller with a monotonic clock.

Acceptance criteria

  • Wreq::Client.new(tls_info: true) enables the existing Rust wreq TLS response extension.
  • Response#tls_info returns Wreq::TlsInfo for an HTTPS response when native information is available.
  • Response#tls_info returns nil predictably when disabled or unavailable.
  • Leaf and chain values are DER-encoded binary strings with documented ownership and immutability.
  • Tests cover enabled, disabled, plain HTTP, leaf certificate, chain, response close/body consumption, and connection reuse.
  • No certificate bytes are included automatically in exception messages, logs, or inspect.

Binding precedent

wreq-python exposes this same Rust feature as opt-in tls_info with peer_certificate, and the underlying Rust TlsInfo also retains the peer certificate chain.

Relevant source

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions