Skip to content

RServer: close a connection that has more than the reply buffered - #381

Open
go-while wants to merge 2 commits into
mainfrom
fix/upstream-stale-reply
Open

go-while wants to merge 2 commits into
mainfrom
fix/upstream-stale-reply

Conversation

@go-while

Copy link
Copy Markdown
Owner

A news server that sends more than its reply put NewsCache's connection to it out of step. Examples are a block after a status that announces none (215 and a list in reply to GROUP, 430 and text) or the rest of an overlong line. The next command read those lines as its reply, and every later reply was shifted, so the client got 430 for an article the server has, or 403.

Fix (NServer-0.9.1/NServer.cc, RServer::issue, new RServer::stale_reply): NewsCache sends one command at a time and reads each reply to its end, so the stream buffer is empty before the next command. issue() now checks rdbuf()->in_avail() first. That counts bytes already read from the socket, from TLS or after inflating, so a compressed connection's trailing flush bytes don't count, and the check doesn't depend on timing. When anything is left, NewsCache closes the connection and connects again, and connect() selects the current group again. Inside connect() the set-up fails instead, and the caller retries as for a lost connection.

Test (tests/e2e/replycorpus_test.go, TestUpstreamReplyCorpus): runs the 82 cases of nntp-testkit v0.14.0's reply corpus whose command NewsCache sends upstream: the greeting, MODE READER, AUTHINFO, COMPRESS, GROUP, LISTGROUP, OVER, ARTICLE, LIST, STAT, POST and QUIT. Each case replaces the first reply on a connection. After the client steps, a command that has to go to the server must get the right reply (an article, or the overview for the ARTICLE cases). DATE must still answer 111, and QUIT must end the session with exit 0. For a reply to a set-up command, which comes again on every connection, a clean 403 is allowed, but wrong data isn't.

  • Without the fix (main ed82e37): 10 cases fail, all with a wrong or missing follow-up reply: group-215-block, group-100-block, listgroup-215-block, list-231-block, list-282-block, over-225-block, article-222-block, article-430-block, stat-220-block, authinfo-user-215-block.
  • With the fix: 82/82 pass. Full e2e suite green (353 s, local build) and unit checks 22/22.

NEWS: changes/fix-upstream-stale-reply.md.

🤖 Generated with Claude Code

go-while and others added 2 commits November 14, 2026 06:48
NewsCache sends one command at a time and reads each reply to its end,
so the stream buffer is empty when the next command goes out. A news
server that sends more than its reply, e.g. a block after a status
line that announces none (215 and a list in reply to GROUP, 430 and
text), left those lines there: the next command read them as its
reply, and every later reply on the connection was shifted. The client
got 430 for an article the server has, or 403.

issue() now checks the buffer before it sends a command (in_avail:
bytes already read from the socket, TLS or inflated, so a compressed
connection's trailing flush bytes don't count) and, when anything is
left, closes the connection and connects again; connect() selects the
current group again. Inside connect() the set-up fails instead, and
the caller tries again as for a lost connection.

TestUpstreamReplyCorpus runs the 82 cases of nntp-testkit's reply
corpus whose command NewsCache sends upstream (greeting, MODE READER,
AUTHINFO, COMPRESS, GROUP, LISTGROUP, OVER, ARTICLE, LIST, STAT, POST,
QUIT). Each case replaces the first reply on a connection; then a
command that has to go to the server must get the right reply, DATE
must still answer 111, and QUIT must end the session cleanly. Without
the fix 10 cases fail (group-215-block, group-100-block,
listgroup-215-block, list-231-block, list-282-block, over-225-block,
article-222-block, article-430-block, stat-220-block,
authinfo-user-215-block).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dist-hook refuses a tests/e2e Go file that isn't in EXTRA_DIST
(found by the PR review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant