If you work with high-frequency trading, VoIP, or real-time gaming, you know that not all network packets are created equal. Sometimes, you need to tell the network, "Hey, this packet is important—move it to the front of the line." This is usually done via DSCP (Differentiated Services Code Point) tagging.
Until recently, Node.js didn’t expose a way to set these values on a TCP socket. You were stuck with the operating system's defaults.
I decided to change that.
This is the story of Pull Request #61503—how I added socket.setTypeOfService() to Node.js, the technical hurdles I faced, and the incredible support from the Node.js maintenance team.
The Technical Gap
The goal was simple: Expose the ability to set the IP Type of Service (ToS) field.
However, Node.js relies on libuv for networking. When I looked into it, I realized libuv didn't have an API for this. To make this work now (rather than waiting years for an upstream change), I had to implement the logic directly in the Node.js C++ layer (src/tcp_wrap.cc) using raw setsockopt calls.
The implementation needed to be smart:
- Attempt to set
IP_TOS(for IPv4). - If that fails or isn't applicable, fallback to
IPV6_TCLASS(for IPv6). - Handle platform differences (Linux vs. macOS vs. Windows).
The Timeline: A Month of Commits, Crashes, and Collaboration
Phase 1: The Initial Pitch & Scope Creep (Jan 24)
I opened the PR originally implementing this for both TCP (net) and UDP (dgram). The immediate feedback from @mcollina was positive ("Good work"), but he rightly requested documentation and better tests.
We hit our first roadblock quickly: Windows.
While Linux and macOS behave somewhat similarly with socket options, Windows headers are a different beast. I was seeing UV_ENOSYS errors and header conflicts.
Phase 2: The Strategic Retreat (Jan 26)
As I debugged the Windows failures, the scope of the PR started to feel too heavy. I had a discussion with @ronag (Robert Nagy), who suggested that TCP was sufficient for the initial implementation.
I decided to rollback the UDP changes. This was a crucial lesson in open source: Don't try to boil the ocean. By focusing strictly on TCP, we isolated the errors and made the PR easier to review.
Phase 3: The Libuv Dilemma
@ronag, @addaleax (Anna Henningsen), and I discussed the architecture. Since we were bypassing libuv, we had to be careful.
- The decision: We agreed to land the implementation in Node.js to unblock users immediately, but I also opened an issue/PR in the libuv repository to add this officially in the future.
- I added
#ifdefguards to handle the raw socket calls safely across platforms.
Phase 4: Naming Matters (Jan 27 - 28)
I originally named the methods setTOS and getTOS.
However, during review, @juanarbol and others pointed out that we needed to be precise. We renamed the API to the more descriptive socket.setTypeOfService() and socket.getTypeOfService().
We also had to refine the behavior on Windows, ensuring that if the OS didn't support the specific call, we failed gracefully or returned the correct error codes.
Phase 5: The "Final Boss" — CI Flakiness (Jan 29 - Jan 31)
The code was ready. The reviewers (@mcollina, @ronag, @addaleax) had all approved it. But the machines were not cooperating.
The Node.js CI pipeline is massive, and we kept hitting unrelated errors:
- "No space left on device" (Arm64 runners)
- Timeout errors
- Git failures
To make matters worse, the Commit Queue bot kept failing to land the PR. Because I am a new contributor, and @ronag had helpfully pushed a commit to my branch to fix a linting issue, the bot got confused by the "mixed authorship" and refused to squash-merge the changes automatically.
The Merge
Finally, on February 3rd, maintainer @aduh95 stepped in to manually land the PR.
The changes were merged into nodejs:main, and the feature was officially released in Node.js v25.6.0.
Key Takeaways
- Cross-Platform is Hard: Writing C++ that runs on Linux is easy. Writing C++ that runs on Linux, macOS, and Windows simultaneously requires patience and
#ifdefmacros. - Community is Everything: This PR wouldn't have landed without the guidance of the maintainers.
- @mcollina for the initial encouragement and doc requirements.
- @ronag for guiding the architecture and helping with the "Scope Cut."
- @addaleax and @juanarbol for the rigorous code reviews.
- @aduh95 for handling the manual merge when the bots failed.
- Persistence Pays Off: There were moments when the CI failed for the 5th time in a row where I felt stuck, but communicating with the team helped push it over the line.
You can now use socket.setTypeOfService() in your Node.js applications to prioritize your traffic!
I'm currently working on upstreaming these changes to libuv so that future versions of Node.js can use the official API instead of my manual implementation.
Check out the full conversation and code changes here: PR #61503
Top comments (0)