Merge branch 'jetty-9.4.x'
This commit is contained in:
commit
fa6d9029fc
|
@ -9,8 +9,9 @@ node {
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stage 'Checkout'
|
stage('Checkout') {
|
||||||
checkout scm
|
checkout scm
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
notifyBuild("Checkout Failure")
|
notifyBuild("Checkout Failure")
|
||||||
throw e
|
throw e
|
||||||
|
@ -18,10 +19,11 @@ node {
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stage 'Compile'
|
stage('Compile') {
|
||||||
withEnv(mvnEnv) {
|
withEnv(mvnEnv) {
|
||||||
timeout(time: 15, unit: 'MINUTES') {
|
timeout(time: 15, unit: 'MINUTES') {
|
||||||
sh "mvn -B clean install -Dtest=None"
|
sh "mvn -B clean install -Dtest=None"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
|
@ -31,10 +33,11 @@ node {
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stage 'Javadoc'
|
stage('Javadoc') {
|
||||||
withEnv(mvnEnv) {
|
withEnv(mvnEnv) {
|
||||||
timeout(time: 15, unit: 'MINUTES') {
|
timeout(time: 15, unit: 'MINUTES') {
|
||||||
sh "mvn -B javadoc:javadoc"
|
sh "mvn -B javadoc:javadoc"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
|
@ -44,31 +47,51 @@ node {
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stage 'Test'
|
stage('Test') {
|
||||||
withEnv(mvnEnv) {
|
withEnv(mvnEnv) {
|
||||||
timeout(time: 60, unit: 'MINUTES') {
|
timeout(time: 60, unit: 'MINUTES') {
|
||||||
// Run test phase / ignore test failures
|
// Run test phase / ignore test failures
|
||||||
sh "mvn -B install -Dmaven.test.failure.ignore=true"
|
sh "mvn -B install -Dmaven.test.failure.ignore=true"
|
||||||
// Report failures in the jenkins UI
|
// Report failures in the jenkins UI
|
||||||
step([$class: 'JUnitResultArchiver',
|
step([$class: 'JUnitResultArchiver',
|
||||||
testResults: '**/target/surefire-reports/TEST-*.xml'])
|
testResults: '**/target/surefire-reports/TEST-*.xml'])
|
||||||
// Collect up the jacoco execution results
|
// Collect up the jacoco execution results
|
||||||
step([$class: 'JacocoPublisher',
|
def jacocoExcludes =
|
||||||
inclusionPattern: "**/org/eclipse/jetty/**/*.class",
|
// build tools
|
||||||
execPattern: '**/target/jacoco.exec',
|
"**/org/eclipse/jetty/ant/**" +
|
||||||
classPattern: '**/target/classes',
|
",**/org/eclipse/jetty/maven/**" +
|
||||||
sourcePattern: '**/src/main/java'])
|
",**/org/eclipse/jetty/jspc/**" +
|
||||||
// Report on Maven and Javadoc warnings
|
// example code / documentation
|
||||||
step([$class: 'WarningsPublisher',
|
",**/org/eclipse/jetty/embedded/**" +
|
||||||
consoleParsers: [
|
",**/org/eclipse/jetty/asyncrest/**" +
|
||||||
[parserName: 'Maven'],
|
",**/org/eclipse/jetty/demo/**" +
|
||||||
[parserName: 'JavaDoc'],
|
// special environments / late integrations
|
||||||
[parserName: 'JavaC']
|
",**/org/eclipse/jetty/gcloud/**" +
|
||||||
]])
|
",**/org/eclipse/jetty/infinispan/**" +
|
||||||
}
|
",**/org/eclipse/jetty/osgi/**" +
|
||||||
if(isUnstable())
|
",**/org/eclipse/jetty/spring/**" +
|
||||||
{
|
",**/org/eclipse/jetty/http/spi/**" +
|
||||||
notifyBuild("Unstable / Test Errors")
|
// test classes
|
||||||
|
",**/org/eclipse/jetty/tests/**" +
|
||||||
|
",**/org/eclipse/jetty/test/**";
|
||||||
|
step([$class: 'JacocoPublisher',
|
||||||
|
inclusionPattern: '**/org/eclipse/jetty/**/*.class',
|
||||||
|
exclusionPattern: jacocoExcludes,
|
||||||
|
execPattern: '**/target/jacoco.exec',
|
||||||
|
classPattern: '**/target/classes',
|
||||||
|
sourcePattern: '**/src/main/java'])
|
||||||
|
// Report on Maven and Javadoc warnings
|
||||||
|
step([$class: 'WarningsPublisher',
|
||||||
|
consoleParsers: [
|
||||||
|
[parserName: 'Maven'],
|
||||||
|
[parserName: 'JavaDoc'],
|
||||||
|
[parserName: 'JavaC']
|
||||||
|
]])
|
||||||
|
}
|
||||||
|
if(isUnstable())
|
||||||
|
{
|
||||||
|
notifyBuild("Unstable / Test Errors")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# GPG Release Key Fingerprints
|
||||||
|
Jan Bartel AED5 EE6C 45D0 FE8D 5D1B 164F 27DE D4BF 6216 DB
|
||||||
|
Simone Bordet 8B09 6546 B1A8 F026 56B1 5D3B 1677 D141 BCF3 58
|
||||||
|
Joakim Erdfelt <joakim@erdfelt.com> BFBB 21C2 46D7 7768 3628 7A48 A04E 0C74 ABB3 5FEA
|
||||||
|
Joakim Erdfelt <joakim@apache.org> B59B 67FD 7904 9843 67F9 3180 0818 D9D6 8FB6 7BAC
|
||||||
|
Jesse McConnell 2A68 4B57 436A 81FA 8706 B53C 61C3 351A 438A 3B7D
|
10985
VERSION.txt
10985
VERSION.txt
File diff suppressed because it is too large
Load Diff
|
@ -30,6 +30,31 @@
|
||||||
</instructions>
|
</instructions>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>nolog-jar</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<classifier>nolog</classifier>
|
||||||
|
<excludes>
|
||||||
|
<exclude>META-INF/services/org.apache.juli.logging.Log</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
@ -39,11 +64,6 @@
|
||||||
<artifactId>jetty-util</artifactId>
|
<artifactId>jetty-util</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
|
||||||
<artifactId>jetty-server</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Schemas -->
|
<!-- Schemas -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -68,5 +88,25 @@
|
||||||
<groupId>org.eclipse.jdt.core.compiler</groupId>
|
<groupId>org.eclipse.jdt.core.compiler</groupId>
|
||||||
<artifactId>ecj</artifactId>
|
<artifactId>ecj</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- tests -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-servlet</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<classifier>tests</classifier>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-servlet</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
|
<artifactId>jetty-test-helper</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -29,8 +29,9 @@ import javax.servlet.ServletContext;
|
||||||
import org.apache.jasper.servlet.JasperInitializer;
|
import org.apache.jasper.servlet.JasperInitializer;
|
||||||
import org.apache.jasper.servlet.TldPreScanned;
|
import org.apache.jasper.servlet.TldPreScanned;
|
||||||
import org.apache.jasper.servlet.TldScanner;
|
import org.apache.jasper.servlet.TldScanner;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.apache.juli.logging.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.apache.juli.logging.LogFactory;
|
||||||
|
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,8 +39,7 @@ import org.xml.sax.SAXException;
|
||||||
*/
|
*/
|
||||||
public class JettyJasperInitializer extends JasperInitializer
|
public class JettyJasperInitializer extends JasperInitializer
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(JettyJasperInitializer.class);
|
private static final Log LOG = LogFactory.getLog(JasperInitializer.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NullTldScanner
|
* NullTldScanner
|
||||||
*
|
*
|
||||||
|
@ -111,6 +111,4 @@ public class JettyJasperInitializer extends JasperInitializer
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("Defaulting to jasper tld scanning");
|
if (LOG.isDebugEnabled()) LOG.debug("Defaulting to jasper tld scanning");
|
||||||
return super.newTldScanner(context, namespaceAware, validate, blockExternal);
|
return super.newTldScanner(context, namespaceAware, validate, blockExternal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
package org.eclipse.jetty.jsp;
|
package org.eclipse.jetty.jsp;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
@ -26,9 +29,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.jasper.servlet.JspServlet;
|
import org.apache.jasper.servlet.JspServlet;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JettyJspServlet
|
* JettyJspServlet
|
||||||
|
@ -76,7 +77,7 @@ public class JettyJspServlet extends JspServlet
|
||||||
pathInfo = request.getPathInfo();
|
pathInfo = request.getPathInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
String pathInContext = URIUtil.addPaths(servletPath,pathInfo);
|
String pathInContext = addPaths(servletPath,pathInfo);
|
||||||
|
|
||||||
String jspFile = getInitParameter("jspFile");
|
String jspFile = getInitParameter("jspFile");
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ public class JettyJspServlet extends JspServlet
|
||||||
//otherwise the default servlet might handle it
|
//otherwise the default servlet might handle it
|
||||||
if (jspFile == null)
|
if (jspFile == null)
|
||||||
{
|
{
|
||||||
if (pathInContext.endsWith("/"))
|
if (pathInContext != null && pathInContext.endsWith("/"))
|
||||||
{
|
{
|
||||||
//dispatch via forward to the default servlet
|
//dispatch via forward to the default servlet
|
||||||
getServletContext().getNamedDispatcher("default").forward(req, resp);
|
getServletContext().getNamedDispatcher("default").forward(req, resp);
|
||||||
|
@ -93,13 +94,16 @@ public class JettyJspServlet extends JspServlet
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//check if it resolves to a directory
|
//check if it resolves to a directory
|
||||||
Resource resource = ((ContextHandler.Context)getServletContext()).getContextHandler().getResource(pathInContext);
|
String realPath = getServletContext().getRealPath(pathInContext);
|
||||||
|
if (realPath != null)
|
||||||
if (resource!=null && resource.isDirectory())
|
|
||||||
{
|
{
|
||||||
//dispatch via forward to the default servlet
|
Path asPath = Paths.get(realPath);
|
||||||
getServletContext().getNamedDispatcher("default").forward(req, resp);
|
if (Files.exists(asPath) && Files.isDirectory(asPath))
|
||||||
return;
|
{
|
||||||
|
//dispatch via forward to the default servlet
|
||||||
|
getServletContext().getNamedDispatcher("default").forward(req, resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,5 +112,19 @@ public class JettyJspServlet extends JspServlet
|
||||||
super.service(req, resp);
|
super.service(req, resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param servletPath the servletPath of the request
|
||||||
|
* @param pathInfo the pathInfo of the request
|
||||||
|
* @return servletPath with pathInfo appended
|
||||||
|
*/
|
||||||
|
private String addPaths(String servletPath, String pathInfo)
|
||||||
|
{
|
||||||
|
if (servletPath.length()==0)
|
||||||
|
return pathInfo;
|
||||||
|
|
||||||
|
if (pathInfo==null)
|
||||||
|
return servletPath;
|
||||||
|
|
||||||
|
return servletPath+pathInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.jsp;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.jsp.JspFactory;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.eclipse.jetty.servlet.ServletTester;
|
||||||
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.apache.jasper.runtime.JspFactoryImpl;
|
||||||
|
import org.apache.tomcat.InstanceManager;
|
||||||
|
import org.apache.tomcat.SimpleInstanceManager;
|
||||||
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
|
|
||||||
|
public class TestJettyJspServlet
|
||||||
|
{
|
||||||
|
File _dir;
|
||||||
|
ServletTester _tester;
|
||||||
|
|
||||||
|
public static class DfltServlet extends HttpServlet
|
||||||
|
{
|
||||||
|
|
||||||
|
public DfltServlet()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||||
|
{
|
||||||
|
resp.setContentType("html/text");
|
||||||
|
resp.getOutputStream().println("This.Is.The.Default.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp () throws Exception
|
||||||
|
{
|
||||||
|
JspFactory.setDefaultFactory(new JspFactoryImpl());
|
||||||
|
_dir = MavenTestingUtils.getTestResourceDir("base");
|
||||||
|
_tester = new ServletTester("/context");
|
||||||
|
_tester.getContext().setClassLoader(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()));
|
||||||
|
ServletHolder jspHolder = _tester.getContext().addServlet(JettyJspServlet.class, "/*");
|
||||||
|
jspHolder.setInitParameter("scratchdir", MavenTestingUtils.getTargetTestingDir().getAbsolutePath());
|
||||||
|
_tester.getContext().setResourceBase(_dir.getAbsolutePath());
|
||||||
|
_tester.getContext().setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager());
|
||||||
|
ServletHolder dfltHolder = new ServletHolder();
|
||||||
|
dfltHolder.setName("default");
|
||||||
|
dfltHolder.setHeldClass( DfltServlet.class);
|
||||||
|
_tester.getContext().addServlet(dfltHolder, "/");
|
||||||
|
|
||||||
|
_tester.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception
|
||||||
|
{
|
||||||
|
if (_tester != null)
|
||||||
|
_tester.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithJsp() throws Exception
|
||||||
|
{
|
||||||
|
//test that an ordinary jsp is served by jsp servlet
|
||||||
|
String request = "" +
|
||||||
|
"GET /context/foo.jsp HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"Connection: close\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
String response = _tester.getResponses(request);
|
||||||
|
assertTrue(!response.contains("This.Is.The.Default."));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithDirectory() throws Exception
|
||||||
|
{
|
||||||
|
//test that a dir is served by the default servlet
|
||||||
|
String request = "" +
|
||||||
|
"GET /context/dir HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"Connection: close\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
String response = _tester.getResponses(request);
|
||||||
|
assertTrue(response.contains("This.Is.The.Default."));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<html><head>
|
||||||
|
<%@ page import="java.util.Enumeration" %>
|
||||||
|
</head><body>
|
||||||
|
<h1>JSP Dump</h1>
|
||||||
|
|
||||||
|
<table border="1">
|
||||||
|
<tr><th>Request URI:</th><td><%= request.getRequestURI() %></td></tr>
|
||||||
|
<tr><th>ServletPath:</th><td><%= request.getServletPath() %></td></tr>
|
||||||
|
<tr><th>PathInfo:</th><td><%= request.getPathInfo() %></td></tr>
|
||||||
|
|
||||||
|
<%
|
||||||
|
Enumeration e =request.getParameterNames();
|
||||||
|
while(e.hasMoreElements())
|
||||||
|
{
|
||||||
|
String name = (String)e.nextElement();
|
||||||
|
%>
|
||||||
|
<tr>
|
||||||
|
<th>getParameter("<%= name %>")</th>
|
||||||
|
<td><%= request.getParameter(name) %></td></tr>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</body></html>
|
|
@ -22,7 +22,13 @@
|
||||||
<useSystemClassLoader>false</useSystemClassLoader>
|
<useSystemClassLoader>false</useSystemClassLoader>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[files]
|
||||||
|
http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
|
||||||
|
|
||||||
|
[exec]
|
||||||
|
-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
|
|
@ -0,0 +1,5 @@
|
||||||
|
[files]
|
||||||
|
http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.10.v20161026/alpn-boot-8.1.10.v20161026.jar|lib/alpn/alpn-boot-8.1.10.v20161026.jar
|
||||||
|
|
||||||
|
[exec]
|
||||||
|
-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.10.v20161026.jar
|
|
@ -44,7 +44,7 @@ public class PostConstructAnnotationHandler extends AbstractIntrospectableAnnota
|
||||||
public void doHandle(Class clazz)
|
public void doHandle(Class clazz)
|
||||||
{
|
{
|
||||||
//Check that the PostConstruct is on a class that we're interested in
|
//Check that the PostConstruct is on a class that we're interested in
|
||||||
if (Util.supportsPostConstructPreDestroy(clazz))
|
if (supportsPostConstruct(clazz))
|
||||||
{
|
{
|
||||||
Method[] methods = clazz.getDeclaredMethods();
|
Method[] methods = clazz.getDeclaredMethods();
|
||||||
for (int i=0; i<methods.length; i++)
|
for (int i=0; i<methods.length; i++)
|
||||||
|
@ -84,4 +84,27 @@ public class PostConstructAnnotationHandler extends AbstractIntrospectableAnnota
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given class is permitted to have PostConstruct annotation.
|
||||||
|
* @param c the class
|
||||||
|
* @return true if the spec permits the class to have PostConstruct, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean supportsPostConstruct (Class c)
|
||||||
|
{
|
||||||
|
if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.Filter.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.AsyncListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class PreDestroyAnnotationHandler extends AbstractIntrospectableAnnotatio
|
||||||
public void doHandle(Class clazz)
|
public void doHandle(Class clazz)
|
||||||
{
|
{
|
||||||
//Check that the PreDestroy is on a class that we're interested in
|
//Check that the PreDestroy is on a class that we're interested in
|
||||||
if (Util.supportsPostConstructPreDestroy(clazz))
|
if (supportsPreDestroy(clazz))
|
||||||
{
|
{
|
||||||
Method[] methods = clazz.getDeclaredMethods();
|
Method[] methods = clazz.getDeclaredMethods();
|
||||||
for (int i=0; i<methods.length; i++)
|
for (int i=0; i<methods.length; i++)
|
||||||
|
@ -85,4 +85,27 @@ public class PreDestroyAnnotationHandler extends AbstractIntrospectableAnnotatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the spec permits the given class to use the PreDestroy annotation.
|
||||||
|
* @param c the class
|
||||||
|
* @return true if permitted, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean supportsPreDestroy (Class c)
|
||||||
|
{
|
||||||
|
if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.Filter.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.AsyncListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ package org.eclipse.jetty.annotations;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
@ -40,6 +42,10 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(ResourceAnnotationHandler.class);
|
private static final Logger LOG = Log.getLogger(ResourceAnnotationHandler.class);
|
||||||
|
|
||||||
|
protected static final List<Class<?>> ENV_ENTRY_TYPES =
|
||||||
|
Arrays.asList(new Class[] {String.class, Character.class, Integer.class, Boolean.class, Double.class, Byte.class, Short.class, Long.class, Float.class});
|
||||||
|
|
||||||
|
|
||||||
protected WebAppContext _context;
|
protected WebAppContext _context;
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,7 +63,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
||||||
*/
|
*/
|
||||||
public void doHandle(Class<?> clazz)
|
public void doHandle(Class<?> clazz)
|
||||||
{
|
{
|
||||||
if (Util.supportsResourceInjection(clazz))
|
if (supportsResourceInjection(clazz))
|
||||||
{
|
{
|
||||||
handleClass(clazz);
|
handleClass(clazz);
|
||||||
|
|
||||||
|
@ -182,7 +188,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
||||||
//TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination
|
//TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination
|
||||||
metaData.setOrigin("resource-ref."+name+".injection",resource,clazz);
|
metaData.setOrigin("resource-ref."+name+".injection",resource,clazz);
|
||||||
}
|
}
|
||||||
else if (!Util.isEnvEntryType(type))
|
else if (!isEnvEntryType(type))
|
||||||
{
|
{
|
||||||
//if this is an env-entry type resource and there is no value bound for it, it isn't
|
//if this is an env-entry type resource and there is no value bound for it, it isn't
|
||||||
//an error, it just means that perhaps the code will use a default value instead
|
//an error, it just means that perhaps the code will use a default value instead
|
||||||
|
@ -196,7 +202,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
||||||
//if this is an env-entry type resource and there is no value bound for it, it isn't
|
//if this is an env-entry type resource and there is no value bound for it, it isn't
|
||||||
//an error, it just means that perhaps the code will use a default value instead
|
//an error, it just means that perhaps the code will use a default value instead
|
||||||
// JavaEE Spec. sec 5.4.1.3
|
// JavaEE Spec. sec 5.4.1.3
|
||||||
if (!Util.isEnvEntryType(type))
|
if (!isEnvEntryType(type))
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,7 +345,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
||||||
//TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination
|
//TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination
|
||||||
metaData.setOrigin("resource-ref."+name+".injection",resource,clazz);
|
metaData.setOrigin("resource-ref."+name+".injection",resource,clazz);
|
||||||
}
|
}
|
||||||
else if (!Util.isEnvEntryType(paramType))
|
else if (!isEnvEntryType(paramType))
|
||||||
{
|
{
|
||||||
|
|
||||||
//if this is an env-entry type resource and there is no value bound for it, it isn't
|
//if this is an env-entry type resource and there is no value bound for it, it isn't
|
||||||
|
@ -353,11 +359,47 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
||||||
//if this is an env-entry type resource and there is no value bound for it, it isn't
|
//if this is an env-entry type resource and there is no value bound for it, it isn't
|
||||||
//an error, it just means that perhaps the code will use a default value instead
|
//an error, it just means that perhaps the code will use a default value instead
|
||||||
// JavaEE Spec. sec 5.4.1.3
|
// JavaEE Spec. sec 5.4.1.3
|
||||||
if (!Util.isEnvEntryType(paramType))
|
if (!isEnvEntryType(paramType))
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given Class is one that the specification allows to have a Resource annotation.
|
||||||
|
*
|
||||||
|
* @param c the class
|
||||||
|
* @return true if Resource annotation permitted, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean supportsResourceInjection (Class<?> c)
|
||||||
|
{
|
||||||
|
if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.Filter.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.AsyncListener.class.isAssignableFrom(c) ||
|
||||||
|
javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the class is one of the basic java types permitted as
|
||||||
|
* env-entries.
|
||||||
|
* @param clazz the class to check
|
||||||
|
* @return true if class is permitted by the spec to be an env-entry value
|
||||||
|
*/
|
||||||
|
public boolean isEnvEntryType (Class<?> clazz)
|
||||||
|
{
|
||||||
|
return ENV_ENTRY_TYPES.contains(clazz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,275 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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.annotations;
|
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.Loader;
|
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
|
||||||
import org.objectweb.asm.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation Processing Utilities
|
|
||||||
*/
|
|
||||||
public class Util
|
|
||||||
{
|
|
||||||
private static Class[] __envEntryClassTypes =
|
|
||||||
new Class[] {String.class, Character.class, Integer.class, Boolean.class, Double.class, Byte.class, Short.class, Long.class, Float.class};
|
|
||||||
|
|
||||||
|
|
||||||
private static String[] __envEntryTypes =
|
|
||||||
new String[] { Type.getDescriptor(String.class), Type.getDescriptor(Character.class), Type.getDescriptor(Integer.class), Type.getDescriptor(Boolean.class),
|
|
||||||
Type.getDescriptor(Double.class), Type.getDescriptor(Byte.class), Type.getDescriptor(Short.class), Type.getDescriptor(Long.class), Type.getDescriptor(Float.class)};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the presented method belongs to a class that is one
|
|
||||||
* of the classes with which a servlet container should be concerned.
|
|
||||||
* @param c the class
|
|
||||||
* @return true if class is a type of one of the following:
|
|
||||||
* ({@link javax.servlet.Servlet},
|
|
||||||
* {@link javax.servlet.Filter},
|
|
||||||
* {@link javax.servlet.ServletContextListener},
|
|
||||||
* {@link javax.servlet.ServletContextAttributeListener},
|
|
||||||
* {@link javax.servlet.ServletRequestListener},
|
|
||||||
* {@link javax.servlet.ServletRequestAttributeListener},
|
|
||||||
* {@link javax.servlet.http.HttpSessionListener},
|
|
||||||
* {@link javax.servlet.http.HttpSessionAttributeListener})
|
|
||||||
*/
|
|
||||||
public static boolean isServletType (Class c)
|
|
||||||
{
|
|
||||||
boolean isServlet = false;
|
|
||||||
if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.Filter.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.AsyncListener.class.isAssignableFrom(c))
|
|
||||||
|
|
||||||
isServlet=true;
|
|
||||||
|
|
||||||
return isServlet;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static boolean supportsResourceInjection (Class c)
|
|
||||||
{
|
|
||||||
if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.Filter.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.AsyncListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static boolean supportsPostConstructPreDestroy (Class c)
|
|
||||||
{
|
|
||||||
if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.Filter.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.AsyncListener.class.isAssignableFrom(c) ||
|
|
||||||
javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isEnvEntryType (Class type)
|
|
||||||
{
|
|
||||||
boolean result = false;
|
|
||||||
for (int i=0;i<__envEntryClassTypes.length && !result;i++)
|
|
||||||
{
|
|
||||||
result = (type.equals(__envEntryClassTypes[i]));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isEnvEntryType (String desc)
|
|
||||||
{
|
|
||||||
boolean result = false;
|
|
||||||
for (int i=0;i<__envEntryTypes.length && !result;i++)
|
|
||||||
{
|
|
||||||
result = (desc.equals(__envEntryTypes[i]));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String normalizePattern(String p)
|
|
||||||
{
|
|
||||||
if (p!=null && p.length()>0 && !p.startsWith("/") && !p.startsWith("*"))
|
|
||||||
return "/"+p;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static Class[] convertTypes (String params)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
return convertTypes(Type.getArgumentTypes(params));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static Class[] convertTypes (Type[] types)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
if (types==null)
|
|
||||||
return new Class[0];
|
|
||||||
|
|
||||||
Class[] classArray = new Class[types.length];
|
|
||||||
|
|
||||||
for (int i=0; i<types.length; i++)
|
|
||||||
{
|
|
||||||
classArray[i] = convertType(types[i]);
|
|
||||||
}
|
|
||||||
return classArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Class convertType (Type t)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
if (t == null)
|
|
||||||
return (Class)null;
|
|
||||||
|
|
||||||
switch (t.getSort())
|
|
||||||
{
|
|
||||||
case Type.BOOLEAN:
|
|
||||||
{
|
|
||||||
return Boolean.TYPE;
|
|
||||||
}
|
|
||||||
case Type.ARRAY:
|
|
||||||
{
|
|
||||||
Class clazz = convertType(t.getElementType());
|
|
||||||
return Array.newInstance(clazz, 0).getClass();
|
|
||||||
}
|
|
||||||
case Type.BYTE:
|
|
||||||
{
|
|
||||||
return Byte.TYPE;
|
|
||||||
}
|
|
||||||
case Type.CHAR:
|
|
||||||
{
|
|
||||||
return Character.TYPE;
|
|
||||||
}
|
|
||||||
case Type.DOUBLE:
|
|
||||||
{
|
|
||||||
return Double.TYPE;
|
|
||||||
}
|
|
||||||
case Type.FLOAT:
|
|
||||||
{
|
|
||||||
return Float.TYPE;
|
|
||||||
}
|
|
||||||
case Type.INT:
|
|
||||||
{
|
|
||||||
return Integer.TYPE;
|
|
||||||
}
|
|
||||||
case Type.LONG:
|
|
||||||
{
|
|
||||||
return Long.TYPE;
|
|
||||||
}
|
|
||||||
case Type.OBJECT:
|
|
||||||
{
|
|
||||||
return (Loader.loadClass(t.getClassName()));
|
|
||||||
}
|
|
||||||
case Type.SHORT:
|
|
||||||
{
|
|
||||||
return Short.TYPE;
|
|
||||||
}
|
|
||||||
case Type.VOID:
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String asCanonicalName (Type t)
|
|
||||||
{
|
|
||||||
if (t == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
switch (t.getSort())
|
|
||||||
{
|
|
||||||
case Type.BOOLEAN:
|
|
||||||
{
|
|
||||||
return TypeUtil.toName(Boolean.TYPE);
|
|
||||||
}
|
|
||||||
case Type.ARRAY:
|
|
||||||
{
|
|
||||||
return t.getElementType().getClassName();
|
|
||||||
}
|
|
||||||
case Type.BYTE:
|
|
||||||
{
|
|
||||||
return TypeUtil.toName(Byte.TYPE);
|
|
||||||
}
|
|
||||||
case Type.CHAR:
|
|
||||||
{
|
|
||||||
return TypeUtil.toName(Character.TYPE);
|
|
||||||
}
|
|
||||||
case Type.DOUBLE:
|
|
||||||
{
|
|
||||||
return TypeUtil.toName(Double.TYPE);
|
|
||||||
}
|
|
||||||
case Type.FLOAT:
|
|
||||||
{
|
|
||||||
return TypeUtil.toName(Float.TYPE);
|
|
||||||
}
|
|
||||||
case Type.INT:
|
|
||||||
{
|
|
||||||
return TypeUtil.toName(Integer.TYPE);
|
|
||||||
}
|
|
||||||
case Type.LONG:
|
|
||||||
{
|
|
||||||
return TypeUtil.toName(Long.TYPE);
|
|
||||||
}
|
|
||||||
case Type.OBJECT:
|
|
||||||
{
|
|
||||||
return t.getClassName();
|
|
||||||
}
|
|
||||||
case Type.SHORT:
|
|
||||||
{
|
|
||||||
return TypeUtil.toName(Short.TYPE);
|
|
||||||
}
|
|
||||||
case Type.VOID:
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -26,6 +26,7 @@ import javax.servlet.Filter;
|
||||||
import javax.servlet.annotation.WebFilter;
|
import javax.servlet.annotation.WebFilter;
|
||||||
import javax.servlet.annotation.WebInitParam;
|
import javax.servlet.annotation.WebInitParam;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||||
import org.eclipse.jetty.servlet.FilterHolder;
|
import org.eclipse.jetty.servlet.FilterHolder;
|
||||||
import org.eclipse.jetty.servlet.FilterMapping;
|
import org.eclipse.jetty.servlet.FilterMapping;
|
||||||
import org.eclipse.jetty.servlet.Source;
|
import org.eclipse.jetty.servlet.Source;
|
||||||
|
@ -117,7 +118,7 @@ public class WebFilterAnnotation extends DiscoveredAnnotation
|
||||||
ArrayList<String> paths = new ArrayList<String>();
|
ArrayList<String> paths = new ArrayList<String>();
|
||||||
for (String s:urlPatterns)
|
for (String s:urlPatterns)
|
||||||
{
|
{
|
||||||
paths.add(Util.normalizePattern(s));
|
paths.add(ServletPathSpec.normalize(s));
|
||||||
}
|
}
|
||||||
mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
|
mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
|
||||||
}
|
}
|
||||||
|
@ -188,7 +189,7 @@ public class WebFilterAnnotation extends DiscoveredAnnotation
|
||||||
ArrayList<String> paths = new ArrayList<String>();
|
ArrayList<String> paths = new ArrayList<String>();
|
||||||
for (String s:urlPatterns)
|
for (String s:urlPatterns)
|
||||||
{
|
{
|
||||||
paths.add(Util.normalizePattern(s));
|
paths.add(ServletPathSpec.normalize(s));
|
||||||
}
|
}
|
||||||
mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
|
mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import javax.servlet.annotation.WebInitParam;
|
||||||
import javax.servlet.annotation.WebServlet;
|
import javax.servlet.annotation.WebServlet;
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||||
|
import org.eclipse.jetty.servlet.Holder;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.eclipse.jetty.servlet.ServletMapping;
|
import org.eclipse.jetty.servlet.ServletMapping;
|
||||||
import org.eclipse.jetty.servlet.Source;
|
import org.eclipse.jetty.servlet.Source;
|
||||||
|
@ -102,7 +104,7 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
||||||
//canonicalize the patterns
|
//canonicalize the patterns
|
||||||
ArrayList<String> urlPatternList = new ArrayList<String>();
|
ArrayList<String> urlPatternList = new ArrayList<String>();
|
||||||
for (String p : urlPatterns)
|
for (String p : urlPatterns)
|
||||||
urlPatternList.add(Util.normalizePattern(p));
|
urlPatternList.add(ServletPathSpec.normalize(p));
|
||||||
|
|
||||||
String servletName = (annotation.name().equals("")?clazz.getName():annotation.name());
|
String servletName = (annotation.name().equals("")?clazz.getName():annotation.name());
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,13 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
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.LEVEL=INFO
|
||||||
|
|
||||||
org.eclipse.jetty.util.DecoratedObjectFactory.LEVEL=DEBUG
|
# org.eclipse.jetty.util.DecoratedObjectFactory.LEVEL=DEBUG
|
||||||
|
|
||||||
# org.eclipse.jetty.LEVEL=DEBUG
|
# org.eclipse.jetty.LEVEL=DEBUG
|
||||||
org.eclipse.jetty.websocket.LEVEL=DEBUG
|
# org.eclipse.jetty.websocket.LEVEL=DEBUG
|
||||||
# org.eclipse.jetty.websocket.client.LEVEL=DEBUG
|
# org.eclipse.jetty.websocket.client.LEVEL=DEBUG
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ public abstract class HttpConnection implements Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are HTTP 1.1, add the Host header
|
// If we are HTTP 1.1, add the Host header
|
||||||
if (version.getVersion() == 11)
|
if (version.getVersion() <= 11)
|
||||||
{
|
{
|
||||||
if (!headers.containsKey(HttpHeader.HOST.asString()))
|
if (!headers.containsKey(HttpHeader.HOST.asString()))
|
||||||
headers.put(getHttpDestination().getHostField());
|
headers.put(getHttpDestination().getHostField());
|
||||||
|
@ -121,14 +121,15 @@ public abstract class HttpConnection implements Connection
|
||||||
// Add content headers
|
// Add content headers
|
||||||
if (content != null)
|
if (content != null)
|
||||||
{
|
{
|
||||||
if (content instanceof ContentProvider.Typed)
|
if (!headers.containsKey(HttpHeader.CONTENT_TYPE.asString()))
|
||||||
{
|
{
|
||||||
if (!headers.containsKey(HttpHeader.CONTENT_TYPE.asString()))
|
String contentType = null;
|
||||||
{
|
if (content instanceof ContentProvider.Typed)
|
||||||
String contentType = ((ContentProvider.Typed)content).getContentType();
|
contentType = ((ContentProvider.Typed)content).getContentType();
|
||||||
if (contentType != null)
|
if (contentType != null)
|
||||||
headers.put(HttpHeader.CONTENT_TYPE, contentType);
|
headers.put(HttpHeader.CONTENT_TYPE, contentType);
|
||||||
}
|
else
|
||||||
|
headers.put(HttpHeader.CONTENT_TYPE, "application/octet-stream");
|
||||||
}
|
}
|
||||||
long contentLength = content.getLength();
|
long contentLength = content.getLength();
|
||||||
if (contentLength >= 0)
|
if (contentLength >= 0)
|
||||||
|
@ -136,11 +137,6 @@ public abstract class HttpConnection implements Connection
|
||||||
if (!headers.containsKey(HttpHeader.CONTENT_LENGTH.asString()))
|
if (!headers.containsKey(HttpHeader.CONTENT_LENGTH.asString()))
|
||||||
headers.put(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength));
|
headers.put(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!headers.containsKey(HttpHeader.TRANSFER_ENCODING.asString()))
|
|
||||||
headers.put(CHUNKED_FIELD);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cookies
|
// Cookies
|
||||||
|
|
|
@ -143,6 +143,8 @@ public class HttpChannelOverHTTP extends HttpChannel
|
||||||
closeReason = "failure";
|
closeReason = "failure";
|
||||||
else if (receiver.isShutdown())
|
else if (receiver.isShutdown())
|
||||||
closeReason = "server close";
|
closeReason = "server close";
|
||||||
|
else if (sender.isShutdown())
|
||||||
|
closeReason = "client close";
|
||||||
|
|
||||||
if (closeReason == null)
|
if (closeReason == null)
|
||||||
{
|
{
|
||||||
|
@ -157,7 +159,7 @@ public class HttpChannelOverHTTP extends HttpChannel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// HTTP 1.1 or greater closes only if it has an explicit close.
|
// HTTP 1.1 closes only if it has an explicit close.
|
||||||
if (responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()))
|
if (responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()))
|
||||||
closeReason = "http/1.1";
|
closeReason = "http/1.1";
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.eclipse.jetty.util.IteratingCallback;
|
||||||
public class HttpSenderOverHTTP extends HttpSender
|
public class HttpSenderOverHTTP extends HttpSender
|
||||||
{
|
{
|
||||||
private final HttpGenerator generator = new HttpGenerator();
|
private final HttpGenerator generator = new HttpGenerator();
|
||||||
|
private boolean shutdown;
|
||||||
|
|
||||||
public HttpSenderOverHTTP(HttpChannelOverHTTP channel)
|
public HttpSenderOverHTTP(HttpChannelOverHTTP channel)
|
||||||
{
|
{
|
||||||
|
@ -149,7 +150,12 @@ public class HttpSenderOverHTTP extends HttpSender
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Request shutdown output {}", getHttpExchange().getRequest());
|
LOG.debug("Request shutdown output {}", getHttpExchange().getRequest());
|
||||||
getHttpChannel().getHttpConnection().getEndPoint().shutdownOutput();
|
shutdown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isShutdown()
|
||||||
|
{
|
||||||
|
return shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -99,7 +99,10 @@ public class DigestAuthentication extends AbstractAuthentication
|
||||||
clientQOP = "auth-int";
|
clientQOP = "auth-int";
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DigestResult(headerInfo.getHeader(), response.getContent(), getRealm(), user, password, algorithm, nonce, clientQOP, opaque);
|
String realm = getRealm();
|
||||||
|
if (ANY_REALM.equals(realm))
|
||||||
|
realm = headerInfo.getRealm();
|
||||||
|
return new DigestResult(headerInfo.getHeader(), response.getContent(), realm, user, password, algorithm, nonce, clientQOP, opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> parseParameters(String wwwAuthenticate)
|
private Map<String, String> parseParameters(String wwwAuthenticate)
|
||||||
|
|
|
@ -19,26 +19,26 @@
|
||||||
package org.eclipse.jetty.client;
|
package org.eclipse.jetty.client;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletInputStream;
|
import javax.servlet.ServletInputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.ContentProvider;
|
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
|
||||||
|
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
|
||||||
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
||||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -51,43 +51,16 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClientConnectionCloseShutdownOutputWithoutRequestContent() throws Exception
|
public void test_ClientConnectionClose_ServerConnectionClose_ClientClosesAfterExchange() throws Exception
|
||||||
{
|
{
|
||||||
testClientConnectionCloseShutdownOutput(null);
|
byte[] data = new byte[128 * 1024];
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClientConnectionCloseShutdownOutputWithRequestContent() throws Exception
|
|
||||||
{
|
|
||||||
testClientConnectionCloseShutdownOutput(new StringContentProvider("data", StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClientConnectionCloseShutdownOutputWithChunkedRequestContent() throws Exception
|
|
||||||
{
|
|
||||||
DeferredContentProvider content = new DeferredContentProvider()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public long getLength()
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
content.offer(ByteBuffer.wrap("data".getBytes(StandardCharsets.UTF_8)));
|
|
||||||
content.close();
|
|
||||||
testClientConnectionCloseShutdownOutput(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testClientConnectionCloseShutdownOutput(ContentProvider content) throws Exception
|
|
||||||
{
|
|
||||||
AtomicReference<EndPoint> ref = new AtomicReference<>();
|
|
||||||
start(new AbstractHandler()
|
start(new AbstractHandler()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
ref.set(baseRequest.getHttpChannel().getEndPoint());
|
|
||||||
ServletInputStream input = request.getInputStream();
|
ServletInputStream input = request.getInputStream();
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -95,28 +68,190 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
|
||||||
if (read < 0)
|
if (read < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
response.setStatus(HttpStatus.OK_200);
|
|
||||||
|
response.setContentLength(data.length);
|
||||||
|
response.getOutputStream().write(data);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Delay the server from sending the TCP FIN.
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
catch (InterruptedException x)
|
||||||
|
{
|
||||||
|
throw new InterruptedIOException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
String host = "localhost";
|
||||||
|
int port = connector.getLocalPort();
|
||||||
|
|
||||||
|
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
|
||||||
|
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
|
||||||
|
|
||||||
|
ContentResponse response = client.newRequest(host, port)
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.path("/ctx/path")
|
|
||||||
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
|
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
|
||||||
.content(content)
|
.content(new StringContentProvider("0"))
|
||||||
|
.onRequestSuccess(request ->
|
||||||
|
{
|
||||||
|
HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().iterator().next();
|
||||||
|
Assert.assertFalse(connection.getEndPoint().isOutputShutdown());
|
||||||
|
})
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||||
|
Assert.assertArrayEquals(data, response.getContent());
|
||||||
|
Assert.assertEquals(0, connectionPool.getConnectionCount());
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the FIN to arrive to the server
|
@Test
|
||||||
Thread.sleep(1000);
|
public void test_ClientConnectionClose_ServerDoesNotRespond_ClientIdleTimeout() throws Exception
|
||||||
|
{
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
request.startAsync();
|
||||||
|
// Do not respond.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Do not read from the server because it will trigger
|
String host = "localhost";
|
||||||
// the send of the TLS Close Message before the response.
|
int port = connector.getLocalPort();
|
||||||
|
|
||||||
EndPoint serverEndPoint = ref.get();
|
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
|
||||||
ByteBuffer buffer = BufferUtil.allocate(1);
|
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
|
||||||
int read = serverEndPoint.fill(buffer);
|
|
||||||
Assert.assertEquals(-1, read);
|
CountDownLatch resultLatch = new CountDownLatch(1);
|
||||||
|
long idleTimeout = 1000;
|
||||||
|
client.newRequest(host, port)
|
||||||
|
.scheme(scheme)
|
||||||
|
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
|
||||||
|
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.onRequestSuccess(request ->
|
||||||
|
{
|
||||||
|
HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().iterator().next();
|
||||||
|
Assert.assertFalse(connection.getEndPoint().isOutputShutdown());
|
||||||
|
})
|
||||||
|
.send(result ->
|
||||||
|
{
|
||||||
|
if (result.isFailed())
|
||||||
|
resultLatch.countDown();
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.assertTrue(resultLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
|
||||||
|
Assert.assertEquals(0, connectionPool.getConnectionCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ClientConnectionClose_ServerPartialResponse_ClientIdleTimeout() throws Exception
|
||||||
|
{
|
||||||
|
long idleTimeout = 1000;
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
|
||||||
|
ServletInputStream input = request.getInputStream();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int read = input.read();
|
||||||
|
if (read < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.getOutputStream().print("Hello");
|
||||||
|
response.flushBuffer();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Thread.sleep(2 * idleTimeout);
|
||||||
|
}
|
||||||
|
catch (InterruptedException x)
|
||||||
|
{
|
||||||
|
throw new InterruptedIOException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
String host = "localhost";
|
||||||
|
int port = connector.getLocalPort();
|
||||||
|
|
||||||
|
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
|
||||||
|
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
|
||||||
|
|
||||||
|
DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.allocate(8));
|
||||||
|
CountDownLatch resultLatch = new CountDownLatch(1);
|
||||||
|
client.newRequest(host, port)
|
||||||
|
.scheme(scheme)
|
||||||
|
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
|
||||||
|
.content(content)
|
||||||
|
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.onRequestSuccess(request ->
|
||||||
|
{
|
||||||
|
HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().iterator().next();
|
||||||
|
Assert.assertFalse(connection.getEndPoint().isOutputShutdown());
|
||||||
|
})
|
||||||
|
.send(result ->
|
||||||
|
{
|
||||||
|
if (result.isFailed())
|
||||||
|
resultLatch.countDown();
|
||||||
|
});
|
||||||
|
content.offer(ByteBuffer.allocate(8));
|
||||||
|
content.close();
|
||||||
|
|
||||||
|
Assert.assertTrue(resultLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
|
||||||
|
Assert.assertEquals(0, connectionPool.getConnectionCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ClientConnectionClose_ServerNoConnectionClose_ClientCloses() throws Exception
|
||||||
|
{
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
response.setContentLength(0);
|
||||||
|
response.flushBuffer();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Delay the server from sending the TCP FIN.
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
catch (InterruptedException x)
|
||||||
|
{
|
||||||
|
throw new InterruptedIOException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
String host = "localhost";
|
||||||
|
int port = connector.getLocalPort();
|
||||||
|
|
||||||
|
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
|
||||||
|
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
|
||||||
|
|
||||||
|
ContentResponse response = client.newRequest(host, port)
|
||||||
|
.scheme(scheme)
|
||||||
|
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
|
||||||
|
.onRequestSuccess(request ->
|
||||||
|
{
|
||||||
|
HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().iterator().next();
|
||||||
|
Assert.assertFalse(connection.getEndPoint().isOutputShutdown());
|
||||||
|
})
|
||||||
|
.onResponseHeaders(r -> r.getHeaders().remove(HttpHeader.CONNECTION))
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||||
|
Assert.assertEquals(0, connectionPool.getConnectionCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,6 +135,14 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
test_Authentication(new DigestAuthentication(uri, realm, "digest", "digest"));
|
test_Authentication(new DigestAuthentication(uri, realm, "digest", "digest"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_DigestAnyRealm() throws Exception
|
||||||
|
{
|
||||||
|
startDigest(new EmptyServerHandler());
|
||||||
|
URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
|
||||||
|
test_Authentication(new DigestAuthentication(uri, Authentication.ANY_REALM, "digest", "digest"));
|
||||||
|
}
|
||||||
|
|
||||||
private void test_Authentication(Authentication authentication) throws Exception
|
private void test_Authentication(Authentication authentication) throws Exception
|
||||||
{
|
{
|
||||||
AuthenticationStore authenticationStore = client.getAuthenticationStore();
|
AuthenticationStore authenticationStore = client.getAuthenticationStore();
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client;
|
package org.eclipse.jetty.client;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -71,6 +73,7 @@ import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||||
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
||||||
import org.eclipse.jetty.client.util.FutureResponseListener;
|
import org.eclipse.jetty.client.util.FutureResponseListener;
|
||||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||||
|
import org.eclipse.jetty.http.BadMessageException;
|
||||||
import org.eclipse.jetty.http.HttpField;
|
import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
|
@ -1289,13 +1292,29 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testSmallContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception
|
public void testSmallContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception
|
||||||
{
|
{
|
||||||
testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 1024);
|
try
|
||||||
|
{
|
||||||
|
testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 1024);
|
||||||
|
}
|
||||||
|
catch(ExecutionException e)
|
||||||
|
{
|
||||||
|
assertThat(e.getCause(), Matchers.instanceOf(BadMessageException.class));
|
||||||
|
assertThat(e.getCause().getMessage(), Matchers.containsString("Unknown content"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBigContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception
|
public void testBigContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception
|
||||||
{
|
{
|
||||||
testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 128 * 1024);
|
try
|
||||||
|
{
|
||||||
|
testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 128 * 1024);
|
||||||
|
}
|
||||||
|
catch(ExecutionException e)
|
||||||
|
{
|
||||||
|
assertThat(e.getCause(), Matchers.instanceOf(BadMessageException.class));
|
||||||
|
assertThat(e.getCause().getMessage(), Matchers.containsString("Unknown content"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1563,8 +1582,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCopyRequest()
|
public void testCopyRequest() throws Exception
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
startClient();
|
startClient();
|
||||||
|
|
||||||
|
@ -1611,6 +1629,28 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
.header("X-Custom-Header-2", "value"));
|
.header("X-Custom-Header-2", "value"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHostWithHTTP10() 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.assertThat(request.getHeader("Host"), Matchers.notNullValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.scheme(scheme)
|
||||||
|
.version(HttpVersion.HTTP_1_0)
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
private void assertCopyRequest(Request original)
|
private void assertCopyRequest(Request original)
|
||||||
{
|
{
|
||||||
Request copy = client.copyRequest((HttpRequest) original, original.getURI());
|
Request copy = client.copyRequest((HttpRequest) original, original.getURI());
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.eclipse.jetty.io.ByteArrayEndPoint;
|
||||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||||
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
||||||
import org.eclipse.jetty.util.Promise;
|
import org.eclipse.jetty.util.Promise;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -258,7 +259,7 @@ public class HttpSenderOverHTTPTest
|
||||||
|
|
||||||
String requestString = endPoint.takeOutputString();
|
String requestString = endPoint.takeOutputString();
|
||||||
Assert.assertTrue(requestString.startsWith("GET "));
|
Assert.assertTrue(requestString.startsWith("GET "));
|
||||||
Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content1 + content2));
|
Assert.assertThat(requestString,Matchers.endsWith("\r\n\r\n" + content1 + content2));
|
||||||
Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
|
Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
|
||||||
Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
|
Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ public class TypedContentProviderTest extends AbstractHttpClientServerTest
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
Assert.assertEquals("GET", request.getMethod());
|
Assert.assertEquals("GET", request.getMethod());
|
||||||
Assert.assertNull(request.getContentType());
|
Assert.assertNotNull(request.getContentType());
|
||||||
Assert.assertEquals(content, IO.toString(request.getInputStream()));
|
Assert.assertEquals(content, IO.toString(request.getInputStream()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,3 +2,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||||
#org.eclipse.jetty.LEVEL=DEBUG
|
#org.eclipse.jetty.LEVEL=DEBUG
|
||||||
#org.eclipse.jetty.client.LEVEL=DEBUG
|
#org.eclipse.jetty.client.LEVEL=DEBUG
|
||||||
#org.eclipse.jetty.io.ChannelEndPoint.LEVEL=DEBUG
|
#org.eclipse.jetty.io.ChannelEndPoint.LEVEL=DEBUG
|
||||||
|
#org.eclipse.jetty.http.LEVEL=DEBUG
|
||||||
|
|
|
@ -27,9 +27,11 @@ Enabling these frameworks in the Jetty distribution is as easy as activating any
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
[my-base]$ java -jar ../start.jar --add-to-start=logging-jetty
|
[my-base]$ java -jar ../start.jar --add-to-start=logging-jetty
|
||||||
INFO : logging-jetty initialized in ${jetty.base}/start.d/logging-jetty.ini
|
INFO : logging-jetty initialized in ${jetty.base}/start.d/logging-jetty.ini
|
||||||
INFO : resources transitive
|
INFO : resources transitively enabled
|
||||||
INFO : Base directory was modified
|
MKDIR : ${jetty.base}/resources
|
||||||
|
COPY : ${jetty.home}/modules/logging-jetty/resources/jetty-logging.properties to ${jetty.base}/resources/jetty-logging.properties
|
||||||
|
INFO : Base directory was modified
|
||||||
....
|
....
|
||||||
|
|
||||||
As noted above, Jetty supports a wide array of logging technologies.
|
As noted above, Jetty supports a wide array of logging technologies.
|
||||||
|
@ -78,7 +80,6 @@ Most other top level logging modules work in the same way: `logging-jcl`, `loggi
|
||||||
Jetty uses the SLF4J api as a binding to provide logging information to additional frameworks such as Log4j or Logback.
|
Jetty uses the SLF4J api as a binding to provide logging information to additional frameworks such as Log4j or Logback.
|
||||||
It can also be used on it's own to provide simple server logging.
|
It can also be used on it's own to provide simple server logging.
|
||||||
To enable the SLF4J framework, you need to activate the `logging-slf4j` module.
|
To enable the SLF4J framework, you need to activate the `logging-slf4j` module.
|
||||||
By default, log files will be stored in `${jetty.base}/logs`.
|
|
||||||
|
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
|
@ -115,9 +116,9 @@ Proceed (y/N)? y
|
||||||
INFO : slf4j-api transitively enabled
|
INFO : slf4j-api transitively enabled
|
||||||
INFO : logging-slf4j initialized in ${jetty.base}/start.d/logging-slf4j.ini
|
INFO : logging-slf4j initialized in ${jetty.base}/start.d/logging-slf4j.ini
|
||||||
MKDIR : ${jetty.base}/lib/slf4j
|
MKDIR : ${jetty.base}/lib/slf4j
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
||||||
INFO : Base directory was modified
|
INFO : Base directory was modified
|
||||||
ERROR : Module logging-slf4j requires a `slf4j-impl` module from one of [slf4j-simple-impl, slf4j-logback, slf4j-jul, slf4j-jcl, slf4j-log4j2, slf4j-log4j]
|
ERROR : Module logging-slf4j requires a module providing slf4j-impl from one of [slf4j-simple-impl, slf4j-logback, slf4j-jul, slf4j-log4j2, slf4j-log4j]
|
||||||
|
|
||||||
ERROR : Unsatisfied module dependencies: logging-slf4j
|
ERROR : Unsatisfied module dependencies: logging-slf4j
|
||||||
|
|
||||||
|
@ -134,10 +135,9 @@ To enable the simple SLF4J implementation, we will also need to activate the `sl
|
||||||
[my-base]$ java -jar ../start.jar --add-to-start=slf4j-simple-impl
|
[my-base]$ java -jar ../start.jar --add-to-start=slf4j-simple-impl
|
||||||
INFO : slf4j-simple-impl initialized in ${jetty.base}/start.d/slf4j-simple-impl.ini
|
INFO : slf4j-simple-impl initialized in ${jetty.base}/start.d/slf4j-simple-impl.ini
|
||||||
INFO : resources transitively enabled
|
INFO : resources transitively enabled
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-simple/1.7.21/slf4j-simple-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-simple-1.7.21.jar
|
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-simple/1.7.21/slf4j-simple-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-simple-1.7.21.jar
|
||||||
MKDIR : ${jetty.base}/resources
|
MKDIR : ${jetty.base}/resources
|
||||||
COPY : ${jetty.home}/modules/slf4j/simplelogger.properties to ${jetty.base}/resources/simplelogger.properties
|
COPY : ${jetty.home}/modules/slf4j-simple-impl/resources/simplelogger.properties to ${jetty.base}/resources/simplelogger.properties
|
||||||
MKDIR : ${jetty.base}/logs
|
|
||||||
INFO : Base directory was modified
|
INFO : Base directory was modified
|
||||||
|
|
||||||
[my-base]$ tree
|
[my-base]$ tree
|
||||||
|
@ -146,7 +146,6 @@ INFO : Base directory was modified
|
||||||
│ └── slf4j
|
│ └── slf4j
|
||||||
│ ├── slf4j-api-1.7.21.jar
|
│ ├── slf4j-api-1.7.21.jar
|
||||||
│ └── slf4j-simple-1.7.21.jar
|
│ └── slf4j-simple-1.7.21.jar
|
||||||
├── logs
|
|
||||||
├── resources
|
├── resources
|
||||||
│ └── simplelogger.properties
|
│ └── simplelogger.properties
|
||||||
└── start.d
|
└── start.d
|
||||||
|
@ -154,13 +153,15 @@ INFO : Base directory was modified
|
||||||
└── slf4j-simple-impl.ini
|
└── slf4j-simple-impl.ini
|
||||||
....
|
....
|
||||||
|
|
||||||
|
Jetty is now configured to log using the SLF4J framework.
|
||||||
|
A standard SLF4J properties file is located in `${jetty.base}/resources/simplelogger.properties`.
|
||||||
|
|
||||||
[[example-logging-log4j]]
|
[[example-logging-log4j]]
|
||||||
==== Logging with Log4j and Log4j2
|
==== Logging with Log4j and Log4j2
|
||||||
|
|
||||||
It is possible to have the Jetty Server logging configured so that Log4j or Log4j2 controls the output of logging events produced by Jetty.
|
It is possible to have the Jetty Server logging configured so that Log4j or Log4j2 controls the output of logging events produced by Jetty.
|
||||||
This is accomplished by configuring Jetty for logging to http://logging.apache.org/log4j/[Apache Log4j] via http://slf4j.org/manual.html[Slf4j] and the http://slf4j.org/manual.html#swapping[Slf4j binding layer for Log4j].
|
This is accomplished by configuring Jetty for logging to http://logging.apache.org/log4j/[Apache Log4j] via http://slf4j.org/manual.html[Slf4j] and the http://slf4j.org/manual.html#swapping[Slf4j binding layer for Log4j].
|
||||||
Implementation of Log4j can be done by enabling the `logging-log4j` module.
|
Implementation of Log4j can be done by enabling the `logging-log4j` module.
|
||||||
By default, log files will be stored in `${jetty.base}/logs`.
|
|
||||||
|
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
|
@ -204,13 +205,12 @@ INFO : resources transitively enabled
|
||||||
INFO : slf4j-log4j transitively enabled
|
INFO : slf4j-log4j transitively enabled
|
||||||
INFO : logging-log4j initialized in ${jetty.base}/start.d/logging-log4j.ini
|
INFO : logging-log4j initialized in ${jetty.base}/start.d/logging-log4j.ini
|
||||||
MKDIR : ${jetty.base}/lib/slf4j
|
MKDIR : ${jetty.base}/lib/slf4j
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
||||||
MKDIR : ${jetty.base}/resources
|
|
||||||
COPY : ${jetty.home}/modules/log4j/log4j.properties to ${jetty.base}/resources/log4j.properties
|
|
||||||
MKDIR : ${jetty.base}/lib/log4j
|
MKDIR : ${jetty.base}/lib/log4j
|
||||||
DOWNLOAD: http://central.maven.org/maven2/log4j/log4j/1.2.17/log4j-1.2.17.jar to ${jetty.base}/lib/log4j/log4j-1.2.17.jar
|
COPY : /Users/chris/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar to ${jetty.base}/lib/log4j/log4j-1.2.17.jar
|
||||||
MKDIR : ${jetty.base}/logs
|
MKDIR : ${jetty.base}/resources
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-log4j12/1.7.21/slf4j-log4j12-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-log4j12-1.7.21.jar
|
COPY : ${jetty.home}/modules/log4j-impl/resources/log4j.xml to ${jetty.base}/resources/log4j.xml
|
||||||
|
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-log4j12/1.7.21/slf4j-log4j12-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-log4j12-1.7.21.jar
|
||||||
INFO : Base directory was modified
|
INFO : Base directory was modified
|
||||||
|
|
||||||
[my-base]$ tree
|
[my-base]$ tree
|
||||||
|
@ -221,19 +221,20 @@ INFO : Base directory was modified
|
||||||
│ └── slf4j
|
│ └── slf4j
|
||||||
│ ├── slf4j-api-1.7.21.jar
|
│ ├── slf4j-api-1.7.21.jar
|
||||||
│ └── slf4j-log4j12-1.7.21.jar
|
│ └── slf4j-log4j12-1.7.21.jar
|
||||||
├── logs
|
|
||||||
├── resources
|
├── resources
|
||||||
│ └── log4j.properties
|
│ └── log4j.xml
|
||||||
└── start.d
|
└── start.d
|
||||||
└── logging-log4j.ini
|
└── logging-log4j.ini
|
||||||
....
|
....
|
||||||
|
|
||||||
Or, to enable Log4j2, simply enable the `logging-log4j2` module.
|
Jetty is now configured to log using the Log4j framework.
|
||||||
By default, log files will be stored in `${jetty.base}/logs`.
|
A standard Log4j configuration file is located in `${jetty.base}/resources/log4j.xml`.
|
||||||
|
|
||||||
|
Or, to set up Log4j2, enable the `logging-log4j2` module.
|
||||||
|
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
[my-base]$ java -jar ../start.jar --add-to-start=logging-log4j2
|
[my-base]$ java -jar ../start.jar --add-to-start=logging-log4j2
|
||||||
|
|
||||||
ALERT: There are enabled module(s) with licenses.
|
ALERT: There are enabled module(s) with licenses.
|
||||||
The following 2 module(s):
|
The following 2 module(s):
|
||||||
|
@ -274,34 +275,32 @@ INFO : resources transitively enabled
|
||||||
INFO : slf4j-log4j2 transitively enabled
|
INFO : slf4j-log4j2 transitively enabled
|
||||||
INFO : log4j2-impl transitively enabled
|
INFO : log4j2-impl transitively enabled
|
||||||
MKDIR : ${jetty.base}/lib/slf4j
|
MKDIR : ${jetty.base}/lib/slf4j
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
||||||
MKDIR : ${jetty.base}/lib/log4j
|
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.6.1/log4j-api-2.6.1.jar to ${jetty.base}/lib/log4j/log4j-api-2.6.1.jar
|
|
||||||
MKDIR : ${jetty.base}/resources
|
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.6.1/log4j-slf4j-impl-2.6.1.jar to ${jetty.base}/lib/log4j/log4j-slf4j-impl-2.6.1.jar
|
|
||||||
MKDIR : ${jetty.base}/lib/log4j2
|
MKDIR : ${jetty.base}/lib/log4j2
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.6.1/log4j-core-2.6.1.jar to ${jetty.base}/lib/log4j2/log4j-core-2.6.1.jar
|
DOWNLD: http://central.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.6.1/log4j-api-2.6.1.jar to ${jetty.base}/lib/log4j2/log4j-api-2.6.1.jar
|
||||||
COPY : ${jetty.home}/modules/log4j2/log4j2.xml to ${jetty.base}/resources/log4j2.xml
|
MKDIR : ${jetty.base}/resources
|
||||||
MKDIR : ${jetty.base}/logs
|
DOWNLD: http://central.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.6.1/log4j-slf4j-impl-2.6.1.jar to ${jetty.base}/lib/log4j2/log4j-slf4j-impl-2.6.1.jar
|
||||||
|
DOWNLD: http://central.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.6.1/log4j-core-2.6.1.jar to ${jetty.base}/lib/log4j2/log4j-core-2.6.1.jar
|
||||||
|
COPY : ${jetty.home}/modules/log4j2-impl/resources/log4j2.xml to ${jetty.base}/resources/log4j2.xml
|
||||||
INFO : Base directory was modified
|
INFO : Base directory was modified
|
||||||
|
|
||||||
[my-base]$ tree
|
[my-base]$ tree
|
||||||
.
|
.
|
||||||
├── lib
|
├── lib
|
||||||
│ ├── log4j
|
|
||||||
│ │ ├── log4j-api-2.6.1.jar
|
|
||||||
│ │ └── log4j-slf4j-impl-2.6.1.jar
|
|
||||||
│ ├── log4j2
|
│ ├── log4j2
|
||||||
│ │ └── log4j-core-2.6.1.jar
|
│ │ ├── log4j-api-2.6.1.jar
|
||||||
|
│ │ ├── log4j-core-2.6.1.jar
|
||||||
|
│ │ └── log4j-slf4j-impl-2.6.1.jar
|
||||||
│ └── slf4j
|
│ └── slf4j
|
||||||
│ └── slf4j-api-1.7.21.jar
|
│ └── slf4j-api-1.7.21.jar
|
||||||
├── logs
|
|
||||||
├── resources
|
├── resources
|
||||||
│ └── log4j2.xml
|
│ └── log4j2.xml
|
||||||
└── start.d
|
└── start.d
|
||||||
└── logging-log4j2.ini
|
└── logging-log4j2.ini
|
||||||
....
|
....
|
||||||
|
|
||||||
|
At this point Jetty is configured so that the Jetty server itself will log using Log4j2, using the Log4j2 configuration found in `{$jetty.base}/resources/log4j2.xml`.
|
||||||
|
|
||||||
[[example-logging-logback]]
|
[[example-logging-logback]]
|
||||||
==== Logging with Logback
|
==== Logging with Logback
|
||||||
|
|
||||||
|
@ -309,7 +308,6 @@ It is possible to have the Jetty Server logging configured so that Logback contr
|
||||||
This is accomplished by configuring Jetty for logging to `Logback`, which uses http://slf4j.org/manual.html[Slf4j] and the http://logback.qos.ch/[Logback Implementation for Slf4j].
|
This is accomplished by configuring Jetty for logging to `Logback`, which uses http://slf4j.org/manual.html[Slf4j] and the http://logback.qos.ch/[Logback Implementation for Slf4j].
|
||||||
|
|
||||||
To set up Jetty logging via Logback, enable the `logging-logback` module.
|
To set up Jetty logging via Logback, enable the `logging-logback` module.
|
||||||
By default, log files will be stored in `${jetty.base}/logs`.
|
|
||||||
|
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
|
@ -362,13 +360,12 @@ INFO : slf4j-logback transitively enabled
|
||||||
INFO : logging-logback initialized in ${jetty.base}/start.d/logging-logback.ini
|
INFO : logging-logback initialized in ${jetty.base}/start.d/logging-logback.ini
|
||||||
INFO : resources transitively enabled
|
INFO : resources transitively enabled
|
||||||
MKDIR : ${jetty.base}/lib/slf4j
|
MKDIR : ${jetty.base}/lib/slf4j
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
||||||
MKDIR : ${jetty.base}/lib/logback
|
MKDIR : ${jetty.base}/lib/logback
|
||||||
DOWNLOAD: http://central.maven.org/maven2/ch/qos/logback/logback-core/1.1.7/logback-core-1.1.7.jar to ${jetty.base}/lib/logback/logback-core-1.1.7.jar
|
DOWNLD: http://central.maven.org/maven2/ch/qos/logback/logback-core/1.1.7/logback-core-1.1.7.jar to ${jetty.base}/lib/logback/logback-core-1.1.7.jar
|
||||||
MKDIR : ${jetty.base}/resources
|
MKDIR : ${jetty.base}/resources
|
||||||
COPY : ${jetty.home}/modules/logback/logback.xml to ${jetty.base}/resources/logback.xml
|
COPY : ${jetty.home}/modules/logback-impl/resources/logback.xml to ${jetty.base}/resources/logback.xml
|
||||||
MKDIR : ${jetty.base}/logs
|
DOWNLD: http://central.maven.org/maven2/ch/qos/logback/logback-classic/1.1.7/logback-classic-1.1.7.jar to ${jetty.base}/lib/logback/logback-classic-1.1.7.jar
|
||||||
DOWNLOAD: http://central.maven.org/maven2/ch/qos/logback/logback-classic/1.1.7/logback-classic-1.1.7.jar to ${jetty.base}/lib/logback/logback-classic-1.1.7.jar
|
|
||||||
INFO : Base directory was modified
|
INFO : Base directory was modified
|
||||||
|
|
||||||
[my-base]$ tree
|
[my-base]$ tree
|
||||||
|
@ -379,7 +376,6 @@ INFO : Base directory was modified
|
||||||
│ │ └── logback-core-1.1.7.jar
|
│ │ └── logback-core-1.1.7.jar
|
||||||
│ └── slf4j
|
│ └── slf4j
|
||||||
│ └── slf4j-api-1.7.21.jar
|
│ └── slf4j-api-1.7.21.jar
|
||||||
├── logs
|
|
||||||
├── resources
|
├── resources
|
||||||
│ └── logback.xml
|
│ └── logback.xml
|
||||||
└── start.d
|
└── start.d
|
||||||
|
@ -387,7 +383,6 @@ INFO : Base directory was modified
|
||||||
....
|
....
|
||||||
|
|
||||||
At this point Jetty is configured so that the Jetty server itself will log using Logback, using the Logback configuration found in `{$jetty.base}/resources/logback.xml`.
|
At this point Jetty is configured so that the Jetty server itself will log using Logback, using the Logback configuration found in `{$jetty.base}/resources/logback.xml`.
|
||||||
Log files will be stored in `${jetty.base}/logs`.
|
|
||||||
|
|
||||||
==== Logging with Java Util Logging
|
==== Logging with Java Util Logging
|
||||||
|
|
||||||
|
@ -395,8 +390,7 @@ Log files will be stored in `${jetty.base}/logs`.
|
||||||
===== Java Util Logging with SLF4J
|
===== Java Util Logging with SLF4J
|
||||||
It is possible to have the Jetty Server logging configured so that `java.util.logging` controls the output of logging events produced by Jetty.
|
It is possible to have the Jetty Server logging configured so that `java.util.logging` controls the output of logging events produced by Jetty.
|
||||||
|
|
||||||
This example demonstrates how to configuring Jetty for logging to `java.util.logging` via http://slf4j.org/manual.html[Slf4j] and the http://slf4j.org/manual.html#swapping[Slf4j binding layer for java.util.logging].
|
This example demonstrates how to configuring Jetty for logging to `java.util.logging` via http://slf4j.org/manual.html[SLF4J] as a binding layer.
|
||||||
By default, log files will be stored in `${jetty.base}/logs`.
|
|
||||||
|
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
|
@ -436,12 +430,10 @@ INFO : slf4j-jul transitively enabled
|
||||||
INFO : logging-jul initialized in ${jetty.base}/start.d/logging-jul.ini
|
INFO : logging-jul initialized in ${jetty.base}/start.d/logging-jul.ini
|
||||||
INFO : resources transitively enabled
|
INFO : resources transitively enabled
|
||||||
MKDIR : ${jetty.base}/etc
|
MKDIR : ${jetty.base}/etc
|
||||||
COPY : ${jetty.home}/modules/jul-impl/java-util-logging.properties to ${jetty.base}/etc/java-util-logging.properties
|
COPY : ${jetty.home}/modules/jul-impl/etc/java-util-logging.properties to ${jetty.base}/etc/java-util-logging.properties
|
||||||
MKDIR : ${jetty.base}/logs
|
|
||||||
MKDIR : ${jetty.base}/lib/slf4j
|
MKDIR : ${jetty.base}/lib/slf4j
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-jdk14/1.7.21/slf4j-jdk14-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-jdk14-1.7.21.jar
|
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-jdk14/1.7.21/slf4j-jdk14-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-jdk14-1.7.21.jar
|
||||||
MKDIR : ${jetty.base}/resources
|
|
||||||
INFO : Base directory was modified
|
INFO : Base directory was modified
|
||||||
|
|
||||||
[my-base]$ tree
|
[my-base]$ tree
|
||||||
|
@ -452,76 +444,49 @@ INFO : Base directory was modified
|
||||||
│ └── slf4j
|
│ └── slf4j
|
||||||
│ ├── slf4j-api-1.7.21.jar
|
│ ├── slf4j-api-1.7.21.jar
|
||||||
│ └── slf4j-jdk14-1.7.21.jar
|
│ └── slf4j-jdk14-1.7.21.jar
|
||||||
├── logs
|
|
||||||
├── resources
|
|
||||||
└── start.d
|
└── start.d
|
||||||
└── logging-jul.ini
|
└── logging-jul.ini
|
||||||
....
|
....
|
||||||
|
|
||||||
[[example-logging-java-commons-logging]]
|
Jetty is now configured to log using the JUL framework.
|
||||||
==== Logging with Java Commons Logging
|
A standard JUL properties file is located in `${jetty.base}/etc/java-util-logging.properties`.
|
||||||
Jetty provides support of the Java Commons Logging (jcl) through the `logging-jcl` module, using Slf4j as a binding.
|
|
||||||
This is enabled by activating the `logging-jcl` module.
|
==== Capturing Console Output
|
||||||
By default, log files will be stored in `${jetty.base}/logs`.
|
|
||||||
|
By default, enabling the above modules will output log information to the console.
|
||||||
|
Included in the distribution is the `console-capture` module, which can be used in lieu of additional configuration to the selected logging module to capture this output to a `logs` directory in your `${jetty.base}`.
|
||||||
|
To enable this functionality, activate the `console-capture` module.
|
||||||
|
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
[my-base]$ java -jar ../start.jar --add-to-start=logging-jcl
|
[my-base]$ java -jar ../start.jar --add-to-start=console-capture
|
||||||
|
INFO : console-capture initialized in ${jetty.base}/start.d/console-capture.ini
|
||||||
ALERT: There are enabled module(s) with licenses.
|
MKDIR : ${jetty.base}/logs
|
||||||
The following 2 module(s):
|
INFO : Base directory was modified
|
||||||
+ contains software not provided by the Eclipse Foundation!
|
|
||||||
+ contains software not covered by the Eclipse Public License!
|
|
||||||
+ has not been audited for compliance with its license
|
|
||||||
|
|
||||||
Module: jcl-impl
|
|
||||||
+ Log4j is released under the Apache 2.0 license.
|
|
||||||
+ http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
|
|
||||||
Module: slf4j-api
|
|
||||||
+ SLF4J is distributed under the MIT License.
|
|
||||||
+ Copyright (c) 2004-2013 QOS.ch
|
|
||||||
+ All rights reserved.
|
|
||||||
+ Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
+ a copy of this software and associated documentation files (the
|
|
||||||
+ "Software"), to deal in the Software without restriction, including
|
|
||||||
+ without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
+ distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
+ permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
+ the following conditions:
|
|
||||||
+ The above copyright notice and this permission notice shall be
|
|
||||||
+ included in all copies or substantial portions of the Software.
|
|
||||||
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
Proceed (y/N)? y
|
|
||||||
INFO : slf4j-api transitively enabled
|
|
||||||
INFO : jcl-impl transitively enabled
|
|
||||||
INFO : resources transitively enabled
|
|
||||||
INFO : slf4j-jcl transitively enabled
|
|
||||||
INFO : logging-jcl initialized in ${jetty.base}/start.d/logging-jcl.ini
|
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
|
||||||
DOWNLOAD: http://central.maven.org/maven2/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar to ${jetty.base}/lib/jcl/commons-logging-1.1.3.jar
|
|
||||||
MKDIR: ${jetty.base}/logs
|
|
||||||
DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-jcl/1.7.21/slf4j-jcl-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-jcl-1.7.21.jar
|
|
||||||
INFO : Base directory was modified
|
|
||||||
|
|
||||||
[my-base]$ tree
|
[my-base]$ tree
|
||||||
.
|
.
|
||||||
├── lib
|
|
||||||
│ ├── jcl
|
|
||||||
│ │ └── commons-logging-1.1.3.jar
|
|
||||||
│ └── slf4j
|
|
||||||
│ ├── slf4j-api-1.7.21.jar
|
|
||||||
│ └── slf4j-jcl-1.7.21.jar
|
|
||||||
├── logs
|
├── logs
|
||||||
├── resources
|
|
||||||
│ └── commons-logging.properties
|
|
||||||
└── start.d
|
└── start.d
|
||||||
└── logging-jcl.ini
|
└── console-capture.ini
|
||||||
|
....
|
||||||
|
|
||||||
|
As an example, here is the output from Logback before using the `console-capture` module:
|
||||||
|
|
||||||
|
[source, screen, subs="{sub-order}"]
|
||||||
|
....
|
||||||
|
[my-base]$ java -jar ../start.jar
|
||||||
|
419 [main] INFO org.eclipse.jetty.util.log - Logging initialized @508ms to org.eclipse.jetty.util.log.Slf4jLog
|
||||||
|
540 [main] INFO org.eclipse.jetty.server.Server - jetty-9.4.0-SNAPSHOT
|
||||||
|
575 [main] INFO o.e.jetty.server.AbstractConnector - Started ServerConnector@3c0ecd4b{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
|
||||||
|
575 [main] INFO org.eclipse.jetty.server.Server - Started @668ms
|
||||||
|
....
|
||||||
|
|
||||||
|
After enabling `console-capture`, the output is as follows, which displays the location the log is being saved to:
|
||||||
|
|
||||||
|
[source, screen, subs="{sub-order}"]
|
||||||
|
....
|
||||||
|
[my-base]$ java -jar ../start.jar
|
||||||
|
151 [main] INFO org.eclipse.jetty.util.log - Logging initialized @238ms to org.eclipse.jetty.util.log.Slf4jLog
|
||||||
|
196 [main] INFO org.eclipse.jetty.util.log - Console stderr/stdout captured to /installs/jetty-distribution/mybase/logs/2016_10_21.jetty.log
|
||||||
....
|
....
|
||||||
|
|
|
@ -23,21 +23,39 @@
|
||||||
If you do nothing to configure a separate logging framework, Jetty will default to using an internal `org.eclipse.jetty.util.log.StdErrLog` implementation.
|
If you do nothing to configure a separate logging framework, Jetty will default to using an internal `org.eclipse.jetty.util.log.StdErrLog` implementation.
|
||||||
This will output all logging events to STDERR (aka `System.err`).
|
This will output all logging events to STDERR (aka `System.err`).
|
||||||
|
|
||||||
Simply use Jetty and `StdErrLog` based logging is output to the console.
|
Simply use Jetty and `StdErrLog`-based logging is output to the console.
|
||||||
|
|
||||||
Included in the Jetty distribution is a logging module that is capable of performing simple capturing of all STDOUT (`System.out`) and STDERR (`System.err`) output to a file that is rotated daily.
|
Included in the Jetty distribution is a logging module named `console-capture` that is capable of performing simple capturing of all STDOUT (`System.out`) and STDERR (`System.err`) output to a file that is rotated daily.
|
||||||
|
|
||||||
To enable on this feature via the command line:
|
To enable this feature, simply activate the `console-capture` module on the command line:
|
||||||
|
|
||||||
|
[source, screen, subs="{sub-order}"]
|
||||||
|
....
|
||||||
|
[my-base]$ java -jar ../start.jar --add-to-start=console-capture
|
||||||
|
INFO : console-capture initialized in ${jetty.base}/start.d/console-capture.ini
|
||||||
|
MKDIR : ${jetty.base}/logs
|
||||||
|
INFO : Base directory was modified
|
||||||
|
|
||||||
|
[my-base]$ tree
|
||||||
|
.
|
||||||
|
├── logs
|
||||||
|
└── start.d
|
||||||
|
└── console-capture.ini
|
||||||
|
....
|
||||||
|
|
||||||
|
The default configuration for logging output will create a file `${jetty.base}/logs/yyyy_mm_dd.stderrout.log` which allows configuration of the output directory by setting the `jetty.logs` property.
|
||||||
|
|
||||||
|
Just enabling the `console-capture` will simply output the values of STDERR and STDOUT to a log file.
|
||||||
|
To customize the log further, a module named `logging-jetty` is available to provides a default properties file to configure.
|
||||||
|
As with `console-capture`, you activate the `logging-jetty` on the command line.
|
||||||
|
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
[my-base]$ java -jar ../start.jar --add-to-start=logging-jetty
|
[my-base]$ java -jar ../start.jar --add-to-start=logging-jetty
|
||||||
INFO : logging-jetty initialized in ${jetty.base}/start.d/logging-jetty.ini
|
INFO : logging-jetty initialized in ${jetty.base}/start.d/logging-jetty.ini
|
||||||
INFO : console-capture transitively enabled, ini template available with --add-to-start=console-capture
|
|
||||||
INFO : resources transitively enabled
|
INFO : resources transitively enabled
|
||||||
MKDIR : ${jetty.base}/resources
|
MKDIR : ${jetty.base}/resources
|
||||||
COPY : ${jetty.home}/modules/logging-jetty/jetty-logging.properties to ${jetty.base}/resources/jetty-logging.properties
|
COPY : ${jetty.home}/modules/logging-jetty/resources/jetty-logging.properties to ${jetty.base}/resources/jetty-logging.properties
|
||||||
MKDIR : ${jetty.base}/logs
|
|
||||||
INFO : Base directory was modified
|
INFO : Base directory was modified
|
||||||
|
|
||||||
[my-base]$ tree
|
[my-base]$ tree
|
||||||
|
@ -46,26 +64,31 @@ INFO : Base directory was modified
|
||||||
├── resources
|
├── resources
|
||||||
│ └── jetty-logging.properties
|
│ └── jetty-logging.properties
|
||||||
└── start.d
|
└── start.d
|
||||||
|
├── console-capture.ini
|
||||||
└── logging-jetty.ini
|
└── logging-jetty.ini
|
||||||
....
|
....
|
||||||
|
|
||||||
The default configuration for logging output will create a file `${jetty.base}/logs/yyyy_mm_dd.stderrout.log` which allows configuration of the output directory by setting the `jetty.logs` property.
|
Once activated, you can find the properties file at `${jetty.base}/resources/jetty-logging.properties`.
|
||||||
|
By default, the following parameters are defined.
|
||||||
For more advanced logging configurations, please consider use of a separate logging library.
|
To change them, un-comment the line and substitute your naming scheme and configuration choices.
|
||||||
|
|
||||||
The recommended way to configure `StdErrLog` is to create a `${jetty.base}/resources/jetty-logging.properties` file, specify the log implementation to `StdErrLog` and then setup logging levels.
|
|
||||||
|
|
||||||
[source, properties, subs="{sub-order}"]
|
[source, properties, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
# Configure Jetty for StdErrLog Logging
|
## Force jetty logging implementation
|
||||||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StrErrLog
|
#org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||||
# Overall Logging Level is INFO
|
|
||||||
org.eclipse.jetty.LEVEL=INFO
|
## Set logging levels from: ALL, DEBUG, INFO, WARN, OFF
|
||||||
# Detail Logging for WebSocket
|
#org.eclipse.jetty.LEVEL=INFO
|
||||||
org.eclipse.jetty.websocket.LEVEL=DEBUG
|
#com.example.LEVEL=INFO
|
||||||
|
|
||||||
|
## Hide stacks traces in logs?
|
||||||
|
#com.example.STACKS=false
|
||||||
|
|
||||||
|
## Show the source file of a log location?
|
||||||
|
#com.example.SOURCE=false
|
||||||
....
|
....
|
||||||
|
|
||||||
There are a number of properties that can be defined in the configuration that will affect the behavior of `StdErrLog`.
|
There are a number of properties that can be defined in the configuration that will affect the behavior of StdErr logging with `console-capture`.
|
||||||
|
|
||||||
`<name>.LEVEL=<level>`::
|
`<name>.LEVEL=<level>`::
|
||||||
Sets the logging level for all loggers within the `name` specified to the level, which can be (in increasing order of restriction) `ALL`, `DEBUG`, `INFO`, `WARN`, `OFF`.
|
Sets the logging level for all loggers within the `name` specified to the level, which can be (in increasing order of restriction) `ALL`, `DEBUG`, `INFO`, `WARN`, `OFF`.
|
||||||
|
@ -95,22 +118,20 @@ There are a number of properties that can be defined in the configuration that w
|
||||||
+
|
+
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
2014-06-03 14:36:16.013:INFO:oejs.Server:main: jetty-9.2.0.v20140526
|
2016-10-21 15:31:01.248:INFO::main: Logging initialized @332ms to org.eclipse.jetty.util.log.StdErrLog
|
||||||
2014-06-03 14:36:16.028:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:/opt/jetty/demo-base/webapps/] at interval 1
|
2016-10-21 15:31:01.370:INFO:oejs.Server:main: jetty-9.4.0-SNAPSHOT
|
||||||
2014-06-03 14:36:16.051:INFO:oejsh.ContextHandler:main: Started o.e.j.s.h.MovedContextHandler@7d256e50{/oldContextPath,null,AVAILABLE}
|
2016-10-21 15:31:01.400:INFO:oejs.AbstractConnector:main: Started ServerConnector@2c330fbc{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
|
||||||
2014-06-03 14:36:17.880:INFO:oejs.ServerConnector:main: Started ServerConnector@34f2d11a{HTTP/1.1}{0.0.0.0:8080}
|
2016-10-21 15:31:01.400:INFO:oejs.Server:main: Started @485ms
|
||||||
2014-06-03 14:36:17.888:INFO:oejs.Server:main: Started @257ms
|
|
||||||
....
|
....
|
||||||
+
|
+
|
||||||
* Example when set to true:
|
* Example when set to true:
|
||||||
+
|
+
|
||||||
[source, screen, subs="{sub-order}"]
|
[source, screen, subs="{sub-order}"]
|
||||||
....
|
....
|
||||||
2014-06-03 14:38:19.019:INFO:org.eclipse.jetty.server.Server:main: jetty-9.2.0.v20140526
|
2016-10-21 15:31:35.020:INFO::main: Logging initialized @340ms to org.eclipse.jetty.util.log.StdErrLog
|
||||||
2014-06-03 14:38:19.032:INFO:org.eclipse.jetty.deploy.providers.ScanningAppProvider:main: Deployment monitor [file:/opt/jetty/demo-base/webapps/] at interval 1
|
2016-10-21 15:31:35.144:INFO:org.eclipse.jetty.server.Server:main: jetty-9.4.0-SNAPSHOT
|
||||||
2014-06-03 14:38:19.054:INFO:org.eclipse.jetty.server.handler.ContextHandler:main: Started o.e.j.s.h.MovedContextHandler@246d8660{/oldContextPath,null,AVAILABLE}
|
2016-10-21 15:31:35.174:INFO:org.eclipse.jetty.server.AbstractConnector:main: Started ServerConnector@edf4efb{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
|
||||||
2014-06-03 14:38:20.715:INFO:org.eclipse.jetty.server.ServerConnector:main: Started ServerConnector@59f625be{HTTP/1.1}{0.0.0.0:8080}
|
2016-10-21 15:31:35.175:INFO:org.eclipse.jetty.server.Server:main: Started @495ms
|
||||||
2014-06-03 14:38:20.723:INFO:org.eclipse.jetty.server.Server:main: Started @243ms
|
|
||||||
....
|
....
|
||||||
|
|
||||||
[[deprecated-parameters]]
|
[[deprecated-parameters]]
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
[[example-logging-logback-centralized]]
|
[[example-logging-logback-centralized]]
|
||||||
=== Example: Centralized Logging with Logback
|
=== Centralized Logging using Logback
|
||||||
|
|
||||||
The term _Centralized Logging_ refers to a forced logging configuration for the Jetty Server and all web applications that are deployed on the server.
|
The term _Centralized Logging_ refers to a forced logging configuration for the Jetty Server and all web applications that are deployed on the server.
|
||||||
It routes all logging events from the web applications to a single configuration on the Server side.
|
It routes all logging events from the web applications to a single configuration on the Server side.
|
||||||
|
|
|
@ -24,6 +24,7 @@ If you need a session manager that can work in a clustered scenario with multipl
|
||||||
Jetty also offers more niche session managers that leverage backends such as MongoDB, Inifinispan, or even Google's Cloud Data Store.
|
Jetty also offers more niche session managers that leverage backends such as MongoDB, Inifinispan, or even Google's Cloud Data Store.
|
||||||
|
|
||||||
include::session-hierarchy.adoc[]
|
include::session-hierarchy.adoc[]
|
||||||
|
include::sessions-details.adoc[]
|
||||||
include::session-configuration-file-system.adoc[]
|
include::session-configuration-file-system.adoc[]
|
||||||
include::session-configuration-jdbc.adoc[]
|
include::session-configuration-jdbc.adoc[]
|
||||||
include::session-configuration-mongodb.adoc[]
|
include::session-configuration-mongodb.adoc[]
|
||||||
|
|
|
@ -44,7 +44,9 @@ The Google deployment tools will automatically configure the project and authent
|
||||||
|
|
||||||
==== Configuring Indexes for Session Data
|
==== Configuring Indexes for Session Data
|
||||||
|
|
||||||
Regardless of whether you're running inside or outside google infrastructure you will need to upload a file that defines some indexes that are needed by the GCloud datastore session data store.
|
Using some special, composite indexes can speed up session search operations, although it may make write operations slower.
|
||||||
|
By default, indexes will not be used.
|
||||||
|
In order to use them, you will need to manually upload a file that defines the indexes.
|
||||||
This file is named `index.yaml` and you can find it in your distribution in `${jetty.base}/etc/sessions/gcloud/index.yaml`.
|
This file is named `index.yaml` and you can find it in your distribution in `${jetty.base}/etc/sessions/gcloud/index.yaml`.
|
||||||
|
|
||||||
//TODO - Add index.yaml properties? Test with new 9.4.x. It needs uploaded to Google as part of config
|
//TODO - Add index.yaml properties? Test with new 9.4.x. It needs uploaded to Google as part of config
|
||||||
|
|
|
@ -46,7 +46,7 @@ There is only one (1) `SessionDataStore` per `SessionCache`.
|
||||||
// Null cache, memcache, non-sticky load-balancer
|
// Null cache, memcache, non-sticky load-balancer
|
||||||
// in-memory caching
|
// in-memory caching
|
||||||
|
|
||||||
Visually the Session Hierarchy can be represented like this:
|
Visually the session architecture can be represented like this:
|
||||||
|
|
||||||
image::images/SessionsHierarchy.png[]
|
image::images/SessionsHierarchy.png[]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
// ========================================================================
|
||||||
|
// 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.
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
[[sessions-details]]
|
||||||
|
=== Session Configuration and Use Cases
|
||||||
|
|
||||||
|
==== Configuration
|
||||||
|
|
||||||
|
===== SessionIdManager
|
||||||
|
|
||||||
|
There is a maximum of 1 SessionIdManager per jetty Server instance. Its purpose is to generate fresh, unique session ids and to coordinate the re-use of session ids amongst co-operating contexts.
|
||||||
|
|
||||||
|
Unlike in previous versions of jetty, the SessionIdManager is agnostic with respect to the type of clustering technology chosen.
|
||||||
|
|
||||||
|
Jetty provides a default implementation - the DefaultSessionIdManager - which should meet most users' needs.
|
||||||
|
If you do not explicitly enable one of the session modules, or otherwise configure a SessionIdManager, the DefaultSessionIdManager will be used.
|
||||||
|
|
||||||
|
If the DefaultSessionIdManager does not meet your needs, you can extend the org.eclipse.jetty.server.session.AbstractSessionIdManager or do a fresh implementation of the org.eclipse.jetty.server.session.SessionIdManager interface.
|
||||||
|
|
||||||
|
===== HouseKeeper
|
||||||
|
|
||||||
|
There is a maximum of 1 HouseKeeper per SessionIdManager. Its purpose is to periodically poll the SessionHandlers to clean out expired sessions.
|
||||||
|
|
||||||
|
By default the HouseKeeper will poll the SessionHandlers every 10 mins to find and delete expired sessions, although this interval is configurable.
|
||||||
|
|
||||||
|
|
||||||
|
===== SessionCache
|
||||||
|
|
||||||
|
There is 1 SessionCache per context. Its purpose is to provide an L1 cache of Session objects.
|
||||||
|
Having a working set of Session objects in memory allows multiple simultaneous requests for the same session to share the same Session object.
|
||||||
|
|
||||||
|
Jetty provides 2 SessionCache implementations: the DefaultSessionCache and the NullSessionCache.
|
||||||
|
The DefaultSessionCache retains Session objects in memory in a cache and has a number of configuration options to control cache behaviour.
|
||||||
|
It is the default that is used if no other SessionCache has been configured.
|
||||||
|
It is suitable for non-clustered and clustered deployments with a sticky load balancer, as well as clustered deployments with a non-sticky load balancer, with some caveats.
|
||||||
|
The NullSessionCache does not actually cache any objects: each request uses a fresh Session object.
|
||||||
|
It is suitable for clustered deployments without a sticky load balancer and non-clustered deployments when purely minimal support for sessions is needed.
|
||||||
|
|
||||||
|
SessionCaches always write out a Session to the SessionDataStore whenever the last request for the Session exits.
|
||||||
|
|
||||||
|
They can also be configured to do an immediate, eager write of a freshly created session.
|
||||||
|
This can be useful if you are likely to experience multiple, near simultaneous requests referencing the same session, eg with HTTP2 and you don't have a sticky load balancer.
|
||||||
|
Alternatively, if the eager write is not done, application paths which create and then invalidate a session within a single request never incur the cost of writing to persistent storage.
|
||||||
|
|
||||||
|
Additionally, if the EVICT_ON_INACTIVITY eviction policy is in use, you can configure the DefaultSessionCache to force a write of the Session to the SessionDataStore just before the Session is evicted.
|
||||||
|
|
||||||
|
===== SessionDataStore
|
||||||
|
|
||||||
|
There is 1 SessionDataStore per context. Its purpose is to handle all persistance related operations on sessions.
|
||||||
|
|
||||||
|
The common characteristics for all SessionDataStores are whether or not they support passivation, and the length of the grace period.
|
||||||
|
|
||||||
|
Supporting passivation means that session data is serialized.
|
||||||
|
Some persistence mechanisms serialize, such as JDBC, GCloud Datastore etc, whereas others may store an object in shared memory eg Infinispan when configured with a local cache.
|
||||||
|
|
||||||
|
Whether or not a clustering technology entails passivation controls whether or not the session passivation/activation listeners will be called.
|
||||||
|
|
||||||
|
The grace period is an interval, configured in seconds, that attempts to deal with the non-transactional nature of sessions with regard to finding sessions that have expired.
|
||||||
|
Due to the lack of transactionality, in a clustered configuration, even with a sticky load balancer, it is always possible that a Session is live on a node but has not yet been updated in the persistent store.
|
||||||
|
When SessionDataStores search their persistant store to find sessions that have expired, they typically perform a few sequential searches:
|
||||||
|
. the first verifies the expiration of a list of candidate session ids suggested by the SessionCache
|
||||||
|
. the second finds sessions in the store that have expired which were last live on the current node
|
||||||
|
. the third finds sessions that expired a "while" ago, irrespective of on which node they were last used: the definition of "a while" is based on the grace period.
|
||||||
|
|
||||||
|
|
||||||
|
===== CachingSessionDataStore
|
||||||
|
|
||||||
|
The CachingSessionDataStore is a special type of SessionDataStore that inserts an L2 cache of SessionData - the SessionDataMap - in front of a delegate SessionDataStore.
|
||||||
|
The SessionDataMap is preferentially consulted before the actual SessionDataStore on reads.
|
||||||
|
This can improve the performance of slow stores.
|
||||||
|
|
||||||
|
At the time of writing, jetty provides one implementation of the this L2 cache based on Memcached, the MemcachedSessionDataMap.
|
||||||
|
|
||||||
|
|
||||||
|
==== Use Cases
|
||||||
|
|
||||||
|
===== Clustering with a Sticky Load Balancer
|
||||||
|
|
||||||
|
Preferably, your cluster will utilize a sticky load balancer.
|
||||||
|
This will route requests for the same session to the same jetty instance.
|
||||||
|
In this case, the DefaultSessionCache can be used to keep in-use Session objects in memory.
|
||||||
|
You can fine-tune the cache by controlling how long Session objects remain in memory with the eviction policy settings.
|
||||||
|
|
||||||
|
If you have a large number of Sessions or very large Session objects, then you might want to manage your memory allocation by controlling the amount of time Session objects spend in the cache.
|
||||||
|
The EVICT_ON_SESSION_EXIT eviction policy will remove a Session object from the cache as soon as the last simultaneous request referencing it exits.
|
||||||
|
Alternatively, the EVICT_ON_INACTIVITY policy will remove a Session object from the cache after a configurable amount of time has passed without a request referencing it.
|
||||||
|
|
||||||
|
If your Sessions are very long lived and infrequently referenced, you might use the EVICT_ON_INACTIVITY_POLICY to control the size of the cache.
|
||||||
|
|
||||||
|
If your Sessions are small, or relatively few or stable in number or they are read-mostly, then you might select the NEVER_EVICT policy.
|
||||||
|
With this policy, Session objects will remain in the cache until they either expire or are explicitly invalidated.
|
||||||
|
|
||||||
|
If you have a high likelihood of simultaneous requests for the same session object, then the EVICT_ON_SESSION_EXIT policy will ensure the Session object stays in the cache as long as it is needed.
|
||||||
|
|
||||||
|
|
||||||
|
===== Clustering without a Sticky Load Balancer
|
||||||
|
|
||||||
|
Without a sticky load balancer requests for the same session may arrive on any node in the cluster.
|
||||||
|
This means it is likely that the copy of the Session object in any SessionCache is likely to be out-of-date, as the Session was probably last accessed on a different node.
|
||||||
|
In this case, your choices are to use either the NullSessionCache or to de-tuned the DefaultSessionCache.
|
||||||
|
If you use the NullSessionCache all Session object caching is avoided.
|
||||||
|
This means that every time a request references a session it must be brought in from persistent storage.
|
||||||
|
It also means that there can be no sharing of Session objects for multiple requests for the same session: each will have their own Session object.
|
||||||
|
Furthermore, the outcome of session writes are indeterminate because the Servlet Specification does not mandate ACID transactions for sessions.
|
||||||
|
|
||||||
|
If you use the DefaultSessionCache, there is a risk that the caches on some nodes will contain out-of-date session information as simultaneous requests for the same session are scattered over the cluster.
|
||||||
|
To mitigate this somewhat you can use the EVICT_ON_SESSION_EXIT eviction policy: this will ensure that the Session is removed from the cache as soon as the last simultaneous request for it exits.
|
||||||
|
Again, due to the lack of session transactionality, the ordering outcome of write operations cannot be guaranteed.
|
||||||
|
As the Session is cached while at least one request is accessing it, it is possible for multiple simultaneous requests to share the same Session object.
|
||||||
|
|
||||||
|
|
||||||
|
===== Handling corrupted or unloadable session data
|
||||||
|
|
||||||
|
For various reasons it might not be possible for the SessionDataStore to re-read a stored session.
|
||||||
|
One scenario is that the session stores a serialized object in it's attributes, and after a redeployment there in an incompatible class change.
|
||||||
|
Using the setter SessionCache.setRemoveUnloadableSessions(true) will allow the SessionDataStore to delete the unreadable session from persistent storage.
|
||||||
|
This can be useful from preventing the scavenger from continually erroring on the same expired, but unrestorable session.
|
||||||
|
|
|
@ -131,8 +131,8 @@ Jetty attempts to gently close all TCP/IP connections with proper half close sem
|
||||||
[[jetty-connectors-http-configuration]]
|
[[jetty-connectors-http-configuration]]
|
||||||
==== HTTP Configuration
|
==== HTTP Configuration
|
||||||
|
|
||||||
The link:{JDURL}/org/eclipse/jetty/server/HttpConfiguration.html[HttpConfiguration] class holds the configuration for link:{JDURL}/org/eclipse/jetty/server/HttpChannel.html[`HTTPChannel`]s, which you can create 1:1 with each HTTP connection or 1:n on a multiplexed HTTP/2 connection.
|
The link:{JDURL}/org/eclipse/jetty/server/HttpConfiguration.html[`HttpConfiguration`] class holds the configuration for link:{JDURL}/org/eclipse/jetty/server/HttpChannel.html[`HttpChannel`]s, which you can create 1:1 with each HTTP connection or 1:n on a multiplexed HTTP/2 connection.
|
||||||
Thus a `HTTPConfiguration` object is injected into both the HTTP and HTTP/2 connection factories.
|
Thus a `HttpConfiguration` object is injected into both the HTTP and HTTP/2 connection factories.
|
||||||
To avoid duplicate configuration, the standard Jetty distribution creates the common `HttpConfiguration` instance in link:{SRCDIR}/jetty-server/src/main/config/etc/jetty.xml[`jetty.xml`], which is a `Ref` element then used in link:{SRCDIR}/jetty-server/src/main/config/etc/jetty-http.xml[`jetty-http.xml`], link:{SRCDIR}/jetty-server/src/main/config/etc/jetty-https.xml[`jetty-https.xml`] and in link:{SRCDIR}/jetty-http2/http2-server/src/main/config/etc/jetty-http2.xml[`jetty-http2.xml`].
|
To avoid duplicate configuration, the standard Jetty distribution creates the common `HttpConfiguration` instance in link:{SRCDIR}/jetty-server/src/main/config/etc/jetty.xml[`jetty.xml`], which is a `Ref` element then used in link:{SRCDIR}/jetty-server/src/main/config/etc/jetty-http.xml[`jetty-http.xml`], link:{SRCDIR}/jetty-server/src/main/config/etc/jetty-https.xml[`jetty-https.xml`] and in link:{SRCDIR}/jetty-http2/http2-server/src/main/config/etc/jetty-http2.xml[`jetty-http2.xml`].
|
||||||
|
|
||||||
A typical configuration of link:{JDURL}/org/eclipse/jetty/server/HttpConfiguration.html[HttpConfiguration] is:
|
A typical configuration of link:{JDURL}/org/eclipse/jetty/server/HttpConfiguration.html[HttpConfiguration] is:
|
||||||
|
@ -171,6 +171,9 @@ This example HttpConfiguration may be used by reference to the ID "`httpConfig`"
|
||||||
</Call>
|
</Call>
|
||||||
----
|
----
|
||||||
|
|
||||||
|
This same `httpConfig` is referenced by the link:{JDURL}/org/eclipse/jetty/server/handler/SecuredRedirectHandler.html[`SecuredRedirectHandler`] when redirecting secure requests.
|
||||||
|
Please note that if your `httpConfig` does not include a `secureScheme` or `securePort` or there is no `HttpConfiguration` present these types of secured requests will be returned a `403` error.
|
||||||
|
|
||||||
For SSL based connectors (in `jetty-https.xml` and `jetty-http2.xml`), the common "`httpConfig`" instance is used as the basis to create an SSL specific configuration with ID "`sslHttpConfig`":
|
For SSL based connectors (in `jetty-https.xml` and `jetty-http2.xml`), the common "`httpConfig`" instance is used as the basis to create an SSL specific configuration with ID "`sslHttpConfig`":
|
||||||
|
|
||||||
[source, xml, subs="{sub-order}"]
|
[source, xml, subs="{sub-order}"]
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -24,3 +24,4 @@ include::jetty-xml/chapter.adoc[]
|
||||||
include::troubleshooting/chapter.adoc[]
|
include::troubleshooting/chapter.adoc[]
|
||||||
include::debugging/chapter.adoc[]
|
include::debugging/chapter.adoc[]
|
||||||
include::contributing/chapter.adoc[]
|
include::contributing/chapter.adoc[]
|
||||||
|
include::upgrading/chapter.adoc[]
|
||||||
|
|
|
@ -24,14 +24,24 @@ https://wiki.debian.org/LSBInitScripts[LSB tags].
|
||||||
|
|
||||||
You can safely replace Jetty 9.3's `jetty.sh` with 9.4's.
|
You can safely replace Jetty 9.3's `jetty.sh` with 9.4's.
|
||||||
|
|
||||||
==== Modules No Longer Available
|
==== Removed Classes
|
||||||
|
|
||||||
|
`ConcurrentArrayQueue` was removed from use in Jetty 9.3 and the class has been removed entirely as part of Jetty 9.4.
|
||||||
|
|
||||||
|
==== Module Changes in Jetty 9.4
|
||||||
|
|
||||||
[cols="1,1", options="header"]
|
[cols="1,1", options="header"]
|
||||||
|===
|
|===
|
||||||
| 9.3 Module | 9.4 Module
|
| 9.3 Module | 9.4 Module
|
||||||
| logging | console-capture
|
| `logging` | `console-capture`
|
||||||
|
| `infinispan` | `session-store-infinispan-embedded` or `session-store-infinispan-remote`
|
||||||
|
| `jdbc-sessions` | `session-store-jdbc`
|
||||||
|
| `gcloud-memcached-sessions`, `gcloud-session-idmgr` and `gcloud-sessions` | `session-store-gcloud` and `session-store-cache`
|
||||||
|
| `nosql` | `session-store-mongo`
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
===== Logging Modules
|
||||||
|
|
||||||
The module `logging` is no longer available in Jetty 9.4.
|
The module `logging` is no longer available in Jetty 9.4.
|
||||||
|
|
||||||
The logging module structure present in Jetty 9.3 has been replaced with
|
The logging module structure present in Jetty 9.3 has been replaced with
|
||||||
|
@ -45,7 +55,7 @@ If you have a Jetty 9.3 installation, and you have both
|
||||||
`$jetty.base/modules/logging.mod` and `$jetty.base/etc/jetty-logging.xml`,
|
`$jetty.base/modules/logging.mod` and `$jetty.base/etc/jetty-logging.xml`,
|
||||||
then this module is local to your `$jetty.base` setup and will be used
|
then this module is local to your `$jetty.base` setup and will be used
|
||||||
by Jetty 9.4 as before.
|
by Jetty 9.4 as before.
|
||||||
No changes required on your part.
|
No changes are required for your implementation.
|
||||||
|
|
||||||
If either `$jetty.base/modules/logging.mod` or `$jetty.base/etc/jetty-logging.xml`
|
If either `$jetty.base/modules/logging.mod` or `$jetty.base/etc/jetty-logging.xml`
|
||||||
are missing, then you were relying on those present in `$jetty.home`,
|
are missing, then you were relying on those present in `$jetty.home`,
|
||||||
|
@ -55,23 +65,191 @@ The Jetty 9.3 `logging` module has been renamed to `console-capture` in Jetty 9.
|
||||||
You need to open your Jetty 9.3 `start.ini` and replace the references to the
|
You need to open your Jetty 9.3 `start.ini` and replace the references to the
|
||||||
`logging` modules with `console-capture`.
|
`logging` modules with `console-capture`.
|
||||||
|
|
||||||
For example:
|
For example, in an existing 9.3 `start.ini` file the module declaration for logging would look like this:
|
||||||
|
|
||||||
.start.ini
|
[source, screen, subs="{sub-order}"]
|
||||||
----
|
----
|
||||||
--module=logging
|
--module=logging
|
||||||
jetty.logging.retainDays=7
|
jetty.logging.retainDays=7
|
||||||
----
|
----
|
||||||
|
|
||||||
should be replaced by:
|
In 9.4, it should be replaced by:
|
||||||
|
|
||||||
.start.ini
|
[source, screen, subs="{sub-order}"]
|
||||||
----
|
----
|
||||||
--module=console-capture
|
--module=console-capture
|
||||||
jetty.console-capture.retainDays=7
|
jetty.console-capture.retainDays=7
|
||||||
----
|
----
|
||||||
|
|
||||||
The properties that may be present in your Jetty 9.3's `start.ini`, such as
|
The properties that may be present in your Jetty 9.3's `start.ini`, such as `jetty.logging.retainDays` will still be working in Jetty 9.4, but a warning will be printed at Jetty 9.4 startup, saying to replace them with correspondent `jetty.console-capture.*` properties such as `jetty.console-capture.retainDays`.
|
||||||
`jetty.logging.retainDays` will still be working in Jetty 9.4, but a warning
|
|
||||||
will be printed at Jetty 9.4 startup, saying to replace them with correspondent
|
For information on logging modules in the Jetty 9.4 architecture please see the section on link:#configuring-logging-modules[configuring logging modules.]
|
||||||
`jetty.console-capture.*` properties such as `jetty.console-capture.retainDays`.
|
|
||||||
|
===== Session Management
|
||||||
|
|
||||||
|
Session management received a significant overhaul in Jetty 9.4.
|
||||||
|
Session functionality has been refactored to promote code-reuse, easier configuration and easier customization.
|
||||||
|
Whereas previously users needed to edit xml configuration files, in Jetty 9.4 all session behavior is controlled by properties that are exposed by the various session modules.
|
||||||
|
Users now configure session management by selecting a composition of session modules.
|
||||||
|
|
||||||
|
====== Change Overview
|
||||||
|
|
||||||
|
SessionIdManager:: Previously there was a different class of SessionIdManager - with different configuration options - depending upon which type of clustering technology chosen.
|
||||||
|
In Jetty 9.4, there is only one type, the link:{JDURL}/org/eclipse/jetty/server/session/DefaultSessionIdManager.html[org.eclipse.jetty.server.session.DefaultSessionIdManager].
|
||||||
|
|
||||||
|
SessionManager:: Previously, there was a different class of SessionManager depending upon which the type of clustering technology chosen.
|
||||||
|
In Jetty 9.4 we have removed the SessionManager class and split its functionality into different, more easily extensible and composable classes:
|
||||||
|
General setters:::
|
||||||
|
All of the common setup of sessions such as the maxInactiveInterval and session cookie-related configuration has been moved to the link:{JDURL}/org/eclipse/jetty/server/session/SessionHandler.html[org.eclipse.jetty.server.session.SessionHandler]
|
||||||
|
[cols="1,1", options="header"]
|
||||||
|
|===
|
||||||
|
| 9.3 SessionManager | 9.4 SessionHandler
|
||||||
|
| setMaxInactiveInterval(sec) | setMaxInactiveInterval(sec)
|
||||||
|
| setSessionCookie(String) | setSessionCookie(String)
|
||||||
|
| setRefreshCookieAge(sec) | setRefreshCookieAge(sec)
|
||||||
|
| setSecureRequestOnly(boolean) | setSecureRequestOnly(boolean
|
||||||
|
| setSessionIdPathParameterName(String) | setSessionIdPathParameterName(String)
|
||||||
|
| setSessionTrackingModes(Set<SessionTrackingMode>) | setSessionTrackingModes(Set<SessionTrackingMode>)
|
||||||
|
| setHttpOnly(boolean) | setHttpOnly(boolean)
|
||||||
|
| setUsingCookies(boolean) | setUsingCookies(boolean)
|
||||||
|
| setCheckingRemoteSessionIdEncoding(boolean) | setCheckingRemoteSessionIdEncoding(boolean)
|
||||||
|
|===
|
||||||
|
|
||||||
|
Persistence:::
|
||||||
|
In Jetty 9.3 SessionManagers (and sometimes SessionIdManagers) implemented the persistence mechanism.
|
||||||
|
In Jetty 9.4 we have moved this functionality into the link:{JDURL}/org/eclipse/jetty/server/session/SessionDataStore.html[org.eclipse.jetty.server.session.SessionDataStore].
|
||||||
|
|
||||||
|
Session cache:::
|
||||||
|
In Jetty 9.3 the SessionManager held a map of session objects in memory.
|
||||||
|
In Jetty 9.4 this has been moved into the new link:{JDURL}/org/eclipse/jetty/server/session/SessionCache.html[org.eclipse.jetty.server.session.SessionCache] interface.
|
||||||
|
|
||||||
|
|
||||||
|
For more information, please refer to the documentation on link:#jetty-sessions-architecture[Jetty Session Architecture.]
|
||||||
|
|
||||||
|
====== Default
|
||||||
|
|
||||||
|
As with earlier versions of Jetty, if you do not explicitly configure any session modules, the default session infrastructure will be enabled.
|
||||||
|
In previous versions of Jetty this was referred to as "hash" session management.
|
||||||
|
The new default provides similar features to the old hash session management:
|
||||||
|
* a session scavenger thread that runs every 10mins and removes expired sessions
|
||||||
|
* a session id manager that generates unique session ids and handles session id sharing during context forwarding
|
||||||
|
* an in-memory cache of session objects.
|
||||||
|
Requests for the same session in the same context share the same session object.
|
||||||
|
Session objects remain in the cache until they expire or are explicitly invalidated.
|
||||||
|
|
||||||
|
If you wish to configure the default setup further, enable the `session-cache-default` module.
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
|
||||||
|
As Session objects do not persist beyond a server restart, there are no compatibility issues.
|
||||||
|
|
||||||
|
|
||||||
|
====== Filesystem
|
||||||
|
|
||||||
|
In earlier versions of Jetty, persisting sessions to the local filesystem was an option of the "hash" session manager.
|
||||||
|
In Jetty 9.4 this has been refactored to its own configurable module `session-store-file`.
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
|
||||||
|
Sessions stored to files by earlier versions of jetty are not compatible with jetty-9.4 sessions.
|
||||||
|
Here is a comparison of file formats, note that the file contents are listed in order of file output:
|
||||||
|
|
||||||
|
[cols="1,1", options="header"]
|
||||||
|
|===
|
||||||
|
| 9.3 | 9.4
|
||||||
|
| File name: sessionid | File name: expirytime_contextpath_vhost_sessionid
|
||||||
|
| sessionid (utf) | sessionid (utf)
|
||||||
|
| | contextpath (uft)
|
||||||
|
| | vhost (utf)
|
||||||
|
| nodeid (utf) | lastnode (utlf)
|
||||||
|
| createtime (long) | createtime (long)
|
||||||
|
| accessed (long) | accessed (long)
|
||||||
|
| | lastaccessed (long)
|
||||||
|
| | cookiesettime (long)
|
||||||
|
| | expiry (long)
|
||||||
|
| requests (int) |
|
||||||
|
| | maxInactive (long)
|
||||||
|
| attributes size (int) | attributes size (int)
|
||||||
|
| attributes serialized (obj) | attributes serialized (obj)
|
||||||
|
| maxInactive (long) |
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
====== JDBC
|
||||||
|
|
||||||
|
As with earlier versions of Jetty, sessions may be persisted to a relational database.
|
||||||
|
Enable the `session-store-jdbc` module.
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
|
||||||
|
Sessions stored to the database by earlier versions of jetty are not compatible with jetty-9.4 sessions.
|
||||||
|
The incompatibility is minor: in jetty-9.4 the `rowid` primary key column is no longer used, and the primary key is a composite of `(sessionid,contextpath,vhost)` columns.
|
||||||
|
|
||||||
|
====== NoSQL
|
||||||
|
|
||||||
|
As with earlier versions of Jetty, sessions may be persisted to a document database.
|
||||||
|
Jetty supports the Mongo document database.
|
||||||
|
Enable the `session-store-mongo` module.
|
||||||
|
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
|
||||||
|
Sessions stored to mongo by earlier versions of jetty are not compatible with jetty-9.4 sessions.
|
||||||
|
The key for each subdocument that represents the session information for a context is different between jetty-9.3 and 9.4:
|
||||||
|
|
||||||
|
[cols="1,1", options="header"]
|
||||||
|
|===
|
||||||
|
| 9.3 | 9.4
|
||||||
|
|Each context key is: vhost+context+path, where empty vhosts="::" and root context = "*" and / is replaced by _
|
||||||
|
|Each context key is: vhost:contextpath, where empty vhosts="0_0_0_0" and root context = "" and / replaced by _
|
||||||
|
| eg "::/contextA" | eg " 0_0_0_0:_contextA"
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
====== Infinispan
|
||||||
|
|
||||||
|
As with earlier versions of Jetty, sessions may be clustered via Infinispan to either an in-process or remote infinispan instance.
|
||||||
|
Enable the `session-store-infinispan` module.
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
|
||||||
|
Sessions stored in infinispan by jetty-9.3 are incompatible with jetty-9.4.
|
||||||
|
In jetty-9.3 the serialized object stored to represent the session data was `org.eclipse.jetty.session.infinispan.SerializableSessionData`.
|
||||||
|
In jetty-9.4 the serialized object is `org.eclipse.jetty.serer.session.SessionData`.
|
||||||
|
|
||||||
|
====== GCloud Datastore
|
||||||
|
|
||||||
|
As with earlier versions of Jetty, sessions may be persisted to Google's GCloud Datastore.
|
||||||
|
Enable the `session-store-gcloud` module.
|
||||||
|
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
|
||||||
|
Sessions stored into gcloud datastore by jetty-9.3 are incompatible with jetty-9.4, although the incompatibility is trivial: the name of the session id entity property has changed:
|
||||||
|
|
||||||
|
[cols="1,1", options="header"]
|
||||||
|
|===
|
||||||
|
|9.3 | 9.4
|
||||||
|
|Kind: GCloudSession | Kind: GCloudSession
|
||||||
|
|key: contextpath_vhost_sessionid | key: contextpath_vhost_sessionid
|
||||||
|
|*"clusterId": sessionId* | *"id" : sessionId*
|
||||||
|
|"contextPath" : contextpath | "contextPath": contextpath
|
||||||
|
|"vhost" :vhost | "vhost": vhost
|
||||||
|
|"accessed":accesstime | "accessed": accesstime
|
||||||
|
|"lastAccessed": lastaccesstime | "lastAccessed": lastaccesstime
|
||||||
|
|"createTime": createtime | "createTime": createtime
|
||||||
|
|"cookieSetTime": cookiesettime | "cookieSetTime": cookiesettime
|
||||||
|
|"lastNode": lastnode | "lastNode": lastnode
|
||||||
|
|"expiry": expiry | "expiry": expiry
|
||||||
|
|"maxInactive": maxInactive | "maxInactive": maxInactive
|
||||||
|
|"attributes": blob | "attributes": blob
|
||||||
|
|===
|
||||||
|
|
||||||
|
====== GCloud Datastore with Memcached
|
||||||
|
|
||||||
|
As with earlier versions of Jetty, sessions can be both persisted to Google's GCloud Datastore, and cached into Memcached for faster access.
|
||||||
|
Enable the `session-store-gcloud` and `session-store-cache` modules.
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
|
||||||
|
Sessions stored into memcached by earlier versions of jetty are incompatible with jetty-9.4. Previous versions of jetty stored `org.eclipse.jetty.gcloud.memcached.session.SerializableSessionData` whereas jetty-9.4 stores `org.eclipse.jetty.server.session.SessionData`.
|
||||||
|
|
|
@ -30,9 +30,12 @@ import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.server.HttpTransport;
|
import org.eclipse.jetty.server.HttpTransport;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
public class HttpTransportOverFCGI implements HttpTransport
|
public class HttpTransportOverFCGI implements HttpTransport
|
||||||
{
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(HttpTransportOverFCGI.class);
|
||||||
private final ServerGenerator generator;
|
private final ServerGenerator generator;
|
||||||
private final Flusher flusher;
|
private final Flusher flusher;
|
||||||
private final int request;
|
private final int request;
|
||||||
|
@ -97,6 +100,8 @@ public class HttpTransportOverFCGI implements HttpTransport
|
||||||
|
|
||||||
private void commit(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback)
|
private void commit(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback)
|
||||||
{
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("commit {} {} l={}",this,info,lastContent);
|
||||||
boolean shutdown = this.shutdown = info.getFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
|
boolean shutdown = this.shutdown = info.getFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
|
||||||
|
|
||||||
if (head)
|
if (head)
|
||||||
|
@ -137,7 +142,10 @@ public class HttpTransportOverFCGI implements HttpTransport
|
||||||
@Override
|
@Override
|
||||||
public void abort(Throwable failure)
|
public void abort(Throwable failure)
|
||||||
{
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("abort {} {}",this,failure);
|
||||||
aborted = true;
|
aborted = true;
|
||||||
|
flusher.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -608,7 +608,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
{
|
{
|
||||||
client.newRequest("localhost", connector.getLocalPort())
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.timeout(5, TimeUnit.SECONDS)
|
.timeout(60, TimeUnit.SECONDS)
|
||||||
.send();
|
.send();
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,68 +8,50 @@ gcloud
|
||||||
[depends]
|
[depends]
|
||||||
gcloud
|
gcloud
|
||||||
jcl-slf4j
|
jcl-slf4j
|
||||||
jul-slf4j
|
jul-impl
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
maven://com.google.cloud/google-cloud-datastore/0.4.0|lib/gcloud/google-cloud-datastore-0.4.0.jar
|
|
||||||
maven://com.google.cloud/google-cloud-core/0.4.0|lib/gcloud/google-cloud-core-0.4.0.jar
|
|
||||||
maven://com.google.auth/google-auth-library-credentials/0.3.1|lib/gcloud/google-auth-library-credentials-0.3.1.jar
|
|
||||||
maven://com.google.auth/google-auth-library-oauth2-http/0.3.1|lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar
|
|
||||||
maven://com.google.http-client/google-http-client-jackson2/1.19.0|lib/gcloud/google-http-client-jackson2-1.19.0.jar
|
|
||||||
maven://com.fasterxml.jackson.core/jackson-core/2.1.3|lib/gcloud/jackson-core-2.1.3.jar
|
|
||||||
maven://com.google.http-client/google-http-client/1.21.0|lib/gcloud/google-http-client-1.21.0.jar
|
|
||||||
maven://com.google.code.findbugs/jsr305/1.3.9|lib/gcloud/jsr305-1.3.9.jar
|
|
||||||
maven://org.apache.httpcomponents/httpclient/4.0.1|lib/gcloud/httpclient-4.0.1.jar
|
|
||||||
maven://org.apache.httpcomponents/httpcore/4.0.1|lib/gcloud/httpcore-4.0.1.jar
|
|
||||||
maven://commons-codec/commons-codec/1.3|lib/gcloud/commons-codec-1.3.jar
|
|
||||||
maven://com.google.oauth-client/google-oauth-client/1.21.0|lib/gcloud/google-oauth-client-1.21.0.jar
|
|
||||||
maven://com.google.guava/guava/19.0|lib/gcloud/guava-19.0.jar
|
|
||||||
maven://com.google.api-client/google-api-client-appengine/1.21.0|lib/gcloud/google-api-client-appengine-1.21.0.jar
|
|
||||||
maven://com.google.oauth-client/google-oauth-client-appengine/1.21.0|lib/gcloud/google-oauth-client-appengine-1.21.0.jar
|
|
||||||
maven://com.google.oauth-client/google-oauth-client-servlet/1.21.0|lib/gcloud/google-oauth-client-servlet-1.21.0.jar
|
|
||||||
maven://com.google.http-client/google-http-client-jdo/1.21.0|lib/gcloud/google-http-client-jdo-1.21.0.jar
|
|
||||||
maven://com.google.api-client/google-api-client-servlet/1.21.0|lib/gcloud/google-api-client-servlet-1.21.0.jar
|
|
||||||
maven://javax.jdo/jdo2-api/2.3-eb|lib/gcloud/jdo2-api-2.3-eb.jar
|
|
||||||
maven://javax.transaction/transaction-api/1.1|lib/gcloud/transaction-api-1.1.jar
|
|
||||||
maven://com.google.http-client/google-http-client-appengine/1.21.0|lib/gcloud/google-http-client-appengine-1.21.0.jar
|
|
||||||
maven://com.google.http-client/google-http-client-jackson/1.21.0|lib/gcloud/google-http-client-jackson-1.21.0.jar
|
|
||||||
maven://org.codehaus.jackson/jackson-core-asl/1.9.11|lib/gcloud/jackson-core-asl-1.9.11.jar
|
|
||||||
maven://joda-time/joda-time/2.9.2|lib/gcloud/joda-time-2.9.2.jar
|
|
||||||
maven://org.json/json/20151123|lib/gcloud/json-20151123.jar
|
|
||||||
maven://com.google.protobuf/protobuf-java/3.0.0|lib/gcloud/protobuf-java-3.0.0.jar
|
|
||||||
maven://com.google.api/gax/0.0.18|lib/gcloud/gax-0.0.18.jar
|
|
||||||
maven://com.google.auto.value/auto-value/1.1|lib/gcloud/auto-value-1.1.jar
|
|
||||||
maven://io.grpc/grpc-all/1.0.1|lib/gcloud/grpc-all-1.0.1.jar
|
|
||||||
maven://io.grpc/grpc-auth/1.0.1|lib/gcloud/grpc-auth-1.0.1.jar
|
|
||||||
maven://io.grpc/grpc-context/1.0.1|lib/gcloud/grpc-context-1.0.1.jar
|
|
||||||
maven://io.grpc/grpc-protobuf/1.0.1|lib/gcloud/grpc-protobuf-1.0.1.jar
|
|
||||||
maven://com.google.protobuf/protobuf-java-util/3.0.0|lib/gcloud/protobuf-java-util-3.0.0.jar
|
|
||||||
maven://com.google.code.gson/gson/2.3|lib/gcloud/gson-2.3.jar
|
|
||||||
maven://io.grpc/grpc-netty/1.0.1|lib/gcloud/grpc-netty-1.0.1.jar
|
|
||||||
maven://io.netty/netty-codec-http2/4.1.3.Final|lib/gcloud/netty-codec-http2-4.1.3.jar
|
|
||||||
maven://io.netty/netty-codec-http/4.1.3.Final|lib/gcloud/netty-codec-http-4.1.3.Final.jar
|
|
||||||
maven://io.netty/netty-codec/4.1.3.Final|lib/gcloud/netty-codec-4.1.3.Final.jar
|
|
||||||
maven://io.netty/netty-handler/4.1.3.Final|lib/gcloud/netty-handler-4.1.3.Final.jar
|
|
||||||
maven://io.netty/netty-buffer/4.1.3.Final|lib/gcloud/netty-buffer-4.1.3.Final.jar
|
|
||||||
maven://io.netty/netty-common/4.1.3.Final|lib/gcloud/netty-common-4.1.3.Final.jar
|
|
||||||
maven://io.netty/netty-transport/4.1.3.Final|lib/gcloud/netty-transport-4.1.3.Final.jar
|
|
||||||
maven://io.netty/netty-resolver/4.1.3.Final|lib/gcloud/netty-resolver-4.1.3.Final.jar
|
|
||||||
maven://io.grpc/grpc-stub/1.0.1|lib/gcloud/grpc-stub-1.0.1.jar
|
|
||||||
maven://io.grpc/grpc-protobuf-nano/1.0.1|lib/gcloud/grpc-protobuf-nano-1.0.1.jar
|
|
||||||
maven://com.google.protobuf.nano/protobuf-javanano/3.0.0-alpha-5|lib/gcloud/protobuf-javanano/3.0.0-alpha-5.jar
|
|
||||||
maven://io.grpc/grpc-core/1.0.1|lib/gcloud/grpc-core-1.0.1.jar
|
|
||||||
maven://io.grpc/grpc-okhttp/1.0.1|lib/gcloud/grpc-okhttp-1.0.1.jar
|
|
||||||
maven://com.squareup.okio/okio/1.6.0|lib/gcloud/okio-1.6.0.jar
|
|
||||||
maven://com.squareup.okhttp/okhttp/2.5.0|lib/gcloud/okhttp-2.5.0.jar
|
|
||||||
maven://io.grpc/grpc-protobuf-lite/1.0.1|lib/gcloud/grpc-protobuf-lite-1.0.1.jar
|
|
||||||
maven://com.google.protobuf/protobuf-lite/3.0.1|lib/gcloud/protobuf-lite-3.0.1.jar
|
|
||||||
maven://com.google.inject/guice/4.0|lib/gcloud/guice-4.0.jar
|
|
||||||
maven://javax.inject/javax.inject/1|lib/gcloud/javax.inject-1.jar
|
|
||||||
maven://aopalliance/aopalliance/1.0|lib/gcloud/aopalliance-1.0.jar
|
maven://aopalliance/aopalliance/1.0|lib/gcloud/aopalliance-1.0.jar
|
||||||
|
maven://com.fasterxml.jackson.core/jackson-core/2.1.3|lib/gcloud/jackson-core-2.1.3.jar
|
||||||
|
maven://com.google.api-client/google-api-client-appengine/1.21.0|lib/gcloud/google-api-client-appengine-1.21.0.jar
|
||||||
|
maven://com.google.api-client/google-api-client/1.20.0|lib/gcloud/google-api-client-1.20.0.jar
|
||||||
|
maven://com.google.api-client/google-api-client-servlet/1.21.0|lib/gcloud/google-api-client-servlet-1.21.0.jar
|
||||||
|
maven://com.google.api/gax/0.0.21|lib/gcloud/gax-0.0.21.jar
|
||||||
maven://com.google.api.grpc/grpc-google-common-protos/0.1.0|lib/gcloud/grpc-google-common-protos-0.1.0.jar
|
maven://com.google.api.grpc/grpc-google-common-protos/0.1.0|lib/gcloud/grpc-google-common-protos-0.1.0.jar
|
||||||
maven://com.google.api.grpc/grpc-google-iam-v1/0.1.0|lib/gcloud/grpc-google-iam-v1-0.1.0.jar
|
maven://com.google.api.grpc/grpc-google-iam-v1/0.1.0|lib/gcloud/grpc-google-iam-v1-0.1.0.jar
|
||||||
maven://com.google.cloud.datastore/datastore-v1-protos/1.2.0|lib/gcloud/datastore-v1-protos-1.2.0.jar
|
maven://com.google.auth/google-auth-library-credentials/0.3.1|lib/gcloud/google-auth-library-credentials-0.3.1.jar
|
||||||
maven://com.google.cloud.datastore/datastore-v1-proto-client/1.2.0|lib/gcloud/datastore-v1-proto-client-1.2.0.jar
|
maven://com.google.auth/google-auth-library-oauth2-http/0.3.1|lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar
|
||||||
|
maven://com.google.auto.value/auto-value/1.2|lib/gcloud/auto-value-1.2.jar
|
||||||
|
maven://com.google.cloud.datastore/datastore-v1-proto-client/1.3.0|lib/gcloud/datastore-v1-proto-client-1.3.0.jar
|
||||||
|
maven://com.google.cloud.datastore/datastore-v1-protos/1.3.0|lib/gcloud/datastore-v1-protos-1.3.0.jar
|
||||||
|
maven://com.google.cloud/google-cloud-core/0.5.1|lib/gcloud/google-cloud-core-0.5.0.jar
|
||||||
|
maven://com.google.cloud/google-cloud-datastore/0.5.1|lib/gcloud/google-cloud-datastore-0.5.1.jar
|
||||||
|
maven://com.google.code.findbugs/jsr305/1.3.9|lib/gcloud/jsr305-1.3.9.jar
|
||||||
|
maven://com.google.code.gson/gson/2.3|lib/gcloud/gson-2.3.jar
|
||||||
|
maven://com.google.guava/guava/19.0|lib/gcloud/guava-19.0.jar
|
||||||
|
maven://com.google.http-client/google-http-client-appengine/1.21.0|lib/gcloud/google-http-client-appengine-1.21.0.jar
|
||||||
|
maven://com.google.http-client/google-http-client-jackson2/1.19.0|lib/gcloud/google-http-client-jackson2-1.19.0.jar
|
||||||
|
maven://com.google.http-client/google-http-client-jackson/1.21.0|lib/gcloud/google-http-client-jackson-1.21.0.jar
|
||||||
|
maven://com.google.http-client/google-http-client/1.21.0|lib/gcloud/google-http-client-1.21.0.jar
|
||||||
|
maven://com.google.http-client/google-http-client-jdo/1.21.0|lib/gcloud/google-http-client-jdo-1.21.0.jar
|
||||||
maven://com.google.http-client/google-http-client-protobuf/1.20.0|lib/gcloud/google-http-client-protobuf-1.20.0.jar
|
maven://com.google.http-client/google-http-client-protobuf/1.20.0|lib/gcloud/google-http-client-protobuf-1.20.0.jar
|
||||||
maven://com.google.api-client/google-api-client/1.20.0|lib/gcloud/google-api-client/1.20.0
|
maven://com.google.inject/guice/4.0|lib/gcloud/guice-4.0.jar
|
||||||
|
maven://com.google.oauth-client/google-oauth-client-appengine/1.21.0|lib/gcloud/google-oauth-client-appengine-1.21.0.jar
|
||||||
|
maven://com.google.oauth-client/google-oauth-client/1.21.0|lib/gcloud/google-oauth-client-1.21.0.jar
|
||||||
|
maven://com.google.oauth-client/google-oauth-client-servlet/1.21.0|lib/gcloud/google-oauth-client-servlet-1.21.0.jar
|
||||||
|
maven://com.google.protobuf/protobuf-java/3.0.0|lib/gcloud/protobuf-java-3.0.0.jar
|
||||||
|
maven://com.google.protobuf/protobuf-java-util/3.0.0|lib/gcloud/protobuf-java-util-3.0.0.jar
|
||||||
|
maven://commons-codec/commons-codec/1.3|lib/gcloud/commons-codec-1.3.jar
|
||||||
|
maven://io.grpc/grpc-context/1.0.1|lib/gcloud/grpc-context-1.0.1.jar
|
||||||
|
maven://io.grpc/grpc-core/1.0.1|lib/gcloud/grpc-core-1.0.1.jar
|
||||||
|
maven://io.grpc/grpc-protobuf/1.0.1|lib/gcloud/grpc-protobuf-1.0.1.jar
|
||||||
|
maven://io.grpc/grpc-protobuf-lite/1.0.1|lib/gcloud/grpc-protobuf-lite-1.0.1.jar
|
||||||
|
maven://javax.inject/javax.inject/1|lib/gcloud/javax.inject-1.jar
|
||||||
|
maven://javax.jdo/jdo2-api/2.3-eb|lib/gcloud/jdo2-api-2.3-eb.jar
|
||||||
|
maven://javax.transaction/transaction-api/1.1|lib/gcloud/transaction-api-1.1.jar
|
||||||
|
maven://joda-time/joda-time/2.9.2|lib/gcloud/joda-time-2.9.2.jar
|
||||||
|
maven://org.apache.httpcomponents/httpclient/4.0.1|lib/gcloud/httpclient-4.0.1.jar
|
||||||
|
maven://org.apache.httpcomponents/httpcore/4.0.1|lib/gcloud/httpcore-4.0.1.jar
|
||||||
|
maven://org.codehaus.jackson/jackson-core-asl/1.9.11|lib/gcloud/jackson-core-asl-1.9.11.jar
|
||||||
|
maven://org.json/json/20151123|lib/gcloud/json-20151123.jar
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<name>Jetty :: GCloud</name>
|
<name>Jetty :: GCloud</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<gcloud.version>0.4.0</gcloud.version>
|
<gcloud.version>0.5.1</gcloud.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
|
|
@ -101,6 +101,13 @@
|
||||||
</instructions>
|
</instructions>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -18,16 +18,18 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http;
|
package org.eclipse.jetty.http;
|
||||||
|
|
||||||
|
import static org.eclipse.jetty.http.HttpStatus.INTERNAL_SERVER_ERROR_500;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.BufferOverflowException;
|
import java.nio.BufferOverflowException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
|
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
|
||||||
|
import org.eclipse.jetty.util.ArrayTrie;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
import org.eclipse.jetty.util.Trie;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
@ -46,11 +48,10 @@ public class HttpGenerator
|
||||||
public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT");
|
public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT");
|
||||||
|
|
||||||
private final static byte[] __colon_space = new byte[] {':',' '};
|
private final static byte[] __colon_space = new byte[] {':',' '};
|
||||||
private final static HttpHeaderValue[] CLOSE = {HttpHeaderValue.CLOSE};
|
|
||||||
public static final MetaData.Response CONTINUE_100_INFO = new MetaData.Response(HttpVersion.HTTP_1_1,100,null,null,-1);
|
public static final MetaData.Response CONTINUE_100_INFO = new MetaData.Response(HttpVersion.HTTP_1_1,100,null,null,-1);
|
||||||
public static final MetaData.Response PROGRESS_102_INFO = new MetaData.Response(HttpVersion.HTTP_1_1,102,null,null,-1);
|
public static final MetaData.Response PROGRESS_102_INFO = new MetaData.Response(HttpVersion.HTTP_1_1,102,null,null,-1);
|
||||||
public final static MetaData.Response RESPONSE_500_INFO =
|
public final static MetaData.Response RESPONSE_500_INFO =
|
||||||
new MetaData.Response(HttpVersion.HTTP_1_1,HttpStatus.INTERNAL_SERVER_ERROR_500,null,new HttpFields(){{put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);}},0);
|
new MetaData.Response(HttpVersion.HTTP_1_1,INTERNAL_SERVER_ERROR_500,null,new HttpFields(){{put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);}},0);
|
||||||
|
|
||||||
// states
|
// states
|
||||||
public enum State { START, COMMITTED, COMPLETING, COMPLETING_1XX, END }
|
public enum State { START, COMMITTED, COMPLETING, COMPLETING_1XX, END }
|
||||||
|
@ -63,13 +64,18 @@ public class HttpGenerator
|
||||||
private EndOfContent _endOfContent = EndOfContent.UNKNOWN_CONTENT;
|
private EndOfContent _endOfContent = EndOfContent.UNKNOWN_CONTENT;
|
||||||
|
|
||||||
private long _contentPrepared = 0;
|
private long _contentPrepared = 0;
|
||||||
private boolean _noContent = false;
|
private boolean _noContentResponse = false;
|
||||||
private Boolean _persistent = null;
|
private Boolean _persistent = null;
|
||||||
|
|
||||||
private final int _send;
|
private final int _send;
|
||||||
private final static int SEND_SERVER = 0x01;
|
private final static int SEND_SERVER = 0x01;
|
||||||
private final static int SEND_XPOWEREDBY = 0x02;
|
private final static int SEND_XPOWEREDBY = 0x02;
|
||||||
private final static Set<String> __assumedContentMethods = new HashSet<>(Arrays.asList(new String[]{HttpMethod.POST.asString(),HttpMethod.PUT.asString()}));
|
private final static Trie<Boolean> __assumedContentMethods = new ArrayTrie<>(8);
|
||||||
|
static
|
||||||
|
{
|
||||||
|
__assumedContentMethods.put(HttpMethod.POST.asString(),Boolean.TRUE);
|
||||||
|
__assumedContentMethods.put(HttpMethod.PUT.asString(),Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
public static void setJettyVersion(String serverVersion)
|
public static void setJettyVersion(String serverVersion)
|
||||||
|
@ -101,7 +107,7 @@ public class HttpGenerator
|
||||||
{
|
{
|
||||||
_state = State.START;
|
_state = State.START;
|
||||||
_endOfContent = EndOfContent.UNKNOWN_CONTENT;
|
_endOfContent = EndOfContent.UNKNOWN_CONTENT;
|
||||||
_noContent=false;
|
_noContentResponse=false;
|
||||||
_persistent = null;
|
_persistent = null;
|
||||||
_contentPrepared = 0;
|
_contentPrepared = 0;
|
||||||
_needCRLF = false;
|
_needCRLF = false;
|
||||||
|
@ -160,7 +166,7 @@ public class HttpGenerator
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public boolean isNoContent()
|
public boolean isNoContent()
|
||||||
{
|
{
|
||||||
return _noContent;
|
return _noContentResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -214,7 +220,7 @@ public class HttpGenerator
|
||||||
// If we have not been told our persistence, set the default
|
// If we have not been told our persistence, set the default
|
||||||
if (_persistent==null)
|
if (_persistent==null)
|
||||||
{
|
{
|
||||||
_persistent=info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal();
|
_persistent=info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal();
|
||||||
if (!_persistent && HttpMethod.CONNECT.is(info.getMethod()))
|
if (!_persistent && HttpMethod.CONNECT.is(info.getMethod()))
|
||||||
_persistent=true;
|
_persistent=true;
|
||||||
}
|
}
|
||||||
|
@ -226,8 +232,8 @@ public class HttpGenerator
|
||||||
// generate ResponseLine
|
// generate ResponseLine
|
||||||
generateRequestLine(info,header);
|
generateRequestLine(info,header);
|
||||||
|
|
||||||
if (info.getVersion()==HttpVersion.HTTP_0_9)
|
if (info.getHttpVersion()==HttpVersion.HTTP_0_9)
|
||||||
throw new BadMessageException(500,"HTTP/0.9 not supported");
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,"HTTP/0.9 not supported");
|
||||||
|
|
||||||
generateHeaders(info,header,content,last);
|
generateHeaders(info,header,content,last);
|
||||||
|
|
||||||
|
@ -252,10 +258,17 @@ public class HttpGenerator
|
||||||
|
|
||||||
return Result.FLUSH;
|
return Result.FLUSH;
|
||||||
}
|
}
|
||||||
|
catch(BadMessageException e)
|
||||||
|
{
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
catch(BufferOverflowException e)
|
||||||
|
{
|
||||||
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,"Request header too large",e);
|
||||||
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
String message= (e instanceof BufferOverflowException)?"Request header too large":e.getMessage();
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,e.getMessage(),e);
|
||||||
throw new BadMessageException(500,message,e);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -342,9 +355,9 @@ public class HttpGenerator
|
||||||
{
|
{
|
||||||
if (info==null)
|
if (info==null)
|
||||||
return Result.NEED_INFO;
|
return Result.NEED_INFO;
|
||||||
HttpVersion version=info.getVersion();
|
HttpVersion version=info.getHttpVersion();
|
||||||
if (version==null)
|
if (version==null)
|
||||||
throw new BadMessageException(500,"No version");
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,"No version");
|
||||||
switch(version)
|
switch(version)
|
||||||
{
|
{
|
||||||
case HTTP_1_0:
|
case HTTP_1_0:
|
||||||
|
@ -381,7 +394,7 @@ public class HttpGenerator
|
||||||
int status=info.getStatus();
|
int status=info.getStatus();
|
||||||
if (status>=100 && status<200 )
|
if (status>=100 && status<200 )
|
||||||
{
|
{
|
||||||
_noContent=true;
|
_noContentResponse=true;
|
||||||
|
|
||||||
if (status!=HttpStatus.SWITCHING_PROTOCOLS_101 )
|
if (status!=HttpStatus.SWITCHING_PROTOCOLS_101 )
|
||||||
{
|
{
|
||||||
|
@ -392,7 +405,7 @@ public class HttpGenerator
|
||||||
}
|
}
|
||||||
else if (status==HttpStatus.NO_CONTENT_204 || status==HttpStatus.NOT_MODIFIED_304)
|
else if (status==HttpStatus.NO_CONTENT_204 || status==HttpStatus.NOT_MODIFIED_304)
|
||||||
{
|
{
|
||||||
_noContent=true;
|
_noContentResponse=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateHeaders(info,header,content,last);
|
generateHeaders(info,header,content,last);
|
||||||
|
@ -407,10 +420,17 @@ public class HttpGenerator
|
||||||
}
|
}
|
||||||
_state = last?State.COMPLETING:State.COMMITTED;
|
_state = last?State.COMPLETING:State.COMMITTED;
|
||||||
}
|
}
|
||||||
|
catch(BadMessageException e)
|
||||||
|
{
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
catch(BufferOverflowException e)
|
||||||
|
{
|
||||||
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,"Request header too large",e);
|
||||||
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
String message= (e instanceof BufferOverflowException)?"Response header too large":e.getMessage();
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,e.getMessage(),e);
|
||||||
throw new BadMessageException(500,message,e);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -523,7 +543,7 @@ public class HttpGenerator
|
||||||
header.put((byte)' ');
|
header.put((byte)' ');
|
||||||
header.put(StringUtil.getBytes(request.getURIString()));
|
header.put(StringUtil.getBytes(request.getURIString()));
|
||||||
header.put((byte)' ');
|
header.put((byte)' ');
|
||||||
header.put(request.getVersion().toBytes());
|
header.put(request.getHttpVersion().toBytes());
|
||||||
header.put(HttpTokens.CRLF);
|
header.put(HttpTokens.CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,22 +598,29 @@ public class HttpGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
private void generateHeaders(MetaData _info,ByteBuffer header,ByteBuffer content,boolean last)
|
private void generateHeaders(MetaData info,ByteBuffer header,ByteBuffer content,boolean last)
|
||||||
{
|
{
|
||||||
final MetaData.Request request=(_info instanceof MetaData.Request)?(MetaData.Request)_info:null;
|
final MetaData.Request request=(info instanceof MetaData.Request)?(MetaData.Request)info:null;
|
||||||
final MetaData.Response response=(_info instanceof MetaData.Response)?(MetaData.Response)_info:null;
|
final MetaData.Response response=(info instanceof MetaData.Response)?(MetaData.Response)info:null;
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
{
|
||||||
|
LOG.debug("generateHeaders {} last={} content={}",info,last,BufferUtil.toDetailString(content));
|
||||||
|
LOG.debug(info.getFields().toString());
|
||||||
|
}
|
||||||
|
|
||||||
// default field values
|
// default field values
|
||||||
int send=_send;
|
int send=_send;
|
||||||
HttpField transfer_encoding=null;
|
HttpField transfer_encoding=null;
|
||||||
boolean keep_alive=false;
|
boolean http11 = info.getHttpVersion() == HttpVersion.HTTP_1_1;
|
||||||
boolean close=false;
|
boolean close = false;
|
||||||
boolean content_type=false;
|
boolean chunked = false;
|
||||||
StringBuilder connection = null;
|
boolean content_type = false;
|
||||||
long content_length = _info.getContentLength();
|
long content_length = info.getContentLength();
|
||||||
|
boolean content_length_field = false;
|
||||||
|
|
||||||
// Generate fields
|
// Generate fields
|
||||||
HttpFields fields = _info.getFields();
|
HttpFields fields = info.getFields();
|
||||||
if (fields != null)
|
if (fields != null)
|
||||||
{
|
{
|
||||||
int n=fields.size();
|
int n=fields.size();
|
||||||
|
@ -612,10 +639,11 @@ public class HttpGenerator
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
case CONTENT_LENGTH:
|
case CONTENT_LENGTH:
|
||||||
_endOfContent=EndOfContent.CONTENT_LENGTH;
|
|
||||||
if (content_length<0)
|
if (content_length<0)
|
||||||
content_length=Long.valueOf(field.getValue());
|
content_length = field.getLongValue();
|
||||||
// handle setting the field specially below
|
else if (content_length!=field.getLongValue())
|
||||||
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,String.format("Incorrect Content-Length %d!=%d",content_length,field.getLongValue()));
|
||||||
|
content_length_field = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONTENT_TYPE:
|
case CONTENT_TYPE:
|
||||||
|
@ -628,81 +656,29 @@ public class HttpGenerator
|
||||||
|
|
||||||
case TRANSFER_ENCODING:
|
case TRANSFER_ENCODING:
|
||||||
{
|
{
|
||||||
if (_info.getVersion() == HttpVersion.HTTP_1_1)
|
if (http11)
|
||||||
|
{
|
||||||
|
// Don't add yet, treat this only as a hint that there is content
|
||||||
|
// with a preference to chunk if we can
|
||||||
transfer_encoding = field;
|
transfer_encoding = field;
|
||||||
// Do NOT add yet!
|
chunked = field.contains(HttpHeaderValue.CHUNKED.asString());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CONNECTION:
|
case CONNECTION:
|
||||||
{
|
{
|
||||||
if (request!=null)
|
putTo(field,header);
|
||||||
putTo(field,header);
|
if (field.contains(HttpHeaderValue.CLOSE.asString()))
|
||||||
|
|
||||||
// Lookup and/or split connection value field
|
|
||||||
HttpHeaderValue[] values = HttpHeaderValue.CLOSE.is(field.getValue())?CLOSE:new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
|
|
||||||
String[] split = null;
|
|
||||||
|
|
||||||
if (values[0]==null)
|
|
||||||
{
|
{
|
||||||
split = StringUtil.csvSplit(field.getValue());
|
close=true;
|
||||||
if (split.length>0)
|
_persistent=false;
|
||||||
{
|
|
||||||
values=new HttpHeaderValue[split.length];
|
|
||||||
for (int i=0;i<split.length;i++)
|
|
||||||
values[i]=HttpHeaderValue.CACHE.get(split[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle connection values
|
if (!http11 && field.contains(HttpHeaderValue.KEEP_ALIVE.asString()))
|
||||||
for (int i=0;i<values.length;i++)
|
|
||||||
{
|
{
|
||||||
HttpHeaderValue value=values[i];
|
_persistent=true;
|
||||||
switch (value==null?HttpHeaderValue.UNKNOWN:value)
|
|
||||||
{
|
|
||||||
case UPGRADE:
|
|
||||||
{
|
|
||||||
// special case for websocket connection ordering
|
|
||||||
header.put(HttpHeader.CONNECTION.getBytesColonSpace()).put(HttpHeader.UPGRADE.getBytes());
|
|
||||||
header.put(CRLF);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CLOSE:
|
|
||||||
{
|
|
||||||
close=true;
|
|
||||||
_persistent=false;
|
|
||||||
if (response!=null)
|
|
||||||
{
|
|
||||||
if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
|
|
||||||
_endOfContent=EndOfContent.EOF_CONTENT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case KEEP_ALIVE:
|
|
||||||
{
|
|
||||||
if (_info.getVersion() == HttpVersion.HTTP_1_0)
|
|
||||||
{
|
|
||||||
keep_alive = true;
|
|
||||||
if (response!=null)
|
|
||||||
_persistent=true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
if (connection==null)
|
|
||||||
connection=new StringBuilder();
|
|
||||||
else
|
|
||||||
connection.append(',');
|
|
||||||
connection.append(split==null?field.getValue():split[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do NOT add yet!
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,143 +696,91 @@ public class HttpGenerator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Can we work out the content length?
|
||||||
|
if (last && content_length<0)
|
||||||
|
content_length = _contentPrepared+BufferUtil.length(content);
|
||||||
|
|
||||||
// Calculate how to end _content and connection, _content length and transfer encoding
|
// Calculate how to end _content and connection, _content length and transfer encoding
|
||||||
// settings.
|
// settings from http://tools.ietf.org/html/rfc7230#section-3.3.3
|
||||||
// From http://tools.ietf.org/html/rfc7230#section-3.3.3
|
|
||||||
// From RFC 2616 4.4:
|
|
||||||
// 1. No body for 1xx, 204, 304 & HEAD response
|
|
||||||
// 3. If Transfer-Encoding==(.*,)?chunked && HTTP/1.1 && !HttpConnection==close then chunk
|
|
||||||
// 5. Content-Length without Transfer-Encoding
|
|
||||||
// 6. Request and none over the above, then Content-Length=0 if POST/PUT
|
|
||||||
// 7. close
|
|
||||||
|
|
||||||
|
boolean assumed_content_request = request!=null && Boolean.TRUE.equals(__assumedContentMethods.get(request.getMethod()));
|
||||||
|
boolean assumed_content = assumed_content_request || content_type || chunked;
|
||||||
|
boolean nocontent_request = request!=null && content_length<=0 && !assumed_content;
|
||||||
|
|
||||||
int status=response!=null?response.getStatus():-1;
|
// If the message is known not to have content
|
||||||
switch (_endOfContent)
|
if (_noContentResponse || nocontent_request)
|
||||||
{
|
{
|
||||||
case UNKNOWN_CONTENT:
|
// We don't need to indicate a body length
|
||||||
// It may be that we have no _content, or perhaps _content just has not been
|
_endOfContent=EndOfContent.NO_CONTENT;
|
||||||
// written yet?
|
|
||||||
|
|
||||||
// Response known not to have a body
|
// But it is an error if there actually is content
|
||||||
if (_contentPrepared == 0 && response!=null && _noContent)
|
if (_contentPrepared>0 || content_length>0)
|
||||||
_endOfContent=EndOfContent.NO_CONTENT;
|
{
|
||||||
else if (_info.getContentLength()>0)
|
if (_contentPrepared==0 && last)
|
||||||
{
|
{
|
||||||
// we have been given a content length
|
// TODO discard content for backward compatibility with 9.3 releases
|
||||||
_endOfContent=EndOfContent.CONTENT_LENGTH;
|
// TODO review if it is still needed in 9.4 or can we just throw.
|
||||||
if ((response!=null || content_length>0 || content_type ) && !_noContent)
|
content.clear();
|
||||||
{
|
content_length=0;
|
||||||
// known length but not actually set.
|
|
||||||
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
|
|
||||||
BufferUtil.putDecLong(header, content_length);
|
|
||||||
header.put(HttpTokens.CRLF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (last)
|
|
||||||
{
|
|
||||||
// we have seen all the _content there is, so we can be content-length limited.
|
|
||||||
_endOfContent=EndOfContent.CONTENT_LENGTH;
|
|
||||||
long actual_length = _contentPrepared+BufferUtil.length(content);
|
|
||||||
|
|
||||||
if (content_length>=0 && content_length!=actual_length)
|
|
||||||
throw new BadMessageException(500,"Content-Length header("+content_length+") != actual("+actual_length+")");
|
|
||||||
|
|
||||||
// Do we need to tell the headers about it
|
|
||||||
putContentLength(header,actual_length,content_type,request,response);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,"Content for no content response");
|
||||||
// No idea, so we must assume that a body is coming.
|
|
||||||
_endOfContent = EndOfContent.CHUNKED_CONTENT;
|
|
||||||
// HTTP 1.0 does not understand chunked content, so we must use EOF content.
|
|
||||||
// For a request with HTTP 1.0 & Connection: keep-alive
|
|
||||||
// we *must* close the connection, otherwise the client
|
|
||||||
// has no way to detect the end of the content.
|
|
||||||
if (!isPersistent() || _info.getVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal())
|
|
||||||
_endOfContent = EndOfContent.EOF_CONTENT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONTENT_LENGTH:
|
|
||||||
{
|
|
||||||
putContentLength(header,content_length,content_type,request,response);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case NO_CONTENT:
|
|
||||||
throw new BadMessageException(500);
|
|
||||||
|
|
||||||
case EOF_CONTENT:
|
|
||||||
_persistent = request!=null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CHUNKED_CONTENT:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
// Else if we are HTTP/1.1 and the content length is unknown and we are either persistent
|
||||||
// Add transfer_encoding if needed
|
// or it is a request with content (which cannot EOF)
|
||||||
if (isChunking())
|
else if (http11 && content_length<0 && (_persistent || assumed_content_request))
|
||||||
{
|
{
|
||||||
|
// we use chunking
|
||||||
|
_endOfContent = EndOfContent.CHUNKED_CONTENT;
|
||||||
|
chunked = true;
|
||||||
|
|
||||||
// try to use user supplied encoding as it may have other values.
|
// try to use user supplied encoding as it may have other values.
|
||||||
if (transfer_encoding != null && !HttpHeaderValue.CHUNKED.toString().equalsIgnoreCase(transfer_encoding.getValue()))
|
if (transfer_encoding == null)
|
||||||
|
header.put(TRANSFER_ENCODING_CHUNKED);
|
||||||
|
else if (transfer_encoding.toString().endsWith(HttpHeaderValue.CHUNKED.toString()))
|
||||||
{
|
{
|
||||||
String c = transfer_encoding.getValue();
|
putTo(transfer_encoding,header);
|
||||||
if (c.endsWith(HttpHeaderValue.CHUNKED.toString()))
|
transfer_encoding = null;
|
||||||
putTo(transfer_encoding,header);
|
|
||||||
else
|
|
||||||
throw new BadMessageException(500,"BAD TE");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
header.put(TRANSFER_ENCODING_CHUNKED);
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,"Bad Transfer-Encoding");
|
||||||
}
|
}
|
||||||
|
// Else if we known the content length and are a request or a persistent response,
|
||||||
// Handle connection if need be
|
else if (content_length>=0 && (request!=null || _persistent))
|
||||||
if (_endOfContent==EndOfContent.EOF_CONTENT)
|
|
||||||
{
|
{
|
||||||
keep_alive=false;
|
// Use the content length
|
||||||
|
_endOfContent = EndOfContent.CONTENT_LENGTH;
|
||||||
|
putContentLength(header,content_length);
|
||||||
|
}
|
||||||
|
// Else if we are a response
|
||||||
|
else if (response!=null)
|
||||||
|
{
|
||||||
|
// We must use EOF - even if we were trying to be persistent
|
||||||
|
_endOfContent = EndOfContent.EOF_CONTENT;
|
||||||
_persistent=false;
|
_persistent=false;
|
||||||
}
|
if (content_length>=0 && ( content_length> 0 || assumed_content || content_length_field ))
|
||||||
|
putContentLength(header,content_length);
|
||||||
|
|
||||||
// If this is a response, work out persistence
|
if (http11 && !close)
|
||||||
if (response!=null)
|
header.put(CONNECTION_CLOSE);
|
||||||
|
}
|
||||||
|
// Else we must be a request
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (!isPersistent() && (close || _info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal()))
|
// with no way to indicate body length
|
||||||
{
|
throw new BadMessageException(INTERNAL_SERVER_ERROR_500,"Unknown content length for request");
|
||||||
if (connection==null)
|
|
||||||
header.put(CONNECTION_CLOSE);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
header.put(CONNECTION_CLOSE,0,CONNECTION_CLOSE.length-2);
|
|
||||||
header.put((byte)',');
|
|
||||||
header.put(StringUtil.getBytes(connection.toString()));
|
|
||||||
header.put(CRLF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (keep_alive)
|
|
||||||
{
|
|
||||||
if (connection==null)
|
|
||||||
header.put(CONNECTION_KEEP_ALIVE);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_KEEP_ALIVE.length-2);
|
|
||||||
header.put((byte)',');
|
|
||||||
header.put(StringUtil.getBytes(connection.toString()));
|
|
||||||
header.put(CRLF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (connection!=null)
|
|
||||||
{
|
|
||||||
header.put(HttpHeader.CONNECTION.getBytesColonSpace());
|
|
||||||
header.put(StringUtil.getBytes(connection.toString()));
|
|
||||||
header.put(CRLF);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug(_endOfContent.toString());
|
||||||
|
|
||||||
|
// Add transfer encoding if it is not chunking
|
||||||
|
if (transfer_encoding!=null && !chunked)
|
||||||
|
putTo(transfer_encoding,header);
|
||||||
|
|
||||||
|
// Send server?
|
||||||
|
int status=response!=null?response.getStatus():-1;
|
||||||
if (status>199)
|
if (status>199)
|
||||||
header.put(SEND[send]);
|
header.put(SEND[send]);
|
||||||
|
|
||||||
|
@ -865,19 +789,16 @@ public class HttpGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
private void putContentLength(ByteBuffer header, long contentLength, boolean contentType, MetaData.Request request, MetaData.Response response)
|
private static void putContentLength(ByteBuffer header,long contentLength)
|
||||||
{
|
{
|
||||||
if (contentLength>0)
|
if (contentLength==0)
|
||||||
|
header.put(CONTENT_LENGTH_0);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
|
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
|
||||||
BufferUtil.putDecLong(header, contentLength);
|
BufferUtil.putDecLong(header, contentLength);
|
||||||
header.put(HttpTokens.CRLF);
|
header.put(HttpTokens.CRLF);
|
||||||
}
|
}
|
||||||
else if (!_noContent)
|
|
||||||
{
|
|
||||||
if (contentType || response!=null || (request!=null && __assumedContentMethods.contains(request.getMethod())))
|
|
||||||
header.put(CONTENT_LENGTH_0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
|
@ -905,10 +826,8 @@ public class HttpGenerator
|
||||||
// common _content
|
// common _content
|
||||||
private static final byte[] LAST_CHUNK = { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
|
private static final byte[] LAST_CHUNK = { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
|
||||||
private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
|
private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
|
||||||
private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012");
|
|
||||||
private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
|
private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
|
||||||
private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
|
private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
|
||||||
private static final byte[] CRLF = StringUtil.getBytes("\015\012");
|
|
||||||
private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
|
private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
|
||||||
private static final byte[][] SEND = new byte[][]{
|
private static final byte[][] SEND = new byte[][]{
|
||||||
new byte[0],
|
new byte[0],
|
||||||
|
|
|
@ -57,10 +57,19 @@ public class MetaData implements Iterable<HttpField>
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #getHttpVersion()} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public HttpVersion getVersion()
|
||||||
|
{
|
||||||
|
return getHttpVersion();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the HTTP version of this MetaData object
|
* @return the HTTP version of this MetaData object
|
||||||
*/
|
*/
|
||||||
public HttpVersion getVersion()
|
public HttpVersion getHttpVersion()
|
||||||
{
|
{
|
||||||
return _httpVersion;
|
return _httpVersion;
|
||||||
}
|
}
|
||||||
|
@ -155,7 +164,7 @@ public class MetaData implements Iterable<HttpField>
|
||||||
|
|
||||||
public Request(Request request)
|
public Request(Request request)
|
||||||
{
|
{
|
||||||
this(request.getMethod(),new HttpURI(request.getURI()), request.getVersion(), new HttpFields(request.getFields()), request.getContentLength());
|
this(request.getMethod(),new HttpURI(request.getURI()), request.getHttpVersion(), new HttpFields(request.getFields()), request.getContentLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recycle()
|
public void recycle()
|
||||||
|
@ -216,8 +225,8 @@ public class MetaData implements Iterable<HttpField>
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
HttpFields fields = getFields();
|
HttpFields fields = getFields();
|
||||||
return String.format("%s{u=%s,%s,h=%d}",
|
return String.format("%s{u=%s,%s,h=%d,cl=%d}",
|
||||||
getMethod(), getURI(), getVersion(), fields == null ? -1 : fields.size());
|
getMethod(), getURI(), getHttpVersion(), fields == null ? -1 : fields.size(), getContentLength());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +300,7 @@ public class MetaData implements Iterable<HttpField>
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
HttpFields fields = getFields();
|
HttpFields fields = getFields();
|
||||||
return String.format("%s{s=%d,h=%d}", getVersion(), getStatus(), fields == null ? -1 : fields.size());
|
return String.format("%s{s=%d,h=%d,cl=%d}", getHttpVersion(), getStatus(), fields == null ? -1 : fields.size(), getContentLength());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ package org.eclipse.jetty.http;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -30,14 +29,11 @@ import java.util.HashSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.MissingResourceException;
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTrie;
|
import org.eclipse.jetty.util.ArrayTrie;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Loader;
|
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
import org.eclipse.jetty.util.Trie;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
@ -400,6 +396,12 @@ public class MimeTypes
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(';'==b && state<=8)
|
||||||
|
{
|
||||||
|
state = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -408,8 +410,6 @@ public class MimeTypes
|
||||||
quote=true;
|
quote=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (';'==b)
|
|
||||||
state=1;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break;
|
case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break;
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.http;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
|
@ -51,30 +52,56 @@ public class PreEncodedHttpField extends HttpField
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
encoders.add(iter.next());
|
HttpFieldPreEncoder encoder = iter.next();
|
||||||
|
if (index(encoder.getHttpVersion())>=0)
|
||||||
|
encoders.add(encoder);
|
||||||
}
|
}
|
||||||
catch(Error|RuntimeException e)
|
catch(Error|RuntimeException e)
|
||||||
{
|
{
|
||||||
LOG.debug(e);
|
LOG.debug(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO avoid needing this catch all
|
|
||||||
if (encoders.size()==0)
|
|
||||||
encoders.add(new Http1FieldPreEncoder());
|
|
||||||
LOG.debug("HttpField encoders loaded: {}",encoders);
|
LOG.debug("HttpField encoders loaded: {}",encoders);
|
||||||
__encoders = encoders.toArray(new HttpFieldPreEncoder[encoders.size()]);
|
int size=encoders.size();
|
||||||
|
|
||||||
|
__encoders = new HttpFieldPreEncoder[size==0?1:size];
|
||||||
|
for (HttpFieldPreEncoder e:encoders)
|
||||||
|
{
|
||||||
|
int i = index(e.getHttpVersion());
|
||||||
|
if (__encoders[i]==null)
|
||||||
|
__encoders[i] = e;
|
||||||
|
else
|
||||||
|
LOG.warn("multiple PreEncoders for "+e.getHttpVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always support HTTP1
|
||||||
|
if (__encoders[0]==null)
|
||||||
|
__encoders[0] = new Http1FieldPreEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final byte[][] _encodedField=new byte[2][];
|
private static int index(HttpVersion version)
|
||||||
|
{
|
||||||
|
switch (version)
|
||||||
|
{
|
||||||
|
case HTTP_1_0:
|
||||||
|
case HTTP_1_1:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case HTTP_2:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final byte[][] _encodedField=new byte[__encoders.length][];
|
||||||
|
|
||||||
public PreEncodedHttpField(HttpHeader header,String name,String value)
|
public PreEncodedHttpField(HttpHeader header,String name,String value)
|
||||||
{
|
{
|
||||||
super(header,name, value);
|
super(header,name, value);
|
||||||
|
for (int i=0;i<__encoders.length;i++)
|
||||||
for (HttpFieldPreEncoder e:__encoders)
|
_encodedField[i]=__encoders[i].getEncodedField(header,header.asString(),value);
|
||||||
{
|
|
||||||
_encodedField[e.getHttpVersion()==HttpVersion.HTTP_2?1:0]=e.getEncodedField(header,header.asString(),value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PreEncodedHttpField(HttpHeader header,String value)
|
public PreEncodedHttpField(HttpHeader header,String value)
|
||||||
|
@ -89,6 +116,6 @@ public class PreEncodedHttpField extends HttpField
|
||||||
|
|
||||||
public void putTo(ByteBuffer bufferInFillMode, HttpVersion version)
|
public void putTo(ByteBuffer bufferInFillMode, HttpVersion version)
|
||||||
{
|
{
|
||||||
bufferInFillMode.put(_encodedField[version==HttpVersion.HTTP_2?1:0]);
|
bufferInFillMode.put(_encodedField[index(version)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,10 +18,26 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http.pathmap;
|
package org.eclipse.jetty.http.pathmap;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
import org.eclipse.jetty.util.URIUtil;
|
||||||
|
|
||||||
public class ServletPathSpec extends PathSpec
|
public class ServletPathSpec extends PathSpec
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* If a servlet or filter path mapping isn't a suffix mapping, ensure
|
||||||
|
* it starts with '/'
|
||||||
|
*
|
||||||
|
* @param pathSpec the servlet or filter mapping pattern
|
||||||
|
* @return the pathSpec prefixed by '/' if appropriate
|
||||||
|
*/
|
||||||
|
public static String normalize(String pathSpec)
|
||||||
|
{
|
||||||
|
if (StringUtil.isNotBlank(pathSpec) && !pathSpec.startsWith("/") && !pathSpec.startsWith("*"))
|
||||||
|
return "/" + pathSpec;
|
||||||
|
return pathSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public ServletPathSpec(String servletPathSpec)
|
public ServletPathSpec(String servletPathSpec)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.eclipse.jetty.http;
|
package org.eclipse.jetty.http;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -27,6 +28,7 @@ import static org.junit.Assert.assertThat;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -229,7 +231,7 @@ public class HttpGeneratorServerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResponseNoContent() throws Exception
|
public void testResponseIncorrectContentLength() throws Exception
|
||||||
{
|
{
|
||||||
ByteBuffer header = BufferUtil.allocate(8096);
|
ByteBuffer header = BufferUtil.allocate(8096);
|
||||||
|
|
||||||
|
@ -239,7 +241,36 @@ public class HttpGeneratorServerTest
|
||||||
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
||||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
|
||||||
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
|
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 10);
|
||||||
|
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
|
||||||
|
info.getFields().add("Content-Length", "11");
|
||||||
|
|
||||||
|
result = gen.generateResponse(info, null, null, null, true);
|
||||||
|
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gen.generateResponse(info, header, null, null, true);
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
catch(BadMessageException e)
|
||||||
|
{
|
||||||
|
assertEquals(e._code,500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResponseNoContentPersistent() throws Exception
|
||||||
|
{
|
||||||
|
ByteBuffer header = BufferUtil.allocate(8096);
|
||||||
|
|
||||||
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
|
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
|
||||||
|
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
||||||
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
|
||||||
|
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 0);
|
||||||
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
|
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
|
||||||
|
|
||||||
result = gen.generateResponse(info, null, null, null, true);
|
result = gen.generateResponse(info, null, null, null, true);
|
||||||
|
@ -261,6 +292,40 @@ public class HttpGeneratorServerTest
|
||||||
assertThat(head, containsString("Content-Length: 0"));
|
assertThat(head, containsString("Content-Length: 0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResponseKnownNoContentNotPersistent() throws Exception
|
||||||
|
{
|
||||||
|
ByteBuffer header = BufferUtil.allocate(8096);
|
||||||
|
|
||||||
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
|
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
|
||||||
|
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
||||||
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
|
||||||
|
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 0);
|
||||||
|
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
|
||||||
|
info.getFields().add("Connection", "close");
|
||||||
|
|
||||||
|
result = gen.generateResponse(info, null, null, null, true);
|
||||||
|
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||||
|
|
||||||
|
result = gen.generateResponse(info, header, null, null, true);
|
||||||
|
assertEquals(HttpGenerator.Result.FLUSH, result);
|
||||||
|
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
|
||||||
|
String head = BufferUtil.toString(header);
|
||||||
|
BufferUtil.clear(header);
|
||||||
|
|
||||||
|
result = gen.generateResponse(null, null, null, null, false);
|
||||||
|
assertEquals(HttpGenerator.Result.SHUTDOWN_OUT, result);
|
||||||
|
assertEquals(HttpGenerator.State.END, gen.getState());
|
||||||
|
|
||||||
|
assertEquals(0, gen.getContentPrepared());
|
||||||
|
assertThat(head, containsString("HTTP/1.1 200 OK"));
|
||||||
|
assertThat(head, containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
|
||||||
|
assertThat(head, containsString("Connection: close"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResponseUpgrade() throws Exception
|
public void testResponseUpgrade() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -344,19 +409,23 @@ public class HttpGeneratorServerTest
|
||||||
assertEquals(HttpGenerator.Result.DONE, result);
|
assertEquals(HttpGenerator.Result.DONE, result);
|
||||||
assertEquals(HttpGenerator.State.END, gen.getState());
|
assertEquals(HttpGenerator.State.END, gen.getState());
|
||||||
|
|
||||||
|
|
||||||
assertThat(out, containsString("HTTP/1.1 200 OK"));
|
assertThat(out, containsString("HTTP/1.1 200 OK"));
|
||||||
assertThat(out, containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
|
assertThat(out, containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
|
||||||
assertThat(out, not(containsString("Content-Length")));
|
assertThat(out, not(containsString("Content-Length")));
|
||||||
assertThat(out, containsString("Transfer-Encoding: chunked"));
|
assertThat(out, containsString("Transfer-Encoding: chunked"));
|
||||||
assertThat(out, containsString("\r\n\r\nD\r\n"));
|
|
||||||
assertThat(out, containsString("\r\nHello World! \r\n"));
|
assertThat(out, endsWith(
|
||||||
assertThat(out, containsString("\r\n2E\r\n"));
|
"\r\n\r\nD\r\n"+
|
||||||
assertThat(out, containsString("\r\nThe quick brown fox jumped over the lazy dog. \r\n"));
|
"Hello World! \r\n"+
|
||||||
assertThat(out, containsString("\r\n0\r\n"));
|
"2E\r\n"+
|
||||||
|
"The quick brown fox jumped over the lazy dog. \r\n"+
|
||||||
|
"0\r\n"+
|
||||||
|
"\r\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResponseWithKnownContent() throws Exception
|
public void testResponseWithKnownContentLengthFromMetaData() throws Exception
|
||||||
{
|
{
|
||||||
ByteBuffer header = BufferUtil.allocate(4096);
|
ByteBuffer header = BufferUtil.allocate(4096);
|
||||||
ByteBuffer content0 = BufferUtil.toBuffer("Hello World! ");
|
ByteBuffer content0 = BufferUtil.toBuffer("Hello World! ");
|
||||||
|
@ -403,6 +472,58 @@ public class HttpGeneratorServerTest
|
||||||
assertThat(out, containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. "));
|
assertThat(out, containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResponseWithKnownContentLengthFromHeader() throws Exception
|
||||||
|
{
|
||||||
|
ByteBuffer header = BufferUtil.allocate(4096);
|
||||||
|
ByteBuffer content0 = BufferUtil.toBuffer("Hello World! ");
|
||||||
|
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
|
||||||
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
|
HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
|
||||||
|
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
||||||
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
|
||||||
|
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
|
||||||
|
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
|
||||||
|
info.getFields().add("Content-Length",""+(content0.remaining()+content1.remaining()));
|
||||||
|
result = gen.generateResponse(info, null, null, content0, false);
|
||||||
|
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||||
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
|
||||||
|
result = gen.generateResponse(info, header, null, content0, false);
|
||||||
|
assertEquals(HttpGenerator.Result.FLUSH, result);
|
||||||
|
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
|
||||||
|
|
||||||
|
String out = BufferUtil.toString(header);
|
||||||
|
BufferUtil.clear(header);
|
||||||
|
out += BufferUtil.toString(content0);
|
||||||
|
BufferUtil.clear(content0);
|
||||||
|
|
||||||
|
result = gen.generateResponse(null, null, null, content1, false);
|
||||||
|
assertEquals(HttpGenerator.Result.FLUSH, result);
|
||||||
|
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
|
||||||
|
out += BufferUtil.toString(content1);
|
||||||
|
BufferUtil.clear(content1);
|
||||||
|
|
||||||
|
result = gen.generateResponse(null, null, null, null, true);
|
||||||
|
assertEquals(HttpGenerator.Result.CONTINUE, result);
|
||||||
|
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
|
||||||
|
|
||||||
|
result = gen.generateResponse(null, null, null, null, true);
|
||||||
|
assertEquals(HttpGenerator.Result.DONE, result);
|
||||||
|
assertEquals(HttpGenerator.State.END, gen.getState());
|
||||||
|
|
||||||
|
assertThat(out, containsString("HTTP/1.1 200 OK"));
|
||||||
|
assertThat(out, containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
|
||||||
|
assertThat(out, not(containsString("chunked")));
|
||||||
|
assertThat(out, containsString("Content-Length: 59"));
|
||||||
|
assertThat(out, containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. "));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test100ThenResponseWithContent() throws Exception
|
public void test100ThenResponseWithContent() throws Exception
|
||||||
{
|
{
|
||||||
|
|
|
@ -215,14 +215,20 @@ public class HttpTester
|
||||||
|
|
||||||
ByteBuffer buffer = in.getBuffer();
|
ByteBuffer buffer = in.getBuffer();
|
||||||
|
|
||||||
int len=0;
|
while(true)
|
||||||
while(len>=0)
|
|
||||||
{
|
{
|
||||||
if (BufferUtil.hasContent(buffer))
|
if (BufferUtil.hasContent(buffer))
|
||||||
if (parser.parseNext(buffer))
|
if (parser.parseNext(buffer))
|
||||||
break;
|
break;
|
||||||
if (in.fillBuffer()<=0)
|
int len=in.fillBuffer();
|
||||||
|
if (len==0)
|
||||||
break;
|
break;
|
||||||
|
if (len<=0)
|
||||||
|
{
|
||||||
|
parser.atEOF();
|
||||||
|
parser.parseNext(buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r.isComplete())
|
if (r.isComplete())
|
||||||
|
|
|
@ -18,9 +18,11 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http;
|
package org.eclipse.jetty.http;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -76,24 +78,33 @@ public class MimeTypesTest
|
||||||
assertEquals(prefix,expectedMimeType,contentType);
|
assertEquals(prefix,expectedMimeType,contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertCharsetFromContentType(String contentType, String expectedCharset)
|
||||||
|
{
|
||||||
|
assertThat("getCharsetFromContentType(\"" + contentType + "\")",
|
||||||
|
MimeTypes.getCharsetFromContentType(contentType), is(expectedCharset));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCharsetFromContentType()
|
public void testCharsetFromContentType()
|
||||||
{
|
{
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar;charset=abc;some=else"));
|
assertCharsetFromContentType("foo/bar;charset=abc;some=else", "abc");
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar;charset=abc"));
|
assertCharsetFromContentType("foo/bar;charset=abc", "abc");
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar ; charset = abc"));
|
assertCharsetFromContentType("foo/bar ; charset = abc", "abc");
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar ; charset = abc ; some=else"));
|
assertCharsetFromContentType("foo/bar ; charset = abc ; some=else", "abc");
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar;other=param;charset=abc;some=else"));
|
assertCharsetFromContentType("foo/bar;other=param;charset=abc;some=else", "abc");
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar;other=param;charset=abc"));
|
assertCharsetFromContentType("foo/bar;other=param;charset=abc", "abc");
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = abc"));
|
assertCharsetFromContentType("foo/bar other = param ; charset = abc", "abc");
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = abc ; some=else"));
|
assertCharsetFromContentType("foo/bar other = param ; charset = abc ; some=else", "abc");
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = abc"));
|
assertCharsetFromContentType("foo/bar other = param ; charset = abc", "abc");
|
||||||
assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = \"abc\" ; some=else"));
|
assertCharsetFromContentType("foo/bar other = param ; charset = \"abc\" ; some=else", "abc");
|
||||||
assertEquals(null,MimeTypes.getCharsetFromContentType("foo/bar"));
|
assertCharsetFromContentType("foo/bar", null);
|
||||||
assertEquals("utf-8",MimeTypes.getCharsetFromContentType("foo/bar;charset=uTf8"));
|
assertCharsetFromContentType("foo/bar;charset=uTf8", "utf-8");
|
||||||
assertEquals("utf-8",MimeTypes.getCharsetFromContentType("foo/bar;other=\"charset=abc\";charset=uTf8"));
|
assertCharsetFromContentType("foo/bar;other=\"charset=abc\";charset=uTf8", "utf-8");
|
||||||
assertEquals("utf-8",MimeTypes.getCharsetFromContentType("text/html;charset=utf-8"));
|
assertCharsetFromContentType("application/pdf; charset=UTF-8", "utf-8");
|
||||||
|
assertCharsetFromContentType("application/pdf;; charset=UTF-8", "utf-8");
|
||||||
|
assertCharsetFromContentType("application/pdf;;; charset=UTF-8", "utf-8");
|
||||||
|
assertCharsetFromContentType("application/pdf;;;; charset=UTF-8", "utf-8");
|
||||||
|
assertCharsetFromContentType("text/html;charset=utf-8", "utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||||
|
#org.eclipse.jetty.LEVEL=DEBUG
|
||||||
|
#org.eclipse.jetty.server.LEVEL=DEBUG
|
|
@ -1,3 +1,3 @@
|
||||||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||||
org.eclipse.jetty.alpn.LEVEL=DEBUG
|
# org.eclipse.jetty.alpn.LEVEL=DEBUG
|
||||||
org.eclipse.jetty.http2.LEVEL=DEBUG
|
# org.eclipse.jetty.http2.LEVEL=DEBUG
|
||||||
|
|
|
@ -799,4 +799,90 @@ public class PushCacheFilterTest extends AbstractTest
|
||||||
Assert.assertTrue(pushLatch.await(5, TimeUnit.SECONDS));
|
Assert.assertTrue(pushLatch.await(5, TimeUnit.SECONDS));
|
||||||
Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
|
Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPOSTRequestIsNotPushed() throws Exception
|
||||||
|
{
|
||||||
|
final String primaryResource = "/primary.html";
|
||||||
|
final String secondaryResource = "/secondary.png";
|
||||||
|
final byte[] secondaryData = "SECONDARY".getBytes("UTF-8");
|
||||||
|
start(new HttpServlet()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||||
|
{
|
||||||
|
String requestURI = req.getRequestURI();
|
||||||
|
ServletOutputStream output = resp.getOutputStream();
|
||||||
|
if (requestURI.endsWith(primaryResource))
|
||||||
|
output.print("<html><head></head><body>PRIMARY</body></html>");
|
||||||
|
else if (requestURI.endsWith(secondaryResource))
|
||||||
|
output.write(secondaryData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final Session session = newClient(new Session.Listener.Adapter());
|
||||||
|
|
||||||
|
// Request for the primary and secondary resource to build the cache.
|
||||||
|
final String referrerURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
|
||||||
|
HttpFields primaryFields = new HttpFields();
|
||||||
|
MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
|
||||||
|
final CountDownLatch warmupLatch = new CountDownLatch(1);
|
||||||
|
session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||||
|
{
|
||||||
|
callback.succeeded();
|
||||||
|
if (frame.isEndStream())
|
||||||
|
{
|
||||||
|
// Request for the secondary resource.
|
||||||
|
HttpFields secondaryFields = new HttpFields();
|
||||||
|
secondaryFields.put(HttpHeader.REFERER, referrerURI);
|
||||||
|
MetaData.Request secondaryRequest = newRequest("GET", secondaryResource, secondaryFields);
|
||||||
|
session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||||
|
{
|
||||||
|
callback.succeeded();
|
||||||
|
warmupLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(warmupLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
|
// Request again the primary resource with POST, we should not get the secondary resource pushed.
|
||||||
|
primaryRequest = newRequest("POST", primaryResource, primaryFields);
|
||||||
|
final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch pushLatch = new CountDownLatch(1);
|
||||||
|
session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
|
||||||
|
{
|
||||||
|
return new Adapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||||
|
{
|
||||||
|
callback.succeeded();
|
||||||
|
if (frame.isEndStream())
|
||||||
|
pushLatch.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||||
|
{
|
||||||
|
callback.succeeded();
|
||||||
|
if (frame.isEndStream())
|
||||||
|
primaryResponseLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package org.eclipse.jetty.http2;
|
package org.eclipse.jetty.http2;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
@ -46,7 +45,7 @@ public class HTTP2Flusher extends IteratingCallback
|
||||||
private final HTTP2Session session;
|
private final HTTP2Session session;
|
||||||
private final ByteBufferPool.Lease lease;
|
private final ByteBufferPool.Lease lease;
|
||||||
private Entry stalled;
|
private Entry stalled;
|
||||||
private boolean terminated;
|
private Throwable terminated;
|
||||||
|
|
||||||
public HTTP2Flusher(HTTP2Session session)
|
public HTTP2Flusher(HTTP2Session session)
|
||||||
{
|
{
|
||||||
|
@ -56,52 +55,54 @@ public class HTTP2Flusher extends IteratingCallback
|
||||||
|
|
||||||
public void window(IStream stream, WindowUpdateFrame frame)
|
public void window(IStream stream, WindowUpdateFrame frame)
|
||||||
{
|
{
|
||||||
boolean closed;
|
Throwable closed;
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
closed = terminated;
|
closed = terminated;
|
||||||
if (!closed)
|
if (closed == null)
|
||||||
windows.offer(new WindowEntry(stream, frame));
|
windows.offer(new WindowEntry(stream, frame));
|
||||||
}
|
}
|
||||||
// Flush stalled data.
|
// Flush stalled data.
|
||||||
if (!closed)
|
if (closed == null)
|
||||||
iterate();
|
iterate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean prepend(Entry entry)
|
public boolean prepend(Entry entry)
|
||||||
{
|
{
|
||||||
boolean closed;
|
Throwable closed;
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
closed = terminated;
|
closed = terminated;
|
||||||
if (!closed)
|
if (closed == null)
|
||||||
{
|
{
|
||||||
frames.offerFirst(entry);
|
frames.offerFirst(entry);
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Prepended {}, frames={}", entry, frames.size());
|
LOG.debug("Prepended {}, frames={}", entry, frames.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (closed)
|
if (closed == null)
|
||||||
closed(entry, new ClosedChannelException());
|
return true;
|
||||||
return !closed;
|
closed(entry, closed);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean append(Entry entry)
|
public boolean append(Entry entry)
|
||||||
{
|
{
|
||||||
boolean closed;
|
Throwable closed;
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
closed = terminated;
|
closed = terminated;
|
||||||
if (!closed)
|
if (closed == null)
|
||||||
{
|
{
|
||||||
frames.offer(entry);
|
frames.offer(entry);
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Appended {}, frames={}", entry, frames.size());
|
LOG.debug("Appended {}, frames={}", entry, frames.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (closed)
|
if (closed == null)
|
||||||
closed(entry, new ClosedChannelException());
|
return true;
|
||||||
return !closed;
|
closed(entry, closed);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getQueueSize()
|
public int getQueueSize()
|
||||||
|
@ -113,15 +114,15 @@ public class HTTP2Flusher extends IteratingCallback
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Action process() throws Exception
|
protected Action process() throws Throwable
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Flushing {}", session);
|
LOG.debug("Flushing {}", session);
|
||||||
|
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
if (terminated)
|
if (terminated != null)
|
||||||
throw new ClosedChannelException();
|
throw terminated;
|
||||||
|
|
||||||
while (!windows.isEmpty())
|
while (!windows.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -251,13 +252,13 @@ public class HTTP2Flusher extends IteratingCallback
|
||||||
{
|
{
|
||||||
lease.recycle();
|
lease.recycle();
|
||||||
|
|
||||||
boolean closed;
|
Throwable closed;
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
closed = terminated;
|
closed = terminated;
|
||||||
terminated = true;
|
terminated = x;
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{}, active/queued={}/{}", closed ? "Closing" : "Failing", actives.size(), frames.size());
|
LOG.debug("{}, active/queued={}/{}", closed != null ? "Closing" : "Failing", actives.size(), frames.size());
|
||||||
actives.addAll(frames);
|
actives.addAll(frames);
|
||||||
frames.clear();
|
frames.clear();
|
||||||
}
|
}
|
||||||
|
@ -267,21 +268,21 @@ public class HTTP2Flusher extends IteratingCallback
|
||||||
|
|
||||||
// If the failure came from within the
|
// If the failure came from within the
|
||||||
// flusher, we need to close the connection.
|
// flusher, we need to close the connection.
|
||||||
if (!closed)
|
if (closed == null)
|
||||||
session.abort(x);
|
session.abort(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
void terminate()
|
void terminate(Throwable cause)
|
||||||
{
|
{
|
||||||
boolean closed;
|
Throwable closed;
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
closed = terminated;
|
closed = terminated;
|
||||||
terminated = true;
|
terminated = cause;
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{}", closed ? "Terminated" : "Terminating");
|
LOG.debug("{}", closed != null ? "Terminated" : "Terminating");
|
||||||
}
|
}
|
||||||
if (!closed)
|
if (closed == null)
|
||||||
iterate();
|
iterate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -965,7 +965,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
||||||
endPoint.close();
|
endPoint.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void terminate()
|
private void terminate(Throwable cause)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -978,7 +978,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
||||||
{
|
{
|
||||||
if (closed.compareAndSet(current, CloseState.CLOSED))
|
if (closed.compareAndSet(current, CloseState.CLOSED))
|
||||||
{
|
{
|
||||||
flusher.terminate();
|
flusher.terminate(cause);
|
||||||
for (IStream stream : streams.values())
|
for (IStream stream : streams.values())
|
||||||
stream.close();
|
stream.close();
|
||||||
streams.clear();
|
streams.clear();
|
||||||
|
@ -998,7 +998,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
||||||
protected void abort(Throwable failure)
|
protected void abort(Throwable failure)
|
||||||
{
|
{
|
||||||
notifyFailure(this, failure);
|
notifyFailure(this, failure);
|
||||||
terminate();
|
terminate(failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDisconnected()
|
public boolean isDisconnected()
|
||||||
|
@ -1206,7 +1206,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
||||||
}
|
}
|
||||||
case DISCONNECT:
|
case DISCONNECT:
|
||||||
{
|
{
|
||||||
terminate();
|
terminate(new ClosedChannelException());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -18,10 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http2.hpack;
|
package org.eclipse.jetty.http2.hpack;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.BadMessageException;
|
import org.eclipse.jetty.http.BadMessageException;
|
||||||
|
@ -38,6 +34,10 @@ import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
public class HpackTest
|
public class HpackTest
|
||||||
{
|
{
|
||||||
final static HttpField ServerJetty = new PreEncodedHttpField(HttpHeader.SERVER,"jetty");
|
final static HttpField ServerJetty = new PreEncodedHttpField(HttpHeader.SERVER,"jetty");
|
||||||
|
@ -187,7 +187,7 @@ public class HpackTest
|
||||||
private void assertMetadataSame(MetaData expected, MetaData actual)
|
private void assertMetadataSame(MetaData expected, MetaData actual)
|
||||||
{
|
{
|
||||||
assertThat("Metadata.contentLength",actual.getContentLength(),is(expected.getContentLength()));
|
assertThat("Metadata.contentLength",actual.getContentLength(),is(expected.getContentLength()));
|
||||||
assertThat("Metadata.version" + ".version", actual.getVersion(), is(expected.getVersion()));
|
assertThat("Metadata.version" + ".version", actual.getHttpVersion(),is(expected.getHttpVersion()));
|
||||||
assertHttpFieldsSame("Metadata.fields",expected.getFields(),actual.getFields());
|
assertHttpFieldsSame("Metadata.fields",expected.getFields(),actual.getFields());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements Stream.Listen
|
||||||
|
|
||||||
HttpResponse response = exchange.getResponse();
|
HttpResponse response = exchange.getResponse();
|
||||||
MetaData.Response metaData = (MetaData.Response)frame.getMetaData();
|
MetaData.Response metaData = (MetaData.Response)frame.getMetaData();
|
||||||
response.version(metaData.getVersion()).status(metaData.getStatus()).reason(metaData.getReason());
|
response.version(metaData.getHttpVersion()).status(metaData.getStatus()).reason(metaData.getReason());
|
||||||
|
|
||||||
if (responseBegin(exchange))
|
if (responseBegin(exchange))
|
||||||
{
|
{
|
||||||
|
|
|
@ -159,15 +159,18 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Processing {} on {}", frame, stream);
|
LOG.debug("Processing {} on {}", frame, stream);
|
||||||
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
||||||
Runnable task = channel.onRequestContent(frame, callback);
|
if (channel != null)
|
||||||
if (task != null)
|
{
|
||||||
offerTask(task, false);
|
Runnable task = channel.onRequestContent(frame, callback);
|
||||||
|
if (task != null)
|
||||||
|
offerTask(task, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onStreamTimeout(IStream stream, Throwable failure)
|
public boolean onStreamTimeout(IStream stream, Throwable failure)
|
||||||
{
|
{
|
||||||
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
||||||
boolean result = channel.onStreamTimeout(failure);
|
boolean result = channel != null && channel.onStreamTimeout(failure);
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", stream, failure);
|
LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", stream, failure);
|
||||||
return result;
|
return result;
|
||||||
|
@ -178,7 +181,8 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Processing failure on {}: {}", stream, failure);
|
LOG.debug("Processing failure on {}: {}", stream, failure);
|
||||||
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
||||||
channel.onFailure(failure);
|
if (channel != null)
|
||||||
|
channel.onFailure(failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onSessionTimeout(Throwable failure)
|
public boolean onSessionTimeout(Throwable failure)
|
||||||
|
@ -188,7 +192,8 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
|
||||||
for (Stream stream : session.getStreams())
|
for (Stream stream : session.getStreams())
|
||||||
{
|
{
|
||||||
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
||||||
result &= !channel.isRequestHandled();
|
if (channel != null)
|
||||||
|
result &= !channel.isRequestHandled();
|
||||||
}
|
}
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", session, failure);
|
LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", session, failure);
|
||||||
|
|
|
@ -126,7 +126,7 @@ public class HttpChannelOverHTTP2 extends HttpChannel
|
||||||
LOG.debug("HTTP2 Request #{}/{}, delayed={}:{}{} {} {}{}{}",
|
LOG.debug("HTTP2 Request #{}/{}, delayed={}:{}{} {} {}{}{}",
|
||||||
stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
|
stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
|
||||||
_delayedUntilContent, System.lineSeparator(),
|
_delayedUntilContent, System.lineSeparator(),
|
||||||
request.getMethod(), request.getURI(), request.getVersion(),
|
request.getMethod(), request.getURI(), request.getHttpVersion(),
|
||||||
System.lineSeparator(), fields);
|
System.lineSeparator(), fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ public class HttpChannelOverHTTP2 extends HttpChannel
|
||||||
Stream stream = getStream();
|
Stream stream = getStream();
|
||||||
LOG.debug("HTTP2 PUSH Request #{}/{}:{}{} {} {}{}{}",
|
LOG.debug("HTTP2 PUSH Request #{}/{}:{}{} {} {}{}{}",
|
||||||
stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(),
|
stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(),
|
||||||
request.getMethod(), request.getURI(), request.getVersion(),
|
request.getMethod(), request.getURI(), request.getHttpVersion(),
|
||||||
System.lineSeparator(), request.getFields());
|
System.lineSeparator(), request.getFields());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ public class HttpChannelOverHTTP2 extends HttpChannel
|
||||||
{
|
{
|
||||||
Stream stream = getStream();
|
Stream stream = getStream();
|
||||||
LOG.debug("HTTP2 Commit Response #{}/{}:{}{} {} {}{}{}",
|
LOG.debug("HTTP2 Commit Response #{}/{}:{}{} {} {}{}{}",
|
||||||
stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(), info.getVersion(), info.getStatus(), info.getReason(),
|
stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(), info.getHttpVersion(), info.getStatus(), info.getReason(),
|
||||||
System.lineSeparator(), info.getFields());
|
System.lineSeparator(), info.getFields());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,7 +217,8 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
||||||
// Consume the existing queued data frames to
|
// Consume the existing queued data frames to
|
||||||
// avoid stalling the session flow control.
|
// avoid stalling the session flow control.
|
||||||
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
|
||||||
channel.consumeInput();
|
if (channel != null)
|
||||||
|
channel.consumeInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -9,11 +9,13 @@ session-store
|
||||||
|
|
||||||
[depend]
|
[depend]
|
||||||
sessions
|
sessions
|
||||||
sessions/infinispan/default
|
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
maven://org.infinispan/infinispan-embedded/7.1.1.Final|lib/infinispan/infinispan-embedded-7.1.1.Final.jar
|
maven://org.infinispan/infinispan-embedded/7.1.1.Final|lib/infinispan/infinispan-embedded-7.1.1.Final.jar
|
||||||
|
|
||||||
|
[xml]
|
||||||
|
etc/sessions/infinispan/default.xml
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
lib/jetty-infinispan-${jetty.version}.jar
|
lib/jetty-infinispan-${jetty.version}.jar
|
||||||
lib/infinispan/*.jar
|
lib/infinispan/*.jar
|
||||||
|
@ -22,3 +24,4 @@ lib/infinispan/*.jar
|
||||||
Infinispan is an open source project hosted on Github and released under the Apache 2.0 license.
|
Infinispan is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||||
http://infinispan.org/
|
http://infinispan.org/
|
||||||
http://www.apache.org/licenses/LICENSE-2.0.html
|
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,13 @@ session-store
|
||||||
|
|
||||||
[depend]
|
[depend]
|
||||||
sessions
|
sessions
|
||||||
sessions/infinispan/remote
|
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
maven://org.infinispan/infinispan-remote/7.1.1.Final|lib/infinispan/infinispan-remote-7.1.1.Final.jar
|
maven://org.infinispan/infinispan-remote/7.1.1.Final|lib/infinispan/infinispan-remote-7.1.1.Final.jar
|
||||||
|
basehome:modules/session-store-infinispan-remote/
|
||||||
|
|
||||||
|
[xml]
|
||||||
|
etc/sessions/infinispan/remote.xml
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
lib/jetty-infinispan-${jetty.version}.jar
|
lib/jetty-infinispan-${jetty.version}.jar
|
||||||
|
@ -28,3 +31,8 @@ http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
#jetty.session.infinispan.remoteCacheName=sessions
|
#jetty.session.infinispan.remoteCacheName=sessions
|
||||||
#jetty.session.infinispan.idleTimeout.seconds=0
|
#jetty.session.infinispan.idleTimeout.seconds=0
|
||||||
#jetty.session.gracePeriod.seconds=3600
|
#jetty.session.gracePeriod.seconds=3600
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
[description]
|
|
||||||
Enable use of DefaultCache for session data storage
|
|
||||||
|
|
||||||
[xml]
|
|
||||||
etc/sessions/infinispan/default.xml
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
[description]
|
|
||||||
Enable use of HotRod remote cache for session data storage
|
|
||||||
|
|
||||||
[files]
|
|
||||||
https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-infinispan/src/main/infinispan-resources/hotrod-client.properties?id=${jetty.tag.version}|resources/hotrod-client.properties
|
|
||||||
|
|
||||||
[xml]
|
|
||||||
etc/sessions/infinispan/remote.xml
|
|
||||||
|
|
|
@ -331,7 +331,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint
|
||||||
* @param time Time to wait
|
* @param time Time to wait
|
||||||
* @param unit Units for time to wait
|
* @param unit Units for time to wait
|
||||||
* @return The buffer of output
|
* @return The buffer of output
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException if interrupted
|
||||||
*/
|
*/
|
||||||
public ByteBuffer waitForOutput(long time,TimeUnit unit) throws InterruptedException
|
public ByteBuffer waitForOutput(long time,TimeUnit unit) throws InterruptedException
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,6 +34,7 @@ import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
@ -69,86 +70,14 @@ public class ManagedSelector extends AbstractLifeCycle implements Dumpable
|
||||||
private final ExecutionStrategy _lowPriorityStrategy;
|
private final ExecutionStrategy _lowPriorityStrategy;
|
||||||
private Selector _selector;
|
private Selector _selector;
|
||||||
|
|
||||||
private final Runnable _runStrategy = new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
_strategy.produce();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final Runnable _runLowPriorityStrategy = new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
Thread current = Thread.currentThread();
|
|
||||||
String name = current.getName();
|
|
||||||
int priority = current.getPriority();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (isRunning())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
current.setPriority(Thread.MIN_PRIORITY);
|
|
||||||
current.setName(name+"-lowPrioSelector");
|
|
||||||
_lowPriorityStrategy.produce();
|
|
||||||
}
|
|
||||||
catch (Throwable th)
|
|
||||||
{
|
|
||||||
LOG.warn(th);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
current.setPriority(priority);
|
|
||||||
current.setName(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public ManagedSelector(SelectorManager selectorManager, int id)
|
public ManagedSelector(SelectorManager selectorManager, int id)
|
||||||
{
|
{
|
||||||
_selectorManager = selectorManager;
|
_selectorManager = selectorManager;
|
||||||
_id = id;
|
_id = id;
|
||||||
SelectorProducer producer = new SelectorProducer();
|
SelectorProducer producer = new SelectorProducer();
|
||||||
_strategy = new ExecuteProduceConsume(producer, selectorManager.getExecutor(), Invocable.InvocationType.BLOCKING);
|
Executor executor = selectorManager.getExecutor();
|
||||||
_lowPriorityStrategy = new ProduceExecuteConsume(producer, selectorManager.getExecutor(), Invocable.InvocationType.BLOCKING)
|
_strategy = new ExecuteProduceConsume(producer, executor, Invocable.InvocationType.BLOCKING);
|
||||||
{
|
_lowPriorityStrategy = new LowPriorityProduceExecuteConsume(producer, executor);
|
||||||
@Override
|
|
||||||
protected boolean execute(Runnable task)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Invocable.InvocationType invocation=Invocable.getInvocationType(task);
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Low Prio Selector execute {} {}",invocation,task);
|
|
||||||
switch (Invocable.getInvocationType(task))
|
|
||||||
{
|
|
||||||
case NON_BLOCKING:
|
|
||||||
task.run();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case EITHER:
|
|
||||||
Invocable.invokeNonBlocking(task);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
return super.execute(task);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Allow opportunity for main strategy to take over
|
|
||||||
Thread.yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
setStopTimeout(5000);
|
setStopTimeout(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,9 +85,38 @@ public class ManagedSelector extends AbstractLifeCycle implements Dumpable
|
||||||
protected void doStart() throws Exception
|
protected void doStart() throws Exception
|
||||||
{
|
{
|
||||||
super.doStart();
|
super.doStart();
|
||||||
|
|
||||||
_selector = _selectorManager.newSelector();
|
_selector = _selectorManager.newSelector();
|
||||||
_selectorManager.execute(_runStrategy);
|
|
||||||
_selectorManager.execute(_runLowPriorityStrategy);
|
// The producer used by the strategies will never
|
||||||
|
// be idle (either produces a task or blocks).
|
||||||
|
|
||||||
|
// The normal strategy obtains the produced task, schedules
|
||||||
|
// a new thread to produce more, runs the task and then exits.
|
||||||
|
_selectorManager.execute(_strategy::produce);
|
||||||
|
|
||||||
|
// The low priority strategy knows the producer will never
|
||||||
|
// be idle, that tasks are scheduled to run in different
|
||||||
|
// threads, therefore lowPriorityProduce() never exits.
|
||||||
|
_selectorManager.execute(this::lowPriorityProduce);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lowPriorityProduce()
|
||||||
|
{
|
||||||
|
Thread current = Thread.currentThread();
|
||||||
|
String name = current.getName();
|
||||||
|
int priority = current.getPriority();
|
||||||
|
current.setPriority(Thread.MIN_PRIORITY);
|
||||||
|
current.setName(name+"-lowPrioritySelector");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_lowPriorityStrategy.produce();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
current.setPriority(priority);
|
||||||
|
current.setName(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size()
|
public int size()
|
||||||
|
@ -227,28 +185,75 @@ public class ManagedSelector extends AbstractLifeCycle implements Dumpable
|
||||||
void updateKey();
|
void updateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class LowPriorityProduceExecuteConsume extends ProduceExecuteConsume
|
||||||
|
{
|
||||||
|
private LowPriorityProduceExecuteConsume(SelectorProducer producer, Executor executor)
|
||||||
|
{
|
||||||
|
super(producer, executor, InvocationType.BLOCKING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean execute(Runnable task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InvocationType invocation=Invocable.getInvocationType(task);
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Low Priority Selector executing {} {}",invocation,task);
|
||||||
|
switch (invocation)
|
||||||
|
{
|
||||||
|
case NON_BLOCKING:
|
||||||
|
task.run();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case EITHER:
|
||||||
|
Invocable.invokeNonBlocking(task);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return super.execute(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Allow opportunity for main strategy to take over.
|
||||||
|
Thread.yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class SelectorProducer implements ExecutionStrategy.Producer
|
private class SelectorProducer implements ExecutionStrategy.Producer
|
||||||
{
|
{
|
||||||
private Set<SelectionKey> _keys = Collections.emptySet();
|
private Set<SelectionKey> _keys = Collections.emptySet();
|
||||||
private Iterator<SelectionKey> _cursor = Collections.emptyIterator();
|
private Iterator<SelectionKey> _cursor = Collections.emptyIterator();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized Runnable produce()
|
public Runnable produce()
|
||||||
{
|
{
|
||||||
while (true)
|
// This method is called from both the
|
||||||
|
// normal and low priority strategies.
|
||||||
|
// Only one can produce at a time, so it's synchronized
|
||||||
|
// to enforce that only one strategy actually produces.
|
||||||
|
// When idle in select(), this method blocks;
|
||||||
|
// the other strategy's thread will be blocked
|
||||||
|
// waiting for this lock to be released.
|
||||||
|
synchronized (this)
|
||||||
{
|
{
|
||||||
Runnable task = processSelected();
|
while (true)
|
||||||
if (task != null)
|
{
|
||||||
return task;
|
Runnable task = processSelected();
|
||||||
|
if (task != null)
|
||||||
|
return task;
|
||||||
|
|
||||||
Runnable action = nextAction();
|
Runnable action = nextAction();
|
||||||
if (action != null)
|
if (action != null)
|
||||||
return action;
|
return action;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
if (!select())
|
if (!select())
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,7 +497,7 @@ public class ManagedSelector extends AbstractLifeCycle implements Dumpable
|
||||||
public void destroyEndPoint(final EndPoint endPoint)
|
public void destroyEndPoint(final EndPoint endPoint)
|
||||||
{
|
{
|
||||||
final Connection connection = endPoint.getConnection();
|
final Connection connection = endPoint.getConnection();
|
||||||
submit((Runnable)() ->
|
submit(() ->
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Destroyed {}", endPoint);
|
LOG.debug("Destroyed {}", endPoint);
|
||||||
|
|
|
@ -39,7 +39,6 @@ import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.io.EofException;
|
import org.eclipse.jetty.io.EofException;
|
||||||
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
|
||||||
import org.eclipse.jetty.io.WriteFlusher;
|
import org.eclipse.jetty.io.WriteFlusher;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
@ -51,7 +50,7 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
* and another consumer of an EndPoint (typically an {@link Connection} like HttpConnection) that
|
* and another consumer of an EndPoint (typically an {@link Connection} like HttpConnection) that
|
||||||
* wants unencrypted data.
|
* wants unencrypted data.
|
||||||
* <p>
|
* <p>
|
||||||
* The connector uses an {@link EndPoint} (typically {@link SelectChannelEndPoint}) as
|
* The connector uses an {@link EndPoint} (typically SocketChannelEndPoint) as
|
||||||
* it's source/sink of encrypted data. It then provides an endpoint via {@link #getDecryptedEndPoint()} to
|
* it's source/sink of encrypted data. It then provides an endpoint via {@link #getDecryptedEndPoint()} to
|
||||||
* expose a source/sink of unencrypted data to another connection (eg HttpConnection).
|
* expose a source/sink of unencrypted data to another connection (eg HttpConnection).
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -111,6 +110,33 @@ public class SslConnection extends AbstractConnection
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Callback _sslReadCallback = new Callback()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void succeeded()
|
||||||
|
{
|
||||||
|
onFillable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(final Throwable x)
|
||||||
|
{
|
||||||
|
onFillInterestedFailed(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InvocationType getInvocationType()
|
||||||
|
{
|
||||||
|
return getDecryptedEndPoint().getFillInterest().getCallbackInvocationType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("SSLC.NBReadCB@%x{%s}", SslConnection.this.hashCode(),SslConnection.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine)
|
public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine)
|
||||||
{
|
{
|
||||||
// This connection does not execute calls to onFillable(), so they will be called by the selector thread.
|
// This connection does not execute calls to onFillable(), so they will be called by the selector thread.
|
||||||
|
@ -961,7 +987,11 @@ public class SslConnection extends AbstractConnection
|
||||||
private void ensureFillInterested()
|
private void ensureFillInterested()
|
||||||
{
|
{
|
||||||
if (!SslConnection.this.isFillInterested())
|
if (!SslConnection.this.isFillInterested())
|
||||||
SslConnection.this.fillInterested();
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("fillInterested SSL NB {}",SslConnection.this);
|
||||||
|
SslConnection.this.getEndPoint().fillInterested(_sslReadCallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -34,6 +34,13 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -35,6 +35,13 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -70,6 +70,13 @@
|
||||||
</sourceExcludes>
|
</sourceExcludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
|
|
@ -257,8 +257,11 @@ public class ProxyServletFailureTest
|
||||||
public void testProxyRequestStallsContentServerIdlesTimeout() throws Exception
|
public void testProxyRequestStallsContentServerIdlesTimeout() throws Exception
|
||||||
{
|
{
|
||||||
final byte[] content = new byte[]{'C', '0', 'F', 'F', 'E', 'E'};
|
final byte[] content = new byte[]{'C', '0', 'F', 'F', 'E', 'E'};
|
||||||
|
int expected = -1;
|
||||||
if (proxyServlet instanceof AsyncProxyServlet)
|
if (proxyServlet instanceof AsyncProxyServlet)
|
||||||
{
|
{
|
||||||
|
// TODO should this be a 502 also???
|
||||||
|
expected = 500;
|
||||||
proxyServlet = new AsyncProxyServlet()
|
proxyServlet = new AsyncProxyServlet()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -281,6 +284,7 @@ public class ProxyServletFailureTest
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
expected = 502;
|
||||||
proxyServlet = new ProxyServlet()
|
proxyServlet = new ProxyServlet()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -310,7 +314,7 @@ public class ProxyServletFailureTest
|
||||||
.content(new BytesContentProvider(content))
|
.content(new BytesContentProvider(content))
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
Assert.assertEquals(500, response.getStatus());
|
Assert.assertEquals(expected, response.getStatus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -360,13 +360,18 @@ public class ProxyServletTest
|
||||||
resp.addHeader(PROXIED_HEADER, "true");
|
resp.addHeader(PROXIED_HEADER, "true");
|
||||||
InputStream input = req.getInputStream();
|
InputStream input = req.getInputStream();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
|
byte[] buffer = new byte[16*1024];
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int value = input.read();
|
int value = input.read(buffer);
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
break;
|
break;
|
||||||
Assert.assertEquals("Content mismatch at index=" + index, content[index] & 0xFF, value);
|
for (int i=0;i<value;i++)
|
||||||
++index;
|
{
|
||||||
|
Assert.assertEquals("Content mismatch at index=" + index, content[index] & 0xFF, buffer[i] & 0xFF);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -931,9 +936,10 @@ public class ProxyServletTest
|
||||||
Assert.assertArrayEquals(content, response.getContent());
|
Assert.assertArrayEquals(content, response.getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = TimeoutException.class)
|
@Test
|
||||||
public void testWrongContentLength() throws Exception
|
public void testWrongContentLength() throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
startServer(new HttpServlet()
|
startServer(new HttpServlet()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -948,11 +954,17 @@ public class ProxyServletTest
|
||||||
startProxy();
|
startProxy();
|
||||||
startClient();
|
startClient();
|
||||||
|
|
||||||
client.newRequest("localhost", serverConnector.getLocalPort())
|
try
|
||||||
.timeout(1, TimeUnit.SECONDS)
|
{
|
||||||
|
ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
.send();
|
.send();
|
||||||
|
Assert.assertThat(response.getStatus(),Matchers.greaterThanOrEqualTo(500));
|
||||||
Assert.fail();
|
}
|
||||||
|
catch(ExecutionException e)
|
||||||
|
{
|
||||||
|
Assert.assertThat(e.getCause(),Matchers.instanceOf(IOException.class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -2,3 +2,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||||
#org.eclipse.jetty.LEVEL=DEBUG
|
#org.eclipse.jetty.LEVEL=DEBUG
|
||||||
#org.eclipse.jetty.client.LEVEL=DEBUG
|
#org.eclipse.jetty.client.LEVEL=DEBUG
|
||||||
#org.eclipse.jetty.proxy.LEVEL=DEBUG
|
#org.eclipse.jetty.proxy.LEVEL=DEBUG
|
||||||
|
#org.eclipse.jetty.server.HttpInput.LEVEL=DEBUG
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.quickstart;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
@ -28,12 +29,14 @@ import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
|
@ -44,10 +47,16 @@ import org.eclipse.jetty.util.resource.Resource;
|
||||||
* Replaces and expands:
|
* Replaces and expands:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>${WAR}</li>
|
* <li>${WAR}</li>
|
||||||
|
* <li>${WAR.path}</li>
|
||||||
|
* <li>${WAR.uri}</li>
|
||||||
* <li>${jetty.base}</li>
|
* <li>${jetty.base}</li>
|
||||||
|
* <li>${jetty.base.uri}</li>
|
||||||
* <li>${jetty.home}</li>
|
* <li>${jetty.home}</li>
|
||||||
|
* <li>${jetty.home.uri}</li>
|
||||||
* <li>${user.home}</li>
|
* <li>${user.home}</li>
|
||||||
|
* <li>${user.home.uri}</li>
|
||||||
* <li>${user.dir}</li>
|
* <li>${user.dir}</li>
|
||||||
|
* <li>${user.dir.uri}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class AttributeNormalizer
|
public class AttributeNormalizer
|
||||||
|
@ -55,88 +64,130 @@ public class AttributeNormalizer
|
||||||
private static final Logger LOG = Log.getLogger(AttributeNormalizer.class);
|
private static final Logger LOG = Log.getLogger(AttributeNormalizer.class);
|
||||||
private static final Pattern __propertyPattern = Pattern.compile("(?<=[^$]|^)\\$\\{([^}]*)\\}");
|
private static final Pattern __propertyPattern = Pattern.compile("(?<=[^$]|^)\\$\\{([^}]*)\\}");
|
||||||
|
|
||||||
private static class PathAttribute
|
private static class Attribute
|
||||||
{
|
{
|
||||||
public final Path path;
|
final String key;
|
||||||
public final String key;
|
final String value;
|
||||||
private int weight = -1;
|
final int weight;
|
||||||
|
|
||||||
public PathAttribute(String key, Path path) throws IOException
|
public Attribute(String key, String value, int weight)
|
||||||
{
|
{
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.path = toCanonicalPath(path);
|
this.value = value;
|
||||||
// TODO: Don't allow non-directory paths? (but what if the path doesn't exist?)
|
this.weight = weight;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public PathAttribute(String key, String systemPropertyKey) throws IOException
|
private static URI toCanonicalURI(URI uri)
|
||||||
|
{
|
||||||
|
uri = uri.normalize();
|
||||||
|
String ascii = uri.toASCIIString();
|
||||||
|
if (ascii.endsWith("/"))
|
||||||
{
|
{
|
||||||
this(key, toCanonicalPath(System.getProperty(systemPropertyKey)));
|
try
|
||||||
}
|
|
||||||
|
|
||||||
private static Path toCanonicalPath(String path) throws IOException
|
|
||||||
{
|
|
||||||
if (path == null)
|
|
||||||
{
|
{
|
||||||
return null;
|
uri = new URI(ascii.substring(0,ascii.length()-1));
|
||||||
}
|
}
|
||||||
return toCanonicalPath(FileSystems.getDefault().getPath(path));
|
catch(URISyntaxException e)
|
||||||
}
|
|
||||||
|
|
||||||
private static Path toCanonicalPath(Path path) throws IOException
|
|
||||||
{
|
|
||||||
if (path == null)
|
|
||||||
{
|
{
|
||||||
return null;
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
if (Files.exists(path))
|
}
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path toCanonicalPath(String path)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
return null;
|
||||||
|
if (path.length()>1 && path.endsWith("/"))
|
||||||
|
path = path.substring(0,path.length()-1);
|
||||||
|
return toCanonicalPath(FileSystems.getDefault().getPath(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path toCanonicalPath(Path path)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (Files.exists(path))
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
return path.toRealPath();
|
return path.toRealPath();
|
||||||
}
|
}
|
||||||
return path.toAbsolutePath();
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return path.toAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
public PathAttribute weight(int newweight)
|
private static class PathAttribute extends Attribute
|
||||||
|
{
|
||||||
|
public final Path path;
|
||||||
|
|
||||||
|
public PathAttribute(String key, Path path, int weight)
|
||||||
{
|
{
|
||||||
this.weight = newweight;
|
super(key,path.toString(),weight);
|
||||||
return this;
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("PathAttribute[%s=>%s,%d]",key,path,weight);
|
return String.format("PathAttribute[%s=>%s]",key,path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PathAttributeComparator implements Comparator<PathAttribute>
|
private static class URIAttribute extends Attribute
|
||||||
|
{
|
||||||
|
public final URI uri;
|
||||||
|
|
||||||
|
public URIAttribute(String key, URI uri, int weight)
|
||||||
|
{
|
||||||
|
super(key,uri.toASCIIString(),weight);
|
||||||
|
this.uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("URIAttribute[%s=>%s]",key,uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Comparator<Attribute> attrComparator = new Comparator<Attribute>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public int compare(PathAttribute o1, PathAttribute o2)
|
public int compare(Attribute o1, Attribute o2)
|
||||||
{
|
{
|
||||||
if( (o1.path == null) && (o2.path != null) )
|
if( (o1.value == null) && (o2.value != null) )
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( (o1.path != null) && (o2.path == null) )
|
if( (o1.value != null) && (o2.value == null) )
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( (o1.path == null) && (o2.path == null) )
|
if( (o1.value == null) && (o2.value == null) )
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Different lengths?
|
// Different lengths?
|
||||||
int diff = o2.path.getNameCount() - o1.path.getNameCount();
|
int diff = o2.value.length() - o1.value.length();
|
||||||
if(diff != 0)
|
if(diff != 0)
|
||||||
{
|
{
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Different names?
|
// Different names?
|
||||||
diff = o2.path.compareTo(o1.path);
|
diff = o2.value.compareTo(o1.value);
|
||||||
if(diff != 0)
|
if(diff != 0)
|
||||||
{
|
{
|
||||||
return diff;
|
return diff;
|
||||||
|
@ -145,99 +196,122 @@ public class AttributeNormalizer
|
||||||
// The paths are the same, base now on weight
|
// The paths are the same, base now on weight
|
||||||
return o2.weight - o1.weight;
|
return o2.weight - o1.weight;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
public static String uriSeparators(String path)
|
private static void add(List<PathAttribute>paths,List<URIAttribute> uris,String key,int weight)
|
||||||
{
|
{
|
||||||
StringBuilder ret = new StringBuilder();
|
String value = System.getProperty(key);
|
||||||
for (char c : path.toCharArray())
|
if (value!=null)
|
||||||
{
|
{
|
||||||
if ((c == '/') || (c == '\\'))
|
Path path = toCanonicalPath(value);
|
||||||
{
|
paths.add(new PathAttribute(key,path,weight));
|
||||||
ret.append('/');
|
uris.add(new URIAttribute(key+".uri",toCanonicalURI(path.toUri()),weight));
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret.append(c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private URI warURI;
|
private URI warURI;
|
||||||
private List<PathAttribute> attributes = new ArrayList<>();
|
private Map<String,Attribute> attributes = new HashMap<>();
|
||||||
|
private List<PathAttribute> paths = new ArrayList<>();
|
||||||
|
private List<URIAttribute> uris = new ArrayList<>();
|
||||||
|
|
||||||
public AttributeNormalizer(Resource baseResource)
|
public AttributeNormalizer(Resource baseResource)
|
||||||
{
|
{
|
||||||
// WAR URI is always evaluated before paths.
|
if (baseResource==null)
|
||||||
warURI = baseResource == null ? null : baseResource.getURI();
|
throw new IllegalArgumentException("No base resource!");
|
||||||
// We don't normalize or resolve the baseResource URI
|
|
||||||
|
warURI = toCanonicalURI(baseResource.getURI());
|
||||||
if (!warURI.isAbsolute())
|
if (!warURI.isAbsolute())
|
||||||
throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
|
throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
|
||||||
try
|
|
||||||
|
add(paths,uris,"jetty.base",9);
|
||||||
|
add(paths,uris,"jetty.home",8);
|
||||||
|
add(paths,uris,"user.home",7);
|
||||||
|
add(paths,uris,"user.dir",6);
|
||||||
|
|
||||||
|
if (warURI.getScheme().equalsIgnoreCase("file"))
|
||||||
|
paths.add(new PathAttribute("WAR.path",toCanonicalPath(new File(warURI).toString()),10));
|
||||||
|
uris.add(new URIAttribute("WAR.uri", warURI,9)); // preferred encoding
|
||||||
|
uris.add(new URIAttribute("WAR", warURI,8)); // legacy encoding
|
||||||
|
|
||||||
|
Collections.sort(paths,attrComparator);
|
||||||
|
Collections.sort(uris,attrComparator);
|
||||||
|
|
||||||
|
Stream.concat(paths.stream(),uris.stream()).forEach(a->attributes.put(a.key,a));
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
// Track path attributes for expansion
|
for (Attribute attr : attributes.values())
|
||||||
attributes.add(new PathAttribute("jetty.base", "jetty.base").weight(9));
|
|
||||||
attributes.add(new PathAttribute("jetty.home", "jetty.home").weight(8));
|
|
||||||
attributes.add(new PathAttribute("user.home", "user.home").weight(7));
|
|
||||||
attributes.add(new PathAttribute("user.dir", "user.dir").weight(6));
|
|
||||||
|
|
||||||
Collections.sort(attributes, new PathAttributeComparator());
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
{
|
{
|
||||||
int i = 0;
|
LOG.debug(attr.toString());
|
||||||
for (PathAttribute attr : attributes)
|
|
||||||
{
|
|
||||||
LOG.debug(" [{}] {}", i++, attr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a URI, URL, or File reference by replacing known attributes with ${key} attributes.
|
||||||
|
*
|
||||||
|
* @param o the object to normalize into a string
|
||||||
|
* @return the string representation of the object, with expansion keys.
|
||||||
|
*/
|
||||||
public String normalize(Object o)
|
public String normalize(Object o)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Find a URI
|
// Find a URI
|
||||||
URI uri = null;
|
URI uri = null;
|
||||||
|
Path path = null;
|
||||||
if (o instanceof URI)
|
if (o instanceof URI)
|
||||||
uri = (URI)o;
|
uri = toCanonicalURI(((URI)o));
|
||||||
|
else if (o instanceof Resource)
|
||||||
|
uri = toCanonicalURI(((Resource)o).getURI());
|
||||||
else if (o instanceof URL)
|
else if (o instanceof URL)
|
||||||
uri = ((URL)o).toURI();
|
uri = toCanonicalURI(((URL)o).toURI());
|
||||||
else if (o instanceof File)
|
else if (o instanceof File)
|
||||||
uri = ((File)o).toURI();
|
path = ((File)o).getAbsoluteFile().getCanonicalFile().toPath();
|
||||||
|
else if (o instanceof Path)
|
||||||
|
path = (Path)o;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
String s = o.toString();
|
String s = o.toString();
|
||||||
uri = new URI(s);
|
try
|
||||||
if (uri.getScheme() == null)
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("jar".equalsIgnoreCase(uri.getScheme()))
|
|
||||||
{
|
|
||||||
String raw = uri.getRawSchemeSpecificPart();
|
|
||||||
int bang = raw.indexOf("!/");
|
|
||||||
String normal = normalize(raw.substring(0,bang));
|
|
||||||
String suffix = raw.substring(bang);
|
|
||||||
return "jar:" + normal + suffix;
|
|
||||||
}
|
|
||||||
else if ("file".equalsIgnoreCase(uri.getScheme()))
|
|
||||||
{
|
|
||||||
return "file:" + normalizePath(new File(uri.getRawSchemeSpecificPart()).toPath());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(uri.isAbsolute())
|
|
||||||
{
|
{
|
||||||
return normalizeUri(uri);
|
uri = new URI(s);
|
||||||
|
if (uri.getScheme() == null)
|
||||||
|
{
|
||||||
|
// Unknown scheme? not relevant to normalize
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(URISyntaxException e)
|
||||||
|
{
|
||||||
|
// This path occurs for many reasons, but most common is when this
|
||||||
|
// is executed on MS Windows, on a string like "D:\jetty"
|
||||||
|
// and the new URI() fails for
|
||||||
|
// java.net.URISyntaxException: Illegal character in opaque part at index 2: D:\jetty
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (uri!=null)
|
||||||
|
{
|
||||||
|
if ("jar".equalsIgnoreCase(uri.getScheme()))
|
||||||
|
{
|
||||||
|
String raw = uri.getRawSchemeSpecificPart();
|
||||||
|
int bang = raw.indexOf("!/");
|
||||||
|
String normal = normalize(raw.substring(0,bang));
|
||||||
|
String suffix = raw.substring(bang);
|
||||||
|
return "jar:" + normal + suffix;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(uri.isAbsolute())
|
||||||
|
{
|
||||||
|
return normalizeUri(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (path!=null)
|
||||||
|
return normalizePath(path);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -246,38 +320,62 @@ public class AttributeNormalizer
|
||||||
return String.valueOf(o);
|
return String.valueOf(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String normalizeUri(URI uri)
|
protected String normalizeUri(URI uri)
|
||||||
{
|
{
|
||||||
String uriStr = uri.toASCIIString();
|
for (URIAttribute a : uris)
|
||||||
String warStr = warURI.toASCIIString();
|
|
||||||
if (uriStr.startsWith(warStr))
|
|
||||||
{
|
{
|
||||||
return "${WAR}" + uriStr.substring(warStr.length());
|
|
||||||
}
|
|
||||||
return uriStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String normalizePath(Path path)
|
|
||||||
{
|
|
||||||
for (PathAttribute attr : attributes)
|
|
||||||
{
|
|
||||||
if (attr.path == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (path.startsWith(attr.path) || path.equals(attr.path) || Files.isSameFile(path,attr.path))
|
if (uri.compareTo(a.uri)==0)
|
||||||
{
|
return String.format("${%s}",a.key);
|
||||||
return uriSeparators(URIUtil.addPaths("${" + attr.key + "}",attr.path.relativize(path).toString()));
|
|
||||||
}
|
if (!a.uri.getScheme().equalsIgnoreCase(uri.getScheme()))
|
||||||
|
continue;
|
||||||
|
if (a.uri.getHost()==null && uri.getHost()!=null)
|
||||||
|
continue;
|
||||||
|
if (a.uri.getHost()!=null && !a.uri.getHost().equals(uri.getHost()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (a.uri.getPath().equals(uri.getPath()))
|
||||||
|
return a.value;
|
||||||
|
|
||||||
|
if (!uri.getPath().startsWith(a.uri.getPath()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String s = uri.getPath().substring(a.uri.getPath().length());
|
||||||
|
|
||||||
|
if (s.charAt(0)!='/')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return String.format("${%s}%s",a.key,new URI(s).toASCIIString());
|
||||||
|
}
|
||||||
|
catch(URISyntaxException e)
|
||||||
|
{
|
||||||
|
LOG.ignore(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uri.toASCIIString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String normalizePath(Path path)
|
||||||
|
{
|
||||||
|
for (PathAttribute a : paths)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (path.equals(a.path) || Files.isSameFile(path,a.path))
|
||||||
|
return String.format("${%s}",a.key);
|
||||||
}
|
}
|
||||||
catch (IOException ignore)
|
catch (IOException ignore)
|
||||||
{
|
{
|
||||||
LOG.ignore(ignore);
|
LOG.ignore(ignore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (path.startsWith(a.path))
|
||||||
|
return String.format("${%s}/%s",a.key,a.path.relativize(path).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return uriSeparators(path.toString());
|
return path.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String expand(String str)
|
public String expand(String str)
|
||||||
|
@ -359,25 +457,14 @@ public class AttributeNormalizer
|
||||||
|
|
||||||
private String getString(String property)
|
private String getString(String property)
|
||||||
{
|
{
|
||||||
if(property == null)
|
if(property==null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use war path (if known)
|
Attribute a = attributes.get(property);
|
||||||
if("WAR".equalsIgnoreCase(property))
|
if (a!=null)
|
||||||
{
|
return a.value;
|
||||||
return warURI.toASCIIString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use known path attributes
|
|
||||||
for (PathAttribute attr : attributes)
|
|
||||||
{
|
|
||||||
if (attr.key.equalsIgnoreCase(property))
|
|
||||||
{
|
|
||||||
return uriSeparators(attr.path.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use system properties next
|
// Use system properties next
|
||||||
return System.getProperty(property);
|
return System.getProperty(property);
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
@ -50,8 +51,6 @@ import org.eclipse.jetty.util.security.Constraint;
|
||||||
import org.eclipse.jetty.util.security.Credential;
|
import org.eclipse.jetty.util.security.Credential;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
|
|
||||||
*
|
|
||||||
* The nonce max age in ms can be set with the {@link SecurityHandler#setInitParameter(String, String)}
|
* The nonce max age in ms can be set with the {@link SecurityHandler#setInitParameter(String, String)}
|
||||||
* using the name "maxNonceAge". The nonce max count can be set with {@link SecurityHandler#setInitParameter(String, String)}
|
* using the name "maxNonceAge". The nonce max count can be set with {@link SecurityHandler#setInitParameter(String, String)}
|
||||||
* using the name "maxNonceCount". When the age or count is exceeded, the nonce is considered stale.
|
* using the name "maxNonceCount". When the age or count is exceeded, the nonce is considered stale.
|
||||||
|
@ -59,105 +58,58 @@ import org.eclipse.jetty.util.security.Credential;
|
||||||
public class DigestAuthenticator extends LoginAuthenticator
|
public class DigestAuthenticator extends LoginAuthenticator
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(DigestAuthenticator.class);
|
private static final Logger LOG = Log.getLogger(DigestAuthenticator.class);
|
||||||
SecureRandom _random = new SecureRandom();
|
|
||||||
private long _maxNonceAgeMs = 60*1000;
|
|
||||||
private int _maxNC=1024;
|
|
||||||
private ConcurrentMap<String, Nonce> _nonceMap = new ConcurrentHashMap<String, Nonce>();
|
|
||||||
private Queue<Nonce> _nonceQueue = new ConcurrentLinkedQueue<Nonce>();
|
|
||||||
private static class Nonce
|
|
||||||
{
|
|
||||||
final String _nonce;
|
|
||||||
final long _ts;
|
|
||||||
final BitSet _seen;
|
|
||||||
|
|
||||||
public Nonce(String nonce, long ts, int size)
|
private final SecureRandom _random = new SecureRandom();
|
||||||
{
|
private long _maxNonceAgeMs = 60 * 1000;
|
||||||
_nonce=nonce;
|
private int _maxNC = 1024;
|
||||||
_ts=ts;
|
private ConcurrentMap<String, Nonce> _nonceMap = new ConcurrentHashMap<>();
|
||||||
_seen = new BitSet(size);
|
private Queue<Nonce> _nonceQueue = new ConcurrentLinkedQueue<>();
|
||||||
}
|
|
||||||
|
|
||||||
public boolean seen(int count)
|
|
||||||
{
|
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
if (count>=_seen.size())
|
|
||||||
return true;
|
|
||||||
boolean s=_seen.get(count);
|
|
||||||
_seen.set(count);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public DigestAuthenticator()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.security.authentication.LoginAuthenticator#setConfiguration(org.eclipse.jetty.security.Authenticator.AuthConfiguration)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setConfiguration(AuthConfiguration configuration)
|
public void setConfiguration(AuthConfiguration configuration)
|
||||||
{
|
{
|
||||||
super.setConfiguration(configuration);
|
super.setConfiguration(configuration);
|
||||||
|
|
||||||
String mna=configuration.getInitParameter("maxNonceAge");
|
String mna = configuration.getInitParameter("maxNonceAge");
|
||||||
if (mna!=null)
|
if (mna != null)
|
||||||
{
|
setMaxNonceAge(Long.valueOf(mna));
|
||||||
_maxNonceAgeMs=Long.valueOf(mna);
|
String mnc = configuration.getInitParameter("maxNonceCount");
|
||||||
}
|
if (mnc != null)
|
||||||
String mnc=configuration.getInitParameter("maxNonceCount");
|
setMaxNonceCount(Integer.valueOf(mnc));
|
||||||
if (mnc!=null)
|
|
||||||
{
|
|
||||||
_maxNC=Integer.valueOf(mnc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public int getMaxNonceCount()
|
public int getMaxNonceCount()
|
||||||
{
|
{
|
||||||
return _maxNC;
|
return _maxNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public void setMaxNonceCount(int maxNC)
|
public void setMaxNonceCount(int maxNC)
|
||||||
{
|
{
|
||||||
_maxNC = maxNC;
|
_maxNC = maxNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public long getMaxNonceAge()
|
public long getMaxNonceAge()
|
||||||
{
|
{
|
||||||
return _maxNonceAgeMs;
|
return _maxNonceAgeMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
public void setMaxNonceAge(long maxNonceAgeInMillis)
|
||||||
public synchronized void setMaxNonceAge(long maxNonceAgeInMillis)
|
|
||||||
{
|
{
|
||||||
_maxNonceAgeMs = maxNonceAgeInMillis;
|
_maxNonceAgeMs = maxNonceAgeInMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public String getAuthMethod()
|
public String getAuthMethod()
|
||||||
{
|
{
|
||||||
return Constraint.__DIGEST_AUTH;
|
return Constraint.__DIGEST_AUTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException
|
public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
|
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
|
||||||
{
|
{
|
||||||
|
@ -217,20 +169,20 @@ public class DigestAuthenticator extends LoginAuthenticator
|
||||||
digest.uri = tok;
|
digest.uri = tok;
|
||||||
else if ("response".equalsIgnoreCase(name))
|
else if ("response".equalsIgnoreCase(name))
|
||||||
digest.response = tok;
|
digest.response = tok;
|
||||||
name=null;
|
name = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int n = checkNonce(digest,(Request)request);
|
int n = checkNonce(digest, (Request)request);
|
||||||
|
|
||||||
if (n > 0)
|
if (n > 0)
|
||||||
{
|
{
|
||||||
//UserIdentity user = _loginService.login(digest.username,digest);
|
//UserIdentity user = _loginService.login(digest.username,digest);
|
||||||
UserIdentity user = login(digest.username, digest, req);
|
UserIdentity user = login(digest.username, digest, req);
|
||||||
if (user!=null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
return new UserAuthentication(getAuthMethod(),user);
|
return new UserAuthentication(getAuthMethod(), user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (n == 0)
|
else if (n == 0)
|
||||||
|
@ -261,10 +213,17 @@ public class DigestAuthenticator extends LoginAuthenticator
|
||||||
{
|
{
|
||||||
throw new ServerAuthException(e);
|
throw new ServerAuthException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
@Override
|
||||||
|
public UserIdentity login(String username, Object credentials, ServletRequest request)
|
||||||
|
{
|
||||||
|
Digest digest = (Digest)credentials;
|
||||||
|
if (!Objects.equals(digest.realm, _loginService.getName()))
|
||||||
|
return null;
|
||||||
|
return super.login(username, credentials, request);
|
||||||
|
}
|
||||||
|
|
||||||
public String newNonce(Request request)
|
public String newNonce(Request request)
|
||||||
{
|
{
|
||||||
Nonce nonce;
|
Nonce nonce;
|
||||||
|
@ -274,41 +233,40 @@ public class DigestAuthenticator extends LoginAuthenticator
|
||||||
byte[] nounce = new byte[24];
|
byte[] nounce = new byte[24];
|
||||||
_random.nextBytes(nounce);
|
_random.nextBytes(nounce);
|
||||||
|
|
||||||
nonce = new Nonce(new String(B64Code.encode(nounce)),request.getTimeStamp(),_maxNC);
|
nonce = new Nonce(new String(B64Code.encode(nounce)), request.getTimeStamp(), getMaxNonceCount());
|
||||||
}
|
}
|
||||||
while (_nonceMap.putIfAbsent(nonce._nonce,nonce)!=null);
|
while (_nonceMap.putIfAbsent(nonce._nonce, nonce) != null);
|
||||||
_nonceQueue.add(nonce);
|
_nonceQueue.add(nonce);
|
||||||
|
|
||||||
return nonce._nonce;
|
return nonce._nonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param nstring nonce to check
|
* @param digest the digest data to check
|
||||||
* @param request
|
* @param request the request object
|
||||||
* @return -1 for a bad nonce, 0 for a stale none, 1 for a good nonce
|
* @return -1 for a bad nonce, 0 for a stale none, 1 for a good nonce
|
||||||
*/
|
*/
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
private int checkNonce(Digest digest, Request request)
|
private int checkNonce(Digest digest, Request request)
|
||||||
{
|
{
|
||||||
// firstly let's expire old nonces
|
// firstly let's expire old nonces
|
||||||
long expired = request.getTimeStamp()-_maxNonceAgeMs;
|
long expired = request.getTimeStamp() - getMaxNonceAge();
|
||||||
Nonce nonce=_nonceQueue.peek();
|
Nonce nonce = _nonceQueue.peek();
|
||||||
while (nonce!=null && nonce._ts<expired)
|
while (nonce != null && nonce._ts < expired)
|
||||||
{
|
{
|
||||||
_nonceQueue.remove(nonce);
|
_nonceQueue.remove(nonce);
|
||||||
_nonceMap.remove(nonce._nonce);
|
_nonceMap.remove(nonce._nonce);
|
||||||
nonce=_nonceQueue.peek();
|
nonce = _nonceQueue.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now check the requested nonce
|
// Now check the requested nonce
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
nonce = _nonceMap.get(digest.nonce);
|
nonce = _nonceMap.get(digest.nonce);
|
||||||
if (nonce==null)
|
if (nonce == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
long count = Long.parseLong(digest.nc,16);
|
long count = Long.parseLong(digest.nc, 16);
|
||||||
if (count>=_maxNC)
|
if (count >= _maxNC)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (nonce.seen((int)count))
|
if (nonce.seen((int)count))
|
||||||
|
@ -323,9 +281,32 @@ public class DigestAuthenticator extends LoginAuthenticator
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
private static class Nonce
|
||||||
/* ------------------------------------------------------------ */
|
{
|
||||||
/* ------------------------------------------------------------ */
|
final String _nonce;
|
||||||
|
final long _ts;
|
||||||
|
final BitSet _seen;
|
||||||
|
|
||||||
|
public Nonce(String nonce, long ts, int size)
|
||||||
|
{
|
||||||
|
_nonce = nonce;
|
||||||
|
_ts = ts;
|
||||||
|
_seen = new BitSet(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean seen(int count)
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
if (count >= _seen.size())
|
||||||
|
return true;
|
||||||
|
boolean s = _seen.get(count);
|
||||||
|
_seen.set(count);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class Digest extends Credential
|
private static class Digest extends Credential
|
||||||
{
|
{
|
||||||
private static final long serialVersionUID = -2484639019549527724L;
|
private static final long serialVersionUID = -2484639019549527724L;
|
||||||
|
@ -350,8 +331,8 @@ public class DigestAuthenticator extends LoginAuthenticator
|
||||||
public boolean check(Object credentials)
|
public boolean check(Object credentials)
|
||||||
{
|
{
|
||||||
if (credentials instanceof char[])
|
if (credentials instanceof char[])
|
||||||
credentials=new String((char[])credentials);
|
credentials = new String((char[])credentials);
|
||||||
String password = (credentials instanceof String) ? (String) credentials : credentials.toString();
|
String password = (credentials instanceof String) ? (String)credentials : credentials.toString();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -362,22 +343,22 @@ public class DigestAuthenticator extends LoginAuthenticator
|
||||||
// Credentials are already a MD5 digest - assume it's in
|
// Credentials are already a MD5 digest - assume it's in
|
||||||
// form user:realm:password (we have no way to know since
|
// form user:realm:password (we have no way to know since
|
||||||
// it's a digest, alright?)
|
// it's a digest, alright?)
|
||||||
ha1 = ((Credential.MD5) credentials).getDigest();
|
ha1 = ((Credential.MD5)credentials).getDigest();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// calc A1 digest
|
// calc A1 digest
|
||||||
md.update(username.getBytes(StandardCharsets.ISO_8859_1));
|
md.update(username.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
md.update((byte) ':');
|
md.update((byte)':');
|
||||||
md.update(realm.getBytes(StandardCharsets.ISO_8859_1));
|
md.update(realm.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
md.update((byte) ':');
|
md.update((byte)':');
|
||||||
md.update(password.getBytes(StandardCharsets.ISO_8859_1));
|
md.update(password.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
ha1 = md.digest();
|
ha1 = md.digest();
|
||||||
}
|
}
|
||||||
// calc A2 digest
|
// calc A2 digest
|
||||||
md.reset();
|
md.reset();
|
||||||
md.update(method.getBytes(StandardCharsets.ISO_8859_1));
|
md.update(method.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
md.update((byte) ':');
|
md.update((byte)':');
|
||||||
md.update(uri.getBytes(StandardCharsets.ISO_8859_1));
|
md.update(uri.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
byte[] ha2 = md.digest();
|
byte[] ha2 = md.digest();
|
||||||
|
|
||||||
|
@ -389,15 +370,15 @@ public class DigestAuthenticator extends LoginAuthenticator
|
||||||
// ) > <">
|
// ) > <">
|
||||||
|
|
||||||
md.update(TypeUtil.toString(ha1, 16).getBytes(StandardCharsets.ISO_8859_1));
|
md.update(TypeUtil.toString(ha1, 16).getBytes(StandardCharsets.ISO_8859_1));
|
||||||
md.update((byte) ':');
|
md.update((byte)':');
|
||||||
md.update(nonce.getBytes(StandardCharsets.ISO_8859_1));
|
md.update(nonce.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
md.update((byte) ':');
|
md.update((byte)':');
|
||||||
md.update(nc.getBytes(StandardCharsets.ISO_8859_1));
|
md.update(nc.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
md.update((byte) ':');
|
md.update((byte)':');
|
||||||
md.update(cnonce.getBytes(StandardCharsets.ISO_8859_1));
|
md.update(cnonce.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
md.update((byte) ':');
|
md.update((byte)':');
|
||||||
md.update(qop.getBytes(StandardCharsets.ISO_8859_1));
|
md.update(qop.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
md.update((byte) ':');
|
md.update((byte)':');
|
||||||
md.update(TypeUtil.toString(ha2, 16).getBytes(StandardCharsets.ISO_8859_1));
|
md.update(TypeUtil.toString(ha2, 16).getBytes(StandardCharsets.ISO_8859_1));
|
||||||
byte[] digest = md.digest();
|
byte[] digest = md.digest();
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler.Context;
|
import org.eclipse.jetty.server.handler.ContextHandler.Context;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
|
||||||
import org.eclipse.jetty.util.thread.Scheduler;
|
import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
|
|
||||||
public class AsyncContextEvent extends AsyncEvent implements Runnable
|
public class AsyncContextEvent extends AsyncEvent implements Runnable
|
||||||
|
@ -157,7 +156,7 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
|
||||||
Scheduler.Task task=_timeoutTask;
|
Scheduler.Task task=_timeoutTask;
|
||||||
_timeoutTask=null;
|
_timeoutTask=null;
|
||||||
if (task!=null)
|
if (task!=null)
|
||||||
_state.onTimeout();
|
_state.getHttpChannel().execute(() -> _state.onTimeout());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addThrowable(Throwable e)
|
public void addThrowable(Throwable e)
|
||||||
|
|
|
@ -402,10 +402,12 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
|
||||||
{
|
{
|
||||||
if (!_response.isCommitted() && !_request.isHandled())
|
if (!_response.isCommitted() && !_request.isHandled())
|
||||||
_response.sendError(HttpStatus.NOT_FOUND_404);
|
_response.sendError(HttpStatus.NOT_FOUND_404);
|
||||||
|
else if (!_response.isContentComplete(_response.getHttpOutput().getWritten()))
|
||||||
|
_transport.abort(new IOException("insufficient content written"));
|
||||||
_response.closeOutput();
|
_response.closeOutput();
|
||||||
_request.setHandled(true);
|
_request.setHandled(true);
|
||||||
|
|
||||||
_state.onComplete();
|
_state.onComplete();
|
||||||
|
|
||||||
onCompleted();
|
onCompleted();
|
||||||
|
|
||||||
|
@ -557,7 +559,7 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("REQUEST for {} on {}{}{} {} {}{}{}",request.getURIString(),this,System.lineSeparator(),
|
LOG.debug("REQUEST for {} on {}{}{} {} {}{}{}",request.getURIString(),this,System.lineSeparator(),
|
||||||
request.getMethod(),request.getURIString(),request.getVersion(),System.lineSeparator(),
|
request.getMethod(),request.getURIString(),request.getHttpVersion(),System.lineSeparator(),
|
||||||
request.getFields());
|
request.getFields());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,7 +705,7 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
|
||||||
_committedMetaData=info;
|
_committedMetaData=info;
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("COMMIT for {} on {}{}{} {} {}{}{}",getRequest().getRequestURI(),this,System.lineSeparator(),
|
LOG.debug("COMMIT for {} on {}{}{} {} {}{}{}",getRequest().getRequestURI(),this,System.lineSeparator(),
|
||||||
info.getStatus(),info.getReason(),info.getVersion(),System.lineSeparator(),
|
info.getStatus(),info.getReason(),info.getHttpVersion(),System.lineSeparator(),
|
||||||
info.getFields());
|
info.getFields());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,7 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
|
||||||
|
|
||||||
case EXPECT:
|
case EXPECT:
|
||||||
{
|
{
|
||||||
if (_metadata.getVersion() == HttpVersion.HTTP_1_1)
|
if (_metadata.getHttpVersion() == HttpVersion.HTTP_1_1)
|
||||||
{
|
{
|
||||||
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
|
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
|
||||||
switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
|
switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
|
||||||
|
@ -263,7 +263,7 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
|
||||||
|
|
||||||
boolean persistent;
|
boolean persistent;
|
||||||
|
|
||||||
switch (_metadata.getVersion())
|
switch (_metadata.getHttpVersion())
|
||||||
{
|
{
|
||||||
case HTTP_0_9:
|
case HTTP_0_9:
|
||||||
{
|
{
|
||||||
|
@ -347,7 +347,7 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
throw new IllegalStateException("unsupported version " + _metadata.getVersion());
|
throw new IllegalStateException("unsupported version " + _metadata.getHttpVersion());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,7 +299,7 @@ public class HttpChannelState
|
||||||
{
|
{
|
||||||
listener.onStartAsync(event);
|
listener.onStartAsync(event);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Throwable e)
|
||||||
{
|
{
|
||||||
// TODO Async Dispatch Error
|
// TODO Async Dispatch Error
|
||||||
LOG.warn(e);
|
LOG.warn(e);
|
||||||
|
@ -853,7 +853,7 @@ public class HttpChannelState
|
||||||
{
|
{
|
||||||
listener.onComplete(event);
|
listener.onComplete(event);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Throwable e)
|
||||||
{
|
{
|
||||||
LOG.warn(e+" while invoking onComplete listener " + listener);
|
LOG.warn(e+" while invoking onComplete listener " + listener);
|
||||||
LOG.debug(e);
|
LOG.debug(e);
|
||||||
|
@ -1008,6 +1008,14 @@ public class HttpChannelState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAsyncComplete()
|
||||||
|
{
|
||||||
|
try(Locker.Lock lock= _locker.lock())
|
||||||
|
{
|
||||||
|
return _async==Async.COMPLETE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAsync()
|
public boolean isAsync()
|
||||||
{
|
{
|
||||||
try(Locker.Lock lock= _locker.lock())
|
try(Locker.Lock lock= _locker.lock())
|
||||||
|
@ -1169,6 +1177,31 @@ public class HttpChannelState
|
||||||
return woken;
|
return woken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/** Called to signal that a read has read -1.
|
||||||
|
* Will wake if the read was called while in ASYNC_WAIT state
|
||||||
|
* @return true if woken
|
||||||
|
*/
|
||||||
|
public boolean onReadEof()
|
||||||
|
{
|
||||||
|
boolean woken=false;
|
||||||
|
try(Locker.Lock lock= _locker.lock())
|
||||||
|
{
|
||||||
|
if(DEBUG)
|
||||||
|
LOG.debug("onReadEof {}",toStringLocked());
|
||||||
|
|
||||||
|
if (_state==State.ASYNC_WAIT)
|
||||||
|
{
|
||||||
|
_state=State.ASYNC_WOKEN;
|
||||||
|
_asyncReadUnready=true;
|
||||||
|
_asyncReadPossible=true;
|
||||||
|
woken=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return woken;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isReadPossible()
|
public boolean isReadPossible()
|
||||||
{
|
{
|
||||||
try(Locker.Lock lock= _locker.lock())
|
try(Locker.Lock lock= _locker.lock())
|
||||||
|
|
|
@ -524,6 +524,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
@Override
|
@Override
|
||||||
public void abort(Throwable failure)
|
public void abort(Throwable failure)
|
||||||
{
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("abort {} {}",this,failure);
|
||||||
// Do a direct close of the output, as this may indicate to a client that the
|
// Do a direct close of the output, as this may indicate to a client that the
|
||||||
// response is bad either with RST or by abnormal completion of chunked response.
|
// response is bad either with RST or by abnormal completion of chunked response.
|
||||||
getEndPoint().close();
|
getEndPoint().close();
|
||||||
|
|
|
@ -119,8 +119,8 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static Logger LOG = Log.getLogger(HttpInput.class);
|
private final static Logger LOG = Log.getLogger(HttpInput.class);
|
||||||
private final static Content EOF_CONTENT = new EofContent("EOF");
|
final static Content EOF_CONTENT = new EofContent("EOF");
|
||||||
private final static Content EARLY_EOF_CONTENT = new EofContent("EARLY_EOF");
|
final static Content EARLY_EOF_CONTENT = new EofContent("EARLY_EOF");
|
||||||
|
|
||||||
private final byte[] _oneByteBuffer = new byte[1];
|
private final byte[] _oneByteBuffer = new byte[1];
|
||||||
private Content _content;
|
private Content _content;
|
||||||
|
@ -232,7 +232,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
return available;
|
return available;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void wake()
|
protected void wake()
|
||||||
{
|
{
|
||||||
HttpChannel channel = _channelState.getHttpChannel();
|
HttpChannel channel = _channelState.getHttpChannel();
|
||||||
Executor executor = channel.getConnector().getServer().getThreadPool();
|
Executor executor = channel.getConnector().getServer().getThreadPool();
|
||||||
|
@ -256,8 +256,11 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] b, int off, int len) throws IOException
|
public int read(byte[] b, int off, int len) throws IOException
|
||||||
{
|
{
|
||||||
|
boolean wake = false;
|
||||||
|
int l;
|
||||||
synchronized (_inputQ)
|
synchronized (_inputQ)
|
||||||
{
|
{
|
||||||
|
// Setup blocking only if not async
|
||||||
if (!isAsync())
|
if (!isAsync())
|
||||||
{
|
{
|
||||||
if (_blockUntil == 0)
|
if (_blockUntil == 0)
|
||||||
|
@ -268,6 +271,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Caclulate minimum request rate for DOS protection
|
||||||
long minRequestDataRate = _channelState.getHttpChannel().getHttpConfiguration().getMinRequestDataRate();
|
long minRequestDataRate = _channelState.getHttpChannel().getHttpConfiguration().getMinRequestDataRate();
|
||||||
if (minRequestDataRate > 0 && _firstByteTimeStamp != -1)
|
if (minRequestDataRate > 0 && _firstByteTimeStamp != -1)
|
||||||
{
|
{
|
||||||
|
@ -280,25 +284,39 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Consume content looking for bytes to read
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Content item = nextContent();
|
Content item = nextContent();
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
int l = get(item,b,off,len);
|
l = get(item,b,off,len);
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} read {} from {}",this,l,item);
|
LOG.debug("{} read {} from {}",this,l,item);
|
||||||
|
|
||||||
// Consume any following poison pills
|
// Consume any following poison pills
|
||||||
pollReadableContent();
|
if (item.isEmpty())
|
||||||
|
pollReadableContent();
|
||||||
return l;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No content, so should we block?
|
||||||
if (!_state.blockForContent(this))
|
if (!_state.blockForContent(this))
|
||||||
return _state.noContent();
|
{
|
||||||
|
// Not blocking, so what should we return?
|
||||||
|
l = _state.noContent();
|
||||||
|
|
||||||
|
// If EOF do we need to wake for allDataRead callback?
|
||||||
|
if (l<0)
|
||||||
|
wake = _channelState.onReadEof();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wake)
|
||||||
|
wake();
|
||||||
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -345,19 +363,14 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
// If it is EOF, consume it here
|
// If it is EOF, consume it here
|
||||||
if (content instanceof SentinelContent)
|
if (content instanceof SentinelContent)
|
||||||
{
|
{
|
||||||
if (content == EARLY_EOF_CONTENT)
|
if (content instanceof EofContent)
|
||||||
_state = EARLY_EOF;
|
|
||||||
else if (content instanceof EofContent)
|
|
||||||
{
|
{
|
||||||
if (_listener == null)
|
if (content == EARLY_EOF_CONTENT)
|
||||||
|
_state = EARLY_EOF;
|
||||||
|
else if (_listener == null)
|
||||||
_state = EOF;
|
_state = EOF;
|
||||||
else
|
else
|
||||||
{
|
|
||||||
_state = AEOF;
|
_state = AEOF;
|
||||||
boolean woken = _channelState.onReadReady(); // force callback?
|
|
||||||
if (woken)
|
|
||||||
wake();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume the EOF content, either if it was original content
|
// Consume the EOF content, either if it was original content
|
||||||
|
@ -732,17 +745,25 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
{
|
{
|
||||||
if (_listener != null)
|
if (_listener != null)
|
||||||
throw new IllegalStateException("ReadListener already set");
|
throw new IllegalStateException("ReadListener already set");
|
||||||
if (_state != STREAM)
|
|
||||||
throw new IllegalStateException("State " + STREAM + " != " + _state);
|
|
||||||
|
|
||||||
_state = ASYNC;
|
|
||||||
_listener = readListener;
|
_listener = readListener;
|
||||||
boolean content = nextContent() != null;
|
|
||||||
|
|
||||||
if (content)
|
Content content = nextReadable();
|
||||||
|
if (content!=null)
|
||||||
|
{
|
||||||
|
_state = ASYNC;
|
||||||
woken = _channelState.onReadReady();
|
woken = _channelState.onReadReady();
|
||||||
|
}
|
||||||
|
else if (_state == EOF)
|
||||||
|
{
|
||||||
|
_state = AEOF;
|
||||||
|
woken = _channelState.onReadReady();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
_state = ASYNC;
|
||||||
_channelState.onReadUnready();
|
_channelState.onReadUnready();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
|
@ -780,23 +801,49 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
final Throwable error;
|
|
||||||
final ReadListener listener;
|
final ReadListener listener;
|
||||||
|
Throwable error;
|
||||||
boolean aeof = false;
|
boolean aeof = false;
|
||||||
|
|
||||||
synchronized (_inputQ)
|
synchronized (_inputQ)
|
||||||
{
|
{
|
||||||
|
listener = _listener;
|
||||||
|
|
||||||
if (_state == EOF)
|
if (_state == EOF)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_state == AEOF)
|
if (_state==AEOF)
|
||||||
{
|
{
|
||||||
_state = EOF;
|
_state = EOF;
|
||||||
aeof = true;
|
aeof = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
listener = _listener;
|
error = _state.getError();
|
||||||
error = _state instanceof ErrorState?((ErrorState)_state).getError():null;
|
|
||||||
|
if (!aeof && error==null)
|
||||||
|
{
|
||||||
|
Content content = pollReadableContent();
|
||||||
|
|
||||||
|
// Consume EOF
|
||||||
|
if (content instanceof EofContent)
|
||||||
|
{
|
||||||
|
content.succeeded();
|
||||||
|
if (_content==content)
|
||||||
|
_content = null;
|
||||||
|
if (content == EARLY_EOF_CONTENT)
|
||||||
|
{
|
||||||
|
_state = EARLY_EOF;
|
||||||
|
error = _state.getError();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_state = EOF;
|
||||||
|
aeof = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (content==null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -813,6 +860,16 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listener.onDataAvailable();
|
listener.onDataAvailable();
|
||||||
|
synchronized (_inputQ)
|
||||||
|
{
|
||||||
|
if (_state == AEOF)
|
||||||
|
{
|
||||||
|
_state = EOF;
|
||||||
|
aeof = !_channelState.isAsyncComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (aeof)
|
||||||
|
listener.onAllDataRead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
|
@ -956,6 +1013,11 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Throwable getError()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class EOFState extends State
|
protected static class EOFState extends State
|
||||||
|
@ -1027,7 +1089,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
@Override
|
@Override
|
||||||
public int noContent() throws IOException
|
public int noContent() throws IOException
|
||||||
{
|
{
|
||||||
throw new EofException("Early EOF");
|
throw getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1035,6 +1097,11 @@ public class HttpInput extends ServletInputStream implements Runnable
|
||||||
{
|
{
|
||||||
return "EARLY_EOF";
|
return "EARLY_EOF";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IOException getError()
|
||||||
|
{
|
||||||
|
return new EofException("Early EOF");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected static final State EOF = new EOFState()
|
protected static final State EOF = new EOFState()
|
||||||
|
|
|
@ -685,6 +685,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("sendContent({})", BufferUtil.toDetailString(content));
|
LOG.debug("sendContent({})", BufferUtil.toDetailString(content));
|
||||||
|
|
||||||
|
_written += content.remaining();
|
||||||
write(content, true);
|
write(content, true);
|
||||||
closed();
|
closed();
|
||||||
}
|
}
|
||||||
|
@ -766,6 +767,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("sendContent(buffer={},{})", BufferUtil.toDetailString(content), callback);
|
LOG.debug("sendContent(buffer={},{})", BufferUtil.toDetailString(content), callback);
|
||||||
|
|
||||||
|
_written += content.remaining();
|
||||||
write(content, true, new Callback.Nested(callback)
|
write(content, true, new Callback.Nested(callback)
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -1280,6 +1282,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
// write what we have
|
// write what we have
|
||||||
_buffer.position(0);
|
_buffer.position(0);
|
||||||
_buffer.limit(len);
|
_buffer.limit(len);
|
||||||
|
_written += len;
|
||||||
write(_buffer, _eof, this);
|
write(_buffer, _eof, this);
|
||||||
return Action.SCHEDULED;
|
return Action.SCHEDULED;
|
||||||
}
|
}
|
||||||
|
@ -1338,6 +1341,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
|
|
||||||
// write what we have
|
// write what we have
|
||||||
BufferUtil.flipToFlush(_buffer, 0);
|
BufferUtil.flipToFlush(_buffer, 0);
|
||||||
|
_written += _buffer.remaining();
|
||||||
write(_buffer, _eof, this);
|
write(_buffer, _eof, this);
|
||||||
|
|
||||||
return Action.SCHEDULED;
|
return Action.SCHEDULED;
|
||||||
|
|
|
@ -83,7 +83,6 @@ import org.eclipse.jetty.server.session.Session;
|
||||||
import org.eclipse.jetty.server.session.SessionHandler;
|
import org.eclipse.jetty.server.session.SessionHandler;
|
||||||
import org.eclipse.jetty.util.Attributes;
|
import org.eclipse.jetty.util.Attributes;
|
||||||
import org.eclipse.jetty.util.AttributesMap;
|
import org.eclipse.jetty.util.AttributesMap;
|
||||||
import org.eclipse.jetty.util.HostPort;
|
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.eclipse.jetty.util.MultiMap;
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
import org.eclipse.jetty.util.MultiPartInputStreamParser;
|
import org.eclipse.jetty.util.MultiPartInputStreamParser;
|
||||||
|
@ -1057,7 +1056,7 @@ public class Request implements HttpServletRequest
|
||||||
MetaData.Request metadata = _metaData;
|
MetaData.Request metadata = _metaData;
|
||||||
if (metadata==null)
|
if (metadata==null)
|
||||||
return null;
|
return null;
|
||||||
HttpVersion version = metadata.getVersion();
|
HttpVersion version = metadata.getHttpVersion();
|
||||||
if (version==null)
|
if (version==null)
|
||||||
return null;
|
return null;
|
||||||
return version.toString();
|
return version.toString();
|
||||||
|
@ -1070,7 +1069,7 @@ public class Request implements HttpServletRequest
|
||||||
public HttpVersion getHttpVersion()
|
public HttpVersion getHttpVersion()
|
||||||
{
|
{
|
||||||
MetaData.Request metadata = _metaData;
|
MetaData.Request metadata = _metaData;
|
||||||
return metadata==null?null:metadata.getVersion();
|
return metadata==null?null:metadata.getHttpVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -2000,6 +1999,13 @@ public class Request implements HttpServletRequest
|
||||||
metadata.setMethod(method);
|
metadata.setMethod(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHttpVersion(HttpVersion version)
|
||||||
|
{
|
||||||
|
MetaData.Request metadata = _metaData;
|
||||||
|
if (metadata!=null)
|
||||||
|
metadata.setHttpVersion(version);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public boolean isHead()
|
public boolean isHead()
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,12 +21,9 @@ package org.eclipse.jetty.server;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.channels.IllegalSelectorException;
|
import java.nio.channels.IllegalSelectorException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
@ -254,9 +251,6 @@ public class Response implements HttpServletResponse
|
||||||
|
|
||||||
buf.append('=');
|
buf.append('=');
|
||||||
|
|
||||||
// Remember name= part to look for other matching set-cookie
|
|
||||||
String name_equals=buf.toString();
|
|
||||||
|
|
||||||
// Append the value
|
// Append the value
|
||||||
boolean quote_value=isQuoteNeededForCookie(value);
|
boolean quote_value=isQuoteNeededForCookie(value);
|
||||||
quoteOnlyOrAppend(buf,value,quote_value);
|
quoteOnlyOrAppend(buf,value,quote_value);
|
||||||
|
@ -879,13 +873,13 @@ public class Response implements HttpServletResponse
|
||||||
if (isCommitted() || isIncluding())
|
if (isCommitted() || isIncluding())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_contentLength = len;
|
if (len>0)
|
||||||
if (_contentLength > 0)
|
|
||||||
{
|
{
|
||||||
long written = _out.getWritten();
|
long written = _out.getWritten();
|
||||||
if (written > len)
|
if (written > len)
|
||||||
throw new IllegalArgumentException("setContentLength(" + len + ") when already written " + written);
|
throw new IllegalArgumentException("setContentLength(" + len + ") when already written " + written);
|
||||||
|
|
||||||
|
_contentLength = len;
|
||||||
_fields.putLongField(HttpHeader.CONTENT_LENGTH, len);
|
_fields.putLongField(HttpHeader.CONTENT_LENGTH, len);
|
||||||
if (isAllContentWritten(written))
|
if (isAllContentWritten(written))
|
||||||
{
|
{
|
||||||
|
@ -899,15 +893,19 @@ public class Response implements HttpServletResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_contentLength==0)
|
else if (len==0)
|
||||||
{
|
{
|
||||||
long written = _out.getWritten();
|
long written = _out.getWritten();
|
||||||
if (written > 0)
|
if (written > 0)
|
||||||
throw new IllegalArgumentException("setContentLength(0) when already written " + written);
|
throw new IllegalArgumentException("setContentLength(0) when already written " + written);
|
||||||
|
_contentLength = len;
|
||||||
_fields.put(HttpHeader.CONTENT_LENGTH, "0");
|
_fields.put(HttpHeader.CONTENT_LENGTH, "0");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
_contentLength = len;
|
||||||
_fields.remove(HttpHeader.CONTENT_LENGTH);
|
_fields.remove(HttpHeader.CONTENT_LENGTH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getContentLength()
|
public long getContentLength()
|
||||||
|
@ -920,6 +918,11 @@ public class Response implements HttpServletResponse
|
||||||
return (_contentLength >= 0 && written >= _contentLength);
|
return (_contentLength >= 0 && written >= _contentLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isContentComplete(long written)
|
||||||
|
{
|
||||||
|
return (_contentLength < 0 || written >= _contentLength);
|
||||||
|
}
|
||||||
|
|
||||||
public void closeOutput() throws IOException
|
public void closeOutput() throws IOException
|
||||||
{
|
{
|
||||||
switch (_outputType)
|
switch (_outputType)
|
||||||
|
@ -1094,15 +1097,14 @@ public class Response implements HttpServletResponse
|
||||||
_fields.put(_mimeType.getContentTypeField());
|
_fields.put(_mimeType.getContentTypeField());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBufferSize(int size)
|
public void setBufferSize(int size)
|
||||||
{
|
{
|
||||||
if (isCommitted() || getContentCount() > 0)
|
if (isCommitted() || getContentCount() > 0)
|
||||||
throw new IllegalStateException("cannot set buffer size on committed response");
|
throw new IllegalStateException("cannot set buffer size when response is committed or written to");
|
||||||
if (size <= 0)
|
if (size < __MIN_BUFFER_SIZE)
|
||||||
size = __MIN_BUFFER_SIZE;
|
size = __MIN_BUFFER_SIZE;
|
||||||
_out.setBufferSize(size);
|
_out.setBufferSize(size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.util.Date;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -376,22 +377,35 @@ public class Server extends HandlerWrapper implements Attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
|
HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
|
||||||
MultiException mex=new MultiException();
|
|
||||||
|
|
||||||
// check size of thread pool
|
// Check that the thread pool size is enough.
|
||||||
SizedThreadPool pool = getBean(SizedThreadPool.class);
|
SizedThreadPool pool = getBean(SizedThreadPool.class);
|
||||||
int max=pool==null?-1:pool.getMaxThreads();
|
int max=pool==null?-1:pool.getMaxThreads();
|
||||||
int selectors=0;
|
int selectors=0;
|
||||||
int acceptors=0;
|
int acceptors=0;
|
||||||
if (mex.size()==0)
|
|
||||||
|
for (Connector connector : _connectors)
|
||||||
{
|
{
|
||||||
for (Connector connector : _connectors)
|
if (connector instanceof AbstractConnector)
|
||||||
{
|
{
|
||||||
if (connector instanceof AbstractConnector)
|
AbstractConnector abstractConnector = (AbstractConnector)connector;
|
||||||
acceptors+=((AbstractConnector)connector).getAcceptors();
|
Executor connectorExecutor = connector.getExecutor();
|
||||||
|
|
||||||
|
if (connectorExecutor != pool)
|
||||||
|
{
|
||||||
|
// Do not count the selectors and acceptors from this connector at
|
||||||
|
// the server level, because the connector uses a dedicated executor.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptors += abstractConnector.getAcceptors();
|
||||||
|
|
||||||
if (connector instanceof ServerConnector)
|
if (connector instanceof ServerConnector)
|
||||||
selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
|
{
|
||||||
|
// The SelectorManager uses 2 threads for each selector,
|
||||||
|
// one for the normal and one for the low priority strategies.
|
||||||
|
selectors += 2 * ((ServerConnector)connector).getSelectorManager().getSelectorCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,6 +413,7 @@ public class Server extends HandlerWrapper implements Attributes
|
||||||
if (max>0 && needed>max)
|
if (max>0 && needed>max)
|
||||||
throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d + selectors=%d + request=1)",max,acceptors,selectors));
|
throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d + selectors=%d + request=1)",max,acceptors,selectors));
|
||||||
|
|
||||||
|
MultiException mex=new MultiException();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
super.doStart();
|
super.doStart();
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Set;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.session.SessionHandler;
|
import org.eclipse.jetty.server.session.SessionHandler;
|
||||||
|
import org.eclipse.jetty.server.session.HouseKeeper;
|
||||||
import org.eclipse.jetty.util.component.LifeCycle;
|
import org.eclipse.jetty.util.component.LifeCycle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,4 +115,16 @@ public interface SessionIdManager extends LifeCycle
|
||||||
* @return the set of session handlers
|
* @return the set of session handlers
|
||||||
*/
|
*/
|
||||||
public Set<SessionHandler> getSessionHandlers();
|
public Set<SessionHandler> getSessionHandlers();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param houseKeeper the housekeeper for doing scavenging
|
||||||
|
*/
|
||||||
|
public void setSessionHouseKeeper (HouseKeeper houseKeeper);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the housekeeper for doing scavenging
|
||||||
|
*/
|
||||||
|
public HouseKeeper getSessionHouseKeeper();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,16 +28,27 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Handler;
|
import org.eclipse.jetty.server.Handler;
|
||||||
|
import org.eclipse.jetty.server.HttpChannel;
|
||||||
|
import org.eclipse.jetty.server.HttpConnection;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.server.Response;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||||
|
import org.eclipse.jetty.util.component.Dumpable;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/** AbstractHandler.
|
/** AbstractHandler.
|
||||||
|
* <p>A convenience implementation of {@link Handler} that uses the
|
||||||
|
* {@link ContainerLifeCycle} to provide:<ul>
|
||||||
|
* <li>start/stop behavior
|
||||||
|
* <li>a bean container
|
||||||
|
* <li>basic {@link Dumpable} support
|
||||||
|
* <li>a {@link Server} reference
|
||||||
|
* <li>optional error dispatch handling
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@ManagedObject("Jetty Handler")
|
@ManagedObject("Jetty Handler")
|
||||||
public abstract class AbstractHandler extends ContainerLifeCycle implements Handler
|
public abstract class AbstractHandler extends ContainerLifeCycle implements Handler
|
||||||
|
@ -46,7 +57,6 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand
|
||||||
|
|
||||||
private Server _server;
|
private Server _server;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -55,20 +65,31 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public abstract void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
|
||||||
{
|
|
||||||
if (baseRequest.getDispatcherType()==DispatcherType.ERROR)
|
|
||||||
doError(target,baseRequest,request,response);
|
|
||||||
else
|
|
||||||
doHandle(target,baseRequest,request,response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/**
|
||||||
protected void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
* Convenience method to generate error page.
|
||||||
{
|
* <p>This method can be called from {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when an {@link DispatcherType#ERROR} dispatch
|
||||||
}
|
* is detected and an error page needs to be generated by calling {@link HttpServletResponse#sendError(int, String)} with the appropriate code and reason,
|
||||||
|
* which are taken from {@link HttpServletRequest#getAttribute(String)} for {@link RequestDispatcher#ERROR_STATUS_CODE} and {@link RequestDispatcher#ERROR_MESSAGE}
|
||||||
/* ------------------------------------------------------------ */
|
* @see ErrorDispatchHandler for a conveniance class that calls this method.
|
||||||
|
* @param target
|
||||||
|
* The target of the request - either a URI or a name.
|
||||||
|
* @param baseRequest
|
||||||
|
* The original unwrapped request object.
|
||||||
|
* @param request
|
||||||
|
* The request either as the {@link Request} object or a wrapper of that request. The
|
||||||
|
* <code>{@link HttpConnection#getCurrentConnection()}.{@link HttpConnection#getHttpChannel() getHttpChannel()}.{@link HttpChannel#getRequest() getRequest()}</code>
|
||||||
|
* method can be used access the Request object if required.
|
||||||
|
* @param response
|
||||||
|
* The response as the {@link Response} object or a wrapper of that request. The
|
||||||
|
* <code>{@link HttpConnection#getCurrentConnection()}.{@link HttpConnection#getHttpChannel() getHttpChannel()}.{@link HttpChannel#getResponse() getResponse()}</code>
|
||||||
|
* method can be used access the Response object if required.
|
||||||
|
* @throws IOException
|
||||||
|
* if unable to handle the request or response processing
|
||||||
|
* @throws ServletException
|
||||||
|
* if unable to handle the request or response due to underlying servlet issue
|
||||||
|
*/
|
||||||
protected void doError(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
protected void doError(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
Object o = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
|
Object o = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
|
||||||
|
@ -79,7 +100,6 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand
|
||||||
response.sendError(code,reason);
|
response.sendError(code,reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/*
|
/*
|
||||||
* @see org.eclipse.thread.LifeCycle#start()
|
* @see org.eclipse.thread.LifeCycle#start()
|
||||||
*/
|
*/
|
||||||
|
@ -93,7 +113,6 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand
|
||||||
super.doStart();
|
super.doStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/*
|
/*
|
||||||
* @see org.eclipse.thread.LifeCycle#stop()
|
* @see org.eclipse.thread.LifeCycle#stop()
|
||||||
*/
|
*/
|
||||||
|
@ -105,7 +124,6 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand
|
||||||
super.doStop();
|
super.doStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public void setServer(Server server)
|
public void setServer(Server server)
|
||||||
{
|
{
|
||||||
|
@ -116,14 +134,12 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand
|
||||||
_server=server;
|
_server=server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public Server getServer()
|
public Server getServer()
|
||||||
{
|
{
|
||||||
return _server;
|
return _server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy()
|
public void destroy()
|
||||||
{
|
{
|
||||||
|
@ -132,11 +148,52 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand
|
||||||
super.destroy();
|
super.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public void dumpThis(Appendable out) throws IOException
|
public void dumpThis(Appendable out) throws IOException
|
||||||
{
|
{
|
||||||
out.append(toString()).append(" - ").append(getState()).append('\n');
|
out.append(toString()).append(" - ").append(getState()).append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension of AbstractHandler that handles {@link DispatcherType#ERROR} dispatches.
|
||||||
|
* <p>
|
||||||
|
* {@link DispatcherType#ERROR} dispatches are handled by calling the {@link #doError(String, Request, HttpServletRequest, HttpServletResponse)}
|
||||||
|
* method. All other dispatches are passed to the abstract {@link #doNonErrorHandle(String, Request, HttpServletRequest, HttpServletResponse)}
|
||||||
|
* method, which should be implemented with specific handler behavior
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static abstract class ErrorDispatchHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public final void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
if (baseRequest.getDispatcherType()==DispatcherType.ERROR)
|
||||||
|
doError(target,baseRequest,request,response);
|
||||||
|
else
|
||||||
|
doNonErrorHandle(target,baseRequest,request,response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}
|
||||||
|
* for all non-{@link DispatcherType#ERROR} dispatches.
|
||||||
|
* @param target
|
||||||
|
* The target of the request - either a URI or a name.
|
||||||
|
* @param baseRequest
|
||||||
|
* The original unwrapped request object.
|
||||||
|
* @param request
|
||||||
|
* The request either as the {@link Request} object or a wrapper of that request. The
|
||||||
|
* <code>{@link HttpConnection#getCurrentConnection()}.{@link HttpConnection#getHttpChannel() getHttpChannel()}.{@link HttpChannel#getRequest() getRequest()}</code>
|
||||||
|
* method can be used access the Request object if required.
|
||||||
|
* @param response
|
||||||
|
* The response as the {@link Response} object or a wrapper of that request. The
|
||||||
|
* <code>{@link HttpConnection#getCurrentConnection()}.{@link HttpConnection#getHttpChannel() getHttpChannel()}.{@link HttpChannel#getResponse() getResponse()}</code>
|
||||||
|
* method can be used access the Response object if required.
|
||||||
|
* @throws IOException
|
||||||
|
* if unable to handle the request or response processing
|
||||||
|
* @throws ServletException
|
||||||
|
* if unable to handle the request or response due to underlying servlet issue
|
||||||
|
*/
|
||||||
|
protected abstract void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1127,16 +1127,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
|
LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
|
||||||
|
|
||||||
// start manual inline of nextScope(target,baseRequest,request,response);
|
nextScope(target,baseRequest,request,response);
|
||||||
if (never())
|
|
||||||
nextScope(target,baseRequest,request,response);
|
|
||||||
else if (_nextScope != null)
|
|
||||||
_nextScope.doScope(target,baseRequest,request,response);
|
|
||||||
else if (_outerScope != null)
|
|
||||||
_outerScope.doHandle(target,baseRequest,request,response);
|
|
||||||
else
|
|
||||||
doHandle(target,baseRequest,request,response);
|
|
||||||
// end manual inline (pathentic attempt to reduce stack depth)
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -1160,6 +1151,40 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
protected void requestInitialized(Request baseRequest, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
// Handle the REALLY SILLY request events!
|
||||||
|
if (!_servletRequestAttributeListeners.isEmpty())
|
||||||
|
for (ServletRequestAttributeListener l :_servletRequestAttributeListeners)
|
||||||
|
baseRequest.addEventListener(l);
|
||||||
|
|
||||||
|
if (!_servletRequestListeners.isEmpty())
|
||||||
|
{
|
||||||
|
final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
|
||||||
|
for (ServletRequestListener l : _servletRequestListeners)
|
||||||
|
l.requestInitialized(sre);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
protected void requestDestroyed(Request baseRequest, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
// Handle more REALLY SILLY request events!
|
||||||
|
if (!_servletRequestListeners.isEmpty())
|
||||||
|
{
|
||||||
|
final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
|
||||||
|
for (int i=_servletRequestListeners.size();i-->0;)
|
||||||
|
_servletRequestListeners.get(i).requestDestroyed(sre);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_servletRequestAttributeListeners.isEmpty())
|
||||||
|
{
|
||||||
|
for (int i=_servletRequestAttributeListeners.size();i-->0;)
|
||||||
|
baseRequest.removeEventListener(_servletRequestAttributeListeners.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
|
* @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
|
||||||
|
@ -1173,19 +1198,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (new_context)
|
if (new_context)
|
||||||
{
|
requestInitialized(baseRequest,request);
|
||||||
// Handle the REALLY SILLY request events!
|
|
||||||
if (!_servletRequestAttributeListeners.isEmpty())
|
|
||||||
for (ServletRequestAttributeListener l :_servletRequestAttributeListeners)
|
|
||||||
baseRequest.addEventListener(l);
|
|
||||||
|
|
||||||
if (!_servletRequestListeners.isEmpty())
|
|
||||||
{
|
|
||||||
final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
|
|
||||||
for (ServletRequestListener l : _servletRequestListeners)
|
|
||||||
l.requestInitialized(sre);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(dispatch)
|
switch(dispatch)
|
||||||
{
|
{
|
||||||
|
@ -1203,50 +1216,24 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
if (Boolean.TRUE.equals(baseRequest.getAttribute(Dispatcher.__ERROR_DISPATCH)))
|
if (Boolean.TRUE.equals(baseRequest.getAttribute(Dispatcher.__ERROR_DISPATCH)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
Object error = request.getAttribute(Dispatcher.ERROR_STATUS_CODE);
|
// We can just call doError here. If there is no error page, then one will
|
||||||
// We can just call sendError here. If there is no error page, then one will
|
|
||||||
// be generated. If there is an error page, then a RequestDispatcher will be
|
// be generated. If there is an error page, then a RequestDispatcher will be
|
||||||
// used to route the request through appropriate filters etc.
|
// used to route the request through appropriate filters etc.
|
||||||
response.sendError((error instanceof Integer)?((Integer)error).intValue():500);
|
doError(target,baseRequest,request,response);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// start manual inline of nextHandle(target,baseRequest,request,response);
|
nextHandle(target,baseRequest,request,response);
|
||||||
// noinspection ConstantIfStatement
|
|
||||||
if (never())
|
|
||||||
nextHandle(target,baseRequest,request,response);
|
|
||||||
else if (_nextScope != null && _nextScope == _handler)
|
|
||||||
_nextScope.doHandle(target,baseRequest,request,response);
|
|
||||||
else if (_handler != null)
|
|
||||||
_handler.handle(target,baseRequest,request,response);
|
|
||||||
// end manual inline
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// Handle more REALLY SILLY request events!
|
|
||||||
if (new_context)
|
if (new_context)
|
||||||
{
|
requestDestroyed(baseRequest,request);
|
||||||
if (!_servletRequestListeners.isEmpty())
|
|
||||||
{
|
|
||||||
final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
|
|
||||||
for (int i=_servletRequestListeners.size();i-->0;)
|
|
||||||
_servletRequestListeners.get(i).requestDestroyed(sre);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_servletRequestAttributeListeners.isEmpty())
|
|
||||||
{
|
|
||||||
for (int i=_servletRequestAttributeListeners.size();i-->0;)
|
|
||||||
baseRequest.removeEventListener(_servletRequestAttributeListeners.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param request A request that is applicable to the scope, or null
|
* @param request A request that is applicable to the scope, or null
|
||||||
* @param reason An object that indicates the reason the scope is being entered.
|
* @param reason An object that indicates the reason the scope is being entered.
|
||||||
|
|
|
@ -74,6 +74,12 @@ public class ErrorHandler extends AbstractHandler
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
|
{
|
||||||
|
doError(target,baseRequest,request,response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doError(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
{
|
{
|
||||||
String method = request.getMethod();
|
String method = request.getMethod();
|
||||||
if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method) && !HttpMethod.HEAD.is(method))
|
if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method) && !HttpMethod.HEAD.is(method))
|
||||||
|
|
|
@ -35,94 +35,79 @@ import org.eclipse.jetty.util.InetAddressSet;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inet Address Access Handler
|
* InetAddress Access Handler
|
||||||
* <p>
|
* <p>
|
||||||
* Controls access to the wrapped handler by the real remote IP. Control is provided
|
* Controls access to the wrapped handler using the real remote IP. Control is provided
|
||||||
* by and {@link IncludeExcludeSet} over a {@link InetAddressSet}. This handler
|
* by and {@link IncludeExcludeSet} over a {@link InetAddressSet}. This handler
|
||||||
* uses the real internet address of the connection, not one reported in the forwarded
|
* uses the real internet address of the connection, not one reported in the forwarded
|
||||||
* for headers, as this cannot be as easily forged.
|
* for headers, as this cannot be as easily forged.
|
||||||
* <p>
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
public class InetAccessHandler extends HandlerWrapper
|
public class InetAccessHandler extends HandlerWrapper
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(InetAccessHandler.class);
|
private static final Logger LOG = Log.getLogger(InetAccessHandler.class);
|
||||||
IncludeExcludeSet<String, InetAddress> _set = new IncludeExcludeSet<>(InetAddressSet.class);
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
private final IncludeExcludeSet<String, InetAddress> _set = new IncludeExcludeSet<>(InetAddressSet.class);
|
||||||
/**
|
|
||||||
* Creates new handler object
|
|
||||||
*/
|
|
||||||
public InetAccessHandler()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
/**
|
||||||
* Include a InetAddress pattern
|
* Includes an InetAddress pattern
|
||||||
|
*
|
||||||
|
* @param pattern InetAddress pattern to include
|
||||||
* @see InetAddressSet
|
* @see InetAddressSet
|
||||||
* @param pattern InetAddress pattern to exclude
|
|
||||||
*/
|
*/
|
||||||
public void include(String pattern)
|
public void include(String pattern)
|
||||||
{
|
{
|
||||||
_set.include(pattern);
|
_set.include(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
/**
|
||||||
* Include a InetAddress pattern
|
* Includes InetAddress patterns
|
||||||
|
*
|
||||||
|
* @param patterns InetAddress patterns to include
|
||||||
* @see InetAddressSet
|
* @see InetAddressSet
|
||||||
* @param patterns InetAddress patterns to exclude
|
|
||||||
*/
|
*/
|
||||||
public void include(String... patterns)
|
public void include(String... patterns)
|
||||||
{
|
{
|
||||||
_set.include(patterns);
|
_set.include(patterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
/**
|
||||||
* Exclude a InetAddress pattern
|
* Excludes an InetAddress pattern
|
||||||
* @see InetAddressSet
|
*
|
||||||
* @param pattern InetAddress pattern to exclude
|
* @param pattern InetAddress pattern to exclude
|
||||||
|
* @see InetAddressSet
|
||||||
*/
|
*/
|
||||||
public void exclude(String pattern)
|
public void exclude(String pattern)
|
||||||
{
|
{
|
||||||
_set.exclude(pattern);
|
_set.exclude(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
/**
|
||||||
* Include a InetAddress pattern
|
* Excludes InetAddress patterns
|
||||||
* @see InetAddressSet
|
*
|
||||||
* @param patterns InetAddress patterns to exclude
|
* @param patterns InetAddress patterns to exclude
|
||||||
|
* @see InetAddressSet
|
||||||
*/
|
*/
|
||||||
public void exclude(String... patterns)
|
public void exclude(String... patterns)
|
||||||
{
|
{
|
||||||
_set.exclude(patterns);
|
_set.exclude(patterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
/**
|
||||||
* Checks the incoming request against the whitelist and blacklist
|
* Checks the incoming request against the whitelist and blacklist
|
||||||
*
|
|
||||||
* @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
// Get the real remote IP (not the one set by the forwarded headers (which may be forged))
|
// Get the real remote IP (not the one set by the forwarded headers (which may be forged))
|
||||||
HttpChannel channel = baseRequest.getHttpChannel();
|
HttpChannel channel = baseRequest.getHttpChannel();
|
||||||
if (channel!=null)
|
if (channel != null)
|
||||||
{
|
{
|
||||||
EndPoint endp=channel.getEndPoint();
|
EndPoint endp = channel.getEndPoint();
|
||||||
if (endp!=null)
|
if (endp != null)
|
||||||
{
|
{
|
||||||
InetSocketAddress address = endp.getRemoteAddress();
|
InetSocketAddress address = endp.getRemoteAddress();
|
||||||
if (address!=null && !isAllowed(address.getAddress()))
|
if (address != null && !isAllowed(address.getAddress(), request))
|
||||||
{
|
{
|
||||||
response.sendError(HttpStatus.FORBIDDEN_403);
|
response.sendError(HttpStatus.FORBIDDEN_403);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
|
@ -131,26 +116,27 @@ public class InetAccessHandler extends HandlerWrapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getHandler().handle(target,baseRequest, request, response);
|
getHandler().handle(target, baseRequest, request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
/**
|
||||||
* Check if specified request is allowed by current IPAccess rules.
|
* Checks if specified address and request are allowed by current InetAddress rules.
|
||||||
*
|
|
||||||
* @param address internet address
|
|
||||||
* @return true if address is allowed
|
|
||||||
*
|
*
|
||||||
|
* @param address the inetAddress to check
|
||||||
|
* @param request the request to check
|
||||||
|
* @return true if inetAddress and request are allowed
|
||||||
*/
|
*/
|
||||||
protected boolean isAllowed(InetAddress address)
|
protected boolean isAllowed(InetAddress address, HttpServletRequest request)
|
||||||
{
|
{
|
||||||
return _set.test(address);
|
boolean allowed = _set.test(address);
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("{} {} {} for {}", this, allowed ? "allowed" : "denied", address, request);
|
||||||
|
return allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public void dump(Appendable out, String indent) throws IOException
|
public void dump(Appendable out, String indent) throws IOException
|
||||||
{
|
{
|
||||||
dumpBeans(out,indent,_set.getIncluded(),_set.getExcluded());
|
dumpBeans(out, indent, _set.getIncluded(), _set.getExcluded());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ public abstract class ScopedHandler extends HandlerWrapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/** ------------------------------------------------------------ */
|
||||||
/*
|
/*
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -145,22 +145,23 @@ public abstract class ScopedHandler extends HandlerWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/**
|
||||||
* Scope the handler
|
* Scope the handler
|
||||||
|
* <p>Derived implementations should call {@link #nextScope(String, Request, HttpServletRequest, HttpServletResponse)}
|
||||||
*/
|
*/
|
||||||
public abstract void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
throws IOException, ServletException;
|
throws IOException, ServletException
|
||||||
|
{
|
||||||
|
nextScope(target,baseRequest,request,response);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/**
|
||||||
* Scope the handler
|
* Scope the handler
|
||||||
*/
|
*/
|
||||||
public final void nextScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
public final void nextScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
throws IOException, ServletException
|
throws IOException, ServletException
|
||||||
{
|
{
|
||||||
// this method has been manually inlined in several locations, but
|
|
||||||
// is called protected by an if(never()), so your IDE can find those
|
|
||||||
// locations if this code is changed.
|
|
||||||
if (_nextScope!=null)
|
if (_nextScope!=null)
|
||||||
_nextScope.doScope(target,baseRequest,request, response);
|
_nextScope.doScope(target,baseRequest,request, response);
|
||||||
else if (_outerScope!=null)
|
else if (_outerScope!=null)
|
||||||
|
@ -170,8 +171,9 @@ public abstract class ScopedHandler extends HandlerWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/**
|
||||||
* Do the handler work within the scope.
|
* Do the handler work within the scope.
|
||||||
|
* <p>Derived implementations should call {@link #nextHandle(String, Request, HttpServletRequest, HttpServletResponse)}
|
||||||
*/
|
*/
|
||||||
public abstract void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
public abstract void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
throws IOException, ServletException;
|
throws IOException, ServletException;
|
||||||
|
@ -182,19 +184,9 @@ public abstract class ScopedHandler extends HandlerWrapper
|
||||||
*/
|
*/
|
||||||
public final void nextHandle(String target, final Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public final void nextHandle(String target, final Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
// this method has been manually inlined in several locations, but
|
|
||||||
// is called protected by an if(never()), so your IDE can find those
|
|
||||||
// locations if this code is changed.
|
|
||||||
if (_nextScope!=null && _nextScope==_handler)
|
if (_nextScope!=null && _nextScope==_handler)
|
||||||
_nextScope.doHandle(target,baseRequest,request, response);
|
_nextScope.doHandle(target,baseRequest,request, response);
|
||||||
else if (_handler!=null)
|
else if (_handler!=null)
|
||||||
_handler.handle(target,baseRequest, request, response);
|
super.handle(target,baseRequest,request,response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
protected boolean never()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,9 @@ package org.eclipse.jetty.server.session;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CachingSessionDataStore
|
* CachingSessionDataStore
|
||||||
|
@ -44,7 +47,7 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||||
*/
|
*/
|
||||||
public class CachingSessionDataStore extends ContainerLifeCycle implements SessionDataStore
|
public class CachingSessionDataStore extends ContainerLifeCycle implements SessionDataStore
|
||||||
{
|
{
|
||||||
|
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
|
||||||
/**
|
/**
|
||||||
* The actual store for the session data
|
* The actual store for the session data
|
||||||
*/
|
*/
|
||||||
|
@ -98,8 +101,15 @@ public class CachingSessionDataStore extends ContainerLifeCycle implements Sessi
|
||||||
SessionData d = null;
|
SessionData d = null;
|
||||||
|
|
||||||
|
|
||||||
//check to see if the session data is already in the cache
|
try
|
||||||
d = _cache.load(id);
|
{
|
||||||
|
//check to see if the session data is already in the cache
|
||||||
|
d = _cache.load(id);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn(e);
|
||||||
|
}
|
||||||
|
|
||||||
if (d != null)
|
if (d != null)
|
||||||
return d; //cache hit
|
return d; //cache hit
|
||||||
|
@ -178,10 +188,17 @@ public class CachingSessionDataStore extends ContainerLifeCycle implements Sessi
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id) throws Exception
|
public boolean exists(String id) throws Exception
|
||||||
{
|
{
|
||||||
//check the cache first
|
try
|
||||||
SessionData data = _cache.load(id);
|
{
|
||||||
if (data != null)
|
//check the cache first
|
||||||
return true;
|
SessionData data = _cache.load(id);
|
||||||
|
if (data != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn(e);
|
||||||
|
}
|
||||||
|
|
||||||
//then the delegate store
|
//then the delegate store
|
||||||
return _store.exists(id);
|
return _store.exists(id);
|
||||||
|
|
|
@ -120,6 +120,14 @@ public class DefaultSessionIdManager extends ContainerLifeCycle implements Sessi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the housekeeper
|
||||||
|
*/
|
||||||
|
public HouseKeeper getSessionHouseKeeper()
|
||||||
|
{
|
||||||
|
return _houseKeeper;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -86,12 +86,12 @@ public class Session implements SessionHandler.SessionIf
|
||||||
protected SessionHandler _handler; //the manager of the session
|
protected SessionHandler _handler; //the manager of the session
|
||||||
protected String _extendedId; //the _id plus the worker name
|
protected String _extendedId; //the _id plus the worker name
|
||||||
protected long _requests;
|
protected long _requests;
|
||||||
private boolean _idChanged;
|
protected boolean _idChanged;
|
||||||
private boolean _newSession;
|
protected boolean _newSession;
|
||||||
private State _state = State.VALID; //state of the session:valid,invalid or being invalidated
|
protected State _state = State.VALID; //state of the session:valid,invalid or being invalidated
|
||||||
private Locker _lock = new Locker(); //sync lock
|
protected Locker _lock = new Locker(); //sync lock
|
||||||
private boolean _resident = false;
|
protected boolean _resident = false;
|
||||||
private SessionInactivityTimeout _sessionInactivityTimer = null;
|
protected SessionInactivityTimeout _sessionInactivityTimer = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -169,6 +169,7 @@ public class Session implements SessionHandler.SessionIf
|
||||||
_handler = handler;
|
_handler = handler;
|
||||||
_sessionData = data;
|
_sessionData = data;
|
||||||
_newSession = true;
|
_newSession = true;
|
||||||
|
_sessionData.setDirty(true);
|
||||||
_requests = 1; //access will not be called on this new session, but we are obviously in a request
|
_requests = 1; //access will not be called on this new session, but we are obviously in a request
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +233,7 @@ public class Session implements SessionHandler.SessionIf
|
||||||
long lastAccessed = _sessionData.getAccessed();
|
long lastAccessed = _sessionData.getAccessed();
|
||||||
_sessionData.setAccessed(time);
|
_sessionData.setAccessed(time);
|
||||||
_sessionData.setLastAccessed(lastAccessed);
|
_sessionData.setLastAccessed(lastAccessed);
|
||||||
int maxInterval=getMaxInactiveInterval();
|
_sessionData.calcAndSetExpiry(time);
|
||||||
_sessionData.setExpiry(maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
|
|
||||||
if (isExpiredAt(time))
|
if (isExpiredAt(time))
|
||||||
{
|
{
|
||||||
invalidate();
|
invalidate();
|
||||||
|
@ -859,7 +859,7 @@ public class Session implements SessionHandler.SessionIf
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
//tell id mgr to remove session from all other contexts
|
//tell id mgr to remove session from all other contexts
|
||||||
((DefaultSessionIdManager)_handler.getSessionIdManager()).invalidateAll(_sessionData.getId());
|
_handler.getSessionIdManager().invalidateAll(_sessionData.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
@ -251,6 +251,16 @@ public class SessionData implements Serializable
|
||||||
return (getMaxInactiveMs() <= 0 ? 0 : (System.currentTimeMillis() + getMaxInactiveMs()));
|
return (getMaxInactiveMs() <= 0 ? 0 : (System.currentTimeMillis() + getMaxInactiveMs()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long calcExpiry (long time)
|
||||||
|
{
|
||||||
|
return (getMaxInactiveMs() <= 0 ? 0 : (time + getMaxInactiveMs()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void calcAndSetExpiry (long time)
|
||||||
|
{
|
||||||
|
setExpiry(calcExpiry(time));
|
||||||
|
}
|
||||||
|
|
||||||
public void calcAndSetExpiry ()
|
public void calcAndSetExpiry ()
|
||||||
{
|
{
|
||||||
setExpiry(calcExpiry());
|
setExpiry(calcExpiry());
|
||||||
|
@ -351,8 +361,8 @@ public class SessionData implements Serializable
|
||||||
public boolean isExpiredAt (long time)
|
public boolean isExpiredAt (long time)
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Testing expiry on session {}: expires at {} now {}", _id, getExpiry(), time);
|
LOG.debug("Testing expiry on session {}: expires at {} now {} maxIdle {}", _id, getExpiry(), time, getMaxInactiveMs());
|
||||||
if (getExpiry() <= 0)
|
if (getMaxInactiveMs() <= 0)
|
||||||
return false; //never expires
|
return false; //never expires
|
||||||
return (getExpiry() <= time);
|
return (getExpiry() <= time);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue