From f420f5016dd5714fac657e4ae5e0cef6dfca4bd2 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 19 Oct 2012 10:16:30 +1100 Subject: [PATCH] 392237 Implemented HttpOutput.sendContent for large content --- .../eclipse/jetty/embedded/LikeJettyXml.java | 2 +- .../org/eclipse/jetty/http/HttpContent.java | 9 +++ .../org/eclipse/jetty/io/ChannelEndPoint.java | 7 +- .../org/eclipse/jetty/server/HttpChannel.java | 20 +++--- .../org/eclipse/jetty/server/HttpOutput.java | 72 +++++++++++++++++-- .../eclipse/jetty/server/ResourceCache.java | 9 +++ .../jetty/server/session/AbstractSession.java | 18 ++--- .../jetty/util/resource/FileResource.java | 13 +++- .../eclipse/jetty/util/resource/Resource.java | 8 +++ .../util/resource/ResourceCollection.java | 17 +++++ .../jetty/util/resource/URLResource.java | 7 ++ .../eclipse/jetty/webapp/OrderingTest.java | 7 ++ 12 files changed, 155 insertions(+), 34 deletions(-) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java index c500162b082..92e10b24e63 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java @@ -41,7 +41,7 @@ public class LikeJettyXml { public static void main(String[] args) throws Exception { - String jetty_home = System.getProperty("jetty.home","../jetty-distribution/target/distribution"); + String jetty_home = System.getProperty("jetty.home","../../jetty-distribution/target/distribution"); System.setProperty("jetty.home",jetty_home); // Setup Threadpool diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java index 16ac07bd367..fa7861ea9ac 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.http; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; import org.eclipse.jetty.util.resource.Resource; @@ -38,6 +39,7 @@ public interface HttpContent Resource getResource(); long getContentLength(); InputStream getInputStream() throws IOException; + ReadableByteChannel getReadableByteChannel() throws IOException; void release(); /* ------------------------------------------------------------ */ @@ -130,6 +132,13 @@ public interface HttpContent { return _resource.getInputStream(); } + + /* ------------------------------------------------------------ */ + @Override + public ReadableByteChannel getReadableByteChannel() throws IOException + { + return _resource.getReadableByteChannel(); + } /* ------------------------------------------------------------ */ @Override diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java index 39ac51dcc0d..b0ac7a8e792 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java @@ -120,6 +120,11 @@ public class ChannelEndPoint extends AbstractEndPoint implements SocketBased { LOG.debug(e); } + finally + { + _ishut=true; + _oshut=true; + } } @Override @@ -179,7 +184,7 @@ public class ChannelEndPoint extends AbstractEndPoint implements SocketBased } LOG.debug("flushed {} {}", flushed, this); } - catch (ClosedChannelException | EOFException | SocketException e) + catch (IOException e) { throw new EofException(e); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index fb39ff107c9..126131decd3 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -330,20 +330,16 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable if (!committed) LOG.warn("Could not send response error 500: "+x); } + else if (isCommitted()) + { + if (!(x instanceof EofException)) + LOG.warn("Could not send response error 500: "+x); + } else { - // TODO: this error handling here must be atomic as above. - // TODO: response.sendError() should call back the HttpChannel in order to perform the atomic commit - if (!isCommitted()) - { - _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,x); - _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,x.getClass()); - _response.sendError(500, x.getMessage()); - } - else - { - LOG.warn("Could not send response error 500: "+x); - } + _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,x); + _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,x.getClass()); + _response.sendError(500, x.getMessage()); } } catch (IOException e) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java index 8cbc9d0f6f9..05a80f1282b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java @@ -22,6 +22,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; import javax.servlet.RequestDispatcher; import javax.servlet.ServletOutputStream; @@ -32,6 +33,8 @@ import org.eclipse.jetty.http.HttpContent; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; /** @@ -46,6 +49,7 @@ import org.eclipse.jetty.util.resource.Resource; */ public class HttpOutput extends ServletOutputStream { + private static Logger LOG = Log.getLogger(HttpOutput.class); private final HttpChannel _channel; private boolean _closed; private long _written; @@ -80,14 +84,22 @@ public class HttpOutput extends ServletOutputStream } @Override - public void close() throws IOException + public void close() { if (!_closed) { - if (BufferUtil.hasContent(_aggregate)) - _channel.write(_aggregate, !_channel.getResponse().isIncluding()); - else - _channel.write(BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding()); + try + { + if (BufferUtil.hasContent(_aggregate)) + _channel.write(_aggregate, !_channel.getResponse().isIncluding()); + else + _channel.write(BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding()); + } + catch(IOException e) + { + _channel.getEndPoint().shutdownOutput(); + LOG.ignore(e); + } } _closed = true; if (_aggregate != null) @@ -228,6 +240,8 @@ public class HttpOutput extends ServletOutputStream content = httpContent.getDirectBuffer(); if (content == null) content = httpContent.getIndirectBuffer(); + if (content == null) + content = httpContent.getReadableByteChannel(); if (content == null) content = httpContent.getInputStream(); } @@ -243,12 +257,56 @@ public class HttpOutput extends ServletOutputStream { _channel.write((ByteBuffer)content, true); // TODO: we have written all content ? } + else if (content instanceof ReadableByteChannel) + { + ReadableByteChannel channel = (ReadableByteChannel)content; + ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), true); + try + { + while(channel.isOpen()) + { + int pos = BufferUtil.flipToFill(buffer); + int len=channel.read(buffer); + if (len<0) + break; + BufferUtil.flipToFlush(buffer,pos); + _channel.write(buffer,false); + } + } + finally + { + close(); + _channel.getByteBufferPool().release(buffer); + } + } else if (content instanceof InputStream) { - throw new IllegalArgumentException("not implemented!"); + InputStream in = (InputStream)content; + ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), false); + byte[] array = buffer.array(); + int offset=buffer.arrayOffset(); + int size=array.length-offset; + try + { + while(true) + { + int len=in.read(array,offset,size); + if (len<0) + break; + buffer.position(0); + buffer.limit(len); + _channel.write(buffer,false); + } + _channel.write(BufferUtil.EMPTY_BUFFER,true); + } + finally + { + close(); + _channel.getByteBufferPool().release(buffer); + } } else - throw new IllegalArgumentException("unknown content type?"); + throw new IllegalArgumentException("unknown content type "+content.getClass()); } public int getBufferSize() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java index 9083579d231..0ebe1f3f0ca 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java @@ -22,6 +22,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; import java.util.Comparator; import java.util.SortedSet; import java.util.TreeSet; @@ -526,6 +527,14 @@ public class ResourceCache return _resource.getInputStream(); } + + /* ------------------------------------------------------------ */ + @Override + public ReadableByteChannel getReadableByteChannel() throws IOException + { + return _resource.getReadableByteChannel(); + } + /* ------------------------------------------------------------ */ @Override diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java index aeeb1d6b3ef..89b9612cda3 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java @@ -272,18 +272,6 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI } } - /* ------------------------------------------------------------ */ - protected Map getAttributeMap () - { - return _attributes; - } - - /* ------------------------------------------------------------ */ - protected void addAttributes(Map map) - { - _attributes.putAll(map); - } - /* ------------------------------------------------------------ */ protected boolean access(long time) { @@ -474,6 +462,12 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI } } + /* ------------------------------------------------------------ */ + protected void addAttributes(Map map) + { + _attributes.putAll(map); + } + /* ------------------------------------------------------------- */ public void setIdChanged(boolean changed) { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java index 211de5ba5fc..758aac8d9d7 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java @@ -29,6 +29,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.OpenOption; +import java.nio.file.StandardOpenOption; import java.security.Permission; import org.eclipse.jetty.util.IO; @@ -281,7 +285,14 @@ public class FileResource extends URLResource { return new FileInputStream(_file); } - + + /* ------------------------------------------------------------ */ + @Override + public ReadableByteChannel getReadableByteChannel() throws IOException + { + return FileChannel.open(_file.toPath(),StandardOpenOption.READ); + } + /* --------------------------------------------------------- */ /** * Returns an output stream to the resource diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java index bc13427e07e..5051cb1005b 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java @@ -27,6 +27,7 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.net.URLConnection; +import java.nio.channels.ReadableByteChannel; import java.text.DateFormat; import java.util.Arrays; import java.util.Date; @@ -408,6 +409,13 @@ public abstract class Resource implements ResourceFactory */ public abstract InputStream getInputStream() throws java.io.IOException; + + /* ------------------------------------------------------------ */ + /** + * Returns an readable bytechannel to the resource or null if one is not available. + */ + public abstract ReadableByteChannel getReadableByteChannel() + throws java.io.IOException; /* ------------------------------------------------------------ */ /** diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java index 5197403f3e3..1bc6d7ed7ce 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java @@ -24,6 +24,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; +import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -328,6 +329,22 @@ public class ResourceCollection extends Resource } return null; } + + /* ------------------------------------------------------------ */ + @Override + public ReadableByteChannel getReadableByteChannel() throws IOException + { + if(_resources==null) + throw new IllegalStateException("*resources* not set."); + + for(Resource r : _resources) + { + ReadableByteChannel channel = r.getReadableByteChannel(); + if(channel!=null) + return channel; + } + return null; + } /* ------------------------------------------------------------ */ @Override diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java index d59d7509764..09eaa330935 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java @@ -25,6 +25,7 @@ import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; +import java.nio.channels.ReadableByteChannel; import java.security.Permission; import org.eclipse.jetty.util.URIUtil; @@ -224,6 +225,12 @@ public class URLResource extends Resource } } + /* ------------------------------------------------------------ */ + @Override + public ReadableByteChannel getReadableByteChannel() throws IOException + { + return null; + } /* ------------------------------------------------------------ */ /** diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java index ba5f086a4b0..2babc578155 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java @@ -24,6 +24,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; +import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.List; @@ -96,6 +97,12 @@ public class OrderingTest return null; } + @Override + public ReadableByteChannel getReadableByteChannel() throws IOException + { + return null; + } + /** * @see org.eclipse.jetty.util.resource.Resource#getName() */