From f1c4fbdb952e36c82f7cc51d152ce4875d9eb95c Mon Sep 17 00:00:00 2001 From: garrettmills Date: Thu, 30 Jul 2020 09:30:43 -0500 Subject: [PATCH] HTTP errors! --- TODO.txt | 5 + app/http/controllers/Test.controller.ts | 6 +- lib/src/const/http.ts | 168 ++++++++++++++++++ lib/src/error/HTTPError.ts | 10 ++ lib/src/http/response/ErrorResponseFactory.ts | 3 +- .../http/response/HTTPErrorResponseFactory.ts | 11 ++ lib/src/http/response/ResponseFactory.ts | 5 +- .../TemporaryRedirectResponseFactory.ts | 3 +- lib/src/http/response/helpers.ts | 7 + 9 files changed, 211 insertions(+), 7 deletions(-) create mode 100644 lib/src/const/http.ts create mode 100644 lib/src/error/HTTPError.ts create mode 100644 lib/src/http/response/HTTPErrorResponseFactory.ts diff --git a/TODO.txt b/TODO.txt index ba9fa42..757acb4 100644 --- a/TODO.txt +++ b/TODO.txt @@ -22,3 +22,8 @@ unit tests integration tests documentation & docs site events and observables? +no matching route handler +request timeout +not handlers -> not implemented +method not allowed +error response handler figure out json diff --git a/app/http/controllers/Test.controller.ts b/app/http/controllers/Test.controller.ts index 1120435..ab393a0 100644 --- a/app/http/controllers/Test.controller.ts +++ b/app/http/controllers/Test.controller.ts @@ -1,6 +1,6 @@ import Controller from '../../../lib/src/http/Controller.ts' import {Request} from '../../../lib/src/http/Request.ts' -import {error, html, redirect} from '../../../lib/src/http/response/helpers.ts' +import {error, html, http, redirect} from '../../../lib/src/http/response/helpers.ts' export default class TestController extends Controller { @@ -9,10 +9,10 @@ export default class TestController extends Controller { } get_user_home(request: Request) { - return redirect('/home') + return http(401, 'You should not be here!') } create_user_home(request: Request) { - return error('Not implemented!') + return http(401) } } diff --git a/lib/src/const/http.ts b/lib/src/const/http.ts new file mode 100644 index 0000000..89bff0d --- /dev/null +++ b/lib/src/const/http.ts @@ -0,0 +1,168 @@ +export enum HTTPStatus { + http100 = 100, + http101 = 101, + http102 = 102, + http200 = 200, + http201 = 201, + http202 = 202, + http203 = 203, + http204 = 204, + http205 = 205, + http206 = 206, + http207 = 207, + http300 = 300, + http301 = 301, + http302 = 302, + http303 = 303, + http304 = 304, + http305 = 305, + http307 = 307, + http308 = 308, + http400 = 400, + http401 = 401, + http402 = 402, + http403 = 403, + http404 = 404, + http405 = 405, + http406 = 406, + http407 = 407, + http408 = 408, + http409 = 409, + http410 = 410, + http411 = 411, + http412 = 412, + http413 = 413, + http414 = 414, + http415 = 415, + http416 = 416, + http417 = 417, + http418 = 418, + http419 = 419, + http420 = 420, + http422 = 422, + http423 = 423, + http424 = 424, + http428 = 428, + http429 = 429, + http431 = 431, + http500 = 500, + http501 = 501, + http502 = 502, + http503 = 503, + http504 = 504, + http505 = 505, + http507 = 507, + http511 = 511, + + CONTINUE = 100, + SWITCHING_PROTOCOLS = 101, + PROCESSING = 102, + OK = 200, + CREATED = 201, + ACCEPTED = 202, + NON_AUTHORITATIVE_INFORMATION = 203, + NO_CONTENT = 204, + RESET_CONTENT = 205, + PARTIAL_CONTENT = 206, + MULTI_STATUS = 207, + MULTIPLE_CHOICES = 300, + MOVED_PERMANENTLY = 301, + MOVED_TEMPORARILY = 302, + SEE_OTHER = 303, + NOT_MODIFIED = 304, + USE_PROXY = 305, + TEMPORARY_REDIRECT = 307, + PERMANENT_REDIRECT = 308, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + PAYMENT_REQUIRED = 402, + FORBIDDEN = 403, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + NOT_ACCEPTABLE = 406, + PROXY_AUTHENTICATION_REQUIRED = 407, + REQUEST_TIMEOUT = 408, + CONFLICT = 409, + GONE = 410, + LENGTH_REQUIRED = 411, + PRECONDITION_FAILED = 412, + REQUEST_TOO_LONG = 413, + REQUEST_URI_TOO_LONG = 414, + UNSUPPORTED_MEDIA_TYPE = 415, + REQUESTED_RANGE_NOT_SATISFIABLE = 416, + EXPECTATION_FAILED = 417, + IM_A_TEAPOT = 418, + INSUFFICIENT_SPACE_ON_RESOURCE = 419, + METHOD_FAILURE = 420, + UNPROCESSABLE_ENTITY = 422, + LOCKED = 423, + FAILED_DEPENDENCY = 424, + PRECONDITION_REQUIRED = 428, + TOO_MANY_REQUESTS = 429, + REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + INTERNAL_SERVER_ERROR = 500, + NOT_IMPLEMENTED = 501, + BAD_GATEWAY = 502, + SERVICE_UNAVAILABLE = 503, + GATEWAY_TIMEOUT = 504, + HTTP_VERSION_NOT_SUPPORTED = 505, + INSUFFICIENT_STORAGE = 507, + NETWORK_AUTHENTICATION_REQUIRED = 511, +} + +export const Message = { + 100: 'Continue', + 101: 'Switching Protocols', + 102: 'Processing', + 200: 'OK', + 201: 'Created', + 202: 'Accepted', + 203: 'Non Authoritative Information', + 204: 'No Content', + 205: 'Reset Content', + 206: 'Partial Content', + 207: 'Multi-Status', + 300: 'Multiple Choices', + 301: 'Moved Permanently', + 302: 'Moved Temporarily', + 303: 'See Other', + 304: 'Not Modified', + 305: 'Use Proxy', + 307: 'Temporary Redirect', + 308: 'Permanent Redirect', + 400: 'Bad Request', + 401: 'Unauthorized', + 402: 'Payment Required', + 403: 'Forbidden', + 404: 'Not Found', + 405: 'Method Not Allowed', + 406: 'Not Acceptable', + 407: 'Proxy Authentication Required', + 408: 'Request Timeout', + 409: 'Conflict', + 410: 'Gone', + 411: 'Length Required', + 412: 'Precondition Failed', + 413: 'Request Entity Too Large', + 414: 'Request-URI Too Long', + 415: 'Unsupported Media Type', + 416: 'Request Range Not Satisfiable', + 417: 'Expectation Failed', + 418: 'I\'m a teapot', + 419: 'Insufficient Space on Resource', + 420: 'Method Failure', + 422: 'Unprocessable Entity', + 423: 'Locked', + 424: 'Failed Dependency', + 428: 'Precondition Required', + 429: 'Too Many Requests', + 431: 'Request Header Fields Too Large', + 500: 'Server Error', + 501: 'Not Implemented', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + 504: 'Gateway Timeout', + 505: 'HTTP Version Not Supported', + 507: 'Insufficient Storage', + 511: 'Network Authentication Required', +} diff --git a/lib/src/error/HTTPError.ts b/lib/src/error/HTTPError.ts new file mode 100644 index 0000000..8a8aad7 --- /dev/null +++ b/lib/src/error/HTTPError.ts @@ -0,0 +1,10 @@ +import {HTTPStatus, Message} from '../const/http.ts' + +export default class HTTPError extends Error { + constructor( + public readonly http_status: HTTPStatus, + public readonly http_message?: string + ) { + super(`HTTP ${http_status}: ${http_message || Message[http_status]}`) + } +} diff --git a/lib/src/http/response/ErrorResponseFactory.ts b/lib/src/http/response/ErrorResponseFactory.ts index 62fbacf..e4aa248 100644 --- a/lib/src/http/response/ErrorResponseFactory.ts +++ b/lib/src/http/response/ErrorResponseFactory.ts @@ -1,13 +1,14 @@ import ResponseFactory from './ResponseFactory.ts' import {Request} from '../Request.ts' import * as api from '../../support/api.ts' +import {HTTPStatus} from '../../const/http.ts' export default class ErrorResponseFactory extends ResponseFactory { protected target_mode: 'json' | 'html' = 'html' constructor( public readonly error: Error, - status: number = 500, + status: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR, output: 'json' | 'html' = 'html', ) { super() diff --git a/lib/src/http/response/HTTPErrorResponseFactory.ts b/lib/src/http/response/HTTPErrorResponseFactory.ts new file mode 100644 index 0000000..dcc7127 --- /dev/null +++ b/lib/src/http/response/HTTPErrorResponseFactory.ts @@ -0,0 +1,11 @@ +import ErrorResponseFactory from './ErrorResponseFactory.ts' +import HTTPError from '../../error/HTTPError.ts' + +export default class HTTPErrorResponseFactory extends ErrorResponseFactory { + constructor( + public readonly error: HTTPError, + output: 'json' | 'html', + ) { + super(error, error.http_status, output) + } +} diff --git a/lib/src/http/response/ResponseFactory.ts b/lib/src/http/response/ResponseFactory.ts index 1eb24f4..0e16d1c 100644 --- a/lib/src/http/response/ResponseFactory.ts +++ b/lib/src/http/response/ResponseFactory.ts @@ -1,15 +1,16 @@ import AppClass from '../../lifecycle/AppClass.ts' import {Request} from '../Request.ts' +import {HTTPStatus} from '../../const/http.ts' export default abstract class ResponseFactory extends AppClass { - protected target_status: number = 200 + protected target_status: HTTPStatus = HTTPStatus.OK public async write(request: Request): Promise { request.response.status = this.target_status return request } - public status(status: number): ResponseFactory { + public status(status: HTTPStatus): ResponseFactory { this.target_status = status return this } diff --git a/lib/src/http/response/TemporaryRedirectResponseFactory.ts b/lib/src/http/response/TemporaryRedirectResponseFactory.ts index 472eb2c..c196367 100644 --- a/lib/src/http/response/TemporaryRedirectResponseFactory.ts +++ b/lib/src/http/response/TemporaryRedirectResponseFactory.ts @@ -1,8 +1,9 @@ import ResponseFactory from './ResponseFactory.ts' import {Request} from '../Request.ts' +import {HTTPStatus} from '../../const/http.ts' export default class TemporaryRedirectResponseFactory extends ResponseFactory { - protected target_status: number = 302 + protected target_status: HTTPStatus = HTTPStatus.TEMPORARY_REDIRECT constructor( public readonly destination: string, diff --git a/lib/src/http/response/helpers.ts b/lib/src/http/response/helpers.ts index ec0a8c1..50f93c8 100644 --- a/lib/src/http/response/helpers.ts +++ b/lib/src/http/response/helpers.ts @@ -5,6 +5,9 @@ import ErrorResponseFactory from './ErrorResponseFactory.ts' import {Rehydratable} from '../../support/Rehydratable.ts' import DehydratedStateResponseFactory from './DehydratedStateResponseFactory.ts' import TemporaryRedirectResponseFactory from './TemporaryRedirectResponseFactory.ts' +import {HTTPStatus} from "../../const/http.ts"; +import HTTPErrorResponseFactory from "./HTTPErrorResponseFactory.ts"; +import HTTPError from "../../error/HTTPError.ts"; export function json(value: any): JSONResponseFactory { return make(JSONResponseFactory, value) @@ -26,3 +29,7 @@ export function dehydrate(value: Rehydratable): DehydratedStateResponseFactory { export function redirect(destination: string): TemporaryRedirectResponseFactory { return make(TemporaryRedirectResponseFactory, destination) } + +export function http(status: HTTPStatus, message?: string) { + return make(HTTPErrorResponseFactory, new HTTPError(status, message)) +}