/*
* #%L
* Wisdom-Framework
* %%
* Copyright (C) 2013 - 2014 Wisdom Framework
* %%
* 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.
* #L%
*/
package org.wisdom.framework.vertx;
import com.google.common.base.Charsets;
import io.vertx.core.Vertx;
import io.vertx.core.http.ServerWebSocket;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Test;
import org.wisdom.api.configuration.ApplicationConfiguration;
import org.wisdom.api.content.ContentEngine;
import org.wisdom.api.exceptions.ExceptionMapper;
import org.wisdom.api.http.websockets.WebSocketListener;
import org.wisdom.api.router.Router;
import org.wisdom.framework.vertx.ssl.SSLServerContext;
import javax.net.ssl.*;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
/**
* Test the server.
*/
public class VertxDispatcherTest extends VertxBaseTest {
private WisdomVertxServer server;
Vertx vertx = Vertx.vertx();
@After
public void tearDown() throws InterruptedException {
if (server != null) {
server.stop();
server = null;
}
CountDownLatch latch = new CountDownLatch(1);
if (vertx != null) {
vertx.close(v -> {
latch.countDown();
});
}
latch.await();
FileUtils.deleteQuietly(new File("target/junk/conf/conf/fake.keystore"));
}
public void prepareHttps() throws KeyManagementException, NoSuchAlgorithmException {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = (hostname, session) -> true;
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
}
@Test
public void testServerStartSequence() throws InterruptedException, IOException, NoSuchAlgorithmException, KeyManagementException {
prepareHttps();
prepareServer();
VertxHttpServerTest.waitForStart(server);
VertxBaseTest.waitForHttpsStart(server);
int http = server.httpPort();
int https = server.httpsPort();
HttpURLConnection connection = null;
int responseCode = 0;
for (int i = 0; i < 10; i++) {
URL url = new URL("http://localhost:" + http + "/test");
try {
connection = (HttpURLConnection) url.openConnection();
responseCode = connection.getResponseCode();
} catch (IOException e) {
// Probably not yet started, waiting.
// Wait a maximum of 20 seconds, should be enough on most machine.
Thread.sleep(2000);
}
}
System.out.println("1");
// Here either the server has started, or something really bad happened.
assertThat(connection).isNotNull();
assertThat(responseCode).isEqualTo(404);
System.out.println("2");
connection = null;
responseCode = 0;
for (int i = 0; i < 10; i++) {
URL url = new URL("https://localhost:" + https + "/test");
try {
connection = (HttpURLConnection) url.openConnection();
responseCode = connection.getResponseCode();
} catch (IOException e) {
// Probably not yet started, waiting.
// Wait a maximum of 20 seconds, should be enough on most machine.
Thread.sleep(2000);
}
}
System.out.println("3");
// Here either the server has started, or something really bad happened.
assertThat(connection).isNotNull();
assertThat(responseCode).isEqualTo(404);
assertThat(server.hostname()).isEqualTo("localhost");
assertThat(server.httpPort()).isGreaterThan(9000);
assertThat(server.httpsPort()).isGreaterThan(9001);
}
@Test
public void testWebSocketDispatching() throws InterruptedException {
// Prepare the configuration
ApplicationConfiguration configuration = mock(ApplicationConfiguration.class);
when(configuration.getIntegerWithDefault(eq("vertx.http.port"), anyInt())).thenReturn(-1);
when(configuration.getIntegerWithDefault(eq("vertx.https.port"), anyInt())).thenReturn(-1);
// Prepare an empty router.
Router router = mock(Router.class);
ContentEngine contentEngine = mock(ContentEngine.class);
// Configure the server.
server = new WisdomVertxServer();
server.accessor = new ServiceAccessor(
null,
configuration,
router,
contentEngine,
null,
null,
Collections.<ExceptionMapper>emptyList()
);
server.configuration = configuration;
server.vertx = vertx;
server.start();
final ServerWebSocket socket = mock(ServerWebSocket.class);
final Socket sock = new Socket(socket);
server.addSocket("/hello", sock);
MyWebSocketListener listener = new MyWebSocketListener();
server.register(listener);
// The listener should have been notified.
assertThat(listener.opened).isNotNull();
server.received("/hello", "message".getBytes(Charsets.UTF_8), sock);
// The listener should have received the message.
assertThat(listener.lastMessage).isEqualTo("message");
assertThat(listener.lastClient).isNotNull();
assertThat(listener.closed).isNull();
server.received("/hello", "message2".getBytes(Charsets.UTF_8), sock);
assertThat(listener.lastMessage).isEqualTo("message2");
assertThat(listener.lastClient).isNotNull();
assertThat(listener.closed).isNull();
server.removeSocket("/hello", sock);
assertThat(listener.closed).isNotNull();
}
@Test
public void testWebSocketWithMultiClients() throws InterruptedException, IOException {
// Prepare the configuration
prepareServer();
final ServerWebSocket socket1 = mock(ServerWebSocket.class);
final Socket sock1 = new Socket(socket1);
final ServerWebSocket socket2 = mock(ServerWebSocket.class);
final Socket sock2 = new Socket(socket2);
MyWebSocketListener listener = new MyWebSocketListener();
server.register(listener);
server.addSocket("/hello", sock1);
// The listener should have been notified.
assertThat(listener.opened).isNotNull();
server.received("/hello", "message".getBytes(Charsets.UTF_8), sock1);
// The listener should have received the message.
assertThat(listener.lastMessage).isEqualTo("message");
assertThat(listener.lastClient).isEqualTo(Integer.toOctalString(socket1.hashCode()));
server.addSocket("/hello", sock2);
server.received("/hello", "message2".getBytes(Charsets.UTF_8), sock2);
assertThat(listener.lastMessage).isEqualTo("message2");
assertThat(listener.lastClient).isEqualTo(Integer.toOctalString(socket2.hashCode()));
server.removeSocket("/hello", sock1);
server.removeSocket("/hello", sock2);
assertThat(listener.closed).isNotNull();
}
@Test
public void testWebSocketSending() throws InterruptedException, IOException {
prepareServer();
final ServerWebSocket socket1 = mock(ServerWebSocket.class);
final Socket sock = new Socket(socket1);
when(socket1.textHandlerID()).thenReturn("/hello");
MyWebSocketListener listener = new MyWebSocketListener();
server.register(listener);
server.addSocket("/hello", sock);
// The listener should have been notified.
assertThat(listener.opened).isNotNull();
server.received("/hello", "message".getBytes(Charsets.UTF_8), sock);
// The listener should have received the message.
assertThat(listener.lastMessage).isEqualTo("message");
assertThat(listener.lastClient).isEqualTo(WisdomVertxServer.id(sock));
server.send("/hello", WisdomVertxServer.id(sock), "response");
// Write on missing client.
server.send("/hello", "missing", "response");
server.publish("/hello", "yep !");
}
private void prepareServer() throws IOException, InterruptedException {
ApplicationConfiguration configuration = mock(ApplicationConfiguration.class);
when(configuration.getIntegerWithDefault(eq("vertx.http.port"), anyInt())).thenReturn(0);
when(configuration.getIntegerWithDefault(eq("vertx.https.port"), anyInt())).thenReturn(0);
when(configuration.getIntegerWithDefault("vertx.acceptBacklog", -1)).thenReturn(-1);
when(configuration.getIntegerWithDefault("vertx.receiveBufferSize", -1)).thenReturn(-1);
when(configuration.getIntegerWithDefault("vertx.sendBufferSize", -1)).thenReturn(-1);
when(configuration.getStringArray("wisdom.websocket.subprotocols")).thenReturn(new String[0]);
when(configuration.getStringArray("vertx.websocket-subprotocols")).thenReturn(new String[0]);
when(configuration.getBaseDir()).thenReturn(new File("target/junk/conf"));
// Prepare an empty router.
Router router = mock(Router.class);
ContentEngine contentEngine = mock(ContentEngine.class);
// Configure the server.
server = new WisdomVertxServer();
server.accessor = new ServiceAccessor(
null,
configuration,
router,
contentEngine,
null,
null,
Collections.<ExceptionMapper>emptyList()
);
server.configuration = configuration;
server.vertx = vertx;
server.start();
waitForStart(server);
}
private class MyWebSocketListener implements WebSocketListener {
String lastMessage;
String lastClient;
String opened;
String closed;
@Override
public void received(String uri, String client, byte[] content) {
this.lastMessage = new String(content, Charsets.UTF_8);
this.lastClient = client;
}
@Override
public void opened(String uri, String client) {
this.opened = client;
}
@Override
public void closed(String uri, String client) {
this.closed = client;
}
}
}