feat: add request bins for inspecting captured HTTP requests#150
Conversation
Adds RequestBin-style endpoints so users can mint an ephemeral URL, send arbitrary HTTP requests to it (any method, subpath, or body), and read back what was received. Useful for webhook debugging. - Management API at /bins: create, list, get, delete bins; list/get/clear captured requests - Capture endpoint at /b/:id and /b/:id/* records method, headers, query, body (utf8 or base64 for binary), IP, content-type, size, and truncation flag - BinManager with a pluggable BinStore interface (InMemoryBinStore is the default); configurable TTL (24h default), per-bin request cap (100, FIFO drop), and body size cap (1 MiB, truncate + flag) - Cleanup timer is unref'd and stopped on server close - rewriteUrl now treats wildcard routes with non-wildcard params (like /b/:id/*) as specific, so subpath captures don't get rewritten away
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #150 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 37 41 +4
Lines 1089 1310 +221
Branches 222 259 +37
==========================================
+ Hits 1089 1310 +221 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review
This pull request implements Request Bins, allowing users to capture and inspect incoming HTTP requests through ephemeral URL endpoints. The update includes a BinManager for lifecycle operations, a pluggable storage system with an in-memory default, and new Fastify routes for management and request capture. Review feedback highlighted the need for robust input validation in the BinManager constructor and defensive loop conditions in the storage layer to prevent potential runtime issues with negative configuration values.
…estsPerBin - Clamp BinManager maxRequestsPerBin to >= 0 so a misconfigured negative cap can't drive InMemoryBinStore.addRequest into a runaway pop loop - Add defensive list.length > 0 guard in addRequest's eviction loop - Add tests for the MockHttp.bins getter/setter, route registration with and without the bins flag, registerBinRoutes with an explicit fastify instance, and that close() stops the cleanup timer (lines codecov flagged as uncovered)
…ference - Walk through a full webhook-debugging flow using curl - Show body encoding in three modes: utf8, base64 (binary), and truncated, each with concrete examples - Demonstrate FIFO eviction past maxRequestsPerBin - Add a Vitest example using mock.bins programmatically in a test suite - Sketch a Redis-backed BinStore implementation for the pluggable storage section - List bins on the Features bullet list and Table of Contents - Document bins.* methods, registerBinRoutes(), and the bins property in the API Reference
Summary
Adds requestbin.net-style request bins to mockhttp. Users mint an ephemeral URL, send arbitrary HTTP requests to it (any method, sub-path, or body), and read back what was received — useful for webhook debugging.
/bins/*— create, list, get, delete bins; list/get/clear captured requests/b/:idand/b/:id/*records method, headers, query, body (utf8 for text, base64 for binary), IP, content-type, size, and a truncation flagBinManagerwith a pluggableBinStoreinterface (InMemoryBinStoreis the default — Redis/SQLite can plug in later without breaking changes). Configurable TTL (24h default), per-bin request cap (100, FIFO drop), and body size cap (1 MiB, truncate + flag).unref()'d and stopped on serverclose()so it can never keep the Node process alive.rewriteUrlnow treats wildcard routes that also have non-wildcard params (like/b/:id/*) as specific, so subpath captures don't get rewritten away. Pure catch-all/*routes still defer to more specific prefix matches (existing behavior preserved).Endpoints
/bins/bins/bins/:id/bins/:id/requests/bins/:id/requests/:reqId/bins/:id/requests/bins/:id/b/:idand/b/:id/*Quick example
Test plan
pnpm test— 463 tests pass, lint clean/docsshows the newBinstag with all seven management endpoints plus the capture routerewriteUrltest "prefers a specific route over a wildcard catch-all" still passes — pure/*fallback semantics preservedGenerated by Claude Code