Websockets: wrong session scope in listener component

Inside my websocket listener methods, the value of arguments.sessionScope does not point to the same session scope as my current request. Any ideas why the session scope would be out of sync?

I’m testing this by dumping the user’s session scope on the page where I’m testing my websockets. Then from my listener component, I log the session keys along with the sessionid value. The value of arguments.sessionScope.sessionid looks like it comes from my web context because the prefix is the same as my application.name. (this.name defined in application.cfc).

The keys found in arguments.sessionScope are: [cfid,timecreated,sessionid,urltoken,lastvisit,cftoken], but are missing a few that I create in my onSessionStart() method of application.cfc. So it appears that this session was not created in my web context, or passed into my application.onSessionStart() method.

Any ideas?

How can it be the same as the “current request”? The WebSocket API is event-driven and asynchronous.

There is much more documentation out there for WebSockets in JavaScript (a-la node.js etc.), and many of the concepts apply, as I modeled this extension somewhat after the node.js implementation (socket.io).

Please refer to those to get a better understanding of the WebSocket APIs.

I understand that websockets are asynchronous and the API is great. My assumption is that the sessionScope that’s passed into all the methods in the listener component is the same one that’s created from the app’s web context and is mapped to the current user prior to connecting to the endpoint. Is that correct? All it says in the docs is:

@param sessionScope - the Session Scope associated with the incoming connection

I need the look at the session scope in my listener to determine if the user has access to my endpoints. Is the user logged in? Do they have permission to access the endpoint? In the docs, it says that the onOpen() method can check the session scope to validate the connection, and return false or an exception to reject the connection.

For example:

  1. User logs in, the session scope is updated with some authentication information, like userID, etc.
  2. User goes to a page (e.g.: a chat conversation), and the browser attempts to connect to a websocket endpoint (via javascript).
  3. The websocket’s listener.onOpen() method is called, the session scope is passed in via arguments.sessionScope. The code validates access to the endpoint. If the user doesn’t have permission, return false.

What’s happening is that the session scope I’m seeing in my listener is not the same session scope as the request that connects to an endpoint. In fact, the session doesn’t look like it was created in my app’s web context because it doesn’t have any properties that I explicitly set in my onSessionStart() method in application.cfc.

Yes, that is correct.

There is some “hacking” done in the extension to get the correct Sessions, since Lucee does not expose an API for that. The Application Name is used as a key, so be sure to not have different contexts running in the same JVM with the same Application Name.

Same problem here (checked that there are no different contexts running with the same Application name):

Session of the web Request (with some custom keys ):

{
	"cfid": "574fd54a-7371-4849-9e21-38706e8891a9",
	"timecreated": "May, 03 2017 17:22:56 +0200",
	"sessionid": "projectwww_574fd54a-7371-4849-9e21-38706e8891a9_0",
	"urltoken": "CFID=574fd54a-7371-4849-9e21-38706e8891a9&CFTOKEN=0",
	"lastvisit": "May, 03 2017 17:23:05 +0200",
	"cftoken": "0",
	"stUser": { ... }
}

Session of the websocket connection (returned in OnMessage() Listener, the custom keys are missing and timecreated is also different):

{
	"cfid": "574fd54a-7371-4849-9e21-38706e8891a9",
	"timecreated": "May, 03 2017 17:23:08 +0200",
	"sessionid": "projectwww_574fd54a-7371-4849-9e21-38706e8891a9_0",
	"urltoken": "CFID=574fd54a-7371-4849-9e21-38706e8891a9&CFTOKEN=0",
	"lastvisit": "May, 03 2017 17:34:00 +0200",
	"cftoken": "0"
}

I solved my issue by removing my localhost mapping in my server.xml file. I commented out:

<!--
  <Host name="localhost" appBase="webapps">
    <Context path="" docBase="C:\inetpub\wwwroot\" />
  </Host>
-->

I’m not sure why this worked, and it may not even be an option for some users depending on how they have their hosts setup.

2 Likes

Yes I can confirm that it is working with only one context. I’ve commented everything out except of the default one and a custom key added to the session at a http request was then available in the sessionScope returned by the websocket.

FYI: This issue has been fixed in version 2.0 of the extension, but the setup of the extension has changed because of it, so please see README.md at GitHub - isapir/lucee-websocket: Enables server WebSockets for Lucee via JSR-356 compliant servlet containers (e.g. Tomcat 8, Jetty 9.1, etc.)

Tried to install it, following the README:

  • Step 2 (install the extension from Lucee admin) resulted in: D:\lucee\tomcat\temp\servlet-filter-utils-1.1.1.jar (Das System kann die angegebene Datei nicht finden)

  • Step 3 after finding the jar in the other repo of you (little bit hidden ;-)) and dropping it to tomcat\lib, restarting lucee and try to install the extension => same result that the jar couldn’t be find

Sheesh… I guess @micstriit entered the error message in his native language.

Where do you see that message? Did you edit web.xml to include the Filter? What happens if you comment out the Filter’s entry in the web.xml? Do you still get this error message?

I don’t expect the extension to work without the Filter entry in web.xml, but I’m trying to figure out where this error message is coming from.

It is also included in the .lex file, which is merely a zip archive.

This error message comes in the Lucee Admin after clicking at install version 2.0.1 of the websocket extension (uninstalling the old version and restart Lucee before). So I wasn’t at the point to edit the web.xml.

Tried it at my local dev machine:
Win10 with Lucee 5.2.2.70-RC / Apache Tomcat/8.5.11

And also at a dev server:
Linux with Lucee 5.2.1.9 / Apache Tomcat/8.0.36

Both with the same file not found (and I also wondering a little bit why it is looking in the temp folder after it) result when trying to install.

Catched the jar from here: Release 1.1.1 · isapir/servlet-filter-utils · GitHub

Please download lucee-websocket-extension-2.0.1.lex (99,910 bytes) and servlet-filter-utils-1.1.1.jar from Release 2.0.1 · isapir/lucee-websocket · GitHub

  • Install the downloaded lucee-websocket-extension-2.0.1.lex through the Admin or by dropping it in the deploy directory.

  • Save servlet-filter-utils-1.1.1.jar to {Tomcat}/lib

  • Edit web.xml and add configuration for the Filter:

    <!-- Required for the Lucee WebSocket Extension !-->    
    <filter>
      <filter-name>HttpSessionInitializerFilter</filter-name>
      <filter-class>net.twentyonesolutions.servlet.filter.HttpSessionInitializerFilter</filter-class>
    </filter>

    <filter-mapping>
      <filter-name>HttpSessionInitializerFilter</filter-name>
      <!-- modify url-pattern to match your websocket endpoints !-->
      <url-pattern>/ws/*</url-pattern>
    </filter-mapping> 
  • Restart Tomcat

@21Solutions manual deployment worked thanks, Websocket Extension 2.0.1 is now shown as installed in Lucee Admin. No idea why the installation over the Lucee admin didn’t work.

I will try to improve it in the future, but at least we can get it to work for now.

I have been trying to install using the information on the wiki and video without success. I tried the steps above (july 2017 version 2.0.1) but also without success. The lex file worked. I copied the jar file to correct folder. I tried the filter in various places in the tomcat web.xml and still get the error.

Lucee 5.2.9.31 Error (expression)
Message No matching function [WEBSOCKETREGISTER] found
Stacktrace The Error Occurred in
C:\inetpub\wwwroot\index.cfm: line 4

2: endpoint = “/ws/echo”;
3: listener = new EchoListener();
4: WebsocketRegister(endpoint, listener);
5: </cfscript>

I’m pretty sure that function has been changed to WebsocketServer(). Try that instead.