/* * JBoss, Home of Professional Open Source. * Copyright 2014 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * 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 io.undertow.websockets.jsr.test; import io.undertow.Handlers; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.DeploymentManager; import io.undertow.servlet.api.ServletContainer; import io.undertow.servlet.test.util.TestClassIntrospector; import io.undertow.servlet.test.util.TestResourceLoader; import io.undertow.testutils.DefaultServer; import io.undertow.testutils.HttpOneOnly; import io.undertow.util.FlexBase64; import io.undertow.websockets.jsr.WebSocketDeploymentInfo; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.xnio.FutureResult; import javax.servlet.ServletException; import javax.websocket.ClientEndpointConfig; import javax.websocket.ContainerProvider; import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; import javax.websocket.OnMessage; import javax.websocket.RemoteEndpoint; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @RunWith(DefaultServer.class) @HttpOneOnly public class TestMessagesReceivedInOrder { private static int MESSAGES = 1000; private static final List<Throwable> stacks = new CopyOnWriteArrayList<>(); @BeforeClass public static void setup() throws ServletException { final ServletContainer container = ServletContainer.Factory.newInstance(); DeploymentInfo builder = new DeploymentInfo() .setClassLoader(TestMessagesReceivedInOrder.class.getClassLoader()) .setContextPath("/") .setResourceManager(new TestResourceLoader(TestMessagesReceivedInOrder.class)) .setClassIntrospecter(TestClassIntrospector.INSTANCE) .addServletContextAttribute(WebSocketDeploymentInfo.ATTRIBUTE_NAME, new WebSocketDeploymentInfo() .setBuffers(DefaultServer.getBufferPool()) .setWorker(DefaultServer.getWorker()) .addEndpoint(EchoSocket.class) ) .setDeploymentName("servletContext.war"); DeploymentManager manager = container.addDeployment(builder); manager.deploy(); DefaultServer.setRootHandler(Handlers.path().addPrefixPath("/", manager.start())); } @Test public void testMessagesReceivedInOrder() throws Exception { stacks.clear(); EchoSocket.receivedEchos = new FutureResult<>(); final ClientEndpointConfig clientEndpointConfig = ClientEndpointConfig.Builder.create().build(); final CountDownLatch done = new CountDownLatch(1); final AtomicReference<String> error = new AtomicReference<>(); ContainerProvider.getWebSocketContainer() .connectToServer(new Endpoint() { @Override public void onOpen(final Session session, EndpointConfig endpointConfig) { try { RemoteEndpoint.Basic rem = session.getBasicRemote(); List<String> messages = new ArrayList<>(); for (int i = 0; i < MESSAGES; i++) { byte[] data = new byte[2048]; (new Random()).nextBytes(data); String crc = md5(data); rem.sendBinary(ByteBuffer.wrap(data)); messages.add(crc); } List<String> received = EchoSocket.receivedEchos.getIoFuture().get(); StringBuilder sb = new StringBuilder(); boolean fail = false; for (int i = 0; i < messages.size(); i++) { if (received.size() <= i) { fail = true; sb.append(i + ": should be " + messages.get(i) + " but is empty."); } else { if (!messages.get(i).equals(received.get(i))) { fail = true; sb.append(i + ": should be " + messages.get(i) + " but is " + received.get(i) + " (but found at " + received.indexOf(messages.get(i)) + ")."); } } } if(fail) { error.set(sb.toString()); } done.countDown(); } catch (Throwable t) { System.out.println(t); } } }, clientEndpointConfig, new URI(DefaultServer.getDefaultServerURL() + "/webSocket") ); done.await(30, TimeUnit.SECONDS); if(error.get() != null) { Assert.fail(error.get()); } } @ServerEndpoint("/webSocket") public static class EchoSocket { private final List<String> echos = new CopyOnWriteArrayList<>(); public static volatile FutureResult<List<String>> receivedEchos = new FutureResult<>(); @OnMessage public void onMessage(ByteBuffer dataBuffer, Session session) throws IOException { byte[] hd = new byte[dataBuffer.remaining()]; dataBuffer.get(hd); String hash = md5(hd); echos.add(hash); stacks.add(new RuntimeException()); if (echos.size() == MESSAGES) { receivedEchos.setResult(echos); } session.getBasicRemote().sendBinary(dataBuffer); } } private static String md5(byte[] buffer) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(buffer); byte[] digest = md.digest(); return new String(FlexBase64.encodeBytes(digest, 0, digest.length, false)); } catch (NoSuchAlgorithmException e) { // Should never happen throw new InternalError("MD5 not supported on this platform"); } } }