/* * Copyright (c) 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cometd.javascript; import java.io.IOException; import java.util.EnumSet; import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.cometd.bayeux.server.ServerSession; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.Assert; import org.junit.Test; public class CometDHandshakeDynamicPropsTest extends AbstractCometDLongPollingTest { private BayeuxFilter filter; @Override protected void customizeContext(ServletContextHandler context) throws Exception { super.customizeContext(context); filter = new BayeuxFilter(); FilterHolder filterHolder = new FilterHolder(filter); context.addFilter(filterHolder, cometdServletPath + "/*", EnumSet.of(DispatcherType.REQUEST)); } @Test public void testHandshakeDynamicProps() throws Exception { defineClass(Latch.class); StringBuilder script = new StringBuilder(); script.append("cometd.configure({url: '").append(cometdURL); script.append("', logLevel: '").append(getLogLevel()).append("'});"); script.append("var outHandshake = undefined;"); script.append("var outLatch = new Latch(1);"); script.append("cometd.registerExtension('test', {"); script.append(" outgoing: function(message)"); script.append(" {"); script.append(" if ('/meta/handshake' == message.channel)"); script.append(" {"); script.append(" outHandshake = message;"); script.append(" outLatch.countDown();"); script.append(" }"); script.append(" }"); script.append("});"); script.append("var inHandshake = undefined;"); script.append("var inLatch = new Latch(1);"); script.append("cometd.addListener('/meta/handshake', function(message)"); script.append("{"); script.append(" inHandshake = message;"); script.append(" inLatch.countDown();"); script.append("});"); evaluateScript(script.toString()); script.setLength(0); Latch outLatch = get("outLatch"); script.append("var handshakeProps = { ext: { token: 1 } };"); script.append("cometd.handshake(handshakeProps)"); evaluateScript(script.toString()); script.setLength(0); Assert.assertTrue(outLatch.await(5000)); evaluateScript("window.assert(outHandshake !== undefined, 'handshake is undefined');"); evaluateScript("window.assert(outHandshake.ext !== undefined, 'handshake without ext');"); int token = ((Number)evaluateScript("outHandshake.ext.token")).intValue(); Assert.assertEquals(1, token); Latch inLatch = get("inLatch"); Assert.assertTrue(inLatch.await(5000)); String clientId = evaluateScript("inHandshake.clientId"); evaluateScript("outHandshake = undefined;"); evaluateScript("handshakeProps.ext.token = 2;"); evaluateScript("outLatch = new Latch(1);"); outLatch = get("outLatch"); // This triggers a re-handshake filter.setClientId(clientId); // Wait for the re-handshake Assert.assertTrue(outLatch.await(5000)); evaluateScript("window.assert(outHandshake !== undefined, 'handshake is undefined');"); evaluateScript("window.assert(outHandshake.ext !== undefined, 'handshake without ext');"); token = ((Number)evaluateScript("outHandshake.ext.token")).intValue(); Assert.assertEquals(2, token); evaluateScript("cometd.disconnect(true);"); } public class BayeuxFilter implements Filter { private boolean handshook; private String clientId; @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain); } private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { if (handshook) { synchronized (this) { while (clientId == null) { try { wait(); } catch (InterruptedException x) { throw new ServletException(x); } } // Remove the client, so that the CometD implementation will send // "unknown client" and the JavaScript will re-handshake ServerSession session = bayeuxServer.getSession(clientId); if (session != null) { bayeuxServer.removeServerSession(session, false); } } } else { handshook = true; } chain.doFilter(request, response); } public void setClientId(String clientId) { synchronized (this) { this.clientId = clientId; notifyAll(); } } @Override public void destroy() { } } }