Skip to content

Set TCP_NODELAY on accepted TCP connections - #255

Merged
swhitty merged 1 commit into
swhitty:mainfrom
PADL:tcp-nodelay
Nov 13, 2026
Merged

swhitty merged 1 commit into
swhitty:mainfrom
PADL:tcp-nodelay

Conversation

@lhoward

@lhoward lhoward commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

HTTPServer leaves Nagle's algorithm on for the sockets it accepts. When a small write follows one the client hasn't yet acknowledged, the kernel holds it until the ACK arrives, and most clients delay that ACK by up to 40 ms. Typical cases are the next WebSocket message on a quiet connection, or a response written in more than one piece.

We noticed this on a WebSocket relaying a stream of small binary messages. About 2% of them reached the client 40 to 42 ms late, each one sent just after the client's delayed ACK. Most HTTP servers turn Nagle off on accepted connections for this reason; nginx's tcp_nodelay is on by default, for example.

This change:

  • FlyingSocks: adds a tcpNoDelay socket option (IPPROTO_TCP, TCP_NODELAY), with the two constants added to each platform's Socket extension (Darwin, Glibc, Musl, Android, WinSock2).
  • HTTPServer: sets the option on every connection it accepts on an IPv4 or IPv6 address.
    • A Unix domain socket has no Nagle delay, so it's left alone.
    • If setting the option fails, the server logs a warning and serves the connection anyway.
  • HTTPConnection: socket changes from private to internal, so a test can read the option back. Nothing else uses that.

Tests:

  • SocketTests.socket_Sets_And_Gets_TCPNoDelayOption round-trips the option on an IPv4 stream socket.
  • HTTPServerTests.server_SetsTCPNoDelay_OnAcceptedConnections checks the option on the server's accepted connection. Without the server change it fails.
  • On Linux (Swift 6.4), FlyingSocks passes all 155 tests. In FlyingFox, the only failures out of 314 tests are HTTPDecoderTests' standardizedPath and naughtyPath_IsParsed, which fail the same way on main.
  • I haven't built it on Windows, Musl or Android. On Windows the constants follow the existing IPPROTO_IPV6.rawValue pattern.

If you'd rather make it a Configuration flag than unconditional, I'm happy to change it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CEgKfBwi5LpKqwfbctCEdH

HTTPServer left Nagle's algorithm on for the sockets it accepts. A
small write that follows another still unacknowledged, such as the
next WebSocket message on a quiet connection, then waits for the
client's ACK, which most stacks delay by up to 40 ms. On a WebSocket
relaying a stream of small messages, about 2% of them arrived 40 to
42 ms late, each released by the client's delayed ACK.

FlyingSocks gains a tcpNoDelay socket option (IPPROTO_TCP,
TCP_NODELAY on every platform), and HTTPServer sets it on each
connection it accepts on an IPv4 or IPv6 address. A Unix domain socket
has no Nagle delay and is left alone; a failure to set it is logged as
a warning and the connection is served anyway. HTTPConnection's socket
is now internal so that a test can read the option back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEgKfBwi5LpKqwfbctCEdH
@codecov

codecov Bot commented Nov 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 93.75%. Comparing base (e845dc0) to head (01d567c).

Files with missing lines Patch % Lines
FlyingFox/Sources/HTTPServer.swift 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #255      +/-   ##
==========================================
- Coverage   93.76%   93.75%   -0.01%     
==========================================
  Files          72       72              
  Lines        3847     3859      +12     
==========================================
+ Hits         3607     3618      +11     
- Misses        240      241       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@swhitty

swhitty commented Nov 13, 2026

Copy link
Copy Markdown
Owner

TIL! thank you for this I will merge it and then just extract the switch into its on func on main 🙏

@swhitty
swhitty merged commit 695c2cf into swhitty:main Nov 13, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants