Skip to content

Releases: uNetworking/uWebSockets

v20.76.0 (A decade of uWS)

21 Mar 16:53

Choose a tag to compare

Ten years ago I made the initial commit for uWebSockets. For this release I've attached a retrospective you might find interesting. Either way, it serves as documentation.

How did we end up here?

2014–2015 | The Performance Gap

As a GPGPU programmer, benchmarking the new and widely hyped Node.js reveals a massive disparity between marketing and reality, especially considering its flagship libraries. With Express and Socket.IO running orders of magnitude slower than the equivalent C libraries, the ecosystem is found to be architecturally flawed and over-hyped. Still, the hype of Node.js and its ability to create native C++ addons invites an interest in building something better.

2016 | The Prototype (node-lws)

The first prototype, node-lws, is built to wrap libwebsockets because that was the best overall performing C library of the time. However, further benchmarking reveals a shocking truth: the library’s parser is processing data byte-by-byte in a switch statement—the least efficient implementation possible. Realizing that even the "low-level" industry standards are 10x slower than the hardware permits, it becomes clear that we need a solution from scratch.

2016–2018 | The "Drop-in" Strategy (v0.1–v0.14)

To maximize adoption, uWS is designed as a seamless, drop-in replacement for the popular ws module. Since the ws interface builds on the EventEmitter of the Node.js HTTP server, so must uWS. In order to achieve a seamless bridge between net.Socket and uWS sockets, hacky "black magic" is necessary. This necessitates a "house of cards" architecture: using Unix hacks, opaque memory interpretations, and pure prayer in the OpenSSL state. The module is a viral hit, gaining hundreds of millions of downloads.

2018 | The Architectural Pivot

Seamless compatibility becomes a double-edged sword. While it unlocks immediate traction, it ironically allows uWS to be plugged directly into the modules that were originally found to be the very bloat that warranted uWS itself, effectively neutralizing the performance endeavor. To fix this, and simultaneously remove the hacks and dependency on the Node.js HTTP server, uWS introduces its own HTTP server, URL router, pub/sub feature and breaks the compatibility by introducing its own App-based interface.

2020 | Weathering the Storm

The break in compatibility triggers a storm of online dissatisfaction. Speculative rumors that uWS is deprecated spread across Reddit and various forks pop up. However, as the industry gradually adopts the new architecture, it becomes widely accepted as the superior variant and the forks all die off. The new standalone uWS architecture is modular and testable, offers more high-level features, and receives the highest OSS-Fuzz reward from Google for its "Ideal fuzzing integration." It also brings the fastest HTTP server support available for Node.js.

2022 | The Bun Foundation

Bun adopts uSockets and uWebSockets as its networking, eventing, and crypto foundation (since v0.0.76). The performance standards pioneered by uWS for Node.js become the literal baseline for the next generation of JavaScript runtimes, mainstreaming the architectural solutions uWS was the first to identify. While we have no relation with the Bun team and don't claim any of its success, Bun is clearly a spiritual successor to what uWS started. Although with a wider goal.

2025–Present | The DeFi Transition

Having powered some of the world’s largest centralized exchanges for nearly a decade, uWS remains a preferred choice as the industry moves toward Decentralized Finance (DeFi). Today, the architecture continues to be selected as the underlying server for several of the world’s largest DEXs, proving that its core principles are as relevant for modern on-chain trading as they were for the traditional web.

Security retrospect

CVEs

We've had 2 CVEs since inception.

Year CVE ID Summary Context
2016 CVE-2016-10544 Max Payload DOS Affected early versions with compression. Triggered by JS string limits.
2020 CVE-2020-36406 Pub/Sub Logic Caught by our own fuzzing. Only applicable to specific MQTT wildcard usage.

Project Integrity & Maintenance

uWS maintains a decade-long record of zero supply chain incidents, built on a foundation of strict maintainer-only control with no third-party write access. All critical services are hardened via FIDO2 hardware keys, and every release is strictly traceable to its specific Git commit. Development occurs exclusively on encrypted workstations, ensuring that the code remains as secure as the high-stakes financial systems it powers.

What's changed in this release

  • Added UWS_REMOTE_ADDRESS_USERSPACE: Use this compiler macro to cache remote addresses in userspace. It avoids kernel overhead for getRemoteAddress() calls, nearly doubling performance for IP-heavy apps at the cost of 16 bytes per socket.
  • New onDataV2 Setter: We’ve introduced a superior data handler that replaces the bool isLast flag with uint64_t maxRemainingBodyLength.
    • Equivalence: isLast is now (maxRemainingBodyLength == 0).
    • Pre-allocation: This provides a unified heuristic for both Chunked (returns UINT64_MAX or 0) and Content-Length (returns exact bytes) streaming. If maxRemainingBodyLength is non-zero and below your memory threshold, you can safely pre-allocate.
  • Compatibility: The original onData remains available for now but is implemented as a wrapper around onDataV2.
// Example of the new V2 logic:
res->onDataV2([](std::string_view chunk, uint64_t maxRemainingBodyLength) {
    if (maxRemainingBodyLength == 0) {
        /* Done! */
    } else if (maxRemainingBodyLength < 1024 * 64) {
        /* Optimal: we know exactly how much to reserve() */
    }
});

