Merge remote-tracking branch 'origin/jetty-9.3.x' into jetty-9.4.x

This commit is contained in:
Greg Wilkins 2016-08-16 00:11:48 +10:00
commit a90e197633
5 changed files with 105 additions and 13 deletions

View File

@ -718,6 +718,13 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
_written+=BufferUtil.length(content);
sendResponse(null,content,complete,callback);
}
@Override
public void resetBuffer()
{
if(isCommitted())
throw new IllegalStateException("Committed");
}
public HttpOutput.Interceptor getNextInterceptor()
{

View File

@ -54,12 +54,66 @@ import org.eclipse.jetty.util.log.Logger;
* close the stream, to be reopened after the inclusion ends.</p>
*/
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
{
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();
/**
* @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();
/**
* 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);
@ -863,15 +917,19 @@ public class HttpOutput extends ServletOutputStream implements Runnable
public void recycle()
{
resetBuffer();
_interceptor=_channel;
if (BufferUtil.hasContent(_aggregate))
BufferUtil.clear(_aggregate);
_written = 0;
reopen();
}
public void resetBuffer()
{
_written = 0;
_interceptor.resetBuffer();
if (BufferUtil.hasContent(_aggregate))
BufferUtil.clear(_aggregate);
_written = 0;
reopen();
}

View File

@ -531,6 +531,9 @@ public class Response implements HttpServletResponse
LOG.debug("Aborting on sendError on committed response {} {}",code,message);
code=-1;
}
else
resetBuffer();
switch(code)
{
@ -544,7 +547,7 @@ public class Response implements HttpServletResponse
break;
}
resetBuffer();
_mimeType=null;
_characterEncoding=null;
_outputType = OutputType.NONE;
@ -1213,9 +1216,6 @@ public class Response implements HttpServletResponse
@Override
public void resetBuffer()
{
if (isCommitted())
throw new IllegalStateException("cannot reset buffer on committed response");
_out.resetBuffer();
}

View File

@ -201,6 +201,14 @@ public class BufferedResponseHandler extends HandlerWrapper
_next=interceptor;
_channel=httpChannel;
}
@Override
public void resetBuffer()
{
_buffers.clear();
_aggregating=null;
_aggregate=null;
};
@Override
public void write(ByteBuffer content, boolean last, Callback callback)

View File

@ -41,8 +41,6 @@ import org.junit.Test;
/**
* Resource Handler test
*
* TODO: increase the testing going on here
*/
public class BufferedResponseHandlerTest
{
@ -95,6 +93,7 @@ public class BufferedResponseHandlerTest
_test._writes=10;
_test._flush=false;
_test._close=false;
_test._reset=false;
}
@Test
@ -218,6 +217,18 @@ public class BufferedResponseHandlerTest
assertThat(response,not(containsString("Write: 1")));
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
{
@ -227,16 +238,25 @@ public class BufferedResponseHandlerTest
int _writes;
boolean _flush;
boolean _close;
boolean _reset;
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
if (_bufferSize>0)
response.setBufferSize(_bufferSize);
if (_mimeType!=null)
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++)
{
response.addHeader("Write",Integer.toString(i));
@ -248,7 +268,6 @@ public class BufferedResponseHandlerTest
if (_close)
response.getOutputStream().close();
response.addHeader("Written","true");
}
}
}
}