Merge remote-tracking branch 'origin/master' into servlet-3.1-api

Conflicts:
	jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
This commit is contained in:
Greg Wilkins 2013-05-17 14:09:17 +10:00
commit 5397f16559
31 changed files with 1900 additions and 487 deletions

View File

@ -0,0 +1,181 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.embedded;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.StandardOpenOption;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
/* ------------------------------------------------------------ */
/** Fast FileServer.
*
* <p>This example shows how to use the Jetty APIs for sending static
* as fast as possible using various strategies for small, medium and
* large content.</p>
* <p>The Jetty {@link DefaultServlet} does all this and more, and to
* a lesser extent so does the {@link ResourceHandler}, so unless you
* have exceptional circumstances it is best to use those classes for
* static content</p>
*/
public class FastFileServer
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { new FastFileHandler(new File(".")), new DefaultHandler() });
server.setHandler(handlers);
server.start();
server.join();
}
static class FastFileHandler extends AbstractHandler
{
private final MimeTypes _mimeTypes = new MimeTypes();
private final File _dir;
FastFileHandler(File dir)
{
_dir=dir;
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
// define small medium and large.
// This should be turned for your content, JVM and OS, but we will huge HTTP response buffer size as a measure
final int SMALL=response.getBufferSize();
final int MEDIUM=8*SMALL;
// What file to serve?
final File file = new File(_dir,request.getPathInfo());
// Only handle existing files
if (!file.exists())
return;
// we will handle this request
baseRequest.setHandled(true);
// Handle directories
if (file.isDirectory())
{
if (!request.getPathInfo().endsWith(URIUtil.SLASH))
{
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
return;
}
String listing = Resource.newResource(file).getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().println(listing);
return;
}
// Set some content headers
// Jetty DefaultServlet will cache formatted date strings, but we will reformat for each request here
response.setDateHeader("Last-Modified",file.lastModified());
response.setDateHeader("Content-Length",file.length());
response.setContentType(_mimeTypes.getMimeByExtension(file.getName()));
// send "small" files blocking directly from an input stream
if (file.length()<SMALL)
{
// need to caste to Jetty output stream for best API
((HttpOutput)response.getOutputStream()).sendContent(FileChannel.open(file.toPath(),StandardOpenOption.READ));
return;
}
// send not "small" files asynchronously so we don't hold threads if the client is slow
final AsyncContext async = request.startAsync();
Callback completionCB = new Callback()
{
@Override
public void succeeded()
{
// Async content write succeeded, so complete async response
async.complete();
}
@Override
public void failed(Throwable x)
{
// log error and complete async response;
x.printStackTrace();
async.complete();
}
};
// send "medium" files from an input stream
if (file.length()<MEDIUM)
{
((HttpOutput)response.getOutputStream()).sendContent(FileChannel.open(file.toPath(),StandardOpenOption.READ),completionCB);
return;
}
// for "large" files get the file mapped buffer to send
// Typically the resulting buffer should be cached as allocating kernel memory
// can be hard to GC on some JVMs. But for this example we will create a new buffer per file
ByteBuffer buffer;
try (RandomAccessFile raf = new RandomAccessFile(file,"r");)
{
buffer=raf.getChannel().map(MapMode.READ_ONLY,0,raf.length());
}
// Assuming the file buffer might be shared cached version, so lets take our own view of it
buffer=buffer.asReadOnlyBuffer();
// send the content as a buffer with a callback to complete the async request
// need to caste to Jetty output stream for best API
((HttpOutput)response.getOutputStream()).sendContent(buffer,completionCB);
}
}
}

View File

@ -54,13 +54,13 @@ public class Activator implements BundleActivator
@Override
public void contextInitialized(ServletContextEvent sce)
{
System.err.println("Context is initialized");
// System.err.println("Context is initialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce)
{
System.err.println("CONTEXT IS DESTROYED!");
// System.err.println("CONTEXT IS DESTROYED!");
}
});

View File

@ -120,6 +120,11 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
return _version;
}
BlockingCallback getWriteBlockingCallback()
{
return _writeblock;
}
/**
* @return the number of requests handled by this connection
*/
@ -587,9 +592,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
try
{
if (_state.handling()==Next.CONTINUE)
{
sendResponse(new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),0,status,reason,false),null,true);
}
}
catch (IOException e)
{
@ -609,62 +612,19 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
{
// We need an info to commit
if (info==null)
{
info = _response.newResponseInfo();
}
// wrap callback to process 100 or 500 responses
final int status=info.getStatus();
final Callback committed = new Callback()
{
@Override
public void succeeded()
{
// If we are committing a 1xx response, we need to reset the commit
// status so that the "real" response can be committed again.
if (status<200 && status>=100)
_committed.set(false);
callback.succeeded();
}
@Override
public void failed(final Throwable x)
{
if (x instanceof EofException)
{
LOG.debug(x);
_response.getHttpOutput().closed();
callback.failed(x);
}
else
{
LOG.warn(x);
_transport.send(HttpGenerator.RESPONSE_500_INFO,null,true,new Callback()
{
@Override
public void succeeded()
{
_response.getHttpOutput().closed();
callback.failed(x);
}
@Override
public void failed(Throwable th)
{
LOG.ignore(th);
_response.getHttpOutput().closed();
callback.failed(x);
}
});
}
}
};
final Callback committed = (status<200&&status>=100)?new Commit100Callback(callback):new CommitCallback(callback);
// committing write
_transport.send(info, content, complete, committed);
}
else if (info==null)
{
// This is a normal write
_transport.send(null, content, complete, callback);
_transport.send(content, complete, callback);
}
else
{
@ -676,16 +636,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
protected boolean sendResponse(ResponseInfo info, ByteBuffer content, boolean complete) throws IOException
{
boolean committing=sendResponse(info,content,complete,_writeblock);
try
{
_writeblock.block();
}
catch (InterruptedException | TimeoutException e)
{
throw new IOException(e);
}
_writeblock.block();
return committing;
}
@ -695,8 +646,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
}
/**
* <p>Requests to write (in a blocking way) the given response content buffer,
* committing the response if needed.</p>
* <p>Blocking write, committing the response if needed.</p>
*
* @param content the content buffer to write
* @param complete whether the content is complete for the response
@ -704,7 +654,21 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
*/
protected void write(ByteBuffer content, boolean complete) throws IOException
{
sendResponse(null, content, complete);
sendResponse(null,content,complete,_writeblock);
_writeblock.block();
}
/**
* <p>Non-Blocking write, committing the response if needed.</p>
*
* @param content the content buffer to write
* @param complete whether the content is complete for the response
* @param callback Callback when complete or failed
* @throws IOException if the write fails
*/
protected void write(ByteBuffer content, boolean complete, Callback callback)
{
sendResponse(null,content,complete,callback);
}
protected void execute(Runnable task)
@ -725,4 +689,68 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
{
return getEndPoint() instanceof ChannelEndPoint;
}
private class CommitCallback implements Callback
{
private final Callback _callback;
private CommitCallback(Callback callback)
{
_callback = callback;
}
@Override
public void succeeded()
{
_callback.succeeded();
}
@Override
public void failed(final Throwable x)
{
if (x instanceof EofException)
{
LOG.debug(x);
_response.getHttpOutput().closed();
_callback.failed(x);
}
else
{
LOG.warn(x);
_transport.send(HttpGenerator.RESPONSE_500_INFO,null,true,new Callback()
{
@Override
public void succeeded()
{
_response.getHttpOutput().closed();
_callback.failed(x);
}
@Override
public void failed(Throwable th)
{
LOG.ignore(th);
_response.getHttpOutput().closed();
_callback.failed(x);
}
});
}
}
}
private class Commit100Callback extends CommitCallback
{
private Commit100Callback(Callback callback)
{
super(callback);
}
@Override
public void succeeded()
{
_committed.set(false);
super.succeeded();
}
}
}

View File