Closing thoughts

As the timeline alludes, uWebSockets now provides an interface with seven years of near-perfect backward compatibility. An app written for v15.0.0 remains functionally identical on v20.x.x. This stability is intentional. Our top priority is to provide a rock-solid foundation for the most demanding services in the world, ensuring that performance never comes at the cost of reliability.

Commercial Support & Consulting: Inquiries are welcome via email.

v20.75.0

20 Feb 00:56
737379d

Choose a tag to compare

  • Adds UWS_USE_SIMDUTF for using simdutf instead of default Utf8 validation
  • Show method and URL in terminating error message
  • Fixes some compilation error for modern C++23 compilers
  • Adds secure reloading gzip file server example

v20.74.0

21 Feb 15:47

Choose a tag to compare

Minor libdeflate correction

  • libdeflate is now compatible with uWS::DEDICATED_DECOMPRESSOR and will be used if (and only if) the WebSocket's negotiation downgraded to equivalent of uWS::SHARED_DECOMPRESSOR (client_no_context_takeover). This will boost performance when possible. Of course, uWS::SHARED_DECOMPRESSOR as decompressor will always use libdeflate.
  • In other cases, zlib will be used regardless.

v20.73.0

20 Feb 13:29
556215c

Choose a tag to compare

libdeflate as inflation ("decompression") fast path

Resurrecting support of libdeflate, now specifically only for inflation and only as a fast path to zlib.

WITH_LIBDEFLATE=1 make

builds examples using this fast path (you need to build the submodule first). Improved load_test.c benchmark can now highlight inflation cost:

For me and my particular test system:

  • With dynamically linked zlib - 110k msg/sec inflated and echoed 300 byte JSON messages
  • With statically linked zlib-cloudflare - 130k
  • With libdeflate as fast path - 180k
  • Without any inflation - 320k

Note: WITH_DEFLATE is only compatible with uWS::SHARED_DECOMPRESSOR!

Inflation performance for 300 byte JSON (msg_sec echoed) (1)

v20.72.0

28 Jan 14:40

Choose a tag to compare

Prepared Messages for Efficient Sending and Publishing

A feature from v0.14, previously removed due to lack of interest, has now been revived! Check out the demo in examples/Precompress.cpp to see it in action.

A PreparedMessage is a message that is compressed once but can be sent multiple times. This requires the SHARED_COMPRESSOR option but works with any decompressor option. The core idea is to decouple the compression process from the act of sending the message, reducing the per-socket computational overhead of compression to near zero.

Ideal Use Case

This feature is ideal for scenarios where you need to send snapshots of a data stream to multiple recipients. Here’s how it could work:

  • A timer periodically creates a new PreparedMessage containing the latest snapshot of the data (e.g., every 500ms).
  • When a WebSocket connects, it receives the most recent prepared snapshot, followed by incremental updates (deltas) from that point onward.
  • If the snapshot is large, pre-compressing it as a PreparedMessage eliminates the need for repeated compression, significantly reducing overhead for each socket.

Important Notes

  • This feature only works with SHARED_COMPRESSOR. Using a sliding window compression method is incompatible with this optimization.
  • New methods have been added to support this feature:
    • WebSocket::sendPrepared
    • App::publishPrepared
    • Loop::prepareMessage
  • You can create PreparedMessage objects on any thread, as long as that thread has its own Loop (which is automatically created for you via Loop::get()). However, in such cases, you must handle synchronization manually. Refer to examples/Precompress.cpp for guidance.

This release introduces the interface and its initial implementation. While memory-saving optimizations are not included in this release, they may be addressed in future updates.

v20.71.0

04 Dec 02:59

Choose a tag to compare

  • Examples are built in parallel
  • Supports building with all _LIBCPP_HARDENING_MODE
  • (Experimental) cached HTTP support is reverted for now, restoring fuzz builds
  • h1spec and test subject is added to CI
  • Disallow duplicate Host headers as per RFC9112

v20.70.0

26 Oct 21:18

Choose a tag to compare

More HTTP testing & corrections

This release adds more HTTP tests and makes some minor corrections for passing those tests:

Screenshot 2024-10-26 230016

v20.69.0

25 Oct 21:14

Choose a tag to compare

HTTP spec. compliance testing

A basic HTTP RFC9112 spec. compliance test has been added and minor changes have been made to pass all tests immediately:

Screenshot 2024-10-25 230748

This basic test catches no issues in Node.js, but it catches 2 issues in Deno (reported). The test can be found here:

https://github.com/uNetworkingAB/h1spec

In contrast, running the test on completely broken servers like mrhttp you get an obvious indication of so:

Screenshot 2024-10-25 231156

v20.68.0

21 Oct 10:47

Choose a tag to compare

Improved (restored) backwards compatibility

For some users, recent introduction of CachedApp broke seamless backwards compatibility. If you rely on uWS::App being uWS::TemplatedApp<false> and not something else (like uWS::CachedApp<false> 😉), this release fixes your build:

    typedef uWS::TemplatedApp<false> App;
    typedef uWS::TemplatedApp<true> SSLApp;

v20.67.0

24 Sep 06:18

Choose a tag to compare

  • Adds removeChildApp