Skip to content
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

HttpClient#doOnRequest doesnt execute BEFORE request is sent #558

Closed
simonbasle opened this issue Dec 21, 2018 · 4 comments
Closed

HttpClient#doOnRequest doesnt execute BEFORE request is sent #558

simonbasle opened this issue Dec 21, 2018 · 4 comments
Labels
type/bug A general bug
Milestone

Comments

@simonbasle
Copy link
Member

Expected behavior

HttpClient#doOnRequest listener should be invoked before the request is sent, allowing to mutate it (eg. add a basic set of headers on every request).

Actual behavior

It seems to be invoked AFTER the request is sent. In the listener:

  • doing req.requestHeaders().set("foo", "bar") mutates the headers but is never used
  • doing req.header("foo", "bar") throws an java.lang.IllegalStateException: Status and headers already sent

Steps to reproduce

@Test
public void postMutatesHeaders() {
	HttpClient client = HttpClient.create()
			.doOnRequest((req, con) -> req.header("foo", "dufafa"))
			.baseUrl("https://postman-echo.com/");

	String resp =
			client.post()
					.uri("/post")
					.send((req, outbound) -> {
						String foo = req.requestHeaders().get("foo");
						if (foo != null) {
							req.header("foo", foo+"bar");
						}
						else {
							req.header("foo", "baz");
						}
						return outbound.sendString(Flux.just("post"));
					})
					.response((res, byteBufFlux) -> byteBufFlux.asString())
					.blockFirst();

	//check what the echo server saw as headers
	Assertions.assertThat(resp).contains("\"foo\":\"dufafabar\"");
}

Analysis

HttpClientConnect is always added as the first observer, after doOnRequest's HttpClientDoOn has set itself as observer, resulting if I understand correctly in HCC calling the handler and sending the request before the doOnRequest is invoked.

Both react to the CONFIGURED state, and due to the order HttpClientConnect "wins".

The workaround would be to add a new intermediate state, eg. CONFIGURING, letting some observers alter the request before HttpClientConnect does its thing. For instance, doOnRequest would match on this one.

CONFIGURING would be fired right before CONFIGURED everywhere the later is currently fired.

Reactor Netty version

0.8.x

simonbasle added a commit that referenced this issue Jan 3, 2019
This additional step lets listeners react before a request body is sent,
since once the CONFIGURED state is reached, it triggers sending the
request over the wire.
@violetagg violetagg added this to the 0.8.4.RELEASE milestone Jan 3, 2019
@violetagg violetagg added the type/bug A general bug label Jan 3, 2019
@violetagg violetagg modified the milestones: 0.8.4.RELEASE, 0.8.5.RELEASE Jan 8, 2019
@smaldini smaldini modified the milestones: 0.8.5.RELEASE, 0.8.x Backlog, 0.9.x Backlog Feb 7, 2019
@crankydillo
Copy link

crankydillo commented Apr 4, 2019

Hi, my project has a need for this fix. I've been allotted a small amount of time to look into this and started with this failing test.

If I revert the observer reordering from this commit, my test passes. Any thoughts on this?

I'm still new to this, so I'm not entirely sure how to proceed, but I'll do the best in the time I've got!

@crankydillo
Copy link

crankydillo commented Apr 4, 2019

I should note that other tests, unsurprisingly, fail when I change that back.

If I apply this diff to my branch:

diff --git a/src/main/java/reactor/netty/http/client/HttpClientConnect.java b/src/main/java/reactor/netty/http/client/HttpClientConnect.java
index 88f764f..a8768f6 100644
--- a/src/main/java/reactor/netty/http/client/HttpClientConnect.java
+++ b/src/main/java/reactor/netty/http/client/HttpClientConnect.java
@@ -326,7 +326,7 @@ final class HttpClientConnect extends HttpClient {
 				}
 
 				BootstrapHandlers.connectionObserver(finalBootstrap,
-						new HttpObserver(sink, handler).then(BootstrapHandlers.connectionObserver(finalBootstrap)));
+					BootstrapHandlers.connectionObserver(finalBootstrap).then(new HttpObserver(sink, handler)));
 
 				tcpClient.connect(finalBootstrap)
 				         .subscribe(new TcpClientSubscriber(sink));

I get the following failing tests:

image

Note that my newly added hookFiringOrder test is failing as well; however, that's due to the doAfterResponse hook not firing (or not firing in time). Not sure what's going on there, but my colleague is complaining of similar behavior even after integrating the fix for #651. When I run the test in isolation, it 'always' passes. Anyhow, not trying to sidetrack the original issue.. I just wanted to explain why that test was in the image:)

violetagg added a commit that referenced this issue Apr 5, 2019
Adapt reactor.netty.http.client.WebsocketTest to the change: as the
I/O handler is applied as the last observer, the availability of
NettyPipeline.WsCompressionHandler cannot be check using
doOnConnected, because the compression handler is applied
as part of the I/O handler, however the test check whether the
corresponding compression handler is available so it can be
checked in the handle method.
@violetagg violetagg modified the milestones: 0.9.x Backlog, 0.8.7.RELEASE Apr 5, 2019
@violetagg
Copy link
Member

@crankydillo Thanks for the analysis. Please check this PR #701

@crankydillo
Copy link

Thanks @violetagg !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants