Merge remote-tracking branch 'origin/jetty-http2' into pathresource
This commit is contained in:
commit
fa9b60c77e
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -57,7 +59,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Switching Protocols</td>
|
||||
* <td> </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> </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> </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> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -111,7 +113,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Non Authoritative Information</td>
|
||||
* <td> </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> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -130,7 +132,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Reset Content</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -139,7 +141,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Partial Content</td>
|
||||
* <td> </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> </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> </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> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -212,7 +214,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>See Other</td>
|
||||
* <td> </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> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -231,7 +233,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Use Proxy</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -240,7 +242,7 @@ package org.eclipse.jetty.http;
|
|||
* <td><em>(Unused)</em></td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -249,7 +251,17 @@ package org.eclipse.jetty.http;
|
|||
* <td>Temporary Redirect</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
*
|
||||
* <tr>
|
||||
* <td>{@link #PERMANENT_REDIRECT_308}</td>
|
||||
* <td>307</td>
|
||||
* <td>Permanent Redirect</td>
|
||||
* <td> </td>
|
||||
* <td>
|
||||
* <a href="http://tools.ietf.org/html/rfc7238">RFC7238</a></td>
|
||||
* <td> </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> </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> </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> </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> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -314,7 +326,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Method Not Allowed</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -323,7 +335,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Not Acceptable</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -332,7 +344,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Proxy Authentication Required</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -341,7 +353,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Request Timeout</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -350,7 +362,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Conflict</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
|
@ -360,7 +372,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Gone</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
|
@ -370,7 +382,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Length Required</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
|
@ -380,7 +392,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Precondition Failed</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
|
@ -390,7 +402,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Request Entity Too Large</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
|
@ -400,7 +412,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Request-URI Too Long</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
|
@ -410,7 +422,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Unsupported Media Type</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
|
@ -420,7 +432,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Requested Range Not Satisfiable</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
|
@ -430,7 +442,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Expectation Failed</td>
|
||||
* <td> </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> </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> </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> </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> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -576,7 +588,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>Gateway Timeout</td>
|
||||
* <td> </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> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -585,7 +597,7 @@ package org.eclipse.jetty.http;
|
|||
* <td>HTTP Version Not Supported</td>
|
||||
* <td> </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> </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.
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]);
|
||||
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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<>();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue