Merge remote-tracking branch 'origin/jetty-9.3.x' into jetty-9.4.x
This commit is contained in:
commit
a90e197633
|
@ -718,6 +718,13 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
|
||||||
_written+=BufferUtil.length(content);
|
_written+=BufferUtil.length(content);
|
||||||
sendResponse(null,content,complete,callback);
|
sendResponse(null,content,complete,callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetBuffer()
|
||||||
|
{
|
||||||
|
if(isCommitted())
|
||||||
|
throw new IllegalStateException("Committed");
|
||||||
|
}
|
||||||
|
|
||||||
public HttpOutput.Interceptor getNextInterceptor()
|
public HttpOutput.Interceptor getNextInterceptor()
|
||||||
{
|
{
|
||||||
|
|
|
@ -54,12 +54,66 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
* close the stream, to be reopened after the inclusion ends.</p>
|
* close the stream, to be reopened after the inclusion ends.</p>
|
||||||
*/
|
*/
|
||||||
public class HttpOutput extends ServletOutputStream implements Runnable
|
public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The HttpOutput.Inteceptor is a single intercept point for all
|
||||||
|
* output written to the HttpOutput: via writer; via output stream;
|
||||||
|
* asynchronously; or blocking.
|
||||||
|
* <p>
|
||||||
|
* The Interceptor can be used to implement translations (eg Gzip) or
|
||||||
|
* additional buffering that acts on all output. Interceptors are
|
||||||
|
* created in a chain, so that multiple concerns may intercept.
|
||||||
|
* <p>
|
||||||
|
* The {@link HttpChannel} is an {@link Interceptor} and is always the
|
||||||
|
* last link in any Interceptor chain.
|
||||||
|
* <p>
|
||||||
|
* Responses are committed by the first call to
|
||||||
|
* {@link #write(ByteBuffer, boolean, Callback)}
|
||||||
|
* and closed by a call to {@link #write(ByteBuffer, boolean, Callback)}
|
||||||
|
* with the last boolean set true. If no content is available to commit
|
||||||
|
* or close, then a null buffer is passed.
|
||||||
|
*/
|
||||||
public interface Interceptor
|
public interface Interceptor
|
||||||
{
|
{
|
||||||
void write(ByteBuffer content, boolean complete, Callback callback);
|
/**
|
||||||
|
* Write content.
|
||||||
|
* The response is committed by the first call to write and is closed by
|
||||||
|
* a call with last == true. Empty content buffers may be passed to
|
||||||
|
* force a commit or close.
|
||||||
|
* @param content The content to be written or an empty buffer.
|
||||||
|
* @param last True if this is the last call to write
|
||||||
|
* @param callback The callback to use to indicate {@link Callback#succeeded()}
|
||||||
|
* or {@link Callback#failed(Throwable)}.
|
||||||
|
*/
|
||||||
|
void write(ByteBuffer content, boolean last, Callback callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The next Interceptor in the chain or null if this is the
|
||||||
|
* last Interceptor in the chain.
|
||||||
|
*/
|
||||||
Interceptor getNextInterceptor();
|
Interceptor getNextInterceptor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if the Interceptor is optimized to receive direct
|
||||||
|
* {@link ByteBuffer}s in the {@link #write(ByteBuffer, boolean, Callback)}
|
||||||
|
* method. If false is returned, then passing direct buffers may cause
|
||||||
|
* inefficiencies.
|
||||||
|
*/
|
||||||
boolean isOptimizedForDirectBuffers();
|
boolean isOptimizedForDirectBuffers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the buffers.
|
||||||
|
* <p>If the Interceptor contains buffers then reset them.
|
||||||
|
* @throws IllegalStateException Thrown if the response has been
|
||||||
|
* committed and buffers and/or headers cannot be reset.
|
||||||
|
*/
|
||||||
|
default void resetBuffer() throws IllegalStateException
|
||||||
|
{
|
||||||
|
Interceptor next = getNextInterceptor();
|
||||||
|
if (next!=null)
|
||||||
|
next.resetBuffer();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Logger LOG = Log.getLogger(HttpOutput.class);
|
private static Logger LOG = Log.getLogger(HttpOutput.class);
|
||||||
|
@ -863,15 +917,19 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
|
|
||||||
public void recycle()
|
public void recycle()
|
||||||
{
|
{
|
||||||
resetBuffer();
|
|
||||||
_interceptor=_channel;
|
_interceptor=_channel;
|
||||||
|
if (BufferUtil.hasContent(_aggregate))
|
||||||
|
BufferUtil.clear(_aggregate);
|
||||||
|
_written = 0;
|
||||||
|
reopen();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetBuffer()
|
public void resetBuffer()
|
||||||
{
|
{
|
||||||
_written = 0;
|
_interceptor.resetBuffer();
|
||||||
if (BufferUtil.hasContent(_aggregate))
|
if (BufferUtil.hasContent(_aggregate))
|
||||||
BufferUtil.clear(_aggregate);
|
BufferUtil.clear(_aggregate);
|
||||||
|
_written = 0;
|
||||||
reopen();
|
reopen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -531,6 +531,9 @@ public class Response implements HttpServletResponse
|
||||||
LOG.debug("Aborting on sendError on committed response {} {}",code,message);
|
LOG.debug("Aborting on sendError on committed response {} {}",code,message);
|
||||||
code=-1;
|
code=-1;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
resetBuffer();
|
||||||
|
|
||||||
|
|
||||||
switch(code)
|
switch(code)
|
||||||
{
|
{
|
||||||
|
@ -544,7 +547,7 @@ public class Response implements HttpServletResponse
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetBuffer();
|
|
||||||
_mimeType=null;
|
_mimeType=null;
|
||||||
_characterEncoding=null;
|
_characterEncoding=null;
|
||||||
_outputType = OutputType.NONE;
|
_outputType = OutputType.NONE;
|
||||||
|
@ -1213,9 +1216,6 @@ public class Response implements HttpServletResponse
|
||||||
@Override
|
@Override
|
||||||
public void resetBuffer()
|
public void resetBuffer()
|
||||||
{
|
{
|
||||||
if (isCommitted())
|
|
||||||
throw new IllegalStateException("cannot reset buffer on committed response");
|
|
||||||
|
|
||||||
_out.resetBuffer();
|
_out.resetBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,14 @@ public class BufferedResponseHandler extends HandlerWrapper
|
||||||
_next=interceptor;
|
_next=interceptor;
|
||||||
_channel=httpChannel;
|
_channel=httpChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetBuffer()
|
||||||
|
{
|
||||||
|
_buffers.clear();
|
||||||
|
_aggregating=null;
|
||||||
|
_aggregate=null;
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ByteBuffer content, boolean last, Callback callback)
|
public void write(ByteBuffer content, boolean last, Callback callback)
|
||||||
|
|
|
@ -41,8 +41,6 @@ import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resource Handler test
|
* Resource Handler test
|
||||||
*
|
|
||||||
* TODO: increase the testing going on here
|
|
||||||
*/
|
*/
|
||||||
public class BufferedResponseHandlerTest
|
public class BufferedResponseHandlerTest
|
||||||
{
|
{
|
||||||
|
@ -95,6 +93,7 @@ public class BufferedResponseHandlerTest
|
||||||
_test._writes=10;
|
_test._writes=10;
|
||||||
_test._flush=false;
|
_test._flush=false;
|
||||||
_test._close=false;
|
_test._close=false;
|
||||||
|
_test._reset=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -218,6 +217,18 @@ public class BufferedResponseHandlerTest
|
||||||
assertThat(response,not(containsString("Write: 1")));
|
assertThat(response,not(containsString("Write: 1")));
|
||||||
assertThat(response,containsString("Written: true"));
|
assertThat(response,containsString("Written: true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReset() throws Exception
|
||||||
|
{
|
||||||
|
_test._reset=true;
|
||||||
|
String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
|
||||||
|
assertThat(response,containsString(" 200 OK"));
|
||||||
|
assertThat(response,containsString("Write: 0"));
|
||||||
|
assertThat(response,containsString("Write: 9"));
|
||||||
|
assertThat(response,containsString("Written: true"));
|
||||||
|
assertThat(response,not(containsString("RESET")));
|
||||||
|
}
|
||||||
|
|
||||||
public static class TestHandler extends AbstractHandler
|
public static class TestHandler extends AbstractHandler
|
||||||
{
|
{
|
||||||
|
@ -227,16 +238,25 @@ public class BufferedResponseHandlerTest
|
||||||
int _writes;
|
int _writes;
|
||||||
boolean _flush;
|
boolean _flush;
|
||||||
boolean _close;
|
boolean _close;
|
||||||
|
boolean _reset;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
|
|
||||||
if (_bufferSize>0)
|
if (_bufferSize>0)
|
||||||
response.setBufferSize(_bufferSize);
|
response.setBufferSize(_bufferSize);
|
||||||
if (_mimeType!=null)
|
if (_mimeType!=null)
|
||||||
response.setContentType(_mimeType);
|
response.setContentType(_mimeType);
|
||||||
|
|
||||||
|
if (_reset)
|
||||||
|
{
|
||||||
|
response.getOutputStream().print("THIS WILL BE RESET");
|
||||||
|
response.getOutputStream().flush();
|
||||||
|
response.getOutputStream().print("THIS WILL BE RESET");
|
||||||
|
response.resetBuffer();
|
||||||
|
}
|
||||||
for (int i=0;i<_writes;i++)
|
for (int i=0;i<_writes;i++)
|
||||||
{
|
{
|
||||||
response.addHeader("Write",Integer.toString(i));
|
response.addHeader("Write",Integer.toString(i));
|
||||||
|
@ -248,7 +268,6 @@ public class BufferedResponseHandlerTest
|
||||||
if (_close)
|
if (_close)
|
||||||
response.getOutputStream().close();
|
response.getOutputStream().close();
|
||||||
response.addHeader("Written","true");
|
response.addHeader("Written","true");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue