Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AI Background
ref CLOUDPT-11160
ref EFT-3141
Add ForceResolveHost to ServiceEndPoint for SNI-preserving address override
Background: TLS and SNI
When a client opens a TLS (HTTPS) connection, it performs a handshake before any application data is exchanged. Part of that handshake is the client announcing — in plaintext — which hostname it wants to talk to. This field is called Server Name Indication (SNI). The
server uses SNI to decide which TLS certificate to present, which is essential when many hostnames are served from the same IP address.
The key point: the hostname used for SNI is separate from the IP address the TCP connection is made to. DNS normally ties these together (you look up the hostname, get an IP, connect, and also use that hostname for SNI), but they are fundamentally independent.
Background: curl's --resolve
curl has a flag that exploits this separation:
curl --resolve polling.example.com:10943:127.0.0.1 https://polling.example.com:10943
This tells curl: "when you need to TCP-connect to polling.example.com:10943, use 127.0.0.1 instead of doing a real DNS lookup — but still send polling.example.com as the SNI hostname in the TLS handshake."
This is useful when you want to route traffic through a local tool (like a proxy or traffic shaper) while ensuring the remote server still sees the correct SNI and can respond with the right certificate.
The problem
Halibut derives both the TCP connect address and the TLS SNI hostname from ServiceEndPoint.BaseUri. There was no way to connect to a different address (e.g. a local Toxiproxy instance) while keeping the correct SNI hostname, without corrupting BaseUri to point at the
proxy — which misrepresents the real remote endpoint.
This matters in testing scenarios where Toxiproxy sits in front of a remote server to simulate network conditions (latency, disconnects, bandwidth limits). The TLS layer is still terminated at the real remote server, so SNI must match the real hostname.
This change
Adds a ForceResolveHost property to ServiceEndPoint:
var endpoint = new ServiceEndPoint(
"https://polling.example.com:10943", // real remote — used for SNI
remoteThumbprint,
halibutTimeoutsAndLimits);
endpoint.ForceResolveHost = new Uri("https://127.0.0.1:7778"); // connect here instead
When set, TcpConnectionFactory uses ForceResolveHost's host and port for the TCP connection, while the TLS SNI target remains BaseUri.Host. BaseUri continues to accurately represent the real remote endpoint for logging, identification, and certificate validation
purposes.
This is the Halibut equivalent of curl --resolve.
How to review this PR
Quality ✔️
Pre-requisites