Merge remote-tracking branch 'origin/jetty-http2' into pathresource

This commit is contained in:
Greg Wilkins 2014-08-01 10:17:42 +10:00
commit fa9b60c77e
412 changed files with 150256 additions and 3623 deletions

View File

@ -1,3 +1,5 @@
jetty-9.3.0-SNAPSHOT
jetty-9.2.3-SNAPSHOT
jetty-9.2.2-v20140723 - 23 July 2014
@ -56,6 +58,7 @@ jetty-9.2.2-v20140723 - 23 July 2014
+ 440038 Content decoding may fail.
+ 440114 ContextHandlerCollection does not skip context wrappers
+ 440122 Remove usages of ForkInvoker.
>>>>>>> origin/master
jetty-9.2.1.v20140609 - 09 June 2014
+ 347110 Supprt ClassFileTransormers in WebAppClassLoader

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apache-jsp</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apache-jstl</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>example-async-rest</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.example-async-rest</groupId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>example-async-rest</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.example-async-rest</groupId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.examples</groupId>
<artifactId>examples-parent</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.examples</groupId>
<artifactId>examples-parent</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -18,14 +18,14 @@
package org.eclipse.jetty.embedded;
import org.eclipse.jetty.npn.server.NPNServerConnectionFactory;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@ -72,8 +72,8 @@ public class SpdyConnector
new HTTPSPDYServerConnectionFactory(3,https_config,new ReferrerPushStrategy());
// NPN Factory
SPDYServerConnectionFactory.checkProtocolNegotiationAvailable();
NPNServerConnectionFactory npn =
NegotiatingServerConnectionFactory.checkProtocolNegotiationAvailable();
NPNServerConnectionFactory npn =
new NPNServerConnectionFactory(spdy3.getProtocol(),spdy2.getProtocol(),http.getDefaultProtocol());
npn.setDefaultProtocol(http.getDefaultProtocol());

View File

@ -23,6 +23,7 @@ import java.lang.management.ManagementFactory;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.providers.WebAppProvider;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.npn.server.NPNServerConnectionFactory;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.server.AsyncNCSARequestLog;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
@ -30,6 +31,7 @@ import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.NCSARequestLog;
import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
@ -39,8 +41,6 @@ import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
import org.eclipse.jetty.spdy.server.http.PushStrategy;
import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy;
@ -110,7 +110,7 @@ public class SpdyServer
// Spdy Connector
// Make sure that the required NPN implementations are available.
SPDYServerConnectionFactory.checkProtocolNegotiationAvailable();
NegotiatingServerConnectionFactory.checkProtocolNegotiationAvailable();
// A ReferrerPushStrategy is being initialized.
// See: http://www.eclipse.org/jetty/documentation/current/spdy-configuring-push.html for more details.

View File

@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.jetty.examples</groupId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.examples</groupId>
<artifactId>examples-parent</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -2,13 +2,11 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-parent</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-client</artifactId>
<name>Jetty :: ALPN Client</name>
<description>Jetty ALPN client services</description>
<url>http://www.eclipse.org/jetty</url>
<name>Jetty :: ALPN :: Client</name>
<properties>
<bundle-symbolic-name>${project.groupId}.alpn.client</bundle-symbolic-name>
</properties>

View File

@ -2,13 +2,11 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-parent</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-server</artifactId>
<name>Jetty :: ALPN Server</name>
<description>Jetty ALPN server services</description>
<url>http://www.eclipse.org/jetty</url>
<name>Jetty :: ALPN :: Server</name>
<properties>
<bundle-symbolic-name>${project.groupId}.alpn.server</bundle-symbolic-name>
</properties>

View File

@ -2,14 +2,12 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-parent</artifactId>
<packaging>pom</packaging>
<name>Jetty :: ALPN :: Parent</name>
<description>Jetty ALPN services parent</description>
<url>http://www.eclipse.org/jetty</url>
<modules>
<module>jetty-alpn-server</module>
<module>jetty-alpn-client</module>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-annotations</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ant</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -950,7 +950,7 @@ public class HttpClient extends ContainerLifeCycle
return port > 0 ? port : HttpScheme.HTTPS.is(scheme) ? 443 : 80;
}
protected boolean isDefaultPort(String scheme, int port)
public boolean isDefaultPort(String scheme, int port)
{
return HttpScheme.HTTPS.is(scheme) ? port == 443 : port == 80;
}

View File

@ -705,7 +705,7 @@ public class HttpRequest implements Request
if (value == null)
return "";
String encoding = "UTF-8";
String encoding = "utf-8";
try
{
return URLEncoder.encode(value, encoding);
@ -736,7 +736,7 @@ public class HttpRequest implements Request
private String urlDecode(String value)
{
String charset = "UTF-8";
String charset = "utf-8";
try
{
return URLDecoder.decode(value, charset);

View File

@ -35,7 +35,7 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.CompletableCallback;
public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.ResponseHandler
{
private final HttpParser parser = new HttpParser(this);
private ByteBuffer buffer;
@ -198,14 +198,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
}
@Override
public boolean parsedHeader(HttpField field)
public void parsedHeader(HttpField field)
{
HttpExchange exchange = getHttpExchange();
if (exchange == null)
return false;
responseHeader(exchange, field);
return false;
if (exchange != null)
responseHeader(exchange, field);
}
@Override

View File

@ -93,7 +93,7 @@ public class HttpClientCustomProxyTest
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
if (!URI.create(baseRequest.getUri().toString()).isAbsolute())
if (!URI.create(baseRequest.getHttpURI().toString()).isAbsolute())
response.setStatus(HttpServletResponse.SC_USE_PROXY);
else if (serverHost.equals(request.getServerName()))
response.setStatus(status);

View File

@ -58,7 +58,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
if (!URI.create(baseRequest.getUri().toString()).isAbsolute())
if (!URI.create(baseRequest.getHttpURI().toString()).isAbsolute())
response.setStatus(HttpServletResponse.SC_USE_PROXY);
else if (serverHost.equals(request.getServerName()))
response.setStatus(status);

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-continuation</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-deploy</artifactId>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<artifactId>jetty-distribution</artifactId>
<name>Jetty :: Distribution Assemblies</name>
@ -301,7 +301,7 @@
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
<excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs</excludeGroupIds>
<excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.http2,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs</excludeGroupIds>
<excludeArtifactIds>jetty-all,jetty-jsp,apache-jsp,jetty-start,jetty-monitor,jetty-spring</excludeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib</outputDirectory>
@ -320,6 +320,19 @@
<outputDirectory>${assembly-directory}/lib/websocket</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-lib-http2-deps</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty.http2</includeGroupIds>
<includeArtifactIds>http2-hpack,http2-common,http2-server</includeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/http2</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-lib-fcgi-deps</id>
<phase>generate-resources</phase>
@ -805,6 +818,11 @@
<artifactId>jetty-rewrite</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-core</artifactId>
@ -831,6 +849,11 @@
<artifactId>jetty-alpn-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-npn-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.example-async-rest</groupId>
<artifactId>example-async-rest-webapp</artifactId>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-parent</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.fcgi.generator;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@ -46,7 +47,7 @@ public class ClientGenerator extends Generator
{
request &= 0xFF_FF;
Charset utf8 = Charset.forName("UTF-8");
final Charset utf8 = StandardCharsets.UTF_8;
List<byte[]> bytes = new ArrayList<>(fields.size() * 2);
int fieldsLength = 0;
for (HttpField field : fields)

View File

@ -80,6 +80,7 @@ public class Generator
return result;
}
// TODO: rewrite this class in light of ByteBufferPool.Lease.
public static class Result implements Callback
{
private final List<Callback> callbacks = new ArrayList<>(2);

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.fcgi.generator;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@ -54,7 +55,7 @@ public class ServerGenerator extends Generator
{
request &= 0xFF_FF;
Charset utf8 = Charset.forName("UTF-8");
final Charset utf8 = StandardCharsets.UTF_8;
List<byte[]> bytes = new ArrayList<>(fields.size() * 2);
int length = 0;

View File

@ -71,7 +71,7 @@ public class ResponseContentParser extends StreamContentParser
parsers.remove(request);
}
private class ResponseParser implements HttpParser.ResponseHandler<ByteBuffer>
private class ResponseParser implements HttpParser.ResponseHandler
{
private final HttpFields fields = new HttpFields();
private ClientParser.Listener listener;
@ -154,7 +154,7 @@ public class ResponseContentParser extends StreamContentParser
}
@Override
public boolean parsedHeader(HttpField httpField)
public void parsedHeader(HttpField httpField)
{
try
{
@ -190,7 +190,6 @@ public class ResponseContentParser extends StreamContentParser
if (LOG.isDebugEnabled())
LOG.debug("Exception while invoking listener " + listener, x);
}
return false;
}
private void notifyBegin(int code, String reason)
@ -299,7 +298,7 @@ public class ResponseContentParser extends StreamContentParser
// Methods overridden to make them visible here
private static class FCGIHttpParser extends HttpParser
{
private FCGIHttpParser(ResponseHandler<ByteBuffer> handler)
private FCGIHttpParser(ResponseHandler handler)
{
super(handler, 65 * 1024, true);
reset();

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-parent</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -18,18 +18,18 @@
package org.eclipse.jetty.fcgi.server;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpChannel;
@ -39,18 +39,19 @@ import org.eclipse.jetty.server.HttpTransport;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer>
public class HttpChannelOverFCGI extends HttpChannel
{
private static final Logger LOG = Log.getLogger(HttpChannelOverFCGI.class);
private final List<HttpField> fields = new ArrayList<>();
private final HttpFields fields = new HttpFields();
private final Dispatcher dispatcher;
private String method;
private String path;
private String query;
private String version;
private HostPortHttpField hostPort;
public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput input)
{
super(connector, configuration, endPoint, transport, input);
this.dispatcher = new Dispatcher(connector.getExecutor(), this);
@ -67,26 +68,27 @@ public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer>
else if (FCGI.Headers.SERVER_PROTOCOL.equalsIgnoreCase(field.getName()))
version = field.getValue();
else
fields.add(field);
processField(field);
}
@Override
public boolean headerComplete()
private void processField(HttpField field)
{
HttpField httpField = convertHeader(field);
if (httpField != null)
{
fields.add(httpField);
if (HttpHeader.HOST.is(httpField.getName()))
hostPort = (HostPortHttpField)httpField;
}
}
public void onRequest()
{
String uri = path;
if (query != null && query.length() > 0)
uri += "?" + query;
startRequest(HttpMethod.fromString(method), method, ByteBuffer.wrap(uri.getBytes(StandardCharsets.UTF_8)),
HttpVersion.fromString(version));
for (HttpField fcgiField : fields)
{
HttpField httpField = convertHeader(fcgiField);
if (httpField != null)
parsedHeader(httpField);
}
return super.headerComplete();
// TODO https?
onRequest(new MetaData.Request(method, HttpScheme.HTTP.asString(), hostPort, uri, HttpVersion.fromString(version), fields));
}
private HttpField convertHeader(HttpField field)
@ -105,7 +107,11 @@ public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer>
httpName.append(Character.toUpperCase(part.charAt(0)));
httpName.append(part.substring(1).toLowerCase(Locale.ENGLISH));
}
return new HttpField(httpName.toString(), field.getValue());
String headerName = httpName.toString();
if (HttpHeader.HOST.is(headerName))
return new HostPortHttpField(field.getValue());
else
return new HttpField(httpName.toString(), field.getValue());
}
return null;
}

View File

@ -49,6 +49,36 @@ public class HttpTransportOverFCGI implements HttpTransport
@Override
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
{
if (info!=null)
commit(info,content,lastContent,callback);
else
{
if (head)
{
if (lastContent)
{
Generator.Result result = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback);
flusher.flush(result);
}
else
{
// Skip content generation
callback.succeeded();
}
}
else
{
Generator.Result result = generateResponseContent(content, lastContent, callback);
flusher.flush(result);
}
if (lastContent && shutdown)
flusher.shutdown();
}
}
private void commit(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
{
boolean head = this.head = info.isHead();
boolean shutdown = this.shutdown = info.getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
@ -78,32 +108,6 @@ public class HttpTransportOverFCGI implements HttpTransport
flusher.shutdown();
}
@Override
public void send(ByteBuffer content, boolean lastContent, Callback callback)
{
if (head)
{
if (lastContent)
{
Generator.Result result = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback);
flusher.flush(result);
}
else
{
// Skip content generation
callback.succeeded();
}
}
else
{
Generator.Result result = generateResponseContent(content, lastContent, callback);
flusher.flush(result);
}
if (lastContent && shutdown)
flusher.shutdown();
}
protected Generator.Result generateResponseHeaders(HttpGenerator.ResponseInfo info, Callback callback)
{
return generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), info.getHttpFields(), callback);

View File

@ -29,9 +29,10 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.ByteBufferQueuedHttpInput;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpInput;
import org.eclipse.jetty.server.QueuedHttpInput;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -123,7 +124,7 @@ public class ServerFCGIConnection extends AbstractConnection
// TODO: handle flags
HttpChannelOverFCGI channel = new HttpChannelOverFCGI(connector, configuration, getEndPoint(),
new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request, sendStatus200),
new ByteBufferQueuedHttpInput());
new QueuedHttpInput());
HttpChannelOverFCGI existing = channels.putIfAbsent(request, channel);
if (existing != null)
throw new IllegalStateException();
@ -149,8 +150,8 @@ public class ServerFCGIConnection extends AbstractConnection
LOG.debug("Request {} headers on {}", request, channel);
if (channel != null)
{
if (channel.headerComplete())
channel.dispatch();
channel.onRequest();
channel.dispatch();
}
}
@ -162,8 +163,8 @@ public class ServerFCGIConnection extends AbstractConnection
LOG.debug("Request {} {} content {} on {}", request, stream, buffer, channel);
if (channel != null)
{
if (channel.content(buffer))
channel.dispatch();
// TODO avoid creating content all the time
channel.onContent(new HttpInput.Content(buffer));
}
return false;
}
@ -176,8 +177,8 @@ public class ServerFCGIConnection extends AbstractConnection
LOG.debug("Request {} end on {}", request, channel);
if (channel != null)
{
if (channel.messageComplete())
channel.dispatch();
channel.onRequestComplete();
channel.dispatch();
}
}
@ -189,7 +190,7 @@ public class ServerFCGIConnection extends AbstractConnection
LOG.debug("Request {} failure on {}: {}", request, channel, failure);
if (channel != null)
{
channel.badMessage(400, failure.toString());
channel.onBadMessage(400, failure.toString());
}
}
}

View File

@ -19,8 +19,10 @@
package org.eclipse.jetty.fcgi.server.proxy;
import java.net.URI;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@ -31,6 +33,7 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.proxy.AsyncProxyServlet;
import org.eclipse.jetty.proxy.ProxyServlet;
@ -127,6 +130,33 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
proxyRequest.attribute(REQUEST_URI_ATTRIBUTE, originalURI);
}
// If the Host header is missing, add it.
if (!proxyRequest.getHeaders().containsKey(HttpHeader.HOST.asString()))
{
String host = request.getServerName();
int port = request.getServerPort();
if (!getHttpClient().isDefaultPort(request.getScheme(), port))
host += ":" + port;
proxyRequest.header(HttpHeader.HOST, host);
proxyRequest.header(HttpHeader.X_FORWARDED_HOST, host);
}
// PHP does not like multiple Cookie headers, coalesce into one.
List<String> cookies = proxyRequest.getHeaders().getValuesList(HttpHeader.COOKIE.asString());
if (cookies.size() > 1)
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < cookies.size(); ++i)
{
if (i > 0)
builder.append("; ");
String cookie = cookies.get(i);
builder.append(cookie);
}
proxyRequest.header(HttpHeader.COOKIE, null);
proxyRequest.header(HttpHeader.COOKIE, builder.toString());
}
super.customizeProxyRequest(proxyRequest, request);
}

View File

@ -24,6 +24,7 @@ import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.fcgi.server;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

View File

@ -32,6 +32,7 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;

View File

@ -25,6 +25,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.fcgi.server.proxy;
import java.io.IOException;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.fcgi.server.proxy;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;
import org.eclipse.jetty.server.Server;

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http-spi</artifactId>

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
<version>9.2.3-SNAPSHOT</version>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http</artifactId>

View File

@ -0,0 +1,64 @@
//
// ========================================================================
// 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.http;
/* ------------------------------------------------------------------------------- */
public class BadMessageException extends RuntimeException
{
final int _code;
final String _reason;
public BadMessageException()
{
this(400,null);
}
public BadMessageException(int code)
{
this(code,null);
}
public BadMessageException(String reason)
{
this(400,reason);
}
public BadMessageException(int code, String reason)
{
_code=code;
_reason=reason;
}
public BadMessageException(int code, String reason, Throwable cause)
{
super(cause);
_code=code;
_reason=reason;
}
public int getCode()
{
return _code;
}
public String getReason()
{
return _reason;
}
}

View File

