VOOZH about

URL: https://deepwiki.com/hypervel/http-client/10.1-middleware-and-handler-stack

⇱ Middleware and Handler Stack | hypervel/http-client | DeepWiki


Loading...
Menu

Middleware and Handler Stack

This document explains how to extend HTTP request processing by adding custom middleware to the Guzzle handler stack in the Hypervel HTTP Client. The handler stack provides a flexible mechanism for intercepting, modifying, and observing HTTP requests and responses at various stages of the request lifecycle.

For information about testing-specific handlers (faking and stubbing), see Faking Requests. For retry logic configuration, see Retry Logic and Error Recovery. For event-based observability, see Event System.

Handler Stack Overview

The HTTP client uses Guzzle's HandlerStack to manage a chain of middleware handlers. Each handler can intercept requests before they are sent, modify them, and/or process responses after they are received. The stack operates as a chain of responsibility pattern, where each handler wraps the next handler in the stack.


Sources: src/PendingRequest.php1057-1076

The handler stack is built when a request is executed, combining user-provided middleware with internal handlers that manage request recording, stubbing, and pre-send callbacks.

Adding Custom Middleware

The PendingRequest class provides three methods for adding custom middleware to the handler stack. User-defined middleware is stored in the middleware collection property and applied to every request made by that PendingRequest instance.

General Middleware

The withMiddleware() method adds raw middleware callables directly to the stack. This provides maximum flexibility for custom request/response processing.

Method Signature:


Implementation:

  • Pushes the middleware callable to the middleware collection src/PendingRequest.php526-531
  • Middleware receives a $handler parameter and must return a callable that accepts ($request, $options) and returns a promise

Sources: src/PendingRequest.php526-531

Request Middleware

The withRequestMiddleware() method adds middleware specifically for modifying requests before they are sent. This uses Guzzle's Middleware::mapRequest() helper internally.

Method Signature:


Implementation:

  • Wraps the user callback with Middleware::mapRequest() src/PendingRequest.php536-541
  • The callback receives a RequestInterface and must return a RequestInterface
  • Simplifies request modification by abstracting the handler wrapping logic

Sources: src/PendingRequest.php536-541

Response Middleware

The withResponseMiddleware() method adds middleware specifically for processing responses after they are received. This uses Guzzle's Middleware::mapResponse() helper internally.

Method Signature:


Implementation:

  • Wraps the user callback with Middleware::mapResponse() src/PendingRequest.php546-551
  • The callback receives a ResponseInterface and must return a ResponseInterface
  • Simplifies response processing by abstracting the handler wrapping logic

Sources: src/PendingRequest.php546-551

Handler Stack Construction

The handler stack is constructed in two phases: creation and population. This occurs when the Guzzle client is built before executing a request.


Sources: src/PendingRequest.php1033-1076

buildHandlerStack Method

This method creates a new HandlerStack instance with an optional custom handler, then populates it with all middleware.

Implementation Details:

  • Creates handler stack via HandlerStack::create($this->handler) src/PendingRequest.php1057-1060
  • The $this->handler property can be set via setHandler() to use a custom base handler
  • If no custom handler is set, Guzzle uses its default handler
  • Delegates to pushHandlers() to populate the stack

Sources: src/PendingRequest.php1057-1060 src/PendingRequest.php1295-1300

pushHandlers Method

This method adds all middleware and internal handlers to the handler stack in a specific order.

Execution Order:

  1. User-defined middleware (from the middleware collection)
  2. Before sending handler
  3. Recorder handler
  4. Stub handler

Implementation:


Sources: src/PendingRequest.php1065-1076

Handler Execution Flow

Handlers in the stack execute in reverse order to how they were pushed. The last handler pushed is the first to receive the request. This creates a wrapping effect where each handler can process the request before passing it down and process the response on the way back up.


Sources: src/PendingRequest.php1065-1076

This execution order ensures that:

  • Stub handler can intercept requests first (for testing)
  • Recorder handler captures all requests/responses (even stubbed ones)
  • Before sending callbacks execute immediately before the request is sent
  • User middleware executes closest to the actual HTTP transport

Internal Handlers

The HTTP client includes three internal handlers that provide core functionality. These handlers are automatically added to every handler stack.

Before Sending Handler

The before sending handler executes all callbacks registered via the beforeSending() method immediately before the request is sent to the network.

Purpose:

  • Execute pre-request callbacks
  • Allow request modification at the last moment
  • Set up request tracking (stores the request in $this->request)
  • Dispatch the RequestSending event

Implementation:


The handler calls runBeforeSendingCallbacks() which iterates through the beforeSendingCallbacks collection src/PendingRequest.php1171-1189 Each callback receives a Request wrapper, the options array, and the PendingRequest instance. Callbacks can return a modified RequestInterface or Request object.

Sources: src/PendingRequest.php1081-1088 src/PendingRequest.php1171-1189 src/PendingRequest.php202-209

Recorder Handler

The recorder handler captures request/response pairs for testing assertions. It records interactions regardless of whether the request was stubbed or actually sent over the network.

Purpose:

  • Record all request/response pairs
  • Enable test assertions (see Assertions)
  • Attach request data to the Request object for convenient access

Implementation:


Sources: src/PendingRequest.php1093-1109

The handler:

  1. Calls the next handler to get a promise
  2. Attaches a fulfillment callback to the promise
  3. Records the request/response pair via Factory::recordRequestResponsePair()
  4. Wraps the PSR-7 request in a Request object with attached data
  5. Returns the original response unchanged

Stub Handler

The stub handler enables request interception for testing purposes. It checks if a matching stub callback exists and returns a fake response instead of making a real HTTP request.

Purpose:

  • Enable request faking in tests (see Faking Requests)
  • Prevent network requests during testing
  • Enforce strict fake matching with preventStrayRequests

Implementation:


Sources: src/PendingRequest.php1114-1145

The handler:

  1. Iterates through stubCallbacks collection (if present)
  2. Invokes each callback with the wrapped request and options
  3. Returns the first non-null response from callbacks
  4. If no stub matched and preventStrayRequests is true, throws RuntimeException
  5. If no stub matched and preventStrayRequests is false, passes request to next handler
  6. Converts array responses to PSR-7 responses via Factory::response()
  7. Handles sink option for streaming responses to files

Middleware Collection Storage

User-defined middleware is stored in the middleware property, which is a Collection instance initialized in the constructor.

Property Declaration:


Initialization:


Sources: src/PendingRequest.php145 src/PendingRequest.php187-191

The constructor accepts an initial array of middleware that can be provided by the Factory when creating PendingRequest instances. This allows global middleware to be applied to all requests created by a factory.

Custom Middleware Examples

Example 1: Request Logging Middleware


Implementation Pattern:


Sources: src/PendingRequest.php526-531

Example 2: Request Modification Middleware

For simpler request modification, use withRequestMiddleware():


Sources: src/PendingRequest.php536-541

Example 3: Response Transformation Middleware

For simpler response processing, use withResponseMiddleware():


Sources: src/PendingRequest.php546-551

Middleware vs Before Sending Callbacks

The HTTP client provides two mechanisms for intercepting requests: middleware and before sending callbacks. Understanding the difference is important for choosing the right approach.

FeatureMiddlewareBefore Sending Callbacks
MethodwithMiddleware(), withRequestMiddleware(), withResponseMiddleware()beforeSending()
Execution TimingWraps entire handler chainExecutes immediately before network request
Can Modify RequestYesYes
Can Modify ResponseYesNo
Access to ResponseYes (via promise chain)No
Can Prevent RequestYes (by not calling next handler)No
Typical Use CaseComplex request/response transformations, retry logic, cachingRequest inspection, setting tracking data, debugging

Sources: src/PendingRequest.php526-561 src/PendingRequest.php1081-1088

Handler Stack Diagram: Code Entity Mapping


Sources: src/PendingRequest.php145 src/PendingRequest.php526-551 src/PendingRequest.php1057-1076 src/PendingRequest.php1081-1145

Integration with Guzzle's Middleware System

The HTTP client's middleware system is built directly on top of Guzzle's HandlerStack and uses Guzzle's middleware helpers where appropriate.

Key Integration Points:

  1. HandlerStack Creation: Uses HandlerStack::create() with optional custom handler src/PendingRequest.php1059

  2. Middleware Helpers: Leverages Middleware::mapRequest() and Middleware::mapResponse() for simplified middleware src/PendingRequest.php538 src/PendingRequest.php548

  3. Handler Interface: All handlers follow Guzzle's handler signature: function($request, $options): PromiseInterface

  4. Promise Chain: Uses Guzzle's promise implementation for asynchronous request handling

This tight integration ensures compatibility with any Guzzle-compatible middleware while providing a clean, fluent API for common use cases.

Sources: src/PendingRequest.php14-15 src/PendingRequest.php526-551 src/PendingRequest.php1057-1076