Fix #3207 by providing async compliant print methods

Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Greg Wilkins 2018-12-19 13:57:58 +11:00
parent 28533d4ca6
commit 225760c2de
2 changed files with 134 additions and 1 deletions

View File

@ -21,8 +21,13 @@ package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritePendingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.util.ResourceBundle;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@ -56,6 +61,9 @@ import org.eclipse.jetty.util.log.Logger;
*/
public class HttpOutput extends ServletOutputStream implements Runnable
{
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle(LSTRING_FILE);
/**
* The HttpOutput.Interceptor is a single intercept point for all
* output written to the HttpOutput: via writer; via output stream;
@ -130,6 +138,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
private int _bufferSize;
private int _commitSize;
private WriteListener _writeListener;
private CharsetEncoder _encoder;
private volatile Throwable _onError;
/*
ACTION OPEN ASYNC READY PENDING UNREADY CLOSED
@ -551,6 +560,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
public void write(ByteBuffer buffer) throws IOException
{
// This write always bypasses aggregate buffer
// Async or Blocking ?
while (true)
{
@ -673,11 +684,109 @@ public class HttpOutput extends ServletOutputStream implements Runnable
@Override
public void print(String s) throws IOException
{
print(s,false);
}
@Override
public void println(String s) throws IOException
{
print(s,true);
}
private void print(String s, boolean eoln) throws IOException
{
if (isClosed())
throw new IOException("Closed");
write(s.getBytes(_channel.getResponse().getCharacterEncoding()));
// TODO would a Threadlocal pool be better?
String charset = _channel.getResponse().getCharacterEncoding();
if (_encoder==null || !_encoder.charset().name().equals(charset))
_encoder = Charset.forName(charset).newEncoder();
else
_encoder.reset();
CharBuffer in = CharBuffer.wrap(s);
CharBuffer crlf = eoln?CharBuffer.wrap("\r\n"):null;
ByteBuffer out = getHttpChannel().getByteBufferPool().acquire((int)(1+(s.length()+2)*_encoder.averageBytesPerChar()),false);
BufferUtil.flipToFill(out);
for(;;)
{
CoderResult result;
if (in.hasRemaining())
{
result = _encoder.encode(in, out, false);
if (result.isUnderflow())
if (crlf==null)
break;
else
continue;
}
else if (crlf.hasRemaining())
{
result = _encoder.encode(crlf, out, true);
if (result.isUnderflow())
{
if (!_encoder.flush(out).isUnderflow())
result.throwException();
break;
}
}
else
break;
if (result.isOverflow())
{
BufferUtil.flipToFlush(out,0);
ByteBuffer bigger = BufferUtil.ensureCapacity(out,out.capacity()+s.length()+2);
getHttpChannel().getByteBufferPool().release(out);
BufferUtil.flipToFill(bigger);
out = bigger;
continue;
}
result.throwException();
}
BufferUtil.flipToFlush(out,0);
write(out.array(),out.arrayOffset(),out.remaining());
getHttpChannel().getByteBufferPool().release(out);
}
@Override
public void println(boolean b) throws IOException
{
println(lStrings.getString(b? "value.true":"value.false"));
}
@Override
public void println(char c) throws IOException
{
println(String.valueOf(c));
}
@Override
public void println(int i) throws IOException
{
println(String.valueOf(i));
}
@Override
public void println(long l) throws IOException
{
println(String.valueOf(l));
}
@Override
public void println(float f) throws IOException
{
println(String.valueOf(f));
}
@Override
public void println(double d) throws IOException
{
println(String.valueOf(d));
}
/**

View File

@ -578,6 +578,30 @@ public class ResponseTest
assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
}
@Test
public void testPrintln() throws Exception
{
Response response = getResponse();
Request request = response.getHttpChannel().getRequest();
SessionHandler session_handler = new SessionHandler();
session_handler.setServer(_server);
session_handler.setUsingCookies(true);
session_handler.start();
request.setSessionHandler(session_handler);
HttpSession session = request.getSession(true);
assertThat(session,not(nullValue()));
assertTrue(session.isNew());
response.getOutputStream().println("ABC");
response.getOutputStream().println("XYZ");
response.getOutputStream().close();
assertEquals("ABC\r\nXYZ\r\n",BufferUtil.toString(_content));
}
@Test
public void testContentTypeWithOther() throws Exception
{