@ -569,6 +569,24 @@ public class HttpChannelState
}
}
public boolean isAsyncStarted()
{
synchronized (this)
{
switch(_state)
{
case ASYNCSTARTED:
case REDISPATCHING:
case COMPLETECALLED:
case ASYNCWAIT:
return true;
default:
return false;
}
}
}
public boolean isAsync()
{
synchronized (this)

View File

@ -41,6 +41,7 @@ import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.util.BlockingCallback;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingCallback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -304,50 +305,54 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
onFillable();
}
@Override
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent) throws IOException
{
try
{
// If we are still expecting a 100 continues
if (info !=null && _channel.isExpecting100Continue())
// then we can't be persistent
_generator.setPersistent(false);
Sender sender = new Sender(content,lastContent,_writeBlocker);
sender.process(info);
if (info==null)
new ContentCallback(content,lastContent,_writeBlocker).iterate();
else
{
// If we are still expecting a 100 continues
if (_channel.isExpecting100Continue())
// then we can't be persistent
_generator.setPersistent(false);
new CommitCallback(info,content,lastContent,_writeBlocker).iterate();
}
_writeBlocker.block();
}
catch (InterruptedException x)
{
x.printStackTrace();
throw (IOException)new InterruptedIOException().initCause(x);
}
catch (TimeoutException e)
{
e.printStackTrace();
throw new IOException(e);
}
catch (ClosedChannelException e)
{
e.printStackTrace();
throw new EofException(e);
}
catch (IOException e)
{
throw e;
}
}
@Override
public void send(ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
{
// If we are still expecting a 100 continues
if (info !=null && _channel.isExpecting100Continue())
// then we can't be persistent
_generator.setPersistent(false);
new Sender(content,lastContent,callback).process(info);
if (info==null)
new ContentCallback(content,lastContent,callback).iterate();
else
{
// If we are still expecting a 100 continues
if (_channel.isExpecting100Continue())
// then we can't be persistent
_generator.setPersistent(false);
new CommitCallback(info,content,lastContent,callback).iterate();
}
}
@Override
public void send(ByteBuffer content, boolean lastContent, Callback callback)
{
new ContentCallback(content,lastContent,callback).iterate();
}
@Override
public void completed()
{
@ -422,68 +427,57 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
default implementation of a blocking queue with an implementation
that uses the calling thread to block on a readable callback and
then to do the parsing before before attempting the read.
*/
try
*/
while (true)
{
while (true)
// Can the parser progress (even with an empty buffer)
boolean event=_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer);
// If there is more content to parse, loop so we can queue all content from this buffer now without the
// need to call blockForContent again
while (event && BufferUtil.hasContent(_requestBuffer) && _parser.inContentState())
_parser.parseNext(_requestBuffer);
// If we have an event, return
if (event)
return;
// Do we have content ready to parse?
if (BufferUtil.isEmpty(_requestBuffer))
{
// Can the parser progress (even with an empty buffer)
boolean event=_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer);
// If there is more content to parse, loop so we can queue all content from this buffer now without the
// need to call blockForContent again
while (event && BufferUtil.hasContent(_requestBuffer) && _parser.inContentState())
_parser.parseNext(_requestBuffer);
// If we have an event, return
if (event)
return;
// Do we have content ready to parse?
if (BufferUtil.isEmpty(_requestBuffer))
// If no more input
if (getEndPoint().isInputShutdown())
{
// If no more input
if (getEndPoint().isInputShutdown())
{
_parser.shutdownInput();
shutdown();
return;
}
_parser.shutdownInput();
shutdown();
return;
}
// Wait until we can read
block(_readBlocker);
LOG.debug("{} block readable on {}",this,_readBlocker);
_readBlocker.block();
// Wait until we can read
block(_readBlocker);
LOG.debug("{} block readable on {}",this,_readBlocker);
_readBlocker.block();
// We will need a buffer to read into
if (_requestBuffer==null)
{
long content_length=_channel.getRequest().getContentLength();
int size=getInputBufferSize();
if (size<content_length)
size=size*4; // TODO tune this
_requestBuffer=_bufferPool.acquire(size,REQUEST_BUFFER_DIRECT);
}
// We will need a buffer to read into
if (_requestBuffer==null)
{
long content_length=_channel.getRequest().getContentLength();
int size=getInputBufferSize();
if (size<content_length)
size=size*4; // TODO tune this
_requestBuffer=_bufferPool.acquire(size,REQUEST_BUFFER_DIRECT);
}
// read some data
int filled=getEndPoint().fill(_requestBuffer);
LOG.debug("{} block filled {}",this,filled);
if (filled<0)
{
_parser.shutdownInput();
return;
}
// read some data
int filled=getEndPoint().fill(_requestBuffer);
LOG.debug("{} block filled {}",this,filled);
if (filled<0)
{
_parser.shutdownInput();
return;
}
}
}
catch (TimeoutException e)
{
throw new EofException(e);
}
catch (final InterruptedException x)
{
throw new InterruptedIOException(getEndPoint().toString()){{initCause(x);}};
}
}
@Override
@ -586,147 +580,208 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
_generator.setPersistent(false);
super.handleException(x);
}
}
private class Sender implements Callback
private class CommitCallback extends IteratingCallback
{
final ByteBuffer _content;
final boolean _lastContent;
final Callback _callback;
final ResponseInfo _info;
ByteBuffer _header;
Sender(ByteBuffer content, boolean last, Callback callback)
CommitCallback(ResponseInfo info, ByteBuffer content, boolean last, Callback callback)
{
_callback=callback;
super(callback);
_info=info;
_content=content;
_lastContent=last;
}
public void process(ResponseInfo info)
@Override
public boolean process() throws Exception
{
try
ByteBuffer chunk = _chunk;
while (true)
{
ByteBuffer header = null;
ByteBuffer chunk = null;
while (true)
{
HttpGenerator.Result result = _generator.generateResponse(info, header, chunk, _content, _lastContent);
if (LOG.isDebugEnabled())
LOG.debug("{} generate: {} ({},{},{})@{}",
this,
result,
BufferUtil.toSummaryString(header),
BufferUtil.toSummaryString(_content),
_lastContent,
_generator.getState());
HttpGenerator.Result result = _generator.generateResponse(_info, _header, chunk, _content, _lastContent);
if (LOG.isDebugEnabled())
LOG.debug("{} generate: {} ({},{},{})@{}",
this,
result,
BufferUtil.toSummaryString(_header),
BufferUtil.toSummaryString(_content),
_lastContent,
_generator.getState());
switch (result)
switch (result)
{
case NEED_HEADER:
{
case NEED_HEADER:
if (_lastContent && _content!=null && BufferUtil.space(_content)>_config.getResponseHeaderSize() && _content.hasArray() )
{
if (_lastContent && _content!=null && BufferUtil.space(_content)>_config.getResponseHeaderSize() && _content.hasArray() )
// use spare space in content buffer for header buffer
int p=_content.position();
int l=_content.limit();
_content.position(l);
_content.limit(l+_config.getResponseHeaderSize());
_header=_content.slice();
_header.limit(0);
_content.position(p);
_content.limit(l);
}
else
_header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT);
continue;
}
case NEED_CHUNK:
{
chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, CHUNK_BUFFER_DIRECT);
continue;
}
case FLUSH:
{
// Don't write the chunk or the content if this is a HEAD response
if (_channel.getRequest().isHead())
{
BufferUtil.clear(chunk);
BufferUtil.clear(_content);
}
// If we have a header
if (BufferUtil.hasContent(_header))
{
if (BufferUtil.hasContent(_content))
{
// use spare space in content buffer for header buffer
int p=_content.position();
int l=_content.limit();
_content.position(l);
_content.limit(l+_config.getResponseHeaderSize());
header=_content.slice();
header.limit(0);
_content.position(p);
_content.limit(l);
if (BufferUtil.hasContent(chunk))
getEndPoint().write(this, _header, chunk, _content);
else
getEndPoint().write(this, _header, _content);
}
else
header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT);
continue;
getEndPoint().write(this, _header);
}
case NEED_CHUNK:
else if (BufferUtil.hasContent(chunk))
{
chunk = _chunk;
if (chunk==null)
chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, CHUNK_BUFFER_DIRECT);
continue;
}
case FLUSH:
{
// Don't write the chunk or the content if this is a HEAD response
if (_channel.getRequest().isHead())
{
BufferUtil.clear(chunk);
BufferUtil.clear(_content);
}
// If we have a header
if (BufferUtil.hasContent(header))
{
// we know there will not be a chunk, so write either header+content or just the header
if (BufferUtil.hasContent(_content))
getEndPoint().write(this, header, _content);
else
getEndPoint().write(this, header);
}
else if (BufferUtil.hasContent(chunk))
{
if (BufferUtil.hasContent(_content))
getEndPoint().write(this, chunk, _content);
else
getEndPoint().write(this, chunk);
}
else if (BufferUtil.hasContent(_content))
{
getEndPoint().write(this, _content);
}
if (BufferUtil.hasContent(_content))
getEndPoint().write(this, chunk, _content);
else
continue;
return;
getEndPoint().write(this, chunk);
}
case SHUTDOWN_OUT:
else if (BufferUtil.hasContent(_content))
{
getEndPoint().shutdownOutput();
getEndPoint().write(this, _content);
}
else
continue;
}
case DONE:
return false;
}
case SHUTDOWN_OUT:
{
getEndPoint().shutdownOutput();
continue;
}
case DONE:
{
if (_header!=null)
{
if (header!=null)
{
// don't release header in spare content buffer
if (!_lastContent || _content==null || !_content.hasArray() || !header.hasArray() || _content.array()!=header.array())
_bufferPool.release(header);
}
if (chunk!=null)
_bufferPool.release(chunk);
_callback.succeeded();
return;
}
case CONTINUE:
{
break;
}
default:
{
throw new IllegalStateException("generateResponse="+result);
// don't release header in spare content buffer
if (!_lastContent || _content==null || !_content.hasArray() || !_header.hasArray() || _content.array()!=_header.array())
_bufferPool.release(_header);
}
return true;
}
case CONTINUE:
{
break;
}
default:
{
throw new IllegalStateException("generateResponse="+result);
}
}
}
catch(Exception e)
{
_callback.failed(e);
}
}
@Override
public void succeeded()
{
process(null);
}
@Override
public void failed(Throwable x)
{
_callback.failed(x);
}
}
private class ContentCallback extends IteratingCallback
{
final ByteBuffer _content;
final boolean _lastContent;
ContentCallback(ByteBuffer content, boolean last, Callback callback)
{
super(callback);
_content=content;
_lastContent=last;
}
@Override
public boolean process() throws Exception
{
ByteBuffer chunk = _chunk;
while (true)
{
HttpGenerator.Result result = _generator.generateResponse(null, null, chunk, _content, _lastContent);
if (LOG.isDebugEnabled())
LOG.debug("{} generate: {} ({},{})@{}",
this,
result,
BufferUtil.toSummaryString(_content),
_lastContent,
_generator.getState());
switch (result)
{
case NEED_HEADER:
throw new IllegalStateException();
case NEED_CHUNK:
{
chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, CHUNK_BUFFER_DIRECT);
continue;
}
case FLUSH:
{
// Don't write the chunk or the content if this is a HEAD response
if (_channel.getRequest().isHead())
{
BufferUtil.clear(chunk);
BufferUtil.clear(_content);
continue;
}
else if (BufferUtil.hasContent(chunk))
{
if (BufferUtil.hasContent(_content))
getEndPoint().write(this, chunk, _content);
else
getEndPoint().write(this, chunk);
}
else if (BufferUtil.hasContent(_content))
{
getEndPoint().write(this, _content);
}
else
continue;
return false;
}
case SHUTDOWN_OUT:
{
getEndPoint().shutdownOutput();
continue;
}
case DONE:
{
return true;
}
case CONTINUE:
{
break;
}
default:
{
throw new IllegalStateException("generateResponse="+result);
}
}
}
}
}
}

