From 480a3779b5c5358bd8b1e8328cf4649961be965c Mon Sep 17 00:00:00 2001 From: BennyFranciscus <268274351+BennyFranciscus@users.noreply.github.com> Date: Sun, 29 Mar 2026 16:04:24 +0000 Subject: [PATCH] =?UTF-8?q?Add=20uWebSockets.js=20=E2=80=94=20native=20C++?= =?UTF-8?q?=20HTTP=20engine=20for=20Node.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit uWebSockets.js v20.61.0 by Alex Hultman / uNetworking AB. Written in C++, exposed as a native V8 addon — bypasses Node's HTTP stack entirely. Uses µSockets event loop with epoll for high-performance HTTP/WebSocket serving. Multi-core via Node cluster module (SO_REUSEPORT). Type: engine Language: JS Tests: baseline, pipelined, limited-conn Validation: 7/7 passed --- frameworks/uwebsockets/Dockerfile | 13 ++++ frameworks/uwebsockets/meta.json | 14 ++++ frameworks/uwebsockets/package.json | 7 ++ frameworks/uwebsockets/server.js | 113 ++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 frameworks/uwebsockets/Dockerfile create mode 100644 frameworks/uwebsockets/meta.json create mode 100644 frameworks/uwebsockets/package.json create mode 100644 frameworks/uwebsockets/server.js diff --git a/frameworks/uwebsockets/Dockerfile b/frameworks/uwebsockets/Dockerfile new file mode 100644 index 00000000..82328b53 --- /dev/null +++ b/frameworks/uwebsockets/Dockerfile @@ -0,0 +1,13 @@ +FROM ubuntu:24.04 +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl ca-certificates git python3 make g++ && \ + curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ + apt-get install -y nodejs && \ + rm -rf /var/lib/apt/lists/* +WORKDIR /app +COPY package.json . +RUN npm install --omit=dev +COPY server.js . +ENV NODE_ENV=production +EXPOSE 8080 +CMD ["node", "server.js"] diff --git a/frameworks/uwebsockets/meta.json b/frameworks/uwebsockets/meta.json new file mode 100644 index 00000000..cca77b58 --- /dev/null +++ b/frameworks/uwebsockets/meta.json @@ -0,0 +1,14 @@ +{ + "display_name": "uWebSockets.js", + "language": "JS", + "type": "engine", + "engine": "uWebSockets", + "description": "Native C++ HTTP/WebSocket server exposed as a Node.js addon. Uses µSockets event loop with epoll, bypassing Node's HTTP stack entirely.", + "repo": "https://github.com/uNetworking/uWebSockets.js", + "enabled": true, + "tests": [ + "baseline", + "pipelined", + "limited-conn" + ] +} diff --git a/frameworks/uwebsockets/package.json b/frameworks/uwebsockets/package.json new file mode 100644 index 00000000..056b8896 --- /dev/null +++ b/frameworks/uwebsockets/package.json @@ -0,0 +1,7 @@ +{ + "name": "httparena-uwebsockets", + "private": true, + "dependencies": { + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.61.0" + } +} diff --git a/frameworks/uwebsockets/server.js b/frameworks/uwebsockets/server.js new file mode 100644 index 00000000..6efcc678 --- /dev/null +++ b/frameworks/uwebsockets/server.js @@ -0,0 +1,113 @@ +const uWS = require('uWebSockets.js'); +const cluster = require('cluster'); +const os = require('os'); + +const SERVER_HEADER = 'uwebsockets'; + +function sumQuery(qs) { + if (!qs) return 0; + let sum = 0; + let i = 0; + while (i < qs.length) { + const eq = qs.indexOf('=', i); + if (eq === -1) break; + let amp = qs.indexOf('&', eq); + if (amp === -1) amp = qs.length; + const n = parseInt(qs.slice(eq + 1, amp), 10); + if (n === n) sum += n; + i = amp + 1; + } + return sum; +} + +function readBody(res, cb) { + let buffer; + res.onData((ab, isLast) => { + const chunk = Buffer.from(ab); + if (isLast) { + if (buffer) { + cb(Buffer.concat([buffer, chunk]).toString()); + } else { + cb(chunk.toString()); + } + } else { + if (buffer) { + buffer = Buffer.concat([buffer, chunk]); + } else { + buffer = Buffer.concat([chunk]); + } + } + }); +} + +function startServer() { + const app = uWS.App(); + + // /pipeline — lightweight endpoint for pipelining test + app.get('/pipeline', (res, req) => { + res.cork(() => { + res.writeHeader('content-type', 'text/plain'); + res.writeHeader('server', SERVER_HEADER); + res.end('ok'); + }); + }); + + // /baseline2 — GET: sum query params + app.get('/baseline2', (res, req) => { + const qs = req.getQuery(); + const body = String(sumQuery(qs)); + res.cork(() => { + res.writeHeader('content-type', 'text/plain'); + res.writeHeader('server', SERVER_HEADER); + res.end(body); + }); + }); + + // Catch-all GET — /baseline11 etc: sum query params + app.get('/*', (res, req) => { + const qs = req.getQuery(); + const body = String(sumQuery(qs)); + res.cork(() => { + res.writeHeader('content-type', 'text/plain'); + res.writeHeader('server', SERVER_HEADER); + res.end(body); + }); + }); + + // Catch-all POST — /baseline11 etc: sum query params + body + app.post('/*', (res, req) => { + const qs = req.getQuery(); + const querySum = sumQuery(qs); + + let aborted = false; + res.onAborted(() => { aborted = true; }); + + readBody(res, (bodyStr) => { + if (aborted) return; + let total = querySum; + const n = parseInt(bodyStr.trim(), 10); + if (n === n) total += n; + res.cork(() => { + res.writeHeader('content-type', 'text/plain'); + res.writeHeader('server', SERVER_HEADER); + res.end(String(total)); + }); + }); + }); + + app.listen(8080, (listenSocket) => { + if (listenSocket) { + console.log(`Worker ${process.pid} listening on port 8080`); + } else { + console.error(`Worker ${process.pid} failed to listen on port 8080`); + process.exit(1); + } + }); +} + +if (cluster.isPrimary) { + const numCPUs = os.availableParallelism ? os.availableParallelism() : os.cpus().length; + for (let i = 0; i < numCPUs; i++) cluster.fork(); +} else { + startServer(); +}