@ -27,7 +27,7 @@ import java.util.TimeZone;
* ThreadLocal data parsers for HTTP style dates
*
*/
class DateParser
public class DateParser
{
private static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
static

View File

@ -0,0 +1,105 @@
//
// ========================================================================
// 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.http;
import org.eclipse.jetty.util.StringUtil;
/* ------------------------------------------------------------ */
/**
*/
public class HostPortHttpField extends HttpField
{
private final String _host;
private final int _port;
public HostPortHttpField(String authority)
{
this(HttpHeader.HOST,HttpHeader.HOST.asString(),authority);
}
public HostPortHttpField(HttpHeader header, String name, String authority)
{
super(header,name,authority);
try
{
if (authority.charAt(0)=='[')
{
// ipv6reference
int close=authority.lastIndexOf(']');
if (close<0)
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad ipv6");
_host=authority.substring(0,close+1);
if (authority.length()>close+1)
{
if (authority.charAt(close+1)!=':')
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad ipv6 port");
_port=StringUtil.toInt(authority,close+2);
}
else
_port=0;
}
else
{
// ipv4address or hostname
int c = authority.lastIndexOf(':');
if (c>=0)
{
_host=authority.substring(0,c);
_port=StringUtil.toInt(authority,c+1);
}
else
{
_host=authority;
_port=0;
}
}
}
catch (BadMessageException bm)
{
throw bm;
}
catch(Exception e)
{
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad HostPort",e);
}
}
/* ------------------------------------------------------------ */
/** Get the host.
* @return the host
*/
public String getHost()
{
return _host;
}
/* ------------------------------------------------------------ */
/** Get the port.
* @return the port
*/
public int getPort()
{
return _port;
}
}

View File

@ -18,12 +18,16 @@
package org.eclipse.jetty.http;
import java.util.ArrayList;
import org.eclipse.jetty.util.StringUtil;
/* ------------------------------------------------------------ */
/** A HTTP Field
*/
public class HttpField
{
private final static String __zeroquality="q=0";
private final HttpHeader _header;
private final String _name;
private final String _value;
@ -65,6 +69,293 @@ public class HttpField
return _value;
}
public long getLongValue()
{
return StringUtil.toLong(_value);
}
public String[] getValues()
{
ArrayList<String> list = new ArrayList<>();
int state = 0;
int start=0;
int end=0;
StringBuilder builder = new StringBuilder();
for (int i=0;i<_value.length();i++)
{
char c = _value.charAt(i);
switch(state)
{
case 0: // initial white space
switch(c)
{
case '"': // open quote
state=2;
break;
case ',': // ignore leading empty field
break;
case ' ': // more white space
case '\t':
break;
default: // character
start=i;
end=i;
state=1;
}
break;
case 1: // In token
switch(c)
{
case ',': // next field
list.add(_value.substring(start,end+1));
state=0;
break;
case ' ': // more white space
case '\t':
break;
default:
end=i;
}
break;
case 2: // In Quoted
switch(c)
{
case '\\': // next field
state=3;
break;
case '"': // end quote
list.add(builder.toString());
builder.setLength(0);
state=4;
break;
default:
builder.append(c);
}
break;
case 3: // In Quoted Quoted
builder.append(c);
state=2;
break;
case 4: // WS after end quote
switch(c)
{
case ' ': // white space
case '\t': // white space
break;
case ',': // white space
state=0;
break;
default:
throw new IllegalArgumentException("c="+(int)c);
}
break;
}
}
switch(state)
{
case 0:
break;
case 1:
list.add(_value.substring(start,end+1));
break;
case 4:
break;
default:
throw new IllegalArgumentException("state="+state);
}
return list.toArray(new String[list.size()]);
}
/* ------------------------------------------------------------ */
/** Look for a value in a possible multi valued field
* @param search Values to search for
* @return True iff the value is contained in the field value entirely or
* as an element of a quoted comma separated list. List element parameters (eg qualities) are ignored,
* except if they are q=0, in which case the item itself is ignored.
*/
public boolean contains(String search)
{
if (_value==null || search==null)
return _value==search;
if (search.length()==0)
return false;
int state=0;
int match=0;
int param=0;
for (int i=0;i<_value.length();i++)
{
char c = _value.charAt(i);
switch(state)
{
case 0: // initial white space
switch(c)
{
case '"': // open quote
match=0;
state=2;
break;
case ',': // ignore leading empty field
break;
case ';': // ignore leading empty field parameter
param=-1;
match=-1;
state=5;
break;
case ' ': // more white space
case '\t':
break;
default: // character
match = c==search.charAt(0)?1:-1;
state=1;
break;
}
break;
case 1: // In token
switch(c)
{
case ',': // next field
// Have we matched the token?
if (match==search.length())
return true;
state=0;
break;
case ';':
param=match>=0?0:-1;
state=5; // parameter
break;
default:
if (match>0)
{
if (match<search.length())
match=c==search.charAt(match)?(match+1):-1;
else if (c!=' ' && c!= '\t')
match=-1;
}
break;
}
break;
case 2: // In Quoted token
switch(c)
{
case '\\': // quoted character
state=3;
break;
case '"': // end quote
state=4;
break;
default:
if (match>=0)
{
if (match<search.length())
match=c==search.charAt(match)?(match+1):-1;
else
match=-1;
}
}
break;
case 3: // In Quoted character in quoted token
if (match>=0)
{
if (match<search.length())
match=c==search.charAt(match)?(match+1):-1;
else
match=-1;
}
state=2;
break;
case 4: // WS after end quote
switch(c)
{
case ' ': // white space
case '\t': // white space
break;
case ';':
state=5; // parameter
break;
case ',': // end token
// Have we matched the token?
if (match==search.length())
return true;
state=0;
break;
default:
// This is an illegal token, just ignore
match=-1;
}
break;
case 5: // parameter
switch(c)
{
case ',': // end token
// Have we matched the token and not q=0?
if (param!=__zeroquality.length() && match==search.length())
return true;
param=0;
state=0;
break;
case ' ': // white space
case '\t': // white space
break;
default:
if (param>=0)
{
if (param<__zeroquality.length())
param=c==__zeroquality.charAt(param)?(param+1):-1;
else if (c!='0'&&c!='.'&&c!=' ')
param=-1;
}
}
break;
default:
throw new IllegalStateException();
}
}
return param!=__zeroquality.length() && match==search.length();
}
@Override
public String toString()
{
@ -72,7 +363,7 @@ public class HttpField
return getName() + ": " + (v==null?"":v);
}
public boolean isSame(HttpField field)
public boolean isSameName(HttpField field)
{
if (field==null)
return false;
@ -85,5 +376,66 @@ public class HttpField
return false;
}
private int nameHashCode()
{
int hash=13;
int len = _name.length();
for (int i = 0; i < len; i++)
{
char c = Character.toUpperCase(_name.charAt(i));
hash = 31 * hash + c;
}
return hash;
}
@Override
public int hashCode()
{
if (_header==null)
return _value.hashCode() ^ nameHashCode();
return _value.hashCode() ^ _header.hashCode();
}
@Override
public boolean equals(Object o)
{
if (o==this)
return true;
if (!(o instanceof HttpField))
return false;
HttpField field=(HttpField)o;
if (_header!=field.getHeader())
return false;
if (!_name.equalsIgnoreCase(field.getName()))
return false;
if (_value==null && field.getValue()!=null)
return false;
if (!_value.equals(field.getValue()))
return false;
return true;
}
public static class LongValueHttpField extends HttpField
{
final long _long;
public LongValueHttpField(HttpHeader header, long value)
{
super(header,Long.toString(value));
_long=value;
}
public LongValueHttpField(HttpHeader header, String value)
{
super(header,value);
_long=StringUtil.toLong(value);
}
@Override
public long getLongValue()
{
return _long;
}
}
}

View File

@ -30,7 +30,6 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.LazyList;
@ -52,17 +51,34 @@ import org.eclipse.jetty.util.log.Logger;
*/
public class HttpFields implements Iterable<HttpField>
{
public static final String __separators = ", \t";
private static final Logger LOG = Log.getLogger(HttpFields.class);
private final static Pattern __splitter = Pattern.compile("\\s*,\\s*");
public final static String __separators = ", \t";
private final ArrayList<HttpField> _fields = new ArrayList<>(20);
private final List<HttpField> _fields;
public HttpFields()
{
_fields=new ArrayList<>();
}
/**
* Constructor.
*/
public HttpFields()
public HttpFields(int capacity)
{
_fields=new ArrayList<>(capacity);
}
public int size()
{
return _fields.size();
}
@Override
public Iterator<HttpField> iterator()
{
return _fields.iterator();
}
/**
@ -71,7 +87,7 @@ public class HttpFields implements Iterable<HttpField>
public Collection<String> getFieldNamesCollection()
{
final Set<String> list = new HashSet<>(_fields.size());
for (HttpField f : _fields)
for (HttpField f : this)
{
if (f!=null)
list.add(f.getName());
@ -88,11 +104,6 @@ public class HttpFields implements Iterable<HttpField>
return Collections.enumeration(getFieldNamesCollection());
}
public int size()
{
return _fields.size();
}
/**
* Get a Field by index.
* @return A Field value or null if the Field value has not been set
@ -103,12 +114,6 @@ public class HttpFields implements Iterable<HttpField>
return _fields.get(i);
}
@Override
public Iterator<HttpField> iterator()
{
return _fields.iterator();
}
public HttpField getField(HttpHeader header)
{
for (int i=0;i<_fields.size();i++)
@ -130,13 +135,24 @@ public class HttpFields implements Iterable<HttpField>
}
return null;
}
public boolean contains(HttpHeader header, String value)
public boolean contains(HttpField field)
{
for (int i=0;i<_fields.size();i++)
for (int i=_fields.size();i-->0;)
{
HttpField f=_fields.get(i);
if (f.getHeader()==header && contains(f,value))
if (f.isSameName(field) && f.contains(field.getValue()))
return true;
}
return false;
}
public boolean contains(HttpHeader header, String value)
{
for (int i=_fields.size();i-->0;)
{
HttpField f=_fields.get(i);
if (f.getHeader()==header && f.contains(value))
return true;
}
return false;
@ -144,37 +160,19 @@ public class HttpFields implements Iterable<HttpField>
public boolean contains(String name, String value)
{
for (int i=0;i<_fields.size();i++)
for (int i=_fields.size();i-->0;)
{
HttpField f=_fields.get(i);
if (f.getName().equalsIgnoreCase(name) && contains(f,value))
if (f.getName().equalsIgnoreCase(name) && f.contains(value))
return true;
}
return false;
}
private boolean contains(HttpField field,String value)
{
String v = field.getValue();
if (v==null)
return false;
if (value.equalsIgnoreCase(v))
return true;
String[] split = __splitter.split(v);
for (int i = 0; split!=null && i < split.length; i++)
{
if (value.equals(split[i]))
return true;
}
return false;
}
public boolean containsKey(String name)
{
for (int i=0;i<_fields.size();i++)
for (int i=_fields.size();i-->0;)
{
HttpField f=_fields.get(i);
if (f.getName().equalsIgnoreCase(name))
@ -218,7 +216,7 @@ public class HttpFields implements Iterable<HttpField>
public List<String> getValuesList(String name)
{
final List<String> list = new ArrayList<>();
for (HttpField f : _fields)
for (HttpField f : this)
if (f.getName().equalsIgnoreCase(name))
list.add(f.getValue());
return list;
@ -333,7 +331,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i=_fields.size();i-->0;)
{
HttpField f=_fields.get(i);
if (f.isSame(field))
if (f.isSameName(field))
{
if (put)
_fields.remove(i);
@ -476,7 +474,7 @@ public class HttpFields implements Iterable<HttpField>
public long getLongField(String name) throws NumberFormatException
{
HttpField field = getField(name);
return field==null?-1L:StringUtil.toLong(field.getValue());
return field==null?-1L:field.getLongValue();
}
/**
@ -564,13 +562,43 @@ public class HttpFields implements Iterable<HttpField>
}
@Override
public String
toString()
public int hashCode()
{
int hash=0;
for (HttpField field:_fields)
hash+=field.hashCode();
return hash;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof HttpFields))
return false;
HttpFields that = (HttpFields)o;
// Order is not important, so we cannot rely on List.equals().
if (size() != that.size())
return false;
for (HttpField field : this)
{
if (!that.contains(field))
return false;
}
return true;
}
@Override
public String toString()
{
try
{
StringBuilder buffer = new StringBuilder();
for (HttpField field : _fields)
for (HttpField field : this)
{
if (field != null)
{
@ -592,21 +620,21 @@ public class HttpFields implements Iterable<HttpField>
}
}
/**
* Clear the header.
*/
public void clear()
{
_fields.clear();
}
public void add(HttpField field)
{
_fields.add(field);
}
public void addAll(HttpFields fields)
{
_fields.addAll(fields._fields);
}
/**
* Add fields from another HttpFields instance. Single valued fields are replaced, while all
* others are added.

View File

@ -585,6 +585,10 @@ public class HttpGenerator
{
for (HttpField field : _info.getHttpFields())
{
String v = field.getValue();
if (v==null || v.length()==0)
continue; // rfc7230 does not allow no value
HttpHeader h = field.getHeader();
switch (h==null?HttpHeader.UNKNOWN:h)

View File

@ -29,10 +29,12 @@ package org.eclipse.jetty.http;
* <th>Enum</th>
* <th>Code</th>
* <th>Message</th>
* <th>
* <th>
* <a href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a></th>
* <th>
* <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1 Semantics and Content</a></th>
* <th>
* <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a></th>
* <a href="http://tools.ietf.org/html/rfc7238">RFC 7238 - HTTP/1.1 Permanent Redirect</a></th>
* <th>
* <a href="http://tools.ietf.org/html/rfc2518">RFC 2518 - WEBDAV</a></th>
* </tr>
@ -48,7 +50,7 @@ package org.eclipse.jetty.http;
* <td>Continue</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.1.1">Sec. 10.1.1</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.2.1">Sec. 6.2.1</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -57,7 +59,7 @@ package org.eclipse.jetty.http;
* <td>Switching Protocols</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.1.2">Sec. 10.1.2</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.2.2">Sec. 6.2.2</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -82,7 +84,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.2">Sec. 9.2</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.2.1">Sec. 10.2.1</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.3.1">Sec. 6.3.1</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -92,7 +94,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.2">Sec. 9.2</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.2.2">Sec. 10.2.2</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.3.2">Sec. 6.3.2</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -102,7 +104,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.2">Sec. 9.2</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.2.3">Sec. 10.2.3</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.3.3">Sec. 6.3.3</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -111,7 +113,7 @@ package org.eclipse.jetty.http;
* <td>Non Authoritative Information</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.2.4">Sec. 10.2.4</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.3.4">Sec. 6.3.4</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -121,7 +123,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.2">Sec. 9.2</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.2.5">Sec. 10.2.5</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.3.5">Sec. 6.3.5</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -130,7 +132,7 @@ package org.eclipse.jetty.http;
* <td>Reset Content</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.2.6">Sec. 10.2.6</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.3.6">Sec. 6.3.6</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -139,7 +141,7 @@ package org.eclipse.jetty.http;
* <td>Partial Content</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.2.7">Sec. 10.2.7</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.3.7">Sec. 6.3.7</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -175,7 +177,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.3">Sec. 9.3</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.3.1">Sec. 10.3.1</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.4.1">Sec. 6.4.1</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -185,7 +187,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.3">Sec. 9.3</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.3.2">Sec. 10.3.2</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.4.2">Sec. 6.4.2</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -203,7 +205,7 @@ package org.eclipse.jetty.http;
* <td>Found</td>
* <td>(was "<code>302 Moved Temporarily</code>")</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.3.3">Sec. 10.3.3</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.4.3">Sec. 6.4.3</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -212,7 +214,7 @@ package org.eclipse.jetty.http;
* <td>See Other</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.3.4">Sec. 10.3.4</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.4.4">Sec. 6.4.4</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -222,7 +224,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.3">Sec. 9.3</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.3.5">Sec. 10.3.5</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.4.5">Sec. 6.4.5</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -231,7 +233,7 @@ package org.eclipse.jetty.http;
* <td>Use Proxy</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.3.6">Sec. 10.3.6</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.4.6">Sec. 6.4.6</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -240,7 +242,7 @@ package org.eclipse.jetty.http;
* <td><em>(Unused)</em></td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.3.7">Sec. 10.3.7</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.4.7">Sec. 6.4.7</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -249,7 +251,17 @@ package org.eclipse.jetty.http;
* <td>Temporary Redirect</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.3.8">Sec. 10.3.8</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.4.8">Sec. 6.4.8</a></td>
* <td>&nbsp;</td>
* </tr>
*
* <tr>
* <td>{@link #PERMANENT_REDIRECT_308}</td>
* <td>307</td>
* <td>Permanent Redirect</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc7238">RFC7238</a></td>
* <td>&nbsp;</td>
* </tr>
*
@ -265,7 +277,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.1">Sec. 10.4.1</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.5.1">Sec. 6.5.1</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -275,7 +287,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.2">Sec. 10.4.2</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.5.2">Sec. 6.5.2</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -285,7 +297,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.3">Sec. 10.4.3</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.5.3">Sec. 6.5.3</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -295,7 +307,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.4">Sec. 10.4.4</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.5.4">Sec. 6.5.4</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -305,7 +317,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.5">Sec. 10.4.5</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.5.5">Sec. 6.5.5</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -314,7 +326,7 @@ package org.eclipse.jetty.http;
* <td>Method Not Allowed</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.6">Sec. 10.4.6</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.5.6">Sec. 6.5.6</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -323,7 +335,7 @@ package org.eclipse.jetty.http;
* <td>Not Acceptable</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.7">Sec. 10.4.7</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.5.7">Sec. 6.5.7</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -332,7 +344,7 @@ package org.eclipse.jetty.http;
* <td>Proxy Authentication Required</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.8">Sec. 10.4.8</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.5.8">Sec. 6.5.8</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -341,7 +353,7 @@ package org.eclipse.jetty.http;
* <td>Request Timeout</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.9">Sec. 10.4.9</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.5.9">Sec. 6.5.9</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -350,7 +362,7 @@ package org.eclipse.jetty.http;
* <td>Conflict</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.10">Sec. 10.4.10</a>
* <a href="http://tools.ietf.org/html/rfc7231#section-10.4.10">Sec. 10.4.10</a>
* </td>
* <td>&nbsp;</td>
* </tr>
@ -360,7 +372,7 @@ package org.eclipse.jetty.http;
* <td>Gone</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.11">Sec. 10.4.11</a>
* <a href="http://tools.ietf.org/html/rfc7231#section-10.4.11">Sec. 10.4.11</a>
* </td>
* <td>&nbsp;</td>
* </tr>
@ -370,7 +382,7 @@ package org.eclipse.jetty.http;
* <td>Length Required</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.12">Sec. 10.4.12</a>
* <a href="http://tools.ietf.org/html/rfc7231#section-10.4.12">Sec. 10.4.12</a>
* </td>
* <td>&nbsp;</td>
* </tr>
@ -380,7 +392,7 @@ package org.eclipse.jetty.http;
* <td>Precondition Failed</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.13">Sec. 10.4.13</a>
* <a href="http://tools.ietf.org/html/rfc7231#section-10.4.13">Sec. 10.4.13</a>
* </td>
* <td>&nbsp;</td>
* </tr>
@ -390,7 +402,7 @@ package org.eclipse.jetty.http;
* <td>Request Entity Too Large</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.14">Sec. 10.4.14</a>
* <a href="http://tools.ietf.org/html/rfc7231#section-10.4.14">Sec. 10.4.14</a>
* </td>
* <td>&nbsp;</td>
* </tr>
@ -400,7 +412,7 @@ package org.eclipse.jetty.http;
* <td>Request-URI Too Long</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.15">Sec. 10.4.15</a>
* <a href="http://tools.ietf.org/html/rfc7231#section-10.4.15">Sec. 10.4.15</a>
* </td>
* <td>&nbsp;</td>
* </tr>
@ -410,7 +422,7 @@ package org.eclipse.jetty.http;
* <td>Unsupported Media Type</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.16">Sec. 10.4.16</a>
* <a href="http://tools.ietf.org/html/rfc7231#section-10.4.16">Sec. 10.4.16</a>
* </td>
* <td>&nbsp;</td>
* </tr>
@ -420,7 +432,7 @@ package org.eclipse.jetty.http;
* <td>Requested Range Not Satisfiable</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.17">Sec. 10.4.17</a>
* <a href="http://tools.ietf.org/html/rfc7231#section-10.4.17">Sec. 10.4.17</a>
* </td>
* <td>&nbsp;</td>
* </tr>
@ -430,7 +442,7 @@ package org.eclipse.jetty.http;
* <td>Expectation Failed</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.4.18">Sec. 10.4.18</a>
* <a href="http://tools.ietf.org/html/rfc7231#section-10.4.18">Sec. 10.4.18</a>
* </td>
* <td>&nbsp;</td>
* </tr>
@ -537,7 +549,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.5">Sec. 9.5</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.5.1">Sec. 10.5.1</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.6.1">Sec. 6.6.1</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -547,7 +559,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.5">Sec. 9.5</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.5.2">Sec. 10.5.2</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.6.2">Sec. 6.6.2</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -557,7 +569,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.5">Sec. 9.5</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.5.3">Sec. 10.5.3</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.6.3">Sec. 6.6.3</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -567,7 +579,7 @@ package org.eclipse.jetty.http;
* <td>
* <a href="http://tools.ietf.org/html/rfc1945#section-9.5">Sec. 9.5</a></td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.5.4">Sec. 10.5.4</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.6.4">Sec. 6.6.4</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -576,7 +588,7 @@ package org.eclipse.jetty.http;
* <td>Gateway Timeout</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.5.5">Sec. 10.5.5</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.6.5">Sec. 6.6.5</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -585,7 +597,7 @@ package org.eclipse.jetty.http;
* <td>HTTP Version Not Supported</td>
* <td>&nbsp;</td>
* <td>
* <a href="http://tools.ietf.org/html/rfc2616#section-10.5.6">Sec. 10.5.6</a></td>
* <a href="http://tools.ietf.org/html/rfc7231#section-6.6.6">Sec. 6.6.6</a></td>
* <td>&nbsp;</td>
* </tr>
* <tr>
@ -633,6 +645,7 @@ public class HttpStatus
public final static int NOT_MODIFIED_304 = 304;
public final static int USE_PROXY_305 = 305;
public final static int TEMPORARY_REDIRECT_307 = 307;
public final static int PERMANENT_REDIRECT_308 = 308;
public final static int BAD_REQUEST_400 = 400;
public final static int UNAUTHORIZED_401 = 401;
@ -683,7 +696,7 @@ public class HttpStatus
/*
* --------------------------------------------------------------------
* Informational messages in 1xx series. As defined by ... RFC 1945 -
* HTTP/1.0 RFC 2616 - HTTP/1.1 RFC 2518 - WebDAV
* HTTP/1.0 RFC 7231 - HTTP/1.1 RFC 2518 - WebDAV
*/
/** <code>100 Continue</code> */
@ -696,7 +709,7 @@ public class HttpStatus
/*
* --------------------------------------------------------------------
* Success messages in 2xx series. As defined by ... RFC 1945 - HTTP/1.0
* RFC 2616 - HTTP/1.1 RFC 2518 - WebDAV
* RFC 7231 - HTTP/1.1 RFC 2518 - WebDAV
*/
/** <code>200 OK</code> */
@ -719,7 +732,7 @@ public class HttpStatus
/*
* --------------------------------------------------------------------
* Redirection messages in 3xx series. As defined by ... RFC 1945 -
* HTTP/1.0 RFC 2616 - HTTP/1.1
* HTTP/1.0 RFC 7231 - HTTP/1.1
*/
/** <code>300 Mutliple Choices</code> */
@ -738,11 +751,13 @@ public class HttpStatus
USE_PROXY(USE_PROXY_305, "Use Proxy"),
/** <code>307 Temporary Redirect</code> */
TEMPORARY_REDIRECT(TEMPORARY_REDIRECT_307, "Temporary Redirect"),
/** <code>308 Permanent Redirect</code> */
PERMANET_REDIRECT(PERMANENT_REDIRECT_308, "Permanent Redirect"),
/*
* --------------------------------------------------------------------
* Client Error messages in 4xx series. As defined by ... RFC 1945 -
* HTTP/1.0 RFC 2616 - HTTP/1.1 RFC 2518 - WebDAV
* HTTP/1.0 RFC 7231 - HTTP/1.1 RFC 2518 - WebDAV
*/
/** <code>400 Bad Request</code> */
@ -791,7 +806,7 @@ public class HttpStatus
/*
* --------------------------------------------------------------------
* Server Error messages in 5xx series. As defined by ... RFC 1945 -
* HTTP/1.0 RFC 2616 - HTTP/1.1 RFC 2518 - WebDAV
* HTTP/1.0 RFC 7231 - HTTP/1.1 RFC 2518 - WebDAV
*/
/** <code>500 Server Error</code> */
@ -844,7 +859,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Informational</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
* and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
* and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
* HTTP/1.1</a>.
*
* @return true if within range of codes that belongs to
@ -859,7 +874,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Success</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
* and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
* and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
* HTTP/1.1</a>.
*
* @return true if within range of codes that belongs to
@ -874,7 +889,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Redirection</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
* and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
* and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
* HTTP/1.1</a>.
*
* @return true if within range of codes that belongs to
@ -889,7 +904,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Client Error</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
* and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
* and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
* HTTP/1.1</a>.
*
* @return true if within range of codes that belongs to
@ -904,7 +919,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Server Error</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
* and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
* and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
* HTTP/1.1</a>.
*
* @return true if within range of codes that belongs to
@ -958,7 +973,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Informational</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
* href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
* href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
*
* @param code
* the code to test.
@ -974,7 +989,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Success</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
* href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
* href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
*
* @param code
* the code to test.
@ -990,7 +1005,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Redirection</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
* href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
* href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
*
* @param code
* the code to test.
@ -1006,7 +1021,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Client Error</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
* href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
* href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
*
* @param code
* the code to test.
@ -1022,7 +1037,7 @@ public class HttpStatus
* Simple test against an code to determine if it falls into the
* <code>Server Error</code> message category as defined in the <a
* href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
* href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
* href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
*
* @param code
* the code to test.

View File

@ -73,7 +73,7 @@ public class HttpTester
}
public abstract static class Message extends HttpFields implements HttpParser.HttpHandler<ByteBuffer>
public abstract static class Message extends HttpFields implements HttpParser.HttpHandler
{
ByteArrayOutputStream _content;
HttpVersion _version=HttpVersion.HTTP_1_0;
@ -132,10 +132,9 @@ public class HttpTester
}
}
@Override
public boolean parsedHeader(HttpField field)
public void parsedHeader(HttpField field)
{
put(field.getName(),field.getValue());
return false;
}
@Override
@ -250,16 +249,16 @@ public class HttpTester
}
public static class Request extends Message implements HttpParser.RequestHandler<ByteBuffer>
public static class Request extends Message implements HttpParser.RequestHandler
{
private String _method;
private String _uri;
@Override
public boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion version)
public boolean startRequest(String method, String uri, HttpVersion version)
{
_method=methodString;
_uri=BufferUtil.toUTF8String(uri);
_method=method;
_uri=uri.toString();
_version=version;
return false;
}
@ -300,15 +299,9 @@ public class HttpTester
{
put(name,value);
}
@Override
public boolean parsedHostHeader(String host,int port)
{
return false;
}
}
public static class Response extends Message implements HttpParser.ResponseHandler<ByteBuffer>
public static class Response extends Message implements HttpParser.ResponseHandler
{
private int _status;
private String _reason;

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ public enum HttpVersion
HTTP_0_9("HTTP/0.9",9),
HTTP_1_0("HTTP/1.0",10),
HTTP_1_1("HTTP/1.1",11),
HTTP_2_0("HTTP/2.0",20);
HTTP_2("HTTP/2",20);
/* ------------------------------------------------------------ */
public final static Trie<HttpVersion> CACHE= new ArrayTrie<HttpVersion>();
@ -74,7 +74,7 @@ public enum HttpVersion
switch(bytes[position+7])
{
case '0':
return HTTP_2_0;
return HTTP_2;
}
break;
}

View File

@ -0,0 +1,316 @@
//
// ========================================================================
// 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.http;
import java.util.Iterator;
public class MetaData implements Iterable<HttpField>
{
private HttpVersion _httpVersion;
private HttpFields _fields;
/* ------------------------------------------------------------ */
public MetaData()
{
}
/* ------------------------------------------------------------ */
public MetaData(HttpVersion version, HttpFields fields)
{
_httpVersion = version;
_fields = fields;
}
/* ------------------------------------------------------------ */
public boolean isRequest()
{
return false;
}
/* ------------------------------------------------------------ */
public boolean isResponse()
{
return false;
}
/* ------------------------------------------------------------ */
/** Get the httpVersion.
* @return the httpVersion
*/
public HttpVersion getVersion()
{
return _httpVersion;
}
/* ------------------------------------------------------------ */
/** Set the httpVersion.
* @param httpVersion the httpVersion to set
*/
public void setHttpVersion(HttpVersion httpVersion)
{
_httpVersion = httpVersion;
}
/* ------------------------------------------------------------ */
/** Get the fields.
* @return the fields
*/
public HttpFields getFields()
{
return _fields;
}
/* ------------------------------------------------------------ */
/** Set the fields.
* @param fields the fields to set
*/
public void setFields(HttpFields fields)
{
_fields = fields;
}
/* ------------------------------------------------------------ */
public Iterator<HttpField> iterator()
{
return getFields().iterator();
}
/* ------------------------------------------------------------ */
@Override
public int hashCode()
{
return 31 * getVersion().hashCode() + getFields().hashCode();
}
/* ------------------------------------------------------------ */
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof MetaData))
return false;
MetaData that = (MetaData)o;
if (getVersion() != that.getVersion())
return false;
return getFields().equals(that.getFields());
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
StringBuilder out = new StringBuilder();
for (HttpField field: this)
out.append(field).append(System.lineSeparator());
return out.toString();
}
/* -------------------------------------------------------- */
/* -------------------------------------------------------- */
/* -------------------------------------------------------- */
public static class Request extends MetaData
{
private String _method;
private HttpURI _uri;
public Request()
{
}
/* ------------------------------------------------------------ */
/**
* @param method
* @param uri
* @param version
* @param fields
*/
public Request(String method, HttpURI uri, HttpVersion version, HttpFields fields)
{
super(version,fields);
_method = method;
_uri = uri;
}
/* ------------------------------------------------------------ */
public Request(String method, HttpScheme scheme, HostPortHttpField hostPort, String uri, HttpVersion version, HttpFields fields)
{
this(method,new HttpURI(scheme==null?null:scheme.asString(),hostPort.getHost(),hostPort.getPort(),uri),version,fields);
}
/* ------------------------------------------------------------ */
public Request(String method, String scheme, HostPortHttpField hostPort, String uri, HttpVersion version, HttpFields fields)
{
this(method,new HttpURI(scheme,hostPort.getHost(),hostPort.getPort(),uri),version,fields);
}
/* ------------------------------------------------------------ */
@Override
public boolean isRequest()
{
return true;
}
/* ------------------------------------------------------------ */
/** Get the method.
* @return the method
*/
public String getMethod()
{
return _method;
}
/* ------------------------------------------------------------ */
/** Set the method.
* @param method the method to set
*/
public void setMethod(String method)
{
_method = method;
}
/* ------------------------------------------------------------ */
/** Get the uri.
* @return the uri
*/
public HttpURI getURI()
{
return _uri;
}
/* ------------------------------------------------------------ */
/** Set the uri.
* @param uri the uri to set
*/
public void setURI(HttpURI uri)
{
_uri = uri;
}
/* ------------------------------------------------------------ */
@Override
public int hashCode()
{
return ((super.hashCode()*31)+_method.hashCode())*31+_uri.hashCode();
}
/* ------------------------------------------------------------ */
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof MetaData.Request))
return false;
MetaData.Request that = (MetaData.Request)o;
if (!getMethod().equals(that.getMethod()) ||
!getURI().equals(that.getURI()))
return false;
return super.equals(o);
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return String.format("%s %s %s%s%s",
getMethod(), getURI(), getVersion(), System.lineSeparator(), super.toString());
}
}
/* -------------------------------------------------------- */
/* -------------------------------------------------------- */
/* -------------------------------------------------------- */
public static class Response extends MetaData
{
private int _status;
public Response()
{
}
/* ------------------------------------------------------------ */
/**
* @param version
* @param fields
* @param status
*/
public Response(HttpVersion version, int status, HttpFields fields)
{
super(version,fields);
_status=status;
}
/* ------------------------------------------------------------ */
@Override
public boolean isResponse()
{
return true;
}
/* ------------------------------------------------------------ */
/** Get the status.
* @return the status
*/
public int getStatus()
{
return _status;
}
/* ------------------------------------------------------------ */
/** Set the status.
* @param status the status to set
*/
public void setStatus(int status)
{
_status = status;
}
/* ------------------------------------------------------------ */
@Override
public int hashCode()
{
return 31 * getStatus() + super.hashCode();
}
/* ------------------------------------------------------------ */
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof MetaData.Response))
return false;
MetaData.Response that = (MetaData.Response)o;
if (getStatus() != that.getStatus())
return false;
return super.equals(o);
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return String.format("%s %d%s%s",getVersion(), getStatus(), System.lineSeparator(), super.toString());
}
}
}

View File

@ -56,20 +56,20 @@ public class MimeTypes
TEXT_JSON("text/json",StandardCharsets.UTF_8),
APPLICATION_JSON("application/json",StandardCharsets.UTF_8),
TEXT_HTML_8859_1("text/html; charset=ISO-8859-1",TEXT_HTML),
TEXT_HTML_UTF_8("text/html; charset=UTF-8",TEXT_HTML),
TEXT_HTML_8859_1("text/html;charset=iso-8859-1",TEXT_HTML),
TEXT_HTML_UTF_8("text/html;charset=utf-8",TEXT_HTML),
TEXT_PLAIN_8859_1("text/plain; charset=ISO-8859-1",TEXT_PLAIN),
TEXT_PLAIN_UTF_8("text/plain; charset=UTF-8",TEXT_PLAIN),
TEXT_PLAIN_8859_1("text/plain;charset=iso-8859-1",TEXT_PLAIN),
TEXT_PLAIN_UTF_8("text/plain;charset=utf-8",TEXT_PLAIN),
TEXT_XML_8859_1("text/xml; charset=ISO-8859-1",TEXT_XML),
TEXT_XML_UTF_8("text/xml; charset=UTF-8",TEXT_XML),
TEXT_XML_8859_1("text/xml;charset=iso-8859-1",TEXT_XML),
TEXT_XML_UTF_8("text/xml;charset=utf-8",TEXT_XML),
TEXT_JSON_8859_1("text/json; charset=ISO-8859-1",TEXT_JSON),
TEXT_JSON_UTF_8("text/json; charset=UTF-8",TEXT_JSON),
TEXT_JSON_8859_1("text/json;charset=iso-8859-1",TEXT_JSON),
TEXT_JSON_UTF_8("text/json;charset=utf-8",TEXT_JSON),
APPLICATION_JSON_8859_1("text/json; charset=ISO-8859-1",APPLICATION_JSON),
APPLICATION_JSON_UTF_8("text/json; charset=UTF-8",APPLICATION_JSON);
APPLICATION_JSON_8859_1("text/json;charset=iso-8859-1",APPLICATION_JSON),
APPLICATION_JSON_UTF_8("text/json;charset=utf-8",APPLICATION_JSON);
/* ------------------------------------------------------------ */
@ -77,6 +77,7 @@ public class MimeTypes
private final Type _base;
private final ByteBuffer _buffer;
private final Charset _charset;
private final String _charsetString;
private final boolean _assumedCharset;
private final HttpField _field;
@ -87,6 +88,7 @@ public class MimeTypes
_buffer=BufferUtil.toBuffer(s);
_base=this;
_charset=null;
_charsetString=null;
_assumedCharset=false;
_field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string);
}
@ -97,8 +99,9 @@ public class MimeTypes
_string=s;
_buffer=BufferUtil.toBuffer(s);
_base=base;
int i=s.indexOf("; charset=");
_charset=Charset.forName(s.substring(i+10));
int i=s.indexOf(";charset=");
_charset=Charset.forName(s.substring(i+9));
_charsetString=_charset==null?null:_charset.toString().toLowerCase();
_assumedCharset=false;
_field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string);
}
@ -110,6 +113,7 @@ public class MimeTypes
_base=this;
_buffer=BufferUtil.toBuffer(s);
_charset=cs;
_charsetString=_charset==null?null:_charset.toString().toLowerCase();
_assumedCharset=true;
_field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string);
}
@ -126,6 +130,12 @@ public class MimeTypes
return _charset;
}
/* ------------------------------------------------------------ */
public String getCharsetString()
{
return _charsetString;
}
/* ------------------------------------------------------------ */
public boolean is(String s)
{
@ -181,8 +191,9 @@ public class MimeTypes
int charset=type.toString().indexOf(";charset=");
if (charset>0)
{
CACHE.put(type.toString().replace(";charset=","; charset="),type);
TYPES.put(type.toString().replace(";charset=","; charset="),type.asBuffer());
String alt=type.toString().replace(";charset=","; charset=");
CACHE.put(alt,type);
TYPES.put(alt,type.asBuffer());
}
}

View File

@ -1,4 +1,4 @@
text/html = ISO-8859-1
text/plain = ISO-8859-1
text/xml = UTF-8
text/json = UTF-8
text/html = iso-8859-1
text/plain = iso-8859-1
text/xml = utf-8
text/json = utf-8

View File

@ -0,0 +1,138 @@
//
// ========================================================================
// 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.http;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/**
*
*/
public class HttpFieldTest
{
@Test
public void testContainsSimple() throws Exception
{
HttpField field = new HttpField("name","somevalue");
assertTrue(field.contains("somevalue"));
assertFalse(field.contains("other"));
assertFalse(field.contains("some"));
assertFalse(field.contains("value"));
assertFalse(field.contains("v"));
assertFalse(field.contains(""));
assertFalse(field.contains(null));
}
@Test
public void testContainsList() throws Exception
{
HttpField field = new HttpField("name",",aaa,bbb,ccc, ddd , e e, \"\\\"f,f\\\"\", ");
assertTrue(field.contains("aaa"));
assertTrue(field.contains("bbb"));
assertTrue(field.contains("ccc"));
assertTrue(field.contains("ddd"));
assertTrue(field.contains("e e"));
assertTrue(field.contains("\"f,f\""));
assertFalse(field.contains(""));
assertFalse(field.contains("aa"));
assertFalse(field.contains("bb"));
assertFalse(field.contains("cc"));
assertFalse(field.contains(null));
}
@Test
public void testQualityContainsList() throws Exception
{
HttpField field;
field = new HttpField("name","yes");
assertTrue(field.contains("yes"));
assertFalse(field.contains("no"));
field = new HttpField("name",",yes,");
assertTrue(field.contains("yes"));
assertFalse(field.contains("no"));
field = new HttpField("name","other,yes,other");
assertTrue(field.contains("yes"));
assertFalse(field.contains("no"));
field = new HttpField("name","other, yes ,other");
assertTrue(field.contains("yes"));
assertFalse(field.contains("no"));
field = new HttpField("name","other, y s ,other");
assertTrue(field.contains("y s"));
assertFalse(field.contains("no"));
field = new HttpField("name","other, \"yes\" ,other");
assertTrue(field.contains("yes"));
assertFalse(field.contains("no"));
field = new HttpField("name","other, \"\\\"yes\\\"\" ,other");
assertTrue(field.contains("\"yes\""));
assertFalse(field.contains("no"));
field = new HttpField("name",";no,yes,;no");
assertTrue(field.contains("yes"));
assertFalse(field.contains("no"));
field = new HttpField("name","no;q=0,yes;q=1,no; q = 0");
assertTrue(field.contains("yes"));
assertFalse(field.contains("no"));
field = new HttpField("name","no;q=0.0000,yes;q=0.0001,no; q = 0.00000");
assertTrue(field.contains("yes"));
assertFalse(field.contains("no"));
}
@Test
public void testValues()
{
String[] values = new HttpField("name","value").getValues();
assertEquals(1,values.length);
assertEquals("value",values[0]);
values = new HttpField("name","a,b,c").getValues();
assertEquals(3,values.length);
assertEquals("a",values[0]);
assertEquals("b",values[1]);
assertEquals("c",values[2]);
values = new HttpField("name","a,\"x,y,z\",c").getValues();
assertEquals(3,values.length);
assertEquals("a",values[0]);
assertEquals("x,y,z",values[1]);
assertEquals("c",values[2]);
values = new HttpField("name","a,\"x,\\\"p,q\\\",z\",c").getValues();
assertEquals(3,values.length);
assertEquals("a",values[0]);
assertEquals("x,\"p,q\",z",values[1]);
assertEquals("c",values[2]);
}
}

View File

@ -27,6 +27,9 @@ import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.eclipse.jetty.util.BufferUtil;
import org.hamcrest.Matchers;
import org.junit.Assert;
@ -416,20 +419,22 @@ public class HttpFieldsTest
{
HttpFields header = new HttpFields();
header.add("0", "");
header.add("1", ",");
header.add("2", ",,");
header.add("3", "abc");
header.add("4", "def");
header.add("5", "abc,def,hig");
header.add("6", "abc");
header.add("6", "def");
header.add("6", "hig");
header.add("n0", "");
header.add("n1", ",");
header.add("n2", ",,");
header.add("N3", "abc");
header.add("N4", "def");
header.add("n5", "abc,def,hig");
header.add("N6", "abc");
header.add("n6", "def");
header.add("N6", "hig");
header.add("n7", "abc , def;q=0.9 , hig");
for (int i=0;i<7;i++)
for (int i=0;i<8;i++)
{
assertFalse(""+i,header.contains(""+i,"xyz"));
assertEquals(""+i,i>=4,header.contains(""+i,"def"));
assertFalse(""+i,header.contains("n"+i,"xyz"));
assertEquals(""+i,i>=4,header.contains("n"+i,"def"));
}
}
}

View File

@ -18,6 +18,13 @@
package org.eclipse.jetty.http;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
@ -31,13 +38,6 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@RunWith(Parameterized.class)
public class HttpGeneratorServerHTTPTest
{
@ -216,7 +216,7 @@ public class HttpGeneratorServerHTTPTest
}
}
private class Handler implements HttpParser.ResponseHandler<ByteBuffer>
private class Handler implements HttpParser.ResponseHandler
{
@Override
public boolean content(ByteBuffer ref)
@ -247,9 +247,8 @@ public class HttpGeneratorServerHTTPTest
}
@Override
public boolean parsedHeader(HttpField field)
public void parsedHeader(HttpField field)
{
return false;
}
@Override

View File

