-
Notifications
You must be signed in to change notification settings - Fork 181
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
Add initializer API for headers of HTTP proxy CONNECT request #2744
Conversation
Motivation: After apple#2697 moved HTTP proxy `CONNECT` logic before user-defined `ConnectionFactoryFilter`s, users lost the ability to intercept `CONNECT` requests for the purpose of adding custom headers, like auth, tracing, etc. Modifications: - Introduce `ProxyConfig` and `ProxyConfigBuilder` in `http-api` that can be used to provide additional options for proxy behavior; - Add `SingleAddressHttpClientBuilder.proxyConfig(...)` overload that takes `ProxyConfig`; - Deprecate pre-existing `SingleAddressHttpClientBuilder.proxyAddress()` method, recommend switching to new API; - Enhance `HttpsProxyTest` to verify that new API can be used to send `Proxy-Authorization` header; Result: Users have explicit API to alter HTTP `CONNECT` request headers, if necessary.
@@ -89,7 +90,7 @@ class GrpcProxyTunnelTest { | |||
.listenAndAwait((Greeter.BlockingGreeterService) (ctx, request) -> | |||
HelloReply.newBuilder().setMessage(GREETING_PREFIX + request.getName()).build()); | |||
client = GrpcClients.forAddress(serverHostAndPort(serverContext)) | |||
.initializeHttp(httpBuilder -> httpBuilder.proxyAddress(proxyAddress) | |||
.initializeHttp(httpBuilder -> httpBuilder.proxyConfig(ProxyConfig.of(proxyAddress)) |
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 debated between ProxyConfig.forAddress(proxyAddress)
and ProxyConfig.of(proxyAddress)
. Went with of
only because wanted to be consistent with similar static method on other interfaces/classes, like HostAndPort
, HttpRequestMethod
, etc. If of
doesn't read well for this use-case, happy to rename it by your recommendations.
* | ||
* @param <A> the type of address | ||
*/ | ||
public interface ProxyConfig<A> { |
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.
For others who review: this may look like an overkill to add a new "config" API while we need only one more config parameter, which could be a 2nd argument for original proxyAddress(...)
method, like it was implemented in #2698. However, we discussed with @daschl and agreed that having a config object now (similar to RedirectConfig
) will minimize API changes on SingleAddressHttpClientBuilder
in the future, when we may need to add more options, like:
- Timeout for
CONNECT
request, similar to what Netty does: https://github.com/netty/netty/blob/7e18e8344819428a5560064ac15a2f035b4728fd/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyHandler.java#L57 - SOCKS proxy config options
Note that SingleAddressHttpClientBuilder
is used by users when they rely on HttpProviders
. So, it's important to minimize API friction for them.
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.
Other than the open question wrt equals
and hashCode
it looks good.
servicetalk-http-api/src/main/java/io/servicetalk/http/api/ProxyConfigBuilder.java
Outdated
Show resolved
Hide resolved
servicetalk-http-api/src/main/java/io/servicetalk/http/api/ProxyConfigBuilder.java
Show resolved
Hide resolved
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { |
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.
Because one of the fields is a Consumer
, which is going to often be set by anonymous lambda, I think notions of equality and hash-code are going to be error prone. Does this class need to have such notions?
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 think it's fine to keep best effort and we can rely on users to care about equality for consumer. If they don't care, having different lambdas will make configs different. If they care, they must provide the same instance of Consumer or override equals/hashCode for consumer.
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.
If that's an invariant which is important for the semantics of the executed code, I think we should point this out in a javadoc somewhere.
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 feels kind of obvious that if users pass 2 different lambdas, the resulting "pojo" will be different. Note that there is no use-case for equals/hashCode right now to really worry about it. This is a pkg-private class, which makes it harder to document impl details. Not sure if we need to put this info on interface bcz it's not required right now.
servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/ProxyConnectChannelSingle.java
Show resolved
Hide resolved
…xyConfigBuilder.java Co-authored-by: Bryce Anderson <bryce_anderson@apple.com>
servicetalk-http-api/src/main/java/io/servicetalk/http/api/ProxyConfig.java
Outdated
Show resolved
Hide resolved
* always initialized using {@code HTTP/1.1 CONNECT} request. This {@link Consumer} can be used to set custom | ||
* {@link HttpHeaders} for that request, like {@link HttpHeaderNames#PROXY_AUTHORIZATION proxy-authorization}, | ||
* tracing, or any other header. | ||
* |
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.
If (are we?) doing any retries on the connect request, this comment I think should clarified if it is called N times or just once.
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.
No, there are no retries specifically for a CONNECT
request. In case of a general retry, it will go through process of opening a new connection and then it's not different from any other new connection event, regardless of if it was retried or not.
* @return a {@link ProxyConfig} for the specified {@code address} | ||
* @see ProxyConfigBuilder | ||
*/ | ||
static <A> ProxyConfig<A> of(A 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.
"forAddress" I think reads easier (since you are creating a proxy config for an address, not of an address), but as you mentioned using of aligns it with HostAndPort. I'm ok either way and it's not a big deal.
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.
Another option would be to not have this static method at all and let users go through the builder all the time?
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 had it originally and it looked quite verbose
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 will rename to forAddress
to make it more readable when someone uses a static import.
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { |
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.
If that's an invariant which is important for the semantics of the executed code, I think we should point this out in a javadoc somewhere.
+ getClass().getName()); | ||
// FIXME: 0.43 - remove deprecated method | ||
@Deprecated | ||
@SuppressWarnings("DeprecatedIsStillUsed") |
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.
Should we keep this method for convenience? Related to my suggestions of the naming of the factory method: if we force them to go through the builder I would keep this convenience method, if we have the static factory I guess there is no harm in removing this since its going to be very short anyways.
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.
Usually, users have more confusion when there are multiple overloads on the builder for the same functionality. Especially when they override each other. People don't really read javadoc and as a result, there is a high chance of confusing their understanding of runtime behavior when they use both of them:
builder.proxyAddress(address)
.proxyConfig(config)
Another use-case is HttpProviders
. It makes it harder to implement a provider that need to track 2 different methods for the same functionality. As a result, I proposed a static factory.
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.
good point, makes sense. thanks!
…xyConfig.java Co-authored-by: Michael Nitschinger <michael@nitschinger.at>
Motivation:
After #2697 moved HTTP proxy
CONNECT
logic before user-definedConnectionFactoryFilter
s, users lost the ability to interceptCONNECT
requests for the purpose of adding custom headers, like auth,tracing, etc.
Modifications:
ProxyConfig
andProxyConfigBuilder
inhttp-api
thatcan be used to provide additional options for proxy behavior;
SingleAddressHttpClientBuilder.proxyConfig(...)
overload thattakes
ProxyConfig
;SingleAddressHttpClientBuilder.proxyAddress()
method, recommend switching to new API;
HttpsProxyTest
to verify that new API can be used to sendProxy-Authorization
header;Result:
Users have explicit API to alter HTTP
CONNECT
request headers, ifnecessary.