uWebSockets.js icon indicating copy to clipboard operation
uWebSockets.js copied to clipboard

Support symbols as key in UserData

Open dragosnicolae opened this issue 4 months ago • 6 comments

Hello,

Starting from the Upgrade.js example, I tried storing the values I need (taken from some headers) under a symbol on the UserData object. The issue is the symbol is not present on the UserData in the 'open' handler, seems like only string properties are copied.

Could symbol properties also be propagated? I find this useful as using symbols won't pollute the ws object.

Thanks!

dragosnicolae avatar Aug 20 '25 14:08 dragosnicolae

No properties are copied, the entire object you pass to res.upgrade(obj is passed by reference to the open handler. So it's the ideical object passed around

uNetworkingAB avatar Aug 20 '25 16:08 uNetworkingAB

Than something weird happens along the way. I created a simple example that highlights the described issue:

"use strict";

const uWS = require("uWebSockets.js");

const symbol = Symbol("symbol");
const myData = {
	data: "string-property",
	[symbol]: "symbol-property"
};

uWS.App()
	.ws("/", {
		upgrade: (res, req, context) => {
			res.upgrade(
				myData,
				req.getHeader("sec-websocket-key"),
				req.getHeader("sec-websocket-protocol"),
				req.getHeader("sec-websocket-extensions"),
				context
			);
		},
		open: (ws) => {
			console.log("ws.data: ", ws.data);
			console.log("ws.getUserData().data: ", ws.getUserData().data);
			console.log("ws[symbol]: ", ws[symbol]);
			console.log("ws.getUserData()[symbol]: ", ws.getUserData()[symbol]);
			console.log("myData === ws.getUserData(): ", myData === ws.getUserData());
			console.log("ws === ws.getUserData(): ", ws === ws.getUserData());
		}
	})
	.listen(3000, (token) => {
		console.log(token ? "Listening to port 3000" : "Failed to listen to port 3000");
	});

When a client connects, the following logs are produced:

Listening to port 3000
ws.data:  string-property
ws.getUserData().data:  string-property
ws[symbol]:  undefined
ws.getUserData()[symbol]:  undefined
myData === ws.getUserData():  false
ws === ws.getUserData():  true

As you can see:

  • string properties are accessible while symbol properties have undefined values
  • the object returned by getUserData() is the Websocket instance itself but is different from the object passed to upgrade()
  • the UserData properties are accessibile on the ws object, I'm guessing there is some prototype chaining involved

dragosnicolae avatar Aug 20 '25 20:08 dragosnicolae

Ah, okay here is the issue https://github.com/uNetworking/uWebSockets.js/blob/da323442662b64c2b38e9585e044d7624e88d691/src/AppWrapper.h#L163

uNetworkingAB avatar Aug 20 '25 22:08 uNetworkingAB

So, are you going to make the code even uglier, by iterating over userData->GetOwnPropertySymbols?

If I may, judgeing by what I can see in the C++ code, Object.assign should also be available in the header file and does the intended job in a single line.

acarstoiu avatar Oct 06 '25 14:10 acarstoiu

Relax buddy. There is no Object::Assign

uNetworkingAB avatar Oct 06 '25 16:10 uNetworkingAB

So, are you going to make the code even uglier, by iterating over userData->GetOwnPropertySymbols?

Indeed.

uNetworkingAB avatar Oct 20 '25 01:10 uNetworkingAB