Airframe

Airframe

  • Docs
  • Blog
  • Release Notes
  • GitHub

›Utilities

Resources

  • Overview
  • Articles
  • Release Notes
  • Logos

Framework

  • airframe-di: Dependency Injection
  • Airframe RPC
  • airframe-http: Creating REST Service
  • airframe-rx: ReactiveX interface
  • AirSpec: Testing Framework

Core Modules

  • airframe-codec: Schema-On-Read Object Serializer
  • airframe-config: Application Config Flow
  • airframe-control: Retry/Rate Control
  • airframe-log: Application Logger
  • airframe-metrics: Human-Friendly Measures for Time and Data Size
  • airframe-surface: Object Shape Inspector

Utilities

  • airframe-benchmark: JMH Benchmark
  • airframe-canvas: Off-Heap Memory Manager
  • airframe-fluentd: Fluentd Logger
  • airframe-http-recorder: Web Request/Response Recorder
  • airframe-jdbc: JDBC Connection Pool
  • airframe-jmx: JMX Application Monitor
  • airframe-json: Pure-Scala JSON Parser
  • airframe-launcher: Command-Line Program Launcher
  • airframe-msgpack: Pure-Scala MessagePack Parser
  • airframe-parquet: Parquet Columnar File Reader and Writer
  • airframe-sql: SQL Parser
  • airframe-ulid: ULID Generator

airframe-http-recorder: Web Request/Response Recorder

airframe-http-recorder is an HTTP server for recording and replaying HTTP responses. This is useful for testing HTTP server interactions in an environment with limited resources (e.g., CI servers)

With HttpRecorder, you can:

  • Create a proxy server to the actual server and record the responses automatically.
    • This reduces the burden of writing mock servers and dummy responses.
  • Record custom HTTP request/response pairs.
    • This is useful for simulating server failures (e.g., returning 5xx responses)
  • Replay recorded responses, stored in SQLite databases.
    • This is useful for running HTTP server/client integration tests on CI, without actually accessing the real destination servers.

Usage

build.sbt

Maven Central

libraryDependencies += "org.wvlet.airframe" %% "airframe-http-recorder" %% (version)

Record & Replay

import wvlet.airframe.control.Control._
import wvlet.airframe.http.{Http,HttpStatus}
import wvlet.airframe.http.recorder.{HttpRecorder,HttpRecorderConfig}
import wvlet.airframe.http.finagle.FinagleClient

// Create a proxy server that will record responses for matching requests,
// and make actual requests the destination for non-recorded requests.
val recorderConfig = HttpRecorderConfig(destUri = "https://www.google.com")
withResource(HttpRecorder.createRecorderProxy(recorderConfig)) { server =>

  // Create an HTTP client
  val client = Http.client.newSyncClient(server.localAddress)  // "localhost:(port number)"

  // Requests to the local server will be recorded
  val response = client.send(Http.GET("/"))

  // You can record your own dummy responses
  server.record(Http.GET("/dummy"), Http.response(HttpStatus.NotFound_404))
}


// Create a proxy server only for recording server responses
withResource(HttpRecorder.createRecordOnlyServer(recorderConfig)) { server =>
  val addr = server.localAddress // "localhost:(port number)"
  // Requests to the local server will be recorded

  // If necessary, add custom request-response pairs
  server.recordIfNotExists(Http.GET("/dummy"), Http.response(HttpStatus.Ok_200))
}

// Create a replay server that returns recorded responses for matching requests 
withResource(HttpRecorder.createReplayOnlyServer(recorderConfig)) { server =>
  val addr = server.localAddress // "localhost:(port number)"
  // Requests to the local server will return the recorded responses
}

Programmable HttpRecorderServer

import wvlet.airframe.http.Http
import wvlet.airframe.http.recorder._
import wvlet.airframe.control.Control._

val recorderConfig = HttpRecorderConfig(sessionName="my-recording")
val response = withResource(HttpRecorder.createProgrammableServer(recorderConfig)) { server =>
  // Add custom server responses
  val request = Http.GET("/index.html")
  val response = Http.response().withContent("Hello World!")
  server.record(request, response)

  val addr = server.localAddress // "localhost:(port number)"
  // Requests to the local server will return the programmed responses
}

If you don't need to persist your recoded responsoe (e.g., in unit tests), use HttpRecorder.createInMemoryProgrammableServer. The recorded responses wiil be discarded after closing the server.

Customize Request Matcher

By default, http-recorder finds records that have the same method type (e.g., GET, POST, etc.), path, query string, request body, and HTTP headers. Some unstable HTTP headers like Date, User-Agent, etc. (defined in defaultExcludeHeaderPrefixes) will not be used for matching.

If this matching is too strict, we can use PathOnlyMatcher (checks only method types and uri) or your own implementation to compute hash values for matching requests:

import wvlet.airframe.http.recorder._
import wvlet.airframe.control.Control._

val config = HttpRecorderConfig(requestMatcher = HttpRequestMatcher.PathOnlyMatcher)
withResource(HttpRecorder.createInMemoryServer(config)) { server =>
  // 
}

Related Projects

  • VCR (for Ruby)
  • Betamax (no longer maintained)
← airframe-fluentd: Fluentd Loggerairframe-jdbc: JDBC Connection Pool →
  • Usage
    • Record & Replay
    • Programmable HttpRecorderServer
    • Customize Request Matcher
  • Related Projects
Airframe
Docs
Documentation
Community
Gitter Chat
More
GitHubStar
airframe logo
Copyright © 2024 wvlet.org