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:="."