/* * 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.client; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.cometd.bayeux.BinaryData; import org.cometd.bayeux.Channel; import org.cometd.bayeux.Message; import org.cometd.bayeux.client.ClientSession; import org.cometd.bayeux.client.ClientSessionChannel; import org.cometd.bayeux.server.BayeuxServer; import org.cometd.bayeux.server.ServerChannel; import org.cometd.bayeux.server.ServerMessage; import org.cometd.bayeux.server.ServerSession; import org.cometd.server.ext.BinarySessionExtension; import org.eclipse.jetty.util.IO; import org.junit.Assert; import org.junit.Test; public class ExtensionProcessingTest extends ClientServerTest { @Test public void testServerExtensionProcessing() throws Exception { startServer(null); bayeux.addExtension(new org.cometd.server.ext.BinaryExtension()); bayeux.addExtension(new GZIPServerExtension()); final BayeuxClient client = newBayeuxClient(); client.addExtension(new org.cometd.client.ext.BinaryExtension()); client.addExtension(new GZIPClientExtension()); String channelName = "/server_ext_proc"; final String data = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; testExtensionProcessing(client, channelName, data); } @Test public void testServerSessionExtensionProcessing() throws Exception { startServer(null); bayeux.addExtension(new org.cometd.server.ext.BinaryExtension()); bayeux.addExtension(new BayeuxServer.Extension.Adapter() { @Override public boolean rcvMeta(ServerSession from, ServerMessage.Mutable message) { if (Channel.META_HANDSHAKE.equals(message.getChannel())) { from.addExtension(new BinarySessionExtension(bayeux)); } return true; } }); bayeux.addExtension(new BayeuxServer.Extension.Adapter() { @Override public boolean rcvMeta(ServerSession from, ServerMessage.Mutable message) { if (Channel.META_HANDSHAKE.equals(message.getChannel())) { from.addExtension(new GZIPServerSessionExtension(bayeux)); } return true; } }); bayeux.addExtension(new BayeuxServer.Extension.Adapter() { @Override public boolean rcvMeta(ServerSession from, ServerMessage.Mutable message) { if (Channel.META_HANDSHAKE.equals(message.getChannel())) { from.addExtension(new ScrambleServerSessionExtension(bayeux)); } return true; } }); final BayeuxClient client = newBayeuxClient(); client.addExtension(new org.cometd.client.ext.BinaryExtension()); client.addExtension(new GZIPClientExtension()); client.addExtension(new ScrambleClientExtension()); String channelName = "/session_ext_proc"; String data = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; testExtensionProcessing(client, channelName, data); } private void testExtensionProcessing(final BayeuxClient client, final String channelName, final String data) throws Exception { bayeux.createChannelIfAbsent(channelName).getReference().addListener(new ServerChannel.MessageListener() { @Override public boolean onMessage(ServerSession from, ServerChannel channel, ServerMessage.Mutable message) { return data.equals(message.getData()); } }); final CountDownLatch latch = new CountDownLatch(1); client.handshake(new ClientSessionChannel.MessageListener() { @Override public void onMessage(ClientSessionChannel channel, Message message) { client.batch(new Runnable() { @Override public void run() { ClientSessionChannel c = client.getChannel(channelName); c.subscribe(new ClientSessionChannel.MessageListener() { @Override public void onMessage(ClientSessionChannel channel, Message message) { if (data.equals(message.getData())) { latch.countDown(); } } }); c.publish(data); } }); } }); Assert.assertTrue(latch.await(555, TimeUnit.SECONDS)); disconnectBayeuxClient(client); } private static void gunzip(Message.Mutable message) throws IOException { BinaryData binary = (BinaryData)message.getData(); GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(binary.asBytes())); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); IO.copy(gzip, bytes); gzip.close(); message.setData(new String(bytes.toByteArray(), StandardCharsets.UTF_8)); } private static byte[] gzip(Message message) throws IOException { String data = (String)message.getData(); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); GZIPOutputStream gzip = new GZIPOutputStream(bytes); gzip.write(data.getBytes(StandardCharsets.UTF_8)); gzip.close(); return bytes.toByteArray(); } private static void unscramble(Message.Mutable message) { String data = (String)message.getData(); StringBuilder result = new StringBuilder(data.length()); for (int i = 0; i < data.length(); ++i) { result.append((char)(data.charAt(i) - 1)); } message.setData(result.toString()); } private static String scramble(Message message) { String data = (String)message.getData(); StringBuilder result = new StringBuilder(data.length()); for (int i = 0; i < data.length(); ++i) { result.append((char)(data.charAt(i) + 1)); } return result.toString(); } private static class GZIPClientExtension extends ClientSession.Extension.Adapter { @Override public boolean rcv(ClientSession session, Message.Mutable message) { try { if (!message.isPublishReply()) { gunzip(message); } return true; } catch (IOException x) { return false; } } @Override public boolean send(ClientSession session, Message.Mutable message) { try { message.setData(new BinaryData(gzip(message), true, null)); return true; } catch (IOException x) { return false; } } } private static class ScrambleClientExtension extends ClientSession.Extension.Adapter { @Override public boolean rcv(ClientSession session, Message.Mutable message) { if (!message.isPublishReply()) { unscramble(message); } return true; } @Override public boolean send(ClientSession session, Message.Mutable message) { message.setData(scramble(message)); return true; } } private static class GZIPServerExtension extends BayeuxServer.Extension.Adapter { @Override public boolean rcv(ServerSession from, ServerMessage.Mutable message) { try { gunzip(message); return true; } catch (IOException x) { return false; } } @Override public boolean send(ServerSession from, ServerSession to, ServerMessage.Mutable message) { try { if (!message.isPublishReply()) { byte[] bytes = gzip(message); message.setData(new BinaryData(bytes, true, null)); } return true; } catch (IOException x) { return false; } } } private static class GZIPServerSessionExtension extends ServerSession.Extension.Adapter { private final BayeuxServer bayeux; private GZIPServerSessionExtension(BayeuxServer bayeux) { this.bayeux = bayeux; } @Override public boolean rcv(ServerSession session, ServerMessage.Mutable message) { try { gunzip(message); return true; } catch (IOException x) { return false; } } @Override public ServerMessage send(ServerSession session, ServerMessage message) { try { if (message.isPublishReply()) { return message; } else { ServerMessage.Mutable result = bayeux.newMessage(); result.putAll(message); result.setData(new BinaryData(gzip(message), true, null)); return result; } } catch (IOException x) { return null; } } } private static class ScrambleServerSessionExtension extends ServerSession.Extension.Adapter { private final BayeuxServer bayeux; private ScrambleServerSessionExtension(BayeuxServer bayeux) { this.bayeux = bayeux; } @Override public boolean rcv(ServerSession session, ServerMessage.Mutable message) { unscramble(message); return true; } @Override public ServerMessage send(ServerSession session, ServerMessage message) { if (message.isPublishReply()) { return message; } else { ServerMessage.Mutable result = bayeux.newMessage(); result.putAll(message); result.setData(scramble(message)); return result; } } } }