-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve error handling and response parser (#232)
* feat: add tool to decompress and parse.
- Loading branch information
Showing
22 changed files
with
5,159 additions
and
230 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
# ERRORS | ||
|
||
All errors generated by Httpie failure inherit [`HttpieError`](../src/class/HttpieCommonError.ts). | ||
|
||
```ts | ||
interface HttpieError { | ||
headers: IncomingHttpHeaders; | ||
statusCode: number; | ||
} | ||
``` | ||
|
||
## Tools | ||
|
||
### isHttpieError | ||
|
||
The `isHttpieError` function can be used to find out weither the error is a `@myunisoft/httpie` or a `undici` error. | ||
```ts | ||
function isHttpieError(error: unknown): boolean; | ||
``` | ||
|
||
Example: | ||
```ts | ||
import * as httpie from "@myunisoft/httpie"; | ||
|
||
try { | ||
await httpie.request("GET", "127.0.0.1"); | ||
} | ||
catch (error) { | ||
if (httpie.isHttpieError(error)) { | ||
// This error inherits from HttpieError. | ||
console.log(Boolean(error.headers)) // true | ||
console.log(Boolean(error.statusCode)) // true | ||
} | ||
else { | ||
// This error can be of any error type. | ||
console.log(Boolean(error.headers)) // false | ||
console.log(Boolean(error.statusCode)) // false | ||
} | ||
} | ||
``` | ||
|
||
### isHTTPError | ||
|
||
The `isHTTPError` function can be used to find out if it is an HTTP error. | ||
```ts | ||
function isHTTPError(error: unknown): boolean; | ||
``` | ||
|
||
Example: | ||
```ts | ||
import * as httpie from "@myunisoft/httpie"; | ||
|
||
try { | ||
await httpie.request("GET", "127.0.0.1"); | ||
} | ||
catch (error) { | ||
if (httpie.isHTTPError(error)) { | ||
console.log(Boolean(error.data)) // true | ||
console.log(Boolean(error.statusMessage)) // true | ||
console.log(Boolean(error.headers)) // true | ||
console.log(Boolean(error.statusCode)) // true | ||
} | ||
else { | ||
// This error can be of any error type. | ||
console.log(Boolean(error.data)) // false | ||
console.log(Boolean(error.statusMessage)) // false | ||
} | ||
} | ||
``` | ||
|
||
--- | ||
|
||
## HTTP errors | ||
|
||
If the `RequestOptions.throwOnHttpError` option is set to true, all HTTP responses with a status code higher than 400 will generate an `HttpieOnHttpError` error. | ||
|
||
> [!NOTE] | ||
> Use [`isHTTPError`](#ishttperror) function to know if it is an HTTP error. | ||
```ts | ||
interface HttpieOnHttpError<T> { | ||
statusCode: number; | ||
statusMessage: string; | ||
headers: IncomingHttpHeaders; | ||
data: T; | ||
} | ||
``` | ||
|
||
## Failed to retrieve response body | ||
|
||
```ts | ||
interface HttpieFetchBodyError { | ||
statusCode: number; | ||
headers: IncomingHttpHeaders; | ||
message: string; | ||
/** @description original error */ | ||
error?: Error; | ||
} | ||
``` | ||
|
||
## Failed to decompress response body | ||
|
||
If the `RequestOptions.mode` option is set with `decompress` or `parse`, Httpie will try to decompress the response body based on the **content-encoding** header. | ||
|
||
If Httpie fails to decompress the response body, an `HttpieDecompressionError` will be raised. | ||
|
||
```ts | ||
interface HttpieDecompressionError { | ||
statusCode: number; | ||
headers: IncomingHttpHeaders; | ||
message: string; | ||
/** @description original error */ | ||
error?: Error; | ||
/** @description original body as buffer */ | ||
buffer: Buffer; | ||
/** @description encodings from 'content-encoding' header */ | ||
encodings: string[]; | ||
} | ||
``` | ||
|
||
## Failed to parse response body | ||
|
||
If the `RequestOptions.mode` option is set with `parse`, Httpie will try to parse the response body based on the **content-type** header. | ||
|
||
If Httpie fails to parse the response body, an `HttpieParserError` will be raised. | ||
|
||
```ts | ||
interface HttpieParserError extends IHttpieHandlerError { | ||
statusCode: number; | ||
headers: IncomingHttpHeaders; | ||
message: string; | ||
/** @description original error */ | ||
error?: Error; | ||
/** @description content-type from 'content-type' header without params */ | ||
contentType: string; | ||
/** @description original body as buffer */ | ||
buffer: Buffer; | ||
/** @description body as string */ | ||
text: string | null; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import * as httpie from "../dist/index.js"; | ||
// import * as httpie from "@myunisoft/httpie"; | ||
|
||
{ | ||
const { data } = await httpie.request<Buffer>("GET", "127.0.0.1", { mode: "raw" }); | ||
console.log(data) // Buffer | ||
} | ||
|
||
{ | ||
const { data } = await httpie.request<Buffer>("GET", "127.0.0.1", { mode: "decompress" }); | ||
console.log(data) // Buffer | ||
} | ||
|
||
{ | ||
const { data } = await httpie.request<{ key: "value" }>("GET", "127.0.0.1", { mode: "raw" }); | ||
console.log(data) // [Object] { key: "value" } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import * as httpie from "../dist/index.js"; | ||
// import * as httpie from "@myunisoft/httpie"; | ||
|
||
// Should not throw | ||
{ | ||
const { statusCode } = await httpie.request("GET", "127.0.0.1", { | ||
throwOnHttpError: false | ||
}); | ||
|
||
console.log(statusCode) // 500 | ||
} | ||
|
||
// Should throw | ||
try { | ||
await httpie.request("GET", "127.0.0.1", { | ||
throwOnHttpError: true | ||
}); | ||
} | ||
catch (error) { | ||
console.log(error.statusCode) // 500 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Import Third-party Dependencies | ||
import { IncomingHttpHeaders } from "undici/types/header"; | ||
|
||
type CommonResponseData = { | ||
statusCode: number; | ||
headers: IncomingHttpHeaders; | ||
} | ||
|
||
export interface HttpieErrorOptions { | ||
response: CommonResponseData; | ||
} | ||
|
||
export class HttpieError extends Error { | ||
headers: IncomingHttpHeaders; | ||
statusCode: number; | ||
|
||
constructor(message: string, options: HttpieErrorOptions) { | ||
super(message); | ||
|
||
this.statusCode = options.response.statusCode; | ||
this.headers = options.response.headers; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* eslint-disable max-classes-per-file */ | ||
|
||
// Import Third-party Dependencies | ||
import { HttpieError, HttpieErrorOptions } from "./HttpieCommonError"; | ||
import { getDecompressionError, getFetchError, getParserError } from "../common/errors"; | ||
|
||
type MessageOfGetDecompressionError = Parameters<typeof getDecompressionError>[0]["message"]; | ||
type MessageOfGetParserError = Parameters<typeof getParserError>[0]["message"]; | ||
type MessageOfGetFetchError = Parameters<typeof getFetchError>[0]["message"]; | ||
|
||
interface HttpieHandlerErrorOptions<T extends string = string> extends HttpieErrorOptions { | ||
/** @description original error */ | ||
error?: Error; | ||
message: T; | ||
} | ||
|
||
interface HttpieDecompressionErrorOptions extends HttpieHandlerErrorOptions<MessageOfGetDecompressionError> { | ||
/** @description original body as buffer */ | ||
buffer: Buffer; | ||
/** @description encodings from 'content-encoding' header */ | ||
encodings: string[]; | ||
} | ||
|
||
interface HttpieParserErrorOptions extends HttpieHandlerErrorOptions<MessageOfGetParserError> { | ||
/** @description content-type from 'content-type' header without params */ | ||
contentType: string; | ||
/** @description original body as buffer */ | ||
buffer: Buffer; | ||
/** @description body as string */ | ||
text: string | null; | ||
} | ||
|
||
class HttpieHandlerError extends HttpieError { | ||
reason: Error | null; | ||
|
||
constructor(message: string, options: HttpieHandlerErrorOptions) { | ||
super(message, options); | ||
|
||
this.name = options.message; | ||
this.reason = options.error ?? null; | ||
} | ||
} | ||
|
||
export class HttpieFetchBodyError extends HttpieHandlerError { | ||
constructor(options: HttpieHandlerErrorOptions<MessageOfGetFetchError>, ...args) { | ||
super(getFetchError(options, ...args), options); | ||
} | ||
} | ||
|
||
export class HttpieDecompressionError extends HttpieHandlerError { | ||
buffer: Buffer; | ||
encodings: string[]; | ||
|
||
constructor(options: HttpieDecompressionErrorOptions, ...args) { | ||
super(getDecompressionError(options, ...args), options); | ||
|
||
this.buffer = options.buffer; | ||
this.encodings = options.encodings; | ||
} | ||
} | ||
|
||
export class HttpieParserError extends HttpieHandlerError { | ||
contentType: string; | ||
buffer: Buffer; | ||
text: string | null; | ||
|
||
constructor(options: HttpieParserErrorOptions, ...args) { | ||
super(getParserError(options, ...args), options); | ||
|
||
this.buffer = options.buffer; | ||
this.contentType = options.contentType; | ||
this.text = options.text ?? null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Import Internal Dependencies | ||
import { HttpieError } from "./HttpieCommonError"; | ||
import { RequestResponse } from "../request"; | ||
|
||
/** | ||
* @description Class to generate an Error with all the required properties from the response. | ||
* We attach these to the error so that they can be retrieved by the developer in a Catch block. | ||
*/ | ||
export class HttpieOnHttpError<T extends RequestResponse<any>> extends HttpieError { | ||
name = "HttpieOnHttpError"; | ||
|
||
statusMessage: string; | ||
data: T["data"]; | ||
|
||
constructor(response: T) { | ||
super(response.statusMessage, { response }); | ||
|
||
this.statusMessage = response.statusMessage; | ||
this.data = response.data; | ||
} | ||
} |
Oops, something went wrong.