View File

@ -23,6 +23,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.concurrent.TimeoutException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
@ -31,7 +33,10 @@ import javax.servlet.WriteListener;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.util.BlockingCallback;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingCallback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@ -142,7 +147,13 @@ public class HttpOutput extends ServletOutputStream
public boolean closeIfAllContentWritten() throws IOException
{
return _channel.getResponse().closeIfAllContentWritten(_written);
Response response=_channel.getResponse();
if (response.isAllContentWritten(_written))
{
response.closeOutput();
return true;
}
return false;
}
@Override
@ -151,53 +162,55 @@ public class HttpOutput extends ServletOutputStream
if (isClosed())
throw new EOFException("Closed");
// Do we have an aggregate buffer already ?
// Do we have an aggregate buffer ?
if (_aggregate == null)
{
// What size should the aggregate be ?
// NO - should we have an aggregate buffer? yes if this write will easily fit in it
int size = getBufferSize();
// If this write would fill more than half the aggregate, just write it directly
if (len > size / 2)
if (len<=size/2)
{
_channel.write(ByteBuffer.wrap(b, off, len), false);
_aggregate = _channel.getByteBufferPool().acquire(size, false);
BufferUtil.append(_aggregate, b, off, len);
_written += len;
closeIfAllContentWritten();
return;
}
// Allocate an aggregate buffer.
// Never direct as it is slow to do little writes to a direct buffer.
_aggregate = _channel.getByteBufferPool().acquire(size, false);
}
// Do we have space to aggregate ?
int space = BufferUtil.space(_aggregate);
if (len > space)
else
{
// No space so write the aggregate out if it is not empty
if (BufferUtil.hasContent(_aggregate))
// YES - fill the aggregate with content from the buffer
int filled = BufferUtil.fill(_aggregate, b, off, len);
_written += filled;
// if closed or there is no content left over and we are not full, then we are done
if (closeIfAllContentWritten() || filled==len && !BufferUtil.isFull(_aggregate))
return;
off+=filled;
len-=filled;
}
// flush the aggregate
if (BufferUtil.hasContent(_aggregate))
{
_channel.write(_aggregate, false);
// should we fill aggregate again from the buffer?
if (len<_aggregate.capacity()/2)
{
_channel.write(_aggregate, false);
space = BufferUtil.space(_aggregate);
BufferUtil.append(_aggregate, b, off, len);
_written += len;
closeIfAllContentWritten();
return;
}
}
// Do we have space to aggregate now ?
if (len > space)
{
// No space so write the content directly
_channel.write(ByteBuffer.wrap(b, off, len), false);
_written += len;
return;
}
// Aggregate the content
BufferUtil.append(_aggregate, b, off, len);
// write any remaining content in the buffer directly
_written += len;
// Check if all written or full
if (!closeIfAllContentWritten() && BufferUtil.isFull(_aggregate))
_channel.write(_aggregate, false);
boolean complete=_channel.getResponse().isAllContentWritten(_written);
_channel.write(ByteBuffer.wrap(b, off, len), complete);
if (complete)
_channel.getResponse().closeOutput();
}
@ -217,7 +230,11 @@ public class HttpOutput extends ServletOutputStream
// Check if all written or full
if (!closeIfAllContentWritten() && BufferUtil.isFull(_aggregate))
_channel.write(_aggregate, false);
{
BlockingCallback callback = _channel.getWriteBlockingCallback();
_channel.write(_aggregate, false, callback);
callback.block();
}
}
@Override
@ -229,105 +246,172 @@ public class HttpOutput extends ServletOutputStream
write(s.getBytes(_channel.getResponse().getCharacterEncoding()));
}
/* ------------------------------------------------------------ */
/** Set headers and send content.
* @deprecated Use {@link Response#setHeaders(HttpContent)} and {@link #sendContent(HttpContent)} instead.
* @param content
* @throws IOException
*/
@Deprecated
public void sendContent(Object content) throws IOException
{
if (isClosed())
throw new IOException("Closed");
final BlockingCallback callback =_channel.getWriteBlockingCallback();
if (content instanceof HttpContent)
{
HttpContent httpContent = (HttpContent)content;
Response response = _channel.getResponse();
String contentType = httpContent.getContentType();
if (contentType != null && !response.getHttpFields().containsKey(HttpHeader.CONTENT_TYPE.asString()))
response.getHttpFields().put(HttpHeader.CONTENT_TYPE, contentType);
if (httpContent.getContentLength() > 0)
response.getHttpFields().putLongField(HttpHeader.CONTENT_LENGTH, httpContent.getContentLength());
String lm = httpContent.getLastModified();
if (lm != null)
response.getHttpFields().put(HttpHeader.LAST_MODIFIED, lm);
else if (httpContent.getResource() != null)
{
long lml = httpContent.getResource().lastModified();
if (lml != -1)
response.getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, lml);
}
String etag=httpContent.getETag();
if (etag!=null)
response.getHttpFields().put(HttpHeader.ETAG,etag);
content = _channel.useDirectBuffers()?httpContent.getDirectBuffer():null;
if (content == null)
content = httpContent.getIndirectBuffer();
if (content == null)
content = httpContent.getReadableByteChannel();
if (content == null)
content = httpContent.getInputStream();
_channel.getResponse().setHeaders((HttpContent)content);
sendContent((HttpContent)content,callback);
}
else if (content instanceof Resource)
{
Resource resource = (Resource)content;
_channel.getResponse().getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, resource.lastModified());
content=resource.getInputStream(); // Closed below
ReadableByteChannel in=((Resource)content).getReadableByteChannel();
if (in!=null)
sendContent(in,callback);
else
sendContent(resource.getInputStream(),callback);
}
// Process content.
if (content instanceof ByteBuffer)
else if (content instanceof ByteBuffer)
{
_channel.write((ByteBuffer)content, true);
_closed=true;
sendContent((ByteBuffer)content,callback);
}
else if (content instanceof ReadableByteChannel)
{
ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.useDirectBuffers());
try (ReadableByteChannel channel = (ReadableByteChannel)content;)
{
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);
}
sendContent((ReadableByteChannel)content,callback);
}
else if (content instanceof InputStream)
{
// allocate non direct buffer so array may be directly accessed.
ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), false);
byte[] array = buffer.array();
int offset=buffer.arrayOffset();
int size=array.length-offset;
try (InputStream in = (InputStream)content;)
{
while(true)
{
int len=in.read(array,offset,size);
if (len<0)
break;
buffer.position(0);
buffer.limit(len);
_channel.write(buffer,false);
}
}
finally
{
close();
_channel.getByteBufferPool().release(buffer);
}
sendContent((InputStream)content,callback);
}
else
throw new IllegalArgumentException("unknown content type "+content.getClass());
callback.failed(new IllegalArgumentException("unknown content type "+content.getClass()));
callback.block();
}
/* ------------------------------------------------------------ */
/** Blocking send of content.
* @param content The content to send
* @throws IOException
*/
public void sendContent(ByteBuffer content) throws IOException
{
final BlockingCallback callback =_channel.getWriteBlockingCallback();
_channel.write(content,true,callback);
callback.block();
}
/* ------------------------------------------------------------ */
/** Blocking send of content.
* @param content The content to send
* @throws IOException
*/
public void sendContent(InputStream in) throws IOException
{
final BlockingCallback callback =_channel.getWriteBlockingCallback();
new InputStreamWritingCB(in,callback).iterate();
callback.block();
}
/* ------------------------------------------------------------ */
/** Blocking send of content.
* @param content The content to send
* @throws IOException
*/
public void sendContent(ReadableByteChannel in) throws IOException
{
final BlockingCallback callback =_channel.getWriteBlockingCallback();
new ReadableByteChannelWritingCB(in,callback).iterate();
callback.block();
}
/* ------------------------------------------------------------ */
/** Blocking send of content.
* @param content The content to send
* @throws IOException
*/
public void sendContent(HttpContent content) throws IOException
{
final BlockingCallback callback =_channel.getWriteBlockingCallback();
sendContent(content,callback);
callback.block();
}
/* ------------------------------------------------------------ */
/** Asynchronous send of content.
* @param content The content to send
* @param callback The callback to use to notify success or failure
*/
public void sendContent(ByteBuffer content, Callback callback)
{
_channel.write(content,true,callback);
}
/* ------------------------------------------------------------ */
/** Asynchronous send of content.
* @param content The content to send
* @param callback The callback to use to notify success or failure
*/
public void sendContent(InputStream in, Callback callback)
{
new InputStreamWritingCB(in,callback).iterate();
}
/* ------------------------------------------------------------ */
/** Asynchronous send of content.
* @param content The content to send
* @param callback The callback to use to notify success or failure
*/
public void sendContent(ReadableByteChannel in, Callback callback)
{
new ReadableByteChannelWritingCB(in,callback).iterate();
}
/* ------------------------------------------------------------ */
/** Asynchronous send of content.
* @param content The content to send
* @param callback The callback to use to notify success or failure
*/
public void sendContent(HttpContent httpContent, Callback callback) throws IOException
{
if (isClosed())
throw new IOException("Closed");
if (BufferUtil.hasContent(_aggregate))
throw new IOException("written");
if (_channel.isCommitted())
throw new IOException("committed");
_closed=true;
ByteBuffer buffer= _channel.useDirectBuffers()?httpContent.getDirectBuffer():null;
if (buffer == null)
buffer = httpContent.getIndirectBuffer();
if (buffer!=null)
{
sendContent(buffer,callback);
return;
}
ReadableByteChannel rbc=httpContent.getReadableByteChannel();
if (rbc!=null)
{
sendContent(rbc,callback);
return;
}
InputStream in = httpContent.getInputStream();
if ( in!=null )
{
sendContent(in,callback);
return;
}
callback.failed(new IllegalArgumentException("unknown content for "+httpContent));
}
public int getBufferSize()
@ -374,4 +458,118 @@ public class HttpOutput extends ServletOutputStream
// TODO 3.1 Auto-generated method stub
return false;
}
/* ------------------------------------------------------------ */
/** An iterating callback that will take content from an
* InputStream and write it to the associated {@link HttpChannel}.
* A non direct buffer of size {@link HttpOutput#getBufferSize()} is used.
* This callback is passed to the {@link HttpChannel#write(ByteBuffer, boolean, Callback)} to
* be notified as each buffer is written and only once all the input is consumed will the
* wrapped {@link Callback#succeeded()} method be called.
*/
private class InputStreamWritingCB extends IteratingCallback
{
final InputStream _in;
final ByteBuffer _buffer;
public InputStreamWritingCB(InputStream in, Callback callback)
{
super(callback);
_in=in;
_buffer = _channel.getByteBufferPool().acquire(getBufferSize(), false);
}
@Override
protected boolean process() throws Exception
{
int len=_in.read(_buffer.array(),0,_buffer.capacity());
if (len==-1)
{
_channel.getByteBufferPool().release(_buffer);
return true;
}
boolean eof=false;
// if we read less than a buffer, are we at EOF?
if (len<_buffer.capacity())
{
int len2=_in.read(_buffer.array(),len,_buffer.capacity()-len);
if (len2<0)
eof=true;
else
len+=len2;
}
_buffer.position(0);
_buffer.limit(len);
_channel.write(_buffer,eof,this);
return false;
}
@Override
public void failed(Throwable x)
{
super.failed(x);
_channel.getByteBufferPool().release(_buffer);
}
}
/* ------------------------------------------------------------ */
/** An iterating callback that will take content from a
* ReadableByteChannel and write it to the {@link HttpChannel}.
* A {@link ByteBuffer} of size {@link HttpOutput#getBufferSize()} is used that will be direct if
* {@link HttpChannel#useDirectBuffers()} is true.
* This callback is passed to the {@link HttpChannel#write(ByteBuffer, boolean, Callback)} to
* be notified as each buffer is written and only once all the input is consumed will the
* wrapped {@link Callback#succeeded()} method be called.
*/
private class ReadableByteChannelWritingCB extends IteratingCallback
{
final ReadableByteChannel _in;
final ByteBuffer _buffer;
public ReadableByteChannelWritingCB(ReadableByteChannel in, Callback callback)
{
super(callback);
_in=in;
_buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.useDirectBuffers());
}
@Override
protected boolean process() throws Exception
{
_buffer.clear();
int len=_in.read(_buffer);
if (len==-1)
{
_channel.getByteBufferPool().release(_buffer);
return true;
}
boolean eof=false;
// if we read less than a buffer, are we at EOF?
if (len<_buffer.capacity())
{
int len2=_in.read(_buffer);
if (len2<0)
eof=true;
else
len+=len2;
}
_buffer.flip();
_channel.write(_buffer,eof,this);
return false;
}
@Override
public void failed(Throwable x)
{
super.failed(x);
_channel.getByteBufferPool().release(_buffer);
}
}
}

