-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
HTTP client refactor #11182
HTTP client refactor #11182
Conversation
This is an intermediate PR of my refactor, in the interest of keeping the changes reviewable.
First version of replacing the previous mixed response handling with a single ByteBody-based handler (similar to PipeliningServerHandler on the server). The purpose of this is to (a) unify some code between the streaming and aggregating code paths in DefaultHttpClient, (b) allow ByteBody-based streaming for ProxyHttpClient in the future so that ProxyHttpClient can be implemented for the jdk client and servlet server, (c) eventually create a ByteBody-based HTTP client API (maybe just in ConnectionManager) that allows lower level access to the request and thus can be used to implement the oci sdk http client.
# Conflicts: # http-netty/src/main/java/io/micronaut/http/netty/body/StreamingNettyByteBody.java
# Conflicts: # http-netty/src/main/java/io/micronaut/http/netty/body/StreamingNettyByteBody.java
# Conflicts: # http-netty/src/main/java/io/micronaut/http/netty/body/StreamingNettyByteBody.java
FYI @dstepanov, this PR also migrates the client to use mostly Mono, so this is 90% of the way to ExecutionFlow already. I may do that in a future PR, it just needs a solution to the cancellation feature. |
# Conflicts: # http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java
http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java
Show resolved
Hide resolved
http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java
Show resolved
Hide resolved
} | ||
} | ||
|
||
pipeline.addLast(ChannelPipelineCustomizer.HANDLER_MICRONAUT_HTTP_RESPONSE, new Http1ResponseHandler(new Http1ResponseHandler.ResponseListener() { |
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.
can this be pulled into an inner class to make it easier to read
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.
id prefer not to, it's only like 50 lines but it would require 7 constructor parameters from the method
http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java
Show resolved
Hide resolved
http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java
Outdated
Show resolved
Hide resolved
http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java
Outdated
Show resolved
Hide resolved
http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java
Outdated
Show resolved
Hide resolved
@@ -627,8 +627,8 @@ public Optional<io.netty.handler.codec.http.HttpRequest> toHttpRequestDirect() { | |||
} | |||
|
|||
@Override | |||
public @NonNull Optional<ByteBody> byteBodyDirect() { | |||
return Optional.of(byteBody()); | |||
public ByteBody byteBodyDirect() { |
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.
is this now Nullable
?
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.
yes, it's marked as such on the super method
http-client/src/main/java/io/micronaut/http/client/netty/StreamWriter.java
Show resolved
Hide resolved
# Conflicts: # http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java
http-client/src/main/java/io/micronaut/http/client/netty/NettyClientByteBodyResponse.java
Show resolved
Hide resolved
http-client/src/main/java/io/micronaut/http/client/netty/StreamWriter.java
Show resolved
Hide resolved
http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java
Outdated
Show resolved
Hide resolved
if (parentRequest != null) { | ||
// todo: migrate to new filter | ||
filters.add( | ||
GenericHttpFilter.createLegacyFilter(new ClientServerContextFilter(parentRequest), new FilterOrder.Fixed(Ordered.HIGHEST_PRECEDENCE)) |
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.
Do we still need this?
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 don't see why not
parentRequest, | ||
null, | ||
request.uri(requestURI), | ||
(req, resp) -> { |
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.
Please extract a new method
} | ||
// first: connect | ||
return connectionManager.connect(requestKey, blockHint) | ||
.flatMap(poolHandle -> { |
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.
Please extract a new methoda
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 dont agree, this code is basically imperative
// send the raw request | ||
return sendRawRequest(poolHandle, request, byteBody, c -> handleResponseError(request, c)); | ||
}) | ||
.flatMap(byteBodyResponse -> { |
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.
Please extract a new method
).asNativeBuffer(); | ||
} | ||
// send the raw request | ||
return sendRawRequest(poolHandle, request, byteBody, c -> handleResponseError(request, c)); |
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.
More correct would be to flatMap response of this method instead of the outer one to avoid an extra call for the error path
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's functionally identical
sink.error(e); | ||
}); | ||
pipeline.addLast(streamWriter); | ||
byteBuf = null; |
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.
Intellij noticed that byteBuf can be null but the Netty API doesn't allow it to be null
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.
all uses of byteBuf are guarded with checks of either byteBuf or streamWriter
).map(r -> (HttpResponse<O>) r); | ||
}); | ||
|
||
Duration requestTimeout = configuration.getRequestTimeout(); |
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.
Please extract applyConfiguration
Some of the code looks way too long, and the code is going beyond my screen. I prefer to extract some of the lamdas to have an appropriate name to make the code more understandable. |
ive extracted the response handling for exchange. For the other four cases, here is my reasoning why I don't want to:
|
Quality Gate passedIssues Measures |
This is the continuation of #11158 and #11177.
Now that the previous PRs have migrated the reading/writing to ByteBody, it is time to unify the code paths. This PR is a complex refactor of DefaultHttpClient that merges most of the code for normal exchange requests, streaming requests, and proxy requests into a single
sendRequestWithRedirects
method. In particular, logging, filtering, connection management, redirect handling and error handling are now merged using the ByteBody API.This PR is a result of various small refactors that all preserve behavior, but the combined change is large enough that it basically looks like a rewrite. Behavior should be almost the same, and all tests still pass. There are some minor changes I can think of, but they seem positive:
GET("/foo")
it would resolve toGET("https://example.com/foo")
depending on service ID) would happen before filters, and then would be carried next to the request through the filters. This means that filters can (a) not replace the request because provideResponse would ignore the new request, and (b) they could not mutate the URI of the original request because the new URI would be ignored in favor of the previously resolved URI. In the new implementation, the URI is still resolved before the filters, but it's set on the request, sent through the filters, and then the filtered request is used in the downstream code. So it should be possible to change the URI in a filter, but I've not added a test.There are still some more changes I want to make to the client, e.g. getting rid of StreamingHttpRequest for the proxy client. I'm also thinking of exposing
sendRawRequest
as a new API (another HttpClient interface). But these will happen in future PRs.