Pairing a cometd session with an HTTP session

We have a web application using Jetty 8.1, dojo, and cometd that interacts between the browser and the web container using (1) the JSON / HTTP REST API for synchronous operations and (2) the cometd API for receiving multiple events from the server .

What we are not completely clear about is how to elegantly manage the authentication sessions of these two different APIs, especially because cometd will use websocket for us instead of regular HTTP when possible. The application uses form-based authentication using the standard Jetty LDAP module. Thus, in terms of HTTP, the container provides the browser with a standard jsessionid that looks like this:

Cookie: jsessionid=758E2FAD7C199D722DA8B5E243E0E27D 

Based on the Simone Bordet post here , it seems that the recommended solution is to transfer this token during the cometd handshake, which is what we are doing.

We have two fundamentally different sessions - an HTTP session and a Bayeux commit session. For reasons such as potential memory leaks and security issues, we want them to end in unison or be paired. If the user HTTP session has ended, we want the corresponding Bayeux session to also end, and vice versa. Is there a recommended way to do this?

+6
source share
2 answers

The HTTP session and the CometD sessions have different life cycles: for example, if there is a temporary connection failure, the CometD session will fail and the server will ask the client to re-acknowledge, thereby creating another CometD session (representing one user, but with a different CometD clientId ). In this case, the HttpSession will remain the same.

With this in mind, you need to maintain, at the application level, a mapping between the username, the HttpSession correspondent, and the ServerSession correspondent. Let me call this mapping HttpCometDMapper . Each time a new user is registered, you register his name (or other unique user identifier), HttpSession and the current ServerSession . You'll probably need a two-step process when you first bind the username and HttpSession , and then the same username with ServerSession .

If you are doing a CometD handshake again, you are updating mapper with the new ServerSession .

You can bind these two sessions by registering the HttpSessionListener with an HttpSession so that when you destroy it, you retrieve the current CometD ServerSession from the display device and call ServerSession.disconnect() on it.

Povterter is a little more complicated, because CometD has no concept of inactivity timeout, for example HttpSession . It should be implemented in an application with your own logic.

One part of this is registering RemoveListener on a ServerSession , for example:

 serverSession.addListener(new ServerSession.RemoveListener() { public void removed(ServerSession session, boolean timeout); { if (!timeout) { // Explicitly disconnected, invalidate the HttpSession httpCometDMapper.invalidate(session); } } }); 

This listener watches for an explicit disconnect from the client (and server - beware of reconnecting).

It’s a little harder to implement the same mechanism for implicit outages. In this case, the timeout parameter will be true, but it may occur due to a temporary network failure (as opposed to a client failure forever), and the same user may already overwrite the new ServerSession .

I think that in this case the application timeout can solve the problem: when you see that ServerSession deleted due to the timeout, you mark this user and start the application timeout. If the same user reboots, cancel the application timeout; otherwise the user has really left, the application timed out, and you also have an invalid HttpSession .

What is higher - only ideas and suggestions; the actual implementation is largely dependent on the details of the application (and therefore CometD is not provided out of the box).

The key points are mapper, HttpSessionListener and RemoveListener , and knowing the life cycles of these components. Once you do this, you can write the right code that does the right thing for your application.

Finally, note that CometD has a transport-agnostic way to interact with HttpSession using a BayeuxContext instance, which you can get from BayeuxServer.getContext() . I also suggest you also see if this can simplify the situation, especially for retrieving tokens stored in HttpSession .

+11
source

Is there any problem if we are going to create a BayeuxClient after a temporary connection failure?

You can try using this code below.

  try { log.info("Running streaming client example...."); makeConnect(); } catch (Exception e) { handleException("Error while setup the salesforce connection.", e); } } private void makeConnect() { try{ client = makeClient(); client.getChannel(Channel.META_HANDSHAKE).addListener (new ClientSessionChannel.MessageListener() { public void onMessage(ClientSessionChannel channel, Message message) { log.info("[CHANNEL:META_HANDSHAKE]: " + message); boolean success = message.isSuccessful(); if (!success) { String error = (String) message.get("error"); if (error != null) { log.error("Error during HANDSHAKE: " + error); } Exception exception = (Exception) message.get("exception"); if (exception != null) { handleException("Exception during HANDSHAKE: ", exception); } } } }); client.getChannel(Channel.META_CONNECT).addListener( new ClientSessionChannel.MessageListener() { public void onMessage(ClientSessionChannel channel, Message message) { log.info("[CHANNEL:META_CONNECT]: " + message); boolean success = message.isSuccessful(); if (!success) { client.disconnect(); makeConnect(); String error = (String) message.get("error"); if (error != null) { //log.error("Error during CONNECT: " + error); } } } }); client.getChannel(Channel.META_SUBSCRIBE).addListener( new ClientSessionChannel.MessageListener() { public void onMessage(ClientSessionChannel channel, Message message) { log.info("[CHANNEL:META_SUBSCRIBE]: " + message); boolean success = message.isSuccessful(); if (!success) { String error = (String) message.get("error"); if (error != null) { makeConnect(); log.error("Error during SUBSCRIBE: " + error); } } } }); client.handshake(); log.info("Waiting for handshake"); boolean handshaken = client.waitFor(waitTime, BayeuxClient.State.CONNECTED); if (!handshaken) { log.error("Failed to handshake: " + client); } log.info("Subscribing for channel: " + channel); client.getChannel(channel).subscribe(new MessageListener() { public void onMessage(ClientSessionChannel channel, Message message) { injectSalesforceMessage(message); } }); log.info("Waiting for streamed data from your organization ..."); }catch (Exception e) { handleException("Error while setup the salesforce connection.", e); } } 
0
source

Source: https://habr.com/ru/post/917033/


All Articles