| /** | |
| * Describes failures raised while handling HTTP server requests. | |
| * | |
| * `HttpServerError` covers failures that happen while accepting a request, | |
| * matching a route, running a handler, or building and sending a response. | |
| * Request-scoped failures keep the request that caused them, and response | |
| * failures keep the response being produced. This module also includes helpers | |
| * for turning failed causes or exits into HTTP responses and an annotation for | |
| * interrupts caused by client aborts. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| import * as Cause from "../../Cause.js"; | |
| import * as Context from "../../Context.js"; | |
| import * as Data from "../../Data.js"; | |
| import * as Effect from "../../Effect.js"; | |
| import * as ErrorReporter from "../../ErrorReporter.js"; | |
| import { constUndefined } from "../../Function.js"; | |
| import * as Option from "../../Option.js"; | |
| import { hasProperty } from "../../Predicate.js"; | |
| import * as Respondable from "./HttpServerRespondable.js"; | |
| import * as Response from "./HttpServerResponse.js"; | |
| const TypeId = "~effect/http/HttpServerError"; | |
| /** | |
| * Tagged error for failures that occur while handling an HTTP server request. | |
| * | |
| * **Details** | |
| * | |
| * It wraps a `HttpServerErrorReason`, exposes the associated request and optional | |
| * response, and can be converted to an HTTP response through the `Respondable` | |
| * protocol. | |
| * | |
| * @category errors | |
| * @since 4.0.0 | |
| */ | |
| export class HttpServerError extends /*#__PURE__*/Data.TaggedError("HttpServerError") { | |
| constructor(props) { | |
| if ("cause" in props.reason) { | |
| super({ | |
| ...props, | |
| cause: props.reason.cause | |
| }); | |
| } else { | |
| super(props); | |
| } | |
| } | |
| [TypeId] = TypeId; | |
| stack = `${this.name}: ${this.message}`; | |
| get request() { | |
| return this.reason.request; | |
| } | |
| get response() { | |
| return "response" in this.reason ? this.reason.response : undefined; | |
| } | |
| [Respondable.symbol]() { | |
| return this.reason[Respondable.symbol](); | |
| } | |
| get [ErrorReporter.ignore]() { | |
| return this.reason[ErrorReporter.ignore] ?? false; | |
| } | |
| get message() { | |
| return this.reason.message; | |
| } | |
| } | |
| /** | |
| * Error describing a failure to parse or read an incoming request. | |
| * | |
| * **Details** | |
| * | |
| * When converted to a response it produces an empty `400` response. | |
| * | |
| * @category errors | |
| * @since 4.0.0 | |
| */ | |
| export class RequestParseError extends /*#__PURE__*/Data.TaggedError("RequestParseError") { | |
| /** | |
| * Converts the request error into a `400 Bad Request` response. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| [Respondable.symbol]() { | |
| return Effect.succeed(Response.empty({ | |
| status: 400 | |
| })); | |
| } | |
| get methodAndUrl() { | |
| return `${this.request.method} ${this.request.url}`; | |
| } | |
| get message() { | |
| return formatRequestMessage(this._tag, this.description, this.methodAndUrl); | |
| } | |
| } | |
| /** | |
| * Error indicating that no route matched the incoming request. | |
| * | |
| * **Details** | |
| * | |
| * When converted to a response it produces an empty `404` response, and it is | |
| * ignored by the error reporter. | |
| * | |
| * @category errors | |
| * @since 4.0.0 | |
| */ | |
| export class RouteNotFound extends /*#__PURE__*/Data.TaggedError("RouteNotFound") { | |
| [Respondable.symbol]() { | |
| return Effect.succeed(Response.empty({ | |
| status: 404 | |
| })); | |
| } | |
| [ErrorReporter.ignore] = true; | |
| get methodAndUrl() { | |
| return `${this.request.method} ${this.request.url}`; | |
| } | |
| get message() { | |
| return formatRequestMessage(this._tag, this.description, this.methodAndUrl); | |
| } | |
| } | |
| /** | |
| * Error describing an unexpected server-side failure while handling a request. | |
| * | |
| * **Details** | |
| * | |
| * When converted to a response it produces an empty `500` response. | |
| * | |
| * @category errors | |
| * @since 4.0.0 | |
| */ | |
| export class InternalError extends /*#__PURE__*/Data.TaggedError("InternalError") { | |
| /** | |
| * Converts the server error into a `500 Internal Server Error` response. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| [Respondable.symbol]() { | |
| return Effect.succeed(Response.empty({ | |
| status: 500 | |
| })); | |
| } | |
| get methodAndUrl() { | |
| return `${this.request.method} ${this.request.url}`; | |
| } | |
| get message() { | |
| return formatRequestMessage(this._tag, this.description, this.methodAndUrl); | |
| } | |
| } | |
| /** | |
| * Returns `true` when the supplied value is an `HttpServerError`. | |
| * | |
| * @category predicates | |
| * @since 4.0.0 | |
| */ | |
| export const isHttpServerError = u => hasProperty(u, TypeId); | |
| /** | |
| * Error describing a failure related to an HTTP response. | |
| * | |
| * **Details** | |
| * | |
| * It carries the request and response involved in the failure. When converted to | |
| * a response it produces an empty `500` response. | |
| * | |
| * @category errors | |
| * @since 4.0.0 | |
| */ | |
| export class ResponseError extends /*#__PURE__*/Data.TaggedError("ResponseError") { | |
| [Respondable.symbol]() { | |
| return Effect.succeed(Response.empty({ | |
| status: 500 | |
| })); | |
| } | |
| get methodAndUrl() { | |
| return `${this.request.method} ${this.request.url}`; | |
| } | |
| get message() { | |
| const info = `${this._tag} (${this.response.status} ${this.methodAndUrl})`; | |
| return this.description ? `${info}: ${this.description}` : info; | |
| } | |
| } | |
| /** | |
| * Error wrapping a low-level failure from the HTTP server implementation. | |
| * | |
| * @category errors | |
| * @since 4.0.0 | |
| */ | |
| export class ServeError extends /*#__PURE__*/Data.TaggedError("ServeError") {} | |
| /** | |
| * Context annotation used to mark an interrupt as caused by the client aborting | |
| * the request. | |
| * | |
| * **Details** | |
| * | |
| * `causeResponse` uses this annotation to map a pure client abort to a `499` | |
| * response instead of a server abort response. | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export class ClientAbort extends /*#__PURE__*/Context.Service()("effect/http/HttpServerError/ClientAbort") { | |
| static annotation = /*#__PURE__*/this.context(true).pipe(/*#__PURE__*/Context.add(Cause.StackTrace, { | |
| name: "ClientAbort", | |
| stack: constUndefined, | |
| parent: undefined | |
| })); | |
| } | |
| const formatRequestMessage = (reason, description, info) => { | |
| const prefix = `${reason} (${info})`; | |
| return description ? `${prefix}: ${description}` : prefix; | |
| }; | |
| /** | |
| * Converts a failed handler cause into the HTTP response that should be sent and | |
| * the cause that should be reported. | |
| * | |
| * **Details** | |
| * | |
| * Respondable failures and defects can choose their own response, defects that | |
| * are already `HttpServerResponse` values are used directly, and pure interrupts | |
| * produce either `499` for client aborts or `503` for server aborts. | |
| * | |
| * @category error handling | |
| * @since 4.0.0 | |
| */ | |
| export const causeResponse = cause => { | |
| let response; | |
| let effect = succeedInternalServerError; | |
| const failures = []; | |
| let interrupts = []; | |
| let isClientInterrupt = false; | |
| for (let i = 0; i < cause.reasons.length; i++) { | |
| const reason = cause.reasons[i]; | |
| switch (reason._tag) { | |
| case "Fail": | |
| { | |
| effect = Respondable.toResponseOrElse(reason.error, internalServerError); | |
| failures.push(reason); | |
| break; | |
| } | |
| case "Die": | |
| { | |
| if (Response.isHttpServerResponse(reason.defect)) { | |
| response = reason.defect; | |
| } else { | |
| effect = Respondable.toResponseOrElseDefect(reason.defect, internalServerError); | |
| failures.push(reason); | |
| } | |
| break; | |
| } | |
| case "Interrupt": | |
| { | |
| isClientInterrupt = reason.annotations.has(ClientAbort.key); | |
| if (failures.length > 0) break; | |
| interrupts.push(reason); | |
| break; | |
| } | |
| } | |
| } | |
| if (response) { | |
| return Effect.succeed([response, Cause.fromReasons(failures)]); | |
| } else if (interrupts.length > 0 && failures.length === 0) { | |
| failures.push(...interrupts); | |
| effect = isClientInterrupt ? clientAbortError : serverAbortError; | |
| } | |
| return Effect.mapEager(effect, response => { | |
| failures.push(Cause.makeDieReason(response)); | |
| return [response, Cause.fromReasons(failures)]; | |
| }); | |
| }; | |
| /** | |
| * Derives an HTTP response from a failed handler cause synchronously. | |
| * | |
| * **Details** | |
| * | |
| * If the cause contains a defect that is already an `HttpServerResponse`, that | |
| * response is used and removed from the remaining cause. Otherwise the response | |
| * defaults to `500`. | |
| * | |
| * @category error handling | |
| * @since 4.0.0 | |
| */ | |
| export const causeResponseStripped = cause => { | |
| let response; | |
| const failures = cause.reasons.filter(f => { | |
| if (f._tag === "Die" && Response.isHttpServerResponse(f.defect)) { | |
| response = f.defect; | |
| return false; | |
| } | |
| return true; | |
| }); | |
| return [response ?? internalServerError, failures.length > 0 ? Option.some(Cause.fromReasons(failures)) : Option.none()]; | |
| }; | |
| const internalServerError = /*#__PURE__*/Response.empty({ | |
| status: 500 | |
| }); | |
| const succeedInternalServerError = /*#__PURE__*/Effect.succeed(internalServerError); | |
| const clientAbortError = /*#__PURE__*/Effect.succeed(/*#__PURE__*/Response.empty({ | |
| status: 499 | |
| })); | |
| const serverAbortError = /*#__PURE__*/Effect.succeed(/*#__PURE__*/Response.empty({ | |
| status: 503 | |
| })); | |
| /** | |
| * Extracts the response from a successful handler exit, or derives a response | |
| * from the failure cause. | |
| * | |
| * @category error handling | |
| * @since 4.0.0 | |
| */ | |
| export const exitResponse = exit => { | |
| if (exit._tag === "Success") { | |
| return exit.value; | |
| } | |
| return causeResponseStripped(exit.cause)[0]; | |
| }; | |
| //# sourceMappingURL=HttpServerError.js.map |
Xet Storage Details
- Size:
- 9.24 kB
- Xet hash:
- cd2b26926e43d714d2cbde06a5dc561b483b35aa7f0b8ec42cba2daaa370776b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.