View File

@ -26,9 +26,12 @@ import org.eclipse.jetty.util.Callback;
public interface HttpTransport
{
@Deprecated
void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent) throws IOException;
void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback);
void send(ByteBuffer content, boolean lastContent, Callback callback);
void completed();
}

View File

@ -38,6 +38,13 @@ public class Iso88591HttpWriter extends HttpWriter
if (length==0)
out.closeIfAllContentWritten();
if (length==1)
{
int c=s[offset];
out.write(c<256?c:'?');
return;
}
while (length > 0)
{
_bytes.reset();

View File

@ -1401,7 +1401,7 @@ public class Request implements HttpServletRequest
@Override
public boolean isAsyncStarted()
{
return getHttpChannelState().isAsync();
return getHttpChannelState().isAsyncStarted();
}

View File

@ -34,6 +34,7 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
@ -129,6 +130,31 @@ public class Response implements HttpServletResponse
_fields.clear();
}
public void setHeaders(HttpContent httpContent)
{
Response response = _channel.getResponse();
String contentType = httpContent.getContentType();
if (contentType != null && !response.getHttpFields().containsKey(HttpHeader.CONTENT_TYPE.asString()))
response.getHttpFields().put(HttpHeader.CONTENT_TYPE, contentType);
if (httpContent.getContentLength() > 0)
response.getHttpFields().putLongField(HttpHeader.CONTENT_LENGTH, httpContent.getContentLength());
String lm = httpContent.getLastModified();
if (lm != null)
response.getHttpFields().put(HttpHeader.LAST_MODIFIED, lm);
else if (httpContent.getResource() != null)
{
long lml = httpContent.getResource().lastModified();
if (lml != -1)
response.getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, lml);
}
String etag=httpContent.getETag();
if (etag!=null)
response.getHttpFields().put(HttpHeader.ETAG,etag);
}
public HttpOutput getHttpOutput()
{
return _out;
@ -753,34 +779,37 @@ public class Response implements HttpServletResponse
if (_contentLength > 0)
{
try
if (isAllContentWritten(written))
{
closeIfAllContentWritten(written);
}
catch(IOException e)
{
throw new RuntimeIOException(e);
try
{
closeOutput();
}
catch(IOException e)
{
throw new RuntimeIOException(e);
}
}
}
}
public boolean closeIfAllContentWritten(long written) throws IOException
public boolean isAllContentWritten(long written)
{
if (_contentLength >= 0 && written >= _contentLength)
return (_contentLength >= 0 && written >= _contentLength);
}
public void closeOutput() throws IOException
{
switch (_outputType)
{
switch (_outputType)
{
case WRITER:
_writer.close();
break;
case STREAM:
getOutputStream().close();
break;
default:
}
return true;
case WRITER:
_writer.close();
break;
case STREAM:
getOutputStream().close();
break;
default:
}
return false;
}
public long getLongContentLength()

