/*
* Copyright (c) 2011-2015 The original author or authors
*
* 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 io.vertx.ext.stomp.impl;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.WebSocket;
import io.vertx.ext.stomp.*;
import io.vertx.ext.stomp.utils.Headers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static com.jayway.awaitility.Awaitility.await;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Checks that the server can handle web socket connection. These tests mimic the behavior of StompJS
* (http://jmesnil.net/stomp-websocket/doc/).
*
* @author <a href="http://escoffier.me">Clement Escoffier</a>
*/
public class WebSocketBridgeTest {
private Vertx vertx;
private StompServer server;
private HttpServer http;
private List<StompClient> clients = new ArrayList<>();
@Before
public void setUp() {
vertx = Vertx.vertx();
AsyncLock<HttpServer> httpLock = new AsyncLock<>();
AsyncLock<StompServer> stompLock = new AsyncLock<>();
vertx = Vertx.vertx();
server = StompServer.create(vertx, new StompServerOptions().setWebsocketBridge(true))
.handler(StompServerHandler.create(vertx))
.listen(stompLock.handler());
stompLock.waitForSuccess();
http = vertx.createHttpServer().websocketHandler(server.webSocketHandler()).listen(8080, httpLock.handler());
httpLock.waitForSuccess();
}
@After
public void tearDown() {
clients.forEach(StompClient::close);
clients.clear();
AsyncLock<Void> lock = new AsyncLock<>();
server.close(lock.handler());
lock.waitForSuccess();
lock = new AsyncLock<>();
http.close(lock.handler());
lock.waitForSuccess();
lock = new AsyncLock<>();
vertx.close(lock.handler());
lock.waitForSuccess();
}
@Test
public void testConnection() {
AtomicReference<Throwable> error = new AtomicReference<>();
AtomicReference<Buffer> frame = new AtomicReference<>();
AtomicReference<WebSocket> socket = new AtomicReference<>();
vertx.createHttpClient().websocket(8080, "localhost", "/stomp", MultiMap.caseInsensitiveMultiMap().add
("Sec-WebSocket-Protocol", "v10.stomp, v11.stomp, v12.stomp"), ws -> {
socket.set(ws);
ws.exceptionHandler(error::set)
.handler(frame::set)
.write(new Frame(Frame.Command.CONNECT, Headers.create("accept-version", "1.2,1.1,1.0",
"heart-beat", "10000,10000"), null).toBuffer());
});
await().atMost(10, TimeUnit.SECONDS).until(() -> error.get() == null && frame.get() != null);
assertThat(frame.get().toString()).startsWith("CONNECTED")
.contains("server:vertx-stomp", "heart-beat:", "session:", "version:1.2");
socket.get().close();
}
@Test
public void testReceivingAMessage() {
AtomicReference<Throwable> error = new AtomicReference<>();
AtomicReference<Buffer> frame = new AtomicReference<>();
AtomicReference<WebSocket> socket = new AtomicReference<>();
AtomicReference<StompClientConnection> client = new AtomicReference<>();
clients.add(StompClient.create(vertx).connect(61613, "localhost", connection -> {
client.set(connection.result());
}));
await().atMost(10, TimeUnit.SECONDS).until(() -> client.get() != null);
vertx.createHttpClient().websocket(8080, "localhost", "/stomp", MultiMap.caseInsensitiveMultiMap().add
("Sec-WebSocket-Protocol", "v10.stomp, v11.stomp, v12.stomp"), ws -> {
socket.set(ws);
ws.exceptionHandler(error::set)
.handler(buffer -> {
if (buffer.toString().startsWith("CONNECTED")) {
ws.write(
new Frame(Frame.Command.SUBSCRIBE, Headers.create("id", "sub-0", "destination", "foo"), null)
.toBuffer());
return;
}
if (frame.get() == null) {
frame.set(buffer);
}
})
.write(new Frame(Frame.Command.CONNECT, Headers.create("accept-version", "1.2,1.1,1.0",
"heart-beat", "10000,10000"), null).toBuffer());
});
await().atMost(10, TimeUnit.SECONDS).until(() -> server.stompHandler().getDestination("foo") != null);
client.get().send("foo", Headers.create("header", "value"), Buffer.buffer("hello"));
await().atMost(10, TimeUnit.SECONDS).until(() -> error.get() == null && frame.get() != null);
assertThat(frame.get().toString()).startsWith("MESSAGE")
.contains("destination:foo", "content-length:5", "header:value", "subscription:sub-0", "\nhello");
socket.get().close();
}
@Test
public void testSendingAMessage() {
AtomicReference<Throwable> error = new AtomicReference<>();
AtomicReference<Frame> frame = new AtomicReference<>();
AtomicReference<WebSocket> socket = new AtomicReference<>();
AtomicReference<StompClientConnection> client = new AtomicReference<>();
clients.add(StompClient.create(vertx).connect(61613, "localhost", connection -> {
connection.result().subscribe("foo", frame::set, r -> {
client.set(connection.result());
});
}));
await().atMost(10, TimeUnit.SECONDS).until(() -> client.get() != null);
await().atMost(10, TimeUnit.SECONDS).until(() -> server.stompHandler().getDestination("foo") != null);
vertx.createHttpClient().websocket(8080, "localhost", "/stomp", MultiMap.caseInsensitiveMultiMap().add
("Sec-WebSocket-Protocol", "v10.stomp, v11.stomp, v12.stomp"), ws -> {
socket.set(ws);
ws.exceptionHandler(error::set)
.handler(buffer -> {
if (buffer.toString().startsWith("CONNECTED")) {
ws.write(
new Frame(Frame.Command.SEND, Headers.create("header", "value", "destination", "foo"), Buffer
.buffer("hello")).toBuffer());
}
})
.write(new Frame(Frame.Command.CONNECT, Headers.create("accept-version", "1.2,1.1,1.0",
"heart-beat", "10000,10000"), null).toBuffer());
});
await().atMost(10, TimeUnit.SECONDS).until(() -> error.get() == null && frame.get() != null);
assertThat(frame.get().toString()).startsWith("MESSAGE")
.contains("destination:foo", "header:value", "\nhello");
socket.get().close();
}
@Test
public void testPingFromServer() {
AtomicReference<Throwable> error = new AtomicReference<>();
AtomicReference<WebSocket> socket = new AtomicReference<>();
AtomicReference<Boolean> flag = new AtomicReference<>();
AtomicReference<StompClientConnection> client = new AtomicReference<>();
clients.add(StompClient.create(vertx).connect(61613, "localhost", connection -> {
client.set(connection.result());
}));
await().atMost(10, TimeUnit.SECONDS).until(() -> client.get() != null);
vertx.createHttpClient().websocket(8080, "localhost", "/stomp", MultiMap.caseInsensitiveMultiMap().add
("Sec-WebSocket-Protocol", "v10.stomp, v11.stomp, v12.stomp"), ws -> {
socket.set(ws);
ws.exceptionHandler(error::set)
.handler(buffer -> {
vertx.setTimer(1000, id -> {
flag.set(true);
});
})
.write(new Frame(Frame.Command.CONNECT, Headers.create("accept-version", "1.2,1.1,1.0",
"heart-beat", "100,0"), null).toBuffer());
});
await().atMost(10, TimeUnit.SECONDS).until(() -> error.get() == null && flag.get() != null);
socket.get().close();
}
@Test
public void testWebSocketsWhenTCPDisabled() {
AsyncLock<Void> lock = new AsyncLock<>();
server.close(lock.handler());
lock.waitForSuccess();
lock = new AsyncLock<>();
http.close(lock.handler());
lock.waitForSuccess();
server = StompServer.create(vertx, new StompServerOptions().setWebsocketBridge(true).setPort(-1)
.setWebsocketPath("/something"))
.handler(StompServerHandler.create(vertx));
AsyncLock<HttpServer> httpLock = new AsyncLock<>();
http = vertx.createHttpServer().websocketHandler(server.webSocketHandler()).listen(8080, httpLock.handler());
httpLock.waitForSuccess();
AtomicReference<Throwable> error = new AtomicReference<>();
AtomicReference<WebSocket> sender = new AtomicReference<>();
AtomicReference<WebSocket> receiver = new AtomicReference<>();
AtomicReference<Buffer> frame = new AtomicReference<>();
vertx.createHttpClient().websocket(8080, "localhost", "/something", MultiMap.caseInsensitiveMultiMap().add
("Sec-WebSocket-Protocol", "v10.stomp, v11.stomp, v12.stomp"), ws -> {
receiver.set(ws);
ws.exceptionHandler(error::set)
.handler(buffer -> {
if (buffer.toString().startsWith("CONNECTED")) {
ws.write(
new Frame(Frame.Command.SUBSCRIBE, Headers.create("id", "sub-0", "destination", "foo"), null)
.toBuffer());
return;
}
if (frame.get() == null) {
frame.set(buffer);
}
})
.write(new Frame(Frame.Command.CONNECT, Headers.create("accept-version", "1.2,1.1,1.0",
"heart-beat", "10000,10000"), null).toBuffer());
});
await().atMost(10, TimeUnit.SECONDS).until(() -> server.stompHandler().getDestination("foo") != null);
vertx.createHttpClient().websocket(8080, "localhost", "/something", MultiMap.caseInsensitiveMultiMap().add
("Sec-WebSocket-Protocol", "v10.stomp, v11.stomp, v12.stomp"), ws -> {
sender.set(ws);
ws.exceptionHandler(error::set)
.handler(buffer -> {
if (buffer.toString().startsWith("CONNECTED")) {
ws.write(
new Frame(Frame.Command.SEND, Headers.create("header", "value", "destination", "foo"), Buffer
.buffer("hello")).toBuffer());
}
})
.write(new Frame(Frame.Command.CONNECT, Headers.create("accept-version", "1.2,1.1,1.0",
"heart-beat", "10000,10000"), null).toBuffer());
});
await().atMost(10, TimeUnit.SECONDS).until(() -> error.get() == null && frame.get() != null);
assertThat(frame.get().toString()).startsWith("MESSAGE")
.contains("destination:foo", "header:value", "subscription:sub-0", "\nhello");
receiver.get().close();
sender.get().close();
}
}