4. Session Handling
1. What is socket.handshake
in Socket.IO?
-
It’s an object containing info about the initial HTTP/WebSocket connection request.
-
Represents the handshake phase where the client and server upgrade from HTTP to WebSocket.
-
Includes details like:
headers
— HTTP headers sent by the clientquery
— URL query parameters from client connection URLauth
— authentication data sent during connection (like tokens)time
— timestamp of the handshakeaddress
— client’s IP address
A WebSocket connection starts with an HTTP GET
request like this:
GET /ws HTTP/1.1Host: example.comUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Sec-WebSocket-Version: 13Authorization: Bearer <token> <-- custom header (optional)Cookie: sessionId=abc123 <-- or cookie (optional)
The server can inspect headers and either accept or reject the connection.
1.1 Why is it called “handshake”?
Socket.IO uses a HTTP polling handshake first, then upgrades to WebSocket (or directly WebSocket if possible).
This is a higher-level handshake on top of the underlying TCP connection, which establishes the WebSocket session and exchanges metadata before real-time communication starts.
TCP handshake vs Socket.IO handshake:
Aspect | TCP Handshake | socket.handshake in Socket.IO |
---|---|---|
Level | Transport Layer (TCP protocol) | Application Layer (Socket.IO/WebSocket) |
What it does | Establishes a TCP connection (SYN, SYN-ACK, ACK) | Contains info about the HTTP/WebSocket upgrade request |
When it happens | Before any data transfer | During the initial Socket.IO connection setup |
Purpose | Low-level connection establishment | Exchanging metadata & authenticating clients |
1.2 Summary
socket.handshake
is metadata about the client’s connection request.- It’s not the TCP handshake, but a logical handshake at the Socket.IO layer.
- Useful for extracting headers, auth info, cookies, etc., at the time the socket connects.
2. Session Handling
2.1 Session handling in WebSockets is tricky
- Express sessions rely on HTTP cookies.
- WebSocket connections start with an HTTP handshake, then upgrade.
- After upgrade, no HTTP headers are sent, so no new cookies.
- You must extract the session cookie during the handshake and load the session manually.
2.2 Share Express sessions with Socket.IO
To share Express sessions with WebSockets:
- Set up
express-session
. - Use the same session middleware with
Socket.IO
’s handshake. - Access session data on socket connections.
1. Setup Express and express-session
const express = require('express');const session = require('express-session');
const app = express();
const sessionMiddleware = session({ secret: 'your-secret-key', resave: false, saveUninitialized: true, cookie: { secure: false } // set true if using HTTPS});
app.use(sessionMiddleware);
2. Setup Socket.IO and share session middleware
const http = require('http');const { Server } = require('socket.io');
const server = http.createServer(app);const io = new Server(server);
// Share session middleware with Socket.IOio.use((socket, next) => { sessionMiddleware(socket.request, {}, next); // Here you are attaching the session to the socket.request during the WebSocket handshake. // This makes the session data available at socket.request.session. // Sessions are only available through socket.request.session once — during the initial connection (WebSocket handshake). // // After the handshake, regular WebSocket messages (e.g., socket.on(...)) don’t go through middleware again, // but socket.request.session still holds the session data, including any mutations you make to it. // // This means: // You can read and modify the session during the connection lifecycle. // But session persistence (e.g. saving updated values) needs a manual call to session.save().});
3. Access session data inside socket connection
io.on('connection', (socket) => { const session = socket.request.session; console.log('Session ID:', session.id);
// You can read/write session properties here if (!session.views) { session.views = 0; } session.views++; session.save(); // Refer to the notes below
socket.emit('sessionData', { views: session.views });});
Why in non-websockets context, session.save()
is not required?
In non-WebSocket (i.e. normal HTTP) contexts, like handling a request in Express:
app.get('/some-route', (req, res) => { req.session.counter = (req.session.counter || 0) + 1; res.send(`Counter: ${req.session.counter}`);});
You usually don’t need to call req.session.save()
manually, because:
✅ Why session.save()
is not needed in HTTP
- Automatic save at the end of the request
express-session
automatically detects if the session was modified, and saves it before the response ends.- It hooks into
res.end()
orres.send()
internally.
- Middleware lifecycle handles persistence
- When the HTTP request completes, Express finalizes the response, and
express-session
saves the session data to the store (memory, Redis, etc.). - This happens behind the scenes — no manual save needed.
❌ Why this doesn’t work in WebSockets:
- WebSocket events (like
socket.on('event', ...)
) are not part of the Express request-response lifecycle. - There’s no
res.end()
for WebSocket events — soexpress-session
has no signal to save the session automatically. - Therefore, you must call
socket.request.session.save()
manually if you modify the session inside a WebSocket event.
🧠 Summary
Context | session.save() needed? | Why? |
---|---|---|
HTTP | ❌ Not needed | Auto-saved at end of request |
WebSocket | ✅ Required (if modified) | No auto-save trigger — must call manually |
4. Example route that initializes session data
app.get('/', (req, res) => { if (!req.session.username) { req.session.username = 'Nadith'; } res.sendFile(__dirname + '/index.html');});
5. [OPTIONAL] Express + Socket.IO with Redis session store
const RedisStore = require('connect-redis')(session);const redisClient = require('redis').createClient();
const sessionMiddleware = session({ store: new RedisStore({ client: redisClient }), secret: 'your-secret-key', resave: false, saveUninitialized: false});
Summary
Step | Description |
---|---|
Use express-session | Setup session middleware in Express |
Share session with Socket.IO | Use io.use() to run session middleware on socket requests |
Access session data | Read/write via socket.request.session |
Save changes | Call session.save() if you modify session |
3. Cookies and Web Sockets
WebSocket-based connections, the HTTP cookies (including connect.sid
) are only sent during the initial handshake, not after.
So how does the server identify the session in subsequent WebSocket messages?
✅ How It Works in Practice (Socket.IO + Express Sessions)
🔁 1. During Handshake:
-
The browser sends cookies:
connect.sid=abc123
-
Socket.IO middleware runs:
sessionMiddleware(socket.request, {}, next); -
This sets
socket.request.session
with the session data -
You can now check
socket.request.session.userId
or anything you stored
💾 2. Persist User Info in Socket
Once authenticated, store relevant session data on the socket:
io.on('connection', (socket) => { const session = socket.request.session; socket.userId = session.userId;
socket.on('message', (msg) => { console.log(`[${socket.userId}] sent: ${msg}`); });});
Now you don’t need to read cookies anymore — you’re using socket.userId
.
3. 🔄 What About Reconnects?
When the client disconnects and reconnects:
- A new handshake is made
- Cookies are sent again
- The session middleware runs again
session.userId
is re-attached
So the session still works seamlessly.
4. 🛡️ Summary
Step | Mechanism |
---|---|
Initial handshake | Cookies are sent (e.g. connect.sid ) |
Session loaded | Via express-session middleware |
User info stored | Attached to socket object |
Persistent state | Session lives in memory for connection |
Reconnects | Cookies sent again — session restored |
4. Socket.IO Middleware
Socket.IO has its own middleware system**, separate from Express.
4.1 🔧 WebSocket (Socket.IO) Middleware
Socket.IO supports connection-level middleware:
io.use((socket, next) => { // This runs during the handshake (one time per connection) const token = socket.handshake.auth.token; if (!token) return next(new Error('Unauthorized')); // Attach user info, etc. next();});
And you can do application logic in event handlers like:
io.on('connection', (socket) => { socket.on('chat', (msg) => { // Handle real-time message });});
4.2 🔁 Express Lifecycle vs. Socket.IO Lifecycle
Feature | HTTP (Express) | WebSocket (Socket.IO) |
---|---|---|
Session auto-save | ✅ Yes | ❌ No (session.save() required) |
Uses req , res , next() | ✅ Yes | ❌ Only during handshake via socket.request |
Middleware runs per request | ✅ Yes | ❌ Runs only once per connection |
Custom event-based logic | ❌ Not native | ✅ Yes (socket.on(...) ) |
✅ Conclusion
-
WebSocket events are not in the Express lifecycle, but Socket.IO provides its own middleware, specifically for:
- Authentication (during connection)
- Custom logic (inside
socket.on()
)
If you need per-event middleware, you’d have to write your own wrappers or handlers — Socket.IO doesn’t have built-in per-event middleware like Express has per-route.