View File

@ -21,7 +21,10 @@ package org.eclipse.jetty.server.handler;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -33,12 +36,16 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.WriterOutputStream;
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.FileResource;
import org.eclipse.jetty.util.resource.Resource;
@ -65,6 +72,8 @@ public class ResourceHandler extends HandlerWrapper
String _cacheControl;
boolean _directory;
boolean _etags;
int _minMemoryMappedContentLength=-1;
int _minAsyncContentLength=0;
/* ------------------------------------------------------------ */
public ResourceHandler()
@ -102,6 +111,50 @@ public class ResourceHandler extends HandlerWrapper
_directory = directory;
}
/* ------------------------------------------------------------ */
/** Get minimum memory mapped file content length.
* @return the minimum size in bytes of a file resource that will
* be served using a memory mapped buffer, or -1 (default) for no memory mapped
* buffers.
*/
public int getMinMemoryMappedContentLength()
{
return _minMemoryMappedContentLength;
}
/* ------------------------------------------------------------ */
/** Set minimum memory mapped file content length.
* @param minMemoryMappedFileSize the minimum size in bytes of a file resource that will
* be served using a memory mapped buffer, or -1 for no memory mapped
* buffers.
*/
public void setMinMemoryMappedContentLength(int minMemoryMappedFileSize)
{
_minMemoryMappedContentLength = minMemoryMappedFileSize;
}
/* ------------------------------------------------------------ */
/** Get the minimum content length for async handling.
* @return The minimum size in bytes of the content before asynchronous
* handling is used, or -1 for no async handling or 0 (default) for using
* {@link HttpServletResponse#getBufferSize()} as the minimum length.
*/
public int getMinAsyncContentLength()
{
return _minAsyncContentLength;
}
/* ------------------------------------------------------------ */
/** Set the minimum content length for async handling.
* @param minAsyncContentLength The minimum size in bytes of the content before asynchronous
* handling is used, or -1 for no async handling or 0 for using
* {@link HttpServletResponse#getBufferSize()} as the minimum length.
*/
public void setMinAsyncContentLength(int minAsyncContentLength)
{
_minAsyncContentLength = minAsyncContentLength;
}
/* ------------------------------------------------------------ */
/**
* @return True if ETag processing is done
@ -358,9 +411,10 @@ public class ResourceHandler extends HandlerWrapper
}
Resource resource = getResource(request);
// If resource is not found
if (resource==null || !resource.exists())
{
// inject the jetty-dir.css file if it matches
if (target.endsWith("/jetty-dir.css"))
{
resource = getStylesheet();
@ -379,6 +433,7 @@ public class ResourceHandler extends HandlerWrapper
// We are going to serve something
baseRequest.setHandled(true);
// handle directories
if (resource.isDirectory())
{
if (!request.getPathInfo().endsWith(URIUtil.SLASH))
@ -398,7 +453,7 @@ public class ResourceHandler extends HandlerWrapper
}
}
// set some headers
// Handle ETAGS
long last_modified=resource.lastModified();
String etag=null;
if (_etags)
@ -414,7 +469,7 @@ public class ResourceHandler extends HandlerWrapper
}
}
// Handle if modified since
if (last_modified>0)
{
long if_modified=request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
@ -425,34 +480,88 @@ public class ResourceHandler extends HandlerWrapper
}
}
// set the headers
String mime=_mimeTypes.getMimeByExtension(resource.toString());
if (mime==null)
mime=_mimeTypes.getMimeByExtension(request.getPathInfo());
// set the headers
doResponseHeaders(response,resource,mime!=null?mime.toString():null);
response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),last_modified);
if (_etags)
baseRequest.getResponse().getHttpFields().put(HttpHeader.ETAG,etag);
if(skipContentBody)
return;
// Send the content
OutputStream out =null;
try {out = response.getOutputStream();}
catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());}
// See if a short direct method can be used?
/* TODO file mapped buffers
if (out instanceof HttpOutput)
{
// TODO file mapped buffers
((HttpOutput)out).send(resource.getInputStream());
}
else*/
{
// Write content normally
// Has the output been wrapped
if (!(out instanceof HttpOutput))
// Write content via wrapped output
resource.writeTo(out,0,resource.length());
else
{
// select async by size
int min_async_size=_minAsyncContentLength==0?response.getBufferSize():_minAsyncContentLength;
if (request.isAsyncSupported() &&
min_async_size>0 &&
resource.length()>=min_async_size)
{
final AsyncContext async = request.startAsync();
Callback callback = new Callback()
{
@Override
public void succeeded()
{
async.complete();
}
@Override
public void failed(Throwable x)
{
async.complete();
}
};
// Can we use a memory mapped file?
if (_minMemoryMappedContentLength>0 &&
resource.length()>_minMemoryMappedContentLength &&
resource instanceof FileResource)
{
ByteBuffer buffer = BufferUtil.toBuffer(resource.getFile());
((HttpOutput)out).sendContent(buffer,callback);
}
else // Do a blocking write of a channel (if available) or input stream
{
ReadableByteChannel channel= resource.getReadableByteChannel();
if (channel!=null)
((HttpOutput)out).sendContent(channel,callback);
else
((HttpOutput)out).sendContent(resource.getInputStream(),callback);
}
}
else
{
// Can we use a memory mapped file?
if (_minMemoryMappedContentLength>0 &&
resource.length()>_minMemoryMappedContentLength &&
resource instanceof FileResource)
{
ByteBuffer buffer = BufferUtil.toBuffer(resource.getFile());
((HttpOutput)out).sendContent(buffer);
}
else // Do a blocking write of a channel (if available) or input stream
{
ReadableByteChannel channel= resource.getReadableByteChannel();
if (channel!=null)
((HttpOutput)out).sendContent(channel);
else
((HttpOutput)out).sendContent(resource.getInputStream());
}
}
}
}

View File

@ -37,6 +37,7 @@ import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Logger;
@ -553,8 +554,7 @@ public class HashSessionManager extends AbstractSessionManager
}
finally
{
if (in != null)
try {in.close();} catch (Exception x) {__log.ignore(x);}
if (in != null) IO.close(in);
if (error != null)
{

View File

@ -150,18 +150,14 @@ public class HashedSession extends AbstractSession
file.createNewFile();
fos = new FileOutputStream(file);
save(fos);
IO.close(fos);
}
catch (Exception e)
{
saveFailed();
if (fos != null)
{
// Must not leave the file open if the saving failed
IO.close(fos);
// No point keeping the file if we didn't save the whole session
file.delete();
throw e;
}
saveFailed(); // We won't try again for this session
if (fos != null) IO.close(fos);
if (file != null) file.delete(); // No point keeping the file if we didn't save the whole session
throw e;
}
}
}
@ -219,17 +215,18 @@ public class HashedSession extends AbstractSession
fis = new FileInputStream(file);
_idled = false;
_hashSessionManager.restoreSession(fis, this);
IO.close(fis);
didActivate();
// If we are doing period saves, then there is no point deleting at this point
// If we are doing period saves, then there is no point deleting at this point
if (_hashSessionManager._savePeriodMs == 0)
file.delete();
}
catch (Exception e)
{
LOG.warn("Problem de-idling session " + super.getId(), e);
IO.close(fis);
if (fis != null) IO.close(fis);//Must ensure closed before invalidate
invalidate();
}
}

View File

