Async::HTTP::CaptureGuidesGetting Started

Getting Started

This guide explains how to get started with async-http-capture, a Ruby gem for recording and replaying HTTP requests using Protocol::HTTP.

Installation

Add the gem to your project:

$ bundle add async-http-capture

Core Concepts

async-http-capture has several core concepts:

Usage

The basic workflow involves:

  1. Recording: Capture HTTP interactions using middleware
  2. Storage: Save interactions using pluggable store backends
  3. Replay: Load and replay recorded interactions

Basic Recording

Here's how to record HTTP interactions to files:

require "async/http/capture"

# Create a store that saves to timestamped files:
store = Async::HTTP::Capture::CassetteStore.new("interactions")

# Create your application
app = ->(request) {Protocol::HTTP::Response[200, {}, ["OK"]]}

# Wrap it with recording middleware:
middleware = Async::HTTP::Capture::Middleware.new(app, store: store)

# Make requests - they will be automatically recorded:
request = Protocol::HTTP::Request["GET", "/users"]
response = middleware.call(request)
# This creates a file like recordings/20250821-105406-271633-12345-67890.json

Recording with Console Output

For debugging, you can log interactions to the console:

# Create a console store for debugging:
console_store = Async::HTTP::Capture::ConsoleStore.new
middleware = Async::HTTP::Capture::Middleware.new(app, store: console_store)

# This will log interactions to console:
middleware.call(request)
# Output: "Recorded: GET /users"

Loading and Replaying Interactions

# Load recorded interactions:
cassette = Async::HTTP::Capture::Cassette.load("interactions")

# Option 1: Use the built-in replay method for application warmup
cassette.replay(app)

# Option 2: Manual iteration for custom processing
cassette.each do |interaction|
	request = interaction.request  # Lazy Protocol::HTTP::Request construction
	response = app.call(request)   # Send to your app
	puts "#{request.method} #{request.path} -> #{response.status}"
end

Recording HTTP Requests and Responses

The middleware automatically records both requests and responses:

middleware = Async::HTTP::Capture::Middleware.new(
	app, 
	store: store
)

response = middleware.call(request)
# Both request and response are recorded.

Timestamped Storage

Each interaction is saved to a file named with timestamp, process ID, and object ID, providing several benefits:

recordings/
├── 20250821-105406-271633-12345-67890.json  # GET /users
├── 20250821-105006-257022-12346-67891.json  # POST /orders
└── 20250820-101234-567890-12347-67892.json  # GET /health

Benefits:

Application Warmup

A common use case is warming up your application with recorded traffic:

require "async/http/capture"

# Step 1: Record requests during development/testing
endpoint = Async::HTTP::Endpoint.parse("https://api.example.com")
store = Async::HTTP::Capture::CassetteStore.new("warmup_interactions")

recording_middleware = Async::HTTP::Capture::Middleware.new(
	nil,
	store: store
)

client = Async::HTTP::Client.new(endpoint, middleware: [recording_middleware])

# Make the requests you want to record
Async do
	client.get("/health")
	client.get("/api/popular-items") 
	client.post("/api/user-sessions", {user_id: 123})
end

# Step 2: Use recorded interactions to warm up your application
cassette = Async::HTTP::Capture::Cassette.load("warmup_interactions")
app = MyApplication.new

puts "Warming up with #{cassette.interactions.size} recorded interactions..."
cassette.each do |interaction|
	request = interaction.request
	begin
		app_response = app.call(request)
		puts "Warmed up #{request.method} #{request.path} -> #{app_response.status}"
	rescue => error
		puts "Warning: #{request.method} #{request.path} -> #{error.message}"
	end
end

puts "Warmup complete!"

Custom Storage Backends

You can create custom storage backends by implementing the Async::HTTP::Capture::Store interface:

class MyCustomStore
	def call(interaction)
		# Handle the interaction as needed
		# e.g., send to a database, external service, etc.
		puts "Custom handling: #{interaction.request.method} #{interaction.request.path}"
	end
end

# Use your custom store
custom_store = MyCustomStore.new
middleware = Async::HTTP::Capture::Middleware.new(app, store: custom_store)