... blocks,
and formats the JavaScript code within them.
"""
def replacer(match):
original_code_content = match.group(1)
formatted_code_content = format_js_code(original_code_content)
# Reconstruct the block with the formatted content
return f"{formatted_code_content}
"
# Use re.sub with a callback function to find and replace the content
# The pattern targets content within `` tags that are themselves within `` tags.
# `(.*?)` uses a non-greedy match to capture the code content.
# `re.DOTALL` (re.S) allows `.` to match newlines, important if code was already broken.
formatted_html = re.sub(
r'\s*(.*?)\s*
',
replacer,
html_content,
flags=re.DOTALL
)
return formatted_html
# The original HTML content string
html_content = """
In today's hyper-connected digital landscape, real-time communication is no longer a luxury but a fundamental expectation. From collaborative editing platforms and live dashboards to instant messaging and online gaming, users demand immediate feedback and seamless interactivity. At the heart of many of these experiences lies WebSockets, a powerful protocol providing full-duplex communication channels over a single TCP connection.
While Node.js excels at handling concurrent connections due to its event-driven, non-blocking I/O model, scaling real-time WebSocket applications to support millions of simultaneous users presents unique architectural challenges. This article will deep dive into the strategies, technologies, and best practices required to build high-performance, resilient, and scalable WebSocket applications with Node.js.
The Foundation: Understanding WebSockets
Before we embark on scaling, it's crucial to understand why WebSockets are superior to traditional HTTP for real-time scenarios.
HTTP vs. WebSockets: A Fundamental Shift
Traditional HTTP is a request-response protocol; clients initiate requests, and servers respond. For real-time updates, this typically involves polling (client repeatedly asking for updates) or long polling (server holds connection open until new data is available). Both are inefficient and resource-intensive for true real-time needs.
WebSockets, on the other hand, establish a persistent, bi-directional communication channel between a client and a server. After an initial HTTP handshake, the connection is upgraded to a WebSocket, allowing both client and server to send messages at any time without initiating a new request. This significantly reduces overhead and latency.
How WebSockets Work
- Handshake: A client sends an HTTP GET request with an
Upgrade: websocket header. - Upgrade: If the server supports WebSockets, it responds with a
101 Switching Protocols status, and the connection is upgraded. - Persistent Connection: The TCP connection remains open, allowing full-duplex message exchange until either side closes it.
Node.js, with its asynchronous nature, is an ideal runtime for handling the numerous concurrent connections inherent in WebSocket-based applications.
Core Technologies for Real-time in Node.js
Node.js offers excellent libraries for WebSocket implementation.
1. The ws Library
The ws library is a popular, fast, and feature-rich WebSocket client and server implementation for Node.js. It's a low-level library, providing direct control over the WebSocket protocol.
// server.js using 'ws' library (install with: npm install ws)import { WebSocketServer } from 'ws';import http from 'http';const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('WebSocket server is running');});const wss = new WebSocketServer({ server });wss.on('connection', ws => { console.log('Client connected!'); ws.on('message', message => { console.log(`Received message from client: ${message}`); // Echo the message back to the client ws.send(`Server received: ${message}`); }); ws.on('close', () => { console.log('Client disconnected.'); }); ws.on('error', error => { console.error('WebSocket error:', error); });});server.listen(8080, () => { console.log('WebSocket server listening on port 8080');});
2. Socket.IO
Socket.IO is a widely used library that builds on top of WebSockets, providing additional features and fallbacks for environments where WebSockets are not supported (e.g., older browsers). It offers auto-reconnection, packet buffering, multiplexing (namespaces), and broadcasting capabilities, making it easier to build robust real-time applications.
// server.js using Socket.IO (install with: npm install socket.io express)import express from 'express';import { createServer } from 'http';import { Server } from 'socket.io';const app = express();const httpServer = createServer(app);const io = new Server(httpServer, { cors: { origin: '*', // Adjust for production security }});io.on('connection', socket => { console.log(`Client connected with ID: ${socket.id}`); // Listen for 'chat message' events from the client socket.on('chat message', msg => { console.log(`Message from ${socket.id}: ${msg}`); // Emit the message to all connected clients (including sender) io.emit('chat message', { user: socket.id, message: msg }); }); // Listen for 'disconnect' event socket.on('disconnect', () => { console.log(`Client disconnected with ID: ${socket.id}`); }); socket.on('error', error => { console.error('Socket.IO error:', error); });});app.get('/', (req, res) => { res.send('Socket.IO Server is Running
');});httpServer.listen(3000, () => { console.log('Socket.IO server listening on port 3000');});
The Scalability Challenge for Real-time Applications
The fundamental challenge with scaling WebSockets lies in their stateful nature. Unlike stateless HTTP requests that can be routed to any available server, a WebSocket connection maintains state between a specific client and a specific server. This
"""
# Call the formatter and print the result
formatted_html_content = format_html_code_blocks(html_content)
print(formatted_html_content)


