/*
* 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.server.ext;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.cometd.bayeux.Channel;
import org.cometd.bayeux.Message;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.common.JSONContext;
import org.cometd.server.AbstractBayeuxClientServerTest;
import org.cometd.server.AbstractService;
import org.cometd.server.BayeuxServerImpl;
import org.cometd.server.JettyJSONContextServer;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class BayeuxExtensionTest extends AbstractBayeuxClientServerTest {
private static final String CLIENT_MESSAGE_FIELD = "clientMessageField";
private static final String CLIENT_EXT_FIELD = "clientExtField";
private static final String CLIENT_DATA_FIELD = "clientDataField";
private static final String SERVER_EXT_MESSAGE_FIELD = "serverExtMessageField";
private static final String SERVER_EXT_DATA_FIELD = "serverExtDataField";
private static final String SERVER_EXT_EXT_FIELD = "serverExtExtField";
private static final String SERVER_MESSAGE_FIELD = "serverMessageField";
private static final String SERVER_DATA_FIELD = "serverDataField";
private static final String SERVER_EXT_FIELD = "serverExtField";
private static final String SERVER_EXT_INFO = "fromExtension";
private static final String CLIENT_INFO = "fromClient";
private static final String SERVICE_INFO = "fromService";
public BayeuxExtensionTest(String serverTransport) {
super(serverTransport);
}
@Before
public void prepare() throws Exception {
startServer(null);
}
@Test
public void testBayeuxExtensionOnHandshake() throws Exception {
bayeux.addExtension(new MetaExtension());
final AtomicReference<ServerMessage> handshakeRef = new AtomicReference<>();
new MetaHandshakeService(bayeux, handshakeRef);
Request handshake = newBayeuxRequest("" +
"[{" +
"\"channel\": \"/meta/handshake\"," +
"\"version\": \"1.0\"," +
"\"minimumVersion\": \"1.0\"," +
"\"supportedConnectionTypes\": [\"long-polling\"]," +
"\"" + CLIENT_MESSAGE_FIELD + "\": \"" + CLIENT_INFO + "\"," +
"\"ext\": { \"" + CLIENT_EXT_FIELD + "\": \"" + CLIENT_INFO + "\" }," +
"\"data\": { \"" + CLIENT_DATA_FIELD + "\": \"" + CLIENT_INFO + "\" }" +
"}]");
ContentResponse response = handshake.send();
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals(SERVER_EXT_INFO, handshakeRef.get().get(SERVER_EXT_MESSAGE_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, handshakeRef.get().getDataAsMap().get(SERVER_EXT_DATA_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, handshakeRef.get().getExt().get(SERVER_EXT_EXT_FIELD));
Assert.assertEquals(CLIENT_INFO, handshakeRef.get().get(CLIENT_MESSAGE_FIELD));
Assert.assertEquals(CLIENT_INFO, handshakeRef.get().getExt().get(CLIENT_EXT_FIELD));
Assert.assertEquals(CLIENT_INFO, handshakeRef.get().getDataAsMap().get(CLIENT_DATA_FIELD));
JSONContext.Server jsonContext = new JettyJSONContextServer();
ServerMessage.Mutable[] messages = jsonContext.parse(response.getContentAsString());
Assert.assertEquals(1, messages.length);
Map<String, Object> message = messages[0];
Assert.assertEquals(SERVER_EXT_INFO, message.get(SERVER_EXT_MESSAGE_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, ((Map)message.get(Message.DATA_FIELD)).get(SERVER_EXT_DATA_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, ((Map)message.get(Message.EXT_FIELD)).get(SERVER_EXT_EXT_FIELD));
}
@Test
public void testBayeuxExtensionOnServiceChannel() throws Exception {
final String channel = "/service/test";
bayeux.addExtension(new NonMetaExtension(channel));
final AtomicReference<ServerMessage> publishRef = new AtomicReference<>();
new ServiceChannelService(bayeux, channel, publishRef);
Request handshake = newBayeuxRequest("" +
"[{" +
"\"channel\": \"/meta/handshake\"," +
"\"version\": \"1.0\"," +
"\"minimumVersion\": \"1.0\"," +
"\"supportedConnectionTypes\": [\"long-polling\"]" +
"}]");
ContentResponse response = handshake.send();
Assert.assertEquals(200, response.getStatus());
String clientId = extractClientId(response);
Request publish = newBayeuxRequest("" +
"[{" +
"\"clientId\": \"" + clientId + "\"," +
"\"channel\": \"" + channel + "\"," +
"\"" + CLIENT_MESSAGE_FIELD + "\": \"" + CLIENT_INFO + "\"," +
"\"ext\": { \"" + CLIENT_EXT_FIELD + "\": \"" + CLIENT_INFO + "\" }," +
"\"data\": { \"" + CLIENT_DATA_FIELD + "\": \"" + CLIENT_INFO + "\" }" +
"}]");
response = publish.send();
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals(SERVER_EXT_INFO, publishRef.get().get(SERVER_EXT_MESSAGE_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, publishRef.get().getDataAsMap().get(SERVER_EXT_DATA_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, publishRef.get().getExt().get(SERVER_EXT_EXT_FIELD));
Assert.assertEquals(CLIENT_INFO, publishRef.get().get(CLIENT_MESSAGE_FIELD));
Assert.assertEquals(CLIENT_INFO, publishRef.get().getExt().get(CLIENT_EXT_FIELD));
Assert.assertEquals(CLIENT_INFO, publishRef.get().getDataAsMap().get(CLIENT_DATA_FIELD));
JSONContext.Server jsonContext = new JettyJSONContextServer();
ServerMessage.Mutable[] messages = jsonContext.parse(response.getContentAsString());
Assert.assertEquals(2, messages.length);
Map<String, Object> message = messages[0].containsKey(Message.DATA_FIELD) ? messages[0] : messages[1];
Assert.assertEquals(SERVER_EXT_INFO, message.get(SERVER_EXT_MESSAGE_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, ((Map)message.get(Message.DATA_FIELD)).get(SERVER_EXT_DATA_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, ((Map)message.get(Message.EXT_FIELD)).get(SERVER_EXT_EXT_FIELD));
Assert.assertEquals(SERVICE_INFO, message.get(SERVER_MESSAGE_FIELD));
Assert.assertEquals(SERVICE_INFO, ((Map)message.get(Message.DATA_FIELD)).get(SERVER_DATA_FIELD));
Assert.assertEquals(SERVICE_INFO, ((Map)message.get(Message.EXT_FIELD)).get(SERVER_EXT_FIELD));
}
@Test
public void testBayeuxExtensionOnBroadcastChannel() throws Exception {
final String channel = "/test";
bayeux.addExtension(new NonMetaExtension(channel));
final AtomicReference<ServerMessage> publishRef = new AtomicReference<>();
new BroadcastChannelService(bayeux, channel, publishRef);
Request handshake = newBayeuxRequest("" +
"[{" +
"\"channel\": \"/meta/handshake\"," +
"\"version\": \"1.0\"," +
"\"minimumVersion\": \"1.0\"," +
"\"supportedConnectionTypes\": [\"long-polling\"]" +
"}]");
ContentResponse response = handshake.send();
Assert.assertEquals(200, response.getStatus());
String clientId = extractClientId(response);
Request subscribe = newBayeuxRequest("" +
"[{" +
"\"clientId\": \"" + clientId + "\"," +
"\"channel\": \"/meta/subscribe\"," +
"\"subscription\": \"" + channel + "\"," +
"}]");
response = subscribe.send();
Assert.assertEquals(200, response.getStatus());
Request publish = newBayeuxRequest("" +
"[{" +
"\"clientId\": \"" + clientId + "\"," +
"\"channel\": \"" + channel + "\"," +
"\"" + CLIENT_MESSAGE_FIELD + "\": \"" + CLIENT_INFO + "\"," +
"\"ext\": { \"" + CLIENT_EXT_FIELD + "\": \"" + CLIENT_INFO + "\" }," +
"\"data\": { \"" + CLIENT_DATA_FIELD + "\": \"" + CLIENT_INFO + "\" }" +
"}]");
response = publish.send();
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals(SERVER_EXT_INFO, publishRef.get().get(SERVER_EXT_MESSAGE_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, publishRef.get().getDataAsMap().get(SERVER_EXT_DATA_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, publishRef.get().getExt().get(SERVER_EXT_EXT_FIELD));
Assert.assertEquals(CLIENT_INFO, publishRef.get().get(CLIENT_MESSAGE_FIELD));
Assert.assertEquals(CLIENT_INFO, publishRef.get().getExt().get(CLIENT_EXT_FIELD));
Assert.assertEquals(CLIENT_INFO, publishRef.get().getDataAsMap().get(CLIENT_DATA_FIELD));
JSONContext.Server jsonContext = new JettyJSONContextServer();
ServerMessage.Mutable[] messages = jsonContext.parse(response.getContentAsString());
Assert.assertEquals(2, messages.length);
Map<String, Object> message = messages[0].containsKey(Message.DATA_FIELD) ? messages[0] : messages[1];
Assert.assertEquals(SERVER_EXT_INFO, message.get(SERVER_EXT_MESSAGE_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, ((Map)message.get(Message.DATA_FIELD)).get(SERVER_EXT_DATA_FIELD));
Assert.assertEquals(SERVER_EXT_INFO, ((Map)message.get(Message.EXT_FIELD)).get(SERVER_EXT_EXT_FIELD));
Assert.assertEquals(SERVICE_INFO, message.get(SERVER_MESSAGE_FIELD));
Assert.assertEquals(SERVICE_INFO, ((Map)message.get(Message.DATA_FIELD)).get(SERVER_DATA_FIELD));
Assert.assertEquals(SERVICE_INFO, ((Map)message.get(Message.EXT_FIELD)).get(SERVER_EXT_FIELD));
}
private class MetaExtension extends BayeuxServer.Extension.Adapter {
@Override
public boolean rcvMeta(ServerSession from, ServerMessage.Mutable message) {
if (Channel.META_HANDSHAKE.equals(message.getChannel()) && (from == null || !from.isLocalSession())) {
message.put(SERVER_EXT_MESSAGE_FIELD, SERVER_EXT_INFO);
message.getDataAsMap(true).put(SERVER_EXT_DATA_FIELD, SERVER_EXT_INFO);
message.getExt(true).put(SERVER_EXT_EXT_FIELD, SERVER_EXT_INFO);
}
return true;
}
@Override
public boolean sendMeta(ServerSession to, ServerMessage.Mutable message) {
if (Channel.META_HANDSHAKE.equals(message.getChannel()) && to != null && !to.isLocalSession()) {
message.put(SERVER_EXT_MESSAGE_FIELD, SERVER_EXT_INFO);
message.getDataAsMap(true).put(SERVER_EXT_DATA_FIELD, SERVER_EXT_INFO);
message.getExt(true).put(SERVER_EXT_EXT_FIELD, SERVER_EXT_INFO);
}
return true;
}
}
private class NonMetaExtension extends BayeuxServer.Extension.Adapter {
private final String channel;
private NonMetaExtension(String channel) {
this.channel = channel;
}
@Override
public boolean rcv(ServerSession from, ServerMessage.Mutable message) {
if (from != null && !from.isLocalSession() && channel.equals(message.getChannel())) {
message.put(SERVER_EXT_MESSAGE_FIELD, SERVER_EXT_INFO);
message.getDataAsMap(true).put(SERVER_EXT_DATA_FIELD, SERVER_EXT_INFO);
message.getExt(true).put(SERVER_EXT_EXT_FIELD, SERVER_EXT_INFO);
}
return true;
}
@Override
public boolean send(ServerSession from, ServerSession to, ServerMessage.Mutable message) {
if (from != null && from.isLocalSession() && to != null && !to.isLocalSession()) {
message.put(SERVER_EXT_MESSAGE_FIELD, SERVER_EXT_INFO);
message.getDataAsMap(true).put(SERVER_EXT_DATA_FIELD, SERVER_EXT_INFO);
message.getExt(true).put(SERVER_EXT_EXT_FIELD, SERVER_EXT_INFO);
}
return true;
}
}
public static class MetaHandshakeService extends AbstractService {
private final AtomicReference<ServerMessage> handshakeRef;
public MetaHandshakeService(BayeuxServer bayeux, AtomicReference<ServerMessage> handshakeRef) {
super(bayeux, "test");
this.handshakeRef = handshakeRef;
addService(Channel.META_HANDSHAKE, "metaHandshake");
}
public void metaHandshake(ServerSession remote, ServerMessage message) {
handshakeRef.set(message);
}
}
public static class ServiceChannelService extends AbstractService {
private final AtomicReference<ServerMessage> publishRef;
public ServiceChannelService(BayeuxServer bayeux, String channel, AtomicReference<ServerMessage> publishRef) {
super(bayeux, "test");
this.publishRef = publishRef;
addService(channel, "test");
}
public void test(ServerSession remote, ServerMessage message) {
publishRef.set(message);
ServerMessage.Mutable response = getBayeux().newMessage();
response.put(SERVER_MESSAGE_FIELD, SERVICE_INFO);
response.getDataAsMap(true).put(SERVER_DATA_FIELD, SERVICE_INFO);
response.getExt(true).put(SERVER_EXT_FIELD, SERVICE_INFO);
remote.deliver(getServerSession(), response);
}
}
public static class BroadcastChannelService extends AbstractService {
private final AtomicReference<ServerMessage> publishRef;
public BroadcastChannelService(BayeuxServerImpl bayeux, String channel, AtomicReference<ServerMessage> publishRef) {
super(bayeux, "test");
this.publishRef = publishRef;
addService(channel, "test");
}
public void test(ServerSession remote, ServerMessage.Mutable message) {
message.put(SERVER_MESSAGE_FIELD, SERVICE_INFO);
message.getDataAsMap(true).put(SERVER_DATA_FIELD, SERVICE_INFO);
message.getExt(true).put(SERVER_EXT_FIELD, SERVICE_INFO);
publishRef.set(message);
}
}
}