Click the Castor logo or press Ctrl Alt T to change theme.
# Error handling and empty bodies Castor HTTP keeps routine absence separate from exceptional failures. Missing headers and cookies have explicit fallback APIs, while protocol, handler, and response-writing failures use dedicated exception types. ## Missing values are not errors Several APIs return safe defaults for expected absence: ```php $headers->lookup('Authorization'); // null $headers->get('Authorization'); // "" $headers->values('Set-Cookie'); // [] $cookies->lookup('session'); // Cookie|null $cookies->get('session'); // new Cookie('session') when absent RequestInfo::clientIp($ctx); // "0.0.0.0" when absent RequestInfo::parsedCookies($ctx); // empty Cookies collection when absent RequestInfo::parsedBody($ctx); // null when absent ``` Use these defaults for normal control flow. Reserve exceptions for invalid input, failed handlers, and unexpected protocol results. ## NoBody `Castor\Net\Http\NoBody` is the default body for manually created requests and responses without content. It implements `Castor\Io\Reader` and `Castor\Io\Closer`. ```php <?php use Castor\Net\Http\NoBody; $body = new NoBody(); $count = $body->read(8192, $buffer); $count; // 0 $buffer; // "" ``` `NoBody` can be read once. The first read returns `0` bytes and an empty buffer. Subsequent reads throw `Castor\Io\EndOfFile`. ```php $body->read(8192, $buffer); // first read succeeds $body->read(8192, $buffer); // EndOfFile ``` `close()` is a no-op. This behavior makes empty request and response bodies compatible with the same streaming APIs used for real bodies. ## HandlerError `HandlerError` extends `Exception` and represents a handler-level failure that can be translated into a response status. ```php <?php use Castor\Net\Http\HandlerError; use Castor\Net\Http\Status\Code; throw HandlerError::fromStatus(Code::BadRequest->value, 'Invalid JSON payload'); ``` `Handler\Callback` catches `Error` and `Exception` thrown by the callback and wraps them in `HandlerError` with status code `500`. This gives server adapters one error type to catch for application handler failures. ## HeadersAlreadySent `HeadersAlreadySent` extends `Castor\Io\Error`. It is thrown when a response writer is asked to send the status line and headers more than once. ```php $writer->writeHeaders(); $writer->writeHeaders(); // HeadersAlreadySent ``` Avoid it by treating headers as immutable once `writeHeaders` or `write` has been called. ## ProtocolError `ProtocolError` is used by clients and higher-level protocols that expect successful responses. It stores the original `Request` and `Response` for diagnostics. ```php <?php use Castor\Net\Http\ProtocolError; ProtocolError::check($request, $response); ``` `check` returns silently for successful `2xx` responses. It throws for: - `3xx` redirects: `Unexpected redirect status (...) on METHOD URI` - `4xx` client errors: `Unexpected client error status (...) on METHOD URI` - `5xx` server errors: `Unexpected server error status (...) on METHOD URI` - any other non-success status: `Unexpected out of range status (...) on METHOD URI` The thrown exception exposes: ```php $error->request; // original Request $error->response; // original Response $error->getCode(); // response status code ``` ## Invalid primitives Factories that coerce strings may throw native exceptions from the underlying value objects: - `Method::from` throws `ValueError` for unknown method strings. - `Version::parse` throws `InvalidArgumentException` for invalid version strings. - `Request::create` and request/response `copyWith` methods may propagate URI, method, or version parsing errors. Let these exceptions surface during construction, or catch them at application boundaries and convert them to a `400 Bad Request` or equivalent response.
Castor ecosystem