uttrflow / uttrflow/uttrflow-swift

Loopback sign-in rejects a callback when its HTTP request line arrives in multiple TCP reads

Open
#790 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.