@ -580,11 +580,11 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
@Override
public void doStart()
throws Exception
{
{
initializeDatabase();
prepareTables();
prepareTables();
super.doStart();
if (LOG.isDebugEnabled())
if (LOG.isDebugEnabled())
LOG.debug("Scavenging interval = "+getScavengeInterval()+" sec");
_timer=new Timer("JDBCSessionScavenger", true);
setScavengeInterval(getScavengeInterval());

View File

@ -305,7 +305,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
}
@Test
public void testHandledBufferOverflow() throws Exception
public void testHandledOverflow() throws Exception
{
server.setHandler(new OverflowHandler(false));
server.start();
@ -317,6 +317,34 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
if (!"HTTP/1.0".equals(httpVersion))
assertHeader(response, "transfer-encoding", "chunked");
}
@Test
public void testHandledOverflow2() throws Exception
{
server.setHandler(new Overflow2Handler(false));
server.start();
SimpleHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertResponseBody(response, "foobar");
if (!"HTTP/1.0".equals(httpVersion))
assertHeader(response, "transfer-encoding", "chunked");
}
@Test
public void testHandledOverflow3() throws Exception
{
server.setHandler(new Overflow3Handler(false));
server.start();
SimpleHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertResponseBody(response, "foobar");
if (!"HTTP/1.0".equals(httpVersion))
assertHeader(response, "transfer-encoding", "chunked");
}
@Test
public void testHandledBufferOverflowAndThrow() throws Exception
@ -350,6 +378,43 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
}
}
private class Overflow2Handler extends ThrowExceptionOnDemandHandler
{
private Overflow2Handler(boolean throwException)
{
super(throwException);
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setBufferSize(3);
response.getWriter().write("f");
response.getWriter().write("oobar");
super.handle(target, baseRequest, request, response);
}
}
private class Overflow3Handler extends ThrowExceptionOnDemandHandler
{
private Overflow3Handler(boolean throwException)
{
super(throwException);
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setBufferSize(5);
response.getWriter().write("fo");
response.getWriter().write("ob");
response.getWriter().write("ar");
super.handle(target, baseRequest, request, response);
}
}
@Test
public void testSetContentLengthAndWriteExactlyThatAmountOfBytes() throws Exception
{

View File

@ -29,6 +29,8 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.concurrent.TimeoutException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
@ -46,6 +48,7 @@ import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.session.HashSessionIdManager;
import org.eclipse.jetty.server.session.HashSessionManager;
import org.eclipse.jetty.server.session.HashedSession;
import org.eclipse.jetty.util.BlockingCallback;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.TimerScheduler;
@ -84,6 +87,9 @@ public class ResponseTest
@Override
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent) throws IOException
{
BlockingCallback cb = new BlockingCallback();
send(info,content,lastContent,cb);
cb.block();
}
@Override
@ -91,6 +97,12 @@ public class ResponseTest
{
callback.succeeded();
}
@Override
public void send(ByteBuffer responseBodyContent, boolean lastContent, Callback callback)
{
send(null,responseBodyContent, lastContent, callback);
}
@Override
public void completed()

View File

