diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
index 79ab25a4f81..c3cd2ecad84 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
@@ -61,6 +61,7 @@ import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
@@ -1300,4 +1301,79 @@ public class HttpClientTest extends AbstractHttpClientServerTest
Assert.assertEquals(200, response.getStatus());
Assert.assertArrayEquals(data, response.getContent());
}
+
+ @Test
+ public void testRequestRetries() throws Exception
+ {
+ final int maxRetries = 3;
+ final AtomicInteger requests = new AtomicInteger();
+ start(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ int count = requests.incrementAndGet();
+ if (count == maxRetries)
+ baseRequest.setHandled(true);
+ }
+ });
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ new RetryListener(client, scheme, "localhost", connector.getLocalPort(), maxRetries)
+ {
+ @Override
+ protected void completed(Result result)
+ {
+ latch.countDown();
+ }
+ }.perform();
+
+ Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+
+ public static abstract class RetryListener implements Response.CompleteListener
+ {
+ private final HttpClient client;
+ private final String scheme;
+ private final String host;
+ private final int port;
+ private final int maxRetries;
+ private int retries;
+
+ public RetryListener(HttpClient client, String scheme, String host, int port, int maxRetries)
+ {
+ this.client = client;
+ this.scheme = scheme;
+ this.host = host;
+ this.port = port;
+ this.maxRetries = maxRetries;
+ }
+
+ protected abstract void completed(Result result);
+
+ @Override
+ public void onComplete(Result result)
+ {
+ if (retries > maxRetries || result.isSucceeded() && result.getResponse().getStatus() == 200)
+ completed(result);
+ else
+ retry();
+ }
+
+ private void retry()
+ {
+ ++retries;
+ perform();
+ }
+
+ public void perform()
+ {
+ client.newRequest(host, port)
+ .scheme(scheme)
+ .method("POST")
+ .param("attempt", String.valueOf(retries))
+ .content(new StringContentProvider("0123456789ABCDEF"))
+ .send(this);
+ }
+ }
}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
index c8e8f4a74da..f809a5e7925 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
@@ -303,7 +303,7 @@ public class SslConnection extends AbstractConnection
{
@Override
public void succeeded()
- {
+ {
}
@Override
@@ -313,7 +313,7 @@ public class SslConnection extends AbstractConnection
getFillInterest().onFail(x);
getWriteFlusher().onFail(x);
}
-
+
},x);
}
};
@@ -345,7 +345,7 @@ public class SslConnection extends AbstractConnection
@Override
protected void onIncompleteFlush()
- {
+ {
// This means that the decrypted endpoint write method was called and not
// all data could be wrapped. So either we need to write some encrypted data,
// OR if we are handshaking we need to read some encrypted data OR
@@ -379,8 +379,8 @@ public class SslConnection extends AbstractConnection
try_again = true;
}
}
-
-
+
+
if (try_again)
{
// If the output is closed,
@@ -520,7 +520,9 @@ public class SslConnection extends AbstractConnection
HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
Status unwrapResultStatus = unwrapResult.getStatus();
- _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW;
+ // Extra check on unwrapResultStatus == OK with zero length buffer is due
+ // to SSL client on android (see bug #454773)
+ _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW || unwrapResultStatus == Status.OK && unwrapResult.bytesConsumed()==0 && unwrapResult.bytesProduced()==0;
if (_underFlown)
{
@@ -729,11 +731,11 @@ public class SslConnection extends AbstractConnection
LOG.debug("{} wrap {}", SslConnection.this, wrapResult.toString().replace('\n',' '));
BufferUtil.flipToFlush(_encryptedOutput, pos);
Status wrapResultStatus = wrapResult.getStatus();
-
+
boolean allConsumed=true;
for (ByteBuffer b : appOuts)
if (BufferUtil.hasContent(b))
- allConsumed=false;
+ allConsumed=false;
// and deal with the results returned from the sslEngineWrap
switch (wrapResultStatus)
@@ -798,7 +800,7 @@ public class SslConnection extends AbstractConnection
// try again.
if (!allConsumed && wrapResult.getHandshakeStatus()==HandshakeStatus.FINISHED && BufferUtil.isEmpty(_encryptedOutput))
continue;
-
+
// Return true if we consumed all the bytes and encrypted are all flushed
return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml
index 398a2f2cb5a..83532920c5f 100644
--- a/jetty-jaspi/pom.xml
+++ b/jetty-jaspi/pom.xml
@@ -38,5 +38,18 @@
org.eclipse.jetty.orbit
javax.security.auth.message
+
+
+ org.apache.geronimo.components
+ geronimo-jaspi
+ 2.0.0
+
+
+ org.apache.geronimo.specs
+ geronimo-jaspic_1.0_spec
+
+
+ test
+
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
index a6027487c69..c5228f44171 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
@@ -18,6 +18,7 @@
package org.eclipse.jetty.security.jaspi;
+import java.io.IOException;
import java.security.Principal;
import java.util.Map;
import java.util.Set;
@@ -32,6 +33,7 @@ import javax.security.auth.message.config.ServerAuthContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.eclipse.jetty.security.IdentityService;
@@ -192,10 +194,16 @@ public class JaspiAuthenticator extends LoginAuthenticator
// we are processing a message in a secureResponse dialog.
return Authentication.SEND_SUCCESS;
}
+ if (authStatus == AuthStatus.FAILURE)
+ {
+ HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return Authentication.SEND_FAILURE;
+ }
// should not happen
- throw new NullPointerException("No AuthStatus returned");
+ throw new IllegalStateException("No AuthStatus returned");
}
- catch (AuthException e)
+ catch (IOException|AuthException e)
{
throw new ServerAuthException(e);
}
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
index 6dbb00f8906..b31b0cafb4c 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
@@ -102,6 +102,9 @@ public class JaspiAuthenticatorFactory extends DefaultAuthenticatorFactory
Subject serviceSubject=findServiceSubject(server);
String serverName=findServerName(server,serviceSubject);
+ String contextPath=context.getContextPath();
+ if (contextPath==null || contextPath.length()==0)
+ contextPath="/";
String appContext = serverName + " " + context.getContextPath();
AuthConfigProvider authConfigProvider = authConfigFactory.getConfigProvider(MESSAGE_LAYER,appContext,listener);
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
index 7b691717c97..a5ce15855d8 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
@@ -46,7 +46,6 @@ import org.eclipse.jetty.util.security.Credential;
import org.eclipse.jetty.util.security.Password;
/**
- * @deprecated use *ServerAuthentication
* @version $Rev: 4792 $ $Date: 2009-03-18 22:55:52 +0100 (Wed, 18 Mar 2009) $
*/
public class BaseAuthModule implements ServerAuthModule, ServerAuthContext
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
index cc87cedff8e..1fedc09081a 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
@@ -150,7 +150,8 @@ public class FormAuthModule extends BaseAuthModule
HttpSession session = request.getSession(mandatory);
// not mandatory or its the login or login error page don't authenticate
- if (!mandatory || isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()))) return AuthStatus.SUCCESS;
+ if (!mandatory || isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())))
+ return AuthStatus.SUCCESS; // TODO return null for do nothing?
try
{
diff --git a/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/HttpHeaderAuthModule.java b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/HttpHeaderAuthModule.java
new file mode 100644
index 00000000000..12cfad0c688
--- /dev/null
+++ b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/HttpHeaderAuthModule.java
@@ -0,0 +1,125 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.security.jaspi;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.AuthStatus;
+import javax.security.auth.message.MessageInfo;
+import javax.security.auth.message.MessagePolicy;
+import javax.security.auth.message.callback.CallerPrincipalCallback;
+import javax.security.auth.message.callback.GroupPrincipalCallback;
+import javax.security.auth.message.module.ServerAuthModule;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Example JASPI Auth Module based on http://www.trajano.net/2014/06/creating-a-simple-jaspic-auth-module/
+ */
+public class HttpHeaderAuthModule implements ServerAuthModule
+{
+
+ /**
+ * Supported message types. For our case we only need to deal with HTTP servlet request and responses. On Java EE 7 this will handle WebSockets as well.
+ */
+ private static final Class>[] SUPPORTED_MESSAGE_TYPES = new Class>[]
+ { HttpServletRequest.class, HttpServletResponse.class };
+
+ /**
+ * Callback handler that is passed in initialize by the container. This processes the callbacks which are objects that populate the "subject".
+ */
+ private CallbackHandler handler;
+
+ /**
+ * Does nothing of note for what we need.
+ */
+ @Override
+ public void cleanSubject(final MessageInfo messageInfo, final Subject subject) throws AuthException
+ {
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Class[] getSupportedMessageTypes()
+ {
+ return SUPPORTED_MESSAGE_TYPES;
+ }
+
+ /**
+ * Initializes the module. Allows you to pass in options.
+ *
+ * @param requestPolicy
+ * request policy, ignored
+ * @param responsePolicy
+ * response policy, ignored
+ * @param h
+ * callback handler
+ * @param options
+ * options
+ */
+ @Override
+ public void initialize(final MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler h, Map options) throws AuthException
+ {
+ handler = h;
+ }
+
+ /**
+ * @return AuthStatus.SEND_SUCCESS
+ */
+ @Override
+ public AuthStatus secureResponse(final MessageInfo paramMessageInfo, final Subject subject) throws AuthException
+ {
+ return AuthStatus.SEND_SUCCESS;
+ }
+
+ /**
+ * Validation occurs here.
+ */
+ @Override
+ public AuthStatus validateRequest(final MessageInfo messageInfo, final Subject client, final Subject serviceSubject) throws AuthException
+ {
+
+ // Take the request from the messageInfo structure.
+ final HttpServletRequest req = (HttpServletRequest)messageInfo.getRequestMessage();
+ try
+ {
+ // Get the user name from the header. If not there then fail authentication.
+ final String userName = req.getHeader("X-Forwarded-User");
+ if (userName == null)
+ {
+ return AuthStatus.FAILURE;
+ }
+
+ // Store the user name that was in the header and also set a group.
+ handler.handle(new Callback[]
+ { new CallerPrincipalCallback(client,userName), new GroupPrincipalCallback(client,new String[]
+ { "users" }) });
+ return AuthStatus.SUCCESS;
+ }
+ catch (final Exception e)
+ {
+ throw new AuthException(e.getMessage());
+ }
+ }
+
+}
diff --git a/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java
new file mode 100644
index 00000000000..771b15433be
--- /dev/null
+++ b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java
@@ -0,0 +1,167 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.security.jaspi;
+
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.util.security.Password;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class JaspiTest
+{
+ Server _server;
+ LocalConnector _connector;
+
+ @Before
+ public void before() throws Exception
+ {
+ System.setProperty("org.apache.geronimo.jaspic.configurationFile","src/test/resources/jaspi.xml");
+ _server = new Server();
+ _connector = new LocalConnector(_server);
+ _server.addConnector(_connector);
+
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ _server.setHandler(contexts);
+
+ HashLoginService loginService = new HashLoginService("TestRealm");
+ loginService.putUser("user",new Password("password"),new String[]{"users"});
+ loginService.putUser("admin",new Password("secret"),new String[]{"users","admins"});
+ _server.addBean(loginService);
+
+ ContextHandler context = new ContextHandler();
+ contexts.addHandler(context);
+ context.setContextPath("/ctx");
+
+ JaspiAuthenticatorFactory jaspiAuthFactory = new JaspiAuthenticatorFactory();
+
+ ConstraintSecurityHandler security = new ConstraintSecurityHandler();
+ context.setHandler(security);
+ security.setAuthenticatorFactory(jaspiAuthFactory);
+ // security.setAuthenticator(new BasicAuthenticator());
+
+ Constraint constraint = new Constraint("All","users");
+ constraint.setAuthenticate(true);
+ ConstraintMapping mapping = new ConstraintMapping();
+ mapping.setPathSpec("/jaspi/*");
+ mapping.setConstraint(constraint);
+ security.addConstraintMapping(mapping);
+
+ TestHandler handler = new TestHandler();
+ security.setHandler(handler);
+
+ ContextHandler other = new ContextHandler();
+ contexts.addHandler(other);
+ other.setContextPath("/other");
+ ConstraintSecurityHandler securityOther = new ConstraintSecurityHandler();
+ other.setHandler(securityOther);
+ securityOther.setAuthenticatorFactory(jaspiAuthFactory);
+ securityOther.addConstraintMapping(mapping);
+ securityOther.setHandler(new TestHandler());
+
+ _server.start();
+ }
+
+ @After
+ public void after() throws Exception
+ {
+ _server.stop();
+ }
+
+ @Test
+ public void testNoConstraint() throws Exception
+ {
+ String response = _connector.getResponses("GET /ctx/test HTTP/1.0\n\n");
+ assertThat(response,startsWith("HTTP/1.1 200 OK"));
+ }
+
+ @Test
+ public void testConstraintNoAuth() throws Exception
+ {
+ String response = _connector.getResponses("GET /ctx/jaspi/test HTTP/1.0\n\n");
+ assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
+ assertThat(response,Matchers.containsString("WWW-Authenticate: basic realm=\"TestRealm\""));
+ }
+
+ @Test
+ public void testConstraintWrongAuth() throws Exception
+ {
+ String response = _connector.getResponses("GET /ctx/jaspi/test HTTP/1.0\n"+
+ "Authorization: Basic " + B64Code.encode("user:wrong") + "\n\n");
+ assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
+ assertThat(response,Matchers.containsString("WWW-Authenticate: basic realm=\"TestRealm\""));
+ }
+
+ @Test
+ public void testConstraintAuth() throws Exception
+ {
+ String response = _connector.getResponses("GET /ctx/jaspi/test HTTP/1.0\n"+
+ "Authorization: Basic " + B64Code.encode("user:password") + "\n\n");
+ assertThat(response,startsWith("HTTP/1.1 200 OK"));
+ }
+
+ @Test
+ public void testOtherNoAuth() throws Exception
+ {
+ String response = _connector.getResponses("GET /other/test HTTP/1.0\n\n");
+ assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
+ }
+
+ @Test
+ public void testOtherAuth() throws Exception
+ {
+ String response = _connector.getResponses("GET /other/test HTTP/1.0\n"+
+ "X-Forwarded-User: user\n\n");
+ assertThat(response,startsWith("HTTP/1.1 200 OK"));
+ }
+
+ public class TestHandler extends AbstractHandler
+ {
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.setStatus(200);
+ response.setContentType("text/plain");
+ response.getWriter().println("All OK");
+ response.getWriter().println("requestURI="+request.getRequestURI());
+ }
+ }
+}
diff --git a/jetty-jaspi/src/test/resources/jaspi.xml b/jetty-jaspi/src/test/resources/jaspi.xml
new file mode 100644
index 00000000000..23a2ba5c7ed
--- /dev/null
+++ b/jetty-jaspi/src/test/resources/jaspi.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ HTTP
+ server /ctx
+ description
+
+ authenticationContextID1
+ true
+
+
+ org.eclipse.jetty.security.jaspi.modules.BasicAuthModule
+
+ org.eclipse.jetty.security.jaspi.modules.RealmName=TestRealm
+
+
+
+
+ true
+
+
+ HTTP
+ server /other
+ description
+
+ authenticationContextID2
+ true
+
+
+ org.eclipse.jetty.security.jaspi.HttpHeaderAuthModule
+
+
+
+
+
+ true
+
+
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
index 18d44061c34..e517f8aab6e 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
@@ -259,6 +259,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
*/
protected void purge()
{
+ __log.debug("PURGING");
BasicDBObject invalidQuery = new BasicDBObject();
invalidQuery.put(MongoSessionManager.__ACCESSED, new BasicDBObject("$lt",System.currentTimeMillis() - _purgeInvalidAge));
@@ -432,6 +433,8 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
_scavengerTask = _scheduler.schedule(new Scavenger(), _scavengePeriod, TimeUnit.MILLISECONDS);
}
+ else if (__log.isDebugEnabled())
+ __log.debug("Scavenger disabled");
//if purging is enabled, setup the purge thread
@@ -444,6 +447,8 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
}
_purgerTask = _scheduler.schedule(new Purger(), _purgeDelay, TimeUnit.MILLISECONDS);
}
+ else if (__log.isDebugEnabled())
+ __log.debug("Purger disabled");
}
}
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
index 48fd1c2f00a..3daa0a0d906 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
@@ -42,6 +42,8 @@ import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
+import com.mongodb.WriteConcern;
+import com.mongodb.WriteResult;
/**
@@ -268,7 +270,7 @@ public class MongoSessionManager extends NoSqlSessionManager
Long currentExpiry = (Long)o.get(__EXPIRY);
if (currentMaxIdle != null && getMaxInactiveInterval() > 0 && getMaxInactiveInterval() < currentMaxIdle)
sets.put(__MAX_IDLE, getMaxInactiveInterval());
- if (currentExpiry != null && expiry > 0 && expiry < currentExpiry)
+ if (currentExpiry != null && expiry > 0 && expiry != currentExpiry)
sets.put(__EXPIRY, currentExpiry);
}
}
@@ -302,9 +304,11 @@ public class MongoSessionManager extends NoSqlSessionManager
if (!unsets.isEmpty())
update.put("$unset",unsets);
- _dbSessions.update(key,update,upsert,false);
- __log.debug("MongoSessionManager:save:db.sessions.update( {}, {}, true) ", key, update);
+ _dbSessions.update(key,update,upsert,false,WriteConcern.SAFE);
+ if (__log.isDebugEnabled())
+ __log.debug("MongoSessionManager:save:db.sessions.update( {}, {} )", key, update);
+
if (activateAfterSave)
session.didActivate();
@@ -421,7 +425,7 @@ public class MongoSessionManager extends NoSqlSessionManager
update.put("$set",sets);
}
- _dbSessions.update(key,update,false,false);
+ _dbSessions.update(key,update,false,false,WriteConcern.SAFE);
session.didActivate();
@@ -520,7 +524,7 @@ public class MongoSessionManager extends NoSqlSessionManager
BasicDBObject unsets = new BasicDBObject();
unsets.put(getContextKey(),1);
remove.put("$unset",unsets);
- _dbSessions.update(key,remove);
+ _dbSessions.update(key,remove,false,false,WriteConcern.SAFE);
return true;
}
@@ -556,7 +560,7 @@ public class MongoSessionManager extends NoSqlSessionManager
update.put("$set",sets);
BasicDBObject key = new BasicDBObject(__ID,idInCluster);
- _dbSessions.update(key,update);
+ _dbSessions.update(key,update,false,false,WriteConcern.SAFE);
}
}
@@ -573,7 +577,7 @@ public class MongoSessionManager extends NoSqlSessionManager
BasicDBObject sets = new BasicDBObject();
BasicDBObject update = new BasicDBObject(__ID, newClusterId);
sets.put("$set", update);
- _dbSessions.update(key, sets, false, false);
+ _dbSessions.update(key, sets, false, false,WriteConcern.SAFE);
}
/*------------------------------------------------------------ */
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
index 254a853892d..08eca6c2b8d 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
@@ -103,6 +103,10 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
//index and scan fragments
for (Bundle bundle : fragAndRequiredBundles)
{
+ //skip bundles that have been uninstalled since we discovered them
+ if (bundle.getState() == Bundle.UNINSTALLED)
+ continue;
+
Resource bundleRes = oparser.indexBundle(bundle);
if (!context.getMetaData().getWebInfJars().contains(bundleRes))
{
@@ -127,6 +131,10 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
//scan the required bundles
for (Bundle requiredBundle : fragAndRequiredBundles)
{
+ //skip bundles that have been uninstalled since we discovered them
+ if (requiredBundle.getState() == Bundle.UNINSTALLED)
+ continue;
+
if (requiredBundle.getHeaders().get(Constants.FRAGMENT_HOST) == null)
{
//a bundle indeed:
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
index 549542be065..f8410713259 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.osgi.annotations;
import java.io.File;
+import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Comparator;
@@ -196,10 +197,21 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa
//remove the starting '/'
name = path.substring(1);
}
+ if (name == null)
+ {
+ //found some .class file in the archive that was not under one of the prefix paths
+ //or the bundle classpath wasn't simply ".", so skip it
+ continue;
+ }
//transform into a classname to pass to the resolver
String shortName = name.replace('/', '.').substring(0,name.length()-6);
- if ((resolver == null)|| (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
- scanClass(handlers, getResource(bundle), classUrl.openStream());
+ if ((resolver == null) || (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
+ {
+ try (InputStream classInputStream = classUrl.openStream())
+ {
+ scanClass(handlers, getResource(bundle), classInputStream);
+ }
+ }
}
}
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java
index 28f72c814f2..030ac005886 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java
@@ -403,7 +403,7 @@ public abstract class AbstractWebAppProvider extends AbstractLifeCycle implement
throw new IllegalStateException("Unable to get PackageAdmin reference to locate required Tld bundles");
StringBuilder paths = new StringBuilder();
- String[] symbNames = requireTldBundles.split(", ");
+ String[] symbNames = requireTldBundles.split("[, ]");
for (String symbName : symbNames)
{
@@ -452,7 +452,7 @@ public abstract class AbstractWebAppProvider extends AbstractLifeCycle implement
String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (tmp != null)
{
- String[] filenames = tmp.split(",;");
+ String[] filenames = tmp.split("[,;]");
if (filenames != null && filenames.length > 0)
{
String filename = filenames[0]; //should only be 1 filename in this usage
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java
index 65466f506a5..6b72ffd48a7 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java
@@ -125,7 +125,7 @@ public class BundleContextProvider extends AbstractContextProvider implements Bu
//bundle defines JETTY_CONTEXT_FILE_PATH header,
//a comma separated list of context xml files that each define a ContextHandler
//TODO: (could be WebAppContexts)
- String[] tmp = contextFiles.split(",;");
+ String[] tmp = contextFiles.split("[,;]");
for (String contextFile : tmp)
{
String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-"+contextFile;
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java
index b4537249196..697ebd66d8e 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java
@@ -177,6 +177,10 @@ public class OSGiWebInfConfiguration extends WebInfConfiguration
for (Bundle b : bundles)
{
+ //skip bundles that are not installed
+ if (b.getState() == Bundle.UNINSTALLED)
+ continue;
+
//add to context attribute storing associated fragments and required bundles
fragsAndReqsBundles.add(b);
File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(b);
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
index 1347c65563a..5acb688fad7 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
@@ -128,6 +128,9 @@ public class DefaultJettyAtJettyHomeHelper
jettyHomeBundleSysProp = Util.resolvePropertyValue(jettyHomeBundleSysProp);
for (Bundle b : bundleContext.getBundles())
{
+ if (b.getState() == Bundle.UNINSTALLED)
+ continue;
+
if (b.getSymbolicName().equals(jettyHomeBundleSysProp))
{
jettyHomeBundle = b;
diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml
index 1daa9c2ee91..23dcdcaf6ef 100644
--- a/jetty-osgi/test-jetty-osgi/pom.xml
+++ b/jetty-osgi/test-jetty-osgi/pom.xml
@@ -140,7 +140,7 @@
org.eclipse.jetty.toolchain
jetty-osgi-servlet-api
- 3.1.0.M0
+ 3.1.0.M3
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRule.java
new file mode 100644
index 00000000000..87d7c179abf
--- /dev/null
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRule.java
@@ -0,0 +1,128 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.rewrite.handler;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/* ------------------------------------------------------------ */
+/** Rule to add a header based on a Regex match
+ */
+public class HeaderRegexRule extends RegexRule
+{
+
+ private String _name;
+ private String _value;
+ private boolean _add=false;
+
+ /* ------------------------------------------------------------ */
+ public HeaderRegexRule()
+ {
+ _handling = false;
+ _terminating = false;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Sets the header name.
+ *
+ * @param name name of the header field
+ */
+ public void setName(String name)
+ {
+ _name = name;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Sets the header value. The value can be either a String
or int
value.
+ *
+ * @param value of the header field
+ */
+ public void setValue(String value)
+ {
+ _value = value;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Sets the Add flag.
+ * @param add If true, the header is added to the response, otherwise the header it is set on the response.
+ */
+ public void setAdd(boolean add)
+ {
+ _add = add;
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ protected String apply(String target, HttpServletRequest request, HttpServletResponse response, Matcher matcher)
+ throws IOException
+ {
+ // process header
+ if (_add)
+ response.addHeader(_name, _value);
+ else
+ response.setHeader(_name, _value);
+ return target;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Returns the header name.
+ * @return the header name.
+ */
+ public String getName()
+ {
+ return _name;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Returns the header value.
+ * @return the header value.
+ */
+ public String getValue()
+ {
+ return _value;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Returns the add flag value.
+ */
+ public boolean isAdd()
+ {
+ return _add;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Returns the header contents.
+ */
+ @Override
+ public String toString()
+ {
+ return super.toString()+"["+_name+","+_value+"]";
+ }
+}
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRuleTest.java
new file mode 100644
index 00000000000..0edfde24c2c
--- /dev/null
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRuleTest.java
@@ -0,0 +1,131 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.rewrite.handler;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+public class HeaderRegexRuleTest extends AbstractRuleTestCase
+{
+
+ private HeaderRegexRule _rule;
+
+ @Before
+ public void init() throws Exception
+ {
+ start(false);
+ _rule = new HeaderRegexRule();
+ _rule.setRegex("\\*");
+ }
+
+ @Test
+ public void testHeaderWithTextValues() throws IOException
+ {
+ // different keys
+ String headers[][] =
+ {
+ { "hnum#1", "test1" },
+ { "hnum#2", "2test2" },
+ { "hnum#3", "test3" } };
+ assertHeaders(headers);
+ }
+
+ @Test
+ public void testHeaderWithNumberValues() throws IOException
+ {
+ String headers[][] =
+ {
+ { "hello", "1" },
+ { "hello", "-1" },
+ { "hello", "100" },
+ { "hello", "100" },
+ { "hello", "100" },
+ { "hello", "100" },
+ { "hello", "100" },
+ { "hello1", "200" } };
+ assertHeaders(headers);
+ }
+
+ @Test
+ public void testHeaderOverwriteValues() throws IOException
+ {
+ String headers[][] =
+ {
+ { "size", "100" },
+ { "size", "200" },
+ { "size", "300" },
+ { "size", "400" },
+ { "size", "500" },
+ { "title", "abc" },
+ { "title", "bac" },
+ { "title", "cba" },
+ { "title1", "abba" },
+ { "title1", "abba1" },
+ { "title1", "abba" },
+ { "title1", "abba1" } };
+ assertHeaders(headers);
+ Iterator e = _response.getHeaders("size").iterator();
+ int count = 0;
+ while (e.hasNext())
+ {
+ e.next();
+ count++;
+ }
+ assertEquals(1,count);
+ assertEquals("500",_response.getHeader("size"));
+ assertEquals("cba",_response.getHeader("title"));
+ assertEquals("abba1",_response.getHeader("title1"));
+ }
+
+ @Test
+ public void testMatch() throws Exception
+ {
+ _rule.setRegex("/my/dir/file/(.*)$");
+ _rule.setName("cache-control");
+ _rule.setValue("no-store");
+ _rule.matchAndApply("/my/dir/file/",_request,_response);
+ assertEquals("no-store",_response.getHeader("cache-control"));
+ }
+
+ @Test
+ public void testNotMatch() throws Exception
+ {
+ reset();
+ _rule.setRegex("/my/dir/file/(.*)$");
+ _rule.setName("cache-control");
+ _rule.setValue("no-store");
+ _rule.matchAndApply("/my/dir/file_not_match/",_request,_response);
+ assertEquals(null,_response.getHeader("cache-control"));
+ }
+
+ private void assertHeaders(String headers[][]) throws IOException
+ {
+ for (String[] header : headers)
+ {
+ _rule.setName(header[0]);
+ _rule.setValue(header[1]);
+ _rule.apply(null,_request,_response,null);
+ assertEquals(header[1],_response.getHeader(header[0]));
+ }
+ }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
index e68102e93ed..7c418ff4d1b 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
@@ -256,11 +256,12 @@ public class HttpConfiguration
* before being sent to the client. A larger buffer can improve performance by allowing
* a content producer to run without blocking, however larger buffers consume more memory and
* may induce some latency before a client starts processing the content.
- * @param responseBufferSize buffer size in bytes.
+ * @param outputBufferSize buffer size in bytes.
*/
- public void setOutputBufferSize(int responseBufferSize)
+ public void setOutputBufferSize(int outputBufferSize)
{
- _outputBufferSize = responseBufferSize;
+ _outputBufferSize = outputBufferSize;
+ setOutputAggregationSize(outputBufferSize / 4);
}
/* ------------------------------------------------------------ */
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
index 756e15a6db7..ec564a05969 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
@@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.URIUtil;
@@ -41,11 +42,19 @@ public class SecuredRedirectHandler extends AbstractHandler
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
- HttpConfiguration httpConfig = HttpChannel.getCurrentHttpChannel().getHttpConfiguration();
-
- if (baseRequest.isSecure())
+ HttpConnection connection = HttpConnection.getCurrentConnection();
+ if (baseRequest.isSecure() || (connection == null))
{
- return; // all done
+ // nothing to do
+ return;
+ }
+
+ HttpConfiguration httpConfig = connection.getHttpConfiguration();
+ if (httpConfig == null)
+ {
+ // no config, show error
+ response.sendError(HttpStatus.FORBIDDEN_403,"No http configuration available");
+ return;
}
if (httpConfig.getSecurePort() > 0)
@@ -61,6 +70,7 @@ public class SecuredRedirectHandler extends AbstractHandler
{
response.sendError(HttpStatus.FORBIDDEN_403,"Not Secure");
}
+
baseRequest.setHandled(true);
}
}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 9a9bf228c64..6311383a6c3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -81,7 +81,7 @@
2
${project.name}
- ${bundle-symbolic-name}.source;singleton:=true
+ ${bundle-symbolic-name}.source
Eclipse.org - Jetty
${parsedVersion.osgiVersion}
${bundle-symbolic-name};version="${parsedVersion.osgiVersion}";roots:="."