Merge branch 'jetty-9.1' into javawebsocket-jsr

This commit is contained in:
Joakim Erdfelt 2013-06-19 15:07:15 -07:00
commit f8c457f75e
153 changed files with 2781 additions and 1086 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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();

View File

@ -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>

View File

@ -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>

View File

@ -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)
{
}
}

View File

@ -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++)

View File

@ -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++)

View File

@ -57,7 +57,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
*/
public void doHandle(Class<?> clazz)
{
if (Util.isServletType(clazz))
if (Util.supportsResourceInjection(clazz))
{
handleClass(clazz);

View File

@ -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);
}

View File

@ -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)
{

View File

@ -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();

View File

@ -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());

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -356,6 +356,7 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
__log.warn(e);
}
}
super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 -->

View File

@ -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());

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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>

View File

@ -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>

View File

@ -88,7 +88,7 @@
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.servlet-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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();
}

View File

@ -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 "&lt;method&gt;.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.
* &lt;method&gt;.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 "&lt;method&gt;.&lt;method&gt;.&lt;method&gt;.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 &lt;method&gt;.&lt;method&gt;.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;
}
}

View File

@ -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;
}

View File

@ -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:"")+"}";
}
}

View File

@ -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

View File

@ -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()

View File

@ -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;
}
};

View File

@ -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;
}

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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()
{

View File

@ -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>
*/

View File

@ -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)

View File

@ -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();
}
}

View File

@ -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

View File

@ -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;

View File

@ -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
}
}

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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
{

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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())
{

View File

@ -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;

View File

@ -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);
}
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */

View File

@ -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)
{

View File

@ -647,6 +647,8 @@ public class JDBCSessionManager extends AbstractSessionManager
LOG.warn(e);
}
}
super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
}

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
});
}
}
}

View File

@ -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>

View File

@ -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.
*

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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