-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to get the socket information from HttpClient request? #63159
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsHow to get the socket information(such as source ip address and target ip address) from HttpResponseMessage? If it is not currently supported, can it be provided in the next version?
|
e.g: [HttpGet]
public string Get()
{
var feature = Request.HttpContext.Features.Get<IHttpConnectionFeature>();
//return feature.LocalIpAddress.ToString();
return feature.RemoteIpAddress.ToString();
} Good luck. |
Is http client request, not asp.net/asp.net core. |
Can you explain why you care about this information? edit: To answer the original question, there's no way to get this info from HttpResponseMessage currently. We could consider adding this in the future, but to do so we need to understand the motivation for it. |
.Net 6 using System.Net.Sockets;
SocketsHttpHandler handler = new SocketsHttpHandler();
handler.ConnectCallback = async (context, cancellationToken) =>
{
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.NoDelay = true;
try
{
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false);
Console.WriteLine(context.DnsEndPoint);
Console.WriteLine(socket.RemoteEndPoint.ToString()); //IP Address
return new NetworkStream(socket, true);
}
catch
{
socket.Dispose();
throw;
}
};
using (var httpClient = new HttpClient(handler))
{
var result = await httpClient.GetAsync("https://www.google.com");
} |
Some hosts hava many ip, I need to know which ip failed when accessing. |
Re: @zxzhuty Not only cannot connect, but also should get the infomation when status code 4XX, 5XX |
audit logs was big one I have seen in the past @geoffkizer. Either for regulatory reasons or for troubleshooting. |
Tagging subscribers to this area: @dotnet/ncl Issue DetailsHow to get the socket information(such as source ip address and target ip address) from HttpResponseMessage or related exception? If it is not currently supported, can it be provided in the next version?
|
I used other methods to achieve it.There may be additional performance loss and danger.Use caution. .NET 6 using System.Net.Sockets;
SocketsHttpHandler handler = new SocketsHttpHandler();
handler.ConnectCallback = async (context, cancellationToken) =>
{
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.NoDelay = true;
try
{
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false);
context.InitialRequestMessage.Options.TryAdd("IP", socket.RemoteEndPoint);
return new NetworkStream(socket, true);
}
catch
{
socket.Dispose();
throw;
}
};
ResponseHandler responseHandler = new ResponseHandler();
responseHandler.InnerHandler = handler;
using (var httpClient = new HttpClient(responseHandler))
{
var result = await httpClient.GetAsync("https://www.google.com");
Console.WriteLine(result.StatusCode);
if (result.StatusCode == System.Net.HttpStatusCode.OK)// or other
{
Console.WriteLine(result.RequestMessage.Options.FirstOrDefault(x => x.Key == "IP").Value.ToString());
}
}
public class ResponseHandler : DelegatingHandler
{
public ResponseHandler()
{
InnerHandler = new HttpClientHandler();
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
response.RequestMessage.Options.TryAdd("IP", request.Options.Where(x => x.Key == "IP").FirstOrDefault());
return response;
}
} |
👍👍👍But only the first request works. |
Can you elaborate on that? I tried a few links.it work well,The console prints the information I want |
As @geoffkizer mentioned, there is no good way how to associate response with given connection. The code above always works for first request since it is passed in. Then later connection can be reused for other requests unless `Connection: close' is used (or server does it). Or new connection can be created if request is still pending and the old one may or may not be used in the future. |
using var httpClient = new HttpClient(responseHandler);
var result = await httpClient.GetAsync("https://www.bing.com");
Console.WriteLine(result.StatusCode);
Console.WriteLine(result.RequestMessage.Options.First(x => x.Key == "IP").Value.ToString());
result = await httpClient.GetAsync("https://www.bing.com");
Console.WriteLine(result.StatusCode);
Console.WriteLine(result.RequestMessage.Options.First(x => x.Key == "IP").Value.ToString()); Console output:
|
@wfurt I guess he's right .
|
Triage:
|
@karelz - exposing the source and destination IP on the Response for logging purposes would probably make sense. If the info is not available, then return null. I am less concerned about doing that than providing the socket that people then may want to muck with. |
This will be solved with LLHTTP (unpooled HTTP) APIS. However, I've seen this requested a few times now. It seems reasonable to also look at how we could provide this data in an API, or a broader callback that gives access to the socket at one or more points in a request. It seems very trivial for HTTP/1, but HTTP/2's multiplexing and HTTP/3 using QUIC raise some questions.
|
While I feel StreamID would be nice, basic IP info would be sufficient for debugging with upstream firewalls/servers and it would allow to find relevant data easily with lot for concurrency. |
BTW #38407 seems to dup of this. |
Triage: It is reasonable to ask for this and we have seen occasionally customers to ask for it. We should likely expose the transport Stream or add some callback? Prototyping would be useful. Another idea: Expose ConnectionInfo for diagnostics. Lifetime management will be also tricky if we link back or initialize lazily. |
Please prioritize this feature. I’m working on improving the logs for my service. Basically, I would like to understand to what IP address my request was sent both in situations where the requests got a response and for cases were the request doesn’t. The destination host name is supported by a hierarchy of Azure Traffic Managers (ATM) that load balances traffic to a list (+50) of Classic Cloud Services (CCS) and I would like to be able to pinpoint issues for a specific CCS. Since I own the down stream service I'm appending customer headers to successful responses, but this provides little help when requests are timed out or in any cases where a response is not provided. If an ETA exists can you please provide it or maybe provide a temporary workaround here until the fix is in. |
Here's a temporary workaround for how to do this sort of correlation reliably, limited to HTTP/1.X: https://gist.github.com/MihaZupan/8f28566fdc4b8e20491221ed84362fcf Due to how HTTP/2 multiplexing works, the same approach doesn't work there. |
Would this work for parallel requests and cases when the connection is already established e.g. used from the ConnectionPool @MihaZupan ? |
Yes, but only for HTTP/1. The code is looking at which stream is seeing |
No async locals please 😢 |
Triage: Moving to 8.0 as multiple customers run into this with very ugly workaround. |
Worth noting is that we shouldn't forget about the failure scenario where you don't even get a response message object. This likely means including information on the exception as well. |
Another aspect worth discussing: If we add new information (properties?) to |
Isn't that why properties is a bag? ASP.NET Core deals with this via the feature collection model. General features can be agnostic of the underlying transport, and transports can influence features that flow from connection -> request. We should do something similar here. The transport (Connect callback), needs to a bag that flows from connection to request. |
Please consider that IP information is also important in cases where connection was not established, but only attempted. If the information was appended to e.g. the options dictionary this would provide an opportunity to back port to previous .NET versions. |
We had several rounds of offline discussions on this topic, involving @MihaZupan, @karelz and @mortengc. Unfortunately our conclusion is that we can only provide a limited solution for this feature request. Starting with .NET 6.0, connection pooling has been reworked so that requests and connection attempts are decoupled to improve throughput. Even though a request may initiate a new connection attempt, it might be served by a different connection that becomes available in the meanwhile. The exact logic is an implementation detail, and may change between releases. This means that - by design - there is no way to associate a remote IP endpoints with an Considering the above limitation, and the fact this is a diagnostic concern, we do not think that exposing an API would be a good idea. We should instead consider extending our telemetry with connection-level information, eg. in the way proposed in #85729: - ConnectionEstablished(byte versionMajor, byte versionMinor)
+ ConnectionEstablished(byte versionMajor, byte versionMinor, long connectionId, string scheme, string host, int port, string? remoteEndPoint)
- ConnectionClosed(byte versionMajor, byte versionMinor)
+ ConnectionClosed(byte versionMajor, byte versionMinor, long connectionId)
- RequestHeadersStart()
+ RequestHeadersStart(long connectionId) This extension would enable tracking connections together with their Users who would want to log IP information together outgoing To log remote endpoints for failed connection attempts, we don't think there is a need for new telemetry extensions. Existing events from the Users who don't want to use telemetry and/or need tighter control on their logging can override |
Triage:
|
@dotnet/ncl in the discussion resulting to the proposal in #63159 (comment), for some reason we assumed connections already store a Since we generally prefer "pay for play" and likely don't want to penalize the majority of the use-cases which have telemetry events turned off, I recommend to proceed with the first idea from #85729 emitting data on -void RequestHeadersStart();
+void RequestHeadersStart(string scheme, string host, int port, string pathAndQuery, byte versionMajor, byte versionMinor, string? remoteEndPoint); |
If we think that the added diagnostic data is useful, I think it's acceptable to pay the 8 bytes+interlocked per connection (or are there other costs involved?). |
No, that's all. TBH the second option seems to be much cleaner from telemetry perspective and I like it better if we don't need to stick to the "pay for play" rule strictly. |
How to get the socket information(such as source ip address and target ip address) from HttpResponseMessage or related exception?
If it is not currently supported, can it be provided in the next version?
The text was updated successfully, but these errors were encountered: