-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Fix proxied ipv6 address request formatting and fix port handling for proxied IP address on SocketsHttpHandler #28740
Conversation
@dotnet-bot test Outerloop Windows x64 Debug Build |
if (!request.RequestUri.IsDefaultPort) | ||
{ | ||
await WriteByteAsync((byte)':').ConfigureAwait(false); | ||
await WriteDecimalInt32Async(request.RequestUri.Port).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seem costly to separate out the ":" character and the small number of decimal digits of the port number into separate async operations requiring Task's to be created and awaited, etc.
In fact, this entire request line construction involves too many async Task-based operations. It probably would be more efficient to buffer all these bytes into a single async Write call.
You don't need to block on this for the PR but we should investigate this for future perf gains.
cc: @geoffkizer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do want to revisit this, as there is overhead here we can likely do away with. Note, though, that on the fast path here, we don't actually create any tasks; these methods end up returning an already completed task, such that nothing is allocated and the await sees IsCompleted==true and skips hooking up any callbacks. That said, it's still extra code being executed that we could benefit from doing away with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may also want to optimize here. WriteHostHeaderAsync
{ | ||
connectionAccepted = true; | ||
List<string> headers = await connection.ReadRequestHeaderAndSendResponseAsync(); | ||
Assert.Contains($"GET {ipAddress}/ HTTP/1.1", headers); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't expect this test to ever work. The "GET" line sent thru a proxy for a non-tunneled request would look more like this:
GET http://servername/ HTTP/1.1
Host: servername
etc.
So, you need to check for full URI with scheme such as:
GET http://321.123.456.789:8080/ HTTP/1.1
Host: 321.123.456.789:8080
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See: https://tools.ietf.org/html/rfc7230#section-5.3.2
When making a request to a proxy, other than a CONNECT or server-wide OPTIONS request (as detailed below), a client MUST send the target URI in absolute-form as the request-target.... An example absolute-form of request-line would be:
GET http://www.example.org/pub/WWW/TheProject.html HTTP/1.1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the code adds the "http://" part above. But yeah, it's confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will rename the variable.
[InlineData("[::1234]:8080")] | ||
public async Task ProxiedIPAddressRequest_NotDefaultPort_CorrectlyFormatted(string host) | ||
{ | ||
string ipAddress = "http://" + host; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"ipAddress" is confusing; call this "uri" or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change.
@@ -497,6 +498,91 @@ public static IEnumerable<object[]> GetAsync_IPBasedUri_Success_MemberData() | |||
Assert.True(connectionAccepted); | |||
} | |||
|
|||
[Theory] | |||
[InlineData("321.123.456.789")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of weird. Do you mean for this to be a valid IPv4 address, or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and similarly below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any non-existent ipv4 endpoint.
We only care about the traffic through proxy. I think if the endpoint doesn't exist, we can save time connecting to it. I could be wrong.
Do you have any suggestions on generating IPv4 address?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this should not exist? We should be able to verify CONNECT parameter without actually fetching the content, right? If you really want to try failing connect you can probably use multicast address or address from special blocks (like 127.0.0.127)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As @wfurt said, we're not actually using the IPv4 address, so it can be anything.
@dotnet-bot test Outerloop Windows x64 Debug Build |
@dotnet-bot test Outerloop Linux x64 Debug Build please |
Failures unrelated. @dotnet-bot test Outerloop Linux x64 Debug Build please |
OSX failure: #21037
@dotnet-bot test Outerloop Linux x64 Debug Build please |
@dotnet-bot test Outerloop Linux x64 Debug Build please |
CI is green now. @dotnet/ncl @stephentoub PTAL |
{ | ||
// Https proxy request will use CONNECT tunnel, even the default 443 port is specified, it will not be stripped off. | ||
string expectedAddressUri = "corefx-net.cloudapp.net:443"; | ||
string addressUri = "https://corefx-net.cloudapp.net:443/"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually don't hard-code these external server addresses.
You should use the information in the Common\test\ files, i.e.
https://github.com/dotnet/corefx/blob/master/src/Common/tests/System/Net/Configuration.Http.cs#L11
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change.
I'm also thinking about changing the inline test data to DefaultAzureServer
in ProxiedRequest_DefaultPort_PortStrippedOffInUri
. Not sure if this will work, I will explore it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InlineData might not work. But you could change that to a MemberData and use a method that does "yield return" for each test case.
public async Task ProxyTunnelRequest_PortSpecified_NotStrippedOffInUri() | ||
{ | ||
// Https proxy request will use CONNECT tunnel, even the default 443 port is specified, it will not be stripped off. | ||
string expectedAddressUri = "corefx-net.cloudapp.net:443"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: And strictly speaking, expectedAddressUri
isn't really a Uri. It is a string consisting of hostname and port. So, perhaps a better name for this local variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use "requestTarget" since that is the term used in the RFC:
https://tools.ietf.org/html/rfc7231#page-30
A client sending a CONNECT request MUST send the authority form of
request-target (Section 5.3 of [RFC7230]); i.e., the request-target
consists of only the host name and port number of the tunnel
destination, separated by a colon. For example,
CONNECT server.example.com:80 HTTP/1.1
Host: server.example.com:80
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change the naming.
@dotnet-bot test Outerloop Linux x64 Debug Build please [EDIT] |
Failures unrelated. |
… proxied IP address on SocketsHttpHandler (dotnet#28740) * fix proxied ipv6 address request format and fix port handling for ip address * address feedback * change naming for test data
… proxied IP address on SocketsHttpHandler (dotnet/corefx#28740) * fix proxied ipv6 address request format and fix port handling for ip address * address feedback * change naming for test data Commit migrated from dotnet/corefx@18d685f
[]
in proxied request.If default ports 80 (for http connection) and 443 (for https connection) are specified for proxied request, 80 will be stripped off, while 443 will not. Because for https scheme, it will try to tunnel through the proxy (
CONNECT
), and port will not be stripped off.Tests are added to document the behavior.
Fix: #28609 (moved to dotnet/runtime#25677)