Skip to content

Commit

Permalink
#12153 fix serving content async even when a filter already called st…
Browse files Browse the repository at this point in the history
…artAsync

Signed-off-by: Ludovic Orban <[email protected]>
  • Loading branch information
lorban committed Dec 4, 2024
1 parent 7014ab7 commit 741ac12
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse
(contentLength < 0 || contentLength > coreRequest.getConnectionMetaData().getHttpConfiguration().getOutputBufferSize()))
{
// send the content asynchronously
AsyncContext asyncContext = httpServletRequest.startAsync();
AsyncContext asyncContext = httpServletRequest.isAsyncStarted() ? httpServletRequest.getAsyncContext() : httpServletRequest.startAsync();
Callback callback = new AsyncContextCallback(asyncContext, httpServletResponse);
_resourceService.doGet(coreRequest, coreResponse, callback, content);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.regex.Matcher;
Expand All @@ -38,6 +39,7 @@
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import jakarta.servlet.AsyncContext;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
Expand Down Expand Up @@ -3773,6 +3775,39 @@ public void testNotAcceptRanges() throws Exception
assertThat(response.getContent(), is("Test 2 to too two\n"));
}

@Test
public void testServeResourceAsyncWhileStartAsyncAlreadyCalled() throws Exception
{
// The OutputBufferSize must be smaller than the content length otherwise the request is not served async.
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setOutputBufferSize(0);

AtomicBoolean filterCalled = new AtomicBoolean();
context.addFilter((request, response, chain) ->
{
filterCalled.set(true);
AsyncContext asyncContext = request.startAsync();
chain.doFilter(request, response);
asyncContext.complete();
}, "/*", EnumSet.of(DispatcherType.REQUEST));

ResourceServlet resourceServlet = new ResourceServlet();
context.addServlet(resourceServlet, "/*");
Resource memResource = ResourceFactory.of(context).newMemoryResource(getClass().getResource("/contextResources/test.txt"));
resourceServlet.getResourceService().setHttpContentFactory(path -> new ResourceHttpContent(memResource, "text/plain"));

String rawResponse = connector.getResponse("""
GET /context/ HTTP/1.1\r
Host: local\r
Connection: close\r
\r
""");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat(response.toString(), response.getStatus(), is(HttpStatus.OK_200));
assertThat(response.get(HttpHeader.CONTENT_LENGTH), is("18"));
assertThat(response.getContent(), is("Test 2 to too two\n"));
assertThat(filterCalled.get(), is(true));
}

public static class WriterFilter implements Filter
{
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ else if (written)
// write the content asynchronously if supported
if (request.isAsyncSupported())
{
final AsyncContext context = request.startAsync();
AsyncContext context = request.isAsyncStarted() ? request.getAsyncContext() : request.startAsync();
context.setTimeout(0);

((HttpOutput)out).sendContent(content, new Callback()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.regex.Matcher;
Expand All @@ -34,6 +35,7 @@
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import jakarta.servlet.AsyncContext;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
Expand Down Expand Up @@ -2783,6 +2785,39 @@ public void testMemoryResourceRange() throws Exception
assertThat(response.getContent(), is("too"));
}

@Test
public void testServeResourceAsyncWhileStartAsyncAlreadyCalled() throws Exception
{
AtomicBoolean filterCalled = new AtomicBoolean();
startServer((context) ->
{
Resource memResource = ResourceFactory.of(context).newMemoryResource(getClass().getResource("/contextResources/test.txt"));
ResourceService resourceService = new ResourceService();
resourceService.setHttpContentFactory(path -> new ResourceHttpContent(memResource, "text/plain"));
DefaultServlet defaultServlet = new DefaultServlet(resourceService);
context.addServlet(new ServletHolder(defaultServlet), "/*");
context.addFilter(new FilterHolder((request, response, chain) ->
{
filterCalled.set(true);
AsyncContext asyncContext = request.startAsync();
chain.doFilter(request, response);
asyncContext.complete();
}), "/*", EnumSet.of(DispatcherType.REQUEST));
});

String rawResponse = connector.getResponse("""
GET /context/ HTTP/1.1\r
Host: local\r
Connection: close\r
\r
""");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat(response.toString(), response.getStatus(), is(HttpStatus.OK_200));
assertThat(response.get(HttpHeader.CONTENT_LENGTH), is("17"));
assertThat(response.getContent(), is("Test 2 to too two"));
assertThat(filterCalled.get(), is(true));
}

@Test
public void testMemoryResourceMultipleRanges() throws Exception
{
Expand Down

0 comments on commit 741ac12

Please sign in to comment.