diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java b/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java deleted file mode 100644 index dd10e475544..00000000000 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java +++ /dev/null @@ -1,683 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.Writer; - -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; - -/* ------------------------------------------------------------ */ -/** - * A wrapper for the {@link java.io.PrintWriter} that re-throws the instances of - * {@link java.io.IOException} thrown by the underlying implementation of - * {@link java.io.Writer} as {@link RuntimeIOException} instances. - */ -public class UncheckedPrintWriter extends PrintWriter -{ - private static final Logger LOG = Log.getLogger(UncheckedPrintWriter.class); - - private boolean _autoFlush = false; - private IOException _ioException; - private boolean _isClosed = false; - - /* ------------------------------------------------------------ */ - /** - * Line separator string. This is the value of the line.separator property - * at the moment that the stream was created. - */ - private String _lineSeparator; - - public UncheckedPrintWriter(Writer out) - { - this(out,false); - } - - /* ------------------------------------------------------------ */ - /** - * Create a new PrintWriter. - * - * @param out - * A character-output stream - * @param autoFlush - * A boolean; if true, the println() methods will flush the - * output buffer - */ - public UncheckedPrintWriter(Writer out, boolean autoFlush) - { - super(out,autoFlush); - this._autoFlush = autoFlush; - this._lineSeparator = System.getProperty("line.separator"); - } - - /* ------------------------------------------------------------ */ - /** - * Create a new PrintWriter, without automatic line flushing, from an - * existing OutputStream. This convenience constructor creates the necessary - * intermediate OutputStreamWriter, which will convert characters into bytes - * using the default character encoding. - * - * @param out - * An output stream - * - * @see java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream) - */ - public UncheckedPrintWriter(OutputStream out) - { - this(out,false); - } - - /* ------------------------------------------------------------ */ - /** - * Create a new PrintWriter from an existing OutputStream. This convenience - * constructor creates the necessary intermediate OutputStreamWriter, which - * will convert characters into bytes using the default character encoding. - * - * @param out - * An output stream - * @param autoFlush - * A boolean; if true, the println() methods will flush the - * output buffer - * - * @see java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream) - */ - public UncheckedPrintWriter(OutputStream out, boolean autoFlush) - { - this(new BufferedWriter(new OutputStreamWriter(out)),autoFlush); - } - - - /* ------------------------------------------------------------ */ - public boolean checkError() - { - return _ioException!=null || super.checkError(); - } - - /* ------------------------------------------------------------ */ - private void setError(Throwable th) - { - - super.setError(); - - if (th instanceof IOException) - _ioException=(IOException)th; - else - { - _ioException=new IOException(String.valueOf(th)); - _ioException.initCause(th); - } - - if (LOG.isDebugEnabled()) - LOG.debug(th); - } - - - @Override - protected void setError() - { - setError(new IOException()); - } - - /* ------------------------------------------------------------ */ - /** Check to make sure that the stream has not been closed */ - private void isOpen() throws IOException - { - if (_ioException!=null) - throw new RuntimeIOException(_ioException); - - if (_isClosed) - throw new IOException("Stream closed"); - } - - /* ------------------------------------------------------------ */ - /** - * Flush the stream. - */ - @Override - public void flush() - { - try - { - synchronized (lock) - { - isOpen(); - out.flush(); - } - } - catch (IOException ex) - { - setError(ex); - } - } - - /* ------------------------------------------------------------ */ - /** - * Close the stream. - */ - @Override - public void close() - { - try - { - synchronized (lock) - { - out.close(); - _isClosed = true; - } - } - catch (IOException ex) - { - setError(ex); - } - } - - /* ------------------------------------------------------------ */ - /** - * Write a single character. - * - * @param c - * int specifying a character to be written. - */ - @Override - public void write(int c) - { - try - { - synchronized (lock) - { - isOpen(); - out.write(c); - } - } - catch (InterruptedIOException x) - { - Thread.currentThread().interrupt(); - } - catch (IOException ex) - { - setError(ex); - } - } - - /* ------------------------------------------------------------ */ - /** - * Write a portion of an array of characters. - * - * @param buf - * Array of characters - * @param off - * Offset from which to start writing characters - * @param len - * Number of characters to write - */ - @Override - public void write(char buf[], int off, int len) - { - try - { - synchronized (lock) - { - isOpen(); - out.write(buf,off,len); - } - } - catch (InterruptedIOException x) - { - Thread.currentThread().interrupt(); - } - catch (IOException ex) - { - setError(ex); - } - } - - /* ------------------------------------------------------------ */ - /** - * Write an array of characters. This method cannot be inherited from the - * Writer class because it must suppress I/O exceptions. - * - * @param buf - * Array of characters to be written - */ - @Override - public void write(char buf[]) - { - this.write(buf,0,buf.length); - } - - /* ------------------------------------------------------------ */ - /** - * Write a portion of a string. - * - * @param s - * A String - * @param off - * Offset from which to start writing characters - * @param len - * Number of characters to write - */ - @Override - public void write(String s, int off, int len) - { - try - { - synchronized (lock) - { - isOpen(); - out.write(s,off,len); - } - } - catch (InterruptedIOException x) - { - Thread.currentThread().interrupt(); - } - catch (IOException ex) - { - setError(ex); - } - } - - /* ------------------------------------------------------------ */ - /** - * Write a string. This method cannot be inherited from the Writer class - * because it must suppress I/O exceptions. - * - * @param s - * String to be written - */ - @Override - public void write(String s) - { - this.write(s,0,s.length()); - } - - private void newLine() - { - try - { - synchronized (lock) - { - isOpen(); - out.write(_lineSeparator); - if (_autoFlush) - out.flush(); - } - } - catch (InterruptedIOException x) - { - Thread.currentThread().interrupt(); - } - catch (IOException ex) - { - setError(ex); - } - } - - /* ------------------------------------------------------------ */ - /** - * Print a boolean value. The string produced by {@link - * java.lang.String#valueOf(boolean)} is translated into bytes - * according to the platform's default character encoding, and these bytes - * are written in exactly the manner of the {@link - * #write(int)} method. - * - * @param b - * The boolean to be printed - */ - @Override - public void print(boolean b) - { - this.write(b?"true":"false"); - } - - /* ------------------------------------------------------------ */ - /** - * Print a character. The character is translated into one or more bytes - * according to the platform's default character encoding, and these bytes - * are written in exactly the manner of the {@link - * #write(int)} method. - * - * @param c - * The char to be printed - */ - @Override - public void print(char c) - { - this.write(c); - } - - /* ------------------------------------------------------------ */ - /** - * Print an integer. The string produced by {@link - * java.lang.String#valueOf(int)} is translated into bytes according - * to the platform's default character encoding, and these bytes are written - * in exactly the manner of the {@link #write(int)} method. - * - * @param i - * The int to be printed - * @see java.lang.Integer#toString(int) - */ - @Override - public void print(int i) - { - this.write(String.valueOf(i)); - } - - /* ------------------------------------------------------------ */ - /** - * Print a long integer. The string produced by {@link - * java.lang.String#valueOf(long)} is translated into bytes according - * to the platform's default character encoding, and these bytes are written - * in exactly the manner of the {@link #write(int)} method. - * - * @param l - * The long to be printed - * @see java.lang.Long#toString(long) - */ - @Override - public void print(long l) - { - this.write(String.valueOf(l)); - } - - /* ------------------------------------------------------------ */ - /** - * Print a floating-point number. The string produced by {@link - * java.lang.String#valueOf(float)} is translated into bytes - * according to the platform's default character encoding, and these bytes - * are written in exactly the manner of the {@link #write(int)} - * method. - * - * @param f - * The float to be printed - * @see java.lang.Float#toString(float) - */ - @Override - public void print(float f) - { - this.write(String.valueOf(f)); - } - - /* ------------------------------------------------------------ */ - /** - * Print a double-precision floating-point number. The string produced by - * {@link java.lang.String#valueOf(double)} is translated into - * bytes according to the platform's default character encoding, and these - * bytes are written in exactly the manner of the {@link - * #write(int)} method. - * - * @param d - * The double to be printed - * @see java.lang.Double#toString(double) - */ - @Override - public void print(double d) - { - this.write(String.valueOf(d)); - } - - /* ------------------------------------------------------------ */ - /** - * Print an array of characters. The characters are converted into bytes - * according to the platform's default character encoding, and these bytes - * are written in exactly the manner of the {@link #write(int)} - * method. - * - * @param s - * The array of chars to be printed - * - * @throws NullPointerException - * If s is null - */ - @Override - public void print(char s[]) - { - this.write(s); - } - - /* ------------------------------------------------------------ */ - /** - * Print a string. If the argument is null then the string - * "null" is printed. Otherwise, the string's characters are - * converted into bytes according to the platform's default character - * encoding, and these bytes are written in exactly the manner of the - * {@link #write(int)} method. - * - * @param s - * The String to be printed - */ - @Override - public void print(String s) - { - if (s == null) - { - s = "null"; - } - this.write(s); - } - - /* ------------------------------------------------------------ */ - /** - * Print an object. The string produced by the {@link - * java.lang.String#valueOf(Object)} method is translated into bytes - * according to the platform's default character encoding, and these bytes - * are written in exactly the manner of the {@link #write(int)} - * method. - * - * @param obj - * The Object to be printed - * @see java.lang.Object#toString() - */ - @Override - public void print(Object obj) - { - this.write(String.valueOf(obj)); - } - - /* ------------------------------------------------------------ */ - /** - * Terminate the current line by writing the line separator string. The line - * separator string is defined by the system property - * line.separator, and is not necessarily a single newline - * character ('\n'). - */ - @Override - public void println() - { - this.newLine(); - } - - /* ------------------------------------------------------------ */ - /** - * Print a boolean value and then terminate the line. This method behaves as - * though it invokes {@link #print(boolean)} and then - * {@link #println()}. - * - * @param x - * the boolean value to be printed - */ - @Override - public void println(boolean x) - { - synchronized (lock) - { - this.print(x); - this.println(); - } - } - - /* ------------------------------------------------------------ */ - /** - * Print a character and then terminate the line. This method behaves as - * though it invokes {@link #print(char)} and then {@link - * #println()}. - * - * @param x - * the char value to be printed - */ - @Override - public void println(char x) - { - synchronized (lock) - { - this.print(x); - this.println(); - } - } - - /* ------------------------------------------------------------ */ - /** - * Print an integer and then terminate the line. This method behaves as - * though it invokes {@link #print(int)} and then {@link - * #println()}. - * - * @param x - * the int value to be printed - */ - @Override - public void println(int x) - { - synchronized (lock) - { - this.print(x); - this.println(); - } - } - - /* ------------------------------------------------------------ */ - /** - * Print a long integer and then terminate the line. This method behaves as - * though it invokes {@link #print(long)} and then - * {@link #println()}. - * - * @param x - * the long value to be printed - */ - @Override - public void println(long x) - { - synchronized (lock) - { - this.print(x); - this.println(); - } - } - - /* ------------------------------------------------------------ */ - /** - * Print a floating-point number and then terminate the line. This method - * behaves as though it invokes {@link #print(float)} and then - * {@link #println()}. - * - * @param x - * the float value to be printed - */ - @Override - public void println(float x) - { - synchronized (lock) - { - this.print(x); - this.println(); - } - } - - /* ------------------------------------------------------------ */ - /** - * Print a double-precision floating-point number and then terminate the - * line. This method behaves as though it invokes {@link - * #print(double)} and then {@link #println()}. - * - * @param x - * the double value to be printed - */ - /* ------------------------------------------------------------ */ - @Override - public void println(double x) - { - synchronized (lock) - { - this.print(x); - this.println(); - } - } - - /* ------------------------------------------------------------ */ - /** - * Print an array of characters and then terminate the line. This method - * behaves as though it invokes {@link #print(char[])} and then - * {@link #println()}. - * - * @param x - * the array of char values to be printed - */ - @Override - public void println(char x[]) - { - synchronized (lock) - { - this.print(x); - this.println(); - } - } - - /* ------------------------------------------------------------ */ - /** - * Print a String and then terminate the line. This method behaves as though - * it invokes {@link #print(String)} and then - * {@link #println()}. - * - * @param x - * the String value to be printed - */ - @Override - public void println(String x) - { - synchronized (lock) - { - this.print(x); - this.println(); - } - } - - /* ------------------------------------------------------------ */ - /** - * Print an Object and then terminate the line. This method behaves as - * though it invokes {@link #print(Object)} and then - * {@link #println()}. - * - * @param x - * the Object value to be printed - */ - @Override - public void println(Object x) - { - synchronized (lock) - { - this.print(x); - this.println(); - } - } -} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index 3ee09bf00e6..d044e867e5f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -917,16 +917,18 @@ public class Response implements HttpServletResponse } } - if (_writer != null && _writer.isFor(encoding)) + Locale locale = getLocale(); + + if (_writer != null && _writer.isFor(locale,encoding)) _writer.reopen(); else { if (StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding)) - _writer = new ResponseWriter(new Iso88591HttpWriter(_out),encoding); + _writer = new ResponseWriter(new Iso88591HttpWriter(_out),locale,encoding); else if (StringUtil.__UTF8.equalsIgnoreCase(encoding)) - _writer = new ResponseWriter(new Utf8HttpWriter(_out),encoding); + _writer = new ResponseWriter(new Utf8HttpWriter(_out),locale,encoding); else - _writer = new ResponseWriter(new EncodingHttpWriter(_out, encoding),encoding); + _writer = new ResponseWriter(new EncodingHttpWriter(_out, encoding),locale,encoding); } // Set the output type at the end, because setCharacterEncoding() checks for it @@ -1321,30 +1323,6 @@ public class Response implements HttpServletResponse } - private static class ResponseWriter extends PrintWriter - { - private final String _encoding; - private final HttpWriter _httpWriter; - - public ResponseWriter(HttpWriter httpWriter,String encoding) - { - super(httpWriter); - _httpWriter=httpWriter; - _encoding=encoding; - } - - public boolean isFor(String encoding) - { - return _encoding.equalsIgnoreCase(encoding); - } - - protected void reopen() - { - super.clearError(); - out=_httpWriter; - } - } - public void putHeaders(HttpContent content,long contentLength, boolean etag) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResponseWriter.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResponseWriter.java new file mode 100644 index 00000000000..c4c52573c26 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResponseWriter.java @@ -0,0 +1,480 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.PrintWriter; +import java.util.Formatter; +import java.util.Locale; + +import javax.servlet.ServletResponse; + +import org.eclipse.jetty.io.RuntimeIOException; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + + +/* ------------------------------------------------------------ */ +/** + *

