Skip to content

Commit

Permalink
Transient timeouts. Fixes #10234 and #10277
Browse files Browse the repository at this point in the history
  • Loading branch information
gregw committed Nov 1, 2023
1 parent 18c25bd commit 203babb
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -361,15 +361,12 @@ public Runnable onIdleTimeout(TimeoutException t)
Runnable invokeOnContentAvailable = _onContentAvailable;
_onContentAvailable = null;

// If a write call is in progress, take the writeCallback to fail below
Runnable invokeWriteFailure = _response.lockedFailWrite(t);

// If demand was in process, then arrange for the next read to return the idle timeout, if no other error
if (invokeOnContentAvailable != null)
{
_failure = Content.Chunk.from(t, false);
System.err.println("failure chunk " + _failure);
}

// If a write call is in progress, take the writeCallback to fail below
Runnable invokeWriteFailure = _response.lockedFailWrite(t);

// If there was an IO operation, just deliver the idle timeout via them
if (invokeOnContentAvailable != null || invokeWriteFailure != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class ServerTimeoutsTest extends AbstractTest
Expand Down Expand Up @@ -75,7 +76,10 @@ public boolean handle(Request request, Response response, Callback callback)
{

if (listener)
{
request.addIdleTimeoutListener(t -> listenerCalled.compareAndSet(false, true));
request.addFailureListener(callback::failed);
}

// Do not complete the callback, so it idle times out.
return true;
Expand Down Expand Up @@ -130,15 +134,13 @@ public boolean handle(Request request, Response response, Callback callback)

// Reads should yield the idle timeout.
Content.Chunk chunk = requestRef.get().read();
// TODO change last to false in the next line if timeouts are transients
assertTrue(Content.Chunk.isFailure(chunk, true));
assertTrue(Content.Chunk.isFailure(chunk, false));
Throwable cause = chunk.getFailure();
assertThat(cause, instanceOf(TimeoutException.class));

/* TODO if transient timeout failures are supported then add this check
// Can read again
assertNull(requestRef.get().read());
*/


// Complete the callback as the error listener promised.
callbackRef.get().failed(cause);
Expand Down Expand Up @@ -198,6 +200,7 @@ public void testIdleTimeoutErrorListenerReturnsFalseThenTrue(Transport transport
public boolean handle(Request request, Response response, Callback callback)
{
request.addIdleTimeoutListener(t -> error.getAndSet(t) != null);
request.addFailureListener(callback::failed);
return true;
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,11 @@ public Content.Chunk nextChunk()
if (LOG.isDebugEnabled())
LOG.debug("nextChunk = {} {}", chunk, this);
if (chunk != null)
{
_servletChannel.getServletRequestState().onReadIdle();
if (Content.Chunk.isFailure(chunk, false))
_chunk = Content.Chunk.next(chunk);
}
return chunk;
}

Expand Down Expand Up @@ -267,7 +271,7 @@ private Content.Chunk produceChunk()
{
if (_chunk != null)
{
if (_chunk.isLast() || _chunk.hasRemaining())
if (_chunk.isLast() || _chunk.hasRemaining() || Content.Chunk.isFailure(_chunk, false))
{
if (LOG.isDebugEnabled())
LOG.debug("chunk not yet depleted, returning it {}", this);
Expand All @@ -292,10 +296,9 @@ private Content.Chunk produceChunk()
LOG.debug("channel has no new chunk {}", this);
return null;
}
else
{
_servletChannel.getServletRequestState().onContentAdded();
}
_servletChannel.getServletRequestState().onContentAdded();
if (Content.Chunk.isFailure(_chunk, false))
return _chunk;
}

// Release the chunk immediately, if it is empty.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,8 @@ protected void service(HttpServletRequest request, HttpServletResponse response)
@Override
public void onDataAvailable() throws IOException
{
assertEquals(0, input.read());
while (input.isReady())
assertEquals(0, input.read());
assertFalse(input.isReady());
}

Expand All @@ -477,17 +478,10 @@ public void onError(Throwable failure)
{
if (failure instanceof TimeoutException)
{
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
response.setStatus(HttpStatus.GATEWAY_TIMEOUT_504);
handlerLatch.countDown();
}

// TODO the problem here is that timeout failures are currently persistent and affect reads
// and writes. So after the 500 is set above, the complete below tries to commit the response,
// but the write/send for that fails with the same timeout exception. Thus the 500 is never
// sent and the connection is just closed.
// This was not apparent until the change in HttpOutput#onWriteComplete to not always abort on
// failure (as this prevents async handling completing on its own terms).
// We can "fix" this here by doing a response.sendError(-1);
asyncContext.complete();
}
});
Expand All @@ -501,7 +495,7 @@ public void onError(Throwable failure)
.body(content)
.send(result ->
{
if (result.getResponse().getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500)
if (result.getResponse().getStatus() == HttpStatus.GATEWAY_TIMEOUT_504)
resultLatch.countDown();
});

Expand Down

0 comments on commit 203babb

Please sign in to comment.