FileSharer Development Notes 3 — When an HTTP Request Arrives in Pieces
한국어로 읽기 · Part 2: Expiration, PINs, and download counts
FileSharer Development Notes · 03 / 10

Where should you start when a download page that opens on the same PC fails intermittently through a relay? If you focus only on addresses and authentication, it is easy to overlook whether the server read the entire request. The FileSharer source has a small function called ReadRequestHead that addresses this problem.

The function accumulates received bytes in a buffer and looks for \r\n\r\n, the end of the HTTP header section. The key is less the loop itself than separating the number of reads from whether the message is complete.
Read does not return the length of one request
Passing a large buffer to NetworkStream.Read does not guarantee that the method will fill it. It can return fewer bytes than requested, depending on what is available to read. It can return 0 when the connection has been closed gracefully. This behavior is also documented in Microsoft's NetworkStream.Read reference.
For example, the first read might return only this string:
GET /d/demo0001 HTTP/1.1\r\nHo
Even the header name Host is still incomplete. Finishing parsing at this point would miss the headers yet to arrive. A single write by the sending program does not guarantee a corresponding single Read on the receiving side, either.
The header terminator can straddle a buffer boundary
The original function uses a 16,384-byte buffer. When new data arrives, it also checks the region where it joins the previous bytes.
int scanFrom = Math.Max(3, filled);
filled += n;
Because the scan examines the preceding three bytes along with the current position, it can detect a terminator even when \r\n\r arrives first and the final \n arrives later. Searching for all four characters only within the newly received fragment would miss this boundary.
Rather than concatenating strings for each fragment, this code collects the bytes before converting them to text. That separates detecting the HTTP terminator from interpreting the text.
Results from sending fragments over loopback
Instead of running the original server in full, I placed the methods in a standalone C# executable. The receiving socket listened only on an ephemeral port at 127.0.0.1, and I sent a sample request in four pieces. The final two pieces also split the header terminator.
| Check | Result |
|---|---|
| Header terminator found after combining the fragments | true |
| User-Agent value extracted | SeriesLab |
| Sender closed before completing the headers | Incomplete string returned |
The first two results show that the normal fragmented-read path works. The third exposes an important limitation. Even when the loop ends because of EOF, a read exception, or a full buffer, the original function returns whatever text it has already received. A nonempty return value does not establish that the headers are complete.
Size limits and time limits are different, too
The calling method, ProcessClient, sets the stream's ReadTimeout to 15,000 milliseconds. The function's buffer is 16 KiB. These limit how long a read waits and how much input can be stored, respectively, but they do not implement an absolute deadline requiring the entire header section to arrive within 15 seconds.
To strengthen this into a production parser, the function could return distinct results for completion, EOF, timeout, and size overflow, and allow processing to continue only when the headers are complete. A separate deadline measured from the start of reception could also be added. These are proposed improvements; they were not implemented as part of this check.
Start by creating input that reproduces the problem
When investigating network errors, creating small inputs that challenge the receiving code can sometimes be faster than repeatedly reconnecting an external relay service. Try splitting a request in the middle of a header name, a line ending, or the header terminator. For each case, check separately whether the code reconstructs a valid message and whether it rejects an incomplete one.
This test established the local behavior of these two methods. It did not validate the HTTP server's overall standards compliance or the reliability of an external tunnel.
FileSharer.cs:4625–4666 ProcessClient · FileSharer.cs:4998–5044 ReadRequestHead / GetHeader
The original project files were not modified. Proposed improvements are distinguished from implemented behavior.
댓글
댓글 쓰기