@ -18,6 +18,12 @@
package org.eclipse.jetty.http;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
@ -25,12 +31,6 @@ import org.eclipse.jetty.util.BufferUtil;
import org.junit.Assert;
import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class HttpGeneratorServerTest
{
@Test

View File

@ -18,7 +18,10 @@
package org.eclipse.jetty.http;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@ -68,7 +71,7 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /foo HTTP/1.0\015\012" + "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
@ -83,13 +86,11 @@ public class HttpParserTest
ByteBuffer buffer= BufferUtil.toBuffer("GET /999\015\012");
_versionOrReason= null;
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("GET", _methodOrVersion);
assertEquals("/999", _uriOrStatus);
assertEquals(null, _versionOrReason);
assertEquals(-1, _headers);
assertEquals("HTTP/0.9 not supported", _bad);
}
@Test
@ -98,13 +99,10 @@ public class HttpParserTest
ByteBuffer buffer= BufferUtil.toBuffer("POST /222 \015\012");
_versionOrReason= null;
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/222", _uriOrStatus);
assertEquals(null, _versionOrReason);
assertEquals(-1, _headers);
assertEquals("HTTP/0.9 not supported", _bad);
}
@Test
@ -112,7 +110,7 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /fo\u0690 HTTP/1.0\015\012" + "\015\012",StandardCharsets.UTF_8);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
@ -126,7 +124,7 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /foo?param=\u0690 HTTP/1.0\015\012" + "\015\012",StandardCharsets.UTF_8);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
@ -140,7 +138,7 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/ HTTP/1.0\015\012" + "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
@ -153,7 +151,7 @@ public class HttpParserTest
public void testConnect() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer("CONNECT 192.168.1.2:80 HTTP/1.1\015\012" + "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("CONNECT", _methodOrVersion);
@ -171,7 +169,7 @@ public class HttpParserTest
"Connection: close\015\012" +
"\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
@ -186,6 +184,73 @@ public class HttpParserTest
assertEquals("close", _val[1]);
assertEquals(1, _headers);
}
@Test
public void test7230NoContinuations() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Name: value\015\012" +
" extra\015\012" +
"\015\012");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
Assert.assertThat(_bad,Matchers.notNullValue());
Assert.assertThat(_bad,Matchers.containsString("Bad Continuation"));
}
@Test
public void test7230NoWhiteSpaceInName() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
" Name: value\015\012" +
"\015\012");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
Assert.assertThat(_bad,Matchers.notNullValue());
Assert.assertThat(_bad,Matchers.containsString("Bad"));
init();
buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"N ame: value\015\012" +
"\015\012");
handler = new Handler();
parser= new HttpParser(handler);
parseAll(parser,buffer);
Assert.assertThat(_bad,Matchers.containsString("Illegal character"));
init();
buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Name : value\015\012" +
"\015\012");
handler = new Handler();
parser= new HttpParser(handler);
parseAll(parser,buffer);
Assert.assertThat(_bad,Matchers.containsString("Illegal character"));
}
@Test
public void testHeaderParseDirect() throws Exception
@ -194,13 +259,11 @@ public class HttpParserTest
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Header1: value1\015\012" +
"Header 2 : value 2a \015\012" +
" value 2b \015\012" +
"Header3: \015\012" +
"Header4 \015\012" +
" value4\015\012" +
"Server5 : notServer\015\012" +
"Host Header: notHost\015\012" +
"Header2: value 2a \015\012" +
"Header3: 3\015\012" +
"Header4:value4\015\012" +
"Server5: notServer\015\012" +
"HostHeader: notHost\015\012" +
"Connection: close\015\012" +
"Accept-Encoding: gzip, deflated\015\012" +
"Accept: unknown\015\012" +
@ -210,7 +273,7 @@ public class HttpParserTest
BufferUtil.put(b0,buffer);
BufferUtil.flipToFlush(buffer,pos);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
@ -221,15 +284,15 @@ public class HttpParserTest
assertEquals("localhost", _val[0]);
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
assertEquals("Header 2", _hdr[2]);
assertEquals("value 2a value 2b", _val[2]);
assertEquals("Header2", _hdr[2]);
assertEquals("value 2a", _val[2]);
assertEquals("Header3", _hdr[3]);
assertEquals(null, _val[3]);
assertEquals("3", _val[3]);
assertEquals("Header4", _hdr[4]);
assertEquals("value4", _val[4]);
assertEquals("Server5", _hdr[5]);
assertEquals("notServer", _val[5]);
assertEquals("Host Header", _hdr[6]);
assertEquals("HostHeader", _hdr[6]);
assertEquals("notHost", _val[6]);
assertEquals("Connection", _hdr[7]);
assertEquals("close", _val[7]);
@ -247,18 +310,16 @@ public class HttpParserTest
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Header1: value1\015\012" +
"Header 2 : value 2a \015\012" +
" value 2b \015\012" +
"Header3: \015\012" +
"Header4 \015\012" +
" value4\015\012" +
"Server5 : notServer\015\012" +
"Host Header: notHost\015\012" +
"Header2: value 2a \015\012" +
"Header3: 3\015\012" +
"Header4:value4\015\012" +
"Server5: notServer\015\012" +
"HostHeader: notHost\015\012" +
"Connection: close\015\012" +
"Accept-Encoding: gzip, deflated\015\012" +
"Accept: unknown\015\012" +
"\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
@ -269,15 +330,15 @@ public class HttpParserTest
assertEquals("localhost", _val[0]);
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
assertEquals("Header 2", _hdr[2]);
assertEquals("value 2a value 2b", _val[2]);
assertEquals("Header2", _hdr[2]);
assertEquals("value 2a", _val[2]);
assertEquals("Header3", _hdr[3]);
assertEquals(null, _val[3]);
assertEquals("3", _val[3]);
assertEquals("Header4", _hdr[4]);
assertEquals("value4", _val[4]);
assertEquals("Server5", _hdr[5]);
assertEquals("notServer", _val[5]);
assertEquals("Host Header", _hdr[6]);
assertEquals("HostHeader", _hdr[6]);
assertEquals("notHost", _val[6]);
assertEquals("Connection", _hdr[7]);
assertEquals("close", _val[7]);
@ -297,18 +358,16 @@ public class HttpParserTest
"GET / HTTP/1.0\n" +
"Host: localhost\n" +
"Header1: value1\n" +
"Header 2 : value 2a \n" +
" value 2b \n" +
"Header3: \n" +
"Header4 \n" +
" value4\n" +
"Server5 : notServer\n" +
"Host Header: notHost\n" +
"Header2: value 2a value 2b \n" +
"Header3: 3\n" +
"Header4:value4\n" +
"Server5: notServer\n" +
"HostHeader: notHost\n" +
"Connection: close\n" +
"Accept-Encoding: gzip, deflated\n" +
"Accept: unknown\n" +
"\n");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
@ -319,15 +378,15 @@ public class HttpParserTest
assertEquals("localhost", _val[0]);
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
assertEquals("Header 2", _hdr[2]);
assertEquals("Header2", _hdr[2]);
assertEquals("value 2a value 2b", _val[2]);
assertEquals("Header3", _hdr[3]);
assertEquals(null, _val[3]);
assertEquals("3", _val[3]);
assertEquals("Header4", _hdr[4]);
assertEquals("value4", _val[4]);
assertEquals("Server5", _hdr[5]);
assertEquals("notServer", _val[5]);
assertEquals("Host Header", _hdr[6]);
assertEquals("HostHeader", _hdr[6]);
assertEquals("notHost", _val[6]);
assertEquals("Connection", _hdr[7]);
assertEquals("close", _val[7]);
@ -347,7 +406,7 @@ public class HttpParserTest
"Name1: \"value\t1\"\n" +
"Name2: \"value\t2A\",\"value,2B\"\t\n" +
"\n");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
@ -376,7 +435,7 @@ public class HttpParserTest
BufferUtil.put(BufferUtil.toBuffer(" \r\n\r\n"),buffer);
BufferUtil.flipToFlush(buffer,0);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
@ -397,7 +456,7 @@ public class HttpParserTest
ByteBuffer buffer= BufferUtil.toBuffer(
"G\u00e6T / HTTP/1.0\r\nHeader0: value0\r\n\n\n");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertThat(_bad,Matchers.notNullValue());
@ -409,7 +468,7 @@ public class HttpParserTest
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / H\u00e6P/1.0\r\nHeader0: value0\r\n\n\n");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertThat(_bad,Matchers.notNullValue());
@ -422,7 +481,7 @@ public class HttpParserTest
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\r\nH\u00e6der0: value0\r\n\n\n");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertThat(_bad,Matchers.notNullValue());
@ -437,7 +496,7 @@ public class HttpParserTest
"Header: value\talternate\r\n" +
"\n\n");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
@ -458,7 +517,7 @@ public class HttpParserTest
"HOST: localhost\015\012" +
"cOnNeCtIoN: ClOsE\015\012"+
"\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler,-1,false);
parseAll(parser,buffer);
@ -480,7 +539,7 @@ public class HttpParserTest
"HOST: localhost\015\012" +
"cOnNeCtIoN: ClOsE\015\012"+
"\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler,-1,true);
parseAll(parser,buffer);
@ -501,11 +560,9 @@ public class HttpParserTest
"XXXXSPLIT / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Header1: value1\015\012" +
"Header2 : value 2a \015\012" +
" value 2b \015\012" +
"Header3: \015\012" +
"Header4 \015\012" +
" value4\015\012" +
"Header2: value 2a \015\012" +
"Header3: 3\015\012" +
"Header4:value4\015\012" +
"Server5: notServer\015\012" +
"\015\012ZZZZ");
buffer.position(2);
@ -514,7 +571,7 @@ public class HttpParserTest
for (int i=0;i<buffer.capacity()-4;i++)
{
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
// System.err.println(BufferUtil.toDetailString(buffer));
@ -539,9 +596,9 @@ public class HttpParserTest
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
assertEquals("Header2", _hdr[2]);
assertEquals("value 2a value 2b", _val[2]);
assertEquals("value 2a", _val[2]);
assertEquals("Header3", _hdr[3]);
assertEquals(null, _val[3]);
assertEquals("3", _val[3]);
assertEquals("Header4", _hdr[4]);
assertEquals("value4", _val[4]);
assertEquals("Server5", _hdr[5]);
@ -564,7 +621,7 @@ public class HttpParserTest
+ "1a\015\012"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
+ "0\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
@ -580,7 +637,7 @@ public class HttpParserTest
@Test
public void testStartEOF() throws Exception
{
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.atEOF();
parser.parseNext(BufferUtil.EMPTY_BUFFER);
@ -597,7 +654,7 @@ public class HttpParserTest
+ "Content-Length: 20\015\012"
+ "\015\012"
+ "0123456789");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.atEOF();
parseAll(parser,buffer);
@ -620,7 +677,7 @@ public class HttpParserTest
+ "\015\012"
+ "a;\015\012"
+ "0123456789\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.atEOF();
parseAll(parser,buffer);
@ -668,7 +725,7 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("GET", _methodOrVersion);
@ -736,7 +793,7 @@ public class HttpParserTest
+ "0123456789\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer0);
parser.atEOF();
@ -782,7 +839,7 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
@ -801,7 +858,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
@ -825,7 +882,7 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
@ -858,7 +915,7 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
@ -879,7 +936,7 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
@ -899,7 +956,7 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.atEOF();
parser.parseNext(buffer);
@ -921,7 +978,7 @@ public class HttpParserTest
+ "Content-Length: 10\015\012"
+ "\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
@ -940,7 +997,7 @@ public class HttpParserTest
+ "Transfer-Encoding: chunked\015\012"
+ "\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
@ -963,7 +1020,7 @@ public class HttpParserTest
+ "HTTP/1.1 400 OK\015\012"); // extra data causes close ??
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -991,7 +1048,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1011,7 +1068,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1030,7 +1087,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1049,7 +1106,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1068,7 +1125,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser.ResponseHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1087,7 +1144,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1121,7 +1178,7 @@ public class HttpParserTest
+ "Connection: close\r"
+ "\r");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1157,7 +1214,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1176,7 +1233,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1195,7 +1252,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
@ -1214,7 +1271,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("host",_host);
@ -1229,7 +1286,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("No Host",_bad);
@ -1244,7 +1301,7 @@ public class HttpParserTest
"GET http://host/ HTTP/1.0\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
Assert.assertNull(_bad);
@ -1260,7 +1317,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("No Host",_bad);
@ -1275,7 +1332,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("192.168.0.1",_host);
@ -1291,7 +1348,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("[::1]",_host);
@ -1307,10 +1364,10 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("Bad IPv6 Host header",_bad);
Assert.assertThat(_bad,Matchers.containsString("Bad"));
}
@Test
@ -1322,7 +1379,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("myhost",_host);
@ -1334,14 +1391,14 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: myhost:xxx\015\012"
+ "Host: myhost:testBadPort\015\012"
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("Bad Host header",_bad);
Assert.assertThat(_bad,Matchers.containsString("Bad Host"));
}
@Test
@ -1353,7 +1410,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("192.168.0.1",_host);
@ -1369,7 +1426,7 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("[::1]",_host);
@ -1384,7 +1441,7 @@ public class HttpParserTest
"Host: www.smh.com.au\r\n"+
"\r\n");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("www.smh.com.au",parser.getFieldCache().get("Host: www.smh.com.au").getValue());
@ -1486,7 +1543,7 @@ public class HttpParserTest
private boolean _headerCompleted;
private boolean _messageCompleted;
private class Handler implements HttpParser.RequestHandler<ByteBuffer>, HttpParser.ResponseHandler<ByteBuffer>, HttpParser.ProxyHandler
private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler, HttpParser.ProxyHandler
{
private HttpFields fields;
String _proxy;
@ -1504,14 +1561,14 @@ public class HttpParserTest
}
@Override
public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
public boolean startRequest(String method, String uri, HttpVersion version)
{
_fields.clear();
_headers= -1;
_hdr= new String[10];
_val= new String[10];
_methodOrVersion= method;
_uriOrStatus= BufferUtil.toUTF8String(uri);
_uriOrStatus= uri.toString();
_versionOrReason= version==null?null:version.asString();
fields=new HttpFields();
@ -1522,21 +1579,19 @@ public class HttpParserTest
}
@Override
public boolean parsedHeader(HttpField field)
public void parsedHeader(HttpField field)
{
_fields.add(field);
//System.err.println("header "+name+": "+value);
_hdr[++_headers]= field.getName();
_val[_headers]= field.getValue();
return false;
}
@Override
public boolean parsedHostHeader(String host,int port)
{
_host=host;
_port=port;
return false;
if (field instanceof HostPortHttpField)
{
HostPortHttpField hpfield = (HostPortHttpField)field;
_host=hpfield.getHost();
_port=hpfield.getPort();
}
}
@Override

View File

@ -20,9 +20,18 @@
package org.eclipse.jetty.http;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.Utf8Appendable;
import org.junit.Assert;
import org.junit.Test;
@ -32,10 +41,10 @@ public class HttpURITest
String[][] tests=
{
{"/path/to/context",null,null,"-1","/path/to/context",null,null,null},
{"http://example.com/path/to/context;param?query=%22value%22#fragment","http","example.com","-1","/path/to/context","param","query=%22value%22","fragment"},
{"http://[::1]/path/to/context;param?query=%22value%22#fragment","http","[::1]","-1","/path/to/context","param","query=%22value%22","fragment"},
{"http://example.com:8080/path/to/context;param?query=%22value%22#fragment","http","example.com","8080","/path/to/context","param","query=%22value%22","fragment"},
{"http://[::1]:8080/path/to/context;param?query=%22value%22#fragment","http","[::1]","8080","/path/to/context","param","query=%22value%22","fragment"},
{"http://example.com/path/to/context;param?query=%22value%22#fragment","http","example.com","-1","/path/to/context;param","param","query=%22value%22","fragment"},
{"http://[::1]/path/to/context;param?query=%22value%22#fragment","http","[::1]","-1","/path/to/context;param","param","query=%22value%22","fragment"},
{"http://example.com:8080/path/to/context;param?query=%22value%22#fragment","http","example.com","8080","/path/to/context;param","param","query=%22value%22","fragment"},
{"http://[::1]:8080/path/to/context;param?query=%22value%22#fragment","http","[::1]","8080","/path/to/context;param","param","query=%22value%22","fragment"},
};
public static int
@ -65,7 +74,8 @@ public class HttpURITest
{
for (String[] test:tests)
{
HttpURI uri = new HttpURI(new URI(test[INPUT]));
URI u=new URI(test[INPUT]);
HttpURI uri = new HttpURI(u);
assertEquals(test[SCHEME], uri.getScheme());
assertEquals(test[HOST], uri.getHost());
@ -74,6 +84,211 @@ public class HttpURITest
assertEquals(test[PARAM], uri.getParam());
assertEquals(test[QUERY], uri.getQuery());
assertEquals(test[FRAGMENT], uri.getFragment());
assertEquals(u,uri.toURI());
}
}
private final String[][] path_tests=
{
/* 0*/ {"/path/info",null,null,null,null,"/path/info",null,null,null},
/* 1*/ {"/path/info#fragment",null,null,null,null,"/path/info",null,null,"fragment"},
/* 2*/ {"/path/info?query",null,null,null,null,"/path/info",null,"query",null},
/* 3*/ {"/path/info?query#fragment",null,null,null,null,"/path/info",null,"query","fragment"},
/* 4*/ {"/path/info;param",null,null,null,null,"/path/info;param","param",null,null},
/* 5*/ {"/path/info;param#fragment",null,null,null,null,"/path/info;param","param",null,"fragment"},
/* 6*/ {"/path/info;param?query",null,null,null,null,"/path/info;param","param","query",null},
/* 7*/ {"/path/info;param?query#fragment",null,null,null,null,"/path/info;param","param","query","fragment"},
/* 8*/ {"//host/path/info",null,null,null,null,"//host/path/info",null,null,null},
/* 9*/ {"//user@host/path/info",null,null,null,null,"//user@host/path/info",null,null,null},
/*10*/ {"//user@host:8080/path/info",null,null,null,null,"//user@host:8080/path/info",null,null,null},
/*11*/ {"//host:8080/path/info",null,null,null,null,"//host:8080/path/info",null,null,null},
/*12*/ {"http:/path/info","http",null,null,null,"/path/info",null,null,null},
/*13*/ {"http:/path/info#fragment","http",null,null,null,"/path/info",null,null,"fragment"},
/*14*/ {"http:/path/info?query","http",null,null,null,"/path/info",null,"query",null},
/*15*/ {"http:/path/info?query#fragment","http",null,null,null,"/path/info",null,"query","fragment"},
/*16*/ {"http:/path/info;param","http",null,null,null,"/path/info;param","param",null,null},
/*17*/ {"http:/path/info;param#fragment","http",null,null,null,"/path/info;param","param",null,"fragment"},
/*18*/ {"http:/path/info;param?query","http",null,null,null,"/path/info;param","param","query",null},
/*19*/ {"http:/path/info;param?query#fragment","http",null,null,null,"/path/info;param","param","query","fragment"},
/*20*/ {"http://user@host:8080/path/info;param?query#fragment","http","//user@host:8080","host","8080","/path/info;param","param","query","fragment"},
/*21*/ {"xxxxx://user@host:8080/path/info;param?query#fragment","xxxxx","//user@host:8080","host","8080","/path/info;param","param","query","fragment"},
/*22*/ {"http:///;?#","http","//","",null,"/;","","",""},
/*23*/ {"/path/info?a=?query",null,null,null,null,"/path/info",null,"a=?query",null},
/*24*/ {"/path/info?a=;query",null,null,null,null,"/path/info",null,"a=;query",null},
/*25*/ {"//host:8080//",null,null,null,null,"//host:8080//",null,null,null},
/*26*/ {"file:///path/info","file","//","",null,"/path/info",null,null,null},
/*27*/ {"//",null,null,null,null,"//",null,null,null},
/*28*/ {"http://localhost/","http","//localhost","localhost",null,"/",null,null,null},
/*29*/ {"http://localhost:8080/", "http", "//localhost:8080", "localhost","8080","/", null, null,null},
/*30*/ {"http://localhost/?x=y", "http", "//localhost", "localhost",null,"/", null,"x=y",null},
/*31*/ {"/;param",null, null, null,null,"/;param", "param",null,null},
/*32*/ {"/?x=y",null, null, null,null,"/", null,"x=y",null},
/*33*/ {"/?abc=test",null, null, null,null,"/", null,"abc=test",null},
/*34*/ {"/#fragment",null, null, null,null,"/", null,null,"fragment"},
/*35*/ {"http://192.0.0.1:8080/","http","//192.0.0.1:8080","192.0.0.1","8080","/",null,null,null},
/*36*/ {"http://[2001:db8::1]:8080/","http","//[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
/*37*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
/*38*/ {"http://[2001:db8::1]/","http","//[2001:db8::1]","[2001:db8::1]",null,"/",null,null,null},
/*39*/ {"//[2001:db8::1]:8080/",null,null,null,null,"//[2001:db8::1]:8080/",null,null,null},
/*40*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
/*41*/ {"*",null,null,null,null,"*",null, null,null}
};
@Test
public void testPathURIs() throws Exception
{
HttpURI uri = new HttpURI();
for (int t=0;t<path_tests.length;t++)
{
uri.parse(path_tests[t][0]);
assertEquals(t+" "+path_tests[t][0],path_tests[t][1],uri.getScheme());
assertEquals(t+" "+path_tests[t][0],path_tests[t][3],uri.getHost());
assertEquals(t+" "+path_tests[t][0],path_tests[t][4]==null?-1:Integer.parseInt(path_tests[t][4]),uri.getPort());
assertEquals(t+" "+path_tests[t][0],path_tests[t][5],uri.getPath());
assertEquals(t+" "+path_tests[t][0],path_tests[t][6],uri.getParam());
assertEquals(t+" "+path_tests[t][0],path_tests[t][7],uri.getQuery());
assertEquals(t+" "+path_tests[t][0],path_tests[t][8],uri.getFragment());
assertEquals(path_tests[t][0], uri.toString());
}
}
@Test
public void testInvalidAddress() throws Exception
{
assertInvalidURI("http://[ffff::1:8080/", "Invalid URL; no closing ']' -- should throw exception");
assertInvalidURI("**", "only '*', not '**'");
assertInvalidURI("*/", "only '*', not '*/'");
}
private void assertInvalidURI(String invalidURI, String message)
{
HttpURI uri = new HttpURI();
try
{
uri.parse(invalidURI);
fail(message);
}
catch (IllegalArgumentException e)
{
assertTrue(true);
}
}
@Test
public void testUnicodeErrors() throws UnsupportedEncodingException
{
String uri="http://server/path?invalid=data%uXXXXhere%u000";
try
{
URLDecoder.decode(uri,"UTF-8");
Assert.assertTrue(false);
}
catch (IllegalArgumentException e)
{
}
HttpURI huri=new HttpURI(uri);
MultiMap<String> params = new MultiMap<>();
huri.decodeQueryTo(params);
assertEquals("data"+Utf8Appendable.REPLACEMENT+"here"+Utf8Appendable.REPLACEMENT,params.getValue("invalid",0));
huri=new HttpURI(uri);
params = new MultiMap<>();
huri.decodeQueryTo(params,StandardCharsets.UTF_8);
assertEquals("data"+Utf8Appendable.REPLACEMENT+"here"+Utf8Appendable.REPLACEMENT,params.getValue("invalid",0));
}
@Test
public void testExtB() throws Exception
{
for (String value: new String[]{"a","abcdABCD","\u00C0","\u697C","\uD869\uDED5","\uD840\uDC08"} )
{
HttpURI uri = new HttpURI("/path?value="+URLEncoder.encode(value,"UTF-8"));
MultiMap<String> parameters = new MultiMap<>();
uri.decodeQueryTo(parameters,StandardCharsets.UTF_8);
assertEquals(value,parameters.getString("value"));
}
}
@Test
public void testParams() throws Exception
{
HttpURI uri = new HttpURI("/foo/bar");
assertEquals("/foo/bar",uri.getPath());
assertEquals("/foo/bar",uri.getDecodedPath());
assertEquals(null,uri.getParam());
uri = new HttpURI("/foo/bar;jsessionid=12345");
assertEquals("/foo/bar;jsessionid=12345",uri.getPath());
assertEquals("/foo/bar",uri.getDecodedPath());
assertEquals("jsessionid=12345",uri.getParam());
uri = new HttpURI("/foo;abc=123/bar;jsessionid=12345");
assertEquals("/foo;abc=123/bar;jsessionid=12345",uri.getPath());
assertEquals("/foo/bar",uri.getDecodedPath());
assertEquals("jsessionid=12345",uri.getParam());
uri = new HttpURI("/foo;abc=123/bar;jsessionid=12345?name=value");
assertEquals("/foo;abc=123/bar;jsessionid=12345",uri.getPath());
assertEquals("/foo/bar",uri.getDecodedPath());
assertEquals("jsessionid=12345",uri.getParam());
uri = new HttpURI("/foo;abc=123/bar;jsessionid=12345#target");
assertEquals("/foo;abc=123/bar;jsessionid=12345",uri.getPath());
assertEquals("/foo/bar",uri.getDecodedPath());
assertEquals("jsessionid=12345",uri.getParam());
}
@Test
public void testMutableURI()
{
HttpURI uri = new HttpURI("/foo/bar");
assertEquals("/foo/bar",uri.toString());
assertEquals("/foo/bar",uri.getPath());
assertEquals("/foo/bar",uri.getDecodedPath());
uri.setScheme("http");
assertEquals("http:/foo/bar",uri.toString());
assertEquals("/foo/bar",uri.getPath());
assertEquals("/foo/bar",uri.getDecodedPath());
uri.setAuthority("host",0);
assertEquals("http://host/foo/bar",uri.toString());
assertEquals("/foo/bar",uri.getPath());
assertEquals("/foo/bar",uri.getDecodedPath());
uri.setAuthority("host",8888);
assertEquals("http://host:8888/foo/bar",uri.toString());
assertEquals("/foo/bar",uri.getPath());
assertEquals("/foo/bar",uri.getDecodedPath());
uri.setPathQuery("/f%30%30;p0/bar;p1;p2");
assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2",uri.toString());
assertEquals("/f%30%30;p0/bar;p1;p2",uri.getPath());
assertEquals("/f00/bar",uri.getDecodedPath());
assertEquals("p2",uri.getParam());
assertEquals(null,uri.getQuery());
uri.setPathQuery("/f%30%30;p0/bar;p1;p2?name=value");
assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2?name=value",uri.toString());
assertEquals("/f%30%30;p0/bar;p1;p2",uri.getPath());
assertEquals("/f00/bar",uri.getDecodedPath());
assertEquals("p2",uri.getParam());
assertEquals("name=value",uri.getQuery());
uri.setQuery("other=123456");
assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2?other=123456",uri.toString());
assertEquals("/f%30%30;p0/bar;p1;p2",uri.getPath());
assertEquals("/f00/bar",uri.getDecodedPath());
assertEquals("p2",uri.getParam());
assertEquals("other=123456",uri.getQuery());
}
}

View File

@ -90,9 +90,9 @@ public class MimeTypesTest
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = abc"));
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = \"abc\" ; some=else"));
assertEquals(null,MimeTypes.getCharsetFromContentType("foo/bar"));
assertEquals("UTF-8",MimeTypes.getCharsetFromContentType("foo/bar;charset=uTf8"));
assertEquals("UTF-8",MimeTypes.getCharsetFromContentType("foo/bar;other=\"charset=abc\";charset=uTf8"));
assertEquals("UTF-8",MimeTypes.getCharsetFromContentType("text/html;charset=utf-8"));
assertEquals("utf-8",MimeTypes.getCharsetFromContentType("foo/bar;charset=uTf8"));
assertEquals("utf-8",MimeTypes.getCharsetFromContentType("foo/bar;other=\"charset=abc\";charset=uTf8"));
assertEquals("utf-8",MimeTypes.getCharsetFromContentType("text/html;charset=utf-8"));
}

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-parent</artifactId>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>http2-client</artifactId>
<name>Jetty :: HTTP2 :: Client</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>manifest</goal>
</goals>
<configuration>
<instructions>
<Import-Package>javax.servlet.*;version="[2.6.0,3.2)",javax.net.*,*</Import-Package>
</instructions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,198 @@
//
// ========================================================================
// 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.http2.client;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import org.eclipse.jetty.http2.HTTP2Connection;
import org.eclipse.jetty.http2.HTTP2FlowControl;
import org.eclipse.jetty.http2.HTTP2Session;
import org.eclipse.jetty.http2.ISession;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.generator.Generator;
import org.eclipse.jetty.http2.parser.ErrorCode;
import org.eclipse.jetty.http2.parser.Parser;
import org.eclipse.jetty.http2.parser.PrefaceParser;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
public class HTTP2Client extends ContainerLifeCycle
{
private final Queue<ISession> sessions = new ConcurrentLinkedQueue<>();
private final SelectorManager selector;
private final ByteBufferPool byteBufferPool;
private long idleTimeout;
public HTTP2Client()
{
this(new QueuedThreadPool());
}
public HTTP2Client(Executor executor)
{
addBean(executor);
Scheduler scheduler = new ScheduledExecutorScheduler();
addBean(scheduler, true);
this.selector = new ClientSelectorManager(executor, scheduler);
addBean(selector, true);
this.byteBufferPool = new MappedByteBufferPool();
addBean(byteBufferPool, true);
}
@Override
protected void doStop() throws Exception
{
closeConnections();
super.doStop();
}
public void connect(InetSocketAddress address, Session.Listener listener, Promise<Session> promise)
{
try
{
SocketChannel channel = SocketChannel.open();
channel.socket().setTcpNoDelay(true);
channel.configureBlocking(false);
channel.connect(address);
selector.connect(channel, new Context(listener, promise));
}
catch (Throwable x)
{
promise.failed(x);
}
}
private void closeConnections()
{
for (ISession session : sessions)
session.close(ErrorCode.NO_ERROR, null, Callback.Adapter.INSTANCE);
sessions.clear();
}
public long getIdleTimeout()
{
return idleTimeout;
}
public void setIdleTimeout(long idleTimeout)
{
this.idleTimeout = idleTimeout;
}
private class ClientSelectorManager extends SelectorManager
{
private ClientSelectorManager(Executor executor, Scheduler scheduler)
{
super(executor, scheduler);
}
@Override
protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey selectionKey) throws IOException
{
return new SelectChannelEndPoint(channel, selector, selectionKey, getScheduler(), getIdleTimeout());
}
@Override
public Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment) throws IOException
{
Context context = (Context)attachment;
Generator generator = new Generator(byteBufferPool, 4096);
HTTP2Session session = new HTTP2ClientSession(getScheduler(), endpoint, generator, context.listener, new HTTP2FlowControl(65535));
Parser parser = new Parser(byteBufferPool, session, 4096, 8192);
return new HTTP2ClientConnection(byteBufferPool, getExecutor(), endpoint, parser, session, 8192, context.promise);
}
}
private class HTTP2ClientConnection extends HTTP2Connection implements Callback
{
private final Promise<Session> promise;
public HTTP2ClientConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise<Session> promise)
{
super(byteBufferPool, executor, endpoint, parser, session, bufferSize);
this.promise = promise;
}
@Override
public void onOpen()
{
super.onOpen();
getEndPoint().write(this, ByteBuffer.wrap(PrefaceParser.PREFACE_BYTES));
}
@Override
public void onClose()
{
super.onClose();
sessions.remove(getSession());
}
@Override
protected boolean onReadTimeout()
{
if (LOG.isDebugEnabled())
LOG.debug("Idle timeout {}ms expired on {}", getEndPoint().getIdleTimeout(), this);
getSession().close(ErrorCode.NO_ERROR, "idle_timeout", closeCallback);
return false;
}
@Override
public void succeeded()
{
sessions.offer(getSession());
promise.succeeded(getSession());
}
@Override
public void failed(Throwable x)
{
close();
promise.failed(x);
}
}
private class Context
{
private final Session.Listener listener;
private final Promise<Session> promise;
private Context(Session.Listener listener, Promise<Session> promise)
{
this.listener = listener;
this.promise = promise;
}
}
}

View File

@ -0,0 +1,79 @@
//
// ========================================================================
// 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.http2.client;
import org.eclipse.jetty.http2.FlowControl;
import org.eclipse.jetty.http2.HTTP2Session;
import org.eclipse.jetty.http2.IStream;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.generator.Generator;
import org.eclipse.jetty.http2.parser.ErrorCode;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
public class HTTP2ClientSession extends HTTP2Session
{
private static final Logger LOG = Log.getLogger(HTTP2ClientSession.class);
public HTTP2ClientSession(Scheduler scheduler, EndPoint endPoint, Generator generator, Listener listener, FlowControl flowControl)
{
super(scheduler, endPoint, generator, listener, flowControl, -1, 1);
}
@Override
public boolean onHeaders(HeadersFrame frame)
{
int streamId = frame.getStreamId();
IStream stream = getStream(streamId);
if (stream == null)
{
ResetFrame reset = new ResetFrame(streamId, ErrorCode.STREAM_CLOSED_ERROR);
reset(reset, disconnectOnFailure());
}
else
{
stream.updateClose(frame.isEndStream(), false);
stream.process(frame, Callback.Adapter.INSTANCE);
notifyHeaders(stream, frame);
if (stream.isClosed())
removeStream(stream, false);
}
return false;
}
private void notifyHeaders(IStream stream, HeadersFrame frame)
{
Stream.Listener listener = stream.getListener();
if (listener == null)
return;
try
{
listener.onHeaders(stream, frame);
}
catch (Throwable x)
{
LOG.info("Failure while notifying listener " + listener, x);
}
}
}

View File

@ -0,0 +1,113 @@
//
// ========================================================================
// 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.http2.client;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServlet;
import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.After;
public class AbstractTest
{
protected ServerConnector connector;
private String path = "/test";
protected HTTP2Client client;
private Server server;
protected void startServer(HttpServlet servlet) throws Exception
{
prepareServer(new HTTP2ServerConnectionFactory(new HttpConfiguration()));
ServletContextHandler context = new ServletContextHandler(server, "/");
context.addServlet(new ServletHolder(servlet), path);
prepareClient();
server.start();
client.start();
}
protected void startServer(ServerSessionListener listener) throws Exception
{
prepareServer(new RawHTTP2ServerConnectionFactory(listener));
prepareClient();
server.start();
client.start();
}
private void prepareServer(ConnectionFactory connectionFactory)
{
QueuedThreadPool serverExecutor = new QueuedThreadPool();
serverExecutor.setName("server");
server = new Server(serverExecutor);
connector = new ServerConnector(server, connectionFactory);
server.addConnector(connector);
}
private void prepareClient()
{
QueuedThreadPool clientExecutor = new QueuedThreadPool();
clientExecutor.setName("client");
client = new HTTP2Client(clientExecutor);
}
protected Session newClient(Session.Listener listener) throws Exception
{
String host = "localhost";
int port = connector.getLocalPort();
InetSocketAddress address = new InetSocketAddress(host, port);
FuturePromise<Session> promise = new FuturePromise<>();
client.connect(address, listener, promise);
return promise.get(5, TimeUnit.SECONDS);
}
@After
public void dispose() throws Exception
{
client.stop();
server.stop();
}
protected MetaData.Request newRequest(String method, HttpFields fields)
{
String host = "localhost";
int port = connector.getLocalPort();
String authority = host + ":" + port;
return new MetaData.Request(method, HttpScheme.HTTP, new HostPortHttpField(authority), path, HttpVersion.HTTP_2, fields);
}
}

View File

@ -16,22 +16,18 @@
// ========================================================================
//
package org.eclipse.jetty.security;
package org.eclipse.jetty.http2.client;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @version $Rev: 4466 $ $Date: 2009-02-10 23:42:54 +0100 (Tue, 10 Feb 2009) $
* @deprecated
*/
public interface CrossContextPsuedoSession<T>
public class EmptyHttpServlet extends HttpServlet
{
T fetch(HttpServletRequest request);
void store(T data, HttpServletResponse response);
void clear(HttpServletRequest request);
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
}
}

View File

@ -0,0 +1,465 @@
//
// ========================================================================
// 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.http2.client;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Promise;
import org.junit.Assert;
import org.junit.Test;
public class FlowControlTest extends AbstractTest
{
@Override
public void dispose() throws Exception
{
// Allow WINDOW_UPDATE frames to be sent/received to avoid exception stack traces.
Thread.sleep(1000);
super.dispose();
}
@Test
public void testFlowControlWithConcurrentSettings() throws Exception
{
// Initial window is 64 KiB. We allow the client to send 1024 B
// then we change the window to 512 B. At this point, the client
// must stop sending data (although the initial window allows it).
final int size = 512;
// We get 3 data frames: the first of 1024 and 2 of 512 each
// after the flow control window has been reduced.
final CountDownLatch dataLatch = new CountDownLatch(3);
final AtomicReference<Callback> callbackRef = new AtomicReference<>();
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
{
HttpFields fields = new HttpFields();
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, 200, fields);
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), response, null, true);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
return new Stream.Listener.Adapter()
{
private final AtomicInteger dataFrames = new AtomicInteger();
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
dataLatch.countDown();
int dataFrameCount = dataFrames.incrementAndGet();
if (dataFrameCount == 1)
{
callbackRef.set(callback);
Map<Integer, Integer> settings = new HashMap<>();
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, size);
stream.getSession().settings(new SettingsFrame(settings, false), Callback.Adapter.INSTANCE);
// Do not succeed the callback here.
}
else if (dataFrameCount > 1)
{
// Consume the data.
callback.succeeded();
}
}
};
}
});
// Two SETTINGS frames, the initial one and the one we send from the server.
final CountDownLatch settingsLatch = new CountDownLatch(2);
Session session = newClient(new Session.Listener.Adapter()
{
@Override
public void onSettings(Session session, SettingsFrame frame)
{
settingsLatch.countDown();
}
});
MetaData.Request request = newRequest("POST", new HttpFields());
FuturePromise<Stream> promise = new FuturePromise<>();
session.newStream(new HeadersFrame(0, request, null, false), promise, new Stream.Listener.Adapter());
Stream stream = promise.get(5, TimeUnit.SECONDS);
// Send first chunk that exceeds the window.
stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(size * 2), false), Callback.Adapter.INSTANCE);
settingsLatch.await(5, TimeUnit.SECONDS);
// Send the second chunk of data, must not arrive since we're flow control stalled on the client.
stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(size * 2), true), Callback.Adapter.INSTANCE);
Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS));
// Consume the data arrived to server, this will resume flow control on the client.
callbackRef.get().succeeded();
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testServerFlowControlOneBigWrite() throws Exception
{
final int windowSize = 1536;
final int length = 5 * windowSize;
final CountDownLatch settingsLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public void onSettings(Session session, SettingsFrame frame)
{
settingsLatch.countDown();
}
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
{
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.allocate(length), true);
stream.data(dataFrame, Callback.Adapter.INSTANCE);
return null;
}
});
Session session = newClient(new Session.Listener.Adapter());
Map<Integer, Integer> settings = new HashMap<>();
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, windowSize);
session.settings(new SettingsFrame(settings, false), Callback.Adapter.INSTANCE);
Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
final CountDownLatch dataLatch = new CountDownLatch(1);
final Exchanger<Callback> exchanger = new Exchanger<>();
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, true);
session.newStream(requestFrame, new Promise.Adapter<Stream>(), new Stream.Listener.Adapter()
{
private AtomicInteger dataFrames = new AtomicInteger();
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
try
{
int dataFrames = this.dataFrames.incrementAndGet();
if (dataFrames == 1 || dataFrames == 2)
{
// Do not consume the data frame.
// We should then be flow-control stalled.
exchanger.exchange(callback);
}
else if (dataFrames == 3 || dataFrames == 4 || dataFrames == 5)
{
// Consume totally.
callback.succeeded();
if (frame.isEndStream())
dataLatch.countDown();
}
else
{
Assert.fail();
}
}
catch (InterruptedException x)
{
callback.failed(x);
}
}
});
Callback callback = exchanger.exchange(null, 5, TimeUnit.SECONDS);
checkThatWeAreFlowControlStalled(exchanger);
// Consume the first chunk.
callback.succeeded();
callback = exchanger.exchange(null, 5, TimeUnit.SECONDS);
checkThatWeAreFlowControlStalled(exchanger);
// Consume the second chunk.
callback.succeeded();
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testClientFlowControlOneBigWrite() throws Exception
{
final int windowSize = 1536;
final Exchanger<Callback> exchanger = new Exchanger<>();
final CountDownLatch settingsLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public Map<Integer, Integer> onPreface(Session session)
{
Map<Integer, Integer> settings = new HashMap<>();
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, windowSize);
return settings;
}
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
{
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
return new Stream.Listener.Adapter()
{
private AtomicInteger dataFrames = new AtomicInteger();
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
try
{
int dataFrames = this.dataFrames.incrementAndGet();
if (dataFrames == 1 || dataFrames == 2)
{
// Do not consume the data frame.
// We should then be flow-control stalled.
exchanger.exchange(callback);
}
else if (dataFrames == 3 || dataFrames == 4 || dataFrames == 5)
{
// Consume totally.
callback.succeeded();
if (frame.isEndStream())
dataLatch.countDown();
}
else
{
Assert.fail();
}
}
catch (InterruptedException x)
{
callback.failed(x);
}
}
};
}
});
Session session = newClient(new Session.Listener.Adapter()
{
@Override
public void onSettings(Session session, SettingsFrame frame)
{
settingsLatch.countDown();
}
});
Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, false);
FuturePromise<Stream> streamPromise = new FuturePromise<>();
session.newStream(requestFrame, streamPromise, null);
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
final int length = 5 * windowSize;
DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.allocate(length), true);
stream.data(dataFrame, Callback.Adapter.INSTANCE);
Callback callback = exchanger.exchange(null, 5, TimeUnit.SECONDS);
checkThatWeAreFlowControlStalled(exchanger);
// Consume the first chunk.
callback.succeeded();
callback = exchanger.exchange(null, 5, TimeUnit.SECONDS);
checkThatWeAreFlowControlStalled(exchanger);
// Consume the second chunk.
callback.succeeded();
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
private void checkThatWeAreFlowControlStalled(Exchanger<Callback> exchanger) throws Exception
{
try
{
exchanger.exchange(null, 1, TimeUnit.SECONDS);
}
catch (TimeoutException x)
{
// Expected.
}
}
@Test
public void testSessionStalledStallsNewStreams() throws Exception
{
final int windowSize = 1024;
final CountDownLatch settingsLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public void onSettings(Session session, SettingsFrame frame)
{
settingsLatch.countDown();
}
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
{
// For every stream, send down half the window size of data.
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.allocate(windowSize / 2), true);
stream.data(dataFrame, Callback.Adapter.INSTANCE);
return null;
}
});
Session session = newClient(new Session.Listener.Adapter());
Map<Integer, Integer> settings = new HashMap<>();
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, windowSize);
session.settings(new SettingsFrame(settings, false), Callback.Adapter.INSTANCE);
Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
final AtomicReference<Callback> callbackRef1 = new AtomicReference<>();
final AtomicReference<Callback> callbackRef2 = new AtomicReference<>();
// First request will consume half the session window.
MetaData.Request request1 = newRequest("GET", new HttpFields());
session.newStream(new HeadersFrame(0, request1, null, true), new Promise.Adapter<Stream>(), new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
// Do not consume it to stall flow control.
callbackRef1.set(callback);
}
});
// Second request will consume the session window, which is now stalled.
// A third request will not be able to receive data.
MetaData.Request request2 = newRequest("GET", new HttpFields());
session.newStream(new HeadersFrame(0, request2, null, true), new Promise.Adapter<Stream>(), new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
// Do not consume it to stall flow control.
callbackRef2.set(callback);
}
});
// Third request is now stalled.
final CountDownLatch latch = new CountDownLatch(1);
MetaData.Request request3 = newRequest("GET", new HttpFields());
session.newStream(new HeadersFrame(0, request3, null, true), new Promise.Adapter<Stream>(), new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
if (frame.isEndStream())
latch.countDown();
}
});
// Verify that the data does not arrive because the server session is stalled.
Assert.assertFalse(latch.await(1, TimeUnit.SECONDS));
// Consume the data of the second response.
// This will open up the session window, allowing the third stream to send data.
Callback callback2 = callbackRef2.getAndSet(null);
if (callback2 != null)
callback2.succeeded();
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@Test
public void testServerSendsBigContent() throws Exception
{
final byte[] data = new byte[1024 * 1024];
new Random().nextBytes(data);
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
{
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.wrap(data), true);
stream.data(dataFrame, Callback.Adapter.INSTANCE);
return null;
}
});
Session session = newClient(new Session.Listener.Adapter());
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, true);
final byte[] bytes = new byte[data.length];
final CountDownLatch latch = new CountDownLatch(1);
session.newStream(requestFrame, new Promise.Adapter<Stream>(), new Stream.Listener.Adapter()
{
private int received;
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
int remaining = frame.remaining();
frame.getData().get(bytes, received, remaining);
this.received += remaining;
callback.succeeded();
if (frame.isEndStream())
latch.countDown();
}
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
Assert.assertArrayEquals(data, bytes);
}
}

View File

@ -0,0 +1,127 @@
//
// ========================================================================
// 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.http2.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.junit.Assert;
import org.junit.Test;
public class HTTP2Test extends AbstractTest
{
@Test
public void testRequestNoContentResponseNoContent() throws Exception
{
startServer(new EmptyHttpServlet());
Session session = newClient(new Session.Listener.Adapter());
HttpFields fields = new HttpFields();
MetaData.Request metaData = newRequest("GET", fields);
HeadersFrame frame = new HeadersFrame(1, metaData, null, true);
final CountDownLatch latch = new CountDownLatch(1);
session.newStream(frame, new Promise.Adapter<Stream>(), new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
Assert.assertTrue(stream.isClosed());
Assert.assertTrue(stream.getId() > 0);
Assert.assertTrue(frame.isEndStream());
Assert.assertEquals(stream.getId(), frame.getStreamId());
Assert.assertTrue(frame.getMetaData().isResponse());
MetaData.Response response = (MetaData.Response)frame.getMetaData();
Assert.assertEquals(200, response.getStatus());
latch.countDown();
}
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@Test
public void testRequestNoContentResponseContent() throws Exception
{
final byte[] content = "Hello World!".getBytes(StandardCharsets.UTF_8);
startServer(new HttpServlet()
{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.getOutputStream().write(content);
}
});
Session session = newClient(new Session.Listener.Adapter());
HttpFields fields = new HttpFields();
MetaData.Request metaData = newRequest("GET", fields);
HeadersFrame frame = new HeadersFrame(1, metaData, null, true);
final CountDownLatch latch = new CountDownLatch(2);
session.newStream(frame, new Promise.Adapter<Stream>(), new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
Assert.assertFalse(stream.isClosed());
Assert.assertTrue(stream.getId() > 0);
Assert.assertFalse(frame.isEndStream());
Assert.assertEquals(stream.getId(), frame.getStreamId());
Assert.assertTrue(frame.getMetaData().isResponse());
MetaData.Response response = (MetaData.Response)frame.getMetaData();
Assert.assertEquals(200, response.getStatus());
latch.countDown();
}
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
Assert.assertTrue(stream.isClosed());
Assert.assertTrue(frame.isEndStream());
Assert.assertEquals(ByteBuffer.wrap(content), frame.getData());
callback.succeeded();
latch.countDown();
}
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
}

View File

@ -0,0 +1,484 @@
//
// ========================================================================
// 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.http2.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.GoAwayFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.junit.Assert;
import org.junit.Test;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertThat;
public class IdleTimeoutTest extends AbstractTest
{
private final int idleTimeout = 1000;
@Test
public void testServerEnforcingIdleTimeout() throws Exception
{
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
{
stream.setIdleTimeout(10 * idleTimeout);
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
return null;
}
});
connector.setIdleTimeout(idleTimeout);
final CountDownLatch latch = new CountDownLatch(1);
Session session = newClient(new Session.Listener.Adapter()
{
@Override
public void onClose(Session session, GoAwayFrame frame)
{
latch.countDown();
}
});
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, true);
session.newStream(requestFrame, new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream stream)
{
stream.setIdleTimeout(10 * idleTimeout);
}
}, new Stream.Listener.Adapter());
Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
}
@Test
public void testServerEnforcingIdleTimeoutWithUnrespondedStream() throws Exception
{
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
stream.setIdleTimeout(10 * idleTimeout);
return null;
}
});
connector.setIdleTimeout(idleTimeout);
final CountDownLatch latch = new CountDownLatch(1);
Session session = newClient(new Session.Listener.Adapter()
{
@Override
public void onClose(Session session, GoAwayFrame frame)
{
latch.countDown();
}
});
// The request is not replied, and the server should idle timeout.
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, true);
session.newStream(requestFrame, new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream stream)
{
stream.setIdleTimeout(10 * idleTimeout);
}
}, new Stream.Listener.Adapter());
Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
}
@Test
public void testServerNotEnforcingIdleTimeoutWithPendingStream() throws Exception
{
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
try
{
stream.setIdleTimeout(10 * idleTimeout);
Thread.sleep(2 * idleTimeout);
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
return null;
}
catch (InterruptedException x)
{
Assert.fail();
return null;
}
}
});
connector.setIdleTimeout(idleTimeout);
final CountDownLatch closeLatch = new CountDownLatch(1);
Session session = newClient(new ServerSessionListener.Adapter()
{
@Override
public void onClose(Session session, GoAwayFrame frame)
{
closeLatch.countDown();
}
});
final CountDownLatch replyLatch = new CountDownLatch(1);
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, true);
session.newStream(requestFrame, new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream stream)
{
stream.setIdleTimeout(10 * idleTimeout);
}
}, new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
replyLatch.countDown();
}
});
Assert.assertTrue(replyLatch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
// Just make sure onClose() has never been called, but don't wait too much
Assert.assertFalse(closeLatch.await(idleTimeout / 2, TimeUnit.MILLISECONDS));
}
@Test
public void testClientEnforcingIdleTimeout() throws Exception
{
final CountDownLatch closeLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
stream.setIdleTimeout(10 * idleTimeout);
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
return null;
}
@Override
public void onClose(Session session, GoAwayFrame frame)
{
closeLatch.countDown();
}
});
client.setIdleTimeout(idleTimeout);
Session session = newClient(new Session.Listener.Adapter());
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, true);
session.newStream(requestFrame, new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream stream)
{
stream.setIdleTimeout(10 * idleTimeout);
}
}, new Stream.Listener.Adapter());
Assert.assertTrue(closeLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
}
@Test
public void testClientEnforcingIdleTimeoutWithUnrespondedStream() throws Exception
{
final CountDownLatch closeLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
stream.setIdleTimeout(10 * idleTimeout);
return null;
}
@Override
public void onClose(Session session, GoAwayFrame frame)
{
closeLatch.countDown();
}
});
client.setIdleTimeout(idleTimeout);
Session session = newClient(new Session.Listener.Adapter());
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, true);
session.newStream(requestFrame, new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream stream)
{
stream.setIdleTimeout(10 * idleTimeout);
}
}, new Stream.Listener.Adapter());
Assert.assertTrue(closeLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
}
@Test
public void testClientNotEnforcingIdleTimeoutWithPendingStream() throws Exception
{
final CountDownLatch closeLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
stream.setIdleTimeout(10 * idleTimeout);
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
return null;
}
@Override
public void onClose(Session session, GoAwayFrame frame)
{
closeLatch.countDown();
}
});
client.setIdleTimeout(idleTimeout);
Session session = newClient(new Session.Listener.Adapter());
final CountDownLatch replyLatch = new CountDownLatch(1);
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, true);
session.newStream(requestFrame, new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream stream)
{
stream.setIdleTimeout(10 * idleTimeout);
}
}, new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
try
{
Thread.sleep(2 * idleTimeout);
replyLatch.countDown();
}
catch (InterruptedException e)
{
Assert.fail();
}
}
});
Assert.assertFalse(closeLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
Assert.assertTrue(replyLatch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
}
@Test
public void testClientEnforcingStreamIdleTimeout() throws Exception
{
final int idleTimeout = 1000;
startServer(new HttpServlet()
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
try
{
Thread.sleep(2 * idleTimeout);
}
catch (InterruptedException x)
{
throw new RuntimeException(x);
}
}
});
Session session = newClient(new Session.Listener.Adapter());
final CountDownLatch dataLatch = new CountDownLatch(1);
final CountDownLatch timeoutLatch = new CountDownLatch(1);
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, true);
session.newStream(requestFrame, new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream stream)
{
stream.setIdleTimeout(idleTimeout);
}
}, new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
dataLatch.countDown();
}
@Override
public void onFailure(Stream stream, Throwable x)
{
assertThat(x, instanceOf(TimeoutException.class));
timeoutLatch.countDown();
}
});
Assert.assertTrue(timeoutLatch.await(5, TimeUnit.SECONDS));
// We must not receive any DATA frame.
Assert.assertFalse(dataLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
// Stream must be gone.
Assert.assertTrue(session.getStreams().isEmpty());
}
@Test
public void testServerEnforcingStreamIdleTimeout() throws Exception
{
final CountDownLatch timeoutLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
stream.setIdleTimeout(idleTimeout);
return new Stream.Listener.Adapter()
{
@Override
public void onFailure(Stream stream, Throwable x)
{
timeoutLatch.countDown();
}
};
}
});
final CountDownLatch resetLatch = new CountDownLatch(1);
Session session = newClient(new Session.Listener.Adapter()
{
@Override
public void onReset(Session session, ResetFrame frame)
{
resetLatch.countDown();
}
});
MetaData.Request metaData = newRequest("GET", new HttpFields());
// Stream does not end here, but we won't send any DATA frame.
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, false);
session.newStream(requestFrame, new Promise.Adapter<Stream>(), new Stream.Listener.Adapter());
Assert.assertTrue(timeoutLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
// Stream must be gone.
Assert.assertTrue(session.getStreams().isEmpty());
}
@Test
public void testStreamIdleTimeoutIsNotEnforcedWhenReceiving() throws Exception
{
final CountDownLatch timeoutLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
stream.setIdleTimeout(idleTimeout);
return new Stream.Listener.Adapter()
{
@Override
public void onFailure(Stream stream, Throwable x)
{
timeoutLatch.countDown();
}
};
}
});
Session session = newClient(new Session.Listener.Adapter());
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, metaData, null, false);
session.newStream(requestFrame, new Promise.Adapter<Stream>()
{
@Override
public void succeeded(final Stream stream)
{
sleep(idleTimeout / 2);
stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), false), new Callback.Adapter()
{
private int sends;
@Override
public void succeeded()
{
sleep(idleTimeout / 2);
boolean last = ++sends == 2;
stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), last), last ? INSTANCE : this);
}
});
}
}, new Stream.Listener.Adapter());
Assert.assertFalse(timeoutLatch.await(1, TimeUnit.SECONDS));
}
private void sleep(long value)
{
try
{
TimeUnit.MILLISECONDS.sleep(value);
}
catch (InterruptedException x)
{
Assert.fail();
}
}
}

View File

@ -0,0 +1,58 @@
//
// ========================================================================
// 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.http2.client;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.frames.PingFrame;
import org.eclipse.jetty.util.Callback;
import org.junit.Assert;
import org.junit.Test;
public class PingTest extends AbstractTest
{
@Test
public void testPing() throws Exception
{
startServer(new ServerSessionListener.Adapter());
final byte[] payload = new byte[8];
new Random().nextBytes(payload);
final CountDownLatch latch = new CountDownLatch(1);
Session session = newClient(new Session.Listener.Adapter()
{
@Override
public void onPing(Session session, PingFrame frame)
{
Assert.assertTrue(frame.isReply());
Assert.assertArrayEquals(payload, frame.getPayload());
latch.countDown();
}
});
PingFrame frame = new PingFrame(payload, false);
session.ping(frame, Callback.Adapter.INSTANCE);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
}

View File

@ -0,0 +1,182 @@
//
// ========================================================================
// 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.http2.client;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.parser.ErrorCode;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.junit.Assert;
import org.junit.Test;
public class StreamResetTest extends AbstractTest
{
@Test
public void testStreamSendingResetIsRemoved() throws Exception
{
startServer(new ServerSessionListener.Adapter());
Session client = newClient(new Session.Listener.Adapter());
MetaData.Request request = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, request, null, false);
FuturePromise<Stream> promise = new FuturePromise<>();
client.newStream(requestFrame, promise, new Stream.Listener.Adapter());
Stream stream = promise.get(5, TimeUnit.SECONDS);
ResetFrame resetFrame = new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR);
stream.getSession().reset(resetFrame, Callback.Adapter.INSTANCE);
// After reset the stream should be gone.
Assert.assertEquals(0, client.getStreams().size());
}
@Test
public void testStreamReceivingResetIsRemoved() throws Exception
{
final AtomicReference<Stream> streamRef = new AtomicReference<>();
final CountDownLatch resetLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public void onReset(Session session, ResetFrame frame)
{
Stream stream = session.getStream(frame.getStreamId());
Assert.assertNotNull(stream);
Assert.assertTrue(stream.isReset());
streamRef.set(stream);
resetLatch.countDown();
}
});
Session client = newClient(new Session.Listener.Adapter());
MetaData.Request request = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(0, request, null, false);
FuturePromise<Stream> promise = new FuturePromise<>();
client.newStream(requestFrame, promise, new Stream.Listener.Adapter());
Stream stream = promise.get(5, TimeUnit.SECONDS);
ResetFrame resetFrame = new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR);
stream.getSession().reset(resetFrame, Callback.Adapter.INSTANCE);
Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
Stream serverStream = streamRef.get();
Assert.assertEquals(0, serverStream.getSession().getStreams().size());
}
@Test
public void testStreamResetDoesNotCloseConnection() throws Exception
{
final CountDownLatch serverResetLatch = new CountDownLatch(1);
final CountDownLatch serverDataLatch = new CountDownLatch(1);
startServer(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
{
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), response, null, false);
stream.headers(responseFrame, Callback.Adapter.INSTANCE);
return new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(16), true), Callback.Adapter.INSTANCE);
serverDataLatch.countDown();
}
};
}
@Override
public void onReset(Session session, ResetFrame frame)
{
Stream stream = session.getStream(frame.getStreamId());
// Simulate that there is pending data to send.
stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(16), true), new Callback.Adapter()
{
@Override
public void failed(Throwable x)
{
serverResetLatch.countDown();
}
});
}
});
Session client = newClient(new Session.Listener.Adapter());
MetaData.Request request1 = newRequest("GET", new HttpFields());
HeadersFrame requestFrame1 = new HeadersFrame(0, request1, null, false);
FuturePromise<Stream> promise1 = new FuturePromise<>();
final CountDownLatch stream1HeadersLatch = new CountDownLatch(1);
final CountDownLatch stream1DataLatch = new CountDownLatch(1);
client.newStream(requestFrame1, promise1, new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
stream1HeadersLatch.countDown();
}
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
stream1DataLatch.countDown();
}
});
Stream stream1 = promise1.get(5, TimeUnit.SECONDS);
Assert.assertTrue(stream1HeadersLatch.await(5, TimeUnit.SECONDS));
MetaData.Request request2 = newRequest("GET", new HttpFields());
HeadersFrame requestFrame2 = new HeadersFrame(0, request2, null, false);
FuturePromise<Stream> promise2 = new FuturePromise<>();
final CountDownLatch stream2DataLatch = new CountDownLatch(1);
client.newStream(requestFrame2, promise2, new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
stream2DataLatch.countDown();
}
});
Stream stream2 = promise2.get(5, TimeUnit.SECONDS);
ResetFrame resetFrame = new ResetFrame(stream1.getId(), ErrorCode.CANCEL_STREAM_ERROR);
stream1.getSession().reset(resetFrame, Callback.Adapter.INSTANCE);
Assert.assertTrue(serverResetLatch.await(5, TimeUnit.SECONDS));
// Stream MUST NOT receive data sent by server after reset.
Assert.assertFalse(stream1DataLatch.await(1, TimeUnit.SECONDS));
// The other stream should still be working.
stream2.data(new DataFrame(stream2.getId(), ByteBuffer.allocate(16), true), Callback.Adapter.INSTANCE);
Assert.assertTrue(serverDataLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(stream2DataLatch.await(5, TimeUnit.SECONDS));
}
}

View File

@ -0,0 +1,3 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.http2.hpack.LEVEL=INFO
org.eclipse.jetty.http2.LEVEL=INFO

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-parent</artifactId>
<version>9.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>http2-common</artifactId>
<name>Jetty :: HTTP2 :: Common</name>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-hpack</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>manifest</goal>
</goals>
<configuration>
<instructions>
<Import-Package>javax.servlet.*;version="[2.6.0,3.2)",javax.net.*,*</Import-Package>
</instructions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,42 @@
//
// ========================================================================
// 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.http2;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
public interface FlowControl
{
public void onNewStream(IStream stream);
public int getInitialWindowSize();
public void updateInitialWindowSize(ISession session, int initialWindowSize);
public void onWindowUpdate(ISession session, IStream stream, WindowUpdateFrame frame);
public void onDataReceived(ISession session, IStream stream, int length);
public void onDataConsumed(ISession session, IStream stream, int length);
public void onDataSent(ISession session, IStream stream, int length);
public void onSessionStalled(ISession session);
public void onStreamStalled(IStream stream);
}

View File

@ -0,0 +1,125 @@
//
// ========================================================================
// 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.http2;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import org.eclipse.jetty.http2.parser.Parser;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class HTTP2Connection extends AbstractConnection
{
protected static final Logger LOG = Log.getLogger(HTTP2Connection.class);
protected final Callback closeCallback = new Callback.Adapter()
{
@Override
public void failed(Throwable x)
{
close();
}
};
private final ByteBufferPool byteBufferPool;
private final Parser parser;
private final ISession session;
private final int bufferSize;
public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize)
{
super(endPoint, executor);
this.byteBufferPool = byteBufferPool;
this.parser = parser;
this.session = session;
this.bufferSize = bufferSize;
}
protected ISession getSession()
{
return session;
}
@Override
public void onOpen()
{
super.onOpen();
fillInterested();
}
@Override
public void onFillable()
{
ByteBuffer buffer = byteBufferPool.acquire(bufferSize, false);
boolean readMore = read(buffer) == 0;
byteBufferPool.release(buffer);
if (readMore)
fillInterested();
}
protected int read(ByteBuffer buffer)
{
EndPoint endPoint = getEndPoint();
while (true)
{
int filled = fill(endPoint, buffer);
if (LOG.isDebugEnabled()) // Avoid boxing of variable 'filled'
LOG.debug("Read {} bytes", filled);
if (filled == 0)
{
return 0;
}
else if (filled < 0)
{
shutdown(endPoint, session);
return -1;
}
else
{
parser.parse(buffer);
}
}
}
private int fill(EndPoint endPoint, ByteBuffer buffer)
{
try
{
if (endPoint.isInputShutdown())
return -1;
return endPoint.fill(buffer);
}
catch (IOException x)
{
LOG.debug("Could not read from " + endPoint, x);
return -1;
}
}
private void shutdown(EndPoint endPoint, ISession session)
{
if (!endPoint.isOutputShutdown())
session.shutdown();
}
}

View File

@ -0,0 +1,147 @@
//
// ========================================================================
// 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.http2;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class HTTP2FlowControl implements FlowControl
{
private static final Logger LOG = Log.getLogger(HTTP2FlowControl.class);
private volatile int initialWindowSize;
public HTTP2FlowControl(int initialWindowSize)
{
this.initialWindowSize = initialWindowSize;
}
@Override
public void onNewStream(IStream stream)
{
stream.updateWindowSize(initialWindowSize);
}
@Override
public int getInitialWindowSize()
{
return initialWindowSize;
}
@Override
public void updateInitialWindowSize(ISession session, int initialWindowSize)
{
int windowSize = this.initialWindowSize;
this.initialWindowSize = initialWindowSize;
int delta = initialWindowSize - windowSize;
// Update the sessions's window size.
int oldSize = session.updateWindowSize(delta);
if (LOG.isDebugEnabled())
LOG.debug("Updated session initial window {} -> {} for {}", oldSize, oldSize + delta, session);
// Update the streams' window size.
for (Stream stream : session.getStreams())
{
oldSize = ((IStream)stream).updateWindowSize(delta);
if (LOG.isDebugEnabled())
LOG.debug("Updated stream initial window {} -> {} for {}", oldSize, oldSize + delta, stream);
}
}
@Override
public void onWindowUpdate(ISession session, IStream stream, WindowUpdateFrame frame)
{
int delta = frame.getWindowDelta();
if (frame.getStreamId() > 0)
{
// The stream may have been reset concurrently.
if (stream != null)
{
int oldSize = stream.updateWindowSize(delta);
if (LOG.isDebugEnabled())
LOG.debug("Updated stream window {} -> {} for {}", oldSize, oldSize + delta, stream);
}
}
else
{
int oldSize = session.updateWindowSize(delta);
if (LOG.isDebugEnabled())
LOG.debug("Updated session window {} -> {} for {}", oldSize, oldSize + delta, session);
}
}
@Override
public void onDataReceived(ISession session, IStream stream, int length)
{
}
@Override
public void onDataConsumed(ISession session, IStream stream, int length)
{
// This is the algorithm for flow control.
// This method is called when a whole flow controlled frame has been consumed.
// We currently send a WindowUpdate every time, even if the frame was very small.
// Other policies may send the WindowUpdate only upon reaching a threshold.
if (LOG.isDebugEnabled())
LOG.debug("Data consumed, increasing window by {} for {}", length, stream);
// Negative streamId allow for generation of bytes for both stream and session
int streamId = stream != null ? -stream.getId() : 0;
WindowUpdateFrame frame = new WindowUpdateFrame(streamId, length);
session.control(stream, frame, Callback.Adapter.INSTANCE);
}
@Override
public void onDataSent(ISession session, IStream stream, int length)
{
if (length == 0)
return;
if (LOG.isDebugEnabled())
LOG.debug("Data sent, decreasing window by {}", length);
int oldSize = session.updateWindowSize(-length);
if (LOG.isDebugEnabled())
LOG.debug("Updated session window {} -> {} for {}", oldSize, oldSize - length, session);
if (stream != null)
{
oldSize = stream.updateWindowSize(-length);
if (LOG.isDebugEnabled())
LOG.debug("Updated stream window {} -> {} for {}", oldSize, oldSize - length, stream);
}
}
@Override
public void onSessionStalled(ISession session)
{
if (LOG.isDebugEnabled())
LOG.debug("Session stalled {}", session);
}
@Override
public void onStreamStalled(IStream stream)
{
if (LOG.isDebugEnabled())
LOG.debug("Stream stalled {}", stream);
}
}

View File

@ -0,0 +1,940 @@
//
// ========================================================================
// 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.http2;
import java.io.EOFException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.Frame;
import org.eclipse.jetty.http2.frames.FrameType;
import org.eclipse.jetty.http2.frames.GoAwayFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.PingFrame;
import org.eclipse.jetty.http2.frames.PriorityFrame;
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
import org.eclipse.jetty.http2.generator.Generator;
import org.eclipse.jetty.http2.parser.ErrorCode;
import org.eclipse.jetty.http2.parser.Parser;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.ArrayQueue;
import org.eclipse.jetty.util.Atomics;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingCallback;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
public abstract class HTTP2Session implements ISession, Parser.Listener
{
private static final Logger LOG = Log.getLogger(HTTP2Session.class);
private final Callback disconnectOnFailure = new Callback.Adapter()
{
@Override
public void failed(Throwable x)
{
disconnect();
}
};
private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
private final AtomicInteger streamIds = new AtomicInteger();
private final AtomicInteger lastStreamId = new AtomicInteger();
private final AtomicInteger streamCount = new AtomicInteger();
private final AtomicInteger windowSize = new AtomicInteger();
private final AtomicBoolean closed = new AtomicBoolean();
private final Scheduler scheduler;
private final EndPoint endPoint;
private final Generator generator;
private final Listener listener;
private final FlowControl flowControl;
private final Flusher flusher;
private volatile int maxStreamCount;
public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Listener listener, FlowControl flowControl, int maxStreams, int initialStreamId)
{
this.scheduler = scheduler;
this.endPoint = endPoint;
this.generator = generator;
this.listener = listener;
this.flowControl = flowControl;
this.flusher = new Flusher(4);
this.maxStreamCount = maxStreams;
this.streamIds.set(initialStreamId);
this.windowSize.set(flowControl.getInitialWindowSize());
}
public Generator getGenerator()
{
return generator;
}
@Override
public boolean onData(final DataFrame frame)
{
if (LOG.isDebugEnabled())
LOG.debug("Received {}", frame);
int streamId = frame.getStreamId();
final IStream stream = getStream(streamId);
if (stream != null)
{
stream.updateClose(frame.isEndStream(), false);
final int length = frame.remaining();
flowControl.onDataReceived(this, stream, length);
boolean result = stream.process(frame, new Callback.Adapter()
{
@Override
public void succeeded()
{
flowControl.onDataConsumed(HTTP2Session.this, stream, length);
}
});
if (stream.isClosed())
removeStream(stream, false);
return result;
}
else
{
ResetFrame resetFrame = new ResetFrame(streamId, ErrorCode.STREAM_CLOSED_ERROR);
reset(resetFrame, disconnectOnFailure());
return false;
}
}
@Override
public abstract boolean onHeaders(HeadersFrame frame);
@Override
public boolean onPriority(PriorityFrame frame)
{
return false;
}
@Override
public boolean onReset(ResetFrame frame)
{
if (LOG.isDebugEnabled())
LOG.debug("Received {}", frame);
IStream stream = getStream(frame.getStreamId());
if (stream != null)
stream.process(frame, Callback.Adapter.INSTANCE);
notifyReset(this, frame);
if (stream != null)
removeStream(stream, false);
return false;
}
@Override
public boolean onSettings(SettingsFrame frame)
{
if (LOG.isDebugEnabled())
LOG.debug("Received {}", frame);
if (frame.isReply())
return false;
Map<Integer, Integer> settings = frame.getSettings();
if (settings.containsKey(SettingsFrame.MAX_CONCURRENT_STREAMS))
{
maxStreamCount = settings.get(SettingsFrame.MAX_CONCURRENT_STREAMS);
if (LOG.isDebugEnabled())
LOG.debug("Updated max concurrent streams to {}", maxStreamCount);
}
if (settings.containsKey(SettingsFrame.INITIAL_WINDOW_SIZE))
{
int windowSize = settings.get(SettingsFrame.INITIAL_WINDOW_SIZE);
flowControl.updateInitialWindowSize(this, windowSize);
}
// TODO: handle other settings
notifySettings(this, frame);
// SPEC: SETTINGS frame MUST be replied.
SettingsFrame reply = new SettingsFrame(Collections.<Integer, Integer>emptyMap(), true);
settings(reply, disconnectOnFailure());
return false;
}
@Override
public boolean onPushPromise(PushPromiseFrame frame)
{
// TODO
return false;
}
@Override
public boolean onPing(PingFrame frame)
{
if (LOG.isDebugEnabled())
LOG.debug("Received {}", frame);
if (frame.isReply())
{
notifyPing(this, frame);
}
else
{
PingFrame reply = new PingFrame(frame.getPayload(), true);
control(null, reply, disconnectOnFailure());
}
return false;
}
@Override
public boolean onGoAway(GoAwayFrame frame)
{
if (LOG.isDebugEnabled())
{
String reason = tryConvertPayload(frame.getPayload());
if (LOG.isDebugEnabled())
LOG.debug("Received {}: {}/'{}'", frame.getType(), frame.getError(), reason);
}
flusher.close();
disconnect();
notifyClose(this, frame);
return false;
}
private String tryConvertPayload(byte[] payload)
{
if (payload == null)
return "";
ByteBuffer buffer = BufferUtil.toBuffer(payload);
try
{
return BufferUtil.toUTF8String(buffer);
}
catch (Throwable x)
{
return BufferUtil.toDetailString(buffer);
}
}
@Override
public boolean onWindowUpdate(WindowUpdateFrame frame)
{
if (LOG.isDebugEnabled())
LOG.debug("Received {}", frame);
int streamId = frame.getStreamId();
IStream stream = null;
if (streamId > 0)
stream = getStream(streamId);
flowControl.onWindowUpdate(this, stream, frame);
// Flush stalled data.
flusher.iterate();
return false;
}
@Override
public void onConnectionFailure(int error, String reason)
{
close(error, reason, disconnectOnFailure());
}
@Override
public void newStream(HeadersFrame frame, final Promise<Stream> promise, Stream.Listener listener)
{
// Synchronization is necessary to atomically create
// the stream id and enqueue the frame to be sent.
synchronized (this)
{
int streamId = streamIds.getAndAdd(2);
PriorityFrame priority = frame.getPriority();
priority = priority == null ? null : new PriorityFrame(streamId, priority.getDependentStreamId(),
priority.getWeight(), priority.isExclusive());
frame = new HeadersFrame(streamId, frame.getMetaData(), priority, frame.isEndStream());
final IStream stream = createLocalStream(frame);
if (stream == null)
{
promise.failed(new IllegalStateException());
return;
}
stream.updateClose(frame.isEndStream(), true);
stream.setListener(listener);
FlusherEntry entry = new FlusherEntry(stream, frame, new PromiseCallback<>(promise, stream));
flusher.append(entry);
}
// Iterate outside the synchronized block.
flusher.iterate();
}
@Override
public void settings(SettingsFrame frame, Callback callback)
{
control(null, frame, callback);
}
@Override
public void ping(PingFrame frame, Callback callback)
{
if (frame.isReply())
callback.failed(new IllegalArgumentException());
else
control(null, frame, callback);
}
@Override
public void reset(ResetFrame frame, Callback callback)
{
if (closed.get())
callback.succeeded();
else
control(getStream(frame.getStreamId()), frame, callback);
}
@Override
public void close(int error, String reason, Callback callback)
{
if (closed.compareAndSet(false, true))
{
byte[] payload = reason == null ? null : reason.getBytes(StandardCharsets.UTF_8);
GoAwayFrame frame = new GoAwayFrame(lastStreamId.get(), error, payload);
if (LOG.isDebugEnabled())
LOG.debug("Sending {}: {}", frame.getType(), reason);
control(null, frame, callback);
}
}
@Override
public void control(IStream stream, Frame frame, Callback callback)
{
// We want to generate as late as possible to allow re-prioritization.
frame(new FlusherEntry(stream, frame, callback));
}
@Override
public void data(IStream stream, DataFrame frame, Callback callback)
{
// We want to generate as late as possible to allow re-prioritization.
frame(new DataFlusherEntry(stream, frame, callback));
}
private void frame(FlusherEntry entry)
{
if (LOG.isDebugEnabled())
LOG.debug("Sending {}", entry.frame);
// Ping frames are prepended to process them as soon as possible.
if (entry.frame.getType() == FrameType.PING)
flusher.prepend(entry);
else
flusher.append(entry);
flusher.iterate();
}
protected IStream createLocalStream(HeadersFrame frame)
{
IStream stream = newStream(frame);
int streamId = stream.getId();
if (streams.putIfAbsent(streamId, stream) == null)
{
stream.setIdleTimeout(endPoint.getIdleTimeout());
flowControl.onNewStream(stream);
if (LOG.isDebugEnabled())
LOG.debug("Created local {}", stream);
return stream;
}
else
{
return null;
}
}
protected IStream createRemoteStream(HeadersFrame frame)
{
int streamId = frame.getStreamId();
// SPEC: exceeding max concurrent streams is treated as stream error.
while (true)
{
int currentStreams = streamCount.get();
int maxStreams = maxStreamCount;
if (maxStreams >= 0 && currentStreams >= maxStreams)
{
reset(new ResetFrame(streamId, ErrorCode.PROTOCOL_ERROR), disconnectOnFailure());
return null;
}
if (streamCount.compareAndSet(currentStreams, currentStreams + 1))
break;
}
IStream stream = newStream(frame);
// SPEC: duplicate stream is treated as connection error.
if (streams.putIfAbsent(streamId, stream) == null)
{
updateLastStreamId(streamId);
stream.setIdleTimeout(endPoint.getIdleTimeout());
flowControl.onNewStream(stream);
if (LOG.isDebugEnabled())
LOG.debug("Created remote {}", stream);
return stream;
}
else
{
close(ErrorCode.PROTOCOL_ERROR, "duplicate_stream", disconnectOnFailure());
return null;
}
}
protected IStream newStream(HeadersFrame frame)
{
return new HTTP2Stream(scheduler, this, frame);
}
protected void removeStream(IStream stream, boolean local)
{
IStream removed = streams.remove(stream.getId());
if (removed != null)
{
assert removed == stream;
if (local)
streamCount.decrementAndGet();
if (LOG.isDebugEnabled())
LOG.debug("Removed {}", stream);
}
}
@Override
public Collection<Stream> getStreams()
{
List<Stream> result = new ArrayList<>();
result.addAll(streams.values());
return result;
}
public IStream getStream(int streamId)
{
return streams.get(streamId);
}
protected int getWindowSize()
{
return windowSize.get();
}
@Override
public int updateWindowSize(int delta)
{
return windowSize.getAndAdd(delta);
}
@Override
public void shutdown()
{
if (LOG.isDebugEnabled())
LOG.debug("Shutting down");
// Append a fake FlusherEntry that disconnects when the queue is drained.
flusher.append(new ShutdownFlusherEntry());
flusher.iterate();
}
public void disconnect()
{
if (LOG.isDebugEnabled())
LOG.debug("Disconnecting");
endPoint.close();
}
private void updateLastStreamId(int streamId)
{
Atomics.updateMax(lastStreamId, streamId);
}
protected Callback disconnectOnFailure()
{
return disconnectOnFailure;
}
protected Stream.Listener notifyNewStream(Stream stream, HeadersFrame frame)
{
try
{
return listener.onNewStream(stream, frame);
}
catch (Throwable x)
{
LOG.info("Failure while notifying listener " + listener, x);
return null;
}
}
protected void notifySettings(Session session, SettingsFrame frame)
{
try
{
listener.onSettings(session, frame);
}
catch (Throwable x)
{
LOG.info("Failure while notifying listener " + listener, x);
}
}
protected void notifyPing(Session session, PingFrame frame)
{
try
{
listener.onPing(session, frame);
}
catch (Throwable x)
{
LOG.info("Failure while notifying listener " + listener, x);
}
}
protected void notifyReset(Session session, ResetFrame frame)
{
try
{
listener.onReset(session, frame);
}
catch (Throwable x)
{
LOG.info("Failure while notifying listener " + listener, x);
}
}
protected void notifyClose(Session session, GoAwayFrame frame)
{
try
{
listener.onClose(session, frame);
}
catch (Throwable x)
{
LOG.info("Failure while notifying listener " + listener, x);
}
}
@Override
public String toString()
{
return String.format("%s@%x{queueSize=%d,windowSize=%s,streams=%d}", getClass().getSimpleName(),
hashCode(), flusher.getQueueSize(), windowSize, streams.size());
}
private class Flusher extends IteratingCallback
{
private final ArrayQueue<FlusherEntry> queue = new ArrayQueue<>(ArrayQueue.DEFAULT_CAPACITY, ArrayQueue.DEFAULT_GROWTH);
private final Map<IStream, Integer> streams = new HashMap<>();
private final List<FlusherEntry> reset = new ArrayList<>();
private final ByteBufferPool.Lease lease = new ByteBufferPool.Lease(generator.getByteBufferPool());
private final int maxGather;
private final List<FlusherEntry> active;
private Flusher(int maxGather)
{
this.maxGather = maxGather;
this.active = new ArrayList<>(maxGather);
}
private void append(FlusherEntry entry)
{
boolean fail = false;
synchronized (queue)
{
if (isClosed())
fail = true;
else
queue.offer(entry);
if (LOG.isDebugEnabled())
LOG.debug("Appended {}, queue={}", entry, queue.size());
}
if (fail)
closed(entry);
}
private void prepend(FlusherEntry entry)
{
boolean fail = false;
synchronized (queue)
{
if (isClosed())
fail = true;
else
queue.add(0, entry);
}
if (fail)
closed(entry);
}
private int getQueueSize()
{
synchronized (queue)
{
return queue.size();
}
}
@Override
protected Action process() throws Exception
{
synchronized (queue)
{
int sessionWindow = getWindowSize();
int nonStalledIndex = 0;
int size = queue.size();
while (nonStalledIndex < size)
{
FlusherEntry entry = queue.get(nonStalledIndex);
IStream stream = entry.stream;
int remaining = 0;
if (entry.frame instanceof DataFrame)
{
DataFrame dataFrame = (DataFrame)entry.frame;
remaining = dataFrame.remaining();
if (remaining > 0)
{
// Is the session stalled ?
if (sessionWindow <= 0)
{
flowControl.onSessionStalled(HTTP2Session.this);
++nonStalledIndex;
// There may be *non* flow controlled frames to send.
continue;
}
if (stream != null)
{
Integer streamWindow = streams.get(stream);
if (streamWindow == null)
{
streamWindow = stream.getWindowSize();
streams.put(stream, streamWindow);
}
// Is it a frame belonging to an already stalled stream ?
if (streamWindow <= 0)
{
flowControl.onStreamStalled(stream);
++nonStalledIndex;
continue;
}
}
}
}
// We will be possibly writing this frame.
queue.remove(nonStalledIndex);
--size;
// If the stream has been reset, don't send flow controlled frames.
if (stream != null && stream.isReset() && remaining > 0)
{
reset.add(entry);
continue;
}
// Reduce the flow control windows.
sessionWindow -= remaining;
if (stream != null && remaining > 0)
streams.put(stream, streams.get(stream) - remaining);
active.add(entry);
if (active.size() == maxGather)
break;
}
streams.clear();
}
for (int i = 0; i < reset.size(); ++i)
{
FlusherEntry entry = reset.get(i);
entry.reset();
}
reset.clear();
if (active.isEmpty())
return Action.IDLE;
for (int i = 0; i < active.size(); ++i)
{
FlusherEntry entry = active.get(i);
entry.generate(lease);
}
List<ByteBuffer> byteBuffers = lease.getByteBuffers();
if (LOG.isDebugEnabled())
LOG.debug("Writing {} buffers ({} bytes) for {}", byteBuffers.size(), lease.getTotalLength(), active);
endPoint.write(this, byteBuffers.toArray(new ByteBuffer[byteBuffers.size()]));
return Action.SCHEDULED;
}
@Override
public void succeeded()
{
lease.recycle();
for (int i = 0; i < active.size(); ++i)
{
FlusherEntry entry = active.get(i);
entry.succeeded();
}
active.clear();
super.succeeded();
}
@Override
protected void onCompleteSuccess()
{
throw new IllegalStateException();
}
@Override
protected void onCompleteFailure(Throwable x)
{
LOG.debug(x);
lease.recycle();
for (int i = 0; i < active.size(); ++i)
{
FlusherEntry entry = active.get(i);
entry.failed(x);
}
active.clear();
}
public void close()
{
Queue<FlusherEntry> queued;
synchronized (queue)
{
super.close();
queued = new ArrayDeque<>(queue);
}
if (LOG.isDebugEnabled())
LOG.debug("Closing, queued={}", queued.size());
for (FlusherEntry item: queued)
closed(item);
}
protected void closed(FlusherEntry item)
{
item.failed(new ClosedChannelException());
}
}
private class FlusherEntry implements Callback
{
protected final IStream stream;
protected final Frame frame;
protected final Callback callback;
private FlusherEntry(IStream stream, Frame frame, Callback callback)
{
this.stream = stream;
this.frame = frame;
this.callback = callback;
}
public void generate(ByteBufferPool.Lease lease)
{
try
{
generator.control(lease, frame);
if (LOG.isDebugEnabled())
LOG.debug("Generated {}", frame);
}
catch (Throwable x)
{
LOG.debug("Frame generation failure", x);
failed(x);
}
}
public void reset()
{
callback.failed(new EOFException("reset"));
}
@Override
public void succeeded()
{
switch (frame.getType())
{
case RST_STREAM:
{
if (stream != null)
removeStream(stream, true);
break;
}
case GO_AWAY:
{
disconnect();
break;
}
default:
{
break;
}
}
callback.succeeded();
}
@Override
public void failed(Throwable x)
{
if (stream != null)
stream.close();
close(ErrorCode.INTERNAL_ERROR, "generator_error", Adapter.INSTANCE);
callback.failed(x);
}
@Override
public String toString()
{
return frame.toString();
}
}
private class DataFlusherEntry extends FlusherEntry
{
private int length;
private DataFlusherEntry(IStream stream, DataFrame frame, Callback callback)
{
super(stream, frame, callback);
}
public void generate(ByteBufferPool.Lease lease)
{
DataFrame dataFrame = (DataFrame)frame;
int windowSize = stream.getWindowSize();
int frameLength = dataFrame.remaining();
this.length = Math.min(frameLength, windowSize);
generator.data(lease, dataFrame, length);
if (LOG.isDebugEnabled())
LOG.debug("Generated {}, maxLength={}", dataFrame, length);
}
@Override
public void succeeded()
{
flowControl.onDataSent(HTTP2Session.this, stream, length);
// Do we have more to send ?
DataFrame dataFrame = (DataFrame)frame;
if (dataFrame.remaining() > 0)
{
// We have written part of the frame, but there is more to write.
// We need to keep the correct ordering of frames, to avoid that other
// frames for the same stream are written before this one is finished.
flusher.prepend(this);
}
else
{
// Only now we can update the close state
// and eventually remove the stream.
stream.updateClose(dataFrame.isEndStream(), true);
if (stream.isClosed())
removeStream(stream, true);
callback.succeeded();
}
}
}
private class ShutdownFlusherEntry extends FlusherEntry
{
public ShutdownFlusherEntry()
{
super(null, null, Adapter.INSTANCE);
}
@Override
public void generate(ByteBufferPool.Lease lease)
{
}
@Override
public void succeeded()
{
flusher.close();
disconnect();
}
@Override
public void failed(Throwable x)
{
flusher.close();
disconnect();
}
@Override
public String toString()
{
return String.format("%s@%x", "ShutdownFrame", hashCode());
}
}
private class PromiseCallback<C> implements Callback
{
private final Promise<C> promise;
private final C value;
private PromiseCallback(Promise<C> promise, C value)
{
this.promise = promise;
this.value = value;
}
@Override
public void succeeded()
{
promise.succeeded(value);
}
@Override
public void failed(Throwable x)
{
promise.failed(x);
}
}
}

View File

@ -0,0 +1,300 @@
//
// ========================================================================
// 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.http2;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.Frame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.parser.ErrorCode;
import org.eclipse.jetty.io.IdleTimeout;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
public class HTTP2Stream extends IdleTimeout implements IStream
{
private static final Logger LOG = Log.getLogger(HTTP2Stream.class);
private final Callback disconnectOnFailure = new Callback.Adapter()
{
@Override
public void failed(Throwable x)
{
session.disconnect();
}
};
private final AtomicReference<ConcurrentMap<String, Object>> attributes = new AtomicReference<>();
private final AtomicReference<CloseState> closeState = new AtomicReference<>(CloseState.NOT_CLOSED);
private final AtomicInteger windowSize = new AtomicInteger();
private final ISession session;
private final HeadersFrame frame;
private volatile Listener listener;
private volatile boolean reset;
public HTTP2Stream(Scheduler scheduler, ISession session, HeadersFrame frame)
{
super(scheduler);
this.session = session;
this.frame = frame;
}
@Override
public int getId()
{
return frame.getStreamId();
}
@Override
public ISession getSession()
{
return session;
}
@Override
public void headers(HeadersFrame frame, Callback callback)
{
session.control(this, frame, callback);
}
@Override
public void data(DataFrame frame, Callback callback)
{
session.data(this, frame, callback);
}
@Override
public Object getAttribute(String key)
{
return attributes().get(key);
}
@Override
public void setAttribute(String key, Object value)
{
attributes().put(key, value);
}
@Override
public Object removeAttribute(String key)
{
return attributes().remove(key);
}
@Override
public boolean isReset()
{
return reset;
}
@Override
public boolean isClosed()
{
return closeState.get() == CloseState.CLOSED;
}
@Override
public boolean isOpen()
{
return !isClosed();
}
@Override
protected void onIdleExpired(TimeoutException timeout)
{
if (LOG.isDebugEnabled())
LOG.debug("Idle timeout {}ms expired on {}", getIdleTimeout(), this);
// The stream is now gone, we must close it to
// avoid that its idle timeout is rescheduled.
close();
session.reset(new ResetFrame(getId(), ErrorCode.CANCEL_STREAM_ERROR), disconnectOnFailure);
notifyFailure(this, timeout);
}
private ConcurrentMap<String, Object> attributes()
{
ConcurrentMap<String, Object> map = attributes.get();
if (map == null)
{
map = new ConcurrentHashMap<>();
if (!attributes.compareAndSet(null, map))
{
map = attributes.get();
}
}
return map;
}
@Override
public Listener getListener()
{
return listener;
}
@Override
public void setListener(Listener listener)
{
this.listener = listener;
}
@Override
public boolean process(Frame frame, Callback callback)
{
notIdle();
switch (frame.getType())
{
case DATA:
{
// TODO: handle cases where:
// TODO: A) stream already remotely close.
// TODO: B) DATA before HEADERS.
notifyData(this, (DataFrame)frame, callback);
return false;
}
case HEADERS:
{
// TODO: handle case where HEADERS after DATA.
return false;
}
case RST_STREAM:
{
reset = true;
return false;
}
default:
{
throw new UnsupportedOperationException();
}
}
}
@Override
public void updateClose(boolean update, boolean local)
{
if (LOG.isDebugEnabled())
LOG.debug("Update close for {} close={} local={}", this, update, local);
if (!update)
return;
while (true)
{
CloseState current = closeState.get();
switch (current)
{
case NOT_CLOSED:
{
CloseState newValue = local ? CloseState.LOCALLY_CLOSED : CloseState.REMOTELY_CLOSED;
if (closeState.compareAndSet(current, newValue))
return;
break;
}
case LOCALLY_CLOSED:
{
if (!local)
close();
return;
}
case REMOTELY_CLOSED:
{
if (local)
close();
return;
}
default:
{
return;
}
}
}
}
@Override
public int getWindowSize()
{
return windowSize.get();
}
@Override
public int updateWindowSize(int delta)
{
return windowSize.getAndAdd(delta);
}
@Override
public void close()
{
closeState.set(CloseState.CLOSED);
onClose();
}
protected void notifyData(Stream stream, DataFrame frame, Callback callback)
{
final Listener listener = this.listener;
if (listener == null)
return;
try
{
listener.onData(stream, frame, callback);
}
catch (Throwable x)
{
LOG.info("Failure while notifying listener " + listener, x);
}
}
private void notifyFailure(Stream stream, Throwable failure)
{
Listener listener = this.listener;
if (listener == null)
return;
try
{
listener.onFailure(stream, failure);
}
catch (Throwable x)
{
LOG.info("Failure while notifying listener " + listener, x);
}
}
@Override
public String toString()
{
return String.format("%s@%x{id=%d,windowSize=%s,reset=%b,%s}", getClass().getSimpleName(),
hashCode(), getId(), windowSize, reset, closeState);
}
private enum CloseState
{
NOT_CLOSED, LOCALLY_CLOSED, REMOTELY_CLOSED, CLOSED
}
}

View File

@ -0,0 +1,40 @@
//
// ========================================================================
// 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.http2;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.Frame;
import org.eclipse.jetty.util.Callback;
public interface ISession extends Session
{
@Override
IStream getStream(int streamId);
public void control(IStream stream, Frame frame, Callback callback);
public void data(IStream stream, DataFrame frame, Callback callback);
public int updateWindowSize(int delta);
public void shutdown();
public void disconnect();
}

View File

@ -0,0 +1,52 @@
//
// ========================================================================
// 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.http2;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.Frame;
import org.eclipse.jetty.util.Callback;
public interface IStream extends Stream
{
@Override
public ISession getSession();
public Listener getListener();
public void setListener(Listener listener);
public boolean process(Frame frame, Callback callback);
/**
* Updates the close state of this stream.
*
* @param update whether to update the close state
* @param local whether the update comes from a local operation
* (such as sending a frame that ends the stream)
* or a remote operation (such as receiving a frame
* that ends the stream).
*/
public void updateClose(boolean update, boolean local);
public int getWindowSize();
public int updateWindowSize(int delta);
public void close();
}

View File

@ -0,0 +1,97 @@
//
// ========================================================================
// 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.http2.api;
import java.util.Collection;
import org.eclipse.jetty.http2.frames.GoAwayFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.PingFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
public interface Session
{
public void newStream(HeadersFrame frame, Promise<Stream> promise, Stream.Listener listener);
public void settings(SettingsFrame frame, Callback callback);
public void ping(PingFrame frame, Callback callback);
public void reset(ResetFrame frame, Callback callback);
public void close(int error, String payload, Callback callback);
public Collection<Stream> getStreams();
public Stream getStream(int streamId);
// TODO: remote and local address, etc. see SPDY's Session
public interface Listener
{
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame);
public void onSettings(Session session, SettingsFrame frame);
public void onPing(Session session, PingFrame frame);
public void onReset(Session session, ResetFrame frame);
public void onClose(Session session, GoAwayFrame frame);
public void onFailure(Session session, Throwable failure);
public static class Adapter implements Session.Listener
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
return null;
}
@Override
public void onSettings(Session session, SettingsFrame frame)
{
}
@Override
public void onPing(Session session, PingFrame frame)
{
}
@Override
public void onReset(Session session, ResetFrame frame)
{
}
@Override
public void onClose(Session session, GoAwayFrame frame)
{
}
@Override
public void onFailure(Session session, Throwable failure)
{
}
}
}
}

View File

@ -0,0 +1,81 @@
//
// ========================================================================
// 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.http2.api;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
public interface Stream
{
public int getId();
public Session getSession();
public void headers(HeadersFrame frame, Callback callback);
public void data(DataFrame frame, Callback callback);
public Object getAttribute(String key);
public void setAttribute(String key, Object value);
public Object removeAttribute(String key);
public boolean isReset();
public boolean isClosed();
public long getIdleTimeout();
public void setIdleTimeout(long idleTimeout);
// TODO: see SPDY's Stream
public interface Listener
{
public void onHeaders(Stream stream, HeadersFrame frame);
public void onData(Stream stream, DataFrame frame, Callback callback);
// TODO: is this method needed ?
public void onFailure(Stream stream, Throwable x);
// TODO: See SPDY's StreamFrameListener
public static class Adapter implements Listener
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
}
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
}
@Override
public void onFailure(Stream stream, Throwable x)
{
}
}
}
}

View File

@ -0,0 +1,44 @@
//
// ========================================================================
// 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.http2.api.server;
import java.util.Map;
import org.eclipse.jetty.http2.api.Session;
public interface ServerSessionListener extends Session.Listener
{
public void onConnect(Session session);
public Map<Integer,Integer> onPreface(Session session);
public static class Adapter extends Session.Listener.Adapter implements ServerSessionListener
{
@Override
public void onConnect(Session session)
{
}
@Override
public Map<Integer, Integer> onPreface(Session session)
{
return null;
}
}
}

View File

@ -0,0 +1,62 @@
//
// ========================================================================
// 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.http2.frames;
import java.nio.ByteBuffer;
public class DataFrame extends Frame
{
private final int streamId;
private final ByteBuffer data;
private final boolean endStream;
public DataFrame(int streamId, ByteBuffer data, boolean endStream)
{
super(FrameType.DATA);
this.streamId = streamId;
this.data = data;
this.endStream = endStream;
}
public int getStreamId()
{
return streamId;
}
public ByteBuffer getData()
{
return data;
}
public boolean isEndStream()
{
return endStream;
}
public int remaining()
{
return data.remaining();
}
@Override
public String toString()
{
return String.format("%s{length:%d,end=%b}", super.toString(), data.remaining(), endStream);
}
}

View File

@ -0,0 +1,32 @@
//
// ========================================================================
// 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.http2.frames;
public interface Flag
{
public static final int NONE = 0x00;
public static final int END_STREAM = 0x01;
public static final int ACK = END_STREAM;
public static final int END_SEGMENT = 0x02;
public static final int END_HEADERS = 0x04;
public static final int PADDING_LOW = 0x08;
public static final int PADDING_HIGH = 0x10;
public static final int COMPRESS = 0x20;
public static final int PRIORITY = COMPRESS;
}

View File

@ -0,0 +1,43 @@
//
// ========================================================================
// 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.http2.frames;
public abstract class Frame
{
public static final int HEADER_LENGTH = 8;
public static final int MAX_LENGTH = 0x3F_FF;
private final FrameType type;
protected Frame(FrameType type)
{
this.type = type;
}
public FrameType getType()
{
return type;
}
@Override
public String toString()
{
return String.format("%s@%x", getClass().getSimpleName(), hashCode());
}
}

View File

@ -0,0 +1,61 @@
//
// ========================================================================
// 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.http2.frames;
import java.util.HashMap;
import java.util.Map;
public enum FrameType
{
DATA(0),
HEADERS(1),
PRIORITY(2),
RST_STREAM(3),
SETTINGS(4),
PUSH_PROMISE(5),
PING(6),
GO_AWAY(7),
WINDOW_UPDATE(8),
CONTINUATION(9),
ALTSVC(10),
BLOCKED(11);
public static FrameType from(int type)
{
return Types.types.get(type);
}
private final int type;
private FrameType(int type)
{
this.type = type;
Types.types.put(type, this);
}
public int getType()
{
return type;
}
private static class Types
{
private static final Map<Integer, FrameType> types = new HashMap<>();
}
}

View File

@ -0,0 +1,49 @@
//
// ========================================================================
// 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.http2.frames;
public class GoAwayFrame extends Frame
{
private final int lastStreamId;
private final int error;
private final byte[] payload;
public GoAwayFrame(int lastStreamId, int error, byte[] payload)
{
super(FrameType.GO_AWAY);
this.lastStreamId = lastStreamId;
this.error = error;
this.payload = payload;
}
public int getLastStreamId()
{
return lastStreamId;
}
public int getError()
{
return error;
}
public byte[] getPayload()
{
return payload;
}
}

View File

@ -0,0 +1,58 @@
//
// ========================================================================
// 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.http2.frames;
import org.eclipse.jetty.http.MetaData;
public class HeadersFrame extends Frame
{
private final int streamId;
private final MetaData metaData;
private final PriorityFrame priority;
private final boolean endStream;
public HeadersFrame(int streamId, MetaData metaData, PriorityFrame priority, boolean endStream)
{
super(FrameType.HEADERS);
this.streamId = streamId;
this.metaData = metaData;
this.priority = priority;
this.endStream = endStream;
}
public int getStreamId()
{
return streamId;
}
public MetaData getMetaData()
{
return metaData;
}
public PriorityFrame getPriority()
{
return priority;
}
public boolean isEndStream()
{
return endStream;
}
}

View File

@ -0,0 +1,42 @@
//
// ========================================================================
// 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.http2.frames;
public class PingFrame extends Frame
{
private final byte[] payload;
private final boolean reply;
public PingFrame(byte[] payload, boolean reply)
{
super(FrameType.PING);
this.payload = payload;
this.reply = reply;
}
public byte[] getPayload()
{
return payload;
}
public boolean isReply()
{
return reply;
}
}

View File

@ -0,0 +1,56 @@
//
// ========================================================================
// 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.http2.frames;
public class PriorityFrame extends Frame
{
private final int streamId;
private final int dependentStreamId;
private final int weight;
private final boolean exclusive;
public PriorityFrame(int streamId, int dependentStreamId, int weight, boolean exclusive)
{
super(FrameType.PRIORITY);
this.streamId = streamId;
this.dependentStreamId = dependentStreamId;
this.weight = weight;
this.exclusive = exclusive;
}
public int getStreamId()
{
return streamId;
}
public int getDependentStreamId()
{
return dependentStreamId;
}
public int getWeight()
{
return weight;
}
public boolean isExclusive()
{
return exclusive;
}
}

View File

@ -0,0 +1,51 @@
//
// ========================================================================
// 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.http2.frames;
import org.eclipse.jetty.http.MetaData;
public class PushPromiseFrame extends Frame
{
private final int streamId;
private final int promisedStreamId;
private final MetaData metaData;
public PushPromiseFrame(int streamId, int promisedStreamId, MetaData metaData)
{
super(FrameType.PUSH_PROMISE);
this.streamId = streamId;
this.promisedStreamId = promisedStreamId;
this.metaData = metaData;
}
public int getStreamId()
{
return streamId;
}
public int getPromisedStreamId()
{
return promisedStreamId;
}
public MetaData getMetaData()
{
return metaData;
}
}

View File

@ -0,0 +1,42 @@
//
// ========================================================================
// 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.http2.frames;
public class ResetFrame extends Frame
{
private final int streamId;
private final int error;
public ResetFrame(int streamId, int error)
{
super(FrameType.RST_STREAM);
this.streamId = streamId;
this.error = error;
}
public int getStreamId()
{
return streamId;
}
public int getError()
{
return error;
}
}

View File

@ -0,0 +1,49 @@
//
// ========================================================================
// 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.http2.frames;
import java.util.Map;
public class SettingsFrame extends Frame
{
public static final int HEADER_TABLE_SIZE = 1;
public static final int ENABLE_PUSH = 2;
public static final int MAX_CONCURRENT_STREAMS = 3;
public static final int INITIAL_WINDOW_SIZE = 4;
private final Map<Integer, Integer> settings;
private final boolean reply;
public SettingsFrame(Map<Integer, Integer> settings, boolean reply)
{
super(FrameType.SETTINGS);
this.settings = settings;
this.reply = reply;
}
public Map<Integer, Integer> getSettings()
{
return settings;
}
public boolean isReply()
{
return reply;
}
}

View File

@ -16,38 +16,33 @@
// ========================================================================
//
package org.eclipse.jetty.server;
package org.eclipse.jetty.http2.frames;
import java.nio.ByteBuffer;
/**
* <p>An implementation of HttpInput using {@link ByteBuffer} as items.</p>
*/
public class ByteBufferQueuedHttpInput extends QueuedHttpInput<ByteBuffer>
public class WindowUpdateFrame extends Frame
{
@Override
protected int remaining(ByteBuffer item)
private final int streamId;
private final int windowDelta;
public WindowUpdateFrame(int streamId, int windowDelta)
{
return item.remaining();
super(FrameType.WINDOW_UPDATE);
this.streamId = streamId;
this.windowDelta = windowDelta;
}
public int getStreamId()
{
return streamId;
}
public int getWindowDelta()
{
return windowDelta;
}
@Override
protected int get(ByteBuffer item, byte[] buffer, int offset, int length)
public String toString()
{
int l = Math.min(item.remaining(), length);
item.get(buffer, offset, l);
return l;
return String.format("%s{delta=%d}", super.toString(), windowDelta);
}
@Override
protected void consume(ByteBuffer item, int length)
{
item.position(item.position()+length);
}
@Override
protected void onContentConsumed(ByteBuffer item)
{
}
}

Some files were not shown because too many files have changed in this diff Show More