Issue #3207
updates after review Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
parent
225760c2de
commit
8f06fa8083
|
@ -27,6 +27,7 @@ import java.nio.channels.WritePendingException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.CharsetEncoder;
|
import java.nio.charset.CharsetEncoder;
|
||||||
import java.nio.charset.CoderResult;
|
import java.nio.charset.CoderResult;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
@ -127,6 +128,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Logger LOG = Log.getLogger(HttpOutput.class);
|
private static Logger LOG = Log.getLogger(HttpOutput.class);
|
||||||
|
private final static ThreadLocal<CharsetEncoder> _encoder = new ThreadLocal<>();
|
||||||
|
|
||||||
private final HttpChannel _channel;
|
private final HttpChannel _channel;
|
||||||
private final SharedBlockingCallback _writeBlocker;
|
private final SharedBlockingCallback _writeBlocker;
|
||||||
|
@ -138,7 +140,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
private int _bufferSize;
|
private int _bufferSize;
|
||||||
private int _commitSize;
|
private int _commitSize;
|
||||||
private WriteListener _writeListener;
|
private WriteListener _writeListener;
|
||||||
private CharsetEncoder _encoder;
|
|
||||||
private volatile Throwable _onError;
|
private volatile Throwable _onError;
|
||||||
/*
|
/*
|
||||||
ACTION OPEN ASYNC READY PENDING UNREADY CLOSED
|
ACTION OPEN ASYNC READY PENDING UNREADY CLOSED
|
||||||
|
@ -699,16 +700,23 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
if (isClosed())
|
if (isClosed())
|
||||||
throw new IOException("Closed");
|
throw new IOException("Closed");
|
||||||
|
|
||||||
// TODO would a Threadlocal pool be better?
|
|
||||||
String charset = _channel.getResponse().getCharacterEncoding();
|
String charset = _channel.getResponse().getCharacterEncoding();
|
||||||
if (_encoder==null || !_encoder.charset().name().equals(charset))
|
CharsetEncoder encoder = _encoder.get();
|
||||||
_encoder = Charset.forName(charset).newEncoder();
|
if (encoder==null || !encoder.charset().name().equalsIgnoreCase(charset))
|
||||||
|
{
|
||||||
|
encoder = Charset.forName(charset).newEncoder();
|
||||||
|
encoder.onMalformedInput(CodingErrorAction.REPLACE);
|
||||||
|
encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||||
|
_encoder.set(encoder);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
_encoder.reset();
|
{
|
||||||
|
encoder.reset();
|
||||||
|
}
|
||||||
|
|
||||||
CharBuffer in = CharBuffer.wrap(s);
|
CharBuffer in = CharBuffer.wrap(s);
|
||||||
CharBuffer crlf = eoln?CharBuffer.wrap("\r\n"):null;
|
CharBuffer crlf = eoln?CharBuffer.wrap("\r\n"):null;
|
||||||
ByteBuffer out = getHttpChannel().getByteBufferPool().acquire((int)(1+(s.length()+2)*_encoder.averageBytesPerChar()),false);
|
ByteBuffer out = getHttpChannel().getByteBufferPool().acquire((int)(1+(s.length()+2)*encoder.averageBytesPerChar()),false);
|
||||||
BufferUtil.flipToFill(out);
|
BufferUtil.flipToFill(out);
|
||||||
|
|
||||||
for(;;)
|
for(;;)
|
||||||
|
@ -716,7 +724,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
CoderResult result;
|
CoderResult result;
|
||||||
if (in.hasRemaining())
|
if (in.hasRemaining())
|
||||||
{
|
{
|
||||||
result = _encoder.encode(in, out, false);
|
result = encoder.encode(in, out, crlf==null);
|
||||||
if (result.isUnderflow())
|
if (result.isUnderflow())
|
||||||
if (crlf==null)
|
if (crlf==null)
|
||||||
break;
|
break;
|
||||||
|
@ -725,10 +733,10 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
}
|
}
|
||||||
else if (crlf.hasRemaining())
|
else if (crlf.hasRemaining())
|
||||||
{
|
{
|
||||||
result = _encoder.encode(crlf, out, true);
|
result = encoder.encode(crlf, out, true);
|
||||||
if (result.isUnderflow())
|
if (result.isUnderflow())
|
||||||
{
|
{
|
||||||
if (!_encoder.flush(out).isUnderflow())
|
if (!encoder.flush(out).isUnderflow())
|
||||||
result.throwException();
|
result.throwException();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -591,14 +591,24 @@ public class ResponseTest
|
||||||
session_handler.start();
|
session_handler.start();
|
||||||
request.setSessionHandler(session_handler);
|
request.setSessionHandler(session_handler);
|
||||||
HttpSession session = request.getSession(true);
|
HttpSession session = request.getSession(true);
|
||||||
|
response.setCharacterEncoding(UTF_8.name());
|
||||||
|
|
||||||
assertThat(session,not(nullValue()));
|
assertThat(session,not(nullValue()));
|
||||||
assertTrue(session.isNew());
|
assertTrue(session.isNew());
|
||||||
|
|
||||||
response.getOutputStream().println("ABC");
|
String expected = "";
|
||||||
|
response.getOutputStream().print("ABC");
|
||||||
|
expected += "ABC";
|
||||||
response.getOutputStream().println("XYZ");
|
response.getOutputStream().println("XYZ");
|
||||||
|
expected += "XYZ\r\n";
|
||||||
|
String s="";
|
||||||
|
for (int i=0; i<100; i++)
|
||||||
|
s += "\u20AC\u20AC\u20AC\u20AC\u20AC\u20AC\u20AC\u20AC\u20AC\u20AC";
|
||||||
|
response.getOutputStream().println(s);
|
||||||
|
expected += s +"\r\n";
|
||||||
|
|
||||||
response.getOutputStream().close();
|
response.getOutputStream().close();
|
||||||
assertEquals("ABC\r\nXYZ\r\n",BufferUtil.toString(_content));
|
assertEquals(expected,BufferUtil.toString(_content, UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue