 
     let delayLatch = Latch(name: "delayed request")
 
-    func matches(_ request: URLRequest) -> Bool
-        {
-        return request.httpMethod == method
-            && request.url?.absoluteString == url
-            && requestHeaders.allSatisfy
-                {
-                key, value in
-                request.allHTTPHeaderFields?[key] == value
-                }
-        }
-
     var description: String
-        { "\(method) \(url) requestHeaders=\(requestHeaders) body=\(requestBody?.description ?? "<any>")" }
+        { "\(matcher) → \(responseCode)" }
     }
 
 @discardableResult
 func stubRequest(_ resource: () -> Resource, _ method: String) -> LSStubRequestDSL
     {
-    let stub = RequestStub(method: method, url: resource().url.absoluteString)
+    let stub = RequestStub(matcher: RequestMatcher(method: method, url: resource().url.absoluteString))
     NetworkStub.add(stub)
     return LSStubRequestDSL(stub: stub)
     }

                { Self.requestInitialization.decrement() }

            let stubs = Self.stubs
            guard let stub = stubs.first(where: { $0.matches(request) }) else
                {
                fatalError(
                    """

    func withHeader(_ key: String, _ value: String?) -> Self
        {
        synchronized
            { stub.matcher.headers[key] = value }
        return self
        }

