moved http/1.1 isms out of HttpChannel into HttpConnection

This commit is contained in:
Greg Wilkins 2014-06-10 11:28:07 +02:00
parent 5883123d07
commit b38bae36f1
5 changed files with 278 additions and 212 deletions

View File

@ -236,8 +236,7 @@ public class FormAuthenticator extends LoginAuthenticator
//restore the original request's method on this request
if (LOG.isDebugEnabled()) LOG.debug("Restoring original method {} for {} with method {}", method, juri,httpRequest.getMethod());
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
HttpMethod m = HttpMethod.fromString(method);
base_request.setMethod(m,m.asString());
base_request.setMethod(method);
}
/* ------------------------------------------------------------ */

View File

@ -67,7 +67,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
* HttpTransport.completed().
*
*/
public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, HttpParser.ProxyHandler
public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
{
private static final Logger LOG = Log.getLogger(HttpChannel.class);
private static final ThreadLocal<HttpChannel<?>> __currentChannel = new ThreadLocal<>();
@ -102,9 +102,6 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, H
private final Request _request;
private final Response _response;
private HttpVersion _version = HttpVersion.HTTP_1_1;
private boolean _expect = false;
private boolean _expect100Continue = false;
private boolean _expect102Processing = false;
public HttpChannel(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<T> input)
{
@ -201,32 +198,12 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, H
*/
public void continue100(int available) throws IOException
{
// If the client is expecting 100 CONTINUE, then send it now.
// TODO: consider using an AtomicBoolean ?
if (isExpecting100Continue())
{
_expect100Continue = false;
// is content missing?
if (available == 0)
{
if (_response.isCommitted())
throw new IOException("Committed before 100 Continues");
// TODO: break this dependency with HttpGenerator
boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
if (!committed)
throw new IOException("Concurrent commit while trying to send 100-Continue");
}
}
throw new UnsupportedOperationException();
}
public void reset()
{
_committed.set(false);
_expect = false;
_expect100Continue = false;
_expect102Processing = false;
_request.recycle();
_response.recycle();
}
@ -458,12 +435,12 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, H
public boolean isExpecting100Continue()
{
return _expect100Continue;
return false;
}
public boolean isExpecting102Processing()
{
return _expect102Processing;
return false;
}
@Override
@ -478,22 +455,9 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, H
);
}
@Override
public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
{
_request.setAttribute("PROXY", protocol);
_request.setServerName(sAddr);
_request.setServerPort(dPort);
_request.setRemoteAddr(InetSocketAddress.createUnresolved(sAddr,sPort));
}
@Override
public boolean startRequest(String method, HttpURI uri, HttpVersion version)
{
_expect = false;
_expect100Continue = false;
_expect102Processing = false;
_request.setTimeStamp(System.currentTimeMillis());
_request.setMethod(method);
@ -515,7 +479,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, H
if (info == null)
{
if( path==null && uri.getScheme()!=null &&uri.getHost()!=null)
if( path==null && uri.getScheme()!=null && uri.getHost()!=null)
{
info = "/";
_request.setRequestURI("");
@ -526,8 +490,9 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, H
return true;
}
}
_request.setPathInfo(info);
_version = version == null ? HttpVersion.HTTP_0_9 : version;
_version = version;
_request.setHttpVersion(_version);
return false;
@ -544,46 +509,6 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, H
{
switch (header)
{
case EXPECT:
if (_version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
{
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
{
case CONTINUE:
_expect100Continue = true;
break;
case PROCESSING:
_expect102Processing = true;
break;
default:
String[] values = value.split(",");
for (int i = 0; values != null && i < values.length; i++)
{
expect = HttpHeaderValue.CACHE.get(values[i].trim());
if (expect == null)
_expect = true;
else
{
switch (expect)
{
case CONTINUE:
_expect100Continue = true;
break;
case PROCESSING:
_expect102Processing = true;
break;
default:
_expect = true;
}
}
}
}
}
break;
case CONTENT_TYPE:
MimeTypes.Type mime = MimeTypes.CACHE.get(value);
String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(value) : mime.getCharset().toString();
@ -614,31 +539,9 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, H
public boolean headerComplete()
{
_requests.incrementAndGet();
switch (_version)
{
case HTTP_0_9:
break;
case HTTP_1_0:
if (_configuration.getSendDateHeader())
_response.getHttpFields().put(_connector.getServer().getDateField());
break;
case HTTP_1_1:
if (_configuration.getSendDateHeader())
_response.getHttpFields().put(_connector.getServer().getDateField());
if (_expect)
{
badMessage(HttpStatus.EXPECTATION_FAILED_417,null);
return true;
}
break;
default:
throw new IllegalStateException();
}
// TODO make this a better field for h2 hpack generation
if (_configuration.getSendDateHeader())
_response.getHttpFields().put(_connector.getServer().getDateField());
return true;
}

View File

@ -0,0 +1,263 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.EndPoint;
/**
* A HttpChannel customized to be transported over the HTTP/1 protocol
*/
class HttpChannelOverHttp extends HttpChannel<ByteBuffer> implements HttpParser.ProxyHandler
{
/**
*
*/
private final HttpConnection _httpConnection;
private boolean _expect = false;
private boolean _expect100Continue = false;
private boolean _expect102Processing = false;
public HttpChannelOverHttp(HttpConnection httpConnection, Connector connector, HttpConfiguration config, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
{
super(connector,config,endPoint,transport,input);
_httpConnection = httpConnection;
}
@Override
public void reset()
{
super.reset();
_expect = false;
_expect100Continue = false;
_expect102Processing = false;
}
@Override
public boolean isExpecting100Continue()
{
return _expect100Continue;
}
@Override
public boolean isExpecting102Processing()
{
return _expect102Processing;
}
@Override
public boolean startRequest(String method, HttpURI uri, HttpVersion version)
{
_expect = false;
_expect100Continue = false;
_expect102Processing = false;
return super.startRequest(method,uri,version);
}
@Override
public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
{
Request request = getRequest();
request.setAttribute("PROXY", protocol);
request.setServerName(sAddr);
request.setServerPort(dPort);
request.setRemoteAddr(InetSocketAddress.createUnresolved(sAddr,sPort));
}
@Override
public boolean parsedHeader(HttpField field)
{
HttpHeader header=field.getHeader();
String value=field.getValue();
if (getRequest().getHttpVersion().getVersion()==HttpVersion.HTTP_1_1.getVersion() && header == HttpHeader.EXPECT)
{
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
{
case CONTINUE:
_expect100Continue = true;
break;
case PROCESSING:
_expect102Processing = true;
break;
default:
String[] values = value.split(",");
for (int i = 0; values != null && i < values.length; i++)
{
expect = HttpHeaderValue.CACHE.get(values[i].trim());
if (expect == null)
_expect = true;
else
{
switch (expect)
{
case CONTINUE:
_expect100Continue = true;
break;
case PROCESSING:
_expect102Processing = true;
break;
default:
_expect = true;
}
}
}
}
}
return super.parsedHeader(field);
}
/**
* If the associated response has the Expect header set to 100 Continue,
* then accessing the input stream indicates that the handler/servlet
* is ready for the request body and thus a 100 Continue response is sent.
*
* @throws IOException if the InputStream cannot be created
*/
@Override
public void continue100(int available) throws IOException
{
// If the client is expecting 100 CONTINUE, then send it now.
// TODO: consider using an AtomicBoolean ?
if (isExpecting100Continue())
{
_expect100Continue = false;
// is content missing?
if (available == 0)
{
if (getResponse().isCommitted())
throw new IOException("Committed before 100 Continues");
// TODO: break this dependency with HttpGenerator
boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
if (!committed)
throw new IOException("Concurrent commit while trying to send 100-Continue");
}
}
}
@Override
public void earlyEOF()
{
// If we have no request yet, just close
if (getRequest().getMethod()==null)
_httpConnection.close();
else
super.earlyEOF();
}
@Override
public boolean content(ByteBuffer item)
{
super.content(item);
return true;
}
@Override
public void badMessage(int status, String reason)
{
_httpConnection._generator.setPersistent(false);
super.badMessage(status,reason);
}
@Override
public boolean headerComplete()
{
boolean persistent;
HttpVersion version = getHttpVersion();
switch (version)
{
case HTTP_1_0:
{
persistent = getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
if (!persistent)
persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
if (persistent)
getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
break;
}
case HTTP_1_1:
{
if (_expect)
{
badMessage(HttpStatus.EXPECTATION_FAILED_417,null);
return true;
}
persistent = !getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
if (!persistent)
persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
if (!persistent)
getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
break;
}
default:
{
throw new IllegalStateException();
}
}
if (!persistent)
_httpConnection._generator.setPersistent(false);
return super.headerComplete();
}
@Override
protected void handleException(Throwable x)
{
_httpConnection._generator.setPersistent(false);
super.handleException(x);
}
@Override
public void failed()
{
getEndPoint().shutdownOutput();
}
@Override
public boolean messageComplete()
{
super.messageComplete();
return false;
}
}

View File

@ -24,12 +24,9 @@ import java.util.concurrent.RejectedExecutionException;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
@ -56,7 +53,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
private final HttpConfiguration _config;
private final Connector _connector;
private final ByteBufferPool _bufferPool;
private final HttpGenerator _generator;
final HttpGenerator _generator;
private final HttpChannelOverHttp _channel;
private final HttpParser _parser;
private volatile ByteBuffer _requestBuffer = null;
@ -118,7 +115,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
protected HttpChannelOverHttp newHttpChannel(HttpInput<ByteBuffer> httpInput)
{
return new HttpChannelOverHttp(_connector, _config, getEndPoint(), this, httpInput);
return new HttpChannelOverHttp(this, _connector, _config, getEndPoint(), this, httpInput);
}
protected HttpParser newHttpParser()
@ -447,102 +444,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
}
protected class HttpChannelOverHttp extends HttpChannel<ByteBuffer>
{
public HttpChannelOverHttp(Connector connector, HttpConfiguration config, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
{
super(connector,config,endPoint,transport,input);
}
@Override
public void earlyEOF()
{
// If we have no request yet, just close
if (getRequest().getMethod()==null)
close();
else
super.earlyEOF();
}
@Override
public boolean content(ByteBuffer item)
{
super.content(item);
return true;
}
@Override
public void badMessage(int status, String reason)
{
_generator.setPersistent(false);
super.badMessage(status,reason);
}
@Override
public boolean headerComplete()
{
boolean persistent;
HttpVersion version = getHttpVersion();
switch (version)
{
case HTTP_0_9:
{
persistent = false;
break;
}
case HTTP_1_0:
{
persistent = getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
if (!persistent)
persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
if (persistent)
getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
break;
}
case HTTP_1_1:
{
persistent = !getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
if (!persistent)
persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
if (!persistent)
getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
break;
}
default:
{
throw new IllegalStateException();
}
}
if (!persistent)
_generator.setPersistent(false);
return super.headerComplete();
}
@Override
protected void handleException(Throwable x)
{
_generator.setPersistent(false);
super.handleException(x);
}
@Override
public void failed()
{
getEndPoint().shutdownOutput();
}
@Override
public boolean messageComplete()
{
super.messageComplete();
return false;
}
}
private class CommitCallback extends IteratingCallback
{
final ByteBuffer _content;

View File

@ -102,7 +102,7 @@ public class ExtendedServerTest extends HttpServerTestBase
@Override
protected HttpChannelOverHttp newHttpChannel(HttpInput<ByteBuffer> httpInput)
{
return new HttpChannelOverHttp(getConnector(), getHttpConfiguration(), getEndPoint(), this, httpInput)
return new HttpChannelOverHttp(this, getConnector(), getHttpConfiguration(), getEndPoint(), this, httpInput)
{
@Override
public boolean startRequest(String method, HttpURI uri, HttpVersion version)