Issue #3207
Fix #3207 by providing async compliant print methods Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
parent
28533d4ca6
commit
225760c2de
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue