X-Git-Url: https://code.wpia.club/?p=gigi.git;a=blobdiff_plain;f=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Fserver%2FHttpConnection.java;h=c489ae9b3da3c94317e8b8620e67a723583960a2;hp=16cbbf93d6b29f9554926e22ea2e8e075afc872a;hb=ba4f228fa9f72d50991a2218cfd83987ef5d385e;hpb=875b5e9651498a0cd8e0001c0742ba843e47cad0 diff --git a/lib/jetty/org/eclipse/jetty/server/HttpConnection.java b/lib/jetty/org/eclipse/jetty/server/HttpConnection.java index 16cbbf93..c489ae9b 100644 --- a/lib/jetty/org/eclipse/jetty/server/HttpConnection.java +++ b/lib/jetty/org/eclipse/jetty/server/HttpConnection.java @@ -1,6 +1,6 @@ // // ======================================================================== -// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// Copyright (c) 1995-2016 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 @@ -20,6 +20,7 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.channels.WritePendingException; import java.util.concurrent.RejectedExecutionException; import org.eclipse.jetty.http.HttpGenerator; @@ -44,7 +45,7 @@ import org.eclipse.jetty.util.log.Logger; /** *

A {@link Connection} that handles the HTTP protocol.

*/ -public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport +public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport, Connection.UpgradeFrom { public static final String UPGRADE_CONNECTION_ATTRIBUTE = "org.eclipse.jetty.server.HttpConnection.UPGRADE"; private static final boolean REQUEST_BUFFER_DIRECT=false; @@ -61,12 +62,13 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http private final HttpParser _parser; private volatile ByteBuffer _requestBuffer = null; private volatile ByteBuffer _chunk = null; + private final SendCallback _sendCallback = new SendCallback(); /* ------------------------------------------------------------ */ /** Get the current connection that this thread is dispatched to. - * Note that a thread may be processing a request asynchronously and - * thus not be dispatched to the connection. + * Note that a thread may be processing a request asynchronously and + * thus not be dispatched to the connection. * @see Request#getAttribute(String) for a more general way to access the HttpConnection * @return the current HttpConnection or null */ @@ -78,10 +80,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http protected static HttpConnection setCurrentConnection(HttpConnection connection) { HttpConnection last=__currentConnection.get(); - if (connection==null) - __currentConnection.remove(); - else - __currentConnection.set(connection); + __currentConnection.set(connection); return last; } @@ -103,24 +102,25 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http HttpInput input = newHttpInput(); _channel = newHttpChannel(input); _parser = newHttpParser(); - LOG.debug("New HTTP Connection {}", this); + if (LOG.isDebugEnabled()) + LOG.debug("New HTTP Connection {}", this); } protected HttpGenerator newHttpGenerator() { return new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy()); } - + protected HttpInput newHttpInput() { return new HttpInputOverHTTP(this); } - + protected HttpChannelOverHttp newHttpChannel(HttpInput httpInput) { return new HttpChannelOverHttp(_connector, _config, getEndPoint(), this, httpInput); } - + protected HttpParser newHttpParser() { return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize()); @@ -163,6 +163,18 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http return getHttpChannel().getRequests(); } + @Override + public ByteBuffer onUpgradeFrom() + { + if (BufferUtil.hasContent(_requestBuffer)) + { + ByteBuffer buffer = _requestBuffer; + _requestBuffer=null; + return buffer; + } + return null; + } + void releaseRequestBuffer() { if (_requestBuffer != null && !_requestBuffer.hasRemaining()) @@ -172,7 +184,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http _bufferPool.release(buffer); } } - + public ByteBuffer getRequestBuffer() { if (_requestBuffer == null) @@ -191,7 +203,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http @Override public void onFillable() { - LOG.debug("{} onFillable {}", this, _channel.getState()); + if (LOG.isDebugEnabled()) + LOG.debug("{} onFillable {}", this, _channel.getState()); final HttpConnection last=setCurrentConnection(this); int filled=Integer.MAX_VALUE; @@ -204,10 +217,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http // Do we need some data to parse if (BufferUtil.isEmpty(_requestBuffer)) { - // If the previous iteration filled 0 bytes or saw a close, then break here + // If the previous iteration filled 0 bytes or saw a close, then break here if (filled<=0) break; - + // Can we fill? if(getEndPoint().isInputShutdown()) { @@ -226,13 +239,13 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http filled = getEndPoint().fill(_requestBuffer); if (filled==0) // Do a retry on fill 0 (optimization for SSL connections) filled = getEndPoint().fill(_requestBuffer); - + // tell parser if (filled < 0) _parser.atEOF(); } } - + // Parse the buffer if (_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer)) { @@ -265,7 +278,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http close(); } finally - { + { setCurrentConnection(last); if (!suspended && getEndPoint().isOpen() && getEndPoint().getConnection()==this) { @@ -282,7 +295,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http { // Not in a race here for the request buffer with #onFillable because an async consumer of // content would only be started after onFillable has given up control. - // In a little bit of a race with #completed, but then not sure if it is legal to be doing + // In a little bit of a race with #completed, but then not sure if it is legal to be doing // async calls to IO and have a completed call at the same time. ByteBuffer requestBuffer = getRequestBuffer(); @@ -302,7 +315,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http if (parsed) break; - + // OK lets read some data int filled=getEndPoint().fill(requestBuffer); if (LOG.isDebugEnabled()) // Avoid boxing of variable 'filled' @@ -318,7 +331,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http } } } - + @Override public void completed() { @@ -328,10 +341,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http Connection connection = (Connection)_channel.getRequest().getAttribute(UPGRADE_CONNECTION_ATTRIBUTE); if (connection != null) { - LOG.debug("Upgrade from {} to {}", this, connection); - onClose(); - getEndPoint().setConnection(connection); - connection.onOpen(); + _channel.getState().upgrade(); + getEndPoint().upgrade(connection); _channel.reset(); _parser.reset(); _generator.reset(); @@ -339,15 +350,32 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http return; } } - + // Finish consuming the request // If we are still expecting if (_channel.isExpecting100Continue()) + { // close to seek EOF _parser.close(); + } else if (_parser.inContentState() && _generator.isPersistent()) - // Complete reading the request - _channel.getRequest().getHttpInput().consumeAll(); + { + // If we are async, then we have problems to complete neatly + if (_channel.getRequest().getHttpInput().isAsync()) + { + if (LOG.isDebugEnabled()) + LOG.debug("unconsumed async input {}", this); + _channel.abort(); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("unconsumed input {}", this); + // Complete reading the request + if (!_channel.getRequest().getHttpInput().consumeAll()) + _channel.abort(); + } + } // Reset the channel, parsers and generator _channel.reset(); @@ -355,7 +383,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http _parser.reset(); else _parser.close(); - + // Not in a race here with onFillable, because it has given up control before calling handle. // in a slight race with #completed, but not sure what to do with that anyway. releaseRequestBuffer(); @@ -398,7 +426,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http getEndPoint().close(); } } - // else the parser must be closed, so seek the EOF if we are still open + // else the parser must be closed, so seek the EOF if we are still open else if (getEndPoint().isOpen()) fillInterested(); } @@ -418,42 +446,65 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http fillInterested(); } + @Override + public void onClose() + { + _sendCallback.close(); + super.onClose(); + } + @Override public void run() { onFillable(); } - @Override public void send(ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback) { - 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(); - } + // If we are still expecting a 100 continues when we commit + if (info!=null && _channel.isExpecting100Continue()) + // then we can't be persistent + _generator.setPersistent(false); + + if(_sendCallback.reset(info,content,lastContent,callback)) + _sendCallback.iterate(); } @Override public void send(ByteBuffer content, boolean lastContent, Callback callback) { - new ContentCallback(content,lastContent,callback).iterate(); + if (!lastContent && BufferUtil.isEmpty(content)) + callback.succeeded(); + else if (_sendCallback.reset(null,content,lastContent,callback)) + _sendCallback.iterate(); + } + + @Override + public void abort() + { + // Do a direct close of the output, as this may indicate to a client that the + // response is bad either with RST or by abnormal completion of chunked response. + getEndPoint().close(); + } + + @Override + public String toString() + { + return String.format("%s[p=%s,g=%s,c=%s]", + super.toString(), + _parser, + _generator, + _channel); } - protected class HttpChannelOverHttp extends HttpChannel { public HttpChannelOverHttp(Connector connector, HttpConfiguration config, EndPoint endPoint, HttpTransport transport, HttpInput input) { super(connector,config,endPoint,transport,input); } - + @Override public void earlyEOF() { @@ -509,6 +560,12 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE); break; } + case HTTP_2: + { + persistent=false; + badMessage(400,null); + return true; + } default: { throw new IllegalStateException(); @@ -518,7 +575,16 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http if (!persistent) _generator.setPersistent(false); - return super.headerComplete(); + if (!super.headerComplete()) + return false; + + // Should we delay dispatch until we have some content? + // We should not delay if there is no content expect or client is expecting 100 or the response is already committed or the request buffer already has something in it to parse + if (getHttpConfiguration().isDelayDispatchUntilContent() && _parser.getContentLength() > 0 && + !isExpecting100Continue() && !isCommitted() && BufferUtil.isEmpty(_requestBuffer)) + return false; + + return true; } @Override @@ -529,11 +595,11 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http } @Override - public void failed() + public void abort() { - getEndPoint().shutdownOutput(); + super.abort(); + _generator.setPersistent(false); } - @Override public boolean messageComplete() @@ -543,25 +609,46 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http } } - private class CommitCallback extends IteratingCallback + private class SendCallback extends IteratingCallback { - final ByteBuffer _content; - final boolean _lastContent; - final ResponseInfo _info; - final Callback _callback; - ByteBuffer _header; + private ResponseInfo _info; + private ByteBuffer _content; + private boolean _lastContent; + private Callback _callback; + private ByteBuffer _header; + private boolean _shutdownOut; + + private SendCallback() + { + super(true); + } - CommitCallback(ResponseInfo info, ByteBuffer content, boolean last, Callback callback) + private boolean reset(ResponseInfo info, ByteBuffer content, boolean last, Callback callback) { - _info=info; - _content=content; - _lastContent=last; - _callback=callback; + if (reset()) + { + _info = info; + _content = content; + _lastContent = last; + _callback = callback; + _header = null; + _shutdownOut = false; + return true; + } + + if (isClosed()) + callback.failed(new EofException()); + else + callback.failed(new WritePendingException()); + return false; } @Override public Action process() throws Exception { + if (_callback==null) + throw new IllegalStateException(); + ByteBuffer chunk = _chunk; while (true) { @@ -579,25 +666,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http { case NEED_HEADER: { - // Look for optimisation to avoid allocating a _header buffer - /* - Cannot use this optimisation unless we work out how not to overwrite data in user passed arrays. - if (_lastContent && _content!=null && !_content.isReadOnly() && _content.hasArray() && BufferUtil.space(_content)>_config.getResponseHeaderSize() ) - { - // 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); - + _header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT); continue; } case NEED_CHUNK: @@ -607,8 +676,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http } case FLUSH: { - // Don't write the chunk or the content if this is a HEAD response - if (_channel.getRequest().isHead()) + // Don't write the chunk or the content if this is a HEAD response, or any other type of response that should have no content + if (_channel.getRequest().isHead() || _generator.isNoContent()) { BufferUtil.clear(chunk); BufferUtil.clear(_content); @@ -639,22 +708,18 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http getEndPoint().write(this, _content); } else - continue; + { + succeeded(); // nothing to write + } return Action.SCHEDULED; } case SHUTDOWN_OUT: { - getEndPoint().shutdownOutput(); + _shutdownOut=true; continue; } case DONE: { - 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); - } return Action.SUCCEEDED; } case CONTINUE: @@ -669,122 +734,36 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http } } - @Override - protected void completed() - { - _callback.succeeded(); - } - - @Override - public void failed(final Throwable x) - { - super.failed(x); - failedCallback(_callback,x); - } - } - - private class ContentCallback extends IteratingCallback - { - final ByteBuffer _content; - final boolean _lastContent; - final Callback _callback; - - ContentCallback(ByteBuffer content, boolean last, Callback callback) + private void releaseHeader() { - _content=content; - _lastContent=last; - _callback=callback; + ByteBuffer h=_header; + _header=null; + if (h!=null) + _bufferPool.release(h); } @Override - public Action process() throws Exception + protected void onCompleteSuccess() { - 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 Action.SCHEDULED; - } - case SHUTDOWN_OUT: - { - getEndPoint().shutdownOutput(); - continue; - } - case DONE: - { - return Action.SUCCEEDED; - } - case CONTINUE: - { - break; - } - default: - { - throw new IllegalStateException("generateResponse="+result); - } - } - } + releaseHeader(); + _callback.succeeded(); + if (_shutdownOut) + getEndPoint().shutdownOutput(); } @Override - protected void completed() + public void onCompleteFailure(final Throwable x) { - _callback.succeeded(); + releaseHeader(); + failedCallback(_callback,x); + if (_shutdownOut) + getEndPoint().shutdownOutput(); } @Override - public void failed(final Throwable x) + public String toString() { - super.failed(x); - failedCallback(_callback,x); + return String.format("%s[i=%s,cb=%s]",super.toString(),_info,_callback); } } - - @Override - public void abort() - { - // Do a direct close of the output, as this may indicate to a client that the - // response is bad either with RST or by abnormal completion of chunked response. - getEndPoint().close(); - } - }