Merge branch 'jetty-9.3.x' into bugs/388
This commit is contained in:
commit
dd8afc34b9
1100
VERSION.txt
1100
VERSION.txt
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.9-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.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-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.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-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.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>apache-jstl</artifactId>
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.jstl;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.IO;
|
||||
import org.eclipse.jetty.toolchain.test.JAR;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JspIncludeTest
|
||||
{
|
||||
private static Server server;
|
||||
private static URI baseUri;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
// Setup Server
|
||||
server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.addConnector(connector);
|
||||
|
||||
// Setup WebAppContext
|
||||
File testWebAppDir = MavenTestingUtils.getProjectDir("src/test/webapp");
|
||||
|
||||
// Prepare WebApp libs
|
||||
File libDir = new File(testWebAppDir, "WEB-INF/lib");
|
||||
FS.ensureDirExists(libDir);
|
||||
File testTagLibDir = MavenTestingUtils.getProjectDir("src/test/taglibjar");
|
||||
JAR.create(testTagLibDir, new File(libDir, "testtaglib.jar"));
|
||||
|
||||
// Configure WebAppContext
|
||||
|
||||
Configuration.ClassList classlist = Configuration.ClassList
|
||||
.setServerDefault(server);
|
||||
|
||||
classlist.addBefore(
|
||||
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
|
||||
"org.eclipse.jetty.annotations.AnnotationConfiguration");
|
||||
|
||||
WebAppContext context = new WebAppContext();
|
||||
context.setContextPath("/");
|
||||
|
||||
File scratchDir = MavenTestingUtils.getTargetFile("tests/" + JspIncludeTest.class.getSimpleName() + "-scratch");
|
||||
FS.ensureEmpty(scratchDir);
|
||||
JspConfig.init(context, testWebAppDir.toURI(), scratchDir);
|
||||
|
||||
server.setHandler(context);
|
||||
|
||||
// Start Server
|
||||
server.start();
|
||||
|
||||
// Figure out Base URI
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
baseUri = new URI(String.format("http://%s:%d/", host, port));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTopWithIncluded() throws IOException
|
||||
{
|
||||
URI uri = baseUri.resolve("/top.jsp");
|
||||
// System.out.println("GET (String): " + uri.toASCIIString());
|
||||
|
||||
InputStream in = null;
|
||||
InputStreamReader reader = null;
|
||||
HttpURLConnection connection = null;
|
||||
|
||||
try
|
||||
{
|
||||
connection = (HttpURLConnection) uri.toURL().openConnection();
|
||||
connection.connect();
|
||||
if (HttpURLConnection.HTTP_OK != connection.getResponseCode())
|
||||
{
|
||||
String body = getPotentialBody(connection);
|
||||
String err = String.format("GET request failed (%d %s) %s%n%s", connection.getResponseCode(), connection.getResponseMessage(),
|
||||
uri.toASCIIString(), body);
|
||||
throw new IOException(err);
|
||||
}
|
||||
in = connection.getInputStream();
|
||||
reader = new InputStreamReader(in);
|
||||
StringWriter writer = new StringWriter();
|
||||
IO.copy(reader, writer);
|
||||
|
||||
String response = writer.toString();
|
||||
// System.out.printf("Response%n%s",response);
|
||||
assertThat("Response", response, containsString("<h2> Hello, this is the top page."));
|
||||
assertThat("Response", response, containsString("<h3> This is the included page"));
|
||||
|
||||
assertThat("Response Header[main-page-key]", connection.getHeaderField("main-page-key"), is("main-page-value"));
|
||||
assertThat("Response Header[included-page-key]", connection.getHeaderField("included-page-key"), is("included-page-value"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
IO.close(reader);
|
||||
IO.close(in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to obtain the body text if available. Do not throw an exception if body is unable to be fetched.
|
||||
*
|
||||
* @param connection the connection to fetch the body content from.
|
||||
* @return the body content, if present.
|
||||
*/
|
||||
private String getPotentialBody(HttpURLConnection connection)
|
||||
{
|
||||
InputStream in = null;
|
||||
InputStreamReader reader = null;
|
||||
try
|
||||
{
|
||||
in = connection.getInputStream();
|
||||
reader = new InputStreamReader(in);
|
||||
StringWriter writer = new StringWriter();
|
||||
IO.copy(reader, writer);
|
||||
return writer.toString();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return "<no body:" + e.getMessage() + ">";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IO.close(reader);
|
||||
IO.close(in);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,16 +76,6 @@ public class JstlTest
|
|||
WebAppContext context = new WebAppContext();
|
||||
context.setContextPath("/");
|
||||
|
||||
/* TODO: Bug #486530 - sub-handler on WebAppContext prevents startup
|
||||
context.setHandler(new AbstractHandler()
|
||||
{
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
File scratchDir = MavenTestingUtils.getTargetFile("tests/" + JstlTest.class.getSimpleName() + "-scratch");
|
||||
FS.ensureEmpty(scratchDir);
|
||||
JspConfig.init(context,testWebAppDir.toURI(),scratchDir);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<%
|
||||
String headerPrefix = "";
|
||||
if(request.getDispatcherType() == DispatcherType.INCLUDE)
|
||||
headerPrefix = "org.eclipse.jetty.server.include.";
|
||||
|
||||
response.setHeader(headerPrefix + "included-page-key","included-page-value");
|
||||
%>
|
||||
<h3> This is the included page
|
|
@ -0,0 +1,5 @@
|
|||
<%
|
||||
application.getRequestDispatcher("/included.jsp").include(request,response);
|
||||
response.setHeader("main-page-key", "main-page-value");
|
||||
%>
|
||||
<h2> Hello, this is the top page.
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>example-async-rest</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-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.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-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.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-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.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -40,6 +40,11 @@
|
|||
<artifactId>jetty-deploy</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-rewrite</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-jmx</artifactId>
|
||||
|
|
|
@ -128,7 +128,7 @@ public class FastFileServer
|
|||
// Jetty DefaultServlet will cache formatted date strings, but we
|
||||
// will reformat for each request here
|
||||
response.setDateHeader("Last-Modified", file.lastModified());
|
||||
response.setDateHeader("Content-Length", file.length());
|
||||
response.setContentLengthLong(file.length());
|
||||
response.setContentType(mimeTypes.getMimeByExtension(file.getName()));
|
||||
|
||||
// send "small" files blocking directly from an input stream
|
||||
|
|
|
@ -179,7 +179,9 @@ public class Http2Server
|
|||
content+="session="+session.getId()+(session.isNew()?"(New)\n":"\n");
|
||||
content+="date="+new Date()+"\n";
|
||||
|
||||
for (Cookie c : request.getCookies())
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies!=null && cookies.length>0)
|
||||
for (Cookie c : cookies)
|
||||
content+="cookie "+c.getName()+"="+c.getValue()+"\n";
|
||||
|
||||
response.setContentLength(content.length());
|
||||
|
|
|
@ -28,7 +28,10 @@ import org.eclipse.jetty.deploy.bindings.DebugListenerBinding;
|
|||
import org.eclipse.jetty.deploy.providers.WebAppProvider;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.jmx.MBeanContainer;
|
||||
import org.eclipse.jetty.rewrite.handler.CompactPathRule;
|
||||
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
||||
import org.eclipse.jetty.security.HashLoginService;
|
||||
import org.eclipse.jetty.server.ConnectorStatistics;
|
||||
import org.eclipse.jetty.server.DebugListener;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
|
@ -62,11 +65,17 @@ public class LikeJettyXml
|
|||
|
||||
// Find jetty home and base directories
|
||||
String homePath = System.getProperty("jetty.home", jettyHomeBuild);
|
||||
File homeDir = new File(homePath);
|
||||
if (!homeDir.exists())
|
||||
File start_jar = new File(homePath,"start.jar");
|
||||
if (!start_jar.exists())
|
||||
{
|
||||
throw new FileNotFoundException(homeDir.getAbsolutePath());
|
||||
homePath = jettyHomeBuild = "jetty-distribution/target/distribution";
|
||||
start_jar = new File(homePath,"start.jar");
|
||||
if (!start_jar.exists())
|
||||
throw new FileNotFoundException(start_jar.toString());
|
||||
}
|
||||
|
||||
File homeDir = new File(homePath);
|
||||
|
||||
String basePath = System.getProperty("jetty.base", homeDir + "/demo-base");
|
||||
File baseDir = new File(basePath);
|
||||
if(!baseDir.exists())
|
||||
|
@ -185,7 +194,12 @@ public class LikeJettyXml
|
|||
StatisticsHandler stats = new StatisticsHandler();
|
||||
stats.setHandler(server.getHandler());
|
||||
server.setHandler(stats);
|
||||
ConnectorStatistics.addToAllConnectors(server);
|
||||
|
||||
// === Rewrite Handler
|
||||
RewriteHandler rewrite = new RewriteHandler();
|
||||
rewrite.setHandler(server.getHandler());
|
||||
server.setHandler(rewrite);
|
||||
|
||||
// === jetty-requestlog.xml ===
|
||||
NCSARequestLog requestLog = new NCSARequestLog();
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.embedded;
|
||||
|
||||
import org.eclipse.jetty.http.HttpCompliance;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
||||
public class OneHandler
|
||||
|
@ -25,6 +27,7 @@ public class OneHandler
|
|||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
Server server = new Server(8080);
|
||||
server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class).setHttpCompliance(HttpCompliance.LEGACY);
|
||||
server.setHandler(new HelloHandler());
|
||||
|
||||
server.start();
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.embedded;
|
||||
|
||||
import org.eclipse.jetty.rewrite.RewriteCustomizer;
|
||||
import org.eclipse.jetty.rewrite.handler.CompactPathRule;
|
||||
import org.eclipse.jetty.rewrite.handler.RewriteRegexRule;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
|
||||
public class RewriteServer
|
||||
{
|
||||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
Server server = new Server(8080);
|
||||
|
||||
HttpConfiguration config=server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration();
|
||||
|
||||
RewriteCustomizer rewrite = new RewriteCustomizer();
|
||||
config.addCustomizer(rewrite);
|
||||
rewrite.addRule(new CompactPathRule());
|
||||
rewrite.addRule(new RewriteRegexRule("(.*)foo(.*)","$1FOO$2"));
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(
|
||||
ServletContextHandler.SESSIONS);
|
||||
context.setContextPath("/");
|
||||
server.setHandler(context);
|
||||
|
||||
context.addServlet(DumpServlet.class, "/*");
|
||||
|
||||
server.start();
|
||||
server.join();
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.eclipse.jetty.examples</groupId>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-parent</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-alpn-client</artifactId>
|
||||
|
|
|
@ -48,7 +48,8 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
|
|||
@Override
|
||||
public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
|
||||
{
|
||||
return new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
|
||||
ALPNClientConnection connection = new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
|
||||
(SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context, protocols);
|
||||
return customize(connection, context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-parent</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-alpn-server</artifactId>
|
||||
|
@ -25,6 +25,33 @@
|
|||
<onlyAnalyze>org.eclipse.jetty.alpn.*</onlyAnalyze>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>1.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>parse-version</id>
|
||||
<goals>
|
||||
<goal>parse-version</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<propertyPrefix>alpn</propertyPrefix>
|
||||
<versionString>${alpn.api.version}</versionString>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-SymbolicName>${bundle-symbolic-name};singleton:=true</Bundle-SymbolicName>
|
||||
<Import-Package>org.eclipse.jetty.alpn;version="${alpn.majorVersion}.${alpn.minorVersion}.${alpn.incrementalVersion}",*</Import-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[name]
|
||||
protonego-boot
|
||||
|
||||
[files]
|
||||
http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.7.v20160121/alpn-boot-8.1.7.v20160121.jar|lib/alpn/alpn-boot-8.1.7.v20160121.jar
|
||||
|
||||
[exec]
|
||||
-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.7.v20160121.jar
|
|
@ -0,0 +1,8 @@
|
|||
[name]
|
||||
protonego-boot
|
||||
|
||||
[files]
|
||||
http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.7.v20160121/alpn-boot-8.1.7.v20160121.jar|lib/alpn/alpn-boot-8.1.7.v20160121.jar
|
||||
|
||||
[exec]
|
||||
-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.7.v20160121.jar
|
|
@ -0,0 +1,8 @@
|
|||
[name]
|
||||
protonego-boot
|
||||
|
||||
[files]
|
||||
http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.8.v20160420/alpn-boot-8.1.8.v20160420.jar|lib/alpn/alpn-boot-8.1.8.v20160420.jar
|
||||
|
||||
[exec]
|
||||
-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.8.v20160420.jar
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-alpn-parent</artifactId>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-annotations</artifactId>
|
||||
|
|
|
@ -452,8 +452,6 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
if (initializers != null && initializers.size()>0)
|
||||
{
|
||||
Map<String, Set<String>> map = ( Map<String, Set<String>>) context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP);
|
||||
if (map == null)
|
||||
LOG.warn ("ServletContainerInitializers: detected. Class hierarchy: empty");
|
||||
for (ContainerInitializer i : initializers)
|
||||
i.resolveClasses(context,map);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-ant</artifactId>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty.cdi</groupId>
|
||||
<artifactId>jetty-cdi-parent</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cdi-core</artifactId>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty.cdi</groupId>
|
||||
<artifactId>jetty-cdi-parent</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cdi-full-servlet</artifactId>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty.cdi</groupId>
|
||||
<artifactId>jetty-cdi-parent</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cdi-servlet</artifactId>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
org.jboss.LEVEL=DEBUG
|
||||
# org.jboss.LEVEL=DEBUG
|
||||
org.eclipse.jetty.LEVEL=INFO
|
||||
|
||||
org.eclipse.jetty.util.DecoratedObjectFactory.LEVEL=DEBUG
|
||||
org.eclipse.jetty.cdi.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.util.DecoratedObjectFactory.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.cdi.LEVEL=DEBUG
|
||||
|
||||
# org.eclipse.jetty.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.LEVEL=DEBUG
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty.cdi</groupId>
|
||||
<artifactId>jetty-cdi-parent</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cdi-websocket</artifactId>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty.cdi</groupId>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty.cdi</groupId>
|
||||
<artifactId>jetty-cdi-parent</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>test-cdi-webapp</artifactId>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
|
@ -40,7 +41,7 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
{
|
||||
public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024;
|
||||
public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class);
|
||||
private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(.*)", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]*)\"(.*)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private final HttpClient client;
|
||||
private final int maxContentLength;
|
||||
|
@ -87,9 +88,8 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
ContentResponse response = new HttpContentResponse(result.getResponse(), getContent(), getMediaType(), getEncoding());
|
||||
if (result.isFailed())
|
||||
{
|
||||
Throwable failure = result.getFailure();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Authentication challenge failed {}", failure);
|
||||
LOG.debug("Authentication challenge failed {}", result.getFailure());
|
||||
forwardFailureComplete(request, result.getRequestFailure(), response, result.getResponseFailure());
|
||||
return;
|
||||
}
|
||||
|
@ -117,12 +117,12 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
|
||||
Authentication authentication = null;
|
||||
Authentication.HeaderInfo headerInfo = null;
|
||||
URI uri = getAuthenticationURI(request);
|
||||
if (uri != null)
|
||||
URI authURI = getAuthenticationURI(request);
|
||||
if (authURI != null)
|
||||
{
|
||||
for (Authentication.HeaderInfo element : headerInfos)
|
||||
{
|
||||
authentication = client.getAuthenticationStore().findAuthentication(element.getType(), uri, element.getRealm());
|
||||
authentication = client.getAuthenticationStore().findAuthentication(element.getType(), authURI, element.getRealm());
|
||||
if (authentication != null)
|
||||
{
|
||||
headerInfo = element;
|
||||
|
@ -151,14 +151,33 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
|
||||
conversation.setAttribute(authenticationAttribute, true);
|
||||
|
||||
Request newRequest = client.copyRequest(request, request.getURI());
|
||||
URI requestURI = request.getURI();
|
||||
String path = null;
|
||||
if (requestURI == null)
|
||||
{
|
||||
String uri = request.getScheme() + "://" + request.getHost();
|
||||
int port = request.getPort();
|
||||
if (port > 0)
|
||||
uri += ":" + port;
|
||||
requestURI = URI.create(uri);
|
||||
path = request.getPath();
|
||||
}
|
||||
Request newRequest = client.copyRequest(request, requestURI);
|
||||
if (path != null)
|
||||
newRequest.path(path);
|
||||
|
||||
authnResult.apply(newRequest);
|
||||
// Copy existing, explicitly set, authorization headers.
|
||||
copyIfAbsent(request, newRequest, HttpHeader.AUTHORIZATION);
|
||||
copyIfAbsent(request, newRequest, HttpHeader.PROXY_AUTHORIZATION);
|
||||
|
||||
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult))
|
||||
.send(null);
|
||||
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult));
|
||||
|
||||
Connection connection = (Connection)request.getAttributes().get(Connection.class.getName());
|
||||
if (connection != null)
|
||||
connection.send(newRequest, null);
|
||||
else
|
||||
newRequest.send(null);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
|
@ -186,7 +205,12 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
{
|
||||
HttpConversation conversation = request.getConversation();
|
||||
conversation.updateResponseListeners(null);
|
||||
notifier.forwardFailureComplete(conversation.getResponseListeners(), request, requestFailure, response, responseFailure);
|
||||
List<Response.ResponseListener> responseListeners = conversation.getResponseListeners();
|
||||
if (responseFailure == null)
|
||||
notifier.forwardSuccess(responseListeners, response);
|
||||
else
|
||||
notifier.forwardFailure(responseListeners, response, responseFailure);
|
||||
notifier.notifyComplete(responseListeners, new Result(request, requestFailure, response, responseFailure));
|
||||
}
|
||||
|
||||
private List<Authentication.HeaderInfo> parseAuthenticateHeader(Response response, HttpHeader header)
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||
import org.eclipse.jetty.client.util.AbstractAuthentication;
|
||||
|
||||
public class HttpAuthenticationStore implements AuthenticationStore
|
||||
{
|
||||
|
@ -85,7 +86,7 @@ public class HttpAuthenticationStore implements AuthenticationStore
|
|||
// TODO: I should match the longest URI
|
||||
for (Map.Entry<URI, Authentication.Result> entry : results.entrySet())
|
||||
{
|
||||
if (uri.toString().startsWith(entry.getKey().toString()))
|
||||
if (AbstractAuthentication.matchesURI(entry.getKey(), uri))
|
||||
return entry.getValue();
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.eclipse.jetty.http.HttpHeader;
|
|||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
import org.eclipse.jetty.util.Jetty;
|
||||
|
@ -408,9 +409,9 @@ public class HttpClient extends ContainerLifeCycle
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new request with the specified URI.
|
||||
* Creates a new request with the specified absolute URI in string format.
|
||||
*
|
||||
* @param uri the URI to request
|
||||
* @param uri the request absolute URI
|
||||
* @return the request just created
|
||||
*/
|
||||
public Request newRequest(String uri)
|
||||
|
@ -419,9 +420,9 @@ public class HttpClient extends ContainerLifeCycle
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new request with the specified URI.
|
||||
* Creates a new request with the specified absolute URI.
|
||||
*
|
||||
* @param uri the URI to request
|
||||
* @param uri the request absolute URI
|
||||
* @return the request just created
|
||||
*/
|
||||
public Request newRequest(URI uri)
|
||||
|
@ -490,6 +491,11 @@ public class HttpClient extends ContainerLifeCycle
|
|||
|
||||
protected HttpDestination destinationFor(String scheme, String host, int port)
|
||||
{
|
||||
if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme))
|
||||
throw new IllegalArgumentException("Invalid protocol " + scheme);
|
||||
|
||||
scheme = scheme.toLowerCase(Locale.ENGLISH);
|
||||
host = host.toLowerCase(Locale.ENGLISH);
|
||||
port = normalizePort(scheme, port);
|
||||
|
||||
Origin origin = new Origin(scheme, host, port);
|
||||
|
@ -524,17 +530,12 @@ public class HttpClient extends ContainerLifeCycle
|
|||
*/
|
||||
public List<Destination> getDestinations()
|
||||
{
|
||||
return new ArrayList<Destination>(destinations.values());
|
||||
return new ArrayList<>(destinations.values());
|
||||
}
|
||||
|
||||
protected void send(final HttpRequest request, List<Response.ResponseListener> listeners)
|
||||
{
|
||||
String scheme = request.getScheme().toLowerCase(Locale.ENGLISH);
|
||||
if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme))
|
||||
throw new IllegalArgumentException("Invalid protocol " + scheme);
|
||||
|
||||
String host = request.getHost().toLowerCase(Locale.ENGLISH);
|
||||
HttpDestination destination = destinationFor(scheme, host, request.getPort());
|
||||
HttpDestination destination = destinationFor(request.getScheme(), request.getHost(), request.getPort());
|
||||
destination.send(request, listeners);
|
||||
}
|
||||
|
||||
|
@ -547,6 +548,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
public void succeeded(List<InetSocketAddress> socketAddresses)
|
||||
{
|
||||
Map<String, Object> context = new HashMap<>();
|
||||
context.put(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY, HttpClient.this);
|
||||
context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, destination);
|
||||
connect(socketAddresses, 0, context);
|
||||
}
|
||||
|
@ -559,12 +561,12 @@ public class HttpClient extends ContainerLifeCycle
|
|||
|
||||
private void connect(List<InetSocketAddress> socketAddresses, int index, Map<String, Object> context)
|
||||
{
|
||||
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, new Promise<Connection>()
|
||||
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, new Promise.Wrapper<Connection>(promise)
|
||||
{
|
||||
@Override
|
||||
public void succeeded(Connection result)
|
||||
{
|
||||
promise.succeeded(result);
|
||||
getPromise().succeeded(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -572,7 +574,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
{
|
||||
int nextIndex = index + 1;
|
||||
if (nextIndex == socketAddresses.size())
|
||||
promise.failed(x);
|
||||
getPromise().failed(x);
|
||||
else
|
||||
connect(socketAddresses, nextIndex, context);
|
||||
}
|
||||
|
@ -1038,7 +1040,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
return host;
|
||||
}
|
||||
|
||||
protected int normalizePort(String scheme, int port)
|
||||
public static int normalizePort(String scheme, int port)
|
||||
{
|
||||
return port > 0 ? port : HttpScheme.HTTPS.is(scheme) ? 443 : 80;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.eclipse.jetty.http.HttpField;
|
|||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -89,7 +89,6 @@ public abstract class HttpConnection implements Connection
|
|||
|
||||
protected void normalizeRequest(Request request)
|
||||
{
|
||||
String method = request.getMethod();
|
||||
HttpVersion version = request.getVersion();
|
||||
HttpFields headers = request.getHeaders();
|
||||
ContentProvider content = request.getContent();
|
||||
|
@ -102,14 +101,17 @@ public abstract class HttpConnection implements Connection
|
|||
path = "/";
|
||||
request.path(path);
|
||||
}
|
||||
if (proxy != null && !HttpMethod.CONNECT.is(method))
|
||||
|
||||
URI uri = request.getURI();
|
||||
|
||||
if (proxy instanceof HttpProxy && !HttpScheme.HTTPS.is(request.getScheme()) && uri != null)
|
||||
{
|
||||
path = request.getURI().toString();
|
||||
path = uri.toString();
|
||||
request.path(path);
|
||||
}
|
||||
|
||||
// If we are HTTP 1.1, add the Host header
|
||||
if (version.getVersion() > 10)
|
||||
if (version.getVersion() == 11)
|
||||
{
|
||||
if (!headers.containsKey(HttpHeader.HOST.asString()))
|
||||
headers.put(getHttpDestination().getHostField());
|
||||
|
@ -144,7 +146,6 @@ public abstract class HttpConnection implements Connection
|
|||
CookieStore cookieStore = getHttpClient().getCookieStore();
|
||||
if (cookieStore != null)
|
||||
{
|
||||
URI uri = request.getURI();
|
||||
StringBuilder cookies = null;
|
||||
if (uri != null)
|
||||
cookies = convertCookies(cookieStore.get(uri), null);
|
||||
|
@ -155,7 +156,7 @@ public abstract class HttpConnection implements Connection
|
|||
|
||||
// Authentication
|
||||
applyAuthentication(request, proxy != null ? proxy.getURI() : null);
|
||||
applyAuthentication(request, request.getURI());
|
||||
applyAuthentication(request, uri);
|
||||
}
|
||||
|
||||
private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)
|
||||
|
|
|
@ -73,6 +73,8 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
|
|||
if (proxy != null)
|
||||
{
|
||||
connectionFactory = proxy.newClientConnectionFactory(connectionFactory);
|
||||
if (proxy.isSecure())
|
||||
connectionFactory = newSslClientConnectionFactory(connectionFactory);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -24,12 +24,15 @@ import java.util.Map;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.client.api.Destination;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
|
||||
|
@ -40,6 +43,8 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
|
||||
public class HttpProxy extends ProxyConfiguration.Proxy
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HttpProxy.class);
|
||||
|
||||
public HttpProxy(String host, int port)
|
||||
{
|
||||
this(new Origin.Address(host, port), false);
|
||||
|
@ -63,41 +68,72 @@ public class HttpProxy extends ProxyConfiguration.Proxy
|
|||
return URI.create(new Origin(scheme, getAddress()).asString());
|
||||
}
|
||||
|
||||
public static class HttpProxyClientConnectionFactory implements ClientConnectionFactory
|
||||
private class HttpProxyClientConnectionFactory implements ClientConnectionFactory
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HttpProxyClientConnectionFactory.class);
|
||||
private final ClientConnectionFactory connectionFactory;
|
||||
|
||||
public HttpProxyClientConnectionFactory(ClientConnectionFactory connectionFactory)
|
||||
private HttpProxyClientConnectionFactory(ClientConnectionFactory connectionFactory)
|
||||
{
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
|
||||
{
|
||||
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
|
||||
boolean secure = HttpScheme.HTTPS.is(destination.getScheme());
|
||||
SslContextFactory sslContextFactory = destination.getHttpClient().getSslContextFactory();
|
||||
if (secure)
|
||||
{
|
||||
if (sslContextFactory != null)
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
|
||||
final ProxyPromise proxyPromise = new ProxyPromise(endPoint, promise, context);
|
||||
// Replace the promise with the proxy one
|
||||
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, proxyPromise);
|
||||
Promise<Connection> wrapped = promise;
|
||||
if (promise instanceof Promise.Wrapper)
|
||||
wrapped = ((Promise.Wrapper<Connection>)promise).unwrap();
|
||||
if (wrapped instanceof TunnelPromise)
|
||||
{
|
||||
((TunnelPromise)wrapped).setEndPoint(endPoint);
|
||||
return connectionFactory.newConnection(endPoint, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replace the promise with the proxy promise that creates the tunnel to the server.
|
||||
CreateTunnelPromise tunnelPromise = new CreateTunnelPromise(connectionFactory, endPoint, promise, context);
|
||||
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, tunnelPromise);
|
||||
return connectionFactory.newConnection(endPoint, context);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException("Cannot tunnel request, missing " +
|
||||
SslContextFactory.class.getName() + " in " + HttpClient.class.getName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return connectionFactory.newConnection(endPoint, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether to establish a proxy tunnel using HTTP CONNECT.
|
||||
* It is implemented as a promise because it needs to establish the
|
||||
* <p>Creates a tunnel using HTTP CONNECT.</p>
|
||||
* <p>It is implemented as a promise because it needs to establish the
|
||||
* tunnel after the TCP connection is succeeded, and needs to notify
|
||||
* the nested promise when the tunnel is established (or failed).
|
||||
* the nested promise when the tunnel is established (or failed).</p>
|
||||
*/
|
||||
private class ProxyPromise implements Promise<Connection>
|
||||
private class CreateTunnelPromise implements Promise<Connection>
|
||||
{
|
||||
private final ClientConnectionFactory connectionFactory;
|
||||
private final EndPoint endPoint;
|
||||
private final Promise<Connection> promise;
|
||||
private final Map<String, Object> context;
|
||||
|
||||
private ProxyPromise(EndPoint endPoint, Promise<Connection> promise, Map<String, Object> context)
|
||||
private CreateTunnelPromise(ClientConnectionFactory connectionFactory, EndPoint endPoint, Promise<Connection> promise, Map<String, Object> context)
|
||||
{
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.endPoint = endPoint;
|
||||
this.promise = promise;
|
||||
this.context = context;
|
||||
|
@ -107,68 +143,59 @@ public class HttpProxy extends ProxyConfiguration.Proxy
|
|||
public void succeeded(Connection connection)
|
||||
{
|
||||
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
|
||||
if (HttpScheme.HTTPS.is(destination.getScheme()))
|
||||
{
|
||||
SslContextFactory sslContextFactory = destination.getHttpClient().getSslContextFactory();
|
||||
if (sslContextFactory != null)
|
||||
{
|
||||
tunnel(destination, connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
String message = String.format("Cannot perform requests over SSL, no %s in %s",
|
||||
SslContextFactory.class.getSimpleName(), HttpClient.class.getSimpleName());
|
||||
tunnelFailed(new IllegalStateException(message));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.succeeded(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
tunnelFailed(x);
|
||||
tunnelFailed(endPoint, x);
|
||||
}
|
||||
|
||||
private void tunnel(HttpDestination destination, final Connection connection)
|
||||
private void tunnel(HttpDestination destination, Connection connection)
|
||||
{
|
||||
String target = destination.getOrigin().getAddress().asString();
|
||||
Origin.Address proxyAddress = destination.getConnectAddress();
|
||||
HttpClient httpClient = destination.getHttpClient();
|
||||
long connectTimeout = httpClient.getConnectTimeout();
|
||||
Request connect = httpClient.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
|
||||
.scheme(HttpScheme.HTTP.asString())
|
||||
.method(HttpMethod.CONNECT)
|
||||
.path(target)
|
||||
.header(HttpHeader.HOST, target)
|
||||
.idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS)
|
||||
.timeout(connectTimeout, TimeUnit.MILLISECONDS);
|
||||
|
||||
final HttpConversation conversation = ((HttpRequest)connect).getConversation();
|
||||
conversation.setAttribute(EndPoint.class.getName(), endPoint);
|
||||
|
||||
connect.attribute(Connection.class.getName(), new ProxyConnection(destination, connection, promise));
|
||||
|
||||
connection.send(connect, result ->
|
||||
{
|
||||
if (result.isFailed())
|
||||
{
|
||||
tunnelFailed(result.getFailure());
|
||||
}
|
||||
else
|
||||
// The EndPoint may have changed during the conversation, get the latest.
|
||||
EndPoint endPoint1 = (EndPoint)conversation.getAttribute(EndPoint.class.getName());
|
||||
if (result.isSucceeded())
|
||||
{
|
||||
Response response = result.getResponse();
|
||||
if (response.getStatus() == 200)
|
||||
if (response.getStatus() == HttpStatus.OK_200)
|
||||
{
|
||||
tunnelSucceeded();
|
||||
tunnelSucceeded(endPoint1);
|
||||
}
|
||||
else
|
||||
{
|
||||
tunnelFailed(new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
|
||||
HttpResponseException failure = new HttpResponseException("Unexpected " + response +
|
||||
" for " + result.getRequest(), response);
|
||||
tunnelFailed(endPoint1, failure);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tunnelFailed(endPoint1, result.getFailure());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void tunnelSucceeded()
|
||||
private void tunnelSucceeded(EndPoint endPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -176,7 +203,8 @@ public class HttpProxy extends ProxyConfiguration.Proxy
|
|||
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise);
|
||||
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
|
||||
HttpClient client = destination.getHttpClient();
|
||||
ClientConnectionFactory sslConnectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
|
||||
ClientConnectionFactory sslConnectionFactory =
|
||||
new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
|
||||
HttpConnectionOverHTTP oldConnection = (HttpConnectionOverHTTP)endPoint.getConnection();
|
||||
org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
|
||||
endPoint.upgrade(newConnection);
|
||||
|
@ -185,15 +213,85 @@ public class HttpProxy extends ProxyConfiguration.Proxy
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
tunnelFailed(x);
|
||||
tunnelFailed(endPoint, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void tunnelFailed(Throwable failure)
|
||||
private void tunnelFailed(EndPoint endPoint, Throwable failure)
|
||||
{
|
||||
endPoint.close();
|
||||
promise.failed(failure);
|
||||
}
|
||||
}
|
||||
|
||||
private class ProxyConnection implements Connection
|
||||
{
|
||||
private final Destination destination;
|
||||
private final Connection connection;
|
||||
private final Promise<Connection> promise;
|
||||
|
||||
private ProxyConnection(Destination destination, Connection connection, Promise<Connection> promise)
|
||||
{
|
||||
this.destination = destination;
|
||||
this.connection = connection;
|
||||
this.promise = promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Request request, Response.CompleteListener listener)
|
||||
{
|
||||
if (connection.isClosed())
|
||||
{
|
||||
destination.newConnection(new TunnelPromise(request, listener, promise));
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.send(request, listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
connection.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed()
|
||||
{
|
||||
return connection.isClosed();
|
||||
}
|
||||
}
|
||||
|
||||
private class TunnelPromise implements Promise<Connection>
|
||||
{
|
||||
private final Request request;
|
||||
private final Response.CompleteListener listener;
|
||||
private final Promise<Connection> promise;
|
||||
|
||||
private TunnelPromise(Request request, Response.CompleteListener listener, Promise<Connection> promise)
|
||||
{
|
||||
this.request = request;
|
||||
this.listener = listener;
|
||||
this.promise = promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded(Connection connection)
|
||||
{
|
||||
connection.send(request, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
promise.failed(x);
|
||||
}
|
||||
|
||||
private void setEndPoint(EndPoint endPoint)
|
||||
{
|
||||
HttpConversation conversation = ((HttpRequest)request).getConversation();
|
||||
conversation.setAttribute(EndPoint.class.getName(), endPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,7 +184,9 @@ public abstract class HttpReceiver
|
|||
case SET_COOKIE:
|
||||
case SET_COOKIE2:
|
||||
{
|
||||
storeCookie(exchange.getRequest().getURI(), field);
|
||||
URI uri = exchange.getRequest().getURI();
|
||||
if (uri != null)
|
||||
storeCookie(uri, field);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -305,6 +307,7 @@ public abstract class HttpReceiver
|
|||
}
|
||||
default:
|
||||
{
|
||||
callback.failed(new IllegalStateException("Invalid response state " + current));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,7 +229,18 @@ public class HttpRedirector
|
|||
private Request redirect(Request request, Response response, Response.CompleteListener listener, URI newURI)
|
||||
{
|
||||
if (!newURI.isAbsolute())
|
||||
newURI = request.getURI().resolve(newURI);
|
||||
{
|
||||
URI requestURI = request.getURI();
|
||||
if (requestURI == null)
|
||||
{
|
||||
String uri = request.getScheme() + "://" + request.getHost();
|
||||
int port = request.getPort();
|
||||
if (port > 0)
|
||||
uri += ":" + port;
|
||||
requestURI = URI.create(uri);
|
||||
}
|
||||
newURI = requestURI.resolve(newURI);
|
||||
}
|
||||
|
||||
int status = response.getStatus();
|
||||
switch (status)
|
||||
|
|
|
@ -88,10 +88,11 @@ public class HttpRequest implements Request
|
|||
this.conversation = conversation;
|
||||
scheme = uri.getScheme();
|
||||
host = client.normalizeHost(uri.getHost());
|
||||
port = client.normalizePort(scheme, uri.getPort());
|
||||
port = HttpClient.normalizePort(scheme, uri.getPort());
|
||||
path = uri.getRawPath();
|
||||
query = uri.getRawQuery();
|
||||
extractParams(query);
|
||||
|
||||
followRedirects(client.isFollowRedirects());
|
||||
idleTimeout = client.getIdleTimeout();
|
||||
HttpField acceptEncodingField = client.getAcceptEncodingField();
|
||||
|
@ -170,8 +171,6 @@ public class HttpRequest implements Request
|
|||
else
|
||||
{
|
||||
String rawPath = uri.getRawPath();
|
||||
if (uri.isOpaque())
|
||||
rawPath = path;
|
||||
if (rawPath == null)
|
||||
rawPath = "";
|
||||
this.path = rawPath;
|
||||
|
@ -788,7 +787,7 @@ public class HttpRequest implements Request
|
|||
URI result = newURI(path);
|
||||
if (result == null)
|
||||
return NULL_URI;
|
||||
if (!result.isAbsolute() && !result.isOpaque())
|
||||
if (!result.isAbsolute())
|
||||
result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
|
||||
return result;
|
||||
}
|
||||
|
@ -797,12 +796,16 @@ public class HttpRequest implements Request
|
|||
{
|
||||
try
|
||||
{
|
||||
return new URI(uri);
|
||||
// Handle specially the "OPTIONS *" case, since it is possible to create a URI from "*" (!).
|
||||
if ("*".equals(uri))
|
||||
return null;
|
||||
URI result = new URI(uri);
|
||||
return result.isOpaque() ? null : result;
|
||||
}
|
||||
catch (URISyntaxException x)
|
||||
{
|
||||
// The "path" of a HTTP request may not be a URI,
|
||||
// for example for CONNECT 127.0.0.1:8080 or OPTIONS *.
|
||||
// for example for CONNECT 127.0.0.1:8080.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,11 +132,11 @@ public abstract class MultiplexHttpDestination<C extends Connection> extends Htt
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Aborted before processing {}: {}", exchange, cause);
|
||||
requestsPerConnection.decrementAndGet();
|
||||
// It may happen that the request is aborted before the exchange
|
||||
// is created. Aborting the exchange a second time will result in
|
||||
// a no-operation, so we just abort here to cover that edge case.
|
||||
exchange.abort(cause);
|
||||
requestsPerConnection.decrementAndGet();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -145,12 +145,12 @@ public abstract class MultiplexHttpDestination<C extends Connection> extends Htt
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Send failed {} for {}", result, exchange);
|
||||
requestsPerConnection.decrementAndGet();
|
||||
if (result.retry)
|
||||
{
|
||||
if (enqueue(getHttpExchanges(), exchange))
|
||||
return true;
|
||||
}
|
||||
|
||||
request.abort(result.failure);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ package org.eclipse.jetty.client;
|
|||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
|
||||
/**
|
||||
* <p>A protocol handler that handles redirect status codes 301, 302, 303, 307 and 308.</p>
|
||||
|
@ -54,6 +56,14 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHeader(Response response, HttpField field)
|
||||
{
|
||||
// Avoid that the content is decoded, which could generate
|
||||
// errors, since we are discarding the content anyway.
|
||||
return field.getHeader() != HttpHeader.CONTENT_ENCODING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
|
|
|
@ -209,8 +209,7 @@ public class ResponseNotifier
|
|||
}
|
||||
notifyHeaders(listeners, response);
|
||||
if (response instanceof ContentResponse)
|
||||
// TODO: handle callback
|
||||
notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter());
|
||||
notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
|
||||
notifySuccess(listeners, response);
|
||||
}
|
||||
|
||||
|
@ -231,8 +230,7 @@ public class ResponseNotifier
|
|||
}
|
||||
notifyHeaders(listeners, response);
|
||||
if (response instanceof ContentResponse)
|
||||
// TODO: handle callback
|
||||
notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter());
|
||||
notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
|
||||
notifyFailure(listeners, response, failure);
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,8 @@ public class Socks4Proxy extends ProxyConfiguration.Proxy
|
|||
{
|
||||
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
|
||||
Executor executor = destination.getHttpClient().getExecutor();
|
||||
return new Socks4ProxyConnection(endPoint, executor, connectionFactory, context);
|
||||
Socks4ProxyConnection connection = new Socks4ProxyConnection(endPoint, executor, connectionFactory, context);
|
||||
return customize(connection, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ public class TimeoutCompleteListener implements Response.CompleteListener, Runna
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Executing timeout task {} for {}", task, request);
|
||||
request.abort(new TimeoutException("Total timeout elapsed"));
|
||||
request.abort(new TimeoutException("Total timeout " + request.getTimeout() + " ms elapsed"));
|
||||
}
|
||||
|
||||
public void cancel()
|
||||
|
|
|
@ -45,4 +45,10 @@ public interface Connection extends Closeable
|
|||
|
||||
@Override
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @return whether this connection has been closed
|
||||
* @see #close()
|
||||
*/
|
||||
boolean isClosed();
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public class HttpClientTransportOverHTTP extends AbstractHttpClientTransport
|
|||
HttpConnectionOverHTTP connection = newHttpConnection(endPoint, destination, promise);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Created {}", connection);
|
||||
return connection;
|
||||
return customize(connection, context);
|
||||
}
|
||||
|
||||
protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
|
||||
|
|
|
@ -90,6 +90,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
|
|||
promise.succeeded(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed()
|
||||
{
|
||||
return closed.get();
|
||||
|
@ -138,17 +139,16 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
|
|||
{
|
||||
if (closed.compareAndSet(false, true))
|
||||
{
|
||||
// First close then abort, to be sure that the connection cannot be reused
|
||||
// from an onFailure() handler or by blocking code waiting for completion.
|
||||
getHttpDestination().close(this);
|
||||
|
||||
abort(failure);
|
||||
|
||||
getEndPoint().shutdownOutput();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Shutdown {}", this);
|
||||
getEndPoint().close();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Closed {}", this);
|
||||
|
||||
abort(failure);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,6 +208,12 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
|
|||
HttpConnectionOverHTTP.this.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed()
|
||||
{
|
||||
return HttpConnectionOverHTTP.this.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -300,25 +300,18 @@ public class HttpSenderOverHTTP extends HttpSender
|
|||
}
|
||||
}
|
||||
|
||||
private class ByteBufferRecyclerCallback implements Callback
|
||||
private class ByteBufferRecyclerCallback extends Callback.Nested
|
||||
{
|
||||
private final Callback callback;
|
||||
private final ByteBufferPool pool;
|
||||
private final ByteBuffer[] buffers;
|
||||
|
||||
private ByteBufferRecyclerCallback(Callback callback, ByteBufferPool pool, ByteBuffer... buffers)
|
||||
{
|
||||
this.callback = callback;
|
||||
super(callback);
|
||||
this.pool = pool;
|
||||
this.buffers = buffers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNonBlocking()
|
||||
{
|
||||
return callback.isNonBlocking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
|
@ -327,7 +320,7 @@ public class HttpSenderOverHTTP extends HttpSender
|
|||
assert !buffer.hasRemaining();
|
||||
pool.release(buffer);
|
||||
}
|
||||
callback.succeeded();
|
||||
super.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -335,7 +328,7 @@ public class HttpSenderOverHTTP extends HttpSender
|
|||
{
|
||||
for (ByteBuffer buffer : buffers)
|
||||
pool.release(buffer);
|
||||
callback.failed(x);
|
||||
super.failed(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.client.util;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
|
||||
public abstract class AbstractAuthentication implements Authentication
|
||||
{
|
||||
private final URI uri;
|
||||
private final String realm;
|
||||
|
||||
public AbstractAuthentication(URI uri, String realm)
|
||||
{
|
||||
this.uri = uri;
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public abstract String getType();
|
||||
|
||||
public URI getURI()
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
|
||||
public String getRealm()
|
||||
{
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String type, URI uri, String realm)
|
||||
{
|
||||
if (!getType().equalsIgnoreCase(type))
|
||||
return false;
|
||||
|
||||
if (!this.realm.equals(realm))
|
||||
return false;
|
||||
|
||||
return matchesURI(this.uri, uri);
|
||||
}
|
||||
|
||||
public static boolean matchesURI(URI uri1, URI uri2)
|
||||
{
|
||||
String scheme = uri1.getScheme();
|
||||
if (scheme.equalsIgnoreCase(uri2.getScheme()))
|
||||
{
|
||||
if (uri1.getHost().equalsIgnoreCase(uri2.getHost()))
|
||||
{
|
||||
// Handle default HTTP ports.
|
||||
int thisPort = HttpClient.normalizePort(scheme, uri1.getPort());
|
||||
int thatPort = HttpClient.normalizePort(scheme, uri2.getPort());
|
||||
if (thisPort == thatPort)
|
||||
{
|
||||
// Use decoded URI paths.
|
||||
return uri2.getPath().startsWith(uri1.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@ import java.net.URI;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
|
@ -37,10 +36,8 @@ import org.eclipse.jetty.util.B64Code;
|
|||
* {@link AuthenticationStore} retrieved from the {@link HttpClient}
|
||||
* via {@link HttpClient#getAuthenticationStore()}.
|
||||
*/
|
||||
public class BasicAuthentication implements Authentication
|
||||
public class BasicAuthentication extends AbstractAuthentication
|
||||
{
|
||||
private final URI uri;
|
||||
private final String realm;
|
||||
private final String user;
|
||||
private final String password;
|
||||
|
||||
|
@ -52,42 +49,49 @@ public class BasicAuthentication implements Authentication
|
|||
*/
|
||||
public BasicAuthentication(URI uri, String realm, String user, String password)
|
||||
{
|
||||
this.uri = uri;
|
||||
this.realm = realm;
|
||||
super(uri, realm);
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String type, URI uri, String realm)
|
||||
public String getType()
|
||||
{
|
||||
if (!"basic".equalsIgnoreCase(type))
|
||||
return false;
|
||||
|
||||
if (!uri.toString().startsWith(this.uri.toString()))
|
||||
return false;
|
||||
|
||||
return this.realm.equals(realm);
|
||||
return "Basic";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context)
|
||||
{
|
||||
String value = "Basic " + B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1);
|
||||
return new BasicResult(headerInfo.getHeader(), uri, value);
|
||||
return new BasicResult(getURI(), headerInfo.getHeader(), user, password);
|
||||
}
|
||||
|
||||
private static class BasicResult implements Result
|
||||
/**
|
||||
* Basic authentication result.
|
||||
* <p>
|
||||
* Application may utilize this class directly via
|
||||
* {@link AuthenticationStore#addAuthenticationResult(Result)}
|
||||
* to perform preemptive authentication, that is immediately
|
||||
* sending the authorization header based on the fact that the
|
||||
* URI is known to require authentication and that username
|
||||
* and password are known a priori.
|
||||
*/
|
||||
public static class BasicResult implements Result
|
||||
{
|
||||
private final HttpHeader header;
|
||||
private final URI uri;
|
||||
private final HttpHeader header;
|
||||
private final String value;
|
||||
|
||||
public BasicResult(HttpHeader header, URI uri, String value)
|
||||
public BasicResult(URI uri, String user, String password)
|
||||
{
|
||||
this(uri, HttpHeader.AUTHORIZATION, user, password);
|
||||
}
|
||||
|
||||
public BasicResult(URI uri, HttpHeader header, String user, String password)
|
||||
{
|
||||
this.header = header;
|
||||
this.uri = uri;
|
||||
this.value = value;
|
||||
this.header = header;
|
||||
this.value = "Basic " + B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -105,7 +109,7 @@ public class BasicAuthentication implements Authentication
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("Basic authentication result for %s", uri);
|
||||
return String.format("Basic authentication result for %s", getURI());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -217,11 +217,6 @@ public class DeferredContentProvider implements AsyncContentProvider, Callback,
|
|||
return closed.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable failure)
|
||||
{
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -34,7 +33,6 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
|
@ -50,12 +48,10 @@ import org.eclipse.jetty.util.TypeUtil;
|
|||
* {@link AuthenticationStore} retrieved from the {@link HttpClient}
|
||||
* via {@link HttpClient#getAuthenticationStore()}.
|
||||
*/
|
||||
public class DigestAuthentication implements Authentication
|
||||
public class DigestAuthentication extends AbstractAuthentication
|
||||
{
|
||||
private static final Pattern PARAM_PATTERN = Pattern.compile("([^=]+)=(.*)");
|
||||
|
||||
private final URI uri;
|
||||
private final String realm;
|
||||
private final String user;
|
||||
private final String password;
|
||||
|
||||
|
@ -67,22 +63,15 @@ public class DigestAuthentication implements Authentication
|
|||
*/
|
||||
public DigestAuthentication(URI uri, String realm, String user, String password)
|
||||
{
|
||||
this.uri = uri;
|
||||
this.realm = realm;
|
||||
super(uri, realm);
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String type, URI uri, String realm)
|
||||
public String getType()
|
||||
{
|
||||
if (!"digest".equalsIgnoreCase(type))
|
||||
return false;
|
||||
|
||||
if (!uri.toString().startsWith(this.uri.toString()))
|
||||
return false;
|
||||
|
||||
return this.realm.equals(realm);
|
||||
return "Digest";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,7 +99,7 @@ public class DigestAuthentication implements Authentication
|
|||
clientQOP = "auth-int";
|
||||
}
|
||||
|
||||
return new DigestResult(headerInfo.getHeader(), uri, response.getContent(), realm, user, password, algorithm, nonce, clientQOP, opaque);
|
||||
return new DigestResult(headerInfo.getHeader(), response.getContent(), getRealm(), user, password, algorithm, nonce, clientQOP, opaque);
|
||||
}
|
||||
|
||||
private Map<String, String> parseParameters(String wwwAuthenticate)
|
||||
|
@ -181,7 +170,6 @@ public class DigestAuthentication implements Authentication
|
|||
{
|
||||
private final AtomicInteger nonceCount = new AtomicInteger();
|
||||
private final HttpHeader header;
|
||||
private final URI uri;
|
||||
private final byte[] content;
|
||||
private final String realm;
|
||||
private final String user;
|
||||
|
@ -191,10 +179,9 @@ public class DigestAuthentication implements Authentication
|
|||
private final String qop;
|
||||
private final String opaque;
|
||||
|
||||
public DigestResult(HttpHeader header, URI uri, byte[] content, String realm, String user, String password, String algorithm, String nonce, String qop, String opaque)
|
||||
public DigestResult(HttpHeader header, byte[] content, String realm, String user, String password, String algorithm, String nonce, String qop, String opaque)
|
||||
{
|
||||
this.header = header;
|
||||
this.uri = uri;
|
||||
this.content = content;
|
||||
this.realm = realm;
|
||||
this.user = user;
|
||||
|
@ -208,7 +195,7 @@ public class DigestAuthentication implements Authentication
|
|||
@Override
|
||||
public URI getURI()
|
||||
{
|
||||
return uri;
|
||||
return DigestAuthentication.this.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -221,7 +208,8 @@ public class DigestAuthentication implements Authentication
|
|||
String A1 = user + ":" + realm + ":" + password;
|
||||
String hashA1 = toHexString(digester.digest(A1.getBytes(StandardCharsets.ISO_8859_1)));
|
||||
|
||||
String A2 = request.getMethod() + ":" + request.getURI();
|
||||
URI uri = request.getURI();
|
||||
String A2 = request.getMethod() + ":" + uri;
|
||||
if ("auth-int".equals(qop))
|
||||
A2 += ":" + toHexString(digester.digest(content));
|
||||
String hashA2 = toHexString(digester.digest(A2.getBytes(StandardCharsets.ISO_8859_1)));
|
||||
|
@ -250,7 +238,7 @@ public class DigestAuthentication implements Authentication
|
|||
if (opaque != null)
|
||||
value.append(", opaque=\"").append(opaque).append("\"");
|
||||
value.append(", algorithm=\"").append(algorithm).append("\"");
|
||||
value.append(", uri=\"").append(request.getURI()).append("\"");
|
||||
value.append(", uri=\"").append(uri).append("\"");
|
||||
if (qop != null)
|
||||
{
|
||||
value.append(", qop=\"").append(qop).append("\"");
|
||||
|
|
|
@ -136,11 +136,6 @@ public class InputStreamContentProvider implements ContentProvider, Callback, Cl
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable failure)
|
||||
{
|
||||
|
|
|
@ -32,9 +32,11 @@ import org.junit.After;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
@Ignore
|
||||
public class ExternalSiteTest
|
||||
{
|
||||
@Rule
|
||||
|
|
|
@ -109,23 +109,10 @@ public class HostnameVerificationTest
|
|||
}
|
||||
catch (ExecutionException x)
|
||||
{
|
||||
// The test may fail in 2 ways, since the CertificateException thrown because of the hostname
|
||||
// verification failure is not rethrown immediately by the JDK SSL implementation, but only
|
||||
// rethrown on the next read or write.
|
||||
// Therefore this test may catch a SSLHandshakeException, or a ClosedChannelException.
|
||||
// If it is the former, we verify that its cause is a CertificateException.
|
||||
|
||||
// ExecutionException wraps an SSLHandshakeException
|
||||
Throwable cause = x.getCause();
|
||||
if (cause==null)
|
||||
{
|
||||
x.printStackTrace();
|
||||
Assert.fail("No cause?");
|
||||
}
|
||||
if (cause instanceof SSLHandshakeException)
|
||||
Assert.assertThat(cause.getCause().getCause(), Matchers.instanceOf(CertificateException.class));
|
||||
else
|
||||
Assert.assertThat(cause.getCause(), Matchers.instanceOf(ClosedChannelException.class));
|
||||
Assert.assertThat(cause, Matchers.instanceOf(SSLHandshakeException.class));
|
||||
Throwable root = cause.getCause().getCause();
|
||||
Assert.assertThat(root, Matchers.instanceOf(CertificateException.class));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.client;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.BasicAuthentication;
|
||||
import org.eclipse.jetty.client.util.DigestAuthentication;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HttpAuthenticationStoreTest
|
||||
{
|
||||
@Test
|
||||
public void testFindAuthenticationWithDefaultHTTPPort() throws Exception
|
||||
{
|
||||
AuthenticationStore store = new HttpAuthenticationStore();
|
||||
|
||||
URI uri1 = URI.create("http://host:80");
|
||||
URI uri2 = URI.create("http://host");
|
||||
String realm = "realm";
|
||||
store.addAuthentication(new BasicAuthentication(uri1, realm, "user", "password"));
|
||||
|
||||
Authentication result = store.findAuthentication("Basic", uri2, realm);
|
||||
Assert.assertNotNull(result);
|
||||
|
||||
store.clearAuthentications();
|
||||
|
||||
// Flip the URIs.
|
||||
uri1 = URI.create("https://server/");
|
||||
uri2 = URI.create("https://server:443/path");
|
||||
store.addAuthentication(new DigestAuthentication(uri1, realm, "user", "password"));
|
||||
result = store.findAuthentication("Digest", uri2, realm);
|
||||
Assert.assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAuthenticationResultWithDefaultHTTPPort() throws Exception
|
||||
{
|
||||
AuthenticationStore store = new HttpAuthenticationStore();
|
||||
|
||||
store.addAuthenticationResult(new Authentication.Result()
|
||||
{
|
||||
@Override
|
||||
public URI getURI()
|
||||
{
|
||||
return URI.create("http://host:80");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Request request)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
URI uri2 = URI.create("http://host");
|
||||
Authentication.Result result = store.findAuthenticationResult(uri2);
|
||||
Assert.assertNotNull(result);
|
||||
|
||||
store.clearAuthenticationResults();
|
||||
|
||||
// Flip the URIs.
|
||||
store.addAuthenticationResult(new Authentication.Result()
|
||||
{
|
||||
@Override
|
||||
public URI getURI()
|
||||
{
|
||||
return URI.create("https://server/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Request request)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
uri2 = URI.create("https://server:443/path");
|
||||
result = store.findAuthenticationResult(uri2);
|
||||
Assert.assertNotNull(result);
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ package org.eclipse.jetty.client;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
@ -37,6 +39,7 @@ import org.eclipse.jetty.client.api.Request;
|
|||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.BasicAuthentication;
|
||||
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
||||
import org.eclipse.jetty.client.util.DigestAuthentication;
|
||||
import org.eclipse.jetty.security.Authenticator;
|
||||
import org.eclipse.jetty.security.ConstraintMapping;
|
||||
|
@ -107,6 +110,15 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
|||
test_Authentication(new BasicAuthentication(uri, realm, "basic", "basic"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_BasicEmptyRealm() throws Exception
|
||||
{
|
||||
realm = "";
|
||||
startBasic(new EmptyServerHandler());
|
||||
URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
|
||||
test_Authentication(new BasicAuthentication(uri, realm, "basic", "basic"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DigestAuthentication() throws Exception
|
||||
{
|
||||
|
@ -366,4 +378,72 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PreemptedAuthentication() throws Exception
|
||||
{
|
||||
startBasic(new EmptyServerHandler());
|
||||
|
||||
AuthenticationStore authenticationStore = client.getAuthenticationStore();
|
||||
URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
|
||||
authenticationStore.addAuthenticationResult(new BasicAuthentication.BasicResult(uri, "basic", "basic"));
|
||||
|
||||
AtomicInteger requests = new AtomicInteger();
|
||||
client.getRequestListeners().add(new Request.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Request request)
|
||||
{
|
||||
requests.incrementAndGet();
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/secure")
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertEquals(1, requests.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_RequestFailsAfterResponse() throws Exception
|
||||
{
|
||||
startBasic(new EmptyServerHandler());
|
||||
|
||||
AuthenticationStore authenticationStore = client.getAuthenticationStore();
|
||||
URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
|
||||
BasicAuthentication authentication = new BasicAuthentication(uri, realm, "basic", "basic");
|
||||
authenticationStore.addAuthentication(authentication);
|
||||
|
||||
CountDownLatch successLatch = new CountDownLatch(1);
|
||||
CountDownLatch resultLatch = new CountDownLatch(1);
|
||||
DeferredContentProvider content = new DeferredContentProvider();
|
||||
Request request = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/secure")
|
||||
.content(content)
|
||||
.onResponseSuccess(response -> successLatch.countDown());
|
||||
request.send(result ->
|
||||
{
|
||||
if (result.isFailed() && result.getResponseFailure() == null)
|
||||
resultLatch.countDown();
|
||||
});
|
||||
|
||||
// Send some content to make sure the request is dispatched on the server.
|
||||
content.offer(ByteBuffer.wrap("hello".getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
// Wait for the response to arrive to
|
||||
// the authentication protocol handler.
|
||||
Thread.sleep(1000);
|
||||
|
||||
// Trigger request failure.
|
||||
request.abort(new Exception());
|
||||
|
||||
// Verify that the response was successful, it's the request that failed.
|
||||
Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
@ -93,9 +92,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.getHttpURI().toString()).isAbsolute())
|
||||
response.setStatus(HttpServletResponse.SC_USE_PROXY);
|
||||
else if (serverHost.equals(request.getServerName()))
|
||||
if (serverHost.equals(request.getServerName()))
|
||||
response.setStatus(status);
|
||||
else
|
||||
response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
|
||||
|
@ -142,7 +139,8 @@ public class HttpClientCustomProxyTest
|
|||
{
|
||||
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
|
||||
Executor executor = destination.getHttpClient().getExecutor();
|
||||
return new CAFEBABEConnection(endPoint, executor, connectionFactory, context);
|
||||
CAFEBABEConnection connection = new CAFEBABEConnection(endPoint, executor, connectionFactory, context);
|
||||
return customize(connection, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||
import java.net.URLDecoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.UnresolvedAddressException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -44,7 +45,6 @@ import org.eclipse.jetty.toolchain.test.IO;
|
|||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -55,15 +55,11 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
super(sslContextFactory);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepare() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_303() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/303/localhost/done")
|
||||
|
@ -77,6 +73,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void test_303_302() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/303/localhost/302/localhost/done")
|
||||
|
@ -90,6 +88,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void test_303_302_OnDifferentDestinations() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/303/127.0.0.1/302/localhost/done")
|
||||
|
@ -103,6 +103,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void test_301() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.method(HttpMethod.HEAD)
|
||||
|
@ -117,6 +119,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void test_301_WithWrongMethod() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
try
|
||||
{
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
|
@ -140,6 +144,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void test_307_WithRequestContent() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
|
@ -157,6 +163,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void testMaxRedirections() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
client.setMaxRedirects(1);
|
||||
|
||||
try
|
||||
|
@ -181,6 +188,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void test_303_WithConnectionClose_WithBigRequest() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/303/localhost/done?close=true")
|
||||
|
@ -194,6 +203,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void testDontFollowRedirects() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.followRedirects(false)
|
||||
|
@ -208,6 +219,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void testRelativeLocation() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/303/localhost/done?relative=true")
|
||||
|
@ -221,6 +234,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void testAbsoluteURIPathWithSpaces() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/303/localhost/a+space?decode=true")
|
||||
|
@ -234,6 +249,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void testRelativeURIPathWithSpaces() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/303/localhost/a+space?relative=true&decode=true")
|
||||
|
@ -247,7 +264,6 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void testRedirectWithWrongScheme() throws Exception
|
||||
{
|
||||
dispose();
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
|
@ -255,7 +271,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setStatus(303);
|
||||
response.setHeader("Location", "ssh://localhost/path");
|
||||
response.setHeader("Location", "ssh://localhost:" + connector.getLocalPort() + "/path");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -264,14 +280,10 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
.scheme(scheme)
|
||||
.path("/path")
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
.send(result ->
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
@ -281,6 +293,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
public void testRedirectFailed() throws Exception
|
||||
{
|
||||
// TODO this test is failing with timout after an ISP upgrade?? DNS dependent?
|
||||
start(new RedirectHandler());
|
||||
|
||||
try
|
||||
{
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
|
@ -370,6 +384,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void testHttpRedirector() throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
final HttpRedirector redirector = new HttpRedirector(client);
|
||||
|
||||
org.eclipse.jetty.client.api.Request request1 = client.newRequest("localhost", connector.getLocalPort())
|
||||
|
@ -390,20 +405,52 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
Assert.assertTrue(redirector.isRedirect(response2));
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
redirector.redirect(request2, response2, new Response.CompleteListener()
|
||||
redirector.redirect(request2, response2, r ->
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Response response3 = result.getResponse();
|
||||
Response response3 = r.getResponse();
|
||||
Assert.assertEquals(200, response3.getStatus());
|
||||
Assert.assertFalse(redirector.isRedirect(response3));
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectWithCorruptedBody() throws Exception
|
||||
{
|
||||
byte[] bytes = "ok".getBytes(StandardCharsets.UTF_8);
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
if (target.startsWith("/redirect"))
|
||||
{
|
||||
response.setStatus(HttpStatus.SEE_OTHER_303);
|
||||
response.setHeader(HttpHeader.LOCATION.asString(), scheme + "://localhost:" + connector.getLocalPort() + "/ok");
|
||||
// Say that we send gzipped content, but actually don't.
|
||||
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), "gzip");
|
||||
response.getOutputStream().write("redirect".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
else
|
||||
{
|
||||
response.setStatus(HttpStatus.OK_200);
|
||||
response.getOutputStream().write(bytes);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/redirect")
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertArrayEquals(bytes, response.getContent());
|
||||
}
|
||||
|
||||
private void testSameMethodRedirect(final HttpMethod method, int redirectCode) throws Exception
|
||||
{
|
||||
testMethodRedirect(method, method, redirectCode);
|
||||
|
@ -416,6 +463,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
|
||||
private void testMethodRedirect(final HttpMethod requestMethod, final HttpMethod redirectMethod, int redirectCode) throws Exception
|
||||
{
|
||||
start(new RedirectHandler());
|
||||
|
||||
final AtomicInteger passes = new AtomicInteger();
|
||||
client.getRequestListeners().add(new org.eclipse.jetty.client.api.Request.Listener.Adapter()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,395 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.client;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HttpClientTLSTest
|
||||
{
|
||||
private Server server;
|
||||
private ServerConnector connector;
|
||||
private HttpClient client;
|
||||
|
||||
private void startServer(SslContextFactory sslContextFactory, Handler handler) throws Exception
|
||||
{
|
||||
QueuedThreadPool serverThreads = new QueuedThreadPool();
|
||||
serverThreads.setName("server");
|
||||
server = new Server(serverThreads);
|
||||
|
||||
connector = new ServerConnector(server, sslContextFactory);
|
||||
server.addConnector(connector);
|
||||
|
||||
server.setHandler(handler);
|
||||
server.start();
|
||||
}
|
||||
|
||||
private void startClient(SslContextFactory sslContextFactory) throws Exception
|
||||
{
|
||||
QueuedThreadPool clientThreads = new QueuedThreadPool();
|
||||
clientThreads.setName("client");
|
||||
client = new HttpClient(sslContextFactory);
|
||||
client.setExecutor(clientThreads);
|
||||
client.start();
|
||||
}
|
||||
|
||||
private SslContextFactory createSslContextFactory()
|
||||
{
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
sslContextFactory.setEndpointIdentificationAlgorithm("");
|
||||
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
|
||||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
|
||||
sslContextFactory.setTrustStorePassword("storepwd");
|
||||
return sslContextFactory;
|
||||
}
|
||||
|
||||
@After
|
||||
public void dispose() throws Exception
|
||||
{
|
||||
if (client != null)
|
||||
client.stop();
|
||||
if (server != null)
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoCommonTLSProtocol() throws Exception
|
||||
{
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
serverTLSFactory.setIncludeProtocols("TLSv1.2");
|
||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||
|
||||
CountDownLatch serverLatch = new CountDownLatch(1);
|
||||
connector.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
serverLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
SslContextFactory clientTLSFactory = createSslContextFactory();
|
||||
clientTLSFactory.setIncludeProtocols("TLSv1.1");
|
||||
startClient(clientTLSFactory);
|
||||
|
||||
CountDownLatch clientLatch = new CountDownLatch(1);
|
||||
client.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
clientLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(HttpScheme.HTTPS.asString())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (ExecutionException x)
|
||||
{
|
||||
// Expected.
|
||||
}
|
||||
|
||||
Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoCommonTLSCiphers() throws Exception
|
||||
{
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
serverTLSFactory.setIncludeCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA");
|
||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||
|
||||
CountDownLatch serverLatch = new CountDownLatch(1);
|
||||
connector.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
serverLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
SslContextFactory clientTLSFactory = createSslContextFactory();
|
||||
clientTLSFactory.setExcludeCipherSuites(".*_SHA$");
|
||||
startClient(clientTLSFactory);
|
||||
|
||||
CountDownLatch clientLatch = new CountDownLatch(1);
|
||||
client.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
clientLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(HttpScheme.HTTPS.asString())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (ExecutionException x)
|
||||
{
|
||||
// Expected.
|
||||
}
|
||||
|
||||
Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMismatchBetweenTLSProtocolAndTLSCiphersOnServer() throws Exception
|
||||
{
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
// TLS 1.1 protocol, but only TLS 1.2 ciphers.
|
||||
serverTLSFactory.setIncludeProtocols("TLSv1.1");
|
||||
serverTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
|
||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||
|
||||
CountDownLatch serverLatch = new CountDownLatch(1);
|
||||
connector.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
serverLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
SslContextFactory clientTLSFactory = createSslContextFactory();
|
||||
startClient(clientTLSFactory);
|
||||
|
||||
CountDownLatch clientLatch = new CountDownLatch(1);
|
||||
client.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
clientLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(HttpScheme.HTTPS.asString())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (ExecutionException x)
|
||||
{
|
||||
// Expected.
|
||||
}
|
||||
|
||||
Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMismatchBetweenTLSProtocolAndTLSCiphersOnClient() throws Exception
|
||||
{
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||
|
||||
CountDownLatch serverLatch = new CountDownLatch(1);
|
||||
connector.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
serverLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
SslContextFactory clientTLSFactory = createSslContextFactory();
|
||||
// TLS 1.1 protocol, but only TLS 1.2 ciphers.
|
||||
clientTLSFactory.setIncludeProtocols("TLSv1.1");
|
||||
clientTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
|
||||
startClient(clientTLSFactory);
|
||||
|
||||
CountDownLatch clientLatch = new CountDownLatch(1);
|
||||
client.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
clientLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(HttpScheme.HTTPS.asString())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (ExecutionException x)
|
||||
{
|
||||
// Expected.
|
||||
}
|
||||
|
||||
Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandshakeSucceeded() throws Exception
|
||||
{
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||
|
||||
CountDownLatch serverLatch = new CountDownLatch(1);
|
||||
connector.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
serverLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
SslContextFactory clientTLSFactory = createSslContextFactory();
|
||||
startClient(clientTLSFactory);
|
||||
|
||||
CountDownLatch clientLatch = new CountDownLatch(1);
|
||||
client.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
clientLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.GET("https://localhost:" + connector.getLocalPort());
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
|
||||
Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandshakeSucceededWithSessionResumption() throws Exception
|
||||
{
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||
|
||||
AtomicReference<byte[]> serverSession = new AtomicReference<>();
|
||||
connector.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
serverSession.set(event.getSSLEngine().getSession().getId());
|
||||
}
|
||||
});
|
||||
|
||||
SslContextFactory clientTLSFactory = createSslContextFactory();
|
||||
startClient(clientTLSFactory);
|
||||
|
||||
AtomicReference<byte[]> clientSession = new AtomicReference<>();
|
||||
client.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
clientSession.set(event.getSSLEngine().getSession().getId());
|
||||
}
|
||||
});
|
||||
|
||||
// First request primes the TLS session.
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(HttpScheme.HTTPS.asString())
|
||||
.header(HttpHeader.CONNECTION, "close")
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
|
||||
Assert.assertNotNull(serverSession.get());
|
||||
Assert.assertNotNull(clientSession.get());
|
||||
|
||||
connector.removeBean(connector.getBean(SslHandshakeListener.class));
|
||||
client.removeBean(client.getBean(SslHandshakeListener.class));
|
||||
|
||||
CountDownLatch serverLatch = new CountDownLatch(1);
|
||||
connector.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
if (Arrays.equals(serverSession.get(), event.getSSLEngine().getSession().getId()))
|
||||
serverLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch clientLatch = new CountDownLatch(1);
|
||||
client.addBean(new SslHandshakeListener()
|
||||
{
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
if (Arrays.equals(clientSession.get(), event.getSSLEngine().getSession().getId()))
|
||||
clientLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// Second request should have the same session ID.
|
||||
response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(HttpScheme.HTTPS.asString())
|
||||
.header(HttpHeader.CONNECTION, "close")
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
|
||||
Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
|
@ -86,6 +86,7 @@ import org.eclipse.jetty.util.FuturePromise;
|
|||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.SocketAddressResolver;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
|
@ -481,6 +482,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
|
||||
client.setMaxConnectionsPerDestination(1);
|
||||
|
||||
try (StacklessLogging stackless = new StacklessLogging(org.eclipse.jetty.server.HttpChannel.class))
|
||||
{
|
||||
final CountDownLatch latch = new CountDownLatch(2);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
|
@ -500,6 +503,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ExchangeIsComplete_OnlyWhenBothRequestAndResponseAreComplete() throws Exception
|
||||
|
|
|
@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
|
@ -472,4 +473,35 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsteriskFormTarget() throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
Assert.assertEquals("*", target);
|
||||
Assert.assertEquals("*", request.getPathInfo());
|
||||
}
|
||||
});
|
||||
|
||||
Request request = client.newRequest("localhost", connector.getLocalPort())
|
||||
.method(HttpMethod.OPTIONS)
|
||||
.scheme(scheme)
|
||||
.path("*")
|
||||
.timeout(5, TimeUnit.SECONDS);
|
||||
|
||||
Assert.assertEquals("*", request.getPath());
|
||||
Assert.assertNull(request.getQuery());
|
||||
Fields params = request.getParams();
|
||||
Assert.assertEquals(0, params.getSize());
|
||||
Assert.assertNull(request.getURI());
|
||||
|
||||
ContentResponse response = request.send();
|
||||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ import org.eclipse.jetty.server.Handler;
|
|||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
@ -399,9 +399,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Test
|
||||
public void test_BigRequestContent_ResponseWithConnectionCloseHeader_RemovesConnection() throws Exception
|
||||
{
|
||||
StdErrLog logger = StdErrLog.getLogger(org.eclipse.jetty.server.HttpConnection.class);
|
||||
logger.setHideStacks(true);
|
||||
try
|
||||
try (StacklessLogging stackless = new StacklessLogging(HttpConnection.class))
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
|
@ -452,10 +450,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
|
||||
server.stop();
|
||||
}
|
||||
finally
|
||||
{
|
||||
logger.setHideStacks(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Slow
|
||||
|
|
|
@ -26,8 +26,6 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
@ -69,17 +67,16 @@ public class Socks4ProxyTest
|
|||
byte ip4 = 13;
|
||||
String serverHost = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
|
||||
int serverPort = proxyPort + 1; // Any port will do
|
||||
String method = "GET";
|
||||
String path = "/path";
|
||||
client.newRequest(serverHost, serverPort)
|
||||
.path("/path")
|
||||
.method(method)
|
||||
.path(path)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isSucceeded())
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
SocketChannel channel = server.accept();
|
||||
|
@ -100,11 +97,11 @@ public class Socks4ProxyTest
|
|||
// Socks4 response.
|
||||
channel.write(ByteBuffer.wrap(new byte[]{0, 0x5A, 0, 0, 0, 0, 0, 0}));
|
||||
|
||||
buffer = ByteBuffer.allocate(3);
|
||||
buffer = ByteBuffer.allocate(method.length() + 1 + path.length());
|
||||
read = channel.read(buffer);
|
||||
Assert.assertEquals(3, read);
|
||||
Assert.assertEquals(buffer.capacity(), read);
|
||||
buffer.flip();
|
||||
Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString());
|
||||
Assert.assertEquals(method + " " + path, StandardCharsets.UTF_8.decode(buffer).toString());
|
||||
|
||||
// Response
|
||||
String response = "" +
|
||||
|
@ -129,19 +126,17 @@ public class Socks4ProxyTest
|
|||
|
||||
String serverHost = "127.0.0.13"; // Test expects an IP address.
|
||||
int serverPort = proxyPort + 1; // Any port will do
|
||||
String method = "GET";
|
||||
client.newRequest(serverHost, serverPort)
|
||||
.method(method)
|
||||
.path("/path")
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isSucceeded())
|
||||
latch.countDown();
|
||||
else
|
||||
result.getFailure().printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
SocketChannel channel = server.accept();
|
||||
|
@ -161,11 +156,11 @@ public class Socks4ProxyTest
|
|||
|
||||
channel.write(ByteBuffer.wrap(chunk2));
|
||||
|
||||
buffer = ByteBuffer.allocate(3);
|
||||
buffer = ByteBuffer.allocate(method.length());
|
||||
read = channel.read(buffer);
|
||||
Assert.assertEquals(3, read);
|
||||
Assert.assertEquals(buffer.capacity(), read);
|
||||
buffer.flip();
|
||||
Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString());
|
||||
Assert.assertEquals(method, StandardCharsets.UTF_8.decode(buffer).toString());
|
||||
|
||||
// Response
|
||||
String response = "" +
|
||||
|
|
|
@ -26,7 +26,6 @@ import java.io.OutputStream;
|
|||
import java.net.SocketTimeoutException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
@ -105,13 +104,10 @@ public class SslBytesClientTest extends SslBytesTest
|
|||
final SSLSocket server = (SSLSocket)acceptor.accept();
|
||||
server.setUseClientMode(false);
|
||||
|
||||
Future<Object> handshake = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
public Object call() throws Exception
|
||||
Future<Object> handshake = threadPool.submit(() ->
|
||||
{
|
||||
server.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Client Hello
|
||||
|
@ -185,13 +181,10 @@ public class SslBytesClientTest extends SslBytesTest
|
|||
final SSLSocket server = (SSLSocket)acceptor.accept();
|
||||
server.setUseClientMode(false);
|
||||
|
||||
Future<Object> handshake = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
public Object call() throws Exception
|
||||
Future<Object> handshake = threadPool.submit(() ->
|
||||
{
|
||||
server.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
|
||||
|
@ -222,13 +215,10 @@ public class SslBytesClientTest extends SslBytesTest
|
|||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
// Renegotiate
|
||||
Future<Object> renegotiation = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
public Object call() throws Exception
|
||||
Future<Object> renegotiation = threadPool.submit(() ->
|
||||
{
|
||||
server.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Renegotiation Handshake
|
||||
|
@ -307,13 +297,10 @@ public class SslBytesClientTest extends SslBytesTest
|
|||
final SSLSocket server = (SSLSocket)acceptor.accept();
|
||||
server.setUseClientMode(false);
|
||||
|
||||
Future<Object> handshake = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
public Object call() throws Exception
|
||||
Future<Object> handshake = threadPool.submit(() ->
|
||||
{
|
||||
server.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
|
||||
|
@ -344,13 +331,10 @@ public class SslBytesClientTest extends SslBytesTest
|
|||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
// Renegotiate
|
||||
threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
public Object call() throws Exception
|
||||
threadPool.submit(() ->
|
||||
{
|
||||
server.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Renegotiation Handshake
|
||||
|
@ -358,6 +342,9 @@ public class SslBytesClientTest extends SslBytesTest
|
|||
Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
||||
proxy.flushToClient(record);
|
||||
|
||||
// Client sends close alert.
|
||||
record = proxy.readFromClient();
|
||||
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
|
||||
record = proxy.readFromClient();
|
||||
Assert.assertNull(record);
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.client.ssl;
|
||||
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
|
@ -32,7 +30,6 @@ import java.nio.channels.SelectionKey;
|
|||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -112,7 +109,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
@Override
|
||||
public Connection newConnection(Connector connector, EndPoint endPoint)
|
||||
{
|
||||
return configure(new HttpConnection(getHttpConfiguration(), connector, endPoint,getHttpCompliance())
|
||||
return configure(new HttpConnection(getHttpConfiguration(), connector, endPoint,getHttpCompliance(),isRecordHttpComplianceViolations())
|
||||
{
|
||||
@Override
|
||||
protected HttpParser newHttpParser(HttpCompliance compliance)
|
||||
|
@ -244,14 +241,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
{
|
||||
final SSLSocket client = newClient();
|
||||
|
||||
Future<Object> handshake = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> handshake = threadPool.submit(() ->
|
||||
{
|
||||
client.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Client Hello
|
||||
|
@ -318,14 +311,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
|
||||
final SSLSocket client2 = newClient(proxy);
|
||||
|
||||
Future<Object> handshake = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
threadPool.submit(() ->
|
||||
{
|
||||
client2.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Client Hello with SessionID
|
||||
|
@ -371,7 +360,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
// Close the raw socket
|
||||
proxy.flushToServer(null);
|
||||
|
||||
// Expect the server to send a FIN as well
|
||||
// Expect the server to send a TLS Alert.
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertNotNull(record);
|
||||
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertNull(record);
|
||||
|
||||
|
@ -387,14 +379,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
{
|
||||
final SSLSocket client = newClient();
|
||||
|
||||
Future<Object> handshake = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> handshake = threadPool.submit(() ->
|
||||
{
|
||||
client.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Client Hello
|
||||
|
@ -492,14 +480,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
{
|
||||
final SSLSocket client = newClient();
|
||||
|
||||
threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
threadPool.submit(() ->
|
||||
{
|
||||
client.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Client Hello
|
||||
|
@ -525,14 +509,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
{
|
||||
final SSLSocket client = newClient();
|
||||
|
||||
threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
threadPool.submit(() ->
|
||||
{
|
||||
client.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Client Hello
|
||||
|
@ -580,10 +560,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
client.startHandshake();
|
||||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -592,7 +569,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
"\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
|
@ -622,10 +598,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
client.startHandshake();
|
||||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -634,7 +607,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
"\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
|
@ -671,14 +643,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
{
|
||||
final SSLSocket client = newClient();
|
||||
|
||||
Future<Object> handshake = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> handshake = threadPool.submit(() ->
|
||||
{
|
||||
client.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Client Hello
|
||||
|
@ -715,10 +683,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
|
||||
Assert.assertNull(handshake.get(1, TimeUnit.SECONDS));
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -727,7 +692,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
"\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
|
@ -795,10 +759,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
client.startHandshake();
|
||||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -807,7 +768,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
"\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
|
@ -870,10 +830,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
client.startHandshake();
|
||||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -882,7 +839,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
"\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
|
@ -940,10 +896,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
client.startHandshake();
|
||||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -952,7 +905,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
"\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
|
@ -999,10 +951,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
client.startHandshake();
|
||||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -1011,7 +960,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
"\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
|
@ -1062,10 +1010,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
byte[] data = new byte[128 * 1024];
|
||||
Arrays.fill(data, (byte)'X');
|
||||
final String content = new String(data, StandardCharsets.UTF_8);
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -1076,7 +1021,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
content).getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Nine TLSRecords will be generated for the request
|
||||
|
@ -1123,10 +1067,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
byte[] data = new byte[128 * 1024];
|
||||
Arrays.fill(data, (byte)'X');
|
||||
final String content = new String(data, StandardCharsets.UTF_8);
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -1137,7 +1078,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
content).getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Nine TLSRecords will be generated for the request,
|
||||
|
@ -1187,10 +1127,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
client.startHandshake();
|
||||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -1199,7 +1136,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
"\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
|
@ -1263,10 +1199,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
|
||||
final String content = "0123456789ABCDEF";
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -1278,7 +1211,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
content).getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
|
@ -1329,10 +1261,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
Arrays.fill(data, (byte)'X');
|
||||
final String content = new String(data, StandardCharsets.UTF_8);
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
|
@ -1344,7 +1273,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
content).getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Nine TLSRecords will be generated for the request
|
||||
|
@ -1428,14 +1356,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
// Renegotiate
|
||||
threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
threadPool.submit(() ->
|
||||
{
|
||||
client.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Renegotiation Handshake
|
||||
|
@ -1452,15 +1376,11 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
Assert.assertNull(record);
|
||||
|
||||
// Write the rest of the request
|
||||
threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
threadPool.submit(() ->
|
||||
{
|
||||
clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Trying to write more application data results in an exception since the server closed
|
||||
|
@ -1475,6 +1395,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
}
|
||||
catch (IOException expected)
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
|
||||
// Check that we did not spin
|
||||
|
@ -1519,14 +1440,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
// Renegotiate
|
||||
Future<Object> renegotiation = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> renegotiation = threadPool.submit(() ->
|
||||
{
|
||||
client.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Renegotiation Handshake
|
||||
|
@ -1574,15 +1491,11 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS));
|
||||
|
||||
// Write the rest of the request
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Three TLSRecords will be generated for the remainder of the content
|
||||
|
@ -1653,14 +1566,10 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
// Renegotiate
|
||||
Future<Object> renegotiation = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> renegotiation = threadPool.submit(() ->
|
||||
{
|
||||
client.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Renegotiation Handshake
|
||||
|
@ -1726,15 +1635,11 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS));
|
||||
|
||||
// Write the rest of the request
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
Future<Object> request = threadPool.submit(() ->
|
||||
{
|
||||
clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Three TLSRecords will be generated for the remainder of the content
|
||||
|
@ -1841,22 +1746,21 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
{
|
||||
final SSLSocket client = newClient();
|
||||
|
||||
threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
threadPool.submit(() ->
|
||||
{
|
||||
client.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Instead of passing the Client Hello, we simulate plain text was passed in
|
||||
proxy.flushToServer(0, "GET / HTTP/1.1\r\n".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// We expect that the server closes the connection immediately
|
||||
// We expect that the server sends the TLS Alert.
|
||||
TLSRecord record = proxy.readFromServer();
|
||||
Assert.assertNull(String.valueOf(record), record);
|
||||
Assert.assertNotNull(record);
|
||||
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertNull(record);
|
||||
|
||||
// Check that we did not spin
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
|
@ -1874,9 +1778,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
final OutputStream clientOutput = client.getOutputStream();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
idleHook = new Runnable()
|
||||
{
|
||||
public void run()
|
||||
idleHook = () ->
|
||||
{
|
||||
if (latch.getCount()==0)
|
||||
return;
|
||||
|
@ -1895,7 +1797,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
// Latch won't trigger and test will fail
|
||||
x.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
|
||||
|
@ -1974,6 +1875,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
Assert.assertEquals(record.getType(), Type.ALERT);
|
||||
record = proxy.readFromServer();
|
||||
}
|
||||
Assert.assertThat(record,nullValue());
|
||||
Assert.assertNull(record);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import java.net.Socket;
|
|||
import java.net.SocketTimeoutException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
@ -49,16 +48,16 @@ public abstract class SslBytesTest
|
|||
|
||||
public static class TLSRecord
|
||||
{
|
||||
private final SslBytesServerTest.TLSRecord.Type type;
|
||||
private final Type type;
|
||||
private final byte[] bytes;
|
||||
|
||||
public TLSRecord(SslBytesServerTest.TLSRecord.Type type, byte[] bytes)
|
||||
public TLSRecord(Type type, byte[] bytes)
|
||||
{
|
||||
this.type = type;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public SslBytesServerTest.TLSRecord.Type getType()
|
||||
public Type getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
@ -80,15 +79,15 @@ public abstract class SslBytesTest
|
|||
|
||||
private int code;
|
||||
|
||||
private Type(int code)
|
||||
Type(int code)
|
||||
{
|
||||
this.code = code;
|
||||
SslBytesServerTest.TLSRecord.Type.Mapper.codes.put(this.code, this);
|
||||
Mapper.codes.put(this.code, this);
|
||||
}
|
||||
|
||||
public static SslBytesServerTest.TLSRecord.Type from(int code)
|
||||
public static Type from(int code)
|
||||
{
|
||||
SslBytesServerTest.TLSRecord.Type result = SslBytesServerTest.TLSRecord.Type.Mapper.codes.get(code);
|
||||
Type result = Mapper.codes.get(code);
|
||||
if (result == null)
|
||||
throw new IllegalArgumentException("Invalid TLSRecord.Type " + code);
|
||||
return result;
|
||||
|
@ -96,7 +95,7 @@ public abstract class SslBytesTest
|
|||
|
||||
private static class Mapper
|
||||
{
|
||||
private static final Map<Integer, SslBytesServerTest.TLSRecord.Type> codes = new HashMap<>();
|
||||
private static final Map<Integer, Type> codes = new HashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +217,7 @@ public abstract class SslBytesTest
|
|||
}
|
||||
}
|
||||
|
||||
private TLSRecord read(SslBytesServerTest.TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException
|
||||
private TLSRecord read(TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException
|
||||
{
|
||||
while (length > 0)
|
||||
{
|
||||
|
@ -291,13 +290,11 @@ public abstract class SslBytesTest
|
|||
}
|
||||
}
|
||||
|
||||
public SslBytesServerTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException
|
||||
public SslBytesTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException
|
||||
{
|
||||
final CountDownLatch startLatch = new CountDownLatch(2);
|
||||
final CountDownLatch stopLatch = new CountDownLatch(2);
|
||||
Future<Object> clientToServer = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
public Object call() throws Exception
|
||||
Future<Object> clientToServer = threadPool.submit(() ->
|
||||
{
|
||||
startLatch.countDown();
|
||||
logger.debug("Automatic flow C --> S started");
|
||||
|
@ -317,11 +314,8 @@ public abstract class SslBytesTest
|
|||
stopLatch.countDown();
|
||||
logger.debug("Automatic flow C --> S finished");
|
||||
}
|
||||
}
|
||||
});
|
||||
Future<Object> serverToClient = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
public Object call() throws Exception
|
||||
Future<Object> serverToClient = threadPool.submit(() ->
|
||||
{
|
||||
startLatch.countDown();
|
||||
logger.debug("Automatic flow C <-- S started");
|
||||
|
@ -341,7 +335,6 @@ public abstract class SslBytesTest
|
|||
stopLatch.countDown();
|
||||
logger.debug("Automatic flow C <-- S finished");
|
||||
}
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(startLatch.await(5, TimeUnit.SECONDS));
|
||||
return new SslBytesServerTest.SimpleProxy.AutomaticFlow(stopLatch, clientToServer, serverToClient);
|
||||
|
|
|
@ -185,11 +185,9 @@ public class MultiPartContentProviderTest extends AbstractHttpClientServerTest
|
|||
.content(multiPart)
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isSucceeded())
|
||||
{
|
||||
Assert.assertTrue(String.valueOf(result.getFailure()), result.isSucceeded());
|
||||
Assert.assertEquals(200, result.getResponse().getStatus());
|
||||
responseLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait until the request has been sent.
|
||||
|
@ -408,11 +406,9 @@ public class MultiPartContentProviderTest extends AbstractHttpClientServerTest
|
|||
.content(multiPart)
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isSucceeded())
|
||||
{
|
||||
Assert.assertTrue(String.valueOf(result.getFailure()), result.isSucceeded());
|
||||
Assert.assertEquals(200, result.getResponse().getStatus());
|
||||
responseLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait until the request has been sent.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-continuation</artifactId>
|
||||
|
|
|
@ -218,6 +218,7 @@ public class Servlet3Continuation implements Continuation, AsyncListener
|
|||
{
|
||||
if (isSuspended())
|
||||
{
|
||||
_initial=false;
|
||||
if (ContinuationFilter.__debug)
|
||||
throw new ContinuationThrowable();
|
||||
throw __exception;
|
||||
|
@ -244,14 +245,12 @@ public class Servlet3Continuation implements Continuation, AsyncListener
|
|||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
_initial=false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
_initial=false;
|
||||
_expired=true;
|
||||
for (ContinuationListener listener:_listeners)
|
||||
listener.onTimeout(this);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-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.3.8-SNAPSHOT</version>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>jetty-distribution</artifactId>
|
||||
<name>Jetty :: Distribution Assemblies</name>
|
||||
|
|
|
@ -0,0 +1,257 @@
|
|||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.3.10-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>jetty-documentation</artifactId>
|
||||
<name>Jetty :: Documentation</name>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<asciidoctor.version>1.5.3</asciidoctor.version>
|
||||
<html.directory>${project.build.directory}/current</html.directory>
|
||||
</properties>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-assets</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<outputDirectory>${html.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<version>${asciidoctor.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>output-html</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>process-asciidoc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<backend>docbook</backend>
|
||||
<doctype>book</doctype>
|
||||
<sourceDocumentName>index.adoc</sourceDocumentName>
|
||||
<attributes>
|
||||
<imagesdir />
|
||||
<version>${project.version}</version>
|
||||
<linkcss>true</linkcss>
|
||||
<allow-uri-read>true</allow-uri-read>
|
||||
<toc>true</toc>
|
||||
<revnumber>${project.version}</revnumber>
|
||||
<JDURL>http://download.eclipse.org/jetty/stable-9/apidocs</JDURL>
|
||||
<JXURL>http://download.eclipse.org/jetty/stable-9/xref</JXURL>
|
||||
<SRCDIR>${basedir}/..</SRCDIR>
|
||||
<GITBROWSEURL>https://github.com/eclipse/jetty.project/master</GITBROWSEURL>
|
||||
</attributes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.agilejava.docbkx</groupId>
|
||||
<artifactId>docbkx-maven-plugin</artifactId>
|
||||
<version>2.0.14</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>html</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>generate-html</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<htmlStylesheet>css/docbook.css</htmlStylesheet>
|
||||
<htmlCustomization>${basedir}/src/main/docbkx-stylesheet/html/docbook.xsl</htmlCustomization>
|
||||
<preProcess>
|
||||
<!-- pull over the images from the source material -->
|
||||
<copy todir="target/docbkx/html/index/images" flatten="true">
|
||||
<fileset dir="src/main/asciidoc">
|
||||
<include name="**/*.png" />
|
||||
<include name="**/*.jpg" />
|
||||
<include name="**/*.svg" />
|
||||
<include name="**/*.dot" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy todir="target/docbkx/html/index/images">
|
||||
<fileset dir="src/main/docbkx-resources/images" />
|
||||
</copy>
|
||||
<copy todir="target/docbkx/html/index/css">
|
||||
<fileset dir="src/main/docbkx-resources/css" />
|
||||
</copy>
|
||||
<copy todir="target/docbkx/html/index/fonts">
|
||||
<fileset dir="src/main/docbkx-resources/fonts" />
|
||||
</copy>
|
||||
<copy todir="target/docbkx/html/index/js">
|
||||
<fileset dir="src/main/docbkx-resources/js" />
|
||||
</copy>
|
||||
</preProcess>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<!-- shared configuration -->
|
||||
<sourceDirectory>${project.build.directory}/generated-docs</sourceDirectory>
|
||||
<includes>index.xml</includes>
|
||||
<generatedSourceDirectory>${project.build.directory}/docbkx/generated</generatedSourceDirectory>
|
||||
<chunkedOutput>true</chunkedOutput>
|
||||
<highlightSource>true</highlightSource>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.sf.docbook</groupId>
|
||||
<artifactId>docbook-xml</artifactId>
|
||||
<!--version>5.0-all</version-->
|
||||
<version>5.1b4-all</version>
|
||||
<classifier>resources</classifier>
|
||||
<type>zip</type>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.xslthl</groupId>
|
||||
<artifactId>xslthl</artifactId>
|
||||
<version>2.0.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-xslt-tools</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/html.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<!--
|
||||
Couple of different approaches to generating pdf's
|
||||
-->
|
||||
<profile>
|
||||
<id>generate-pdf</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.agilejava.docbkx</groupId>
|
||||
<artifactId>docbkx-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-pdf</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>generate-pdf</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>index.xml</includes>
|
||||
<fop1Extensions>1</fop1Extensions>
|
||||
<paperType>A4</paperType>
|
||||
<foCustomization>src/main/docbkx-stylesheet/fo/docbook.xsl</foCustomization>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.sf.offo</groupId>
|
||||
<artifactId>fop-hyph</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<version>${asciidoctor.version}</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctorj-pdf</artifactId>
|
||||
<version>1.5.0-alpha.11</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>output-pdf</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>process-asciidoc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<backend>pdf</backend>
|
||||
<sourceHighlighter>rouge</sourceHighlighter>
|
||||
<sourceDocumentName>index.adoc</sourceDocumentName>
|
||||
<attributes>
|
||||
<imagesdir />
|
||||
<version>${project.version}</version>
|
||||
<linkcss>true</linkcss>
|
||||
<allow-uri-read>true</allow-uri-read>
|
||||
<toc>true</toc>
|
||||
<revnumber>${project.version}</revnumber>
|
||||
<JDURL>http://download.eclipse.org/jetty/stable-9/apidocs</JDURL>
|
||||
<JXURL>http://download.eclipse.org/jetty/stable-9/xref</JXURL>
|
||||
<SRCDIR>${basedir}/../jetty.project/</SRCDIR>
|
||||
<GITBROWSEURL>https://github.com/eclipse/jetty.project/master</GITBROWSEURL>
|
||||
<icons>font</icons>
|
||||
<pagenums />
|
||||
<toc />
|
||||
<idprefix />
|
||||
<idseparator>-</idseparator>
|
||||
</attributes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
|
@ -0,0 +1,281 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[alpn]]
|
||||
=== Introducing ALPN
|
||||
|
||||
The development of new web protocols such as HTTP/2 raised the need of protocol negotiation within a Transport Layer Security (TLS) handshake.
|
||||
A protocol negotiation called https://tools.ietf.org/html/rfc7301[ALPN] (Application Layer Protocol Negotiation) RFC7301 has been defined to accomplish this.
|
||||
|
||||
ALPN has now replaced the older (and now fully deprecated) NPN in the general Web of 2016.
|
||||
|
||||
For those browsers that support HTTP/2, they all now support the ALPN negotiation layers for TLS.
|
||||
|
||||
Starting with Jetty 9.3.0, only ALPN is supported by Jetty.
|
||||
|
||||
The Jetty project provides an implementation of the TLS extension for ALPN for OpenJDK 7 and OpenJDK 8.
|
||||
ALPN allows the application layer to negotiate which protocol to use over the secure connection.
|
||||
|
||||
Any protocol can be negotiated by ALPN within a TLS connection.
|
||||
The protocols that are most commonly negotiated are HTTP/2 (for browsers that support it) and, historically, SPDY.
|
||||
The ALPN implementation is therefore not HTTP/2 or SPDY specific in any way.
|
||||
Jetty's ALPN implementation, although hosted under the umbrella of the Jetty project, is independent of Jetty (the Servlet Container); you can use the ALPN implementation in any other Java network server.
|
||||
|
||||
The Jetty distribution will automatically enable ALPN when it is needed to by a HTTP/2 connector, so for the most part ALPN is transparent to the average deployer.
|
||||
This section provides the detail required for unusual deployments or developing to the ALPN API.
|
||||
|
||||
[[alpn-starting]]
|
||||
==== Starting the JVM
|
||||
|
||||
To enable ALPN support, start the JVM as follows:
|
||||
|
||||
[source,plain]
|
||||
----
|
||||
java -Xbootclasspath/p:<path_to_alpn_boot_jar> ...
|
||||
----
|
||||
|
||||
where `path_to_alpn_boot_jar` is the path on the file system for the ALPN Boot Jar file, for example, one at the Maven coordinates `org.mortbay.jetty.alpn:alpn-boot`.
|
||||
|
||||
Be certain link:#alpn-versions[to get the ALPN Boot Jar version which matches the version of your JRE].
|
||||
|
||||
[[alpn-osgi]]
|
||||
===== Starting in OSGi
|
||||
|
||||
To use ALPN in an OSGi environment, in addition to putting the ALPN jar on the boot classpath for the container, you will also need to deploy the jetty-osgi-alpn jar.
|
||||
This jar contains a Fragment-Host directive that ensures the ALPN classes will be available from the system bundle.
|
||||
|
||||
You can download the http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-alpn/[jetty-osgi-alpn jar] from Maven Central.
|
||||
|
||||
[[alpn-understanding]]
|
||||
==== Understanding the ALPN API
|
||||
|
||||
Applications need to interact with ALPN TLS extension protocol negotiations.
|
||||
For example, server applications need to know whether the client supports ALPN, and client applications needs to know whether the server supports ALPN.
|
||||
|
||||
To implement this interaction, Jetty's ALPN implementation provides an API to applications, hosted at Maven coordinates
|
||||
`org.eclipse.jetty.alpn:alpn-api`.
|
||||
You need to declare this dependency as provided, because the `alpn-boot` Jar already includes it (see the previous section), and it is therefore available from the boot classpath.
|
||||
|
||||
The API consists of a single class, `org.eclipse.jetty.alpn.ALPN`, and applications need to register instances of `SSLSocket` or `SSLEngine` with a `ClientProvider` or `ServerProvider` (depending on whether the application is a client application or server application).
|
||||
Refer to `ALPN` Javadocs and to the examples below for further details about client and server provider methods.
|
||||
|
||||
[[alpn-client-example]]
|
||||
==== Client Example
|
||||
|
||||
[source,java]
|
||||
----
|
||||
SSLContext sslContext = ...;
|
||||
final SSLSocket sslSocket = (SSLSocket)context.getSocketFactory().createSocket("localhost", server.getLocalPort());
|
||||
|
||||
ALPN.put(sslSocket, new ALPN.ClientProvider()
|
||||
{
|
||||
@Override
|
||||
public boolean supports()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols()
|
||||
{
|
||||
return Arrays.asList("h2", "http/1.1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported()
|
||||
{
|
||||
ALPN.remove(sslSocket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(String protocol)
|
||||
{
|
||||
ALPN.remove(sslSocket);
|
||||
System.out.println("Protocol Selected is: " + protocol);
|
||||
}
|
||||
});
|
||||
----
|
||||
|
||||
The ALPN implementation calls `ALPN.ClientProvider` methods `supports()`, `protocols()`, `unsupported()` and `selected(String)`, so that the client application can:
|
||||
|
||||
* decide whether to support ALPN.
|
||||
* provide the protocols supported.
|
||||
* know whether the server supports ALPN.
|
||||
* know the protocol chosen by the server.
|
||||
|
||||
[[alpn-server-example]]
|
||||
==== Server Example
|
||||
|
||||
The example for SSLEngine is identical, and you just need to replace the SSLSocket instance with an SSLEngine instance.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
final SSLSocket sslSocket = ...;
|
||||
ALPN.put(sslSocket, new ALPN.ServerProvider()
|
||||
{
|
||||
@Override
|
||||
public void unsupported()
|
||||
{
|
||||
ALPN.remove(sslSocket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String select(List<String> protocols);
|
||||
{
|
||||
ALPN.remove(sslSocket);
|
||||
return protocols.get(0);
|
||||
}
|
||||
});
|
||||
----
|
||||
|
||||
The ALPN implementation calls `ALPN.ServerProvider` methods `unsupported()`, and `select(List<String>),` so that the server application can:
|
||||
|
||||
* know whether the client supports ALPN.
|
||||
* select one of the protocols the client supports.
|
||||
|
||||
[[alpn-implementation]]
|
||||
==== Implementation Details
|
||||
|
||||
It is important that implementations of `ALPN.ServerProvider` and `ALPN.ClientProvider` remove the `sslSocket` or `sslEngine` when the negotiation is complete, like shown in the examples above.
|
||||
|
||||
Failing to do so will cause a memory leak.
|
||||
|
||||
[[alpn-tests]]
|
||||
==== Unit Tests
|
||||
|
||||
You can write and run unit tests that use the ALPN implementation.
|
||||
The solution that we use with Maven is to specify an additional command line argument to the Surefire plugin:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<project>
|
||||
|
||||
<properties>
|
||||
<alpn-boot-version>8.1.4.v20150727</alpn-boot-version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>
|
||||
-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn-boot-version}/alpn-boot-${alpn-boot-version}.jar
|
||||
</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
...
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
...
|
||||
|
||||
</project>
|
||||
----
|
||||
|
||||
[[alpn-debugging]]
|
||||
==== Debugging
|
||||
|
||||
You can enable debug logging for the ALPN implementation in this way:
|
||||
|
||||
....
|
||||
ALPN.debug = true;
|
||||
....
|
||||
|
||||
Since the ALPN class is in the boot classpath, we chose not to use logging libraries because we do not want to override application logging library choices; therefore the logging is performed directly on `System.err.`
|
||||
|
||||
[[alpn-license-details]]
|
||||
==== License Details
|
||||
|
||||
The ALPN implementation relies on modification of a few OpenJDK classes and on a few new classes that need to live in the `sun.security.ssl` package.
|
||||
These classes are released under the same GPLv2+exception license of OpenJDK.
|
||||
|
||||
The ALPN class and its nested classes are released under same license as the classes of the Jetty project.
|
||||
|
||||
[[alpn-versions]]
|
||||
==== Versions
|
||||
|
||||
The ALPN implementation, relying on modifications of OpenJDK classes, updates every time there are updates to the modified OpenJDK classes.
|
||||
|
||||
.ALPN vs. OpenJDK versions
|
||||
[cols=",",options="header",]
|
||||
|=============================
|
||||
|OpenJDK version |ALPN version
|
||||
|1.7.0u40 |7.1.0.v20141016
|
||||
|1.7.0u45 |7.1.0.v20141016
|
||||
|1.7.0u51 |7.1.0.v20141016
|
||||
|1.7.0u55 |7.1.0.v20141016
|
||||
|1.7.0u60 |7.1.0.v20141016
|
||||
|1.7.0u65 |7.1.0.v20141016
|
||||
|1.7.0u67 |7.1.0.v20141016
|
||||
|1.7.0u71 |7.1.2.v20141202
|
||||
|1.7.0u72 |7.1.2.v20141202
|
||||
|1.7.0u75 |7.1.3.v20150130
|
||||
|1.7.0u76 |7.1.3.v20150130
|
||||
|1.7.0u79 |7.1.3.v20150130
|
||||
|1.7.0u80 |7.1.3.v20150130
|
||||
|1.8.0 |8.1.0.v20141016
|
||||
|1.8.0u05 |8.1.0.v20141016
|
||||
|1.8.0u11 |8.1.0.v20141016
|
||||
|1.8.0u20 |8.1.0.v20141016
|
||||
|1.8.0u25 |8.1.2.v20141202
|
||||
|1.8.0u31 |8.1.3.v20150130
|
||||
|1.8.0u40 |8.1.3.v20150130
|
||||
|1.8.0u45 |8.1.3.v20150130
|
||||
|1.8.0u51 |8.1.4.v20150727
|
||||
|1.8.0u60 |8.1.5.v20150921
|
||||
|1.8.0u65 |8.1.6.v20151105
|
||||
|1.8.0u66 |8.1.6.v20151105
|
||||
|1.8.0u71 |8.1.7.v20160121
|
||||
|1.8.0u72 |8.1.7.v20160121
|
||||
|1.8.0u73 |8.1.7.v20160121
|
||||
|1.8.0u74 |8.1.7.v20160121
|
||||
|1.8.0u77 |8.1.7.v20160121
|
||||
|1.8.0u91 |8.1.7.v20160121
|
||||
|1.8.0u92 |8.1.8.v20160420
|
||||
|=============================
|
||||
|
||||
[[alpn-build]]
|
||||
==== How to build ALPN
|
||||
|
||||
This section is for Jetty developers that need to update the ALPN implementation with the OpenJDK versions.
|
||||
|
||||
Clone the OpenJDK repository with the following command:
|
||||
|
||||
[source, screen]
|
||||
....
|
||||
$ hg clone http://hg.openjdk.java.net/jdk7u/jdk7u jdk7u # OpenJDK 7
|
||||
$ hg clone http://hg.openjdk.java.net/jdk8u/jdk8u jdk8u # OpenJDK 8
|
||||
$ cd !$
|
||||
$ ./get_source.sh
|
||||
|
||||
....
|
||||
|
||||
To update the source to a specific tag, use the following command:
|
||||
|
||||
[source, screen]
|
||||
....
|
||||
$ ./make/scripts/hgforest.sh update <tag-name>
|
||||
|
||||
....
|
||||
|
||||
The list of OpenJDK tags can be obtained from these pages:
|
||||
http://hg.openjdk.java.net/jdk7u/jdk7u/tags[OpenJDK 7] /
|
||||
http://hg.openjdk.java.net/jdk8u/jdk8u/tags[OpenJDK 8].
|
||||
|
||||
Then you need to compare and incorporate the OpenJDK source changes into the modified OpenJDK classes at the https://github.com/jetty-project/jetty-alpn[ALPN GitHub Repository], branch `openjdk7` for OpenJDK 7 and branch `master` for OpenJDK 8.
|
|
@ -0,0 +1,20 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[alpn-chapter]]
|
||||
== ALPN
|
||||
|
||||
include::alpn.adoc[]
|
|
@ -0,0 +1,25 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[annotations]]
|
||||
== Annotations
|
||||
|
||||
Jetty supports the servlet specification annotations.
|
||||
It is not enable by default, so the following sections show you how to enable it, and how to use them.
|
||||
|
||||
include::quick-annotations-setup.adoc[]
|
||||
include::using-annotations.adoc[]
|
||||
include::using-annotations-embedded.adoc[]
|
|
@ -0,0 +1,43 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[annotations-quick-setup]]
|
||||
=== Quick Setup
|
||||
|
||||
==== Jetty Distribution
|
||||
|
||||
If you are using the jetty distribution, then annotations are enabled by default.
|
||||
The *annotations* link:#startup-modules[module] and its transitive dependencies are responsible for making annotation processing available.
|
||||
|
||||
Note that annotations that relate to link:#jndi[JNDI], such as @Resource and @Resources are enabled via the *jndi* module, which is a transitive dependency on the annotations module, and thus is also enabled by default for the distribution.
|
||||
|
||||
==== Jetty Maven Plugin
|
||||
|
||||
Annotations and JNDI are pre-enabled for the maven plugin.
|
||||
|
||||
==== Embedding
|
||||
|
||||
To use annotations in an embedded scenario, you will need to include the jetty-annotations jar and all its dependencies onto your classpath.
|
||||
You will also need to include the org.eclipse.jetty.annotations.AnnotationConfiguration into the list of link:#webapp-configurations[Configuration classes] applied to the org.eclipse.jetty.webapp.WebAppContext representing your webapp.
|
||||
|
||||
Here is an example application that sets up the standard test-spec.war webapp from the distribution in embedded fashion.
|
||||
It can be found in the jetty git repository in the examples/embedded project.
|
||||
Note that the test-spec.war uses not only annotations, but also link:#jndi[JNDI], so this example also enables their processing (via the link:#jndi-configuration-classes[org.eclipse.jetty.plus.webapp.EnvConfiguration], link:#jndi-configuration-classes[org.eclipse.jetty.plus.webapp.PlusConfiguration] and their related jars).
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java[]
|
||||
----
|
|
@ -0,0 +1,190 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[using-annotations-embedded]]
|
||||
=== Using Annotations with Jetty Embedded
|
||||
|
||||
==== Setting up the Classpath
|
||||
|
||||
You will need to place the following jetty jars onto the classpath of your application. You can obtain them from the http://download.eclipse.org/jetty/stable-9/dist/[jetty distribution], or the http://central.maven.org/maven2/org/eclipse/jetty/jetty-annotations[maven repository]:
|
||||
|
||||
....
|
||||
jetty-plus.jar
|
||||
jetty-annotations.jar
|
||||
....
|
||||
|
||||
You will also need the http://asm.ow2.org/[asm] jar, which you can obtain from the http://download.eclipse.org/jetty/orbit/[Jetty dependencies site].
|
||||
|
||||
==== Example
|
||||
|
||||
Here's an example application that sets up a Jetty server, does some setup to ensure that annotations are scanned and deploys a webapp that uses annotations.
|
||||
This example also uses the @Resource annotation which involves JNDI, so we would also link:#jndi-embedded[add the necessary jndi jars to the classpath]., and we also add in the configuration classes that are responsible for JNDI (see line 19).
|
||||
|
||||
Here is the embedding code:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
import org.eclipse.jetty.security.HashLoginService;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
/**
|
||||
* ServerWithAnnotations
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ServerWithAnnotations
|
||||
{
|
||||
public static final void main(String args[]) throws Exception
|
||||
{
|
||||
//Create the server
|
||||
Server server = new Server(8080);
|
||||
|
||||
//Enable parsing of jndi-related parts of web.xml and jetty-env.xml
|
||||
org.eclipse.jetty.webapp.Configuration.ClassList classlist = org.eclipse.jetty.webapp.Configuration.ClassList.setServerDefault(server);
|
||||
classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration");
|
||||
classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.annotations.AnnotationConfiguration");
|
||||
|
||||
//Create a WebApp
|
||||
WebAppContext webapp = new WebAppContext();
|
||||
webapp.setContextPath("/");
|
||||
webapp.setWar("../../tests/test-webapps/test-servlet-spec/test-spec-webapp/target/test-spec-webapp-9.0.4-SNAPSHOT.war");
|
||||
server.setHandler(webapp);
|
||||
|
||||
//Register new transaction manager in JNDI
|
||||
//At runtime, the webapp accesses this as java:comp/UserTransaction
|
||||
org.eclipse.jetty.plus.jndi.Transaction transactionMgr = new org.eclipse.jetty.plus.jndi.Transaction(new com.acme.MockUserTransaction());
|
||||
|
||||
//Define an env entry with webapp scope.
|
||||
org.eclipse.jetty.plus.jndi.EnvEntry maxAmount = new org.eclipse.jetty.plus.jndi.EnvEntry (webapp, "maxAmount", new Double(100), true);
|
||||
|
||||
|
||||
// Register a mock DataSource scoped to the webapp
|
||||
org.eclipse.jetty.plus.jndi.Resource mydatasource = new org.eclipse.jetty.plus.jndi.Resource(webapp, "jdbc/mydatasource", new com.acme.MockDataSource());
|
||||
|
||||
// Configure a LoginService
|
||||
HashLoginService loginService = new HashLoginService();
|
||||
loginService.setName("Test Realm");
|
||||
loginService.setConfig("src/test/resources/realm.properties");
|
||||
server.addBean(loginService);
|
||||
|
||||
|
||||
server.start();
|
||||
server.join();
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
On line 19 we add in the configuration classes responsible for setting up JNDI and java:comp/env.
|
||||
|
||||
On line 20 we add in the configuration class that ensures annotations are inspected.
|
||||
|
||||
On lines 30, 33 and 37 we set up some JNDI resources that we will be able to reference with @Resource annotations.
|
||||
|
||||
With the setup above, we can create a servlet that uses annotations and Jetty will honour the annotations when the webapp is deployed:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
import javax.annotation.security.DeclareRoles;
|
||||
import javax.annotation.security.RunAs;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.annotation.WebInitParam;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.sql.DataSource;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
/**
|
||||
* AnnotationTest
|
||||
*
|
||||
* Use servlet 3.0 annotations from within Jetty.
|
||||
*
|
||||
* Also uses servlet 2.5 resource injection and lifecycle callbacks
|
||||
*/
|
||||
|
||||
@RunAs("special")
|
||||
@WebServlet(urlPatterns = {"/","/test/*"}, name="AnnotationTest", initParams={@WebInitParam(name="fromAnnotation", value="xyz")})
|
||||
@DeclareRoles({"user","client"})
|
||||
public class AnnotationTest extends HttpServlet
|
||||
{
|
||||
private DataSource myDS;
|
||||
|
||||
@Resource(mappedName="UserTransaction")
|
||||
private UserTransaction myUserTransaction;
|
||||
|
||||
@Resource(mappedName="maxAmount")
|
||||
private Double maxAmount;
|
||||
|
||||
|
||||
@Resource(mappedName="jdbc/mydatasource")
|
||||
public void setMyDatasource(DataSource ds)
|
||||
{
|
||||
myDS=ds;
|
||||
}
|
||||
|
||||
|
||||
@PostConstruct
|
||||
private void myPostConstructMethod ()
|
||||
{
|
||||
System.err.println("PostConstruct called");
|
||||
}
|
||||
|
||||
|
||||
@PreDestroy
|
||||
private void myPreDestroyMethod()
|
||||
{
|
||||
System.err.println("PreDestroy called");
|
||||
}
|
||||
|
||||
public void init(ServletConfig config) throws ServletException
|
||||
{
|
||||
super.init(config);
|
||||
}
|
||||
|
||||
|
||||
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
doGet(request, response);
|
||||
}
|
||||
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
response.setContentType("text/html");
|
||||
ServletOutputStream out = response.getOutputStream();
|
||||
out.println("<html>");
|
||||
out.println("<body>");
|
||||
out.println("<h1>Results</h1>");
|
||||
out.println(myDS.toString());
|
||||
out.println("<br/>");
|
||||
out.println(maxAmount.toString());
|
||||
out.println("</body>");
|
||||
out.println("</html>");
|
||||
out.flush();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
|
@ -0,0 +1,154 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[using-annotations]]
|
||||
=== Working with Annotations
|
||||
|
||||
==== Which Annotations Are Supported
|
||||
|
||||
Jetty supports interpretation and application of the following annotations:
|
||||
|
||||
* @Resource
|
||||
* @Resources
|
||||
* @PostConstruct
|
||||
* @PreDestroy
|
||||
* @DeclaredRoles
|
||||
* @RunAs
|
||||
* @MultipartConfig
|
||||
* @WebServlet
|
||||
* @WebFilter
|
||||
* @WebListener
|
||||
* @WebInitParam
|
||||
* @ServletSecurity, @HttpConstraint, @HttpMethodConstraint
|
||||
* @HandlesTypes (on ServletContainerInitializers)
|
||||
|
||||
[[discoverable_introspectable_annotations]]
|
||||
==== Discovered vs Introspected Annotations
|
||||
|
||||
Some types of annotation can be placed on any classes, not necessarily just those with which the container interacts directly.
|
||||
We call these type of annotations "discovered" to indicate that the container must take proactive action to go out and find them.
|
||||
The other type of annotation we call "introspected", meaning that they occur on classes with which the container interacts during their lifecycle (eg javax.servlet.Servlet, javax.servlet.Filter etc), and hence can be found by simple inspection of the class at that point.
|
||||
|
||||
Some examples of discovered annotations are:
|
||||
|
||||
* @WebServlet
|
||||
* @WebFilter
|
||||
* @WebListener
|
||||
|
||||
Some examples of introspected annotations are:
|
||||
|
||||
* @PostConstruct
|
||||
* @PreDestroy
|
||||
* @Resource
|
||||
|
||||
[[jars-scanned-for-annotations]]
|
||||
==== Which Jars Are Scanned For Discovered Annotations
|
||||
|
||||
The web.xml file can contain the attribute `metadata-complete`.
|
||||
If this is set to `true`, then _no_ scanning of discoverable annotations takes place.
|
||||
However, scanning of classes may _still_ occur because of http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[javax.servlet.ServletContainerInitializer]s.
|
||||
Classes implementing this interface are found by Jetty using the http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html[javax.util.ServiceLoader] mechanism, and if one is present _and_ it includes the @HandlesTypes annotation, then Jetty must scan the class hierarchy of the web application.
|
||||
This may be very time-consuming if you have many jars in the container's path or in the webapp's WEB-INF/lib.
|
||||
|
||||
If scanning is to take place - because either `metadata-complete` is `false` or missing, or because there are one or more http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[javax.servlet.ServletContainerIntializer]s with @HandlesTypes - then Jetty must consider both the container's classpath and the webapp's classpath.
|
||||
|
||||
By default, Jetty will _not_ scan any classes that are on the container's classpath.
|
||||
If you need to cause jars and classes that are on the container's classpath to be scanned, then you can use the link:#container-include-jar-pattern[org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern] link:#context_attributes[context attribute] to specify a pattern for jars and directories from the container's classpath to scan.
|
||||
|
||||
By default, Jetty will scan __all__classes from `WEB-INF/classes`, and all jars from `WEB-INF/lib` according to the order, if any, established by absolute or relative ordering clauses in web.xml.
|
||||
If your webapp contains many jars, you can significantly speed up deployment by omitting them from scanning.
|
||||
To do this, use the link:#web-inf-include-jar-pattern[org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern] link:#context_attributes[context attribute] to define the patterns of jars that you specifically want to be scanned.
|
||||
|
||||
Note that if you have configured an link:#using-extra-classpath-method[extraClasspath] for the webapp, then it participates in the scanning process too.
|
||||
Any classes dirs are treated the same for scanning purposes as if they were in WEB-INF/classes and jars are treated as if they were in WEB-INF/lib.
|
||||
|
||||
See also the next section on link:#servlet-container-initializers[ServletContainerInitializers] if you need to link:#servlet-container-initializers[control the order in which they are applied].
|
||||
|
||||
==== Multi-threaded Annotation Scanning
|
||||
|
||||
Since jetty-9.1,link:#jars-scanned-for-annotations[if annotation scanning is to be performed], by default Jetty will do it in a multi-threaded manner in order to complete it in the minimum amount of time.
|
||||
|
||||
If for some reason you don't want to do it multi-threaded, you can configure Jetty to revert to single-threaded scanning.
|
||||
You have several ways to configure this:
|
||||
|
||||
1. set the link:#context_attributes[context attribute] `org.eclipse.jetty.annotations.multiThreaded` to `false`
|
||||
2. set the link:#server_attributes[Server attribute] `org.eclipse.jetty.annotations.multiThreaded` to `false`
|
||||
3. set the System property `org.eclipse.jetty.annotations.multiThreaded` to `false`
|
||||
|
||||
Method 1 will only affect the current webapp.
|
||||
Method 2 will affect all webapps deployed to the same Server instance.
|
||||
Method 3 will affect all webapps deployed in the same jvm.
|
||||
|
||||
By default, Jetty will wait a maximum of 60 seconds for all of the scanning threads to complete.
|
||||
You can set this to a higher or lower number of seconds by doing one of the following:
|
||||
|
||||
1. set the link:#context_attributes[context attribute] `org.eclipse.jetty.annotations.maxWait`
|
||||
2. set the link:#server_attributes[Server attribute] `org.eclipse.jetty.annotations.maxWait`
|
||||
3. set the System property `org.eclipse.jetty.annotations.maxWait`
|
||||
|
||||
Method 1 will only affect the current webapp.
|
||||
Method 2 will affect all webapps deployed to the same Server instance.
|
||||
Method 3 will affect all webapps deployed in the same jvm.
|
||||
|
||||
[[servlet-container-initializers]]
|
||||
==== ServletContainerInitializers
|
||||
|
||||
http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[javax.servlet.ServletContainerInitializers] can exist in: the container's classpath, the webapp's WEB-INF/classes directory, the webapp's WEB-INF/lib jars, or any external link:#using-extra-classpath-method[extraClasspath] that you have configured on the webapp.
|
||||
|
||||
The http://jcp.org/aboutJava/communityprocess/final/jsr340/[Servlet Specification] does not define any order in which these ServletContainerInitializers must be called when the webapp starts.
|
||||
Since jetty-9.1, by default Jetty will call them in the following order:
|
||||
|
||||
1. ServletContainerInitializers from the container's classpath
|
||||
2. ServletContainerInitializers from WEB-INF/classes
|
||||
3. ServletContainerInitializers from WEB-INF/lib jars __in the order established in web.xml__, or in the order that the SCI is returned by the http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html[javax.util.ServiceLoader] if there is _no_ ordering
|
||||
|
||||
As is the case with annotation scanning, the link:#using-extra-classpath-method[extraClasspath] is fully considered for ServletContainerInitializer callbacks. ServletContainerInitializers derived from a classes dir on the extraClasspath and jars from an extraClasspath for the webapp are called in step 2 and 3 respectively.
|
||||
|
||||
===== Controlling the order of ServletContainerInitializer invocation
|
||||
|
||||
If you need ServletContainerInitializers called in a specific order that is different from that outlined above, then you can use the link:#context_attributes[context attribute] `org.eclipse.jetty.containerInitializerOrder`. Set it to a list of comma separated class names of ServletContainerInitializers in the order that you want them applied.
|
||||
You may optionally use the wildcard character "*" *once* in the list.
|
||||
It will match all ServletContainerInitializers not explicitly named in the list. Here's an example, setting the context attribute in code (although you can also do the link:#intro-jetty-configuration-webapps[same in xml]):
|
||||
|
||||
[source,java]
|
||||
----
|
||||
WebAppContext context = new WebAppContext();
|
||||
context.setAttribute("org.eclipse.jetty.containerInitializerOrder",
|
||||
"org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer, com.acme.Foo.MySCI, *");
|
||||
----
|
||||
|
||||
In this example, we ensure that the WebSocketServerContainerInitializer is the very first ServletContainerInitializer that is called, followed by MySCI and then any other ServletContainerInitializers that were discovered but not yet called.
|
||||
|
||||
[[excluding-scis]]
|
||||
===== Excluding ServletContainerInitializers
|
||||
|
||||
By default, as according to the Servlet Specification, all ServletContainerInitializers that are discovered are invoked (see above for how to control the invocation order).
|
||||
Sometimes, you may need to prevent some being called at all.
|
||||
|
||||
In this case, you can define the `org.eclipse.jetty.containerInitializerExclusionPattern` link:#context_attributes[context attribute].
|
||||
This is a regular expression that defines http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html[patterns] of classnames that you want to exclude.
|
||||
Here's an example, setting the context attribute in code, although you may do exactly the link:#intro-jetty-configuration-webapps[same in xml]:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
WebAppContext context = new WebAppContext();
|
||||
context.setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern",
|
||||
"com.acme.*|com.corp.SlowContainerInitializer");
|
||||
----
|
||||
|
||||
In this example we exclude *all* ServletContainerInitializers in the com.acme package, and the SlowContainerInitializer.
|
||||
|
||||
It is possible to use exclusion and ordering together to control ServletContainerInitializer invocation - the exclusions will be applied before the ordering.
|
|
@ -0,0 +1,38 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[balancer-servlet]]
|
||||
=== Balancer Servlet
|
||||
|
||||
[[balancer-servlet-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.proxy.BalancerServlet`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-proxy
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/proxy/BalancerServlet.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/proxy/BalancerServlet.html
|
||||
|
||||
[[balancer-servlet-usage]]
|
||||
==== Usage
|
||||
|
||||
The Balancer servlet allows for simple, sticky round robin load balancing leveraging the ProxyServlet that is distributed with Jetty.
|
||||
|
||||
In addition to the parameters for ProxyServlet, the following are available for the balancer servlet:
|
||||
|
||||
stickySessions::
|
||||
true if sessions should be sticky for subsequent requests
|
||||
balancerMember.<name>.proxyTo::
|
||||
One of more of these are required and will be the locations that are used to proxy traffic to.
|
|
@ -0,0 +1,44 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[cgi-servlet]]
|
||||
=== CGI Servlet
|
||||
|
||||
[[cgi-servlet-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.servlets.CGI`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-servlets
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/servlets/CGI.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/servlets/CGI.html
|
||||
|
||||
[[cgi-servlet-usage]]
|
||||
==== Usage
|
||||
|
||||
The CGI servlet class extends the abstract HttpServlet class.
|
||||
When the init parameter is called, the cgi bin directory is set with the cgibinResourceBase.
|
||||
Otherwise, it defaults to the resource base of the context.
|
||||
See CGI javadoc.
|
||||
|
||||
The cgi bin uses three parameters:
|
||||
|
||||
commandPrefix::
|
||||
The init parameter obtained when there is a prefix set to all commands directed to the method exec.
|
||||
Path::
|
||||
An init parameter passed to the exec environment as a PATH.
|
||||
This must be run unpacked somewhere in the filesystem.
|
||||
ENV_::
|
||||
An init parameter that points to an environment variable with the name stripped of the leading ENV_ and using the init parameter value.
|
|
@ -0,0 +1,44 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[advanced-extras]]
|
||||
== Provided Servlets, Filters, and Handlers
|
||||
|
||||
Jetty ships with a bundle of servlets that interact with the key classes.
|
||||
Most are in the org.eclipse.jetty.servlets package.
|
||||
These servlets and filters are among the principle elements of Jetty as a component-based infrastructure that holds and runs J2EE applications.
|
||||
As described, they play a major role in running and maintaining the Jetty server.
|
||||
|
||||
Also included are a number of Jetty specific handlers that allow access to internals of jetty that would not normally be exposed and are very useful testing environments and many production scenarios.
|
||||
|
||||
include::default-servlet.adoc[]
|
||||
include::proxy-servlet.adoc[]
|
||||
include::balancer-servlet.adoc[]
|
||||
include::cgi-servlet.adoc[]
|
||||
include::qos-filter.adoc[]
|
||||
include::dos-filter.adoc[]
|
||||
include::gzip-filter.adoc[]
|
||||
include::cross-origin-filter.adoc[]
|
||||
include::resource-handler.adoc[]
|
||||
include::debug-handler.adoc[]
|
||||
include::statistics-handler.adoc[]
|
||||
include::ipaccess-handler.adoc[]
|
||||
include::moved-context-handler.adoc[]
|
||||
include::shutdown-handler.adoc[]
|
||||
include::default-handler.adoc[]
|
||||
include::error-handler.adoc[]
|
||||
include::rewrite-handler.adoc[]
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[cross-origin-filter]]
|
||||
=== Cross Origin Filter
|
||||
|
||||
[[cross-origin-filter-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.servlets.CrossOriginFilter`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-servlets
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/servlets/CrossOriginFilter.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/servlets/CrossOriginFilter.html
|
||||
|
||||
[[cross-origin-filter-usage]]
|
||||
==== Usage
|
||||
|
||||
HTTP requests made from a script are subject to well known restrictions, the most prominent being the same domain policy.
|
||||
|
||||
Firefox 3.5 introduced support for W3C's Access Control for Cross-Site Requests specification, which requires a compliant client (for example, Firefox 3.5) and a compliant server (via this servlet filter).
|
||||
|
||||
This filter implements the required bits to support the server-side contract of the specification, and will allow a compliant client to perform cross-domain requests via the standard XMLHttpRequest object.
|
||||
If the client does not issue a compliant cross-domain request, this filter does nothing, and its overhead is the check of the presence of the cross-domain HTTP header.
|
||||
|
||||
This is extremely useful in CometD web applications where it is now possible to perform cross-domain long polling without using script injection (also known as the JSONP transport), and therefore removing all the downsides that the JSONP transport has (it's chattier, does not react quickly to failures, has a message size limit, uses GET instead of POST, etc.).
|
||||
|
||||
[[cross-origin-setup]]
|
||||
==== Setup
|
||||
|
||||
You will need to put the jetty-servlets.jar file onto your classpath.
|
||||
If you are creating a webapp, ensure that this jar is included in your webapp's WEB-INF/lib.
|
||||
Or, if you are running jetty embedded you will need to ensure that jetty-servlets.jar is on the execution classpath.
|
||||
You can download the jetty-servlets.jar from the Maven Central Repository at http://central.maven.org/maven2/org/eclipse/jetty/jetty-servlets/.
|
||||
|
||||
[[cross-origin-config]]
|
||||
==== Configuration
|
||||
|
||||
This is a regular servlet filter that must be configured in web.xml.
|
||||
|
||||
It supports the following configuration parameters:
|
||||
|
||||
allowedOrigins::
|
||||
a comma separated list of origins that are allowed to access the resources.
|
||||
Default value is: * (all origins)
|
||||
allowedMethods::
|
||||
a comma separated list of HTTP methods that are allowed to be used when accessing the resources.
|
||||
Default value is: GET,POST,HEAD
|
||||
allowedHeaders::
|
||||
a comma separated list of HTTP headers that are allowed to be specified when accessing the resources.
|
||||
Default value is: X-Requested-With,Content-Type,Accept,Origin
|
||||
allowCredentials::
|
||||
a boolean indicating if the resource allows requests with credentials.
|
||||
Default value is: true
|
||||
preflightMaxAge::
|
||||
the number of seconds that preflight requests can be cached by the client.
|
||||
Default value is 1800 seconds (30 minutes)
|
||||
chainPreflight::
|
||||
if true preflight requests are chained to their target resource for normal handling (as an OPTION request).
|
||||
Otherwise the filter will response to the preflight.
|
||||
Default is true.
|
||||
exposedHeaders::
|
||||
a comma separated list of HTTP headers that are allowed to be exposed on the client.
|
||||
Default value is the empty list.
|
||||
|
||||
A typical configuration could be:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<web-app>
|
||||
|
||||
<filter>
|
||||
<filter-name>cross-origin</filter-name>
|
||||
<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>cross-origin</filter-name>
|
||||
<url-pattern>/cometd/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
</web-app>
|
||||
|
||||
|
||||
----
|
|
@ -0,0 +1,70 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[debug-handler]]
|
||||
=== Debug Handler
|
||||
|
||||
[[debug-handler-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.server.handler.DebugHandler`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-server
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/DebugHandler.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/server/handler/DebugHandler.html
|
||||
|
||||
[[debug-handler-usage]]
|
||||
==== Usage
|
||||
|
||||
A simple handler that is useful to debug incoming traffic.
|
||||
It will log entry and exit points of http requests as well as the response code.
|
||||
|
||||
==== Usage in standard distribution
|
||||
|
||||
Simply include jetty-debug.xml in your *.ini configs.
|
||||
For example in start.ini.
|
||||
|
||||
==== Embedded usage
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Server server = new Server(8080);
|
||||
RolloverFileOutputStream outputStream = new RolloverFileOutputStream("MeinLogPfad/yyyy_mm_dd.request.log", true,10);
|
||||
|
||||
DebugHandler debugHandler = new DebugHandler();
|
||||
debugHandler.setOutputStream(outputStream);
|
||||
debugHandler.setHandler(server.getHandler());
|
||||
|
||||
server.setHandler(debugHandler);
|
||||
server.start();
|
||||
|
||||
----
|
||||
|
||||
==== Some example output
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
15:14:05.838:qtp551889550-13-selector-0 OPENED HttpConnection@e910ee4{IDLE},g=HttpGenerator{s=START},p=HttpParser{s=START,0 of 0}
|
||||
15:14:05.846:qtp551889550-57:http://0:0:0:0:0:0:0:1:8080/ REQUEST 0:0:0:0:0:0:0:1 GET __utma=111872281.10102721.1321534299.1369833564.1370447492.35; __utmz=111872281.1321534299.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _opt_vi_RPY720HZ=75E12E63-0CD0-4D6F-8383-C90D5C8397C7; Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0
|
||||
15:14:05.894:qtp551889550-57:http://0:0:0:0:0:0:0:1:8080/ RESPONSE 200 null
|
||||
15:14:05.959:qtp551889550-59:http://0:0:0:0:0:0:0:1:8080/jetty.css REQUEST 0:0:0:0:0:0:0:1 GET __utma=111872281.10102721.1321534299.1369833564.1370447492.35; __utmz=111872281.1321534299.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _opt_vi_RPY720HZ=75E12E63-0CD0-4D6F-8383-C90D5C8397C7; visited=yes; Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0
|
||||
15:14:05.962:qtp551889550-59:http://0:0:0:0:0:0:0:1:8080/jetty.css RESPONSE 200 null
|
||||
15:14:06.052:qtp551889550-57:http://0:0:0:0:0:0:0:1:8080/images/jetty-header.jpg REQUEST 0:0:0:0:0:0:0:1 GET __utma=111872281.10102721.1321534299.1369833564.1370447492.35; __utmz=111872281.1321534299.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _opt_vi_RPY720HZ=75E12E63-0CD0-4D6F-8383-C90D5C8397C7; visited=yes; Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0
|
||||
15:14:06.055:qtp551889550-57:http://0:0:0:0:0:0:0:1:8080/images/jetty-header.jpg RESPONSE 200 null
|
||||
15:14:07.248:qtp551889550-59:http://0:0:0:0:0:0:0:1:8080/favicon.ico REQUEST 0:0:0:0:0:0:0:1 GET __utma=111872281.10102721.1321534299.1369833564.1370447492.35; __utmz=111872281.1321534299.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _opt_vi_RPY720HZ=75E12E63-0CD0-4D6F-8383-C90D5C8397C7; visited=yes; Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0
|
||||
15:14:07.251:qtp551889550-59:http://0:0:0:0:0:0:0:1:8080/favicon.ico RESPONSE 404 text/html;charset=ISO-8859-1
|
||||
15:14:09.330:qtp551889550-57 CLOSED HttpConnection@e910ee4{INTERESTED},g=HttpGenerator{s=START},p=HttpParser{s=START,0 of -1}
|
||||
|
||||
----
|
|
@ -0,0 +1,54 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[default-handler]]
|
||||
=== Default Handler
|
||||
|
||||
[[default-handler-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.server.handler.DefaultHandler`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-server
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/DefaultHandler.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/server/handler/DefaultHandler.html
|
||||
|
||||
[[default-handler-usage]]
|
||||
==== Usage
|
||||
|
||||
A simple handler that is useful to terminate handler chains with a clean fashion.
|
||||
As in the example below, if a resource to be served is not matched within the resource handler, the DefaultHandler will take care of producing a 404 page.
|
||||
This class is a useful template to either extend and embrace or simply provide a similar implementation for customizing to your needs.
|
||||
There is also an link:#error-handler[Error Handler] that services errors related to the servlet api specification so it is best to not get the two confused.
|
||||
|
||||
_____
|
||||
[NOTE]
|
||||
The DefaultHandler will also handle serving out the flav.ico file should a request make it through all of the other handlers without being resolved.
|
||||
_____
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
Server server = new Server(8080);
|
||||
HandlerList handlers = new HandlerList();
|
||||
ResourceHandler resourceHandler = new ResourceHandler();
|
||||
resourceHandler.setBaseResource(Resource.newResource("."));
|
||||
handlers.setHandlers(new Handler[]
|
||||
{ resourceHandler, new DefaultHandler() });
|
||||
server.setHandler(handlers);
|
||||
server.start();
|
||||
|
||||
|
||||
----
|
|
@ -0,0 +1,66 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[default-servlet]]
|
||||
=== Default Servlet
|
||||
|
||||
[[default-servlet-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.servlet.DefaultServlet`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-servlet
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/servlet/DefaultServlet.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/servlet/DefaultServlet.html
|
||||
|
||||
[[default-servlet-usage]]
|
||||
==== Usage
|
||||
|
||||
The DefaultServlet implements the ResourceFactory interface and extends the HttpServlet abstract class.
|
||||
It is usually mapped to / and provides handling for static content, OPTION and TRACE methods for the context.
|
||||
The MOVE method is allowed if PUT and DELETE are allowed.
|
||||
See DefaultServlet link:{JDURL}/org/eclipse/jetty/servlet/DefaultServlet.html[javadoc].
|
||||
|
||||
[[default-servlet-init]]
|
||||
==== Init Parameters
|
||||
|
||||
Jetty supports the following initParameters:
|
||||
|
||||
acceptRanges::
|
||||
If true, range requests and responses are supported.
|
||||
dirAllowed::
|
||||
If true, directory listings are returned if no welcome file is found.
|
||||
Otherwise 403 Forbidden displays.
|
||||
redirectWelcome::
|
||||
If true, welcome files are redirected rather that forwarded.
|
||||
gzip::
|
||||
If set to true, then static content is served as gzip content encoded if a matching resource is found ending with ".gz".
|
||||
resourceBase::
|
||||
Set to replace the context resource base.
|
||||
aliases::
|
||||
If true, aliases of resources are allowed (that is, symbolic links and caps variations) and may bypass security constraints.
|
||||
maxCacheSize::
|
||||
Maximum total size of the cache or 0 for no cache.
|
||||
maxCachedFileSize::
|
||||
Maximum size of a file to cache.
|
||||
maxCachedFiles::
|
||||
Maximum number of files to cache.
|
||||
useFileMappedBuffer::
|
||||
If set to true, mapped file buffer serves static content.
|
||||
Setting this value to false means that a direct buffer is used instead of a mapped file buffer.
|
||||
By default, this is set to true.
|
||||
otherGzipFileExtensions::
|
||||
A comma separated list of other file extensions that signify that a file is gzip compressed.
|
||||
If you don't explicitly set this, it defaults to ".svgz".
|
|
@ -0,0 +1,117 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[dos-filter]]
|
||||
=== Denial of Service Filter
|
||||
|
||||
[[dos-filter-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.servlets.DoSFilter`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-servlets
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/servlets/DoSFilter.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/servlets/DoSFilter.html
|
||||
|
||||
[[dos-filter-usage]]
|
||||
==== Usage
|
||||
|
||||
The Denial of Service (DoS) filter limits exposure to request flooding, whether malicious, or as a result of a misconfigured client.
|
||||
The DoS filter keeps track of the number of requests from a connection per second.
|
||||
If the requests exceed the limit, Jetty rejects, delays, or throttles the request, and sends a warning message.
|
||||
The filter works on the assumption that the attacker might be written in simple blocking style, so by suspending requests you are hopefully consuming the attacker's resources.
|
||||
The DoS filter is related to the QoS filter, using Continuations to prioritize requests and avoid thread starvation.
|
||||
|
||||
[[dos-filter-using]]
|
||||
==== Using the DoS Filter
|
||||
|
||||
Jetty places throttled requests in a priority queue, giving priority first to authenticated users and users with an HttpSession, then to connections identified by their IP addresses.
|
||||
Connections with no way to identify them have lowest priority.
|
||||
To uniquely identify authenticated users, you should implement the The extractUserId(ServletRequest request) function.
|
||||
|
||||
===== Required JARs
|
||||
|
||||
To use the DoS Filter, these JAR files must be available in WEB-INF/lib:
|
||||
|
||||
* $JETTY_HOME/lib/ext/jetty-util.jar
|
||||
* $JETTY_HOME/lib/ext/jetty-servlets.jar
|
||||
|
||||
===== Sample Configuration
|
||||
|
||||
Place the configuration in a webapp's web.xml or jetty-web.xml.
|
||||
The default configuration allows 25 requests per connection at a time, servicing more important requests first, and queuing up the rest.
|
||||
This example allow 30 requests at a time:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<filter>
|
||||
<filter-name>DoSFilter</filter-name>
|
||||
<filter-class>org.eclipse.jetty.servlets.DoSFilter</filter-class>
|
||||
<init-param>
|
||||
<param-name>maxRequestsPerSec</param-name>
|
||||
<param-value>30</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
|
||||
|
||||
----
|
||||
|
||||
[[dos-filter-init]]
|
||||
===== Configuring DoS Filter Parameters
|
||||
|
||||
The following init parameters control the behavior of the filter:
|
||||
|
||||
maxRequestsPerSec::
|
||||
Maximum number of requests from a connection per second.
|
||||
Requests in excess of this are first delayed, then throttled.
|
||||
Default is 25.
|
||||
|
||||
delayMs::
|
||||
Delay imposed on all requests over the rate limit, before they are considered at all:
|
||||
+
|
||||
* 100 (ms) = Default
|
||||
* -1 = Reject request
|
||||
* 0 = No delay
|
||||
* any other value = Delay in ms
|
||||
|
||||
maxWaitMs::
|
||||
Length of time, in ms, to blocking wait for the throttle semaphore.
|
||||
Default is 50 ms.
|
||||
throttledRequests::
|
||||
Number of requests over the rate limit able to be considered at once.
|
||||
Default is 5.
|
||||
throttleMs::
|
||||
Length of time, in ms, to async wait for semaphore. Default is 30000L.
|
||||
maxRequestMs::
|
||||
Length of time, in ms, to allow the request to run. Default is 30000L.
|
||||
maxIdleTrackerMs::
|
||||
Length of time, in ms, to keep track of request rates for a connection, before deciding that the user has gone away, and
|
||||
discarding it.
|
||||
Default is 30000L.
|
||||
insertHeaders::
|
||||
If true, insert the DoSFilter headers into the response.
|
||||
Defaults to true.
|
||||
trackSessions::
|
||||
If true, usage rate is tracked by session if a session exists.
|
||||
Defaults to true.
|
||||
remotePort::
|
||||
If true and session tracking is not used, then rate is tracked by IP+port (effectively connection).
|
||||
Defaults to false.
|
||||
ipWhitelist::
|
||||
A comma-separated list of IP addresses that will not be rate limited.
|
||||
managedAttr::
|
||||
If set to true, then this servlet is set as a ServletContext attribute with the filter name as the attribute name.
|
||||
This allows a context external mechanism (for example, JMX via ContextHandler.MANAGED_ATTRIBUTES) to manage the configuration of the filter.
|
|
@ -0,0 +1,33 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[error-handler]]
|
||||
=== Error Handler
|
||||
|
||||
[[error-handler-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.server.handler.ErrorHandler`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-server
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/ErrorHandler.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/server/handler/ErrorHandler.html
|
||||
|
||||
[[error-handler-usage]]
|
||||
==== Usage
|
||||
|
||||
A handler that is used to report errors from servlet contexts and webapp contexts to report error conditions.
|
||||
Primarily handles setting the various servlet spec specific response headers for error conditions.
|
||||
Can be customized by extending, for more information on this see xref:custom-error-pages[].
|
|
@ -0,0 +1,85 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[gzip-filter]]
|
||||
=== Gzip Handler
|
||||
|
||||
[[gzip-filter-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.server.handler.gzip.GzipHandler`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-servlets
|
||||
* Javadoc:
|
||||
{JDURL}/org/eclipse/jetty/server/handler/gzip/GzipHandler.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/server/handler/gzip/GzipHandler.html
|
||||
|
||||
[[gzip-filter-usage]]
|
||||
==== Usage
|
||||
|
||||
The Jetty GzipHandler is a compression handler that you can apply to any dynamic resource (servlet).
|
||||
It fixes many of the bugs in commonly available compression filters: it works with asynchronous servlets; it handles all ways to set content length.
|
||||
We have tested it with Jetty continuations and suspending requests.
|
||||
Some user-agents might be excluded from compression to avoid common browser bugs (yes, this means IE!).
|
||||
|
||||
The GzipHandler is added to the entire server by the etc/jetty-gzip.xml file from the gzip.mod module.
|
||||
It may also be added to individual contexts in a context xml file.
|
||||
|
||||
[[gzip-filter-rules]]
|
||||
==== Gzip Rules
|
||||
|
||||
GZIP Handler will gzip or deflate the content of a response if:
|
||||
|
||||
* It is mapped to a matching path
|
||||
* The request method is configured to support gzip
|
||||
* The request is not from an excluded User-Agent
|
||||
* accept-encoding header is set to either gzip, deflate or a combination of those
|
||||
* The response status code is >=200 and <300
|
||||
* The content length is unknown or more than the minGzipSize initParameter or the minGzipSize is 0(default)
|
||||
* The content-type does not match an excluded mime-type
|
||||
* No content-encoding is specified by the resource
|
||||
|
||||
If both gzip and deflate are specified in the accept-encoding header, then gzip will be used.
|
||||
|
||||
Compressing the content can greatly improve the network bandwidth usage, but at a cost of memory and CPU cycles.
|
||||
The link:#default-servlet[DefaultServlet] is capable of serving pre-compressed static content, which saves memory and CPU.
|
||||
By default, the GzipHandler will check to see if pre-compressed content exists, and pass the request through to be handled by the DefaultServlet.
|
||||
|
||||
[[gzip-filter-init]]
|
||||
==== Gzip Configuration
|
||||
|
||||
minGzipSize::
|
||||
Content will only be compressed if content length is either unknown or greater than minGzipSize.
|
||||
checkGzExists::
|
||||
True by default. If set to false, the handler will not check for pre-compressed content.
|
||||
compressionLevel::
|
||||
The compression level used for deflate compression. (0-9).
|
||||
includedMethods::
|
||||
List of HTTP methods to compress. If not set, only GET requests are compressed.
|
||||
includedMimeTypes::
|
||||
List of mime types to compress.
|
||||
excludedMimeTypes::
|
||||
List of mime types not to compress.
|
||||
excludedAgentPatterns::
|
||||
A list of regex patterns for User-Agent names from which requests should not be compressed.
|
||||
excludedPaths::
|
||||
List of paths to exclude from compression.
|
||||
Does a String.startsWith(String) comparison to check if the path matches.
|
||||
If it does match -> no compression.
|
||||
To match subpaths use excludePathPatterns instead.
|
||||
includedPaths::
|
||||
List of paths to consider for compression.
|
||||
includePaths::
|
||||
List of paths to definitely consider for compression.
|
|
@ -0,0 +1,73 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[ipaccess-handler]]
|
||||
=== IP Access Handler
|
||||
|
||||
[[ipaccess-handler-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.server.handler.IPAccessHandler`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-server
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/IPAccessHandler.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/server/handler/IPAccessHandler.html
|
||||
|
||||
[[ipaccess-handler-usage]]
|
||||
==== Usage
|
||||
|
||||
Controls access to the wrapped handler by the real remote IP.
|
||||
Control is provided by white/black lists that include both internet addresses and URIs.
|
||||
This handler uses the real internet address of the connection, not one reported in the forwarded for headers, as this cannot be as easily forged.
|
||||
|
||||
Typically, the black/white lists will be used in one of three modes:
|
||||
|
||||
* Blocking a few specific IPs/URLs by specifying several black list entries.
|
||||
* Allowing only some specific IPs/URLs by specifying several white lists entries.
|
||||
* Allowing a general range of IPs/URLs by specifying several general white list entries, that are then further refined by several specific black list exceptions.
|
||||
|
||||
An empty white list is treated as match all.
|
||||
If there is at least one entry in the white list, then a request must match a white list entry.
|
||||
Black list entries are always applied, so that even if an entry matches the white list, a black list entry will override it.
|
||||
|
||||
Internet addresses may be specified as absolute address or as a combination of four octet wildcard specifications (a.b.c.d) that are defined as follows.
|
||||
|
||||
* nnn - an absolute value (0-255)
|
||||
* mmm-nnn - an inclusive range of absolute values, with following shorthand notations:
|
||||
** nnn- => nnn-255
|
||||
** -nnn => 0-nnn
|
||||
** - => 0-255
|
||||
* a,b,... - a list of wildcard specifications
|
||||
|
||||
Internet address specification is separated from the URI pattern using the "|" (pipe) character.
|
||||
URI patterns follow the servlet specification for simple * prefix and suffix wild cards (e.g. /, /foo, /foo/bar, /foo/bar/*, *.baz).
|
||||
|
||||
Earlier versions of the handler used internet address prefix wildcard specification to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.).
|
||||
They also used the first "/" character of the URI pattern to separate it from the internet address.
|
||||
Both of these features have been deprecated in the current version.
|
||||
|
||||
Examples of the entry specifications are:
|
||||
|
||||
* 10.10.1.2 - all requests from IP 10.10.1.2
|
||||
* 10.10.1.2|/foo/bar - all requests from IP 10.10.1.2 to URI /foo/bar
|
||||
* 10.10.1.2|/foo/* - all requests from IP 10.10.1.2 to URIs starting with /foo/
|
||||
* 10.10.1.2|*.html - all requests from IP 10.10.1.2 to URIs ending with .html
|
||||
* 10.10.0-255.0-255 - all requests from IPs within 10.10.0.0/16 subnet
|
||||
* 10.10.0-.-255|/foo/bar - all requests from IPs within 10.10.0.0/16 subnet to URI /foo/bar
|
||||
* 10.10.0-3,1,3,7,15|/foo/* - all requests from IPs addresses with last octet equal to 1,3,7,15 in subnet 10.10.0.0/22 to URIs starting with /foo/
|
||||
|
||||
Earlier versions of the handler used internet address prefix wildcard specification to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.).
|
||||
They also used the first "/" character of the URI pattern to separate it from the internet address.
|
||||
Both of these features have been deprecated in the current version.
|
|
@ -0,0 +1,74 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[moved-context-handler]]
|
||||
=== Moved Context Handler
|
||||
|
||||
[[moved-context-handler-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.server.handler.MovedContextHandler`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-server
|
||||
* Javadoc:
|
||||
{JDURL}/org/eclipse/jetty/server/handler/MovedContextHandler.html
|
||||
* Xref:
|
||||
{JXURL}/org/eclipse/jetty/server/handler/MovedContextHandler.html
|
||||
|
||||
[[moved-context-handler-usage]]
|
||||
==== Usage
|
||||
|
||||
You can use the MovedContextHandler to relocate or redirect a context that has changed context path and/or virtual hosts.
|
||||
|
||||
You can configure it to _permanently_ redirect the old URL to the new URL, in which case Jetty sends a Http Status code of 301 to the browser with the new URL.
|
||||
Alternatively, you can make it non-permanent, in which case Jetty sends a 302 Http Status code along with the new URL.
|
||||
|
||||
In addition, as with any other context, you can configure a list of virtual hosts, meaning that this context responds only to requests to one of the listed host names.
|
||||
|
||||
Suppose you have a context deployed at /foo, but that now you want to deploy at the root context / instead.
|
||||
|
||||
* First you reconfigure and redeploy the context on Jetty.
|
||||
* Next you need a way to redirect all the browsers who have bookmarked /foo to the new path.
|
||||
You create a new context xml file in $JETTY_HOME/contexts and configure the MovedContextHandler to do the redirection from /foo to /.
|
||||
|
||||
Here's an example.
|
||||
This is a permanent redirection, which also preserves pathinfo and query strings on the redirect:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||
|
||||
<Configure class="org.eclipse.jetty.server.handler.MovedContextHandler">
|
||||
<Set name="contextPath">/foo</Set>
|
||||
<Set name="newContextURL">/</Set>
|
||||
<Set name="permanent">true</Set>
|
||||
<Set name="discardPathInfo">false</Set>
|
||||
<Set name="discardQuery">false</Set>
|
||||
|
||||
<Set name="virtualHosts">
|
||||
<Array type="String">
|
||||
<Item>209.235.245.73</Item>
|
||||
<Item>127.0.0.73</Item>
|
||||
<Item>acme.org</Item>
|
||||
<Item>www.acme.org</Item>
|
||||
<Item>server.acme.org</Item>
|
||||
</Array>
|
||||
</Set>
|
||||
</Configure>
|
||||
|
||||
|
||||
----
|
|
@ -0,0 +1,81 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[proxy-servlet]]
|
||||
=== Proxy Servlet
|
||||
|
||||
[[proxy-servlet-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.proxy.ProxyServlet`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-proxy
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/proxy/ProxyServlet.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/proxy/ProxyServlet.html
|
||||
|
||||
[[proxy-servlet-usage]]
|
||||
==== Usage
|
||||
|
||||
An asynchronous servlet that forwards requests to another server either as a standard web reverse proxy (as defined by RFC2616) or as a transparent reverse proxy. Internally it uses the async jetty-client.
|
||||
|
||||
To facilitate JMX monitoring, the HttpClient instance is set as context attribute, prefixed with the servlet's name and exposed by the mechanism provided by ContextHandler.MANAGED_ATTRIBUTES.
|
||||
|
||||
[[proxy-servlet-init]]
|
||||
==== Init Parameters
|
||||
|
||||
The following init parameters may be used to configure the servlet:
|
||||
|
||||
hostHeader::
|
||||
forces the host header to a particular value
|
||||
viaHost::
|
||||
the name to use in the Via header: Via: http/1.1 <viaHost>
|
||||
whiteList::
|
||||
comma-separated list of allowed proxy hosts
|
||||
blackList::
|
||||
comma-separated list of forbidden proxy hosts
|
||||
|
||||
|
||||
In addition, there are a number of init parameters that can be used to configure the HttpClient instance used internally for the proxy.
|
||||
|
||||
maxThreads::
|
||||
Default Value: 256
|
||||
+
|
||||
The max number of threads of HttpClient's Executor
|
||||
|
||||
maxConnections::
|
||||
Default Value: 32768
|
||||
+
|
||||
The max number of connections per destination.
|
||||
RFC 2616 suggests that 2 connections should be opened per each destination, but browsers commonly open 6 or more. If this HttpClient is used for load testing, it is common to have only one destination (the server to load test), and it is recommended to set this value to a high value (at least as much as the threads present in the executor).
|
||||
|
||||
idleTimeout::
|
||||
Default Value: 30000
|
||||
+
|
||||
The idle timeout in milliseconds that a connection can be idle, that is without traffic of bytes in either direction.
|
||||
|
||||
timeout::
|
||||
Default Value: 60000
|
||||
+
|
||||
The total timeout in milliseconds for the request/response conversation.
|
||||
|
||||
requestBufferSize::
|
||||
Default Value: 4096
|
||||
+
|
||||
The size of the request buffer the request is written into.
|
||||
|
||||
responseBufferSize::
|
||||
Default Value: 4096
|
||||
+
|
||||
The size of the response buffer the response is written into.
|
|
@ -0,0 +1,171 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ========================================================================
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
[[qos-filter]]
|
||||
=== Quality of Service Filter
|
||||
|
||||
[[qos-filter-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.servlets.QoSFilter`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-servlets
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/servlets/QoSFilter.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/servlets/QoSFilter.html
|
||||
|
||||
[[qos-filter-usage]]
|
||||
==== Usage
|
||||
|
||||
Jetty supports Continuations, which allow non-blocking handling of HTTP requests, so that threads can be allocated in a managed way to provide application specific Quality of Service (QoS).
|
||||
The QoSFilter is a utility servlet filter that implements some QoS features.
|
||||
|
||||
[[qos-understanding]]
|
||||
==== Understanding the Problem
|
||||
|
||||
===== Waiting for Resources
|
||||
|
||||
Web applications frequently use JDBC Connection pools to limit the simultaneous load on the database.
|
||||
This protects the database from peak loads, but makes the web application vulnerable to thread starvation.
|
||||
Consider a thread pool with 20 connections, being used by a web application that that typically receives 200 requests per second and each request holds a JDBC connection for 50ms.
|
||||
Such a pool can service on average 200*20*1000/50 = 400 requests per second.
|
||||
|
||||
However, if the request rate rises above 400 per second, or if the database slows down (due to a large query) or becomes momentarily unavailable, the thread pool can very quickly accumulate many waiting requests.
|
||||
If, for example, the website is slashdotted or experiences some other temporary burst of traffic and the request rate rises from 400 to 500 requests per second, then 100 requests per second join those waiting for a JDBC connection.
|
||||
Typically, a web server's thread pool contains only a few hundred threads, so a burst or slow DB need only persist for a few seconds to consume the entire web server's thread pool.
|
||||
This is called thread starvation.
|
||||
The key issue with thread starvation is that it effects the entire web application, and potentially the entire web server.
|
||||
Even if the requests using the database are only a small proportion of the total requests on the web server, all requests are blocked because all the available threads are waiting on the JDBC connection pool.
|
||||
This represents non graceful degradation under load and provides a very poor quality of service.
|
||||
|
||||
===== Prioritizing Resources
|
||||
|
||||
Consider a web application that is under extreme load.
|
||||
This load might be due to a popularity spike (slashdot), usage burst (Christmas or close of business), or even a denial of service attack.
|
||||
During such periods of load, it is often desirable not to treat all requests as equals, and to give priority to high value customers or administrative users.
|
||||
|
||||
The typical behaviour of a web server under extreme load is to use all its threads to service requests and to build up a backlog of unserviced requests.
|
||||
If the backlog grows deep enough, then requests start to timeout and users experience failures as well as delays.
|
||||
|
||||
Ideally, the web application should be able to examine the requests in the backlog, and give priority to high value customers and administrative users.
|
||||
But with the standard blocking servlet API, it is not possible to examine a request without allocating a thread to that request for the duration of its handling.
|
||||
There is no way to delay the handling of low priority requests, so if the resources are to be reallocated, then the low priority requests must all be failed.
|
||||
|
||||
[[qos-applying]]
|
||||
==== Applying the QoSFilter
|
||||
|
||||
The Quality of Service Filter (QoSFilter) uses Continuations to avoid thread starvation, prioritize requests and give graceful degradation under load, to provide a high quality of service.
|
||||
When you apply the filter to specific URLs within a web application, it limits the number of active requests being handled for those URLs.
|
||||
Any requests in excess of the limit are suspended. When a request completes handling the limited URL, one of the waiting requests resumes and can be handled.
|
||||
You can assign priorities to each suspended request, so that high priority requests resume before lower priority requests.
|
||||
|
||||
===== Required JARs
|
||||
|
||||
To use the QoS Filter, these JAR files must be available in WEB-INF/lib:
|
||||
|
||||
* $JETTY_HOME/lib/ext/jetty-util.jar
|
||||
* $JETTY_HOME/lib/ext/jetty-servlets.jar–contains QoSFilter
|
||||
|
||||
===== Sample Configuration
|
||||
|
||||
Place the configuration in a webapp's web.xml or jetty-web.xml.
|
||||
The default configuration processes ten requests at a time, servicing more important requests first, and queuing up the rest.
|
||||
This example processes fifty requests at a time:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<filter>
|
||||
<filter-name>QoSFilter</filter-name>
|
||||
<filter-class>org.eclipse.jetty.servlets.QoSFilter</filter-class>
|
||||
<init-param>
|
||||
<param-name>maxRequests</param-name>
|
||||
<param-value>50</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
|
||||
|
||||
----
|
||||
|
||||
[[qos-filter-init]]
|
||||
===== Configuring QoS Filter Parameters
|
||||
|
||||
A semaphore polices the "maxRequests" limit.
|
||||
The filter waits a short time while attempting to acquire the semaphore.
|
||||
The "waitMs" init parameter controls the wait, avoiding the expense of a suspend if the semaphore is shortly available.
|
||||
If the semaphore cannot be obtained, Jetty suspends the request for the default suspend period of the container or the value set as the "suspendMs" init parameter.
|
||||
|
||||
The QoS filter uses the following init parameters:
|
||||
|
||||
maxRequests::
|
||||
the maximum number of requests to be serviced at a time. The default is 10.
|
||||
maxPriority::
|
||||
the maximum valid priority that can be assigned to a request.
|
||||
A request with a high priority value is more important than a request with a low priority value. The default is 10.
|
||||
waitMS::
|
||||
waitMS–length of time, in milliseconds, to wait while trying to accept a new request.
|
||||
Used when the maxRequests limit is reached.
|
||||
Default is 50 ms.
|
||||
suspendMS::
|
||||
length of time, in milliseconds, that the request will be suspended if it is not accepted immediately.
|
||||
If not set, the container's default suspend period applies. Default is -1 ms.
|
||||
managedAttr::
|
||||
If set to true, then this servlet is set as a ServletContext attribute with the filter name as the attribute name.
|
||||
This allows a context external mechanism (for example, JMX via ContextHandler.MANAGED_ATTRIBUTES) to manage the configuration of the filter.
|
||||
|
||||
===== Mapping to URLs
|
||||
|
||||
You can use the `<filter-mapping>` syntax to map the QoSFilter to a servlet, either by using the servlet name, or by using a URL pattern.
|
||||
In this example, a URL pattern applies the QoSFilter to every request within the web application context:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>QoSFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
|
||||
----
|
||||
|
||||
===== Setting the Request Priority
|
||||
|
||||
Requests with higher values have a higher priority.
|
||||
The default request priorities assigned by the QoSFilter are:
|
||||
|
||||
* 2 -- For any authenticated request
|
||||
* 1 -- For any request with a non-new valid session
|
||||
* 0 -- For all other requests
|
||||
|
||||
To customize the priority, subclass QoSFilter and then override the getPriority(ServletRequest request) method to return an appropriate priority for the request.
|
||||
You can then use this subclass as your QoS filter.
|
||||
Here's a trivial example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
public class ParsePriorityQoSFilter extends QoSFilter
|
||||
{
|
||||
protected int getPriority(ServletRequest request)
|
||||
{
|
||||
String p = ((HttpServletRequest)request).getParameter("priority");
|
||||
if (p!=null)
|
||||
return Integer.parseInt(p);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
----
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue