uttrflow / uttrflow/uttrflow-swift
Loopback sign-in rejects a callback when its HTTP request line arrives in multiple TCP reads
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## Problem
On main `8d93f5b8574f3c7b8c30086d9c817bf7f5d4827d`, `SystemLoopbackListener.accept` performs one receive with `minimumIncompleteLength: 1` and immediately parses whatever bytes it receives as the complete request. It does not accumulate the request line or request another chunk.
Relevant code: [LoopbackListener+System.swift, lines 95–109](https://github.com/uttrflow/uttrflow-swift/blob/8d93f5b8574f3c7b8c30086d9c817bf7f5d4827d/Sources/UttrflowAccount/LoopbackListener%2BSystem.swift#L95-L109).
A TCP read can end before the request line does. If the first chunk is `G`, the listener immediately sends `400 Bad Request` and closes that connection. The browser cannot finish delivering the callback on it. No callback reaches `HTTPAuthenticationService.awaitBrowser`, so that attempt continues waiting instead of completing.
## Verified reproduction
Tested the unchanged production listener with a real local socket on macOS, using an OS-assigned loopback port. No account, backend, or credentials were used.
1. Call `try await SystemLoopbackListener().bind()` while retaining the listener.
2. Connect a socket to the returned port. Send only `b'G'`, the first byte of the valid request below.
3. Before sending the rest, read the socket: the current listener already responds `HTTP/1.1 400 Bad Request`.
4. Control: on a fresh listener, send the complete request in one write. It responds `HTTP/1.1 200 OK`.
5. Close both listeners after the probe.
```http
GET /callback?code=test-code&state=test-state HTTP/1.1
Host: 127.0.0.1
```
The regression assertion that a fragmented request must not produce an early response failed; the complete-request control passed. The 63 existing account, entitlement, cache, PKCE and loopback parsing tests passed. Existing parsing tests supply complete strings and therefore do not exercise receive framing.
This establishes a transport-framing defect, not its frequency with any particular browser. It is distinct from #695, which covers callback state validation, endpoint reuse, device URL validation and connection limits; none makes the first TCP read a complete request.
## Acceptance criteria
- Accumulate bytes until a complete request line is available before parsing it; handle further reads, EOF and errors explicitly.
- Keep an explicit buffer limit and close malformed or oversized requests without leaving a waiting connection indefinitely.
- Add real-loopback or injected-receiver regression coverage for a request split after its first byte and inside the query, with the same resulting callback as the complete request.
- Preserve the existing callback validation and listener cleanup behavior while addressing #695 separately.
Priority: P2 (sign-in reliability).
Contributor guide
Research direction
Start in Sources/UttrflowAccount/LoopbackListener+System.swift at SystemLoopbackListener.accept and inspect how the receive result is parsed. Add regression coverage using a real loopback or injected receiver for requests split after the first byte and inside the query, then verify the fragmented and complete requests produce the same callback while EOF, errors, malformed input, and oversized requests are handled explicitly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- authentication, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100