/* * 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 com.jayway.awaitility.Awaitility; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetClient; import io.vertx.core.net.NetSocket; import io.vertx.ext.stomp.StompServer; import io.vertx.ext.stomp.StompServerHandler; import io.vertx.ext.unit.Async; import io.vertx.ext.unit.TestContext; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static org.assertj.core.api.Assertions.assertThat; /** * Test STOMP server / client connection protocol. * * @author <a href="http://escoffier.me">Clement Escoffier</a> */ @RunWith(io.vertx.ext.unit.junit.VertxUnitRunner.class) public class ServerConnectionTest { private Vertx vertx; private StompServer server; private NetClient client; @Before public void setUp() { vertx = Vertx.vertx(); AsyncLock<StompServer> lock = new AsyncLock<>(); server = StompServer.create(vertx) .handler(StompServerHandler.create(vertx)) .listen(lock.handler()); lock.waitForSuccess(); } @After public void tearDown() { AsyncLock<Void> lock = new AsyncLock<>(); server.close(lock.handler()); lock.waitForSuccess(); lock = new AsyncLock<>(); vertx.close(lock.handler()); lock.waitForSuccess(); if (client != null) { client.close(); client = null; } } @Test public void testConnection(TestContext context) { Async async = context.async(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); socket.handler(buffer -> { context.assertTrue(buffer.toString().contains("CONNECTED")); context.assertTrue(buffer.toString().contains("version:1.2")); // Optional headers: context.assertTrue(buffer.toString().contains("session:")); context.assertTrue(buffer.toString().contains("server:")); async.complete(); }); socket.write("CONNECT\n" + "accept-version:1.2\n" + "\n" + FrameParser.NULL); }); } @Test public void testConnectionWithSeveralVersions(TestContext context) { Async async = context.async(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); socket.handler(buffer -> { context.assertTrue(buffer.toString().contains("CONNECTED")); context.assertTrue(buffer.toString().contains("version:1.1")); async.complete(); }); socket.write("CONNECT\n" + "accept-version:1.0,1.1\n" + "\n" + FrameParser.NULL); }); } @Test public void testConnectionWithoutVersionHeader(TestContext context) { Async async = context.async(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); socket.handler(buffer -> { context.assertTrue(buffer.toString().contains("CONNECTED")); context.assertTrue(buffer.toString().contains("version:1.0")); async.complete(); }); socket.write("CONNECT\n" + "\n" + FrameParser.NULL); }); } @Test public void testConnectionWithInvalidVersions(TestContext context) { Async async = context.async(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); socket.handler(buffer -> { context.assertTrue(buffer.toString().contains("ERROR")); context.assertTrue(buffer.toString().contains("version:1.2")); context.assertTrue(buffer.toString().contains("Supported protocol versions are 1.2")); async.complete(); }); socket.write("CONNECT\n" + "accept-version:0.0\n" + "\n" + FrameParser.NULL); }); } @Test public void testConnectionWithInvalidVersionLists(TestContext context) { Async async = context.async(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); socket.handler(buffer -> { context.assertTrue(buffer.toString().contains("ERROR")); context.assertTrue(buffer.toString().contains("version:1.2")); context.assertTrue(buffer.toString().contains("Supported protocol versions are 1.2")); async.complete(); }); socket.write("CONNECT\n" + "accept-version:0.0,3.2\n" + "\n" + FrameParser.NULL); }); } @Test public void testConnectionWithStompFrame(TestContext context) { Async async = context.async(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); socket.handler(buffer -> { context.assertTrue(buffer.toString().contains("CONNECTED")); context.assertTrue(buffer.toString().contains("version:1.2")); // Optional headers: context.assertTrue(buffer.toString().contains("session:")); context.assertTrue(buffer.toString().contains("server:")); async.complete(); }); socket.write("STOMP\n" + "accept-version:1.2\n" + "\n" + FrameParser.NULL); }); } @Test public void testAConnectionReusingSubscriptionId(TestContext context) { List<Buffer> frames = new ArrayList<>(); AtomicReference<NetSocket> reference = new AtomicReference<>(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); reference.set(socket); socket.handler(buffer -> { if (buffer.toString().contains("CONNECTED")) { // Send first subscription socket.write("SUBSCRIBE\n" + "destination:/queue\n" + "id:0\n\n" + FrameParser.NULL); } else { frames.add(buffer); } }); socket.write("CONNECT\n" + "accept-version:1.2\n" + "\n" + FrameParser.NULL); }); Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> server.stompHandler().getDestinations().size() >= 1); // Send a second subscribe using the same ID reference.get().write("SUBSCRIBE\n" + "destination:/queue2\n" + "id:0\n\n" + FrameParser.NULL); Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> frames.size() >= 1); assertThat(frames.get(0).toString()).startsWith("ERROR"); } @Test public void testInvalidUnsubscribe(TestContext context) { List<Buffer> frames = new ArrayList<>(); AtomicReference<NetSocket> reference = new AtomicReference<>(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); reference.set(socket); socket.handler(buffer -> { if (buffer.toString().contains("CONNECTED")) { socket.write("UNSUBSCRIBE\n" + "id:0\n\n" + FrameParser.NULL); } else { frames.add(buffer); } }); socket.write("CONNECT\n" + "accept-version:1.2\n" + "\n" + FrameParser.NULL); }); Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> frames.size() >= 1); assertThat(frames.get(0).toString()).startsWith("ERROR"); } @Test public void testUnsubscribeWithoutId(TestContext context) { List<Buffer> frames = new ArrayList<>(); AtomicReference<NetSocket> reference = new AtomicReference<>(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { System.err.println("Connection failed"); context.fail("Connection failed"); return; } NetSocket socket = result.result(); reference.set(socket); socket.handler(buffer -> { if (buffer.toString().contains("CONNECTED")) { socket.write("UNSUBSCRIBE\n" + "\n" + FrameParser.NULL); } else { frames.add(buffer); } }); socket.write("CONNECT\n" + "accept-version:1.2\n" + "\n" + FrameParser.NULL); }); Awaitility.await().atMost(10, TimeUnit.MINUTES).until(() -> frames.size() >= 1); assertThat(frames.get(0).toString()).startsWith("ERROR"); } @Test public void testMalformedFrame(TestContext context) { List<Buffer> frames = new ArrayList<>(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); socket.handler(frames::add); socket.write("CONNECT\n" + "accept-version:1.2\n" + "\n" + "illegal body" + FrameParser.NULL); }); Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> frames.size() >= 1); assertThat(frames.get(0).toString()).startsWith("ERROR"); } @Test public void testNumberOfHeadersExceeded(TestContext context) { server.options().setMaxHeaders(2); List<Buffer> frames = new ArrayList<>(); AtomicReference<NetSocket> reference = new AtomicReference<>(); client = vertx.createNetClient().connect(server.actualPort(), "0.0.0.0", result -> { if (result.failed()) { context.fail("Connection failed"); return; } NetSocket socket = result.result(); reference.set(socket); socket.handler(buffer -> { if (buffer.toString().contains("CONNECTED")) { socket.write("SEND\n" + "header1:value1\n" + "header2:value2\n" + "destination:foo\n" + "\n" + FrameParser.NULL); } else { frames.add(buffer); } }); socket.write("CONNECT\n" + "accept-version:1.2\n" + "\n" + FrameParser.NULL); }); Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> frames.size() >= 1); assertThat(frames.get(0).toString()).startsWith("ERROR") .containsIgnoringCase("number of headers exceeded"); } }