Class: Foam::Otel::IsolatedHttpClient

Inherits:
Object
  • Object
show all
Defined in:
lib/foam/otel/isolated_http_client.rb

Defined Under Namespace

Classes: OpenTimeoutError, ProxyConnectError, ReadTimeoutError, Response, TimeoutError, WriteTimeoutError

Constant Summary collapse

KEEP_ALIVE_TIMEOUT =

seconds a pooled socket may idle, like Net::HTTP's

30
MAX_RESPONSE_BYTES =

Response-size ceiling (fleet-review fix): OTLP success/error bodies are tiny (empty, or a small rpc.Status); a hostile or broken endpoint must not drive unbounded allocation in the app's process.

16 * 1024 * 1024

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri, ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER, certificate_file: nil, client_certificate_file: nil, client_key_file: nil, keep_alive_timeout: KEEP_ALIVE_TIMEOUT) ⇒ IsolatedHttpClient

Returns a new instance of IsolatedHttpClient.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/foam/otel/isolated_http_client.rb', line 80

def initialize(uri, ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER, certificate_file: nil,
               client_certificate_file: nil, client_key_file: nil,
               keep_alive_timeout: KEEP_ALIVE_TIMEOUT)
  @uri = uri
  @host = uri.hostname
  @port = uri.port
  @use_ssl = uri.scheme == "https"
  @ssl_verify_mode = ssl_verify_mode
  @ssl_context = build_ssl_context(ssl_verify_mode, certificate_file,
                                   client_certificate_file, client_key_file) if @use_ssl
  @keep_alive_timeout = keep_alive_timeout
  # Proxy config resolves ONCE at construction (Net::HTTP.new's :ENV
  # behavior) — the exporter lives for the process, like its env config.
  @proxy = resolve_proxy_from_env
  @mutex = Mutex.new
  @socket = nil
  @pid = nil
  @last_used = nil
  @rbuf = +""
end

Instance Attribute Details

#ssl_verify_modeObject (readonly) Also known as: verify_mode

Net::HTTP-facade introspection: the TLS pin audits (F-RB1 specs) read the constructed connection's verify mode; kept as real surface so the pin stays checkable on the isolated client.



104
105
106
# File 'lib/foam/otel/isolated_http_client.rb', line 104

def ssl_verify_mode
  @ssl_verify_mode
end

Instance Method Details

#finishObject



110
111
112
113
# File 'lib/foam/otel/isolated_http_client.rb', line 110

def finish
  @mutex.synchronize { close_socket }
  nil
end

#post(path, body:, headers:, timeout:) ⇒ Object

POST body to path with headers, all within timeout seconds (connect + write + full response read share one deadline). Returns a Response; raises the foam-owned timeout/socket errors above plus OpenSSL::SSL::SSLError / SocketError / SystemCallError / EOFError for the exporters' retry loop to classify.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/foam/otel/isolated_http_client.rb', line 120

def post(path, body:, headers:, timeout:)
  @mutex.synchronize do
    deadline = monotonic_now + timeout
    begin
      reused = ensure_connection(deadline)
      begin
        write_request(path, body, headers, deadline)
        read_response(deadline)
      rescue EOFError, Errno::EPIPE, Errno::ECONNRESET
        # Stale keep-alive socket (server closed between exports): one
        # silent reconnect + retransmit, ONLY when the failed attempt
        # rode a pooled connection. A fresh connection failing is a
        # real error.
        raise unless reused

        close_socket
        ensure_connection(deadline)
        write_request(path, body, headers, deadline)
        read_response(deadline)
      end
    rescue StandardError
      # Fleet-review fix (2026-07-29): ANY transport error — timeout,
      # SSL, syscall, malformed response — poisons the connection
      # (half-written request, partial @rbuf). Net::HTTP closed on
      # every transport error; so does this client. The next post
      # dials fresh instead of desyncing on a dirty stream.
      close_socket
      raise
    end
  end
end

#started?Boolean

Net::HTTP-facade lifecycle bits the exporters' shutdown path calls.

Returns:

  • (Boolean)


108
# File 'lib/foam/otel/isolated_http_client.rb', line 108

def started? = !@socket.nil?