An instance of ResponseWriter is the {@link PrintWriter} subclass returned by {@link Response#getWriter()}. + * It differs from the standard {@link PrintWriter} in that:

+ * + */ +class ResponseWriter extends PrintWriter +{ + private static final Logger LOG = Log.getLogger(ResponseWriter.class); + private final static String __lineSeparator = System.getProperty("line.separator"); + private final static String __trueln = "true"+__lineSeparator; + private final static String __falseln = "false"+__lineSeparator; + + private final HttpWriter _httpWriter; + private final Locale _locale; + private final String _encoding; + private IOException _ioException; + private boolean _isClosed = false; + private Formatter _formatter; + + public ResponseWriter(HttpWriter httpWriter,Locale locale,String encoding) + { + super(httpWriter,false); + _httpWriter=httpWriter; + _locale=locale; + _encoding=encoding; + } + + public boolean isFor(Locale locale, String encoding) + { + if (_locale==null && locale!=null) + return false; + if (_encoding==null && encoding!=null) + return false; + return _encoding.equalsIgnoreCase(encoding) && _locale.equals(locale); + } + + protected void reopen() + { + synchronized (lock) + { + _isClosed=false; + clearError(); + out=_httpWriter; + } + } + + @Override + protected void clearError() + { + synchronized (lock) + { + _ioException=null; + super.clearError(); + } + } + + @Override + public boolean checkError() + { + synchronized (lock) + { + return _ioException!=null || super.checkError(); + } + } + + private void setError(Throwable th) + { + super.setError(); + + if (th instanceof IOException) + _ioException=(IOException)th; + else + { + _ioException=new IOException(String.valueOf(th)); + _ioException.initCause(th); + } + + if (LOG.isDebugEnabled()) + LOG.debug(th); + } + + + @Override + protected void setError() + { + setError(new IOException()); + } + + /** Check to make sure that the stream has not been closed */ + private void isOpen() throws IOException + { + if (_ioException!=null) + throw new RuntimeIOException(_ioException); + + if (_isClosed) + throw new IOException("Stream closed"); + } + + @Override + public void flush() + { + try + { + synchronized (lock) + { + isOpen(); + out.flush(); + } + } + catch (IOException ex) + { + setError(ex); + } + } + + @Override + public void close() + { + try + { + synchronized (lock) + { + out.close(); + _isClosed = true; + } + } + catch (IOException ex) + { + setError(ex); + } + } + + @Override + public void write(int c) + { + try + { + synchronized (lock) + { + isOpen(); + out.write(c); + } + } + catch (InterruptedIOException ex) + { + LOG.debug(ex); + Thread.currentThread().interrupt(); + } + catch (IOException ex) + { + setError(ex); + } + } + + @Override + public void write(char buf[], int off, int len) + { + try + { + synchronized (lock) + { + isOpen(); + out.write(buf,off,len); + } + } + catch (InterruptedIOException ex) + { + LOG.debug(ex); + Thread.currentThread().interrupt(); + } + catch (IOException ex) + { + setError(ex); + } + } + + @Override + public void write(char buf[]) + { + this.write(buf,0,buf.length); + } + + @Override + public void write(String s, int off, int len) + { + try + { + synchronized (lock) + { + isOpen(); + out.write(s,off,len); + } + } + catch (InterruptedIOException ex) + { + LOG.debug(ex); + Thread.currentThread().interrupt(); + } + catch (IOException ex) + { + setError(ex); + } + } + + @Override + public void write(String s) + { + this.write(s,0,s.length()); + } + + @Override + public void print(boolean b) + { + this.write(b?"true":"false"); + } + + @Override + public void print(char c) + { + this.write(c); + } + + @Override + public void print(int i) + { + this.write(String.valueOf(i)); + } + + @Override + public void print(long l) + { + this.write(String.valueOf(l)); + } + + @Override + public void print(float f) + { + this.write(String.valueOf(f)); + } + + @Override + public void print(double d) + { + this.write(String.valueOf(d)); + } + + @Override + public void print(char s[]) + { + this.write(s); + } + + @Override + public void print(String s) + { + if (s == null) + s = "null"; + this.write(s); + } + + @Override + public void print(Object obj) + { + this.write(String.valueOf(obj)); + } + + @Override + public void println() + { + try + { + synchronized (lock) + { + isOpen(); + out.write(__lineSeparator); + } + } + catch (InterruptedIOException ex) + { + LOG.debug(ex); + Thread.currentThread().interrupt(); + } + catch (IOException ex) + { + setError(ex); + } + } + + @Override + public void println(boolean b) + { + println(b?__trueln:__falseln); + } + + @Override + public void println(char c) + { + try + { + synchronized (lock) + { + isOpen(); + out.write(c); + } + } + catch (InterruptedIOException ex) + { + LOG.debug(ex); + Thread.currentThread().interrupt(); + } + catch (IOException ex) + { + setError(ex); + } + } + + @Override + public void println(int x) + { + this.println(String.valueOf(x)); + } + + @Override + public void println(long x) + { + this.println(String.valueOf(x)); + } + + @Override + public void println(float x) + { + this.println(String.valueOf(x)); + } + + @Override + public void println(double x) + { + this.println(String.valueOf(x)); + } + + @Override + public void println(char s[]) + { + try + { + synchronized (lock) + { + isOpen(); + out.write(s,0,s.length); + out.write(__lineSeparator); + } + } + catch (InterruptedIOException ex) + { + LOG.debug(ex); + Thread.currentThread().interrupt(); + } + catch (IOException ex) + { + setError(ex); + } + } + + @Override + public void println(String s) + { + if (s == null) + s = "null"; + + try + { + synchronized (lock) + { + isOpen(); + out.write(s,0,s.length()); + out.write(__lineSeparator); + } + } + catch (InterruptedIOException ex) + { + LOG.debug(ex); + Thread.currentThread().interrupt(); + } + catch (IOException ex) + { + setError(ex); + } + } + + @Override + public void println(Object x) + { + this.println(String.valueOf(x)); + } + + @Override + public PrintWriter printf(String format, Object... args) + { + return format(_locale,format,args); + } + + @Override + public PrintWriter printf(Locale l, String format, Object... args) + { + return format(l,format,args); + } + + @Override + public PrintWriter format(String format, Object... args) + { + return format(_locale,format,args); + } + + @Override + public PrintWriter format(Locale l, String format, Object... args) + { + try + { + synchronized (lock) + { + isOpen(); + if ((_formatter == null) || (_formatter.locale() != l)) + _formatter = new Formatter(this, l); + _formatter.format(l, format, args); + } + } + catch (InterruptedIOException ex) + { + LOG.debug(ex); + Thread.currentThread().interrupt(); + } + catch (IOException ex) + { + setError(ex); + } + return this; + } + + + +} \ No newline at end of file diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java index 41d43353242..a5a708d56ee 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java @@ -50,6 +50,7 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.io.AbstractEndPoint; import org.eclipse.jetty.io.ByteArrayEndPoint; +import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.session.HashSessionIdManager; @@ -83,10 +84,15 @@ public class ResponseTest AbstractEndPoint endp = new ByteArrayEndPoint(_scheduler, 5000); _channel = new HttpChannel(connector, new HttpConfiguration(), endp, new HttpTransport() { + private Throwable _channelError; + @Override public void send(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback) { - callback.succeeded(); + if (_channelError==null) + callback.succeeded(); + else + callback.failed(_channelError); } @Override @@ -108,6 +114,7 @@ public class ResponseTest @Override public void abort(Throwable failure) { + _channelError=failure; } @Override @@ -418,6 +425,32 @@ public class ResponseTest assertEquals("must-revalidate,no-cache,no-store", response.getHeader(HttpHeader.CACHE_CONTROL.asString())); } + @Test + public void testWriteRuntimeIOException() throws Exception + { + Response response = newResponse(); + + PrintWriter writer = response.getWriter(); + writer.println("test"); + writer.flush(); + Assert.assertFalse(writer.checkError()); + + Throwable cause = new IOException("problem at mill"); + _channel.abort(cause); + writer.println("test"); + Assert.assertTrue(writer.checkError()); + try + { + writer.println("test"); + Assert.fail(); + } + catch(RuntimeIOException e) + { + Assert.assertEquals(cause,e.getCause()); + } + + } + @Test public void testEncodeRedirect() throws Exception