@ -18,15 +18,24 @@
package org.eclipse.jetty.server.handler;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.Socket;
import java.net.URI;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.SimpleRequest;
import org.eclipse.jetty.util.IO;
import org.hamcrest.Matchers;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -38,6 +47,7 @@ import org.junit.Test;
public class ResourceHandlerTest
{
private static Server _server;
private static HttpConfiguration _config;
private static ServerConnector _connector;
private static ContextHandler _contextHandler;
private static ResourceHandler _resourceHandler;
@ -45,11 +55,29 @@ public class ResourceHandlerTest
@BeforeClass
public static void setUp() throws Exception
{
File dir = MavenTestingUtils.getTargetFile("test-classes/simple");
File huge = new File(dir,"huge.txt");
File big=new File(dir,"big.txt");
FileOutputStream out = new FileOutputStream(huge);
for (int i=0;i<100;i++)
{
FileInputStream in=new FileInputStream(big);
IO.copy(in,out);
}
huge.deleteOnExit();
_server = new Server();
_connector = new ServerConnector(_server);
_config=new HttpConfiguration();
_config.setOutputBufferSize(2048);
_connector = new ServerConnector(_server,new HttpConnectionFactory(_config));
_server.setConnectors(new Connector[] { _connector });
_resourceHandler = new ResourceHandler();
_resourceHandler.setMinAsyncContentLength(4096);
_resourceHandler.setMinMemoryMappedContentLength(8192);
_resourceHandler.setResourceBase(MavenTestingUtils.getTargetFile("test-classes/simple").getAbsolutePath());
_contextHandler = new ContextHandler("/resource");
_contextHandler.setHandler(_resourceHandler);
@ -62,16 +90,69 @@ public class ResourceHandlerTest
{
_server.stop();
}
@Before
public void before()
{
_config.setOutputBufferSize(4096);
}
@Test
public void testSimpleResourceHandler() throws Exception
public void testMissing() throws Exception
{
_resourceHandler.setResourceBase(MavenTestingUtils.getTestResourceDir("simple").getAbsolutePath());
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
Assert.assertEquals("simple text", sr.getString("/resource/simple.txt"));
Assert.assertNotNull("missing jetty.css" , sr.getString("/resource/jetty-dir.css"));
}
@Test
public void testSimple() throws Exception
{
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
Assert.assertEquals("simple text", sr.getString("/resource/simple.txt"));
}
@Test
public void testBigFile() throws Exception
{
_config.setOutputBufferSize(2048);
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
String response=sr.getString("/resource/big.txt");
Assert.assertThat(response,Matchers.startsWith(" 1\tThis is a big file"));
Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file\n"));
}
@Test
public void testBigFileBigBuffer() throws Exception
{
_config.setOutputBufferSize(16*1024);
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
String response=sr.getString("/resource/big.txt");
Assert.assertThat(response,Matchers.startsWith(" 1\tThis is a big file"));
Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file\n"));
}
@Test
public void testBigFileLittleBuffer() throws Exception
{
_config.setOutputBufferSize(8);
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
String response=sr.getString("/resource/big.txt");
Assert.assertThat(response,Matchers.startsWith(" 1\tThis is a big file"));
Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file\n"));
}
@Test
public void testHuge() throws Exception
{
try (Socket socket = new Socket("localhost",_connector.getLocalPort());)
{
socket.getOutputStream().write("GET /resource/huge.txt HTTP/1.0\n\n".getBytes());
Thread.sleep(1000);
String response = IO.toString(socket.getInputStream());
Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 200 OK"));
Assert.assertThat(response,Matchers.containsString(" 400\tThis is a big file\n 1\tThis is a big file"));
Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file\n"));
}
}
}

View File

@ -0,0 +1,400 @@
1 This is a big file
2 This is a big file
3 This is a big file
4 This is a big file
5 This is a big file
6 This is a big file
7 This is a big file
8 This is a big file
9 This is a big file
10 This is a big file
11 This is a big file
12 This is a big file
13 This is a big file
14 This is a big file
15 This is a big file
16 This is a big file
17 This is a big file
18 This is a big file
19 This is a big file
20 This is a big file
21 This is a big file
22 This is a big file
23 This is a big file
24 This is a big file
25 This is a big file
26 This is a big file
27 This is a big file
28 This is a big file
29 This is a big file
30 This is a big file
31 This is a big file
32 This is a big file
33 This is a big file
34 This is a big file
35 This is a big file
36 This is a big file
37 This is a big file
38 This is a big file
39 This is a big file
40 This is a big file
41 This is a big file
42 This is a big file
43 This is a big file
44 This is a big file
45 This is a big file
46 This is a big file
47 This is a big file
48 This is a big file
49 This is a big file
50 This is a big file
51 This is a big file
52 This is a big file
53 This is a big file
54 This is a big file
55 This is a big file
56 This is a big file
57 This is a big file
58 This is a big file
59 This is a big file
60 This is a big file
61 This is a big file
62 This is a big file
63 This is a big file
64 This is a big file
65 This is a big file
66 This is a big file
67 This is a big file
68 This is a big file
69 This is a big file
70 This is a big file
71 This is a big file
72 This is a big file
73 This is a big file
74 This is a big file
75 This is a big file
76 This is a big file
77 This is a big file
78 This is a big file
79 This is a big file
80 This is a big file
81 This is a big file
82 This is a big file
83 This is a big file
84 This is a big file
85 This is a big file
86 This is a big file
87 This is a big file
88 This is a big file
89 This is a big file
90 This is a big file
91 This is a big file
92 This is a big file
93 This is a big file
94 This is a big file
95 This is a big file
96 This is a big file
97 This is a big file
98 This is a big file
99 This is a big file
100 This is a big file
101 This is a big file
102 This is a big file
103 This is a big file
104 This is a big file
105 This is a big file
106 This is a big file
107 This is a big file
108 This is a big file
109 This is a big file
110 This is a big file
111 This is a big file
112 This is a big file
113 This is a big file
114 This is a big file
115 This is a big file
116 This is a big file
117 This is a big file
118 This is a big file
119 This is a big file
120 This is a big file
121 This is a big file
122 This is a big file
123 This is a big file
124 This is a big file
125 This is a big file
126 This is a big file
127 This is a big file
128 This is a big file
129 This is a big file
130 This is a big file
131 This is a big file
132 This is a big file
133 This is a big file
134 This is a big file
135 This is a big file
136 This is a big file
137 This is a big file
138 This is a big file
139 This is a big file
140 This is a big file
141 This is a big file
142 This is a big file
143 This is a big file
144 This is a big file
145 This is a big file
146 This is a big file
147 This is a big file
148 This is a big file
149 This is a big file
150 This is a big file
151 This is a big file
152 This is a big file
153 This is a big file
154 This is a big file
155 This is a big file
156 This is a big file
157 This is a big file
158 This is a big file
159 This is a big file
160 This is a big file
161 This is a big file
162 This is a big file
163 This is a big file
164 This is a big file
165 This is a big file
166 This is a big file
167 This is a big file
168 This is a big file
169 This is a big file
170 This is a big file
171 This is a big file
172 This is a big file
173 This is a big file
174 This is a big file
175 This is a big file
176 This is a big file
177 This is a big file
178 This is a big file
179 This is a big file
180 This is a big file
181 This is a big file
182 This is a big file
183 This is a big file
184 This is a big file
185 This is a big file
186 This is a big file
187 This is a big file
188 This is a big file
189 This is a big file
190 This is a big file
191 This is a big file
192 This is a big file
193 This is a big file
194 This is a big file
195 This is a big file
196 This is a big file
197 This is a big file
198 This is a big file
199 This is a big file
200 This is a big file
201 This is a big file
202 This is a big file
203 This is a big file
204 This is a big file
205 This is a big file
206 This is a big file
207 This is a big file
208 This is a big file
209 This is a big file
210 This is a big file
211 This is a big file
212 This is a big file
213 This is a big file
214 This is a big file
215 This is a big file
216 This is a big file
217 This is a big file
218 This is a big file
219 This is a big file
220 This is a big file
221 This is a big file
222 This is a big file
223 This is a big file
224 This is a big file
225 This is a big file
226 This is a big file
227 This is a big file
228 This is a big file
229 This is a big file
230 This is a big file
231 This is a big file
232 This is a big file
233 This is a big file
234 This is a big file
235 This is a big file
236 This is a big file
237 This is a big file
238 This is a big file
239 This is a big file
240 This is a big file
241 This is a big file
242 This is a big file
243 This is a big file
244 This is a big file
245 This is a big file
246 This is a big file
247 This is a big file
248 This is a big file
249 This is a big file
250 This is a big file
251 This is a big file
252 This is a big file
253 This is a big file
254 This is a big file
255 This is a big file
256 This is a big file
257 This is a big file
258 This is a big file
259 This is a big file
260 This is a big file
261 This is a big file
262 This is a big file
263 This is a big file
264 This is a big file
265 This is a big file
266 This is a big file
267 This is a big file
268 This is a big file
269 This is a big file
270 This is a big file
271 This is a big file
272 This is a big file
273 This is a big file
274 This is a big file
275 This is a big file
276 This is a big file
277 This is a big file
278 This is a big file
279 This is a big file
280 This is a big file
281 This is a big file
282 This is a big file
283 This is a big file
284 This is a big file
285 This is a big file
286 This is a big file
287 This is a big file
288 This is a big file
289 This is a big file
290 This is a big file
291 This is a big file
292 This is a big file
293 This is a big file
294 This is a big file
295 This is a big file
296 This is a big file
297 This is a big file
298 This is a big file
299 This is a big file
300 This is a big file
301 This is a big file
302 This is a big file
303 This is a big file
304 This is a big file
305 This is a big file
306 This is a big file
307 This is a big file
308 This is a big file
309 This is a big file
310 This is a big file
311 This is a big file
312 This is a big file
313 This is a big file
314 This is a big file
315 This is a big file
316 This is a big file
317 This is a big file
318 This is a big file
319 This is a big file
320 This is a big file
321 This is a big file
322 This is a big file
323 This is a big file
324 This is a big file
325 This is a big file
326 This is a big file
327 This is a big file
328 This is a big file
329 This is a big file
330 This is a big file
331 This is a big file
332 This is a big file
333 This is a big file
334 This is a big file
335 This is a big file
336 This is a big file
337 This is a big file
338 This is a big file
339 This is a big file
340 This is a big file
341 This is a big file
342 This is a big file
343 This is a big file
344 This is a big file
345 This is a big file
346 This is a big file
347 This is a big file
348 This is a big file
349 This is a big file
350 This is a big file
351 This is a big file
352 This is a big file
353 This is a big file
354 This is a big file
355 This is a big file
356 This is a big file
357 This is a big file
358 This is a big file
359 This is a big file
360 This is a big file
361 This is a big file
362 This is a big file
363 This is a big file
364 This is a big file
365 This is a big file
366 This is a big file
367 This is a big file
368 This is a big file
369 This is a big file
370 This is a big file
371 This is a big file
372 This is a big file
373 This is a big file
374 This is a big file
375 This is a big file
376 This is a big file
377 This is a big file
378 This is a big file
379 This is a big file
380 This is a big file
381 This is a big file
382 This is a big file
383 This is a big file
384 This is a big file
385 This is a big file
386 This is a big file
387 This is a big file
388 This is a big file
389 This is a big file
390 This is a big file
391 This is a big file
392 This is a big file
393 This is a big file
394 This is a big file
395 This is a big file
396 This is a big file
397 This is a big file
398 This is a big file
399 This is a big file
400 This is a big file

View File

@ -28,6 +28,7 @@ import java.nio.ByteBuffer;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
@ -49,6 +50,7 @@ import org.eclipse.jetty.server.ResourceCache;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiPartOutputStream;
import org.eclipse.jetty.util.QuotedStringTokenizer;
@ -856,33 +858,55 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
{
resource.writeTo(out,0,content_length);
}
// else if we can't do a bypass write because of wrapping
else if (content==null || written || !(out instanceof HttpOutput))
{
// write normally
writeHeaders(response,content,written?-1:content_length);
ByteBuffer buffer = (content==null)?null:content.getIndirectBuffer();
if (buffer!=null)
BufferUtil.writeTo(buffer,out);
else
resource.writeTo(out,0,content_length);
}
// else do a bypass write
else
{
// See if a direct methods can be used?
if (content!=null && !written && out instanceof HttpOutput)
// write the headers
if (response instanceof Response)
{
if (response instanceof Response)
{
writeOptionHeaders(((Response)response).getHttpFields());
((HttpOutput)out).sendContent(content);
}
else
{
writeHeaders(response,content,content_length);
((HttpOutput)out).sendContent(content.getResource());
}
Response r = (Response)response;
writeOptionHeaders(r.getHttpFields());
r.setHeaders(content);
}
else
{
// Write headers normally
writeHeaders(response,content,written?-1:content_length);
writeHeaders(response,content,content_length);
// Write content normally
ByteBuffer buffer = (content==null)?null:content.getIndirectBuffer();
if (buffer!=null)
BufferUtil.writeTo(buffer,out);
else
resource.writeTo(out,0,content_length);
// write the content asynchronously if supported
if (request.isAsyncSupported())
{
final AsyncContext context = request.startAsync();
((HttpOutput)out).sendContent(content,new Callback()
{
@Override
public void succeeded()
{
context.complete();
}
@Override
public void failed(Throwable x)
{
LOG.debug(x);
context.complete();
}
});
}
// otherwise write content blocking
else
{
((HttpOutput)out).sendContent(content);
}
}
}

View File

@ -104,8 +104,8 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable
{
//if no class already loaded and no classname, make servlet permanently unavailable
if (_class==null && (_className==null || _className.equals("")))
throw new UnavailableException("No class for Servlet or Filter for "+_name, -1);
throw new UnavailableException("No class for Servlet or Filter for "+_name);
//try to load class
if (_class==null)
{
@ -118,7 +118,7 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable
catch (Exception e)
{
LOG.warn(e);
throw new UnavailableException(e.getMessage(), -1);
throw new UnavailableException(e.getMessage());
}
}
}

View File

@ -102,7 +102,7 @@ public class ServletHandler extends ScopedHandler
private int _matchAfterIndex = -1; //index of 1st programmatic FilterMapping with isMatchAfter=true
private boolean _filterChainsCached=true;
private int _maxFilterChainsCacheSize=512;
private boolean _startWithUnavailable=true;
private boolean _startWithUnavailable=false;
private IdentityService _identityService;
private ServletHolder[] _servlets=new ServletHolder[0];

View File

@ -288,6 +288,8 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
_unavailable=0;
if (!_enabled)
return;
//check servlet has a class (ie is not a preliminary registration). If preliminary, fail startup.
try
{
@ -296,9 +298,17 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
catch (UnavailableException ue)
{
makeUnavailable(ue);
throw ue;
if (_servletHandler.isStartWithUnavailable())
{
LOG.ignore(ue);
return;
}
else
throw ue;
}
//servlet is not an instance of javax.servlet.Servlet
try
{
checkServletType();
@ -306,8 +316,13 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
catch (UnavailableException ue)
{
makeUnavailable(ue);
if (!_servletHandler.isStartWithUnavailable())
throw ue; //servlet is not an instance of javax.servlet.Servlet
if (_servletHandler.isStartWithUnavailable())
{
LOG.ignore(ue);
return;
}
else
throw ue;
}

View File

@ -86,6 +86,14 @@ public class HttpTransportOverSPDY implements HttpTransport
return requestHeaders;
}
@Override
public void send(ByteBuffer responseBodyContent, boolean lastContent, Callback callback)
{
// TODO can this be more efficient?
send(null,responseBodyContent, lastContent, callback);
}
@Override
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
{
@ -185,14 +193,14 @@ public class HttpTransportOverSPDY implements HttpTransport
}
@Override
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent) throws EofException
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent) throws IOException
{
send(info, content, lastContent, streamBlocker);
try
{
streamBlocker.block();
}
catch (InterruptedException | TimeoutException | IOException e)
catch (Exception e)
{
LOG.debug(e);
}

View File

@ -264,6 +264,8 @@ public class ProxyHTTPSPDYConnection extends HttpConnection implements HttpParse
long contentLength = fields.getLongField(HttpHeader.CONTENT_LENGTH.asString());
HttpGenerator.ResponseInfo info = new HttpGenerator.ResponseInfo(httpVersion, fields, contentLength, code,
reason, false);
// TODO use the async send
send(info, null, replyInfo.isClose());
if (replyInfo.isClose())
@ -300,6 +302,7 @@ public class ProxyHTTPSPDYConnection extends HttpConnection implements HttpParse
// Data buffer must be copied, as the ByteBuffer is pooled
ByteBuffer byteBuffer = dataInfo.asByteBuffer(false);
// TODO use the async send with callback!
send(null, byteBuffer, dataInfo.isClose());
if (dataInfo.isClose())

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.util;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeoutException;
@ -87,25 +88,25 @@ public class BlockingCallback implements Callback
* This is useful for code that wants to repeatable use a FutureCallback to convert
* an asynchronous API to a blocking API.
* @return
* @throws InterruptedException
* @throws IOException
* @throws TimeoutException
*/
public void block() throws InterruptedException, IOException, TimeoutException
public void block() throws IOException
{
_semaphone.acquire();
try
{
_semaphone.acquire();
if (_cause==COMPLETED)
return;
if (_cause instanceof IOException)
throw (IOException) _cause;
if (_cause instanceof CancellationException)
throw (CancellationException) _cause;
if (_cause instanceof TimeoutException)
throw (TimeoutException) _cause;
throw new IOException(_cause);
}
catch (final InterruptedException e)
{
throw new InterruptedIOException(){{initCause(e);}};
}
finally
{
_done.set(false);

View File

@ -24,6 +24,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.Buffer;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
@ -326,7 +327,7 @@ public class BufferUtil
/* ------------------------------------------------------------ */
/**
*/
public static void append(ByteBuffer to, byte[] b,int off,int len)
public static void append(ByteBuffer to, byte[] b,int off,int len) throws BufferOverflowException
{
int pos= flipToFill(to);
try
@ -338,6 +339,25 @@ public class BufferUtil
flipToFlush(to,pos);
}
}
/* ------------------------------------------------------------ */
/** Like append, but does not throw {@link BufferOverflowException}
*/
public static int fill(ByteBuffer to, byte[] b,int off,int len)
{
int pos= flipToFill(to);
try
{
int remaining=to.remaining();
int take=remaining<len?remaining:len;
to.put(b,off,take);
return take;
}
finally
{
flipToFlush(to,pos);
}
}
/* ------------------------------------------------------------ */
/**
@ -731,9 +751,10 @@ public class BufferUtil
public static ByteBuffer toBuffer(File file) throws IOException
{
RandomAccessFile raf = new RandomAccessFile(file,"r");
MappedByteBuffer buffer=raf.getChannel().map(MapMode.READ_ONLY,0,raf.length());
return buffer;
try(RandomAccessFile raf = new RandomAccessFile(file,"r");)
{
return raf.getChannel().map(MapMode.READ_ONLY,0,raf.length());
}
}
public static String toSummaryString(ByteBuffer buffer)

View File

@ -0,0 +1,111 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.util;
import java.util.concurrent.atomic.AtomicBoolean;
/* ------------------------------------------------------------ */
/** Iterating Callback.
* <p>This specialised callback is used when breaking up an
* asynchronous task into smaller asynchronous tasks. A typical pattern
* is that a successful callback is used to schedule the next sub task, but
* if that task completes quickly and uses the calling thread to callback
* the success notification, this can result in a growing stack depth.
* </p>
* <p>To avoid this issue, this callback uses an Atomicboolean to note
* if the success callback has been called during the processing of a
* sub task, and if so then the processing iterates rather than recurses.
* </p>
* <p>This callback is passed to the asynchronous handling of each sub
* task and a call the {@link #succeeded()} on this call back represents
* completion of the subtask. Only once all the subtasks are completed is
* the {@link Callback#succeeded()} method called on the {@link Callback} instance
* passed the the {@link #IteratingCallback(Callback)} constructor.</p>
*
*/
public abstract class IteratingCallback implements Callback
{
final AtomicBoolean _iterating = new AtomicBoolean();
final Callback _callback;
public IteratingCallback(Callback callback)
{
_callback=callback;
}
/* ------------------------------------------------------------ */
/**
* Process a subtask.
* <p>Called by {@link #iterate()} to process a sub task of the overall task
* <p>
* @return True if the total task is complete. If false is returned
* then this Callback must be scheduled to receive either a call to
* {@link #succeeded()} or {@link #failed(Throwable)}.
* @throws Exception
*/
abstract protected boolean process() throws Exception;
/* ------------------------------------------------------------ */
/** This method is called initially to start processing and
* is then called by subsequent sub task success to continue
* processing.
*/
public void iterate()
{
try
{
// Keep iterating as long as succeeded() is called during process()
while(_iterating.compareAndSet(false,true))
{
// process and test if we are complete
if (process())
{
_callback.succeeded();
return;
}
}
}
catch(Exception e)
{
_iterating.set(false);
_callback.failed(e);
}
finally
{
_iterating.set(false);
}
}
@Override
public void succeeded()
{
if (!_iterating.compareAndSet(true,false))
iterate();
}
@Override
public void failed(Throwable x)
{
_callback.failed(x);
}
}

View File

@ -107,6 +107,17 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
{
l.start();
}
/**
* Stops the given lifecycle.
*
* @param l
* @throws Exception
*/
protected void stop(LifeCycle l) throws Exception
{
l.stop();
}
/**
* Stops the managed lifecycle beans in the reverse order they were added.
@ -124,7 +135,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
{
LifeCycle l = (LifeCycle)b._bean;
if (l.isRunning())
l.stop();
stop(l);
}
}
}
@ -261,7 +272,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
{
LifeCycle l = (LifeCycle)o;
if (!l.isRunning())
l.start();
start(l);
}
break;
@ -276,7 +287,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
else
{
manage(new_bean);
l.start();
start(l);
}
}
else
@ -452,6 +463,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
{
if (_beans.remove(bean))
{
unmanage(bean);
for (Container.Listener l:_listeners)
@ -472,7 +484,23 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
}
}
}
// stop managed beans
if (bean._managed==Managed.MANAGED && bean._bean instanceof LifeCycle)
{
try
{
stop((LifeCycle)bean._bean);
}
catch(RuntimeException | Error e)
{
throw e;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
return true;
}
return false;

View File

@ -22,6 +22,7 @@ package org.eclipse.jetty.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.util.Arrays;
@ -143,6 +144,29 @@ public class BufferUtilTest
assertEquals(2,from.remaining());
assertEquals("1234567890",BufferUtil.toString(to));
}
@Test
public void testAppend() throws Exception
{
ByteBuffer to = BufferUtil.allocate(8);
ByteBuffer from=BufferUtil.toBuffer("12345");
BufferUtil.append(to,from.array(),0,3);
assertEquals("123",BufferUtil.toString(to));
BufferUtil.append(to,from.array(),3,2);
assertEquals("12345",BufferUtil.toString(to));
try
{
BufferUtil.append(to,from.array(),0,5);
Assert.fail();
}
catch(BufferOverflowException e)
{}
}
@Test
public void testPutDirect() throws Exception

View File

@ -492,7 +492,7 @@ public class ResourceTest
// This test is intended to run only on Windows platform
assumeTrue(OS.IS_WINDOWS);
String path = __userURL.toURI().getPath().replace('/','\\')+"ResourceTest.java";
String path = __userURL.toURI().getPath().replace('/','\\')+"resource.txt";
System.err.println(path);
Resource resource = Resource.newResource(path, false);

View File

@ -57,18 +57,7 @@ public class HttpTransportOverMux implements HttpTransport
public void send(ResponseInfo info, ByteBuffer responseBodyContent, boolean lastContent) throws IOException
{
send(info,responseBodyContent,lastContent,streamBlocker);
try
{
streamBlocker.block();
}
catch (IOException e)
{
throw e;
}
catch (Exception e)
{
throw new EofException(e);
}
streamBlocker.block();
}
@Override
@ -87,4 +76,10 @@ public class HttpTransportOverMux implements HttpTransport
// prepare the AddChannelResponse
// TODO: look at HttpSender in jetty-client for generator loop logic
}
@Override
public void send(ByteBuffer responseBodyContent, boolean lastContent, Callback callback)
{
send(null,responseBodyContent, lastContent, callback);
}
}