Skip to content

HTTP Client Release 5.0.0: Structured Responses, Typed Errors, and Full Streaming Playback#52

Merged
dcrockwell merged 3 commits into
mainfrom
develop
Feb 17, 2026
Merged

HTTP Client Release 5.0.0: Structured Responses, Typed Errors, and Full Streaming Playback#52
dcrockwell merged 3 commits into
mainfrom
develop

Conversation

@dcrockwell
Copy link
Copy Markdown
Contributor

Summary

  • send() returns HttpResponse — status, headers, and body instead of just the body string
  • Typed error variantsResponseError for HTTP 4xx/5xx (with full response), RequestError for transport failures
  • Ok always means success — HTTP error responses moved from Ok to Error, eliminating silent failures
  • start_stream() playback support — all three execution modes (send(), stream_yielder(), start_stream()) now fully support recording and playback
  • Bug fixdream_opensearch updated from pre-4.0 client.new to client.new()

Breaking Changes

  • send() return type: Result(String, String)Result(HttpResponse, SendError)
  • HTTP 4xx/5xx responses: Ok(body)Error(ResponseError(response))
  • Transport errors: Error(message)Error(RequestError(message: message))

Test Coverage

All 134 tests pass. See release notes for full details.

dcrockwell and others added 3 commits February 17, 2026 01:32
…, and full streaming playback

## Why This Change Was Made
- `send()` returned `Result(String, String)` in 4.x, which made HTTP errors invisible (a 404 looked identical to a 200), discarded status codes and headers, and forced callers to parse error strings. This made downstream consumers like proxy_request unable to properly map upstream failures.
- `start_stream()` could record streaming responses but could NOT play them back from fixtures, forcing callback-based streaming tests to hit real endpoints on every run. This blocked reliable CI testing for the proxy pipeline.

## What Was Changed
- **`send()` return type**: `Result(String, String)` → `Result(HttpResponse, SendError)` where `HttpResponse` carries status/headers/body and `SendError` distinguishes `ResponseError` (4xx/5xx with full response) from `RequestError` (transport failures)
- **`start_stream()` playback**: Added `replay_recorded_stream()` and `maybe_replay_from_recording()` private functions that replay recorded chunks directly via callbacks (`on_stream_start`, `on_stream_chunk`, `on_stream_end`) — no network calls during playback
- **Recording/playback parity**: All three execution modes (`send()`, `stream_yielder()`, `start_stream()`) now fully support both recording and playback with shared `StreamingResponse` fixture format
- **Hexdocs**: Added "Recording and Playback" sections to `send()`, `stream_yielder()`, and `start_stream()` documenting the behavior in each mode
- **README**: Added recording support table showing all modes, plus two new snippet references
- **Snippets**: Added `recording_stream_yielder.gleam` and `recording_start_stream.gleam` — tested examples proving streaming record/playback works end-to-end
- **Tests**: 134 tests total (up from 127). Added: start_stream playback with StreamingResponse, start_stream playback with BlockingResponse, start_stream playback miss fallthrough, transform_response direct tests
- **Release notes**: `releases/release-5.0.0.md` with migration guide, breaking changes, and streaming playback documentation
- **Downstream**: Updated `dream_opensearch` 2.1.0 to adapt to the new `send()` return type, updated examples

## Note to Future Engineer
- The `StreamingResponse` fixture format is shared between `stream_yielder()` and `start_stream()` — a recording made by one can be played back by either. This is intentional and tested.
- `stream_messages_with_recorder()` still has an error path for "recording found in stream_messages path" — this is a safety net. `run_stream_process()` handles playback via `maybe_replay_from_recording()` before `stream_messages()` is ever called, so this error should never fire in practice. If you see it, something changed the call order.
- The `internal.gleam` functions are `pub` only because Gleam requires FFI functions to be public. They're not part of the consumer API and are tested indirectly through higher-level functions. Don't feel guilty about not unit-testing them directly.
- If you're wondering why there are 134 tests for an HTTP client: because 2.1.0 shipped with streaming recording that was "fundamentally broken" and "tests were rigged." We learned our lesson. Every public function gets tested. Trust but verify. Then verify again.
…hangelog

## Why This Change Was Made
- The 5.0.0 release notes and changelog framed everything as "features" and "breaking changes" but never explicitly called out the bugs that were fixed. Three real bugs were buried under feature descriptions: silent 4xx/5xx success, start_stream playback errors, and stale opensearch syntax.

## What Was Changed
- Added a "Bug Fixes" section to release-5.0.0.md with three numbered items: (1) HTTP 4xx/5xx no longer silently succeed, (2) start_stream() playback no longer errors, (3) dream_opensearch client.new syntax
- Added a "### Fixed" section to CHANGELOG.md covering the same three bugs
- Restructured the "### Added" section in CHANGELOG to separately list HttpResponse type, SendError type, and start_stream playback as distinct additions
- Applied gleam format to test files (auto-formatting from pre-commit hook)

## Note to Future Engineer
- If you fix a bug, call it a bug fix. Don't dress it up as a feature — future you reading the changelog at 2am during an incident will thank present you for the honesty.
…onse

HTTP Client 5.0.0: Structured responses, typed errors, and full streaming playback
@dcrockwell dcrockwell self-assigned this Feb 17, 2026
@dcrockwell dcrockwell added enhancement New feature or request release Official public releases module Change to a dream module labels Feb 17, 2026
@dcrockwell dcrockwell merged commit 2cb0a8a into main Feb 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request module Change to a dream module release Official public releases

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant