mirror of
https://github.com/jetty/jetty.project.git
synced 2025-02-25 17:18:30 +00:00
Merge branch 'jetty-9.1' into javawebsocket-jsr
This commit is contained in:
commit
f8c457f75e
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@ -126,11 +126,20 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1-b01</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-jmx</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>example-async-rest</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty.example-async-rest</groupId>
|
||||
@ -22,8 +22,9 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1-b08</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>example-async-rest</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty.example-async-rest</groupId>
|
||||
@ -25,8 +25,9 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1-b08</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.examples</groupId>
|
||||
<artifactId>examples-parent</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.examples</groupId>
|
||||
<artifactId>examples-parent</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -54,7 +54,6 @@ public class SecuredHelloHandler
|
||||
security.setConstraintMappings(Collections.singletonList(mapping));
|
||||
security.setAuthenticator(new BasicAuthenticator());
|
||||
security.setLoginService(loginService);
|
||||
security.setStrict(false);
|
||||
|
||||
HelloHandler hh = new HelloHandler();
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.eclipse.jetty.examples</groupId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-annotations</artifactId>
|
||||
|
@ -18,19 +18,11 @@
|
||||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler.Decorator;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
/**
|
||||
* WebAppDecoratorWrapper
|
||||
* AnnotationDecorator
|
||||
*
|
||||
*
|
||||
*/
|
||||
@ -53,99 +45,6 @@ public class AnnotationDecorator implements Decorator
|
||||
_introspector.registerHandler(new ServletSecurityAnnotationHandler(context));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param filter
|
||||
* @throws ServletException
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateFilterHolder(org.eclipse.jetty.servlet.FilterHolder)
|
||||
*/
|
||||
public void decorateFilterHolder(FilterHolder filter) throws ServletException
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param <T>
|
||||
* @param filter
|
||||
* @return the decorated filter
|
||||
* @throws ServletException
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateFilterInstance(javax.servlet.Filter)
|
||||
*/
|
||||
public <T extends Filter> T decorateFilterInstance(T filter) throws ServletException
|
||||
{
|
||||
introspect(filter);
|
||||
return filter;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param <T>
|
||||
* @param listener
|
||||
* @return the decorated event listener instance
|
||||
* @throws ServletException
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateListenerInstance(java.util.EventListener)
|
||||
*/
|
||||
public <T extends EventListener> T decorateListenerInstance(T listener) throws ServletException
|
||||
{
|
||||
introspect(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param servlet
|
||||
* @throws ServletException
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateServletHolder(org.eclipse.jetty.servlet.ServletHolder)
|
||||
*/
|
||||
public void decorateServletHolder(ServletHolder servlet) throws ServletException
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param <T>
|
||||
* @param servlet
|
||||
* @return the decorated servlet instance
|
||||
* @throws ServletException
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateServletInstance(javax.servlet.Servlet)
|
||||
*/
|
||||
public <T extends Servlet> T decorateServletInstance(T servlet) throws ServletException
|
||||
{
|
||||
introspect(servlet);
|
||||
return servlet;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param f
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyFilterInstance(javax.servlet.Filter)
|
||||
*/
|
||||
public void destroyFilterInstance(Filter f)
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param s
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyServletInstance(javax.servlet.Servlet)
|
||||
*/
|
||||
public void destroyServletInstance(Servlet s)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param f
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyListenerInstance(java.util.EventListener)
|
||||
*/
|
||||
public void destroyListenerInstance(EventListener f)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for annotations that can be discovered with introspection:
|
||||
* <ul>
|
||||
@ -161,4 +60,17 @@ public class AnnotationDecorator implements Decorator
|
||||
{
|
||||
_introspector.introspect(o.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decorate(Object o)
|
||||
{
|
||||
introspect(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(Object o)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public class PostConstructAnnotationHandler extends AbstractIntrospectableAnnota
|
||||
public void doHandle(Class clazz)
|
||||
{
|
||||
//Check that the PostConstruct is on a class that we're interested in
|
||||
if (Util.isServletType(clazz))
|
||||
if (Util.supportsPostConstructPreDestroy(clazz))
|
||||
{
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
for (int i=0; i<methods.length; i++)
|
||||
|
@ -43,7 +43,7 @@ public class PreDestroyAnnotationHandler extends AbstractIntrospectableAnnotatio
|
||||
public void doHandle(Class clazz)
|
||||
{
|
||||
//Check that the PreDestroy is on a class that we're interested in
|
||||
if (Util.isServletType(clazz))
|
||||
if (Util.supportsPostConstructPreDestroy(clazz))
|
||||
{
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
for (int i=0; i<methods.length; i++)
|
||||
|
@ -57,7 +57,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
||||
*/
|
||||
public void doHandle(Class<?> clazz)
|
||||
{
|
||||
if (Util.isServletType(clazz))
|
||||
if (Util.supportsResourceInjection(clazz))
|
||||
{
|
||||
handleClass(clazz);
|
||||
|
||||
|
@ -112,6 +112,9 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
|
||||
|
||||
for (ConstraintMapping m:constraintMappings)
|
||||
securityHandler.addConstraintMapping(m);
|
||||
|
||||
//Servlet Spec 3.1 requires paths with uncovered http methods to be reported
|
||||
securityHandler.checkPathsWithUncoveredHttpMethods();
|
||||
}
|
||||
|
||||
|
||||
@ -128,12 +131,6 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
|
||||
protected Constraint makeConstraint (Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
|
||||
{
|
||||
return ConstraintSecurityHandler.createConstraint(servlet.getName(), rolesAllowed, permitOrDeny, transport);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,6 +68,42 @@ public class Util
|
||||
|
||||
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.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.AsyncListener.class.isAssignableFrom(c) ||
|
||||
javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isEnvEntryType (Class type)
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.ServletRequestAttributeListener;
|
||||
import javax.servlet.ServletRequestListener;
|
||||
import javax.servlet.http.HttpSessionAttributeListener;
|
||||
import javax.servlet.http.HttpSessionIdListener;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
@ -78,7 +79,8 @@ public class WebListenerAnnotation extends DiscoveredAnnotation
|
||||
ServletRequestListener.class.isAssignableFrom(clazz) ||
|
||||
ServletRequestAttributeListener.class.isAssignableFrom(clazz) ||
|
||||
HttpSessionListener.class.isAssignableFrom(clazz) ||
|
||||
HttpSessionAttributeListener.class.isAssignableFrom(clazz))
|
||||
HttpSessionAttributeListener.class.isAssignableFrom(clazz) ||
|
||||
HttpSessionIdListener.class.isAssignableFrom(clazz))
|
||||
{
|
||||
java.util.EventListener listener = (java.util.EventListener)clazz.newInstance();
|
||||
MetaData metaData = _context.getMetaData();
|
||||
|
@ -115,21 +115,12 @@ public class TestSecurityAnnotationConversions
|
||||
introspector.registerHandler(annotationHandler);
|
||||
|
||||
|
||||
//set up the expected outcomes:
|
||||
//set up the expected outcomes - no constraints at all as per Servlet Spec 3.1 pg 129
|
||||
//1 ConstraintMapping per ServletMapping pathSpec
|
||||
Constraint expectedConstraint = new Constraint();
|
||||
expectedConstraint.setAuthenticate(false);
|
||||
expectedConstraint.setDataConstraint(Constraint.DC_NONE);
|
||||
|
||||
ConstraintMapping[] expectedMappings = new ConstraintMapping[2];
|
||||
expectedMappings[0] = new ConstraintMapping();
|
||||
expectedMappings[0].setConstraint(expectedConstraint);
|
||||
expectedMappings[0].setPathSpec("/foo/*");
|
||||
|
||||
expectedMappings[1] = new ConstraintMapping();
|
||||
expectedMappings[1].setConstraint(expectedConstraint);
|
||||
expectedMappings[1].setPathSpec("*.foo");
|
||||
|
||||
|
||||
ConstraintMapping[] expectedMappings = new ConstraintMapping[]{};
|
||||
|
||||
introspector.introspect(PermitServlet.class);
|
||||
|
||||
compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-ant</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -81,7 +81,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||
|
||||
Constraint constraint = new Constraint();
|
||||
constraint.setAuthenticate(true);
|
||||
constraint.setRoles(new String[]{"*"});
|
||||
constraint.setRoles(new String[]{"**"}); //allow any authenticated user
|
||||
ConstraintMapping mapping = new ConstraintMapping();
|
||||
mapping.setPathSpec("/secure");
|
||||
mapping.setConstraint(constraint);
|
||||
@ -89,7 +89,6 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||
securityHandler.addConstraintMapping(mapping);
|
||||
securityHandler.setAuthenticator(authenticator);
|
||||
securityHandler.setLoginService(loginService);
|
||||
securityHandler.setStrict(false);
|
||||
|
||||
securityHandler.setHandler(handler);
|
||||
start(securityHandler);
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-continuation</artifactId>
|
||||
@ -54,9 +54,9 @@
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -34,7 +34,7 @@ import javax.servlet.ServletResponseWrapper;
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* This implementation of Continuation is used by {@link ContinuationSupport}
|
||||
* when it detects that the application has been deployed in a non-jetty Servlet 3
|
||||
* when it detects that the application has been deployed in a Servlet 3
|
||||
* server.
|
||||
*/
|
||||
public class Servlet3Continuation implements Continuation, AsyncListener
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-deploy</artifactId>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>jetty-distribution</artifactId>
|
||||
<name>Jetty :: Distribution Assemblies</name>
|
||||
@ -298,7 +298,7 @@
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
|
||||
<excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.drafts</excludeGroupIds>
|
||||
<excludeArtifactIds>jetty-all,jetty-start,jetty-monitor,jetty-jsp</excludeArtifactIds>
|
||||
<excludeArtifactIds>jetty-all,jetty-start,jetty-monitor</excludeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib</outputDirectory>
|
||||
</configuration>
|
||||
@ -343,12 +343,17 @@
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1-b08</version>
|
||||
<!--
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<version>${orbit-servlet-api-version}</version>
|
||||
-->
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${assembly-directory}/lib</outputDirectory>
|
||||
<destFileName>servlet-api-3.0.jar</destFileName>
|
||||
<destFileName>servlet-api-3.1.jar</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
@ -426,8 +431,8 @@
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty.orbit</includeGroupIds>
|
||||
<includeArtifactIds>com.sun.el,javax.el,javax.servlet.jsp,javax.servlet.jsp.jstl,org.apache.jasper.glassfish,org.apache.taglibs.standard.glassfish,org.eclipse.jdt.core</includeArtifactIds>
|
||||
<includeGroupIds>org.eclipse.jetty.orbit,org.glassfish.web, org.glassfish, javax.servlet.jsp</includeGroupIds>
|
||||
<includeArtifactIds>javax.servlet.jsp.jstl,org.apache.taglibs.standard.glassfish,org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, javax.el</includeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
|
||||
</configuration>
|
||||
@ -511,6 +516,23 @@
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.security.auth.message</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>org.apache.taglibs.standard.glassfish</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.web</groupId>
|
||||
<artifactId>javax.servlet.jsp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet.jsp</groupId>
|
||||
<artifactId>javax.servlet.jsp-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.el</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- jetty deps -->
|
||||
<dependency>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-http</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-io</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-jaas</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-jaspi</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-jmx</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-jndi</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-jsp</artifactId>
|
||||
@ -14,46 +14,120 @@
|
||||
|
||||
<dependencies>
|
||||
<!-- JSP Api -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet.jsp</groupId>
|
||||
<artifactId>javax.servlet.jsp-api</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet.jsp</artifactId>
|
||||
<version>2.2.0.v201112011158</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- JSP Impl -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.web</groupId>
|
||||
<artifactId>javax.servlet.jsp</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>org.apache.jasper.glassfish</artifactId>
|
||||
<version>2.2.2.v201112011158</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- JSTL Api -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet.jsp.jstl</artifactId>
|
||||
<version>1.2.0.v201105211821</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet.jsp</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- JSTL Impl -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>org.apache.taglibs.standard.glassfish</artifactId>
|
||||
<version>1.2.0.v201112081803</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- EL Api -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.el</artifactId>
|
||||
<version>3.0-b07</version>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.el</artifactId>
|
||||
<version>2.2.0.v201303151357</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- EL Impl -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>com.sun.el</artifactId>
|
||||
<version>2.2.0.v201303151357</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- Eclipse Java Compiler (for JSP Compilation) -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>org.eclipse.jdt.core</artifactId>
|
||||
<version>3.8.2.v20130121</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1-b08</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-jspc-maven-plugin</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-monitor</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-nosql</artifactId>
|
||||
|
@ -356,6 +356,7 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
||||
__log.warn(e);
|
||||
}
|
||||
}
|
||||
super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-osgi-boot-jsp</artifactId>
|
||||
@ -33,8 +33,8 @@
|
||||
</dependency>
|
||||
<!-- Orbit Servlet Deps -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<!-- Orbit JSP Deps -->
|
||||
<dependency>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-osgi-boot</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-httpservice</artifactId>
|
||||
@ -30,8 +30,8 @@
|
||||
<artifactId>org.eclipse.osgi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-osgi-npn</artifactId>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-project</artifactId>
|
||||
@ -26,7 +26,9 @@
|
||||
<module>jetty-osgi-httpservice</module>
|
||||
<module>test-jetty-osgi-webapp</module>
|
||||
<module>test-jetty-osgi-context</module>
|
||||
<!--
|
||||
<module>test-jetty-osgi</module>
|
||||
-->
|
||||
</modules>
|
||||
<build>
|
||||
<resources>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>test-jetty-osgi-context</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -152,8 +152,8 @@
|
||||
-->
|
||||
<!-- Orbit Servlet Deps -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Orbit JSP Deps -->
|
||||
|
@ -78,7 +78,8 @@ public class TestJettyOSGiBootCore
|
||||
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot" ).versionAsInProject().start());
|
||||
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet" ).versionAsInProject().noStart());
|
||||
//res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "javax.servlet" ).artifactId( "javax.servlet-api" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-deploy" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-server" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-servlet" ).versionAsInProject().noStart());
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-plus</artifactId>
|
||||
|
@ -18,8 +18,6 @@
|
||||
|
||||
package org.eclipse.jetty.plus.annotation;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
|
||||
/**
|
||||
@ -57,8 +55,10 @@ public class RunAs
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param holder
|
||||
*/
|
||||
public void setRunAs (ServletHolder holder)
|
||||
throws ServletException
|
||||
{
|
||||
if (holder == null)
|
||||
return;
|
||||
|
@ -52,7 +52,6 @@ public class RunAsCollection
|
||||
}
|
||||
|
||||
public RunAs getRunAs (Object o)
|
||||
throws ServletException
|
||||
{
|
||||
if (o==null)
|
||||
return null;
|
||||
@ -61,7 +60,6 @@ public class RunAsCollection
|
||||
}
|
||||
|
||||
public void setRunAs(Object o)
|
||||
throws ServletException
|
||||
{
|
||||
if (o == null)
|
||||
return;
|
||||
|
@ -18,24 +18,18 @@
|
||||
|
||||
package org.eclipse.jetty.plus.webapp;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.eclipse.jetty.plus.annotation.InjectionCollection;
|
||||
import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
|
||||
import org.eclipse.jetty.plus.annotation.RunAsCollection;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler.Decorator;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
/**
|
||||
* WebAppDecorator
|
||||
* PlusDecorator
|
||||
*
|
||||
*
|
||||
*/
|
||||
@ -50,83 +44,7 @@ public class PlusDecorator implements Decorator
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateFilterHolder(org.eclipse.jetty.servlet.FilterHolder)
|
||||
*/
|
||||
public void decorateFilterHolder(FilterHolder filter) throws ServletException
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateFilterInstance(javax.servlet.Filter)
|
||||
*/
|
||||
public <T extends Filter> T decorateFilterInstance(T filter) throws ServletException
|
||||
{
|
||||
decorate(filter);
|
||||
return filter;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateListenerInstance(java.util.EventListener)
|
||||
*/
|
||||
public <T extends EventListener> T decorateListenerInstance(T listener) throws ServletException
|
||||
{
|
||||
decorate(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateServletHolder(org.eclipse.jetty.servlet.ServletHolder)
|
||||
*/
|
||||
public void decorateServletHolder(ServletHolder holder) throws ServletException
|
||||
{
|
||||
decorate(holder);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateServletInstance(javax.servlet.Servlet)
|
||||
*/
|
||||
public <T extends Servlet> T decorateServletInstance(T servlet) throws ServletException
|
||||
{
|
||||
decorate(servlet);
|
||||
return servlet;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyFilterInstance(javax.servlet.Filter)
|
||||
*/
|
||||
public void destroyFilterInstance(Filter f)
|
||||
{
|
||||
destroy(f);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyServletInstance(javax.servlet.Servlet)
|
||||
*/
|
||||
public void destroyServletInstance(Servlet s)
|
||||
{
|
||||
destroy(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyListenerInstance(java.util.EventListener)
|
||||
*/
|
||||
public void destroyListenerInstance(EventListener l)
|
||||
{
|
||||
destroy(l);
|
||||
}
|
||||
|
||||
|
||||
protected void decorate (Object o)
|
||||
throws ServletException
|
||||
public Object decorate (Object o)
|
||||
{
|
||||
|
||||
RunAsCollection runAses = (RunAsCollection)_context.getAttribute(RunAsCollection.RUNAS_COLLECTION);
|
||||
@ -146,12 +64,13 @@ public class PlusDecorator implements Decorator
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
protected void destroy (Object o)
|
||||
public void destroy (Object o)
|
||||
{
|
||||
LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
|
||||
if (callbacks != null)
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-proxy</artifactId>
|
||||
@ -87,8 +87,13 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-rewrite</artifactId>
|
||||
@ -86,8 +86,8 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -88,7 +88,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -66,7 +66,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-security</artifactId>
|
||||
|
@ -0,0 +1,95 @@
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.security;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.server.Authentication.User;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.server.UserIdentity.Scope;
|
||||
|
||||
/**
|
||||
* AbstractUserAuthentication
|
||||
*
|
||||
*
|
||||
* Base class for representing an authenticated user.
|
||||
*/
|
||||
public abstract class AbstractUserAuthentication implements User
|
||||
{
|
||||
protected String _method;
|
||||
protected UserIdentity _userIdentity;
|
||||
|
||||
|
||||
public AbstractUserAuthentication(String method, UserIdentity userIdentity)
|
||||
{
|
||||
_method = method;
|
||||
_userIdentity = userIdentity;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getAuthMethod()
|
||||
{
|
||||
return _method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserIdentity getUserIdentity()
|
||||
{
|
||||
return _userIdentity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(Scope scope, String role)
|
||||
{
|
||||
String roleToTest = null;
|
||||
if (scope!=null && scope.getRoleRefMap()!=null)
|
||||
roleToTest=scope.getRoleRefMap().get(role);
|
||||
if (roleToTest==null)
|
||||
roleToTest=role;
|
||||
//Servlet Spec 3.1 pg 125 if testing special role **
|
||||
if ("**".equals(roleToTest.trim()))
|
||||
{
|
||||
//if ** is NOT a declared role name, the we return true
|
||||
//as the user is authenticated. If ** HAS been declared as a
|
||||
//role name, then we have to check if the user has that role
|
||||
if (!declaredRolesContains("**"))
|
||||
return true;
|
||||
else
|
||||
return _userIdentity.isUserInRole(role, scope);
|
||||
}
|
||||
|
||||
return _userIdentity.isUserInRole(role, scope);
|
||||
}
|
||||
|
||||
public boolean declaredRolesContains(String roleName)
|
||||
{
|
||||
SecurityHandler security=SecurityHandler.getCurrentSecurityHandler();
|
||||
if (security==null)
|
||||
return false;
|
||||
|
||||
if (security instanceof ConstraintAware)
|
||||
{
|
||||
Set<String> declaredRoles = ((ConstraintAware)security).getRoles();
|
||||
return (declaredRoles != null) && declaredRoles.contains(roleName);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -52,9 +52,25 @@ public interface Authenticator
|
||||
* @return The name of the authentication method
|
||||
*/
|
||||
String getAuthMethod();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Called prior to validateRequest. The authenticator can
|
||||
* manipulate the request to update it with information that
|
||||
* can be inspected prior to validateRequest being called.
|
||||
* The primary purpose of this method is to satisfy the Servlet
|
||||
* Spec 3.1 section 13.6.3 on handling Form authentication
|
||||
* where the http method of the original request causing authentication
|
||||
* is not the same as the http method resulting from the redirect
|
||||
* after authentication.
|
||||
* @param request
|
||||
*/
|
||||
void prepareRequest(ServletRequest request);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Validate a response
|
||||
/** Validate a request
|
||||
* @param request The request
|
||||
* @param response The response
|
||||
* @param mandatory True if authentication is mandatory.
|
||||
|
@ -51,4 +51,20 @@ public interface ConstraintAware
|
||||
* @param role
|
||||
*/
|
||||
void addRole(String role);
|
||||
|
||||
/**
|
||||
* See Servlet Spec 31, sec 13.8.4, pg 145
|
||||
* When true, requests with http methods not explicitly covered either by inclusion or omissions
|
||||
* in constraints, will have access denied.
|
||||
* @param deny
|
||||
*/
|
||||
void setDenyUncoveredHttpMethods(boolean deny);
|
||||
|
||||
boolean isDenyUncoveredHttpMethods();
|
||||
|
||||
/**
|
||||
* See Servlet Spec 31, sec 13.8.4, pg 145
|
||||
* Container must check if there are urls with uncovered http methods
|
||||
*/
|
||||
boolean checkPathsWithUncoveredHttpMethods();
|
||||
}
|
||||
|
@ -45,23 +45,30 @@ import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* ConstraintSecurityHandler
|
||||
*
|
||||
* Handler to enforce SecurityConstraints. This implementation is servlet spec
|
||||
* 3.0 compliant and pre-computes the constraint combinations for runtime
|
||||
* 3.1 compliant and pre-computes the constraint combinations for runtime
|
||||
* efficiency.
|
||||
*
|
||||
*/
|
||||
public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(SecurityHandler.class); //use same as SecurityHandler
|
||||
|
||||
private static final String OMISSION_SUFFIX = ".omission";
|
||||
private static final String ALL_METHODS = "*";
|
||||
private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<>();
|
||||
private final Set<String> _roles = new CopyOnWriteArraySet<>();
|
||||
private final PathMap<Map<String, RoleInfo>> _constraintMap = new PathMap<>();
|
||||
private boolean _strict = true;
|
||||
private boolean _denyUncoveredMethods = false;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
@ -228,76 +235,56 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
|
||||
|
||||
//Create a constraint that will describe the default case (ie if not overridden by specific HttpMethodConstraints)
|
||||
Constraint constraint = ConstraintSecurityHandler.createConstraint(name, securityElement);
|
||||
|
||||
//Create a mapping for the pathSpec for the default case
|
||||
ConstraintMapping defaultMapping = new ConstraintMapping();
|
||||
defaultMapping.setPathSpec(pathSpec);
|
||||
defaultMapping.setConstraint(constraint);
|
||||
mappings.add(defaultMapping);
|
||||
Constraint httpConstraint = null;
|
||||
ConstraintMapping httpConstraintMapping = null;
|
||||
|
||||
if (securityElement.getEmptyRoleSemantic() != EmptyRoleSemantic.PERMIT ||
|
||||
securityElement.getRolesAllowed().length != 0 ||
|
||||
securityElement.getTransportGuarantee() != TransportGuarantee.NONE)
|
||||
{
|
||||
httpConstraint = ConstraintSecurityHandler.createConstraint(name, securityElement);
|
||||
|
||||
//Create a mapping for the pathSpec for the default case
|
||||
httpConstraintMapping = new ConstraintMapping();
|
||||
httpConstraintMapping.setPathSpec(pathSpec);
|
||||
httpConstraintMapping.setConstraint(httpConstraint);
|
||||
mappings.add(httpConstraintMapping);
|
||||
}
|
||||
|
||||
|
||||
//See Spec 13.4.1.2 p127
|
||||
List<String> methodOmissions = new ArrayList<String>();
|
||||
|
||||
//make constraint mappings for this url for each of the HttpMethodConstraintElements
|
||||
Collection<HttpMethodConstraintElement> methodConstraints = securityElement.getHttpMethodConstraints();
|
||||
if (methodConstraints != null)
|
||||
Collection<HttpMethodConstraintElement> methodConstraintElements = securityElement.getHttpMethodConstraints();
|
||||
if (methodConstraintElements != null)
|
||||
{
|
||||
for (HttpMethodConstraintElement methodConstraint:methodConstraints)
|
||||
for (HttpMethodConstraintElement methodConstraintElement:methodConstraintElements)
|
||||
{
|
||||
//Make a Constraint that captures the <auth-constraint> and <user-data-constraint> elements supplied for the HttpMethodConstraintElement
|
||||
Constraint mconstraint = ConstraintSecurityHandler.createConstraint(name, methodConstraint);
|
||||
Constraint methodConstraint = ConstraintSecurityHandler.createConstraint(name, methodConstraintElement);
|
||||
ConstraintMapping mapping = new ConstraintMapping();
|
||||
mapping.setConstraint(mconstraint);
|
||||
mapping.setConstraint(methodConstraint);
|
||||
mapping.setPathSpec(pathSpec);
|
||||
if (methodConstraint.getMethodName() != null)
|
||||
if (methodConstraintElement.getMethodName() != null)
|
||||
{
|
||||
mapping.setMethod(methodConstraint.getMethodName());
|
||||
mapping.setMethod(methodConstraintElement.getMethodName());
|
||||
//See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
|
||||
methodOmissions.add(methodConstraint.getMethodName());
|
||||
methodOmissions.add(methodConstraintElement.getMethodName());
|
||||
}
|
||||
mappings.add(mapping);
|
||||
}
|
||||
}
|
||||
//See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
|
||||
if (methodOmissions.size() > 0)
|
||||
defaultMapping.setMethodOmissions(methodOmissions.toArray(new String[methodOmissions.size()]));
|
||||
//UNLESS the default constraint contains all default values. In that case, we won't add it. See Servlet Spec 3.1 pg 129
|
||||
if (methodOmissions.size() > 0 && httpConstraintMapping != null)
|
||||
httpConstraintMapping.setMethodOmissions(methodOmissions.toArray(new String[methodOmissions.size()]));
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the strict mode.
|
||||
* @return true if the security handler is running in strict mode.
|
||||
*/
|
||||
public boolean isStrict()
|
||||
{
|
||||
return _strict;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Set the strict mode of the security handler.
|
||||
* <p>
|
||||
* When in strict mode (the default), the full servlet specification
|
||||
* will be implemented.
|
||||
* If not in strict mode, some additional flexibility in configuration
|
||||
* is allowed:<ul>
|
||||
* <li>All users do not need to have a role defined in the deployment descriptor
|
||||
* <li>The * role in a constraint applies to ANY role rather than all roles defined in
|
||||
* the deployment descriptor.
|
||||
* </ul>
|
||||
*
|
||||
* @param strict the strict to set
|
||||
* @see #setRoles(Set)
|
||||
* @see #setConstraintMappings(List, Set)
|
||||
*/
|
||||
public void setStrict(boolean strict)
|
||||
{
|
||||
_strict = strict;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
@ -408,8 +395,16 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
{
|
||||
_constraintMappings.add(mapping);
|
||||
if (mapping.getConstraint()!=null && mapping.getConstraint().getRoles()!=null)
|
||||
{
|
||||
//allow for lazy role naming: if a role is named in a security constraint, try and
|
||||
//add it to the list of declared roles (ie as if it was declared with a security-role
|
||||
for (String role : mapping.getConstraint().getRoles())
|
||||
{
|
||||
if ("*".equals(role) || "**".equals(role))
|
||||
continue;
|
||||
addRole(role);
|
||||
}
|
||||
}
|
||||
|
||||
if (isStarted())
|
||||
{
|
||||
@ -424,8 +419,9 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
@Override
|
||||
public void addRole(String role)
|
||||
{
|
||||
//add to list of declared roles
|
||||
boolean modified = _roles.add(role);
|
||||
if (isStarted() && modified && isStrict())
|
||||
if (isStarted() && modified)
|
||||
{
|
||||
// Add the new role to currently defined any role role infos
|
||||
for (Map<String,RoleInfo> map : _constraintMap.values())
|
||||
@ -454,9 +450,13 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
processConstraintMapping(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
//Servlet Spec 3.1 pg 147 sec 13.8.4.2 log paths for which there are uncovered http methods
|
||||
checkPathsWithUncoveredHttpMethods();
|
||||
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
@ -538,13 +538,13 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Constraints that name method omissions are dealt with differently.
|
||||
* We create an entry in the mappings with key "method.omission". This entry
|
||||
* We create an entry in the mappings with key "<method>.omission". This entry
|
||||
* is only ever combined with other omissions for the same method to produce a
|
||||
* consolidated RoleInfo. Then, when we wish to find the relevant constraints for
|
||||
* a given Request (in prepareConstraintInfo()), we consult 3 types of entries in
|
||||
* the mappings: an entry that names the method of the Request specifically, an
|
||||
* entry that names constraints that apply to all methods, entries of the form
|
||||
* method.omission, where the method of the Request is not named in the omission.
|
||||
* <method>.omission, where the method of the Request is not named in the omission.
|
||||
* @param mapping
|
||||
* @param mappings
|
||||
*/
|
||||
@ -559,7 +559,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
sb.append(omissions[i]);
|
||||
}
|
||||
sb.append(OMISSION_SUFFIX);
|
||||
|
||||
RoleInfo ri = new RoleInfo();
|
||||
mappings.put(sb.toString(), ri);
|
||||
configureRoleInfo(ri, mapping);
|
||||
@ -573,7 +572,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
* @param mapping
|
||||
*/
|
||||
protected void configureRoleInfo (RoleInfo ri, ConstraintMapping mapping)
|
||||
{
|
||||
{
|
||||
Constraint constraint = mapping.getConstraint();
|
||||
boolean forbidden = constraint.isForbidden();
|
||||
ri.setForbidden(forbidden);
|
||||
@ -582,7 +581,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
//which we need in order to do combining of omissions in prepareConstraintInfo
|
||||
UserDataConstraint userDataConstraint = UserDataConstraint.get(mapping.getConstraint().getDataConstraint());
|
||||
ri.setUserDataConstraint(userDataConstraint);
|
||||
|
||||
|
||||
//if forbidden, no point setting up roles
|
||||
if (!ri.isForbidden())
|
||||
@ -590,26 +588,29 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
//add in the roles
|
||||
boolean checked = mapping.getConstraint().getAuthenticate();
|
||||
ri.setChecked(checked);
|
||||
|
||||
if (ri.isChecked())
|
||||
{
|
||||
if (mapping.getConstraint().isAnyRole())
|
||||
{
|
||||
if (_strict)
|
||||
{
|
||||
// * means "all defined roles"
|
||||
for (String role : _roles)
|
||||
ri.addRole(role);
|
||||
}
|
||||
else
|
||||
// * means any role
|
||||
ri.setAnyRole(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
// * means matches any defined role
|
||||
for (String role : _roles)
|
||||
ri.addRole(role);
|
||||
ri.setAnyRole(true);
|
||||
}
|
||||
else if (mapping.getConstraint().isAnyAuth())
|
||||
{
|
||||
//being authenticated is sufficient, not necessary to check roles
|
||||
ri.setAnyAuth(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//user must be in one of the named roles
|
||||
String[] newRoles = mapping.getConstraint().getRoles();
|
||||
for (String role : newRoles)
|
||||
{
|
||||
if (_strict &&!_roles.contains(role))
|
||||
//check role has been defined
|
||||
if (!_roles.contains(role))
|
||||
throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
|
||||
ri.addRole(role);
|
||||
}
|
||||
@ -626,7 +627,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
* represents a merged set of user data constraints, roles etc -:
|
||||
* <ol>
|
||||
* <li>A mapping of an exact method name </li>
|
||||
* <li>A mapping will null key that matches every method name</li>
|
||||
* <li>A mapping with key * that matches every method name</li>
|
||||
* <li>Mappings with keys of the form "<method>.<method>.<method>.omission" that indicates it will match every method name EXCEPT those given</li>
|
||||
* </ol>
|
||||
*
|
||||
@ -660,7 +661,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
applicableConstraints.add(entry.getValue());
|
||||
}
|
||||
|
||||
if (applicableConstraints.size() == 1)
|
||||
if (applicableConstraints.size() == 0 && isDenyUncoveredHttpMethods())
|
||||
{
|
||||
roleInfo = new RoleInfo();
|
||||
roleInfo.setForbidden(true);
|
||||
}
|
||||
else if (applicableConstraints.size() == 1)
|
||||
roleInfo = applicableConstraints.get(0);
|
||||
else
|
||||
{
|
||||
@ -672,6 +678,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return roleInfo;
|
||||
}
|
||||
|
||||
@ -693,7 +700,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
|
||||
HttpConfiguration httpConfig = HttpChannel.getCurrentHttpChannel().getHttpConfiguration();
|
||||
|
||||
|
||||
if (dataConstraint == UserDataConstraint.Confidential || dataConstraint == UserDataConstraint.Integral)
|
||||
{
|
||||
if (request.isSecure())
|
||||
@ -750,14 +756,35 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
return true;
|
||||
}
|
||||
|
||||
if (roleInfo.isAnyRole() && request.getAuthType()!=null)
|
||||
//handle ** role constraint
|
||||
if (roleInfo.isAnyAuth() && request.getUserPrincipal() != null)
|
||||
{
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
//check if user is any of the allowed roles
|
||||
boolean isUserInRole = false;
|
||||
for (String role : roleInfo.getRoles())
|
||||
{
|
||||
if (userIdentity.isUserInRole(role, null))
|
||||
return true;
|
||||
{
|
||||
isUserInRole = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//handle * role constraint
|
||||
if (roleInfo.isAnyRole() && request.getUserPrincipal() != null && isUserInRole)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//normal role check
|
||||
if (isUserInRole)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -773,5 +800,135 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||
Collections.singleton(_roles),
|
||||
_constraintMap.entrySet());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.security.ConstraintAware#setDenyUncoveredHttpMethods(boolean)
|
||||
*/
|
||||
@Override
|
||||
public void setDenyUncoveredHttpMethods(boolean deny)
|
||||
{
|
||||
_denyUncoveredMethods = deny;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public boolean isDenyUncoveredHttpMethods()
|
||||
{
|
||||
return _denyUncoveredMethods;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Servlet spec 3.1 pg. 147.
|
||||
*/
|
||||
@Override
|
||||
public boolean checkPathsWithUncoveredHttpMethods()
|
||||
{
|
||||
Set<String> paths = getPathsWithUncoveredHttpMethods();
|
||||
if (paths != null && !paths.isEmpty())
|
||||
{
|
||||
for (String p:paths)
|
||||
LOG.warn("Path with uncovered http methods: {}",p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Servlet spec 3.1 pg. 147.
|
||||
* The container must check all the combined security constraint
|
||||
* information and log any methods that are not protected and the
|
||||
* urls at which they are not protected
|
||||
*
|
||||
* @return list of paths for which there are uncovered methods
|
||||
*/
|
||||
public Set<String> getPathsWithUncoveredHttpMethods ()
|
||||
{
|
||||
//if automatically denying uncovered methods, there are no uncovered methods
|
||||
if (_denyUncoveredMethods)
|
||||
return Collections.emptySet();
|
||||
|
||||
Set<String> uncoveredPaths = new HashSet<String>();
|
||||
|
||||
for (String path:_constraintMap.keySet())
|
||||
{
|
||||
Map<String, RoleInfo> methodMappings = _constraintMap.get(path);
|
||||
//Each key is either:
|
||||
// : an exact method name
|
||||
// : * which means that the constraint applies to every method
|
||||
// : a name of the form <method>.<method>.<method>.omission, which means it applies to every method EXCEPT those named
|
||||
if (methodMappings.get(ALL_METHODS) != null)
|
||||
continue; //can't be any uncovered methods for this url path
|
||||
|
||||
boolean hasOmissions = omissionsExist(path, methodMappings);
|
||||
|
||||
for (String method:methodMappings.keySet())
|
||||
{
|
||||
if (method.endsWith(OMISSION_SUFFIX))
|
||||
{
|
||||
Set<String> omittedMethods = getOmittedMethods(method);
|
||||
for (String m:omittedMethods)
|
||||
{
|
||||
if (!methodMappings.containsKey(m))
|
||||
uncoveredPaths.add(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//an exact method name
|
||||
if (!hasOmissions)
|
||||
//a http-method does not have http-method-omission to cover the other method names
|
||||
uncoveredPaths.add(path);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return uncoveredPaths;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Check if any http method omissions exist in the list of method
|
||||
* to auth info mappings.
|
||||
*
|
||||
* @param methodNames
|
||||
* @return
|
||||
*/
|
||||
protected boolean omissionsExist (String path, Map<String, RoleInfo> methodMappings)
|
||||
{
|
||||
if (methodMappings == null)
|
||||
return false;
|
||||
boolean hasOmissions = false;
|
||||
for (String m:methodMappings.keySet())
|
||||
{
|
||||
if (m.endsWith(OMISSION_SUFFIX))
|
||||
hasOmissions = true;
|
||||
}
|
||||
return hasOmissions;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Given a string of the form <method>.<method>.omission
|
||||
* split out the individual method names.
|
||||
*
|
||||
* @param omission
|
||||
* @return
|
||||
*/
|
||||
protected Set<String> getOmittedMethods (String omission)
|
||||
{
|
||||
if (omission == null || !omission.endsWith(OMISSION_SUFFIX))
|
||||
return Collections.emptySet();
|
||||
|
||||
String[] strings = omission.split("\\.");
|
||||
Set<String> methods = new HashSet<String>();
|
||||
for (int i=0;i<strings.length-1;i++)
|
||||
methods.add(strings[i]);
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
|
@ -54,12 +54,21 @@ public class DefaultUserIdentity implements UserIdentity
|
||||
}
|
||||
|
||||
public boolean isUserInRole(String role, Scope scope)
|
||||
{
|
||||
{
|
||||
//Servlet Spec 3.1, pg 125
|
||||
if ("*".equals(role))
|
||||
return false;
|
||||
|
||||
String roleToTest = null;
|
||||
if (scope!=null && scope.getRoleRefMap()!=null)
|
||||
role=scope.getRoleRefMap().get(role);
|
||||
roleToTest=scope.getRoleRefMap().get(role);
|
||||
|
||||
//Servlet Spec 3.1, pg 125
|
||||
if (roleToTest == null)
|
||||
roleToTest = role;
|
||||
|
||||
for (String r :_roles)
|
||||
if (r.equals(role))
|
||||
if (r.equals(roleToTest))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* RoleInfo
|
||||
*
|
||||
* Badly named class that holds the role and user data constraint info for a
|
||||
* path/http method combination, extracted and combined from security
|
||||
@ -31,11 +32,15 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
*/
|
||||
public class RoleInfo
|
||||
{
|
||||
private boolean _isAnyAuth;
|
||||
private boolean _isAnyRole;
|
||||
private boolean _checked;
|
||||
private boolean _forbidden;
|
||||
private UserDataConstraint _userDataConstraint;
|
||||
|
||||
/**
|
||||
* List of permitted roles
|
||||
*/
|
||||
private final Set<String> _roles = new CopyOnWriteArraySet<String>();
|
||||
|
||||
public RoleInfo()
|
||||
@ -55,6 +60,7 @@ public class RoleInfo
|
||||
_forbidden=false;
|
||||
_roles.clear();
|
||||
_isAnyRole=false;
|
||||
_isAnyAuth=false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +77,7 @@ public class RoleInfo
|
||||
_checked = true;
|
||||
_userDataConstraint = null;
|
||||
_isAnyRole=false;
|
||||
_isAnyAuth=false;
|
||||
_roles.clear();
|
||||
}
|
||||
}
|
||||
@ -84,10 +91,19 @@ public class RoleInfo
|
||||
{
|
||||
this._isAnyRole=anyRole;
|
||||
if (anyRole)
|
||||
{
|
||||
_checked = true;
|
||||
_roles.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAnyAuth ()
|
||||
{
|
||||
return _isAnyAuth;
|
||||
}
|
||||
|
||||
public void setAnyAuth(boolean anyAuth)
|
||||
{
|
||||
this._isAnyAuth=anyAuth;
|
||||
if (anyAuth)
|
||||
_checked = true;
|
||||
}
|
||||
|
||||
public UserDataConstraint getUserDataConstraint()
|
||||
@ -100,6 +116,7 @@ public class RoleInfo
|
||||
if (userDataConstraint == null) throw new NullPointerException("Null UserDataConstraint");
|
||||
if (this._userDataConstraint == null)
|
||||
{
|
||||
|
||||
this._userDataConstraint = userDataConstraint;
|
||||
}
|
||||
else
|
||||
@ -126,6 +143,8 @@ public class RoleInfo
|
||||
setChecked(true);
|
||||
else if (other._isAnyRole)
|
||||
setAnyRole(true);
|
||||
else if (other._isAnyAuth)
|
||||
setAnyAuth(true);
|
||||
else if (!_isAnyRole)
|
||||
{
|
||||
for (String r : other._roles)
|
||||
@ -138,6 +157,6 @@ public class RoleInfo
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "{RoleInfo"+(_forbidden?",F":"")+(_checked?",C":"")+(_isAnyRole?",*":_roles)+"}";
|
||||
return "{RoleInfo"+(_forbidden?",F":"")+(_checked?",C":"")+(_isAnyRole?",*":_roles)+(_userDataConstraint!=null?","+_userDataConstraint:"")+"}";
|
||||
}
|
||||
}
|
||||
|
@ -462,6 +462,10 @@ public abstract class SecurityHandler extends HandlerWrapper implements Authenti
|
||||
|
||||
if (checkSecurity(baseRequest))
|
||||
{
|
||||
//See Servlet Spec 3.1 sec 13.6.3
|
||||
if (authenticator != null)
|
||||
authenticator.prepareRequest(baseRequest);
|
||||
|
||||
RoleInfo roleInfo = prepareConstraintInfo(pathInContext, baseRequest);
|
||||
|
||||
// Check data constraints
|
||||
|
@ -18,39 +18,20 @@
|
||||
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import org.eclipse.jetty.server.Authentication;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.server.UserIdentity.Scope;
|
||||
|
||||
|
||||
/**
|
||||
* @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
|
||||
*/
|
||||
public class UserAuthentication implements Authentication.User
|
||||
public class UserAuthentication extends AbstractUserAuthentication
|
||||
{
|
||||
private final String _method;
|
||||
private final UserIdentity _userIdentity;
|
||||
|
||||
|
||||
public UserAuthentication(String method, UserIdentity userIdentity)
|
||||
{
|
||||
_method = method;
|
||||
_userIdentity = userIdentity;
|
||||
}
|
||||
|
||||
public String getAuthMethod()
|
||||
{
|
||||
return _method;
|
||||
super(method, userIdentity);
|
||||
}
|
||||
|
||||
public UserIdentity getUserIdentity()
|
||||
{
|
||||
return _userIdentity;
|
||||
}
|
||||
|
||||
public boolean isUserInRole(Scope scope, String role)
|
||||
{
|
||||
return _userIdentity.isUserInRole(role, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
|
@ -28,6 +28,7 @@ import java.util.Locale;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@ -310,6 +311,11 @@ public class DeferredAuthentication implements Authentication.Deferred
|
||||
public void setContentLength(int len)
|
||||
{
|
||||
}
|
||||
|
||||
public void setContentLengthLong(long len)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentType(String type)
|
||||
@ -345,6 +351,7 @@ public class DeferredAuthentication implements Authentication.Deferred
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@ -352,17 +359,33 @@ public class DeferredAuthentication implements Authentication.Deferred
|
||||
/* ------------------------------------------------------------ */
|
||||
private static ServletOutputStream __nullOut = new ServletOutputStream()
|
||||
{
|
||||
@Override
|
||||
public void write(int b) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void print(String s) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void println(String s) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setWriteListener(WriteListener writeListener)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -36,6 +36,7 @@ import javax.servlet.http.HttpSession;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.security.ServerAuthException;
|
||||
import org.eclipse.jetty.security.UserAuthentication;
|
||||
@ -43,6 +44,7 @@ import org.eclipse.jetty.server.Authentication;
|
||||
import org.eclipse.jetty.server.Authentication.User;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.util.MultiMap;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
@ -75,6 +77,7 @@ public class FormAuthenticator extends LoginAuthenticator
|
||||
public final static String __FORM_DISPATCH="org.eclipse.jetty.security.dispatch";
|
||||
public final static String __J_URI = "org.eclipse.jetty.security.form_URI";
|
||||
public final static String __J_POST = "org.eclipse.jetty.security.form_POST";
|
||||
public final static String __J_METHOD = "org.eclipse.jetty.security.form_METHOD";
|
||||
public final static String __J_SECURITY_CHECK = "/j_security_check";
|
||||
public final static String __J_USERNAME = "j_username";
|
||||
public final static String __J_PASSWORD = "j_password";
|
||||
@ -198,6 +201,45 @@ public class FormAuthenticator extends LoginAuthenticator
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void prepareRequest(ServletRequest request)
|
||||
{
|
||||
//if this is a request resulting from a redirect after auth is complete
|
||||
//(ie its from a redirect to the original request uri) then due to
|
||||
//browser handling of 302 redirects, the method may not be the same as
|
||||
//that of the original request. Replace the method and original post
|
||||
//params (if it was a post).
|
||||
//
|
||||
//See Servlet Spec 3.1 sec 13.6.3
|
||||
HttpServletRequest httpRequest = (HttpServletRequest)request;
|
||||
HttpSession session = httpRequest.getSession(false);
|
||||
if (session == null || session.getAttribute(SessionAuthentication.__J_AUTHENTICATED) == null)
|
||||
return; //not authenticated yet
|
||||
|
||||
String juri = (String)session.getAttribute(__J_URI);
|
||||
if (juri == null || juri.length() == 0)
|
||||
return; //no original uri saved
|
||||
|
||||
String method = (String)session.getAttribute(__J_METHOD);
|
||||
if (method == null || method.length() == 0)
|
||||
return; //didn't save original request method
|
||||
|
||||
StringBuffer buf = httpRequest.getRequestURL();
|
||||
if (httpRequest.getQueryString() != null)
|
||||
buf.append("?").append(httpRequest.getQueryString());
|
||||
|
||||
if (!juri.equals(buf.toString()))
|
||||
return; //this request is not for the same url as the original
|
||||
|
||||
//restore the original request's method on this request
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Restoring original method {} for {} with method {}", method, juri,httpRequest.getMethod());
|
||||
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
|
||||
HttpMethod m = HttpMethod.fromString(method);
|
||||
base_request.setMethod(m,m.asString());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
@ -249,7 +291,10 @@ public class FormAuthenticator extends LoginAuthenticator
|
||||
LOG.debug("authenticated {}->{}",form_auth,nuri);
|
||||
|
||||
response.setContentLength(0);
|
||||
response.sendRedirect(response.encodeRedirectURL(nuri));
|
||||
Response base_response = HttpChannel.getCurrentHttpChannel().getResponse();
|
||||
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
|
||||
int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
|
||||
base_response.sendRedirect(redirectCode, response.encodeRedirectURL(nuri));
|
||||
return form_auth;
|
||||
}
|
||||
|
||||
@ -273,7 +318,10 @@ public class FormAuthenticator extends LoginAuthenticator
|
||||
else
|
||||
{
|
||||
LOG.debug("auth failed {}->{}",username,_formErrorPage);
|
||||
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formErrorPage)));
|
||||
Response base_response = HttpChannel.getCurrentHttpChannel().getResponse();
|
||||
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
|
||||
int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
|
||||
base_response.sendRedirect(redirectCode, response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formErrorPage)));
|
||||
}
|
||||
|
||||
return Authentication.SEND_FAILURE;
|
||||
@ -298,28 +346,26 @@ public class FormAuthenticator extends LoginAuthenticator
|
||||
String j_uri=(String)session.getAttribute(__J_URI);
|
||||
if (j_uri!=null)
|
||||
{
|
||||
//check if the request is for the same url as the original and restore
|
||||
//params if it was a post
|
||||
LOG.debug("auth retry {}->{}",authentication,j_uri);
|
||||
MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(__J_POST);
|
||||
if (j_post!=null)
|
||||
StringBuffer buf = request.getRequestURL();
|
||||
if (request.getQueryString() != null)
|
||||
buf.append("?").append(request.getQueryString());
|
||||
|
||||
if (j_uri.equals(buf.toString()))
|
||||
{
|
||||
LOG.debug("auth rePOST {}->{}",authentication,j_uri);
|
||||
StringBuffer buf = request.getRequestURL();
|
||||
if (request.getQueryString() != null)
|
||||
buf.append("?").append(request.getQueryString());
|
||||
|
||||
if (j_uri.equals(buf.toString()))
|
||||
MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(__J_POST);
|
||||
if (j_post!=null)
|
||||
{
|
||||
// This is a retry of an original POST request
|
||||
// so restore method and parameters
|
||||
|
||||
session.removeAttribute(__J_POST);
|
||||
LOG.debug("auth rePOST {}->{}",authentication,j_uri);
|
||||
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
|
||||
base_request.setMethod(HttpMethod.POST,HttpMethod.POST.asString());
|
||||
base_request.setParameters(j_post);
|
||||
}
|
||||
}
|
||||
else
|
||||
session.removeAttribute(__J_URI);
|
||||
session.removeAttribute(__J_METHOD);
|
||||
session.removeAttribute(__J_POST);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG.debug("auth {}",authentication);
|
||||
@ -344,6 +390,7 @@ public class FormAuthenticator extends LoginAuthenticator
|
||||
if (request.getQueryString() != null)
|
||||
buf.append("?").append(request.getQueryString());
|
||||
session.setAttribute(__J_URI, buf.toString());
|
||||
session.setAttribute(__J_METHOD, request.getMethod());
|
||||
|
||||
if (MimeTypes.Type.FORM_ENCODED.is(req.getContentType()) && HttpMethod.POST.is(request.getMethod()))
|
||||
{
|
||||
@ -366,7 +413,10 @@ public class FormAuthenticator extends LoginAuthenticator
|
||||
else
|
||||
{
|
||||
LOG.debug("challenge {}->{}",session.getId(),_formLoginPage);
|
||||
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formLoginPage)));
|
||||
Response base_response = HttpChannel.getCurrentHttpChannel().getResponse();
|
||||
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
|
||||
int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
|
||||
base_response.sendRedirect(redirectCode, response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formLoginPage)));
|
||||
}
|
||||
return Authentication.SEND_CONTINUE;
|
||||
}
|
||||
|
@ -40,11 +40,20 @@ public abstract class LoginAuthenticator implements Authenticator
|
||||
protected LoginService _loginService;
|
||||
protected IdentityService _identityService;
|
||||
private boolean _renewSession;
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected LoginAuthenticator()
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void prepareRequest(ServletRequest request)
|
||||
{
|
||||
//empty implementation as the default
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public UserIdentity login(String username, Object password, ServletRequest request)
|
||||
@ -58,7 +67,7 @@ public abstract class LoginAuthenticator implements Authenticator
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void setConfiguration(AuthConfiguration configuration)
|
||||
{
|
||||
@ -70,12 +79,16 @@ public abstract class LoginAuthenticator implements Authenticator
|
||||
throw new IllegalStateException("No IdentityService for "+this+" in "+configuration);
|
||||
_renewSession=configuration.isSessionRenewedOnAuthentication();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public LoginService getLoginService()
|
||||
{
|
||||
return _loginService;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Change the session id.
|
||||
* The session is changed to a new instance with a new ID if and only if:<ul>
|
||||
* <li>A session exists.
|
||||
|
@ -29,16 +29,15 @@ import javax.servlet.http.HttpSessionBindingEvent;
|
||||
import javax.servlet.http.HttpSessionBindingListener;
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
|
||||
import org.eclipse.jetty.security.AbstractUserAuthentication;
|
||||
import org.eclipse.jetty.security.LoginService;
|
||||
import org.eclipse.jetty.security.SecurityHandler;
|
||||
import org.eclipse.jetty.server.Authentication;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.server.UserIdentity.Scope;
|
||||
import org.eclipse.jetty.server.session.AbstractSession;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class SessionAuthentication implements Authentication.User, Serializable, HttpSessionActivationListener, HttpSessionBindingListener
|
||||
public class SessionAuthentication extends AbstractUserAuthentication implements Serializable, HttpSessionActivationListener, HttpSessionBindingListener
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(SessionAuthentication.class);
|
||||
|
||||
@ -48,35 +47,17 @@ public class SessionAuthentication implements Authentication.User, Serializable,
|
||||
|
||||
public final static String __J_AUTHENTICATED="org.eclipse.jetty.security.UserIdentity";
|
||||
|
||||
private final String _method;
|
||||
private final String _name;
|
||||
private final Object _credentials;
|
||||
|
||||
private transient UserIdentity _userIdentity;
|
||||
private transient HttpSession _session;
|
||||
|
||||
public SessionAuthentication(String method, UserIdentity userIdentity, Object credentials)
|
||||
{
|
||||
_method = method;
|
||||
_userIdentity = userIdentity;
|
||||
_name=_userIdentity.getUserPrincipal().getName();
|
||||
super(method, userIdentity);
|
||||
_name=userIdentity.getUserPrincipal().getName();
|
||||
_credentials=credentials;
|
||||
}
|
||||
|
||||
public String getAuthMethod()
|
||||
{
|
||||
return _method;
|
||||
}
|
||||
|
||||
public UserIdentity getUserIdentity()
|
||||
{
|
||||
return _userIdentity;
|
||||
}
|
||||
|
||||
public boolean isUserInRole(Scope scope, String role)
|
||||
{
|
||||
return _userIdentity.isUserInRole(role, scope);
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException
|
||||
|
@ -19,7 +19,9 @@
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.matchers.JUnitMatchers.containsString;
|
||||
@ -28,6 +30,7 @@ import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -37,7 +40,12 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.HttpConstraintElement;
|
||||
import javax.servlet.HttpMethodConstraintElement;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletSecurityElement;
|
||||
import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
|
||||
import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@ -88,7 +96,8 @@ public class ConstraintTest
|
||||
SessionHandler _session = new SessionHandler();
|
||||
|
||||
HashLoginService _loginService = new HashLoginService(TEST_REALM);
|
||||
_loginService.putUser("user",new Password("password"));
|
||||
_loginService.putUser("user0", new Password("password"), new String[]{});
|
||||
_loginService.putUser("user",new Password("password"), new String[] {"user"});
|
||||
_loginService.putUser("user2",new Password("password"), new String[] {"user"});
|
||||
_loginService.putUser("admin",new Password("password"), new String[] {"user","administrator"});
|
||||
_loginService.putUser("user3", new Password("password"), new String[] {"foo"});
|
||||
@ -180,7 +189,16 @@ public class ConstraintTest
|
||||
mapping6.setPathSpec("/data/*");
|
||||
mapping6.setConstraint(constraint6);
|
||||
|
||||
return Arrays.asList(mapping0, mapping1, mapping2, mapping3, mapping4, mapping5, mapping6);
|
||||
Constraint constraint7 = new Constraint();
|
||||
constraint7.setAuthenticate(true);
|
||||
constraint7.setName("** constraint");
|
||||
constraint7.setRoles(new String[]{Constraint.ANY_AUTH,"user"}); //the "user" role is superfluous once ** has been defined
|
||||
ConstraintMapping mapping7 = new ConstraintMapping();
|
||||
mapping7.setPathSpec("/starstar/*");
|
||||
mapping7.setConstraint(constraint7);
|
||||
|
||||
|
||||
return Arrays.asList(mapping0, mapping1, mapping2, mapping3, mapping4, mapping5, mapping6, mapping7);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -208,6 +226,224 @@ public class ConstraintTest
|
||||
assertTrue (mappings.get(2).getConstraint().getAuthenticate());
|
||||
assertFalse(mappings.get(3).getConstraint().getAuthenticate());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-1
|
||||
* @ServletSecurity
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testSecurityElementExample13_1() throws Exception
|
||||
{
|
||||
ServletSecurityElement element = new ServletSecurityElement();
|
||||
List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath("foo", "/foo/*", element);
|
||||
assertTrue(mappings.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-2
|
||||
* @ServletSecurity(@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL))
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testSecurityElementExample13_2() throws Exception
|
||||
{
|
||||
HttpConstraintElement httpConstraintElement = new HttpConstraintElement(TransportGuarantee.CONFIDENTIAL, new String[]{});
|
||||
ServletSecurityElement element = new ServletSecurityElement(httpConstraintElement);
|
||||
List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath("foo", "/foo/*", element);
|
||||
assertTrue(!mappings.isEmpty());
|
||||
assertEquals(1, mappings.size());
|
||||
ConstraintMapping mapping = mappings.get(0);
|
||||
assertEquals(2, mapping.getConstraint().getDataConstraint());
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-3
|
||||
* @ServletSecurity(@HttpConstraint(EmptyRoleSemantic.DENY))
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testSecurityElementExample13_3() throws Exception
|
||||
{
|
||||
HttpConstraintElement httpConstraintElement = new HttpConstraintElement(EmptyRoleSemantic.DENY);
|
||||
ServletSecurityElement element = new ServletSecurityElement(httpConstraintElement);
|
||||
List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath("foo", "/foo/*", element);
|
||||
assertTrue(!mappings.isEmpty());
|
||||
assertEquals(1, mappings.size());
|
||||
ConstraintMapping mapping = mappings.get(0);
|
||||
assertTrue(mapping.getConstraint().isForbidden());
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-4
|
||||
* @ServletSecurity(@HttpConstraint(rolesAllowed = "R1"))
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testSecurityElementExample13_4() throws Exception
|
||||
{
|
||||
HttpConstraintElement httpConstraintElement = new HttpConstraintElement(TransportGuarantee.NONE, "R1");
|
||||
ServletSecurityElement element = new ServletSecurityElement(httpConstraintElement);
|
||||
List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath("foo", "/foo/*", element);
|
||||
assertTrue(!mappings.isEmpty());
|
||||
assertEquals(1, mappings.size());
|
||||
ConstraintMapping mapping = mappings.get(0);
|
||||
assertTrue(mapping.getConstraint().getAuthenticate());
|
||||
assertTrue(mapping.getConstraint().getRoles() != null);
|
||||
assertEquals(1, mapping.getConstraint().getRoles().length);
|
||||
assertEquals("R1", mapping.getConstraint().getRoles()[0]);
|
||||
assertEquals(0, mapping.getConstraint().getDataConstraint());
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-5
|
||||
* @ServletSecurity((httpMethodConstraints = {
|
||||
* @HttpMethodConstraint(value = "GET", rolesAllowed = "R1"),
|
||||
* @HttpMethodConstraint(value = "POST", rolesAllowed = "R1",
|
||||
* transportGuarantee = TransportGuarantee.CONFIDENTIAL)})
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testSecurityElementExample13_5() throws Exception
|
||||
{
|
||||
List<HttpMethodConstraintElement> methodElements = new ArrayList<HttpMethodConstraintElement>();
|
||||
methodElements.add(new HttpMethodConstraintElement("GET", new HttpConstraintElement(TransportGuarantee.NONE, "R1")));
|
||||
methodElements.add(new HttpMethodConstraintElement("POST", new HttpConstraintElement(TransportGuarantee.CONFIDENTIAL, "R1")));
|
||||
ServletSecurityElement element = new ServletSecurityElement(methodElements);
|
||||
List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath("foo", "/foo/*", element);
|
||||
assertTrue(!mappings.isEmpty());
|
||||
assertEquals(2, mappings.size());
|
||||
assertEquals("GET", mappings.get(0).getMethod());
|
||||
assertEquals("R1", mappings.get(0).getConstraint().getRoles()[0]);
|
||||
assertTrue(mappings.get(0).getMethodOmissions() == null);
|
||||
assertEquals(0, mappings.get(0).getConstraint().getDataConstraint());
|
||||
assertEquals("POST", mappings.get(1).getMethod());
|
||||
assertEquals("R1", mappings.get(1).getConstraint().getRoles()[0]);
|
||||
assertEquals(2, mappings.get(1).getConstraint().getDataConstraint());
|
||||
assertTrue(mappings.get(1).getMethodOmissions() == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-6
|
||||
* @ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"), httpMethodConstraints = @HttpMethodConstraint("GET"))
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testSecurityElementExample13_6 () throws Exception
|
||||
{
|
||||
List<HttpMethodConstraintElement> methodElements = new ArrayList<HttpMethodConstraintElement>();
|
||||
methodElements.add(new HttpMethodConstraintElement("GET"));
|
||||
ServletSecurityElement element = new ServletSecurityElement(new HttpConstraintElement(TransportGuarantee.NONE, "R1"), methodElements);
|
||||
List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath("foo", "/foo/*", element);
|
||||
assertTrue(!mappings.isEmpty());
|
||||
assertEquals(2, mappings.size());
|
||||
assertTrue(mappings.get(0).getMethodOmissions() != null);
|
||||
assertEquals("GET", mappings.get(0).getMethodOmissions()[0]);
|
||||
assertTrue(mappings.get(0).getConstraint().getAuthenticate());
|
||||
assertEquals("R1", mappings.get(0).getConstraint().getRoles()[0]);
|
||||
assertEquals("GET", mappings.get(1).getMethod());
|
||||
assertTrue(mappings.get(1).getMethodOmissions() == null);
|
||||
assertEquals(0, mappings.get(1).getConstraint().getDataConstraint());
|
||||
assertFalse(mappings.get(1).getConstraint().getAuthenticate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-7
|
||||
* @ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"),
|
||||
* httpMethodConstraints = @HttpMethodConstraint(value="TRACE",
|
||||
* emptyRoleSemantic = EmptyRoleSemantic.DENY))
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testSecurityElementExample13_7() throws Exception
|
||||
{
|
||||
List<HttpMethodConstraintElement> methodElements = new ArrayList<HttpMethodConstraintElement>();
|
||||
methodElements.add(new HttpMethodConstraintElement("TRACE", new HttpConstraintElement(EmptyRoleSemantic.DENY)));
|
||||
ServletSecurityElement element = new ServletSecurityElement(new HttpConstraintElement(TransportGuarantee.NONE, "R1"), methodElements);
|
||||
List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath("foo", "/foo/*", element);
|
||||
assertTrue(!mappings.isEmpty());
|
||||
assertEquals(2, mappings.size());
|
||||
assertTrue(mappings.get(0).getMethodOmissions() != null);
|
||||
assertEquals("TRACE", mappings.get(0).getMethodOmissions()[0]);
|
||||
assertTrue(mappings.get(0).getConstraint().getAuthenticate());
|
||||
assertEquals("R1", mappings.get(0).getConstraint().getRoles()[0]);
|
||||
assertEquals("TRACE", mappings.get(1).getMethod());
|
||||
assertTrue(mappings.get(1).getMethodOmissions() == null);
|
||||
assertEquals(0, mappings.get(1).getConstraint().getDataConstraint());
|
||||
assertTrue(mappings.get(1).getConstraint().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUncoveredHttpMethodDetection() throws Exception
|
||||
{
|
||||
//Test no methods named
|
||||
Constraint constraint1 = new Constraint();
|
||||
constraint1.setAuthenticate(true);
|
||||
constraint1.setName("** constraint");
|
||||
constraint1.setRoles(new String[]{Constraint.ANY_AUTH,"user"}); //No methods named, no uncovered methods
|
||||
ConstraintMapping mapping1 = new ConstraintMapping();
|
||||
mapping1.setPathSpec("/starstar/*");
|
||||
mapping1.setConstraint(constraint1);
|
||||
|
||||
_security.setConstraintMappings(Collections.singletonList(mapping1));
|
||||
_security.setAuthenticator(new BasicAuthenticator());
|
||||
_server.start();
|
||||
|
||||
Set<String> uncoveredPaths = _security.getPathsWithUncoveredHttpMethods();
|
||||
assertTrue(uncoveredPaths.isEmpty()); //no uncovered methods
|
||||
|
||||
//Test only an explicitly named method, no omissions to cover other methods
|
||||
Constraint constraint2 = new Constraint();
|
||||
constraint2.setAuthenticate(true);
|
||||
constraint2.setName("user constraint");
|
||||
constraint2.setRoles(new String[]{"user"});
|
||||
ConstraintMapping mapping2 = new ConstraintMapping();
|
||||
mapping2.setPathSpec("/user/*");
|
||||
mapping2.setMethod("GET");
|
||||
mapping2.setConstraint(constraint2);
|
||||
|
||||
_security.addConstraintMapping(mapping2);
|
||||
uncoveredPaths = _security.getPathsWithUncoveredHttpMethods();
|
||||
assertNotNull(uncoveredPaths);
|
||||
assertEquals(1, uncoveredPaths.size());
|
||||
assertTrue(uncoveredPaths.contains("/user/*"));
|
||||
|
||||
//Test an explicitly named method with a http-method-omission to cover all other methods
|
||||
Constraint constraint2a = new Constraint();
|
||||
constraint2a.setAuthenticate(true);
|
||||
constraint2a.setName("forbid constraint");
|
||||
ConstraintMapping mapping2a = new ConstraintMapping();
|
||||
mapping2a.setPathSpec("/user/*");
|
||||
mapping2a.setMethodOmissions(new String[]{"GET"});
|
||||
mapping2a.setConstraint(constraint2a);
|
||||
|
||||
_security.addConstraintMapping(mapping2a);
|
||||
uncoveredPaths = _security.getPathsWithUncoveredHttpMethods();
|
||||
assertNotNull(uncoveredPaths);
|
||||
assertEquals(0, uncoveredPaths.size());
|
||||
|
||||
//Test a http-method-omission only
|
||||
Constraint constraint3 = new Constraint();
|
||||
constraint3.setAuthenticate(true);
|
||||
constraint3.setName("omit constraint");
|
||||
ConstraintMapping mapping3 = new ConstraintMapping();
|
||||
mapping3.setPathSpec("/omit/*");
|
||||
mapping3.setMethodOmissions(new String[]{"GET", "POST"});
|
||||
mapping3.setConstraint(constraint3);
|
||||
|
||||
_security.addConstraintMapping(mapping3);
|
||||
uncoveredPaths = _security.getPathsWithUncoveredHttpMethods();
|
||||
assertNotNull(uncoveredPaths);
|
||||
assertTrue(uncoveredPaths.contains("/omit/*"));
|
||||
|
||||
_security.setDenyUncoveredHttpMethods(true);
|
||||
uncoveredPaths = _security.getPathsWithUncoveredHttpMethods();
|
||||
assertNotNull(uncoveredPaths);
|
||||
assertEquals(0, uncoveredPaths.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasic() throws Exception
|
||||
@ -252,7 +488,6 @@ public class ConstraintTest
|
||||
|
||||
|
||||
_security.setAuthenticator(new BasicAuthenticator());
|
||||
_security.setStrict(false);
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
@ -377,7 +612,6 @@ public class ConstraintTest
|
||||
DigestAuthenticator authenticator = new DigestAuthenticator();
|
||||
authenticator.setMaxNonceCount(5);
|
||||
_security.setAuthenticator(authenticator);
|
||||
_security.setStrict(false);
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
@ -464,7 +698,6 @@ public class ConstraintTest
|
||||
public void testFormDispatch() throws Exception
|
||||
{
|
||||
_security.setAuthenticator(new FormAuthenticator("/testLoginPage","/testErrorPage",true));
|
||||
_security.setStrict(false);
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
@ -519,7 +752,6 @@ public class ConstraintTest
|
||||
public void testFormRedirect() throws Exception
|
||||
{
|
||||
_security.setAuthenticator(new FormAuthenticator("/testLoginPage","/testErrorPage",false));
|
||||
_security.setStrict(false);
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
@ -576,7 +808,6 @@ public class ConstraintTest
|
||||
public void testFormPostRedirect() throws Exception
|
||||
{
|
||||
_security.setAuthenticator(new FormAuthenticator("/testLoginPage","/testErrorPage",false));
|
||||
_security.setStrict(false);
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
@ -608,6 +839,7 @@ public class ConstraintTest
|
||||
"Content-Length: 31\r\n" +
|
||||
"\r\n" +
|
||||
"j_username=user&j_password=wrong\r\n");
|
||||
|
||||
assertThat(response,containsString("Location"));
|
||||
|
||||
response = _connector.getResponses("POST /ctx/j_security_check HTTP/1.0\r\n" +
|
||||
@ -646,7 +878,6 @@ public class ConstraintTest
|
||||
public void testFormNoCookies() throws Exception
|
||||
{
|
||||
_security.setAuthenticator(new FormAuthenticator("/testLoginPage","/testErrorPage",false));
|
||||
_security.setStrict(false);
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
@ -719,7 +950,7 @@ public class ConstraintTest
|
||||
assertThat(response,containsString("WWW-Authenticate: basic realm=\"TestRealm\""));
|
||||
|
||||
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n" +
|
||||
"Authorization: Basic " + B64Code.encode("user:password") + "\r\n" +
|
||||
"Authorization: Basic " + B64Code.encode("user3:password") + "\r\n" +
|
||||
"\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 403"));
|
||||
|
||||
@ -793,9 +1024,9 @@ public class ConstraintTest
|
||||
response = _connector.getResponses("POST /ctx/j_security_check HTTP/1.0\r\n" +
|
||||
"Cookie: JSESSIONID=" + session + "\r\n" +
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" +
|
||||
"Content-Length: 35\r\n" +
|
||||
"Content-Length: 36\r\n" +
|
||||
"\r\n" +
|
||||
"j_username=user&j_password=password\r\n");
|
||||
"j_username=user0&j_password=password\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 302 "));
|
||||
assertThat(response,containsString("Location"));
|
||||
assertThat(response,containsString("/ctx/auth/info"));
|
||||
@ -904,9 +1135,9 @@ public class ConstraintTest
|
||||
response = _connector.getResponses("POST /ctx/j_security_check HTTP/1.0\r\n" +
|
||||
"Cookie: JSESSIONID=" + session + "\r\n" +
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" +
|
||||
"Content-Length: 35\r\n" +
|
||||
"Content-Length: 36\r\n" +
|
||||
"\r\n" +
|
||||
"j_username=user&j_password=password\r\n");
|
||||
"j_username=user3&j_password=password\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 302 "));
|
||||
assertThat(response,containsString("Location"));
|
||||
assertThat(response,containsString("/ctx/auth/info"));
|
||||
@ -949,12 +1180,35 @@ public class ConstraintTest
|
||||
"\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
||||
|
||||
//check user2 does not have right role to access /admin/*
|
||||
response = _connector.getResponses("GET /ctx/admin/info HTTP/1.0\r\n" +
|
||||
"Cookie: JSESSIONID=" + session + "\r\n" +
|
||||
"\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 403"));
|
||||
assertThat(response,containsString("!role"));
|
||||
|
||||
//log in as user3, who doesn't have a valid role, but we are checking a constraint
|
||||
//of ** which just means they have to be authenticated
|
||||
response = _connector.getResponses("GET /ctx/starstar/info HTTP/1.0\r\n\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 302 "));
|
||||
assertThat(response,containsString("testLoginPage"));
|
||||
session = response.substring(response.indexOf("JSESSIONID=") + 11, response.indexOf(";Path=/ctx"));
|
||||
|
||||
response = _connector.getResponses("POST /ctx/j_security_check HTTP/1.0\r\n" +
|
||||
"Cookie: JSESSIONID=" + session + "\r\n" +
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" +
|
||||
"Content-Length: 36\r\n" +
|
||||
"\r\n" +
|
||||
"j_username=user3&j_password=password\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 302 "));
|
||||
assertThat(response,containsString("Location"));
|
||||
assertThat(response,containsString("/ctx/starstar/info"));
|
||||
session = response.substring(response.indexOf("JSESSIONID=") + 11, response.indexOf(";Path=/ctx"));
|
||||
|
||||
response = _connector.getResponses("GET /ctx/starstar/info HTTP/1.0\r\n" +
|
||||
"Cookie: JSESSIONID=" + session + "\r\n" +
|
||||
"\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
||||
|
||||
|
||||
// log in again as admin
|
||||
@ -1033,7 +1287,6 @@ public class ConstraintTest
|
||||
RoleCheckHandler check=new RoleCheckHandler();
|
||||
_security.setHandler(check);
|
||||
_security.setAuthenticator(new BasicAuthenticator());
|
||||
_security.setStrict(false);
|
||||
|
||||
_server.start();
|
||||
|
||||
@ -1065,7 +1318,6 @@ public class ConstraintTest
|
||||
public void testDeferredBasic() throws Exception
|
||||
{
|
||||
_security.setAuthenticator(new BasicAuthenticator());
|
||||
_security.setStrict(false);
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
@ -1092,7 +1344,6 @@ public class ConstraintTest
|
||||
public void testRelaxedMethod() throws Exception
|
||||
{
|
||||
_security.setAuthenticator(new BasicAuthenticator());
|
||||
_security.setStrict(false);
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@ -232,6 +233,45 @@ public class SpecExampleConstraintTest
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUncoveredHttpMethodDetection() throws Exception
|
||||
{
|
||||
_security.setAuthenticator(new BasicAuthenticator());
|
||||
_server.start();
|
||||
|
||||
Set<String> paths = _security.getPathsWithUncoveredHttpMethods();
|
||||
assertEquals(1, paths.size());
|
||||
assertEquals("/*", paths.iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUncoveredHttpMethodsDenied() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
_security.setDenyUncoveredHttpMethods(false);
|
||||
_security.setAuthenticator(new BasicAuthenticator());
|
||||
_server.start();
|
||||
|
||||
//There are uncovered methods for GET/POST at url /*
|
||||
//without deny-uncovered-http-methods they should be accessible
|
||||
String response;
|
||||
response = _connector.getResponses("GET /ctx/index.html HTTP/1.0\r\n\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
||||
|
||||
//set deny-uncovered-http-methods true
|
||||
_security.setDenyUncoveredHttpMethods(true);
|
||||
|
||||
//check they cannot be accessed
|
||||
response = _connector.getResponses("GET /ctx/index.html HTTP/1.0\r\n\r\n");
|
||||
assertTrue(response.startsWith("HTTP/1.1 403 Forbidden"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_security.setDenyUncoveredHttpMethods(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@ -239,7 +279,6 @@ public class SpecExampleConstraintTest
|
||||
{
|
||||
|
||||
_security.setAuthenticator(new BasicAuthenticator());
|
||||
_security.setStrict(false);
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
|
@ -2,7 +2,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
@ -89,8 +89,12 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<!--
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
-->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
@ -28,6 +28,8 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
|
||||
|
||||
public class AsyncContextState implements AsyncContext
|
||||
{
|
||||
@ -92,17 +94,20 @@ public class AsyncContextState implements AsyncContext
|
||||
|
||||
@Override
|
||||
public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException
|
||||
{
|
||||
{
|
||||
ContextHandler contextHandler = state().getContextHandler();
|
||||
if (contextHandler != null)
|
||||
return contextHandler.getServletContext().createListener(clazz);
|
||||
try
|
||||
{
|
||||
return clazz.newInstance();
|
||||
}
|
||||
catch(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dispatch()
|
||||
{
|
||||
|
@ -20,6 +20,8 @@ package org.eclipse.jetty.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.servlet.ReadListener;
|
||||
|
||||
/**
|
||||
* <p>An implementation of HttpInput using {@link ByteBuffer} as items.</p>
|
||||
*/
|
||||
|
@ -49,10 +49,10 @@ public class EncodingHttpWriter extends HttpWriter
|
||||
public void write (char[] s,int offset, int length) throws IOException
|
||||
{
|
||||
HttpOutput out = _out;
|
||||
if (length==0)
|
||||
if (length==0 && out.isAllContentWritten())
|
||||
{
|
||||
if (_out.isAllContentWritten())
|
||||
close();
|
||||
out.close();
|
||||
return;
|
||||
}
|
||||
|
||||
while (length > 0)
|
||||
|
@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.WriteListener;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
@ -42,7 +43,7 @@ import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.ChannelEndPoint;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.server.HttpChannelState.Next;
|
||||
import org.eclipse.jetty.server.HttpChannelState.Action;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.util.BlockingCallback;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
@ -249,36 +250,50 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||
// The loop is controlled by the call to async.unhandle in the
|
||||
// finally block below. Unhandle will return false only if an async dispatch has
|
||||
// already happened when unhandle is called.
|
||||
HttpChannelState.Next next = _state.handling();
|
||||
while (next==Next.CONTINUE && getServer().isRunning())
|
||||
HttpChannelState.Action action = _state.handling();
|
||||
loop: while (action.ordinal()<HttpChannelState.Action.WAIT.ordinal() && getServer().isRunning())
|
||||
{
|
||||
try
|
||||
{
|
||||
_request.setHandled(false);
|
||||
_response.getHttpOutput().reopen();
|
||||
|
||||
if (_state.isInitial())
|
||||
switch(action)
|
||||
{
|
||||
_request.setTimeStamp(System.currentTimeMillis());
|
||||
_request.setDispatcherType(DispatcherType.REQUEST);
|
||||
case REQUEST_DISPATCH:
|
||||
_request.setHandled(false);
|
||||
_response.getHttpOutput().reopen();
|
||||
_request.setTimeStamp(System.currentTimeMillis());
|
||||
_request.setDispatcherType(DispatcherType.REQUEST);
|
||||
|
||||
for (HttpConfiguration.Customizer customizer : _configuration.getCustomizers())
|
||||
customizer.customize(getConnector(),_configuration,_request);
|
||||
getServer().handle(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_request.getHttpChannelState().isExpired())
|
||||
{
|
||||
for (HttpConfiguration.Customizer customizer : _configuration.getCustomizers())
|
||||
customizer.customize(getConnector(),_configuration,_request);
|
||||
getServer().handle(this);
|
||||
break;
|
||||
|
||||
case ASYNC_DISPATCH:
|
||||
_request.setHandled(false);
|
||||
_response.getHttpOutput().reopen();
|
||||
_request.setDispatcherType(DispatcherType.ASYNC);
|
||||
getServer().handleAsync(this);
|
||||
break;
|
||||
|
||||
case ASYNC_EXPIRED:
|
||||
_request.setHandled(false);
|
||||
_response.getHttpOutput().reopen();
|
||||
_request.setDispatcherType(DispatcherType.ERROR);
|
||||
_request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500));
|
||||
_request.setAttribute(RequestDispatcher.ERROR_MESSAGE,"Async Timeout");
|
||||
_request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,_request.getRequestURI());
|
||||
_response.setStatusWithReason(500,"Async Timeout");
|
||||
}
|
||||
else
|
||||
_request.setDispatcherType(DispatcherType.ASYNC);
|
||||
getServer().handleAsync(this);
|
||||
|
||||
getServer().handleAsync(this);
|
||||
break;
|
||||
|
||||
case IO_CALLBACK:
|
||||
_response.getHttpOutput().handle();
|
||||
|
||||
break;
|
||||
default:
|
||||
break loop;
|
||||
|
||||
}
|
||||
}
|
||||
catch (Error e)
|
||||
@ -300,7 +315,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||
}
|
||||
finally
|
||||
{
|
||||
next = _state.unhandle();
|
||||
action = _state.unhandle();
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,7 +323,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||
Thread.currentThread().setName(threadName);
|
||||
setCurrentHttpChannel(null);
|
||||
|
||||
if (next==Next.COMPLETE)
|
||||
if (action==Action.COMPLETE)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -330,19 +345,19 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||
}
|
||||
finally
|
||||
{
|
||||
next=Next.RECYCLE;
|
||||
action=Action.RECYCLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (next==Next.RECYCLE)
|
||||
if (action==Action.RECYCLE)
|
||||
{
|
||||
_request.setHandled(true);
|
||||
_transport.completed();
|
||||
}
|
||||
|
||||
LOG.debug("{} handle exit, result {}", this, next);
|
||||
LOG.debug("{} handle exit, result {}", this, action);
|
||||
|
||||
return next!=Next.WAIT;
|
||||
return action!=Action.WAIT;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -598,7 +613,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||
|
||||
try
|
||||
{
|
||||
if (_state.handling()==Next.CONTINUE)
|
||||
if (_state.handling()==Action.REQUEST_DISPATCH)
|
||||
sendResponse(new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),0,status,reason,false),null,true);
|
||||
}
|
||||
catch (IOException e)
|
||||
@ -607,8 +622,10 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_state.unhandle()==Next.COMPLETE)
|
||||
if (_state.unhandle()==Action.COMPLETE)
|
||||
_state.completed();
|
||||
else
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ import javax.servlet.AsyncListener;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.WriteListener;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler.Context;
|
||||
@ -62,22 +63,30 @@ public class HttpChannelState
|
||||
{
|
||||
IDLE, // Idle request
|
||||
DISPATCHED, // Request dispatched to filter/servlet
|
||||
ASYNCSTARTED, // Suspend called, but not yet returned to container
|
||||
REDISPATCHING, // resumed while dispatched
|
||||
ASYNCWAIT, // Suspended and parked
|
||||
REDISPATCH, // Has been scheduled
|
||||
REDISPATCHED, // Request redispatched to filter/servlet
|
||||
COMPLETECALLED,// complete called
|
||||
ASYNCIO, // Has been dispatched for async IO
|
||||
COMPLETING, // Request is completable
|
||||
COMPLETED // Request is complete
|
||||
}
|
||||
|
||||
public enum Next
|
||||
public enum Action
|
||||
{
|
||||
CONTINUE, // Continue handling the channel
|
||||
WAIT, // Wait for further events
|
||||
COMPLETE, // Complete the channel
|
||||
RECYCLE, // Channel is completed
|
||||
REQUEST_DISPATCH, // handle a normal request dispatch
|
||||
ASYNC_DISPATCH, // handle an async request dispatch
|
||||
ASYNC_EXPIRED, // handle an async timeout
|
||||
IO_CALLBACK, // handle an IO callback
|
||||
WAIT, // Wait for further events
|
||||
COMPLETE, // Complete the channel
|
||||
RECYCLE, // Channel is completed
|
||||
}
|
||||
|
||||
public enum Async
|
||||
{
|
||||
STARTED,
|
||||
DISPATCH,
|
||||
COMPLETE,
|
||||
EXPIRING,
|
||||
EXPIRED
|
||||
}
|
||||
|
||||
private final HttpChannel<?> _channel;
|
||||
@ -85,10 +94,9 @@ public class HttpChannelState
|
||||
private List<AsyncListener> _asyncListeners;
|
||||
|
||||
private State _state;
|
||||
private Async _async;
|
||||
private boolean _initial;
|
||||
private boolean _dispatched;
|
||||
private boolean _expired;
|
||||
private volatile boolean _responseWrapped;
|
||||
private boolean _asyncIO;
|
||||
private long _timeoutMs=DEFAULT_TIMEOUT;
|
||||
private AsyncContextEvent _event;
|
||||
|
||||
@ -96,6 +104,7 @@ public class HttpChannelState
|
||||
{
|
||||
_channel=channel;
|
||||
_state=State.IDLE;
|
||||
_async=null;
|
||||
_initial=true;
|
||||
}
|
||||
|
||||
@ -154,17 +163,14 @@ public class HttpChannelState
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return _state+
|
||||
(_initial?",initial":"")+
|
||||
(_dispatched?",resumed":"")+
|
||||
(_expired?",expired":"");
|
||||
return String.format("s=%s i=%b a=%s",_state,_initial,_async);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Next handling of the request should proceed
|
||||
*/
|
||||
protected Next handling()
|
||||
protected Action handling()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
@ -182,60 +188,69 @@ public class HttpChannelState
|
||||
_asyncListeners=_lastAsyncListeners;
|
||||
_lastAsyncListeners=null;
|
||||
}
|
||||
break;
|
||||
|
||||
case COMPLETECALLED:
|
||||
_state=State.COMPLETING;
|
||||
return Next.COMPLETE;
|
||||
return Action.REQUEST_DISPATCH;
|
||||
|
||||
case COMPLETING:
|
||||
return Next.COMPLETE;
|
||||
|
||||
case ASYNCWAIT:
|
||||
return Next.WAIT;
|
||||
|
||||
case COMPLETED:
|
||||
return Next.RECYCLE;
|
||||
return Action.COMPLETE;
|
||||
|
||||
case REDISPATCH:
|
||||
_state=State.REDISPATCHED;
|
||||
break;
|
||||
case COMPLETED:
|
||||
return Action.RECYCLE;
|
||||
|
||||
case ASYNCWAIT:
|
||||
if (_asyncIO)
|
||||
{
|
||||
_state=State.ASYNCIO;
|
||||
_asyncIO=false;
|
||||
return Action.IO_CALLBACK;
|
||||
}
|
||||
|
||||
if (_async!=null)
|
||||
{
|
||||
Async async=_async;
|
||||
switch(async)
|
||||
{
|
||||
case COMPLETE:
|
||||
_state=State.COMPLETING;
|
||||
return Action.COMPLETE;
|
||||
case DISPATCH:
|
||||
_state=State.DISPATCHED;
|
||||
_async=null;
|
||||
return Action.ASYNC_DISPATCH;
|
||||
case EXPIRING:
|
||||
break;
|
||||
case EXPIRED:
|
||||
_state=State.DISPATCHED;
|
||||
_async=null;
|
||||
return Action.ASYNC_EXPIRED;
|
||||
case STARTED:
|
||||
// TODO
|
||||
LOG.warn("TODO Fix this double dispatch",new IllegalStateException(this.getStatusString()));
|
||||
return Action.WAIT;
|
||||
}
|
||||
}
|
||||
|
||||
return Action.WAIT;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
}
|
||||
|
||||
_responseWrapped=false;
|
||||
return Next.CONTINUE;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void startAsync(AsyncContextEvent event)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
{
|
||||
case DISPATCHED:
|
||||
case REDISPATCHED:
|
||||
_dispatched=false;
|
||||
_expired=false;
|
||||
_responseWrapped=event.getSuppliedResponse()!=_channel.getResponse();
|
||||
_responseWrapped=false;
|
||||
_event=event;
|
||||
_state=State.ASYNCSTARTED;
|
||||
List<AsyncListener> listeners=_lastAsyncListeners;
|
||||
_lastAsyncListeners=_asyncListeners;
|
||||
if (listeners!=null)
|
||||
listeners.clear();
|
||||
_asyncListeners=listeners;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
}
|
||||
if (_state!=State.DISPATCHED || _async!=null)
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
|
||||
_async=Async.STARTED;
|
||||
_event=event;
|
||||
List<AsyncListener> listeners=_lastAsyncListeners;
|
||||
_lastAsyncListeners=_asyncListeners;
|
||||
if (listeners!=null)
|
||||
listeners.clear();
|
||||
_asyncListeners=listeners;
|
||||
}
|
||||
|
||||
if (_lastAsyncListeners!=null)
|
||||
@ -270,39 +285,53 @@ public class HttpChannelState
|
||||
* @return next actions
|
||||
* be handled again (eg because of a resume that happened before unhandle was called)
|
||||
*/
|
||||
protected Next unhandle()
|
||||
protected Action unhandle()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
{
|
||||
case REDISPATCHED:
|
||||
case DISPATCHED:
|
||||
_state=State.COMPLETING;
|
||||
return Next.COMPLETE;
|
||||
|
||||
case IDLE:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
|
||||
case ASYNCSTARTED:
|
||||
_initial=false;
|
||||
_state=State.ASYNCWAIT;
|
||||
scheduleTimeout();
|
||||
return Next.WAIT;
|
||||
|
||||
case REDISPATCHING:
|
||||
_initial=false;
|
||||
_state=State.REDISPATCHED;
|
||||
return Next.CONTINUE;
|
||||
|
||||
case COMPLETECALLED:
|
||||
_initial=false;
|
||||
_state=State.COMPLETING;
|
||||
return Next.COMPLETE;
|
||||
|
||||
case ASYNCIO:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
}
|
||||
|
||||
if (_asyncIO)
|
||||
{
|
||||
_asyncIO=false;
|
||||
_state=State.ASYNCIO;
|
||||
return Action.IO_CALLBACK;
|
||||
}
|
||||
|
||||
if (_async!=null)
|
||||
{
|
||||
_initial=false;
|
||||
switch(_async)
|
||||
{
|
||||
case COMPLETE:
|
||||
_state=State.COMPLETING;
|
||||
_async=null;
|
||||
return Action.COMPLETE;
|
||||
case DISPATCH:
|
||||
_state=State.DISPATCHED;
|
||||
_async=null;
|
||||
return Action.ASYNC_DISPATCH;
|
||||
case EXPIRED:
|
||||
_state=State.DISPATCHED;
|
||||
_async=null;
|
||||
return Action.ASYNC_EXPIRED;
|
||||
case EXPIRING:
|
||||
case STARTED:
|
||||
scheduleTimeout();
|
||||
_state=State.ASYNCWAIT;
|
||||
return Action.WAIT;
|
||||
}
|
||||
}
|
||||
|
||||
_state=State.COMPLETING;
|
||||
return Action.COMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,39 +340,26 @@ public class HttpChannelState
|
||||
boolean dispatch;
|
||||
synchronized (this)
|
||||
{
|
||||
if (_async!=Async.STARTED && _async!=Async.EXPIRING)
|
||||
throw new IllegalStateException("AsyncContext#dispath "+this.getStatusString());
|
||||
_async=Async.DISPATCH;
|
||||
_event.setDispatchTarget(context,path);
|
||||
|
||||
switch(_state)
|
||||
{
|
||||
case ASYNCSTARTED:
|
||||
_state=State.REDISPATCHING;
|
||||
_event.setDispatchTarget(context,path);
|
||||
_dispatched=true;
|
||||
return;
|
||||
|
||||
case ASYNCWAIT:
|
||||
dispatch=!_expired;
|
||||
_state=State.REDISPATCH;
|
||||
_event.setDispatchTarget(context,path);
|
||||
_dispatched=true;
|
||||
case DISPATCHED:
|
||||
case ASYNCIO:
|
||||
dispatch=false;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
dispatch=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cancelTimeout();
|
||||
if (dispatch)
|
||||
{
|
||||
cancelTimeout();
|
||||
scheduleDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDispatched()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return _dispatched;
|
||||
}
|
||||
}
|
||||
|
||||
protected void expired()
|
||||
@ -352,17 +368,11 @@ public class HttpChannelState
|
||||
AsyncEvent event;
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
{
|
||||
case ASYNCSTARTED:
|
||||
case ASYNCWAIT:
|
||||
_expired=true;
|
||||
event=_event;
|
||||
aListeners=_asyncListeners;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (_async!=Async.STARTED)
|
||||
return;
|
||||
_async=Async.EXPIRING;
|
||||
event=_event;
|
||||
aListeners=_asyncListeners;
|
||||
}
|
||||
|
||||
if (aListeners!=null)
|
||||
@ -379,22 +389,20 @@ public class HttpChannelState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean dispatch=false;
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
if (_async==Async.EXPIRING)
|
||||
{
|
||||
case ASYNCSTARTED:
|
||||
case ASYNCWAIT:
|
||||
_state=State.REDISPATCH;
|
||||
break;
|
||||
default:
|
||||
_expired=false;
|
||||
break;
|
||||
_async=Async.EXPIRED;
|
||||
if (_state==State.ASYNCWAIT)
|
||||
dispatch=true;
|
||||
}
|
||||
}
|
||||
|
||||
scheduleDispatch();
|
||||
|
||||
if (dispatch)
|
||||
scheduleDispatch();
|
||||
}
|
||||
|
||||
public void complete()
|
||||
@ -403,30 +411,15 @@ public class HttpChannelState
|
||||
boolean handle;
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
{
|
||||
case DISPATCHED:
|
||||
case REDISPATCHED:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
|
||||
case IDLE:
|
||||
case ASYNCSTARTED:
|
||||
_state=State.COMPLETECALLED;
|
||||
return;
|
||||
|
||||
case ASYNCWAIT:
|
||||
_state=State.COMPLETECALLED;
|
||||
handle=!_expired;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
}
|
||||
if (_async!=Async.STARTED && _async!=Async.EXPIRING)
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
_async=Async.COMPLETE;
|
||||
handle=_state==State.ASYNCWAIT;
|
||||
}
|
||||
|
||||
cancelTimeout();
|
||||
if (handle)
|
||||
{
|
||||
cancelTimeout();
|
||||
ContextHandler handler=getContextHandler();
|
||||
if (handler!=null)
|
||||
handler.handle(_channel);
|
||||
@ -487,18 +480,18 @@ public class HttpChannelState
|
||||
switch(_state)
|
||||
{
|
||||
case DISPATCHED:
|
||||
case REDISPATCHED:
|
||||
case ASYNCIO:
|
||||
throw new IllegalStateException(getStatusString());
|
||||
default:
|
||||
_state=State.IDLE;
|
||||
break;
|
||||
}
|
||||
_state=State.IDLE;
|
||||
_async=null;
|
||||
_initial = true;
|
||||
_dispatched=false;
|
||||
_expired=false;
|
||||
_responseWrapped=false;
|
||||
cancelTimeout();
|
||||
_timeoutMs=DEFAULT_TIMEOUT;
|
||||
_event=null;
|
||||
_asyncIO=false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,7 +518,7 @@ public class HttpChannelState
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return _expired;
|
||||
return _async==Async.EXPIRED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -541,17 +534,7 @@ public class HttpChannelState
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
switch(_state)
|
||||
{
|
||||
case ASYNCSTARTED:
|
||||
case REDISPATCHING:
|
||||
case COMPLETECALLED:
|
||||
case ASYNCWAIT:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return _state==State.ASYNCWAIT || _state==State.DISPATCHED && _async==Async.STARTED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -566,16 +549,9 @@ public class HttpChannelState
|
||||
public boolean isAsyncStarted()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
{
|
||||
case ASYNCSTARTED:
|
||||
case ASYNCWAIT:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
{
|
||||
// Either we are started, or we are still dispatched and async has been completed or dispatched
|
||||
return _async==Async.STARTED || _async!=null && _state==State.DISPATCHED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -583,19 +559,7 @@ public class HttpChannelState
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
{
|
||||
case ASYNCSTARTED:
|
||||
case REDISPATCHING:
|
||||
case ASYNCWAIT:
|
||||
case REDISPATCHED:
|
||||
case REDISPATCH:
|
||||
case COMPLETECALLED:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return !_initial || _async!=null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -623,7 +587,7 @@ public class HttpChannelState
|
||||
|
||||
public ServletResponse getServletResponse()
|
||||
{
|
||||
if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
|
||||
if (_event!=null && _event.getSuppliedResponse()!=null)
|
||||
return _event.getSuppliedResponse();
|
||||
return _channel.getResponse();
|
||||
}
|
||||
@ -643,6 +607,31 @@ public class HttpChannelState
|
||||
_channel.getRequest().setAttribute(name,attribute);
|
||||
}
|
||||
|
||||
public void onReadPossible()
|
||||
{
|
||||
}
|
||||
|
||||
public void onWritePossible()
|
||||
{
|
||||
boolean handle;
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
|
||||
_asyncIO=true;
|
||||
handle=_state==State.ASYNCWAIT;
|
||||
}
|
||||
|
||||
if (handle)
|
||||
{
|
||||
ContextHandler handler=getContextHandler();
|
||||
if (handler!=null)
|
||||
handler.handle(_channel);
|
||||
else
|
||||
_channel.handle();
|
||||
}
|
||||
}
|
||||
|
||||
public class AsyncTimeout implements Runnable
|
||||
{
|
||||
@Override
|
||||
|
@ -39,7 +39,7 @@ import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.util.BlockingCallback;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IteratingCallback;
|
||||
import org.eclipse.jetty.util.IteratingNestedCallback;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
@ -586,7 +586,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||
}
|
||||
}
|
||||
|
||||
private class CommitCallback extends IteratingCallback
|
||||
private class CommitCallback extends IteratingNestedCallback
|
||||
{
|
||||
final ByteBuffer _content;
|
||||
final boolean _lastContent;
|
||||
@ -707,7 +707,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||
}
|
||||
}
|
||||
|
||||
private class ContentCallback extends IteratingCallback
|
||||
private class ContentCallback extends IteratingNestedCallback
|
||||
{
|
||||
final ByteBuffer _content;
|
||||
final boolean _lastContent;
|
||||
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.ReadListener;
|
||||
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
@ -291,4 +292,25 @@ public abstract class HttpInput<T> extends ServletInputStream
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener readListener)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -18,26 +18,28 @@
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritePendingException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.WriteListener;
|
||||
|
||||
import org.eclipse.jetty.http.HttpContent;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.util.BlockingCallback;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IteratingCallback;
|
||||
import org.eclipse.jetty.util.IteratingNestedCallback;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
||||
/**
|
||||
* <p>{@link HttpOutput} implements {@link ServletOutputStream}
|
||||
@ -53,10 +55,26 @@ public class HttpOutput extends ServletOutputStream
|
||||
{
|
||||
private static Logger LOG = Log.getLogger(HttpOutput.class);
|
||||
private final HttpChannel<?> _channel;
|
||||
private boolean _closed;
|
||||
private long _written;
|
||||
private ByteBuffer _aggregate;
|
||||
private int _bufferSize;
|
||||
private WriteListener _writeListener;
|
||||
private volatile Throwable _onError;
|
||||
|
||||
/*
|
||||
ACTION OPEN ASYNC READY PENDING UNREADY
|
||||
-------------------------------------------------------------------------------
|
||||
setWriteListener() READY->owp ise ise ise ise
|
||||
write() OPEN ise PENDING wpe wpe
|
||||
flush() OPEN ise PENDING wpe wpe
|
||||
isReady() OPEN:true READY:true READY:true UNREADY:false UNREADY:false
|
||||
write completed - - - ASYNC READY->owp
|
||||
|
||||
*/
|
||||
enum State { OPEN, ASYNC, READY, PENDING, UNREADY, CLOSED }
|
||||
private final AtomicReference<State> _state=new AtomicReference<>(State.OPEN);
|
||||
|
||||
|
||||
|
||||
public HttpOutput(HttpChannel<?> channel)
|
||||
{
|
||||
@ -82,49 +100,63 @@ public class HttpOutput extends ServletOutputStream
|
||||
|
||||
public void reopen()
|
||||
{
|
||||
_closed = false;
|
||||
_state.set(State.OPEN);
|
||||
}
|
||||
|
||||
/** Called by the HttpChannel if the output was closed
|
||||
* externally (eg by a 500 exception handling).
|
||||
*/
|
||||
void closed()
|
||||
public boolean isAllContentWritten()
|
||||
{
|
||||
if (!_closed)
|
||||
{
|
||||
_closed = true;
|
||||
try
|
||||
{
|
||||
_channel.getResponse().closeOutput();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
_channel.failed();
|
||||
LOG.ignore(e);
|
||||
}
|
||||
releaseBuffer();
|
||||
}
|
||||
return _channel.getResponse().isAllContentWritten(_written);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
if (!isClosed())
|
||||
State state=_state.get();
|
||||
while(state!=State.CLOSED)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
_channel.write(_aggregate, !_channel.getResponse().isIncluding());
|
||||
else
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding());
|
||||
}
|
||||
catch(IOException e)
|
||||
if (_state.compareAndSet(state,State.CLOSED))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
_channel.write(_aggregate, !_channel.getResponse().isIncluding());
|
||||
else
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding());
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
_channel.failed();
|
||||
LOG.ignore(e);
|
||||
LOG.ignore(e);
|
||||
}
|
||||
releaseBuffer();
|
||||
return;
|
||||
}
|
||||
state=_state.get();
|
||||
}
|
||||
}
|
||||
|
||||
/* Called to indicated that the output is already closed and the state needs to be updated to match */
|
||||
void closed()
|
||||
{
|
||||
State state=_state.get();
|
||||
while(state!=State.CLOSED)
|
||||
{
|
||||
if (_state.compareAndSet(state,State.CLOSED))
|
||||
{
|
||||
try
|
||||
{
|
||||
_channel.getResponse().closeOutput();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
_channel.failed();
|
||||
LOG.ignore(e);
|
||||
}
|
||||
releaseBuffer();
|
||||
return;
|
||||
}
|
||||
state=_state.get();
|
||||
}
|
||||
closed();
|
||||
}
|
||||
|
||||
private void releaseBuffer()
|
||||
@ -138,111 +170,215 @@ public class HttpOutput extends ServletOutputStream
|
||||
|
||||
public boolean isClosed()
|
||||
{
|
||||
return _closed;
|
||||
return _state.get()==State.CLOSED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException
|
||||
{
|
||||
if (isClosed())
|
||||
return;
|
||||
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
_channel.write(_aggregate, false);
|
||||
else
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER, false);
|
||||
while(true)
|
||||
{
|
||||
switch(_state.get())
|
||||
{
|
||||
case OPEN:
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
_channel.write(_aggregate, false);
|
||||
else
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER, false);
|
||||
return;
|
||||
|
||||
case ASYNC:
|
||||
throw new IllegalStateException("isReady() not called");
|
||||
|
||||
case READY:
|
||||
if (!_state.compareAndSet(State.READY, State.PENDING))
|
||||
continue;
|
||||
new AsyncFlush().process();
|
||||
return;
|
||||
|
||||
case PENDING:
|
||||
case UNREADY:
|
||||
throw new WritePendingException();
|
||||
|
||||
case CLOSED:
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAllContentWritten()
|
||||
{
|
||||
Response response=_channel.getResponse();
|
||||
return response.isAllContentWritten(_written);
|
||||
}
|
||||
|
||||
public void closeOutput() throws IOException
|
||||
{
|
||||
_channel.getResponse().closeOutput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if (isClosed())
|
||||
throw new EofException("Closed");
|
||||
_written+=len;
|
||||
boolean complete=_channel.getResponse().isAllContentWritten(_written);
|
||||
|
||||
_written+=len;
|
||||
boolean complete=_channel.getResponse().isAllContentWritten(_written);
|
||||
int capacity = getBufferSize();
|
||||
// Async or Blocking ?
|
||||
while(true)
|
||||
{
|
||||
switch(_state.get())
|
||||
{
|
||||
case OPEN:
|
||||
// process blocking below
|
||||
break;
|
||||
|
||||
case ASYNC:
|
||||
throw new IllegalStateException("isReady() not called");
|
||||
|
||||
// Should we aggregate?
|
||||
if (!complete && len<=capacity/4)
|
||||
{
|
||||
if (_aggregate == null)
|
||||
_aggregate = _channel.getByteBufferPool().acquire(capacity, false);
|
||||
case READY:
|
||||
if (!_state.compareAndSet(State.READY, State.PENDING))
|
||||
continue;
|
||||
|
||||
// YES - fill the aggregate with content from the buffer
|
||||
int filled = BufferUtil.fill(_aggregate, b, off, len);
|
||||
// Should we aggregate?
|
||||
int capacity = getBufferSize();
|
||||
if (!complete && len<=capacity/4)
|
||||
{
|
||||
if (_aggregate == null)
|
||||
_aggregate = _channel.getByteBufferPool().acquire(capacity, false);
|
||||
|
||||
// return if we are not complete, not full and filled all the content
|
||||
if (!complete && filled == len && !BufferUtil.isFull(_aggregate))
|
||||
return;
|
||||
// YES - fill the aggregate with content from the buffer
|
||||
int filled = BufferUtil.fill(_aggregate, b, off, len);
|
||||
|
||||
// adjust offset/length
|
||||
off += filled;
|
||||
len -= filled;
|
||||
}
|
||||
// return if we are not complete, not full and filled all the content
|
||||
if (!complete && filled==len && !BufferUtil.isFull(_aggregate))
|
||||
{
|
||||
if (!_state.compareAndSet(State.PENDING, State.ASYNC))
|
||||
throw new IllegalStateException();
|
||||
return;
|
||||
}
|
||||
|
||||
// flush any content from the aggregate
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
{
|
||||
_channel.write(_aggregate, complete && len==0);
|
||||
// adjust offset/length
|
||||
off+=filled;
|
||||
len-=filled;
|
||||
}
|
||||
|
||||
// should we fill aggregate again from the buffer?
|
||||
if (len>0 && !complete && len<=_aggregate.capacity()/4)
|
||||
{
|
||||
BufferUtil.append(_aggregate, b, off, len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Do the asynchronous writing from the callback
|
||||
new AsyncWrite(b,off,len,complete).process();
|
||||
return;
|
||||
|
||||
// write any remaining content in the buffer directly
|
||||
if (len>0)
|
||||
_channel.write(ByteBuffer.wrap(b, off, len), complete);
|
||||
case PENDING:
|
||||
case UNREADY:
|
||||
throw new WritePendingException();
|
||||
|
||||
case CLOSED:
|
||||
throw new EofException("Closed");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// handle blocking write
|
||||
|
||||
// Should we aggregate?
|
||||
int capacity = getBufferSize();
|
||||
if (!complete && len<=capacity/4)
|
||||
{
|
||||
if (_aggregate == null)
|
||||
_aggregate = _channel.getByteBufferPool().acquire(capacity, false);
|
||||
|
||||
// YES - fill the aggregate with content from the buffer
|
||||
int filled = BufferUtil.fill(_aggregate, b, off, len);
|
||||
|
||||
// return if we are not complete, not full and filled all the content
|
||||
if (!complete && filled==len && !BufferUtil.isFull(_aggregate))
|
||||
return;
|
||||
|
||||
// adjust offset/length
|
||||
off+=filled;
|
||||
len-=filled;
|
||||
}
|
||||
|
||||
// flush any content from the aggregate
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
{
|
||||
_channel.write(_aggregate, complete && len==0);
|
||||
|
||||
// should we fill aggregate again from the buffer?
|
||||
if (len>0 && !complete && len<=_aggregate.capacity()/4)
|
||||
{
|
||||
BufferUtil.append(_aggregate, b, off, len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// write any remaining content in the buffer directly
|
||||
if (len>0)
|
||||
_channel.write(ByteBuffer.wrap(b, off, len), complete);
|
||||
else if (complete)
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER,complete);
|
||||
|
||||
if (complete)
|
||||
closed();
|
||||
}
|
||||
if (complete)
|
||||
{
|
||||
closed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException
|
||||
{
|
||||
if (isClosed())
|
||||
throw new EOFException("Closed");
|
||||
|
||||
// Allocate an aggregate buffer.
|
||||
// Never direct as it is slow to do little writes to a direct buffer.
|
||||
if (_aggregate == null)
|
||||
_aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
|
||||
|
||||
BufferUtil.append(_aggregate, (byte)b);
|
||||
_written++;
|
||||
|
||||
_written+=1;
|
||||
boolean complete=_channel.getResponse().isAllContentWritten(_written);
|
||||
|
||||
// Check if all written or full
|
||||
if (complete || BufferUtil.isFull(_aggregate))
|
||||
|
||||
// Async or Blocking ?
|
||||
while(true)
|
||||
{
|
||||
BlockingCallback callback = _channel.getWriteBlockingCallback();
|
||||
_channel.write(_aggregate, false, callback);
|
||||
callback.block();
|
||||
if (complete)
|
||||
closed();
|
||||
switch(_state.get())
|
||||
{
|
||||
case OPEN:
|
||||
if (_aggregate == null)
|
||||
_aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
|
||||
BufferUtil.append(_aggregate, (byte)b);
|
||||
|
||||
// Check if all written or full
|
||||
if (complete || BufferUtil.isFull(_aggregate))
|
||||
{
|
||||
BlockingCallback callback = _channel.getWriteBlockingCallback();
|
||||
_channel.write(_aggregate, complete, callback);
|
||||
callback.block();
|
||||
if (complete)
|
||||
closed();
|
||||
}
|
||||
break;
|
||||
|
||||
case ASYNC:
|
||||
throw new IllegalStateException("isReady() not called");
|
||||
|
||||
case READY:
|
||||
if (!_state.compareAndSet(State.READY, State.PENDING))
|
||||
continue;
|
||||
|
||||
if (_aggregate == null)
|
||||
_aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
|
||||
BufferUtil.append(_aggregate, (byte)b);
|
||||
|
||||
// Check if all written or full
|
||||
if (!complete && !BufferUtil.isFull(_aggregate))
|
||||
{
|
||||
if (!_state.compareAndSet(State.PENDING, State.ASYNC))
|
||||
throw new IllegalStateException();
|
||||
return;
|
||||
}
|
||||
|
||||
// Do the asynchronous writing from the callback
|
||||
new AsyncFlush().process();
|
||||
return;
|
||||
|
||||
case PENDING:
|
||||
case UNREADY:
|
||||
throw new WritePendingException();
|
||||
|
||||
case CLOSED:
|
||||
throw new EofException("Closed");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void print(String s) throws IOException
|
||||
{
|
||||
@ -252,51 +388,6 @@ public class HttpOutput extends ServletOutputStream
|
||||
write(s.getBytes(_channel.getResponse().getCharacterEncoding()));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Set headers and send content.
|
||||
* @deprecated Use {@link Response#setHeaders(HttpContent)} and {@link #sendContent(HttpContent)} instead.
|
||||
* @param content
|
||||
* @throws IOException
|
||||
*/
|
||||
@Deprecated
|
||||
public void sendContent(Object content) throws IOException
|
||||
{
|
||||
final BlockingCallback callback =_channel.getWriteBlockingCallback();
|
||||
|
||||
if (content instanceof HttpContent)
|
||||
{
|
||||
_channel.getResponse().setHeaders((HttpContent)content);
|
||||
sendContent((HttpContent)content,callback);
|
||||
}
|
||||
else if (content instanceof Resource)
|
||||
{
|
||||
Resource resource = (Resource)content;
|
||||
_channel.getResponse().getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, resource.lastModified());
|
||||
|
||||
ReadableByteChannel in=((Resource)content).getReadableByteChannel();
|
||||
if (in!=null)
|
||||
sendContent(in,callback);
|
||||
else
|
||||
sendContent(resource.getInputStream(),callback);
|
||||
}
|
||||
else if (content instanceof ByteBuffer)
|
||||
{
|
||||
sendContent((ByteBuffer)content,callback);
|
||||
}
|
||||
else if (content instanceof ReadableByteChannel)
|
||||
{
|
||||
sendContent((ReadableByteChannel)content,callback);
|
||||
}
|
||||
else if (content instanceof InputStream)
|
||||
{
|
||||
sendContent((InputStream)content,callback);
|
||||
}
|
||||
else
|
||||
callback.failed(new IllegalArgumentException("unknown content type "+content.getClass()));
|
||||
|
||||
callback.block();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Blocking send of content.
|
||||
* @param content The content to send
|
||||
@ -332,7 +423,7 @@ public class HttpOutput extends ServletOutputStream
|
||||
new ReadableByteChannelWritingCB(in,callback).iterate();
|
||||
callback.block();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Blocking send of content.
|
||||
@ -345,7 +436,7 @@ public class HttpOutput extends ServletOutputStream
|
||||
sendContent(content,callback);
|
||||
callback.block();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Asynchronous send of content.
|
||||
@ -398,30 +489,43 @@ public class HttpOutput extends ServletOutputStream
|
||||
*/
|
||||
public void sendContent(HttpContent httpContent, Callback callback) throws IOException
|
||||
{
|
||||
if (isClosed())
|
||||
throw new IOException("Closed");
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
throw new IOException("written");
|
||||
if (_channel.isCommitted())
|
||||
throw new IOException("committed");
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
switch(_state.get())
|
||||
{
|
||||
case OPEN:
|
||||
if (!_state.compareAndSet(State.OPEN, State.PENDING))
|
||||
continue;
|
||||
break;
|
||||
case CLOSED:
|
||||
throw new EofException("Closed");
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
break;
|
||||
}
|
||||
ByteBuffer buffer= _channel.useDirectBuffers()?httpContent.getDirectBuffer():null;
|
||||
if (buffer == null)
|
||||
buffer = httpContent.getIndirectBuffer();
|
||||
|
||||
|
||||
if (buffer!=null)
|
||||
{
|
||||
sendContent(buffer,callback);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ReadableByteChannel rbc=httpContent.getReadableByteChannel();
|
||||
if (rbc!=null)
|
||||
{
|
||||
sendContent(rbc,callback);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
InputStream in = httpContent.getInputStream();
|
||||
if ( in!=null )
|
||||
{
|
||||
@ -447,8 +551,202 @@ public class HttpOutput extends ServletOutputStream
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
BufferUtil.clear(_aggregate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void setWriteListener(WriteListener writeListener)
|
||||
{
|
||||
if (_state.compareAndSet(State.OPEN, State.READY))
|
||||
{
|
||||
_writeListener = writeListener;
|
||||
_channel.getState().onWritePossible();
|
||||
}
|
||||
else
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.ServletOutputStream#isReady()
|
||||
*/
|
||||
@Override
|
||||
public boolean isReady()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch(_state.get())
|
||||
{
|
||||
case OPEN:
|
||||
return true;
|
||||
case ASYNC:
|
||||
if (!_state.compareAndSet(State.ASYNC, State.READY))
|
||||
continue;
|
||||
return true;
|
||||
case READY:
|
||||
return true;
|
||||
case PENDING:
|
||||
if (!_state.compareAndSet(State.PENDING, State.UNREADY))
|
||||
continue;
|
||||
return false;
|
||||
case UNREADY:
|
||||
return false;
|
||||
case CLOSED:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handle()
|
||||
{
|
||||
if(_onError!=null)
|
||||
{
|
||||
Throwable th=_onError;
|
||||
_onError=null;
|
||||
_writeListener.onError(th);
|
||||
close();
|
||||
}
|
||||
if (_state.get()==State.READY)
|
||||
{
|
||||
try
|
||||
{
|
||||
_writeListener.onWritePossible();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
_writeListener.onError(e);
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AsyncWrite extends AsyncFlush
|
||||
{
|
||||
private final byte[] _b;
|
||||
private final int _off;
|
||||
private final int _len;
|
||||
private final boolean _complete;
|
||||
|
||||
public AsyncWrite(byte[] b, int off, int len, boolean complete)
|
||||
{
|
||||
_b=b;
|
||||
_off=off;
|
||||
_len=len;
|
||||
_complete=complete;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean process()
|
||||
{
|
||||
// flush any content from the aggregate
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
{
|
||||
_channel.write(_aggregate, _complete && _len==0, this);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO write comments
|
||||
if (!_complete && _len<BufferUtil.space(_aggregate) && _len<_aggregate.capacity()/4)
|
||||
{
|
||||
BufferUtil.append(_aggregate, _b, _off, _len);
|
||||
}
|
||||
// TODO write comments
|
||||
else if (_len>0 && !_flushed)
|
||||
{
|
||||
ByteBuffer buffer=ByteBuffer.wrap(_b, _off, _len);
|
||||
_flushed=true;
|
||||
_channel.write(buffer, _complete, this);
|
||||
return false;
|
||||
}
|
||||
else if (_len==0 && !_flushed)
|
||||
{
|
||||
_flushed=true;
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER, _complete, this);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_complete)
|
||||
closed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class AsyncFlush extends IteratingCallback
|
||||
{
|
||||
protected boolean _flushed;
|
||||
|
||||
public AsyncFlush()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean process()
|
||||
{
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
{
|
||||
_flushed=true;
|
||||
_channel.write(_aggregate, false, this);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_flushed)
|
||||
{
|
||||
_flushed=true;
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER,false,this);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void completed()
|
||||
{
|
||||
try
|
||||
{
|
||||
loop: while(true)
|
||||
{
|
||||
State last=_state.get();
|
||||
switch(last)
|
||||
{
|
||||
case PENDING:
|
||||
if (!_state.compareAndSet(State.PENDING, State.ASYNC))
|
||||
continue;
|
||||
break;
|
||||
|
||||
case UNREADY:
|
||||
if (!_state.compareAndSet(State.UNREADY, State.READY))
|
||||
continue;
|
||||
_channel.getState().onWritePossible();
|
||||
break;
|
||||
|
||||
case CLOSED:
|
||||
_onError=new EofException("Closed");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_onError=e;
|
||||
_channel.getState().onWritePossible();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
_onError=e;
|
||||
_channel.getState().onWritePossible();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** An iterating callback that will take content from an
|
||||
* InputStream and write it to the associated {@link HttpChannel}.
|
||||
@ -457,11 +755,11 @@ public class HttpOutput extends ServletOutputStream
|
||||
* be notified as each buffer is written and only once all the input is consumed will the
|
||||
* wrapped {@link Callback#succeeded()} method be called.
|
||||
*/
|
||||
private class InputStreamWritingCB extends IteratingCallback
|
||||
private class InputStreamWritingCB extends IteratingNestedCallback
|
||||
{
|
||||
final InputStream _in;
|
||||
final ByteBuffer _buffer;
|
||||
|
||||
|
||||
public InputStreamWritingCB(InputStream in, Callback callback)
|
||||
{
|
||||
super(callback);
|
||||
@ -503,7 +801,7 @@ public class HttpOutput extends ServletOutputStream
|
||||
super.failed(x);
|
||||
_channel.getByteBufferPool().release(_buffer);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@ -515,11 +813,11 @@ public class HttpOutput extends ServletOutputStream
|
||||
* be notified as each buffer is written and only once all the input is consumed will the
|
||||
* wrapped {@link Callback#succeeded()} method be called.
|
||||
*/
|
||||
private class ReadableByteChannelWritingCB extends IteratingCallback
|
||||
private class ReadableByteChannelWritingCB extends IteratingNestedCallback
|
||||
{
|
||||
final ReadableByteChannel _in;
|
||||
final ByteBuffer _buffer;
|
||||
|
||||
|
||||
public ReadableByteChannelWritingCB(ReadableByteChannel in, Callback callback)
|
||||
{
|
||||
super(callback);
|
||||
@ -554,7 +852,6 @@ public class HttpOutput extends ServletOutputStream
|
||||
_buffer.flip();
|
||||
_channel.write(_buffer,eof,this);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -564,4 +861,5 @@ public class HttpOutput extends ServletOutputStream
|
||||
_channel.getByteBufferPool().release(_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,10 +35,10 @@ public class Iso88591HttpWriter extends HttpWriter
|
||||
public void write (char[] s,int offset, int length) throws IOException
|
||||
{
|
||||
HttpOutput out = _out;
|
||||
if (length==0)
|
||||
if (length==0 && out.isAllContentWritten())
|
||||
{
|
||||
if (_out.isAllContentWritten())
|
||||
close();
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (length==1)
|
||||
|
@ -57,6 +57,7 @@ import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpUpgradeHandler;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
import org.eclipse.jetty.http.HttpCookie;
|
||||
@ -496,6 +497,16 @@ public class Request implements HttpServletRequest
|
||||
{
|
||||
return (int)_fields.getLongField(HttpHeader.CONTENT_LENGTH.toString());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see javax.servlet.ServletRequest.getContentLengthLong()
|
||||
*/
|
||||
@Override
|
||||
public long getContentLengthLong()
|
||||
{
|
||||
return _fields.getLongField(HttpHeader.CONTENT_LENGTH.toString());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
@ -1619,9 +1630,7 @@ public class Request implements HttpServletRequest
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* Set a request attribute. if the attribute name is "org.eclipse.jetty.server.server.Request.queryEncoding" then the value is also passed in a call to
|
||||
* {@link #setQueryEncoding}. <p> if the attribute name is "org.eclipse.jetty.server.server.ResponseBuffer", then the response buffer is flushed with @{link
|
||||
* #flushResponseBuffer} <p> if the attribute name is "org.eclipse.jetty.io.EndPoint.maxIdleTime", then the value is passed to the associated {@link
|
||||
* EndPoint#setIdleTimeout}.
|
||||
* {@link #setQueryEncoding}.
|
||||
*
|
||||
* @see javax.servlet.ServletRequest#setAttribute(java.lang.String, java.lang.Object)
|
||||
*/
|
||||
@ -1630,35 +1639,11 @@ public class Request implements HttpServletRequest
|
||||
{
|
||||
Object old_value = _attributes == null?null:_attributes.getAttribute(name);
|
||||
|
||||
if (name.startsWith("org.eclipse.jetty."))
|
||||
{
|
||||
if ("org.eclipse.jetty.server.Request.queryEncoding".equals(name))
|
||||
setQueryEncoding(value == null?null:value.toString());
|
||||
else if ("org.eclipse.jetty.server.sendContent".equals(name))
|
||||
{
|
||||
try
|
||||
{
|
||||
((HttpOutput)getServletResponse().getOutputStream()).sendContent(value);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
else if ("org.eclipse.jetty.server.ResponseBuffer".equals(name))
|
||||
{
|
||||
try
|
||||
{
|
||||
throw new IOException("not implemented");
|
||||
//((HttpChannel.Output)getServletResponse().getOutputStream()).sendResponse(byteBuffer);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ("org.eclipse.jetty.server.Request.queryEncoding".equals(name))
|
||||
setQueryEncoding(value == null?null:value.toString());
|
||||
else if ("org.eclipse.jetty.server.sendContent".equals(name))
|
||||
LOG.warn("Deprecated: org.eclipse.jetty.server.sendContent");
|
||||
|
||||
if (_attributes == null)
|
||||
_attributes = new AttributesMap();
|
||||
_attributes.setAttribute(name,value);
|
||||
@ -2209,4 +2194,32 @@ public class Request implements HttpServletRequest
|
||||
setParameters(parameters);
|
||||
setQueryString(query);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServletRequest#upgrade(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException
|
||||
{
|
||||
if (getContext() == null)
|
||||
throw new ServletException ("Unable to instantiate "+handlerClass);
|
||||
|
||||
try
|
||||
{
|
||||
//Instantiate an instance and inject it
|
||||
T h = getContext().createInstance(handlerClass);
|
||||
|
||||
//TODO handle the rest of the upgrade process
|
||||
|
||||
return h;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof ServletException)
|
||||
throw (ServletException)e;
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -454,10 +454,18 @@ public class Response implements HttpServletResponse
|
||||
_channel.sendResponse(HttpGenerator.PROGRESS_102_INFO, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendRedirect(String location) throws IOException
|
||||
|
||||
/**
|
||||
* Sends a response with one of the 300 series redirection codes.
|
||||
* @param code
|
||||
* @param location
|
||||
* @throws IOException
|
||||
*/
|
||||
public void sendRedirect(int code, String location) throws IOException
|
||||
{
|
||||
if ((code < HttpServletResponse.SC_MULTIPLE_CHOICES) || (code >= HttpServletResponse.SC_BAD_REQUEST))
|
||||
throw new IllegalArgumentException("Not a 3xx redirect code");
|
||||
|
||||
if (isIncluding())
|
||||
return;
|
||||
|
||||
@ -467,7 +475,15 @@ public class Response implements HttpServletResponse
|
||||
if (!URIUtil.hasScheme(location))
|
||||
{
|
||||
StringBuilder buf = _channel.getRequest().getRootURL();
|
||||
if (location.startsWith("/"))
|
||||
|
||||
if (location.startsWith("//"))
|
||||
{
|
||||
buf.delete(0, buf.length());
|
||||
buf.append(_channel.getRequest().getScheme());
|
||||
buf.append(":");
|
||||
buf.append(location);
|
||||
}
|
||||
else if (location.startsWith("/"))
|
||||
buf.append(location);
|
||||
else
|
||||
{
|
||||
@ -515,10 +531,16 @@ public class Response implements HttpServletResponse
|
||||
|
||||
resetBuffer();
|
||||
setHeader(HttpHeader.LOCATION, location);
|
||||
setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
|
||||
setStatus(code);
|
||||
closeOutput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendRedirect(String location) throws IOException
|
||||
{
|
||||
sendRedirect(HttpServletResponse.SC_MOVED_TEMPORARILY, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDateHeader(String name, long date)
|
||||
{
|
||||
@ -811,6 +833,12 @@ public class Response implements HttpServletResponse
|
||||
_contentLength = len;
|
||||
_fields.putLongField(HttpHeader.CONTENT_LENGTH.toString(), len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLengthLong(long length)
|
||||
{
|
||||
setLongContentLength(length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCharacterEncoding(String encoding)
|
||||
|
@ -31,10 +31,15 @@ import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpUpgradeHandler;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Class to tunnel a ServletRequest via a HttpServletRequest
|
||||
/**
|
||||
* ServletRequestHttpWrapper
|
||||
*
|
||||
* Class to tunnel a ServletRequest via a HttpServletRequest
|
||||
*/
|
||||
public class ServletRequestHttpWrapper extends ServletRequestWrapper implements HttpServletRequest
|
||||
{
|
||||
@ -209,4 +214,25 @@ public class ServletRequestHttpWrapper extends ServletRequestWrapper implements
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServletRequest#changeSessionId()
|
||||
*/
|
||||
@Override
|
||||
public String changeSessionId()
|
||||
{
|
||||
// TODO 3.1 Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServletRequest#upgrade(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException
|
||||
{
|
||||
// TODO 3.1 Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,10 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Wrapper to tunnel a ServletResponse via a HttpServletResponse
|
||||
/**
|
||||
* ServletResponseHttpWrapper
|
||||
*
|
||||
* Wrapper to tunnel a ServletResponse via a HttpServletResponse
|
||||
*/
|
||||
public class ServletResponseHttpWrapper extends ServletResponseWrapper implements HttpServletResponse
|
||||
{
|
||||
|
@ -43,10 +43,10 @@ public class Utf8HttpWriter extends HttpWriter
|
||||
public void write (char[] s,int offset, int length) throws IOException
|
||||
{
|
||||
HttpOutput out = _out;
|
||||
if (length==0)
|
||||
if (length==0 && out.isAllContentWritten())
|
||||
{
|
||||
if (_out.isAllContentWritten())
|
||||
close();
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
while (length > 0)
|
||||
|
@ -965,7 +965,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||
if (old_context != _scontext)
|
||||
{
|
||||
// check the target.
|
||||
if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch) || (DispatcherType.ERROR.equals(dispatch) && baseRequest.getHttpChannelState().isExpired()))
|
||||
if (DispatcherType.REQUEST.equals(dispatch) ||
|
||||
DispatcherType.ASYNC.equals(dispatch) ||
|
||||
DispatcherType.ERROR.equals(dispatch) && baseRequest.getHttpChannelState().isAsync())
|
||||
{
|
||||
if (_compactPath)
|
||||
target = URIUtil.compactPath(target);
|
||||
@ -2143,13 +2145,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||
{
|
||||
try
|
||||
{
|
||||
return clazz.newInstance();
|
||||
return createInstance(clazz);
|
||||
}
|
||||
catch (InstantiationException e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
@ -2192,6 +2190,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||
{
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
|
||||
public <T> T createInstance (Class<T> clazz) throws Exception
|
||||
{
|
||||
T o = clazz.newInstance();
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2532,6 +2537,16 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||
{
|
||||
LOG.warn(__unimplmented);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.ServletContext#getVirtualServerName()
|
||||
*/
|
||||
@Override
|
||||
public String getVirtualServerName()
|
||||
{
|
||||
// TODO 3.1 Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,7 +81,6 @@ public class StatisticsHandler extends HandlerWrapper
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
|
||||
HttpChannelState state = ((AsyncContextEvent)event).getHttpChannelState();
|
||||
|
||||
Request request = state.getBaseRequest();
|
||||
@ -92,8 +91,7 @@ public class StatisticsHandler extends HandlerWrapper
|
||||
|
||||
updateResponse(request);
|
||||
|
||||
if (!state.isDispatched())
|
||||
_asyncWaitStats.decrement();
|
||||
_asyncWaitStats.decrement();
|
||||
}
|
||||
|
||||
};
|
||||
@ -139,9 +137,7 @@ public class StatisticsHandler extends HandlerWrapper
|
||||
{
|
||||
// resumed request
|
||||
start = System.currentTimeMillis();
|
||||
_asyncWaitStats.decrement();
|
||||
if (state.isDispatched())
|
||||
_asyncDispatches.incrementAndGet();
|
||||
_asyncDispatches.incrementAndGet();
|
||||
}
|
||||
|
||||
try
|
||||
@ -159,8 +155,10 @@ public class StatisticsHandler extends HandlerWrapper
|
||||
if (state.isSuspended())
|
||||
{
|
||||
if (state.isInitial())
|
||||
{
|
||||
state.addListener(_onCompletion);
|
||||
_asyncWaitStats.increment();
|
||||
_asyncWaitStats.increment();
|
||||
}
|
||||
}
|
||||
else if (state.isInitial())
|
||||
{
|
||||
|
@ -51,8 +51,8 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
|
||||
{
|
||||
final static Logger LOG = SessionHandler.LOG;
|
||||
public final static String SESSION_KNOWN_ONLY_TO_AUTHENTICATED="org.eclipse.jetty.security.sessionKnownOnlytoAuthenticated";
|
||||
private String _clusterId; // ID unique within cluster
|
||||
private String _nodeId; // ID unique within node
|
||||
private String _clusterId; // ID without any node (ie "worker") id appended
|
||||
private String _nodeId; // ID of session with node(ie "worker") id appended
|
||||
private final AbstractSessionManager _manager;
|
||||
private final Map<String,Object> _attributes=new HashMap<String, Object>();
|
||||
private boolean _idChanged;
|
||||
|
@ -38,6 +38,7 @@ import javax.servlet.http.HttpSessionAttributeListener;
|
||||
import javax.servlet.http.HttpSessionBindingEvent;
|
||||
import javax.servlet.http.HttpSessionContext;
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionIdListener;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
|
||||
import org.eclipse.jetty.http.HttpCookie;
|
||||
@ -107,6 +108,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
||||
|
||||
protected final List<HttpSessionAttributeListener> _sessionAttributeListeners = new CopyOnWriteArrayList<HttpSessionAttributeListener>();
|
||||
protected final List<HttpSessionListener> _sessionListeners= new CopyOnWriteArrayList<HttpSessionListener>();
|
||||
protected final List<HttpSessionIdListener> _sessionIdListeners = new CopyOnWriteArrayList<HttpSessionIdListener>();
|
||||
|
||||
protected ClassLoader _loader;
|
||||
protected ContextHandler.Context _context;
|
||||
@ -191,6 +193,8 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
||||
_sessionAttributeListeners.add((HttpSessionAttributeListener)listener);
|
||||
if (listener instanceof HttpSessionListener)
|
||||
_sessionListeners.add((HttpSessionListener)listener);
|
||||
if (listener instanceof HttpSessionIdListener)
|
||||
_sessionIdListeners.add((HttpSessionIdListener)listener);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@ -198,6 +202,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
||||
{
|
||||
_sessionAttributeListeners.clear();
|
||||
_sessionListeners.clear();
|
||||
_sessionIdListeners.clear();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@ -990,6 +995,29 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
||||
{
|
||||
_checkingRemoteSessionIdEncoding=remote;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Tell the HttpSessionIdListeners the id changed.
|
||||
* NOTE: this method must be called LAST in subclass overrides, after the session has been updated
|
||||
* with the new id.
|
||||
* @see org.eclipse.jetty.server.SessionManager#renewSessionId(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
|
||||
{
|
||||
if (!_sessionIdListeners.isEmpty())
|
||||
{
|
||||
AbstractSession session = getSession(newClusterId);
|
||||
HttpSessionEvent event = new HttpSessionEvent(session);
|
||||
for (HttpSessionIdListener l:_sessionIdListeners)
|
||||
{
|
||||
l.sessionIdChanged(event, oldClusterId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -440,6 +440,8 @@ public class HashSessionManager extends AbstractSessionManager
|
||||
session.setNodeId(newNodeId);
|
||||
session.save(); //save updated session: TODO consider only saving file if idled
|
||||
sessions.put(newClusterId, session);
|
||||
|
||||
super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -647,6 +647,8 @@ public class JDBCSessionManager extends AbstractSessionManager
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
|
@ -46,6 +46,7 @@ import javax.servlet.SessionTrackingMode;
|
||||
import javax.servlet.descriptor.JspConfigDescriptor;
|
||||
import javax.servlet.descriptor.JspPropertyGroupDescriptor;
|
||||
import javax.servlet.descriptor.TaglibDescriptor;
|
||||
import javax.servlet.http.HttpUpgradeHandler;
|
||||
|
||||
import org.eclipse.jetty.security.ConstraintAware;
|
||||
import org.eclipse.jetty.security.ConstraintMapping;
|
||||
@ -274,10 +275,10 @@ public class ServletContextHandler extends ContextHandler
|
||||
Decorator decorator = _decorators.get(i);
|
||||
if (_servletHandler.getFilters()!=null)
|
||||
for (FilterHolder holder:_servletHandler.getFilters())
|
||||
decorator.decorateFilterHolder(holder);
|
||||
decorator.decorate(holder);
|
||||
if(_servletHandler.getServlets()!=null)
|
||||
for (ServletHolder holder:_servletHandler.getServlets())
|
||||
decorator.decorateServletHolder(holder);
|
||||
decorator.decorate(holder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -574,14 +575,14 @@ public class ServletContextHandler extends ContextHandler
|
||||
void destroyServlet(Servlet servlet)
|
||||
{
|
||||
for (Decorator decorator : _decorators)
|
||||
decorator.destroyServletInstance(servlet);
|
||||
decorator.destroy(servlet);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
void destroyFilter(Filter filter)
|
||||
{
|
||||
for (Decorator decorator : _decorators)
|
||||
decorator.destroyFilterInstance(filter);
|
||||
decorator.destroy(filter);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@ -896,6 +897,9 @@ public class ServletContextHandler extends ContextHandler
|
||||
{
|
||||
if (isStarted())
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (filterName == null || "".equals(filterName.trim()))
|
||||
throw new IllegalStateException("Missing filter name");
|
||||
|
||||
if (!_enabled)
|
||||
throw new UnsupportedOperationException();
|
||||
@ -930,6 +934,9 @@ public class ServletContextHandler extends ContextHandler
|
||||
{
|
||||
if (isStarted())
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (filterName == null || "".equals(filterName.trim()))
|
||||
throw new IllegalStateException("Missing filter name");
|
||||
|
||||
if (!_enabled)
|
||||
throw new UnsupportedOperationException();
|
||||
@ -966,6 +973,9 @@ public class ServletContextHandler extends ContextHandler
|
||||
if (isStarted())
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (filterName == null || "".equals(filterName.trim()))
|
||||
throw new IllegalStateException("Missing filter name");
|
||||
|
||||
if (!_enabled)
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@ -1001,6 +1011,9 @@ public class ServletContextHandler extends ContextHandler
|
||||
if (!isStarting())
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (servletName == null || "".equals(servletName.trim()))
|
||||
throw new IllegalStateException("Missing servlet name");
|
||||
|
||||
if (!_enabled)
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@ -1036,6 +1049,9 @@ public class ServletContextHandler extends ContextHandler
|
||||
if (!isStarting())
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (servletName == null || "".equals(servletName.trim()))
|
||||
throw new IllegalStateException("Missing servlet name");
|
||||
|
||||
if (!_enabled)
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@ -1071,7 +1087,10 @@ public class ServletContextHandler extends ContextHandler
|
||||
{
|
||||
if (!isStarting())
|
||||
throw new IllegalStateException();
|
||||
|
||||
|
||||
if (servletName == null || "".equals(servletName.trim()))
|
||||
throw new IllegalStateException("Missing servlet name");
|
||||
|
||||
if (!_enabled)
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@ -1115,19 +1134,10 @@ public class ServletContextHandler extends ContextHandler
|
||||
{
|
||||
try
|
||||
{
|
||||
T f = c.newInstance();
|
||||
for (int i=_decorators.size()-1; i>=0; i--)
|
||||
{
|
||||
Decorator decorator = _decorators.get(i);
|
||||
f=decorator.decorateFilterInstance(f);
|
||||
}
|
||||
T f = createInstance(c);
|
||||
return f;
|
||||
}
|
||||
catch (InstantiationException e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
@ -1139,23 +1149,29 @@ public class ServletContextHandler extends ContextHandler
|
||||
{
|
||||
try
|
||||
{
|
||||
T s = c.newInstance();
|
||||
for (int i=_decorators.size()-1; i>=0; i--)
|
||||
{
|
||||
Decorator decorator = _decorators.get(i);
|
||||
s=decorator.decorateServletInstance(s);
|
||||
}
|
||||
T s = createInstance(c);
|
||||
return s;
|
||||
}
|
||||
catch (InstantiationException e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public <T> T createInstance (Class<T> c) throws Exception
|
||||
{
|
||||
T o = super.createInstance(c);
|
||||
for (int i=_decorators.size()-1; i>=0; i--)
|
||||
{
|
||||
Decorator decorator = _decorators.get(i);
|
||||
o=decorator.decorate(o);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
|
||||
@ -1286,20 +1302,10 @@ public class ServletContextHandler extends ContextHandler
|
||||
{
|
||||
try
|
||||
{
|
||||
T l = super.createListener(clazz);
|
||||
|
||||
for (int i=_decorators.size()-1; i>=0; i--)
|
||||
{
|
||||
Decorator decorator = _decorators.get(i);
|
||||
l=decorator.decorateListenerInstance(l);
|
||||
}
|
||||
T l = createInstance(clazz);
|
||||
return l;
|
||||
}
|
||||
catch(ServletException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch(Exception e)
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
@ -1340,15 +1346,7 @@ public class ServletContextHandler extends ContextHandler
|
||||
*/
|
||||
public interface Decorator
|
||||
{
|
||||
<T extends Filter> T decorateFilterInstance(T filter) throws ServletException;
|
||||
<T extends Servlet> T decorateServletInstance(T servlet) throws ServletException;
|
||||
<T extends EventListener> T decorateListenerInstance(T listener) throws ServletException;
|
||||
|
||||
void decorateFilterHolder(FilterHolder filter) throws ServletException;
|
||||
void decorateServletHolder(ServletHolder servlet) throws ServletException;
|
||||
|
||||
void destroyServletInstance(Servlet s);
|
||||
void destroyFilterInstance(Filter f);
|
||||
void destroyListenerInstance(EventListener f);
|
||||
<T> T decorate (T o);
|
||||
void destroy (Object o);
|
||||
}
|
||||
}
|
||||
|
@ -906,9 +906,12 @@ public class ServletHandler extends ScopedHandler
|
||||
{
|
||||
setServletMappings(ArrayUtil.addToArray(getServletMappings(), mapping, ServletMapping.class));
|
||||
}
|
||||
|
||||
public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement) {
|
||||
if (_contextHandler != null) {
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement)
|
||||
{
|
||||
if (_contextHandler != null)
|
||||
{
|
||||
return _contextHandler.setServletSecurity(registration, servletSecurityElement);
|
||||
}
|
||||
return Collections.emptySet();
|
||||
@ -1328,7 +1331,11 @@ public class ServletHandler extends ScopedHandler
|
||||
String[] pathSpecs = servletmapping.getPathSpecs();
|
||||
for (String pathSpec : pathSpecs)
|
||||
if (pathSpec != null)
|
||||
pm.put(pathSpec, servlet_holder);
|
||||
{
|
||||
ServletHolder previous = pm.put(pathSpec, servlet_holder);
|
||||
if (previous != null)
|
||||
throw new IllegalStateException("Multiple servlets map to path: "+pathSpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,265 @@
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.servlet;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class AsyncServletIOTest
|
||||
{
|
||||
protected AsyncIOServlet _servlet=new AsyncIOServlet();
|
||||
protected int _port;
|
||||
|
||||
protected Server _server = new Server();
|
||||
protected ServletHandler _servletHandler;
|
||||
protected ServerConnector _connector;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
HttpConfiguration http_config = new HttpConfiguration();
|
||||
http_config.setOutputBufferSize(4096);
|
||||
_connector = new ServerConnector(_server,new HttpConnectionFactory(http_config));
|
||||
|
||||
_server.setConnectors(new Connector[]{ _connector });
|
||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
|
||||
context.setContextPath("/ctx");
|
||||
_server.setHandler(context);
|
||||
_servletHandler=context.getServletHandler();
|
||||
ServletHolder holder=new ServletHolder(_servlet);
|
||||
holder.setAsyncSupported(true);
|
||||
_servletHandler.addServletWithMapping(holder,"/path/*");
|
||||
_server.start();
|
||||
_port=_connector.getLocalPort();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmpty() throws Exception
|
||||
{
|
||||
process();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite() throws Exception
|
||||
{
|
||||
process(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrites() throws Exception
|
||||
{
|
||||
process(10,1,20,10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWritesFlushWrites() throws Exception
|
||||
{
|
||||
process(10,1,0,20,10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigWrite() throws Exception
|
||||
{
|
||||
process(102400);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigWrites() throws Exception
|
||||
{
|
||||
_count.set(0);
|
||||
process(102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400,102400);
|
||||
Assert.assertThat(_count.get(),Matchers.greaterThan(1));
|
||||
}
|
||||
|
||||
|
||||
protected void assertContains(String content,String response)
|
||||
{
|
||||
Assert.assertThat(response,Matchers.containsString(content));
|
||||
}
|
||||
|
||||
protected void assertNotContains(String content,String response)
|
||||
{
|
||||
Assert.assertThat(response,Matchers.not(Matchers.containsString(content)));
|
||||
}
|
||||
|
||||
|
||||
public synchronized List<String> process(int... writes) throws Exception
|
||||
{
|
||||
StringBuilder request = new StringBuilder(512);
|
||||
request.append("GET /ctx/path/info");
|
||||
char s='?';
|
||||
for (int w: writes)
|
||||
{
|
||||
request.append(s).append("w=").append(w);
|
||||
s='&';
|
||||
}
|
||||
|
||||
request.append(" HTTP/1.1\r\n")
|
||||
.append("Host: localhost\r\n")
|
||||
.append("Connection: close\r\n")
|
||||
.append("\r\n");
|
||||
|
||||
|
||||
int port=_port;
|
||||
List<String> list = new ArrayList<>();
|
||||
try (Socket socket = new Socket("localhost",port);)
|
||||
{
|
||||
socket.setSoTimeout(1000000);
|
||||
socket.getOutputStream().write(request.toString().getBytes("ISO-8859-1"));
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()),102400);
|
||||
|
||||
// response line
|
||||
String line = in.readLine();
|
||||
//System.err.println("line: "+line);
|
||||
Assert.assertThat(line,Matchers.startsWith("HTTP/1.1 200 OK"));
|
||||
|
||||
// Skip headers
|
||||
while (line!=null)
|
||||
{
|
||||
line = in.readLine();
|
||||
//System.err.println("line: "+line);
|
||||
if (line.length()==0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Get body slowly
|
||||
while (true)
|
||||
{
|
||||
line = in.readLine();
|
||||
if (line==null)
|
||||
break;
|
||||
//System.err.println("line: "+line.length()+"\t"+(line.length()>40?(line.substring(0,40)+"..."):line));
|
||||
list.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
// check lines
|
||||
int w=0;
|
||||
for (String line : list)
|
||||
{
|
||||
if ("-".equals(line))
|
||||
continue;
|
||||
assertEquals(writes[w],line.length());
|
||||
assertEquals(line.charAt(0),'0'+(w%10));
|
||||
|
||||
w++;
|
||||
if (w<writes.length && writes[w]<=0)
|
||||
w++;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static AtomicInteger _count = new AtomicInteger();
|
||||
|
||||
private static class AsyncIOServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = -8161977157098646562L;
|
||||
|
||||
public AsyncIOServlet()
|
||||
{}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
final String[] writes = request.getParameterValues("w");
|
||||
final AsyncContext async = request.startAsync();
|
||||
final ServletOutputStream out = response.getOutputStream();
|
||||
out.setWriteListener(new WriteListener()
|
||||
{
|
||||
int _w=0;
|
||||
|
||||
@Override
|
||||
public void onWritePossible() throws IOException
|
||||
{
|
||||
//System.err.println("OWP");
|
||||
_count.incrementAndGet();
|
||||
|
||||
while (writes!=null && _w< writes.length)
|
||||
{
|
||||
int write=Integer.valueOf(writes[_w++]);
|
||||
|
||||
if (write==0)
|
||||
out.flush();
|
||||
else
|
||||
{
|
||||
byte[] buf=new byte[write+1];
|
||||
Arrays.fill(buf,(byte)('0'+((_w-1)%10)));
|
||||
buf[write]='\n';
|
||||
out.write(buf);
|
||||
}
|
||||
|
||||
if (!out.isReady())
|
||||
return;
|
||||
}
|
||||
|
||||
//System.err.println("COMPLETE!!!");
|
||||
async.complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t)
|
||||
{
|
||||
async.complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-servlets</artifactId>
|
||||
@ -75,16 +75,16 @@
|
||||
<artifactId>jetty-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-jmx</artifactId>
|
||||
|
@ -26,6 +26,7 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@ -376,6 +377,21 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
|
||||
_response.setHeader(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteListener(WriteListener writeListener)
|
||||
{
|
||||
// TODO 3.1 Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isReady()
|
||||
{
|
||||
// TODO 3.1 Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the stream fitting to the underlying compression type.
|
||||
*
|
||||
|
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-parent</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-parent</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-parent</artifactId>
|
||||
<version>9.0.4-SNAPSHOT</version>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spdy-example-webapp</artifactId>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user