Connection Upgrades
Understanding the robust WebSocket upgrade pipeline.
Connection Upgrades
The critical moment in any OCPP connection is the WebSocket Upgrade. This is where authentication, protocol negotiation, and session initialization happen.
ocpp-ws-io provides a robust, customizable upgrade pipeline designed to prevent common issues like "hanging" connections or silent failures.
The Pipeline
When a Charging Station connects:
- Validation: We check the URL format and extracted Identity.
- Keep-Alive: TCP Keep-Alive is enabled immediately to prevent load balancer timeouts.
- Protocol Negotiation: We match the station's requested protocols against your supported list.
- Authentication: Your
authcallback is triggered. - Timeout Guard: If your auth callback takes too long, the handshake is aborted.
Configuration
You can control the safety mechanisms via OCPPServer options.
Handshake Timeout
Use handshakeTimeoutMs to limit how long a pending connection can wait for authentication. This prevents resource exhaustion from "zombie" handshakes.
const server = new OCPPServer({
// Abort if auth takes > 5 seconds
handshakeTimeoutMs: 5000,
});Events
Monitoring upgrade failures is crucial for debugging connectivity issues.
upgradeAborted
Fired when a handshake is rejected or times out. This gives you visibility into why a station couldn't connect.
server.on("upgradeAborted", ({ identity, reason, request }) => {
console.warn(`Connection rejected for ${identity}: ${reason}`);
console.warn(`IP: ${request.socket.remoteAddress}`);
});upgradeError
Fired when a low-level socket error occurs during the upgrade (e.g., socket hang up).
server.on("upgradeError", ({ error, socket }) => {
console.error("Socket error during upgrade:", error.message);
});Example: Custom Auth with Timeout
// Define server with 2s timeout
const server = new OCPPServer({ handshakeTimeoutMs: 2000 });
server.auth(async (ctx) => {
// Simulate slow DB lookup
const station = await db.findStation(ctx.handshake.identity);
if (!station) {
ctx.reject(404, "Station not found");
} else if (station.password !== ctx.handshake.password) {
ctx.reject(401, "Invalid password");
} else {
ctx.accept({ session: { tenantId: station.tenantId } });
}
});