I've got a case where I can call receiveFullBytes but neither the success
nor the error handler is ever called. My code looks like this:
public static CompletableFuture<ByteSource> getCompleteBody(HttpServerExchange exchange) { log.debug("In getCompleteBody."); CompletableFuture<ByteSource> finalResult = new CompletableFuture<>(); try { log.debug("Calling receiveFullBytes."); exchange.getRequestReceiver().receiveFullBytes( // Success case. (HttpServerExchange excng, byte[] bytes) -> { log.debug("getFullBytes completed with success."); finalResult.complete(ByteSource.wrap(bytes)); }, // Error case (HttpServerExchange exchng, IOException t) -> { log.warn("getFullBytes completed with an error:", t); finalResult.completeExceptionally(t); } ); } catch (Throwable t) { log.warn("receiveFullBytes threw an exception:", t); finalResult.completeExceptionally(t); } return finalResult; }
I've got a unit test that opens a socket, starts sending data, and
then, intentionally throws an exception. The test then ensures that
the CompletableFuture above completes with an exception. Except, it
never completes; the test just hangs forever. You can see that the
"log.debug("Calling receiveFullBytes.")" line does get called and that
the neither of the log lines in either callback is ever called. The
full test looks like this:
@Test public void getCompleteBodyCompletesWithExceptionOnBodyFailure() throws Exception { AtomicBoolean futureRedeemedWithError = new AtomicBoolean(false); CountDownLatch waitForInputToFail = new CountDownLatch(1); HttpHandler handler = new HttpHandler() { @Override public void handleRequest(HttpServerExchange exchange) throws Exception { log.info("Handler called."); exchange.dispatch(); log.info("Calling getCompleteBody"); UndertowUtils.getCompleteBody(exchange).exceptionally((Throwable t) -> { log.debug("As expected, received an exception:", t); futureRedeemedWithError.set(true); waitForInputToFail.countDown(); return null; }); } }; UndertowTestUtils.RunningServer server = UndertowTestUtils.startUndertowOnFreePort(handler); // A Body() implementation that will return a few chunks of data but the fail Body failingRequestBody = new Body() { private AtomicInteger readCalls = new AtomicInteger(0); @Override public long getContentLength() { return 1000; } @Override public long read(ByteBuffer buffer) throws IOException { if (readCalls.getAndIncrement() < 2) { byte[] toSend = "some data".getBytes(Charsets.UTF_8); buffer.put(toSend); return toSend.length; } else { throw new RuntimeException("Fake error on sending data."); } } @Override public void close() throws IOException { } }; // A body generator using the above, busted Body. BodyGenerator failingBodyGenerator = new BodyGenerator() { @Override public Body createBody() throws IOException { return failingRequestBody; } }; // This will start sending data so our handler gets invoked but it will then fail partway through. SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder() .setUrl(String.format("http://localhost:%s", server.getPort())) .build(); client.post(failingBodyGenerator); // Test hangs here forever waitForInputToFail.await(); assertThat(futureRedeemedWithError.get()).isTrue(); }
This feels like a bug. I'd expect the contract of "receiveFullBytes" to be
that either the success or failure handler gets called exactly once.
- clones
-
UNDERTOW-710 receiveFullBytes callbacks may not be called on error with some protocols
- Resolved
- is incorporated by
-
JBEAP-5060 Upgrade Undertow from 1.3.22.Final to 1.3.23.Final
- Closed