You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by "l.penet@senat.fr" <l....@senat.fr> on 2016/07/18 14:25:30 UTC

Websockets : differents behaviours on connection when using WS or WSS

Hi.

I am using Tomcat 8.0.36 with Atmosphere 2.4.5 ( 
https://github.com/Atmosphere/atmosphere ) to implement WebSockets with 
fallbacks such as long polling.

I am writing tests using Tomcat JSR356 implementation.

I studied your WebSocket samples and I am using a fake trustore :

         ClientEndpointConfig cec = 
ClientEndpointConfig.Builder.create().build();
         File keyStorePath = new File("path/to/test.keystore");
cec.getUserProperties().put(WsWebSocketContainer.SSL_TRUSTSTORE_PROPERTY, 
keyStorePath.getAbsolutePath());
cec.getUserProperties().put(WsWebSocketContainer.SSL_TRUSTSTORE_PWD_PROPERTY, 
"pouetpouet");

or similarly I am setting the WsWebSocketContainer.SSL_CONTEXT_PROPERTY 
to a passthrough ssl context, accepting all certificates (it's just for 
tests, it's ok).


My test looks like :

         WebSocketContainer container = 
ContainerProvider.getWebSocketContainer();
         RecordingReceivedMessagesSocket socket = new 
RecordingReceivedMessagesSocket();

         URI echoUri = new URI(getWSTestURI("/ws/delaislimites"));

         ClientEndpointConfig cec = 
ClientEndpointConfig.Builder.create().build();
         File keyStorePath = new File("path/to/test.keystore");
cec.getUserProperties().put(WsWebSocketContainer.SSL_TRUSTSTORE_PROPERTY, 
keyStorePath.getAbsolutePath());
cec.getUserProperties().put(WsWebSocketContainer.SSL_TRUSTSTORE_PWD_PROPERTY, 
"pouetpouet");

         Session wsSession = container.connectToServer(socket, cec, 
echoUri);

         socket.close();
         log.info("Attente de la fermeture de la socket");
         Assert.assertTrue(socket.awaitClose(10,TimeUnit.SECONDS));
         log.info("La socket doit avoir re�u OK");
Assert.assertTrue(socket.getReceivedMessages().contains("OK"));
         Assert.assertTrue(true);

RecordingReceivedMessagesSocket class looks like :


@ClientEndpoint
@Log4j2
@Getter
public class RecordingReceivedMessagesSocket extends BaseTestSocket
{
     List<String> receivedMessages = new ArrayList<>();

     @OnMessage
     public String onMessage(String msg, Session session)
     {
         log.info("Message re�u : {}", msg);
         this.receivedMessages.add(msg);
         return msg;
     }
}

@ClientEndpoint
@Log4j2
@Getter
public class BaseTestSocket extends Endpoint
{
     private Session session;

     private final CountDownLatch closeLatch;

     public BaseTestSocket()
     {
         this.closeLatch = new CountDownLatch(1);
     }

     @OnOpen
     @Override
     public void onOpen(Session session, EndpointConfig config)
     {
         log.info("Connexion �tablie.");
         this.session = session;
     }

     public boolean awaitClose(int duration, TimeUnit unit) throws 
InterruptedException
     {
         return this.closeLatch.await(duration,unit);
     }

     @OnClose
     @Override
     public void onClose(Session session, CloseReason reason)
     {
         log.info("Connexion ferm�e.");
         this.closeLatch.countDown();
     }

     public void write(String message) throws IOException {
         this.session.getBasicRemote().sendText(message);
     }

     public void writeObject(Object object) throws IOException, 
EncodeException {
         this.session.getBasicRemote().sendObject(object);
     }

     public void close() throws IOException {
         this.session.close();
     }
}


When I implement getWSTestURI() so that it returns a url for a non 
encrypted request, such as ws://localhost/8084/ws/delaislimites :

* RecordingReceivedMessagesSocket.onMessage gets called with the 
immediate server reply ("OK").


The ServerEndPoint looks like :


@Log4j2
@ManagedService(path = "/ws/delaislimites")
public class WSDelaisLimites {
     @Inject
     private BroadcasterFactory factory;

     @Inject
     private AtmosphereResourceFactory resourceFactory;

     @Inject
     private MetaBroadcaster metaBroadcaster;

     @Ready(encoders = {JacksonEncoder.class})
     @DeliverTo(DeliverTo.DELIVER_TO.ALL)
     public String onReady(final AtmosphereResource r) {
         log.info("Browser {} connected.", r.uuid());
         return "OK";
     }

     /**
      * Invoked when the client disconnect or when an unexpected closing 
of the underlying connection happens.
      *
      * @param event
      */
     @Disconnect
     public void onDisconnect(AtmosphereResourceEvent event) {
         if (event.isCancelled()) {
             log.info("Browser {} unexpectedly disconnected", 
event.getResource().uuid());
         } else if (event.isClosedByClient()) {
             log.info("Browser {} closed the connection", 
event.getResource().uuid());
         }
     }

/* ... */
     @Message
     public String onMessage(String message) throws IOException {
         StringBuilder sb = new StringBuilder();
         sb.append("Re�u le message texte ");
         sb.append(message);
         log.info(sb.toString());
         return message;
     }
}

So, I am getting "OK".


When I connect through SSL, with a url like 
wss://localhost:8765/ws/delaislimites :

* RecordingReceivedMessagesSocket.onMessage never gets called with the 
"OK" reply.


I tried to add the following block :

         CountDownLatch latch = new CountDownLatch(1);
         BasicText handler = new BasicText(latch);
         wsSession.addMessageHandler(handler);
         socket.write("Pouet");
         Assert.assertTrue(handler.getLatch().await(10, TimeUnit.SECONDS));

between

         Session wsSession = container.connectToServer(socket, cec, 
echoUri);

and

         socket.close();

to check that some other stuff works (like in your tests).

As expected, it does, and the BasicText handler gets a reply from the 
server.

However, it does not get the initial reply too (and it seems quite 
normal to me as it is added too late).

If I only add a

         socket.write("Pouet");

RecordingReceivedMessagesSocket.onMessage is also called with server 
messages.

So, is this difference in behaviour to be expected ? Is there some point 
in spec that I missed that commands it ?

Thanks in advance for your explanations.

Ludovic


|
| AVANT D'IMPRIMER, PENSEZ A L'ENVIRONNEMENT.
|


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Websockets : differents behaviours on connection when using WS or WSS

Posted by Mark Thomas <ma...@apache.org>.
On 18/07/2016 16:25, l.penet@senat.fr wrote:
> Hi.
> 
> I am using Tomcat 8.0.36 with Atmosphere 2.4.5 (
> https://github.com/Atmosphere/atmosphere ) to implement WebSockets with
> fallbacks such as long polling.

Can you reproduce this without Atmosphere? If not, that suggests an
Atmosphere bug. If yes, then it is likely there is a Tomcat bug.

It could also be a bug in you test case.

The simpler the test case the easier it will be to identify the root cause.

<snip/>

> So, is this difference in behaviour to be expected ?

No.

> Is there some point
> in spec that I missed that commands it ?

No.

Mark


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Websockets : differents behaviours on connection when using WS or WSS

Posted by "l.penet@senat.fr" <l....@senat.fr>.
On 18/07/2016 16:25, l.penet@senat.fr wrote:
> Hi.
>
> I am using Tomcat 8.0.36 with Atmosphere 2.4.5 ( 
> https://github.com/Atmosphere/atmosphere ) to implement WebSockets 
> with fallbacks such as long polling.
>
> I am writing tests using Tomcat JSR356 implementation.
>
> I studied your WebSocket samples and I am using a fake trustore :
>
>         ClientEndpointConfig cec = 
> ClientEndpointConfig.Builder.create().build();
>         File keyStorePath = new File("path/to/test.keystore");
> cec.getUserProperties().put(WsWebSocketContainer.SSL_TRUSTSTORE_PROPERTY, 
> keyStorePath.getAbsolutePath());
> cec.getUserProperties().put(WsWebSocketContainer.SSL_TRUSTSTORE_PWD_PROPERTY, 
> "pouetpouet");
>
> or similarly I am setting the 
> WsWebSocketContainer.SSL_CONTEXT_PROPERTY to a passthrough ssl 
> context, accepting all certificates (it's just for tests, it's ok).
>
>
> My test looks like :
>
>         WebSocketContainer container = 
> ContainerProvider.getWebSocketContainer();
>         RecordingReceivedMessagesSocket socket = new 
> RecordingReceivedMessagesSocket();
>
>         URI echoUri = new URI(getWSTestURI("/ws/delaislimites"));
>
>         ClientEndpointConfig cec = 
> ClientEndpointConfig.Builder.create().build();
>         File keyStorePath = new File("path/to/test.keystore");
> cec.getUserProperties().put(WsWebSocketContainer.SSL_TRUSTSTORE_PROPERTY, 
> keyStorePath.getAbsolutePath());
> cec.getUserProperties().put(WsWebSocketContainer.SSL_TRUSTSTORE_PWD_PROPERTY, 
> "pouetpouet");
>
>         Session wsSession = container.connectToServer(socket, cec, 
> echoUri);
I realised that my message was incomplete and, by the way, found the 
answer myself.

When I do not go SSL, I do not call the same version of connectToServer. 
I am doing

     Session wsSession = container.connectToServer(socket, echoUri);

This version os connectToServer gets an Object rather than a endpoint as 
first parameter and, among other things, looks for the ClientEndpoint 
annotation.

If it finds it, it wraps it in a PojoEndpointClient (pojo is the first 
parameter of connectToServer) :

          Endpoint ep = new PojoEndpointClient(pojo, 
Arrays.asList(annotation.decoders()));

So, if I do

     container.connectToServer(new PojoEndpointClient(socket, new 
ArrayList<>()), cec, echoUri);

everything is fine, because of the inits that PojoEndpointClient does.

Sorry for the noise.

Ludovic



>
>         socket.close();
>         log.info("Attente de la fermeture de la socket");
>         Assert.assertTrue(socket.awaitClose(10,TimeUnit.SECONDS));
>         log.info("La socket doit avoir re�u OK");
> Assert.assertTrue(socket.getReceivedMessages().contains("OK"));
>         Assert.assertTrue(true);
>
> RecordingReceivedMessagesSocket class looks like :
>
>
> @ClientEndpoint
> @Log4j2
> @Getter
> public class RecordingReceivedMessagesSocket extends BaseTestSocket
> {
>     List<String> receivedMessages = new ArrayList<>();
>
>     @OnMessage
>     public String onMessage(String msg, Session session)
>     {
>         log.info("Message re�u : {}", msg);
>         this.receivedMessages.add(msg);
>         return msg;
>     }
> }
>
> @ClientEndpoint
> @Log4j2
> @Getter
> public class BaseTestSocket extends Endpoint
> {
>     private Session session;
>
>     private final CountDownLatch closeLatch;
>
>     public BaseTestSocket()
>     {
>         this.closeLatch = new CountDownLatch(1);
>     }
>
>     @OnOpen
>     @Override
>     public void onOpen(Session session, EndpointConfig config)
>     {
>         log.info("Connexion �tablie.");
>         this.session = session;
>     }
>
>     public boolean awaitClose(int duration, TimeUnit unit) throws 
> InterruptedException
>     {
>         return this.closeLatch.await(duration,unit);
>     }
>
>     @OnClose
>     @Override
>     public void onClose(Session session, CloseReason reason)
>     {
>         log.info("Connexion ferm�e.");
>         this.closeLatch.countDown();
>     }
>
>     public void write(String message) throws IOException {
>         this.session.getBasicRemote().sendText(message);
>     }
>
>     public void writeObject(Object object) throws IOException, 
> EncodeException {
>         this.session.getBasicRemote().sendObject(object);
>     }
>
>     public void close() throws IOException {
>         this.session.close();
>     }
> }
>
>
> When I implement getWSTestURI() so that it returns a url for a non 
> encrypted request, such as ws://localhost/8084/ws/delaislimites :
>
> * RecordingReceivedMessagesSocket.onMessage gets called with the 
> immediate server reply ("OK").
>
>
> The ServerEndPoint looks like :
>
>
> @Log4j2
> @ManagedService(path = "/ws/delaislimites")
> public class WSDelaisLimites {
>     @Inject
>     private BroadcasterFactory factory;
>
>     @Inject
>     private AtmosphereResourceFactory resourceFactory;
>
>     @Inject
>     private MetaBroadcaster metaBroadcaster;
>
>     @Ready(encoders = {JacksonEncoder.class})
>     @DeliverTo(DeliverTo.DELIVER_TO.ALL)
>     public String onReady(final AtmosphereResource r) {
>         log.info("Browser {} connected.", r.uuid());
>         return "OK";
>     }
>
>     /**
>      * Invoked when the client disconnect or when an unexpected 
> closing of the underlying connection happens.
>      *
>      * @param event
>      */
>     @Disconnect
>     public void onDisconnect(AtmosphereResourceEvent event) {
>         if (event.isCancelled()) {
>             log.info("Browser {} unexpectedly disconnected", 
> event.getResource().uuid());
>         } else if (event.isClosedByClient()) {
>             log.info("Browser {} closed the connection", 
> event.getResource().uuid());
>         }
>     }
>
> /* ... */
>     @Message
>     public String onMessage(String message) throws IOException {
>         StringBuilder sb = new StringBuilder();
>         sb.append("Re�u le message texte ");
>         sb.append(message);
>         log.info(sb.toString());
>         return message;
>     }
> }
>
> So, I am getting "OK".
>
>
> When I connect through SSL, with a url like 
> wss://localhost:8765/ws/delaislimites :
>
> * RecordingReceivedMessagesSocket.onMessage never gets called with the 
> "OK" reply.
>
>
> I tried to add the following block :
>
>         CountDownLatch latch = new CountDownLatch(1);
>         BasicText handler = new BasicText(latch);
>         wsSession.addMessageHandler(handler);
>         socket.write("Pouet");
>         Assert.assertTrue(handler.getLatch().await(10, 
> TimeUnit.SECONDS));
>
> between
>
>         Session wsSession = container.connectToServer(socket, cec, 
> echoUri);
>
> and
>
>         socket.close();
>
> to check that some other stuff works (like in your tests).
>
> As expected, it does, and the BasicText handler gets a reply from the 
> server.
>
> However, it does not get the initial reply too (and it seems quite 
> normal to me as it is added too late).
>
> If I only add a
>
>         socket.write("Pouet");
>
> RecordingReceivedMessagesSocket.onMessage is also called with server 
> messages.
>
> So, is this difference in behaviour to be expected ? Is there some 
> point in spec that I missed that commands it ?
>
> Thanks in advance for your explanations.
>
> Ludovic
>


|
| AVANT D'IMPRIMER, PENSEZ A L'ENVIRONNEMENT.
|


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org