diff --git a/smalltrip/exception/400.go b/smalltrip/exception/400.go deleted file mode 100644 index 2c513da..0000000 --- a/smalltrip/exception/400.go +++ /dev/null @@ -1,913 +0,0 @@ -// Copyright 2025-present Gustavo "Guz" L. de Mello -// Copyright 2025-present The Lored.dev Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package exception - -import ( - "errors" - "fmt" - "net/http" - "strings" -) - -// BadRequest creates a new [Exception] with the "400 Bad Request" status code, -// a human readable message and the provided error describing what in the request -// was wrong. The severity of this Exception by default is [WARN]. -// -// // "The HTTP 400 Bad Request client error response status code indicates that the -// // server would not process the request due to something the server considered to -// // be a client error. The reason for a 400 response is typically due to malformed -// // request syntax, invalid request message framing, or deceptive request routing. -// // -// // Clients that receive a 400 response should expect that repeating the request -// // without modification will fail with the same error." -// // -// // - Quoted from "400 Bad Request" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400 -func BadRequest(err error, opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusBadRequest), - WithCode("Bad Request"), - WithMessage("The request sent is malformed, see the provided error for more information."), - WithError(err), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// Unathorized creates a new [Exception] with the "401 Unathorized" status code, -// a human readable message and error. The severity of this Exception by default -// is [WARN]. A "WWW-Authenticate" header should be sent with this exception, -// provided via the "authenticate" parameter. -// -// // "The HTTP 401 Unauthorized client error response status code indicates that a -// // request was not successful because it lacks valid authentication credentials -// // for the requested resource. This status code is sent with an HTTP WWW-Authenticate -// // response header that contains information on the authentication scheme the -// // server expects the client to include to make the request successfully. -// // -// // A 401 Unauthorized is similar to the 403 Forbidden response, except that a 403 -// // is returned when a request contains valid credentials, but the client does not -// // have permissions to perform a certain action." -// // -// // - Quoted from "401 Unathorized" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 -func Unathorized(authenticate string, opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusUnauthorized), - WithCode("Unathorized"), - WithMessage("Not authorized/authenticated to be able to do this request."), - WithError(errors.New("user agent is not authenticated to do the request")), - WithSeverity(WARN), - - WithHeader("WWW-Authenticate", authenticate), - } - o = append(o, opts...) - - return newException(o...) -} - -// PaymentRequired creates a new [Exception] with the "402 Payment Required" status code, -// a human readable message and error. The severity of this Exception by default -// is [WARN]. -// -// // "The HTTP 402 Payment Required client error response status code is a nonstandard -// // response status code reserved for future use. -// // -// // This status code was created to enable digital cash or (micro) payment systems -// // and would indicate that requested content is not available until the client makes -// // a payment. No standard use convention exists and different systems use it in -// // different contexts. -// // -// // [...] -// // -// // This status code is reserved but not defined. Actual implementations vary in -// // the format and contents of the response. No browser supports a 402, and an error -// // will be displayed as a generic 4xx status code." -// // -// // - Quoted from "402 Payment Required" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402 -func PaymentRequired(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusPaymentRequired), - WithCode("Payment Required"), - WithMessage("Payment is required to be able to see this page."), - WithError(errors.New("user agent needs to have payed to see this content")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// Forbidden creates a new [Exception] with the "403 Forbidden" status code, -// a human readable message and error. The severity of this Exception by default -// is [WARN]. -// -// // "The HTTP 403 Forbidden client error response status code indicates that the -// // server understood the request but refused to process it. This status is similar -// // to 401, except that for 403 Forbidden responses, authenticating or -// // re-authenticating makes no difference. The request failure is tied to application -// // logic, such as insufficient permissions to a resource or action. -// // -// // Clients that receive a 403 response should expect that repeating the request -// // without modification will fail with the same error. Server owners may decide -// // to send a 404 response instead of a 403 if acknowledging the existence of a -// // resource to clients with insufficient privileges is not desired." -// // -// // - Quoted from "403 Forbidden" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403 -func Forbidden(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusForbidden), - WithCode("Forbidden"), - WithMessage("You do not have the rights to do this request."), - WithError(errors.New("user agent does not have the rights to do the request")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// NotFound creates a new [Exception] with the "404 Not Found" status code, -// a human readable message and error. The severity of this Exception by default -// is [WARN]. -// -// // "The HTTP 404 Not Found client error response status code indicates that the -// // server cannot find the requested resource. Links that lead to a 404 page are -// // often called broken or dead links and can be subject to link rot. -// // -// // A 404 status code only indicates that the resource is missing without indicating -// // if this is temporary or permanent. If a resource is permanently removed, servers -// // should send the 410 Gone status instead. -// // -// // 404 errors on a website can lead to a poor user experience for your visitors, -// // so the number of broken links (internal and external) should be minimized to -// // prevent frustration for readers. Common causes of 404 responses are mistyped -// // URLs or pages that are moved or deleted without redirection. For more -// // information, see the Redirections in HTTP guide." -// // -// // - Quoted from "404 Not Found" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404 -func NotFound(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusNotFound), - WithCode("Not Found"), - WithMessage("This content does not exists."), - WithError(errors.New("user agent requested content that does not exists")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// MethodNotAllowed creates a new [Exception] with the "405 Method Not Allowed" status code, -// a human readable message and error, and a "Allow" header with the methods provided via the -// "allowed" parameter. The severity of this Exception by default is [WARN]. -// -// // "The HTTP 405 Method Not Allowed client error response status code indicates -// // that the server knows the request method, but the target resource doesn't support -// // this method. The server must generate an Allow header in a 405 response with a -// // list of methods that the target resource currently supports. -// // -// // Improper server-side permissions set on files or directories may cause a 405 -// // response when the request would otherwise be expected to succeed." -// // -// // - Quoted from "405 Method Not Allowed" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 -func MethodNotAllowed(allowed []string, opts ...Option) Exception { - a := strings.Join(allowed, ", ") - o := []Option{ - WithStatus(http.StatusMethodNotAllowed), - WithCode("Method Not Allowed"), - WithMessage("The method is not allowed for this endpoints."), - WithError(fmt.Errorf("user agent tried to use method which is not a allowed method (allowed: %s)", a)), - WithSeverity(WARN), - - WithHeader("Allow", a), - } - o = append(o, opts...) - - return newException(o...) -} - -// NotAcceptable creates a new [Exception] with the "406 Not Acceptable" status code, -// a human readable message and error, and a list of accepted mime types provided via -// the "accepted" parameter. The severity of this Exception by default is [WARN]. -// -// // "The HTTP 406 Not Acceptable client error response status code indicates that the -// // server could not produce a response matching the list of acceptable values defined -// // in the request's proactive content negotiation headers and that the server was -// // unwilling to supply a default representation. -// // -// // [...] -// // -// // A server may return responses that differ from the request's accept headers. -// // In such cases, a 200 response with a default resource that doesn't match the client's -// // list of acceptable content negotiation values may be preferable to sending a 406 response. -// // -// // If a server returns a 406, the body of the message should contain the list of -// // available representations for the resource, allowing the user to choose, although -// // no standard way for this is defined." -// // -// // - Quoted from "406 Not Acceptable" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406 -func NotAcceptable(accepted []string, opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusNotAcceptable), - WithCode("Not Acceptable"), - WithMessage("Unable to find any content that conforms to the request."), - WithError(errors.New("no content conforms to the requested criteria of the user agent")), - WithData("accepted", accepted), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// ProxyAuthenticationRequired creates a new [Exception] with the "407 Proxy Authentication Required" -// status code, a human readable message and error. The severity of this Exception by default is [WARN]. -// A "Proxy-Authenticate" header should be sent with this exception, provided via the -// "authenticate" parameter. -// -// // "The HTTP 407 Proxy Authentication Required client error response status code indicates that -// // the request did not succeed because it lacks valid authentication credentials for the proxy -// // server that sits between the client and the server with access to the requested resource. -// // -// // This response is sent with a Proxy-Authenticate header that contains information on how to -// // correctly authenticate requests. The client may repeat the request with a new or replaced -// // Proxy-Authorization header field." -// // -// // - Quoted from "407 Proxy Authentication Required" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/407 -func ProxyAuthenticationRequired(authenticate string, opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusProxyAuthRequired), - WithCode("Proxy Authentication Required"), - WithMessage("Authorization/authentication via proxy is needed to access this content."), - WithError(errors.New("user agent is missing proxy authorization/authentication")), - WithSeverity(WARN), - - WithHeader("Proxy-Authenticate", authenticate), - } - o = append(o, opts...) - - return newException(o...) -} - -// RequestTimeout creates a new [Exception] with the "408 Request Timeout" status code, a human -// readable message and error, with a "Connection: close" header alongside. The severity of this -// Exception by default is [WARN]. -// -// // "The HTTP 408 Request Timeout client error response status code indicates that the server -// // would like to shut down this unused connection. A 408 is sent on an idle connection by some -// // servers, even without any previous request by the client. -// // -// // A server should send the Connection: close header field in the response, since 408 implies -// // that the server has decided to close the connection rather than continue waiting. -// // -// // This response is used much more since some browsers, like Chrome and Firefox, use HTTP -// // pre-connection mechanisms to speed up surfing." -// // -// // - Quoted from "408 Request Timeout" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408 -func RequestTimeout(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusRequestTimeout), - WithCode("Request Timeout"), - WithMessage("This request was shut down."), - WithError(errors.New("request timed out")), - WithSeverity(WARN), - - WithHeader("Connection", "close"), - } - o = append(o, opts...) - - return newException(o...) -} - -// Conflict creates a new [Exception] with the "409 Conflict" status code, a human -// readable message and error. The severity of this Exception by default is [WARN]. -// -// // "The HTTP 409 Conflict client error response status code indicates a request -// // conflict with the current state of the target resource. -// // -// // In WebDAV remote web authoring, 409 conflict responses are errors sent to the -// // client so that a user might be able to resolve a conflict and resubmit the -// // request. [...] Additionally, you may get a 409 response when uploading a file -// // that is older than the existing one on the server, resulting in a version -// // control conflict. -// // -// // In other systems, 409 responses may be used for implementation-specific purposes, -// // such as to indicate that the server has received multiple requests to update -// // the same resource." -// // -// // - Quoted from "409 Conflict" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409 -func Conflict(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusConflict), - WithCode("Conflict"), - WithMessage("Request conflicts with the current state of the server."), - WithError(errors.New("user agent sent a request which conflicts with the current state")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// Gone creates a new [Exception] with the "410 Gone" status code, a human -// readable message and error. The severity of this Exception by default is [WARN]. -// -// // "The HTTP 410 Gone client error response status code indicates that the target -// // resource is no longer available at the origin server and that this condition -// // is likely to be permanent. A 410 response is cacheable by default. -// // -// // Clients should not repeat requests for resources that return a 410 response, -// // and website owners should remove or replace links that return this code. -// // If server owners don't know whether this condition is temporary or permanent, Exception -// // a 404 status code should be used instead." -// // -// // - Quoted from "410 Gone" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410 -func Gone(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusGone), - WithCode("Gone"), - WithMessage("The requested content has been permanently deleted."), - WithError(errors.New("user agent has requested content that has been permanently deleted")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// LengthRequired creates a new [Exception] with the "411 Length Required" status -// code, a human readable message and error. The severity of this Exception by -// default is [WARN]. -// -// // "The HTTP 411 Length Required client error response status code indicates that -// // the server refused to accept the request without a defined Content-Length header." -// // -// // - Quoted from "411 Length Required" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/411 -func LengthRequired(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusLengthRequired), - WithCode("Length Required"), - WithMessage(`The request does not contain a "Content-Length" header, which is required.`), - WithError(errors.New(`user agent has requested without required "Content-Length" header`)), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// PreconditionFailed creates a new [Exception] with the "412 Precondition Failed" -// status code, a human readable message and error. The severity of this Exception -// by default is [WARN]. -// -// // "The HTTP 412 Precondition Failed client error response status code indicates -// // that access to the target resource was denied. This happens with conditional -// // requests on methods other than GET or HEAD when the condition defined by the -// // If-Unmodified-Since or If-Match headers is not fulfilled. In that case, the -// // request (usually an upload or a modification of a resource) cannot be made -// // and this error response is sent back." -// // -// // - Quoted from "412 Precondition Failed" by Mozilla Contributors, licensed -// // under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412 -func PreconditionFailed(err error, opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusPreconditionFailed), - WithCode("Precondition Failed"), - WithMessage("The request does passes required preconditions."), - WithError(err), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// ContentTooLarge creates a new [Exception] with the "413 Content Too Large" -// status code, a human readable message and error. The severity of this -// Exception by default is [WARN]. -// -// // "The HTTP 413 Content Too Large client error response status code indicates -// // that the request entity was larger than limits defined by server. The server -// // might close the connection or return a Retry-After header field." -// // -// // - Quoted from "413 Content Too Large" by Mozilla Contributors, licensed -// // under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413 -func ContentTooLarge(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusRequestEntityTooLarge), - WithCode("Content Too Large"), - WithMessage("The request body is larger than expected."), - WithError(errors.New("user agent sent request body that is larger than expected")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// URITooLong creates a new [Exception] with the "414 URI Too Long" -// status code, a human readable message and error. The severity of this -// Exception by default is [WARN]. -// -// // "The HTTP 414 URI Too Long client error response status code indicates -// // that a URI requested by the client was longer than the server is willing -// // to interpret. -// // -// // There are a few rare conditions when this error might occur: -// // -// // - a client has improperly converted a POST request to a GET request with -// // long query information, -// // -// // - a client has descended into a loop of redirection (for example, a -// // redirected URI prefix that points to a suffix of itself), or -// // -// // - the server is under attack by a client attempting to exploit potential -// // security holes." -// // -// // - Quoted from "414 URI Too Long" by Mozilla Contributors, licensed under -// // CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/414 -func URITooLong(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusRequestURITooLong), - WithCode("URI Too Long"), - WithMessage("The request has a URI longer than expected."), - WithError(errors.New("user agent sent request with URI longer than expected")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// UnsupportedMediaType creates a new [Exception] with the "415 Unsupported Media Type" -// status code, a human readable message and error. The severity of this Exception by -// default is [WARN]. -// -// // "The HTTP 415 Unsupported Media Type client error response status code indicates -// // that the server refused to accept the request because the message content format -// // is not supported. -// // -// // The format problem might be due to the request's indicated Content-Type or -// // Content-Encoding, or as a result of processing the request message content. Some -// // servers may be strict about the expected Content-Type of requests. For example, -// // sending UTF8 instead of UTF-8 to specify the UTF-8 charset may cause the server -// // to consider the media type invalid." -// // -// // - Quoted from "415 Unsupported Media Type" by Mozilla Contributors, licensed -// // under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/415 -func UnsupportedMediaType(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusUnsupportedMediaType), - WithCode("Unsupported Media Type"), - WithMessage("The request unsupported media type."), - WithError(errors.New("user agent sent request with unsupported media type")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// RangeNotSatisfiable creates a new [Exception] with the "416 Range Not Satisfiable" -// status code, a human readable message and error. The severity of this Exception by -// default is [WARN]. A "Content-Range" header is sent with the provided number of -// bytes via the "contentRange" parameter. -// -// // "The HTTP 416 Range Not Satisfiable client error response status code indicates -// // that a server could not serve the requested ranges. The most likely reason for -// // this response is that the document doesn't contain such ranges, or that the -// // Range header value, though syntactically correct, doesn't make sense. -// // -// // The 416 response message should contain a Content-Range indicating an unsatisfied -// // range (that is a '*') followed by a '/' and the current length of the resource, -// // e.g., Content-Range: bytes */12777 -// // -// // When encountering this error, browsers typically either abort the operation -// // (for example, a download will be considered non-resumable) or request the whole -// // document again without ranges." -// // -// // - Quoted from "416 Range Not Satisfiable" by Mozilla Contributors, licensed -// // under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416 -func RangeNotSatisfiable(contentRange int, opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusUnsupportedMediaType), - WithCode("Range Not Satisfiable"), - WithMessage(`Request's "Range" header cannot be satified.`), - WithError(errors.New(`user agent sent request with unsitisfiable "Range" header`)), - WithSeverity(WARN), - - WithHeader("Content-Range", fmt.Sprintf("bytes */%d", contentRange)), - } - o = append(o, opts...) - - return newException(o...) -} - -// ExpectationFailed creates a new [Exception] with the "417 Expectation Failed" -// status code, a human readable message and error. The severity of this Exception -// by default is [WARN]. -// -// // "The HTTP 417 Expectation Failed client error response status code indicates -// // that the expectation given in the request's Expect header could not be met. -// // After receiving a 417 response, a client should repeat the request without an -// // Expect request header, including the file in the request body without waiting -// // for a 100 response. See the Expect header documentation for more details." -// // -// // - Quoted from "417 Expectation Failed" by Mozilla Contributors, licensed under -// // CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/417 -func ExpectationFailed(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusExpectationFailed), - WithCode("Exception Failed"), - WithMessage(`Request's "Expect" header cannot be met.`), - WithError(errors.New(`user agent sent request with unmetable "Expect" header`)), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// ImATeapot creates a new [Exception] with the "418 I'm a teapot" status code, -// a human readable message and error. The severity of this Exception by default -// is [WARN]. -// -// // "The HTTP 418 I'm a teapot status response code indicates that the server -// // refuses to brew coffee because it is, permanently, a teapot. A combined -// // coffee/tea pot that is temporarily out of coffee should instead return 503. -// // This error is a reference to Hyper Text Coffee Pot Control Protocol defined -// // in April Fools' jokes in 1998 and 2014. -// // -// // Some websites use this response for requests they do not wish to handle, -// // such as automated queries." -// // -// // - Quoted from "418 I'm a teapot" by Mozilla Contributors, licensed under -// // CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 -func ImATeapot(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusTeapot), - WithCode("I'm a teapot"), - WithMessage("The request to brew coffee with a teapot is impossible."), - WithError(errors.New("user agent tried to brew coffee with a teapot, an impossible request")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// MisdirectedRequest creates a new [Exception] with the "421 Misdirected Request" -// status code, a human readable message and error. The severity of this Exception -// by default is [WARN]. -// -// // "The HTTP 421 Misdirected Request client error response status code indicates -// // that the request was directed to a server that is not able to produce a response. -// // This can be sent by a server that is not configured to produce responses for the -// // combination of scheme and authority that are included in the request URI. -// // -// // Clients may retry the request over a different connection." -// // -// // - Quoted from "421 Misdirected Request" by Mozilla Contributors, licensed under -// // CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/421 -func MisdirectedRequest(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusMisdirectedRequest), - WithCode("Misdirected Request"), - WithMessage("The request was directed to a location unable to produce a response."), - WithError(errors.New("user agent requested on a location that is unable to produce a response.")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// UnprocessableContent creates a new [Exception] with the "422 Unprocessable Content" -// status code, a human readable message and error. The severity of this Exception -// by default is [WARN]. -// -// // "The HTTP 422 Unprocessable Content client error response status code indicates -// // that the server understood the content type of the request content, and the syntax -// // of the request content was correct, but it was unable to process the contained -// // instructions. -// // -// // Clients that receive a 422 response should expect that repeating the request -// // without modification will fail with the same error." -// // -// // - Quoted from "422 Unprocessable Content" by Mozilla Contributors, licensed -// // under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422 -func UnprocessableContent(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusUnprocessableEntity), - WithCode("Unprocessable Content"), - WithMessage("Unable to follow request due to semantic errors."), - WithError(errors.New("user agent sent request containing semantic errors.")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// Locked creates a new [Exception] with the "423 Locked" status code, a human -// readable message and error. The severity of this Exception by default is [WARN]. -// -// // "The HTTP 423 Locked client error response status code indicates that a -// // resource is locked, meaning it can't be accessed. Its response body should -// // contain information in WebDAV's XML format. -// // -// // NOTE: The ability to lock a resource to prevent conflicts is specific to -// // some WebDAV servers. Browsers accessing web pages will never encounter -// // this status code; in the erroneous cases it happens, they will handle -// // it as a generic 400 status code." -// // -// // - Quoted from "423 Locked" by Mozilla Contributors, licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/423 -func Locked(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusUnprocessableEntity), - WithCode("Locked"), - WithMessage("This resource is locked."), - WithError(errors.New("user agent requested a locked resource")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// FailedDependency creates a new [Exception] with the "424 Failed Dependency" -// status code, a human readable message and error. The severity of this -// Exception by default is [WARN]. -// -// // "The HTTP 424 Failed Dependency client error response status code -// // indicates that the method could not be performed on the resource -// // because the requested action depended on another action, and that -// // action failed. -// // -// // Regular web servers typically do not return this status code, but -// // some protocols like WebDAV can return it. For example, in WebDAV, -// // if a PROPPATCH request was issued, and one command fails then -// // automatically every other command will also fail with -// // 424 Failed Dependency." -// // -// // - Quoted from "424 Failed Dependency" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/424 -func FailedDependency(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusFailedDependency), - WithCode("Failed Dependency"), - WithMessage("Cannot respond due to failure of a previous request."), - WithError(errors.New("request cannot be due to failure of a previous request")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// TooEarly creates a new [Exception] with the "425 Too Early" status code, -// a human readable message and error. The severity of this Exception by -// default is [WARN]. -// -// // "LIMITED AVAILABILITY -// // -// // The HTTP 425 Too Early client error response status code indicates -// // that the server was unwilling to risk processing a request that might -// // be replayed to avoid potential replay attacks. -// // -// // If a client has interacted with a server recently, early data -// // (also known as zero round-trip time (0-RTT) data) allows the client -// // to send data to a server in the first round trip of a connection, -// // without waiting for the TLS handshake to complete. A client that -// // sends a request in early data does not need to include the -// // Early-Data header." -// // -// // - Quoted from "425 Too Early" by Mozilla Contributors, licensed under -// // CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/425 -func TooEarly(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusTooEarly), - WithCode("Too Early"), - WithMessage("Unwilling to process the request to avoid replay attacks."), - WithError(errors.New("request was rejected to avoid replay attacks")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// UpgradeRequired creates a new [Exception] with the "426 Upgrade Required" -// status code, a human readable message and error. The severity of this -// Exception by default is [WARN]. A "Upgrade" header is sent with the value -// provided by the "upgrade" parameter. -// -// // "The HTTP 426 Upgrade Required client error response status code -// // indicates that the server refused to perform the request using the -// // current protocol but might be willing to do so after the client -// // upgrades to a different protocol. -// // -// // The server sends an Upgrade header with this response to indicate -// // the required protocol(s)." -// // -// // - Quoted from "426 Upgrade Required" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/426 -func UpgradeRequired(upgrade string, opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusUpgradeRequired), - WithCode("Upgrade Required"), - WithMessage("An upgrade to the protocol is required to do this request."), - WithError(fmt.Errorf("user agent needs to upgrade to protocol %q", upgrade)), - WithHeader("Upgrade", upgrade), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// PreconditionRequired creates a new [Exception] with the "428 Precondition Required" -// status code, a human readable message and error. The severity of this Exception -// by default is [WARN]. -// -// // "The HTTP 428 Precondition Required client error response status code indicates -// // that the server requires the request to be conditional. -// // -// // Typically, a 428 response means that a required precondition header such as -// // If-Match is missing. When a precondition header does not match the server-side -// // state, the response should be 412 Precondition Failed." -// // -// // - Quoted from "428 Precondition Required" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/426 -func PreconditionRequired(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusPreconditionRequired), - WithCode("Precondition Required"), - WithMessage("The request needs to be conditional."), - WithError(errors.New("user agent sent request that is not conditional")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// TooManyRequests creates a new [Exception] with the "429 Too Many Requests" -// status code, a human readable message and error. The severity of this -// Exception by default is [WARN]. -// -// To provide a "Retry-After" header, use the [WithHeader] option function. -// -// // "The HTTP 429 Too Many Requests client error response status code -// // indicates the client has sent too many requests in a given amount -// // of time. This mechanism of asking the client to slow down the rate -// // of requests is commonly called "rate limiting". -// // -// // A Retry-After header may be included to this response to indicate -// // how long a client should wait before making the request again." -// // -// // - Quoted from "429 Too Many Requests" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429 -func TooManyRequests(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusTooManyRequests), - WithCode("Too Many Requests"), - WithMessage("Too many requests were sent in the span of a short time."), - WithError(errors.New("user agent sent too many requests")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// RequestHeaderFieldsTooLarge creates a new [Exception] with the -// "431 Request Header Fields Too Large" status code, a human readable -// message and error. The severity of this Exception by default is [WARN]. -// -// // "The HTTP 431 Request Header Fields Too Large client error response -// // status code indicates that the server refuses to process the request -// // because the request's HTTP headers are too long. The request may -// // be resubmitted after reducing the size of the request headers. -// // -// // 431 can be used when the total size of request headers is too large -// // or when a single header field is too large. To help clients running -// // into this error, indicate which of the two is the problem in the -// // response body and, ideally, say which headers are too large. This -// // lets people attempt to fix the problem, such as by clearing cookies. -// // -// // Servers will often produce this status if: -// // - The Referer URL is too long -// // - There are too many Cookies in the request" -// // -// // - Quoted from "431 Request Header Fields Too Large" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431 -func RequestHeaderFieldsTooLarge(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusRequestHeaderFieldsTooLarge), - WithCode("Request Header Fields Too Large"), - WithMessage("Headers fields are larger than expected."), - WithError(errors.New("user agent sent header fields larger than expected")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} - -// UnavailableForLegalReasons creates a new [Exception] with the -// "451 Unavailable For Legal Reasons" status code, a human readable -// message and error. The severity of this Exception by default is [WARN]. -// -// // "The HTTP 451 Unavailable For Legal Reasons client error response -// // status code indicates that the user requested a resource that is -// // not available due to legal reasons, such as a web page for which -// // a legal action has been issued." -// // -// // - Quoted from "451 Unavailable For Legal Reasons" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/451 -func UnavailableForLegalReasons(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusUnavailableForLegalReasons), - WithCode("Unavailable For Legal Reasons"), - WithMessage("Content cannot be legally be provided."), - WithError(errors.New("user agent requested content that cannot be legally provided")), - WithSeverity(WARN), - } - o = append(o, opts...) - - return newException(o...) -} diff --git a/smalltrip/exception/500.go b/smalltrip/exception/500.go deleted file mode 100644 index a8613fe..0000000 --- a/smalltrip/exception/500.go +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright 2025-present Gustavo "Guz" L. de Mello -// Copyright 2025-present The Lored.dev Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package exception - -import ( - "errors" - "net/http" - "time" -) - -// InternalServerError creates a new [Exception] with the "500 Internal Server Error" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// An error should be provided to add context to the exception. -// -// // "The HTTP 500 Internal Server Error server error response status -// // code indicates that the server encountered an unexpected condition -// // that prevented it from fulfilling the request. This error is a generic -// // "catch-all" response to server issues, indicating that the server -// // cannot find a more appropriate 5XX error to respond with." -// // -// // - Quoted from "500 Internal Server Error" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502 -func InternalServerError(err error, opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusInternalServerError), - WithCode("Internal Server Error"), - WithMessage("A unexpected error occurred."), - WithError(err), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} - -// NotImplemented creates a new [Exception] with the "501 Not Implemented" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// // "The HTTP 501 Not Implemented server error response status -// // code means that the server does not support the functionality -// // required to fulfill the request. -// // -// // A response with this status may also include a Retry-After header, -// // telling the client that they can retry the request after the specified -// // time has elapsed. A 501 response is cacheable by default unless -// // caching headers instruct otherwise. -// // -// // 501 is the appropriate response when the server does not recognize -// // the request method and is incapable of supporting it for any resource. -// // Servers are required to support GET and HEAD, and therefore must not -// // return 501 in response to requests with these methods. If the server -// // does recognize the method, but intentionally does not allow it, the -// // appropriate response is 405 Method Not Allowed." -// // -// // - Quoted from "501 Not Implemented" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502 -func NotImplemented(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusNotImplemented), - WithCode("Not Implemented"), - WithMessage("Functionality is not supported."), - WithError(errors.New("user agent requested functionality that is not supported")), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} - -// BadGateway creates a new [Exception] with the "502 Bad Gateway" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// // "The HTTP 502 Bad Gateway server error response status code -// // indicates that a server was acting as a gateway or proxy and -// // that it received an invalid response from the upstream server. -// // -// // This response is similar to a 500 Internal Server Error response -// // in the sense that it is a generic "catch-call" for server errors. -// // The difference is that it is specific to the point in the request -// // chain that the error has occurred. If the origin server sends a -// // valid HTTP error response to the gateway, the response should be -// // passed on to the client instead of a 502 to make the failure -// // reason transparent. If the proxy or gateway did not receive any -// // HTTP response from the origin, it instead sends a -// // 504 Gateway Timeout to the client." -// // -// // - Quoted from "502 Bad Gateway" by Mozilla Contributors, licensed -// // under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502 -func BadGateway(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusBadGateway), - WithCode("Bad Gateway"), - WithMessage("Invalid response from upstream."), - WithError(errors.New("upstream response is invalid")), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} - -// ServiceUnavailable creates a new [Exception] with the "503 Service Unavailable" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// A Retry-After header is passed with the duration provided by the "retryAfter" -// parameter. -// -// // "The HTTP 503 Service Unavailable server error response status code -// // indicates that the server is not ready to handle the request. -// // -// // Common causes are that a server is down for maintenance or overloaded. -// // During maintenance, server administrators may temporarily route all traffic -// // to a 503 page, or this may happen automatically during software updates. -// // In overload cases, some server-side applications will reject requests with -// // a 503 status when resource thresholds like memory, CPU, or connection pool -// // limits are met. Dropping incoming requests creates backpressure that prevents -// // the server's compute resources from being exhausted, avoiding more severe -// // failures. If requests from specific clients are being restricted due to -// // rate limiting, the appropriate response is 429 Too Many Requests. -// // -// // This response should be used for temporary conditions and the Retry-After -// // HTTP header should contain the estimated time for the recovery of the -// // service, if possible. -// // -// // A user-friendly page explaining the problem should be sent along with -// // this response." -// // -// // - Quoted from "503 Service Unavailable" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503 -func ServiceUnavailable(retryAfter time.Time, opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusServiceUnavailable), - WithCode("Service Unavailable"), - WithMessage("Not ready to handle the request."), - WithError(errors.New("server is not ready to handle the request")), - WithSeverity(ERROR), - - WithHeader("Retry-After", retryAfter.Format("Mon, 02 Jan 2006 15:04:05 GMT")), - } - o = append(o, opts...) - - return newException(o...) -} - -// GatewayTimeout creates a new [Exception] with the "504 Gateway Timeout" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// // "The HTTP 504 Gateway Timeout server error response status code -// // indicates that the server, while acting as a gateway or proxy, -// // did not get a response in time from the upstream server in order -// // to complete the request. This is similar to a 502 Bad Gateway, -// // except that in a 504 status, the proxy or gateway did not receive -// // any HTTP response from the origin within a certain time. -// // -// // There are many causes of 504 errors, and fixing such problems -// // likely requires investigation and debugging by server administrators, -// // or the site may work again at a later time. Exceptions are client -// // networking errors, particularly if the service works for other -// // visitors, and if clients use VPNs or other custom networking setups. -// // In such cases, clients should check network settings, firewall setup, -// // proxy settings, DNS configuration, etc." -// // -// // - Quoted from "504 Gateway Timeout" by Mozilla Contributors, licensed -// // under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504 -func GatewayTimeout(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusGatewayTimeout), - WithCode("Gateway Timeout"), - WithMessage("Did not get the response from upstream in time."), - WithError(errors.New("upstream did not respond in time")), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} - -// HTTPVersionNotSupported creates a new [Exception] with the "505 HTTP Version Not Supported" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// // "The HTTP 505 HTTP Version Not Supported server error response status -// // code indicates that the HTTP version used in the request is not supported -// // by the server. -// // -// // It's common to see this error when a request line is improperly formed -// // such as GET /path to resource HTTP/1.1 or with \n terminating the request -// // line instead of \r\n." -// // -// // - Quoted from "505 HTTP Version Not Supported" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/505 -func HTTPVersionNotSupported(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusHTTPVersionNotSupported), - WithCode("HTTP Version Not Supported"), - WithMessage("Version of HTTP is not supported."), - WithError(errors.New("server does not support requested HTTP version")), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} - -// VariantAlsoNegotiates creates a new [Exception] with the "506 Variant Also Negotiates" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// // "The HTTP 506 Variant Also Negotiates server error response status code -// // is returned during content negotiation when there is recursive loop in -// // the process of selecting a resource. -// // -// // Agent-driven content negotiation enables a client and server to -// // collaboratively decide the best variant of a given resource when the server -// // has multiple variants. A server sends a 506 status code due to server -// // misconfiguration that results in circular references when creating responses." -// // -// // - Quoted from "506 Variant Also Negotiates" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/506 -func VariantAlsoNegotiates(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusVariantAlsoNegotiates), - WithCode("Variant Also Negotiates"), - WithMessage("A recursive loop found in the process of the request."), - WithError(errors.New("variant also negotiates, recursive loop found on request")), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} - -// InsufficientStorage creates a new [Exception] with the "507 Insufficient Storage" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// // "The HTTP 507 Insufficient Storage server error response status code -// // indicates that an action could not be performed because the server does -// // not have enough available storage to successfully complete the request. -// // -// // [...] Common causes of this error can be from server directories running -// // out of available space, not enough available RAM for an operation, or -// // internal limits reached (such as application-specific memory limits, -// // for example). The request causing this error does not necessarily need to -// // include content, as it may be a request that would create a resource on -// // the server if it was successful." -// // -// // - Quoted from "507 Insufficient Storage" by Mozilla Contributors, licensed under -// // CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/507 -func InsufficientStorage(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusInsufficientStorage), - WithCode("Insufficient Storage"), - WithMessage("There is not enough available storage to complete the request."), - WithError(errors.New("not enough available storage to complete request")), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} - -// LoopDetected creates a new [Exception] with the "508 Loop Detected" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// // "The HTTP 508 Loop Detected server error response status code indicates -// // that the entire operation failed because it encountered an infinite loop -// // while processing a request with Depth: infinity. -// // -// // The status may be given in the context of the Web Distributed Authoring -// // and Versioning (WebDAV). It was introduced as a fallback for cases where -// // WebDAV clients do not support 208 Already Reported responses (when requests -// // do not explicitly include a DAV header)." -// // -// // - Quoted from "508 Loop Detected" by Mozilla Contributors, licensed under -// // CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508 -func LoopDetected(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusLoopDetected), - WithCode("Loop Detected"), - WithMessage("Infinite loop found while processing the request."), - WithError(errors.New("infinite loop found while processing request")), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} - -// NotExtended creates a new [Exception] with the "510 Not Extended" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// // "The HTTP 510 Not Extended server error response status code is sent -// // when the client request declares an HTTP Extension (RFC 2774) that -// // should be used to process the request, but the extension is not supported." -// // -// // - Quoted from "510 Not Extended" by Mozilla Contributors, licensed under -// // CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/510 -func NotExtended(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusLoopDetected), - WithCode("Not Extended"), - WithMessage("HTTP extension is not supported."), - WithError(errors.New("user agent requested with a HTTP extension that is not supported")), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} - -// NetworkAuthenticationRequired creates a new [Exception] with the "511 Network Authentication Required" -// status code, a human readable message and the provided error describing what in -// the request was wrong. The severity of this Exception by default is [ERROR]. -// -// // "The HTTP 511 Network Authentication Required server error response status -// // code indicates that the client needs to authenticate to gain network access. -// // This status is not generated by origin servers, but by intercepting proxies -// // that control access to a network. -// // -// // Network operators sometimes require some authentication, acceptance of terms, -// // or other user interaction before granting access (for example in an internet -// // café or at an airport). They often identify clients who have not done so using -// // their Media Access Control (MAC) addresses." -// // -// // - Quoted from "511 Network Authentication Required" by Mozilla Contributors, -// // licensed under CC-BY-SA 2.5. -// // -// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/511 -func NetworkAuthenticationRequired(opts ...Option) Exception { - o := []Option{ - WithStatus(http.StatusNetworkAuthenticationRequired), - WithCode("Network Authentication Required"), - WithMessage("Authentication to access network access is necessary."), - WithError(errors.New("user agent requested without being network authenticated")), - WithSeverity(ERROR), - } - o = append(o, opts...) - - return newException(o...) -} diff --git a/smalltrip/exception/exceptions.go b/smalltrip/exception/exceptions.go deleted file mode 100644 index ca8dca9..0000000 --- a/smalltrip/exception/exceptions.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2025-present Gustavo "Guz" L. de Mello -// Copyright 2025-present The Lored.dev Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package exception - -import ( - "errors" - "fmt" - "net/http" -) - -type Exception struct { - Status int `json:"status"` // HTTP Status Code - Code string `json:"code"` // Application error code - Message string `json:"message"` // User friendly message - Err error `json:"error,omitempty"` // Go error - Data map[string]any `json:"data,omitempty"` // Additional contextual data - Severity Severity `json:"severity"` // Exception level - - // Handler to be used. This is normally provided by a middleware via the - // request context. Setting this field overrides any provided by the middleware - // and can be used to add a handler when using a middleware is not possible. - handler HandlerFunc `json:"-"` - - headers http.Header -} - -var ( - _ fmt.Stringer = Exception{} - _ error = Exception{} - _ http.Handler = Exception{} -) - -func (e Exception) String() string { - return fmt.Sprintf("%s %3d %s Exception %q", e.Severity, e.Status, e.Code, e.Message) -} - -func (e Exception) Error() string { - return e.String() -} - -func (e Exception) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if e.handler != nil { - e.handler(e, w, r) - } - - e.handler = HandlerJSON(HandlerText) - - handler, ok := r.Context().Value(handlerFuncCtxKey).(HandlerFunc) - if !ok { - e.handler(e, w, r) - return - } - - handler(e, w, r) -} - -func newException(options ...Option) Exception { - e := Exception{ - Status: http.StatusInternalServerError, - Code: "Internal Server Error", - Message: "", - Err: nil, - Severity: ERROR, - } - - for _, option := range options { - option(&e) - } - - return e -} - -type Option = func(*Exception) - -func WithStatus(s int) Option { - return func(e *Exception) { e.Status = s } -} - -func WithCode(c string) Option { - return func(e *Exception) { e.Code = c } -} - -func WithMessage(m string) Option { - return func(e *Exception) { e.Message = m } -} - -func WithError(err error, errs ...error) Option { - if len(errs) > 0 { - es := []error{err} - es = append(es, errs...) - err = errors.Join(es...) - } - return func(e *Exception) { e.Err = err } -} - -func WithSeverity(s Severity) Option { - return func(e *Exception) { e.Severity = s } -} - -func WithData(key string, v any) Option { - return func(e *Exception) { - if e.Data == nil { - e.Data = make(map[string]any) - } - e.Data[key] = v - } -} - -func WithHeader(header string, v string) Option { - return func(e *Exception) { - if e.headers == nil { - e.headers = http.Header{} - } - e.headers.Add(header, v) - } -} - -func WithoutHeader(header string) Option { - return func(e *Exception) { - if e.headers == nil { - e.headers = http.Header{} - } - e.headers.Del(header) - } -} - -func WithHandler(h HandlerFunc) Option { - return func(e *Exception) { e.handler = h } -} diff --git a/smalltrip/exception/middleware.go b/smalltrip/exception/middleware.go deleted file mode 100644 index 841044e..0000000 --- a/smalltrip/exception/middleware.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2025-present Gustavo "Guz" L. de Mello -// Copyright 2025-present The Lored.dev Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package exception - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "html/template" - "net/http" - "slices" - "strings" - - "forge.capytal.company/loreddev/x/smalltrip/middleware" -) - -func Middleware(options ...MiddlewareOption) middleware.Middleware { - opts := middlewareOpts{ - templates: make(map[int]*template.Template), - handlers: make(map[string]HandlerFunc), - defaultHandler: HandlerJSON(HandlerText), - } - - for _, option := range options { - option(&opts) - } - - if _, ok := opts.templates[0]; !ok { - opts.templates[0] = defaultTemplate - } - - if _, ok := opts.handlers["application/json"]; !ok { - opts.handlers["application/json"] = HandlerJSON(HandlerText) - } - if _, ok := opts.handlers["text/html"]; !ok { - opts.handlers["text/html"] = HandlerTemplates(opts.templates, opts.defaultHandler) - } - if _, ok := opts.handlers["application/xhtml+xml"]; !ok { - opts.handlers["application/xhtml+xml"] = HandlerTemplates(opts.templates, opts.defaultHandler) - } - if _, ok := opts.handlers["application/xml"]; !ok { - opts.handlers["application/xml"] = HandlerTemplates(opts.templates, opts.defaultHandler) - } - - return NewMiddleware(func(e Exception, w http.ResponseWriter, r *http.Request) { - for k, v := range opts.handlers { - if strings.Contains(r.Header.Get("Accept"), k) { - v(e, w, r) - return - } - } - opts.defaultHandler(e, w, r) - }) -} - -func PanicMiddleware() middleware.Middleware { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - defer func() { - if r := recover(); r != nil { - err := fmt.Errorf("panic recovered: %+v", r) - InternalServerError(err, WithSeverity(FATAL)).ServeHTTP(w, req) - } - }() - next.ServeHTTP(w, req) - }) - } -} - -var defaultTemplate = template.Must(template.New("xx-small-trip-default-Exception-template").Parse(` - - - - Smalltrip Exception - - -

Exception:

- - - -`)) - -type MiddlewareOption = func(*middlewareOpts) - -func MiddlewareTemplate(t *template.Template, statusCode ...int) MiddlewareOption { - return func(mo *middlewareOpts) { - if len(statusCode) > 0 { - mo.templates[statusCode[0]] = t - } else { - mo.templates[0] = t - } - } -} - -func MiddlewareHandler(h HandlerFunc, mimeType ...string) MiddlewareOption { - return func(mo *middlewareOpts) { - if len(mimeType) > 0 { - mo.handlers[mimeType[0]] = h - } else { - mo.defaultHandler = h - } - } -} - -type middlewareOpts struct { - templates map[int]*template.Template - handlers map[string]HandlerFunc - defaultHandler HandlerFunc -} - -func NewMiddleware(handler HandlerFunc) middleware.Middleware { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - r = r.WithContext(context.WithValue(r.Context(), handlerFuncCtxKey, handler)) - - mw := &middlewareReponseWriter{w, false} - - next.ServeHTTP(mw, r) - }) - } -} - -type middlewareReponseWriter struct { - http.ResponseWriter - done bool -} - -func (w *middlewareReponseWriter) WriteHeader(s int) { - if !w.done { - w.ResponseWriter.WriteHeader(s) - } - w.done = true -} - -const handlerFuncCtxKey = "xx-smalltrip-Exception-handler-func" - -type HandlerFunc = func(e Exception, w http.ResponseWriter, r *http.Request) - -func HandlerTemplates(ts map[int]*template.Template, fallback HandlerFunc) HandlerFunc { - return func(e Exception, w http.ResponseWriter, r *http.Request) { - if len(ts) == 0 { - fallback(e, w, r) - return - } - - t, ok := ts[e.Status] - if ok { - HandlerTemplate(t, fallback)(e, w, r) - return - } - - // Loops over ordered list and gets the last one that is small or equal - // the current Status. For example, if the current Exception has Status 404, - // and we provide a map like: - // - // map[int]*template.Template{ - // 100: Template100, - // 200: Template200, - // 300: Template300, - // 400: Template400, - // 500: Template500, - // } - // - // It will be converted to a ordered list of keys: 100, 200, 300, 400, 500. - // This loops iterates on all keys until a value bigger than the current Status - // is found (in this example, 500), then it uses the previous (in this example 400). - // - // So the 404 Exception will be rendered using the Template400. - - keys := make([]int, len(ts), len(ts)) - - var i int - for k := range ts { - keys[i] = k - i++ - } - - slices.Sort(keys) - - key := keys[0] - for _, k := range keys { - if k > e.Status { - break - } - key = k - } - - t, ok = ts[key] - if ok { - HandlerTemplate(t, fallback)(e, w, r) - return - } - - fallback(e, w, r) - } -} - -func HandlerTemplate(t *template.Template, fallback HandlerFunc) HandlerFunc { - return func(e Exception, w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/html") - for k := range e.headers { - w.Header().Set(k, e.headers.Get(k)) - } - - w.WriteHeader(e.Status) - - err := t.Execute(w, e) - if err != nil { - e.Err = errors.Join(fmt.Errorf("executing Exception template: %s", e.Error()), e.Err) - - fallback(e, w, r) - return - } - } -} - -func HandlerJSON(fallback HandlerFunc) HandlerFunc { - return func(e Exception, w http.ResponseWriter, r *http.Request) { - j, err := json.Marshal(e) - if err != nil { - e.Err = errors.Join(fmt.Errorf("marshalling Exception struct: %s", e.Error()), e.Err) - - fallback(e, w, r) - return - } - - w.Header().Set("Content-Type", "application/json") - for k := range e.headers { - w.Header().Set(k, e.headers.Get(k)) - } - - w.WriteHeader(e.Status) - - _, err = w.Write(j) - if err != nil { - e.Err = errors.Join(fmt.Errorf("writing JSON response to body: %s", e.Error()), e.Err) - - HandlerText(e, w, r) - return - } - } -} - -var _ HandlerFunc = HandlerJSON(HandlerText) - -func HandlerText(e Exception, w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain") - for k := range e.headers { - w.Header().Set(k, e.headers.Get(k)) - } - - w.WriteHeader(e.Status) - - _, err := w.Write([]byte(fmt.Sprintf( - "Status: %3d\n"+ - "Code: %s"+ - "Message: %s\n"+ - "Err: %s\n"+ - "Severity: %s\n\n"+ - "%+v\n\n"+ - "%#v", - e.Status, - e.Code, - e.Message, - e.Err, - e.Severity, - - e, e, - ))) - if err != nil { - _, _ = w.Write([]byte(fmt.Sprintf( - "Ok, what should we do at this point? You fucked up so bad that this message " + - "shouldn't even be able to be sent in the first place. If you are a normal user I'm " + - "so sorry for you to be reading this. If you're a developer, go fix your ResponseWriter " + - "implementation, because this should never happen in any normal codebase. " + - "I hope for the life of anyone you love you don't use this message in some " + - "error checking or any sort of API-contract, because there will be no more hope " + - "for you or your project. May God or any other or any other divinity that you may " + - "or may not believe be with you when trying to fix this mistake, you will need it.", - // If someone use this as part of the API-contract I'll not even be surprised. - // So any change to this message is still considered a breaking change. - ))) - } -} - -var _ HandlerFunc = HandlerText diff --git a/smalltrip/exception/severity.go b/smalltrip/exception/severity.go deleted file mode 100644 index bd68e50..0000000 --- a/smalltrip/exception/severity.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2025-present Gustavo "Guz" L. de Mello -// Copyright 2025-present The Lored.dev Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package exception - -import ( - "encoding" - "fmt" - "log/slog" -) - -type Severity int - -var ( - _ fmt.Stringer = (Severity)(0) - _ encoding.TextMarshaler = (Severity)(0) - _ encoding.TextUnmarshaler = (*Severity)(nil) -) - -const ( - DEBUG Severity = Severity(slog.LevelDebug) - INFO Severity = Severity(slog.LevelInfo) - WARN Severity = Severity(slog.LevelWarn) - ERROR Severity = Severity(slog.LevelError) - FATAL Severity = Severity(slog.LevelError * 2) - - stringDEBUG = "DEBUG" - stringINFO = "INFO" - stringWARN = "WARN" - stringERROR = "ERROR" - stringFATAL = "FATAL" - - stringUNDEFINED = "UNDEFINED" -) - -func (s Severity) String() string { - switch s { - case DEBUG: - return stringDEBUG - case INFO: - return stringINFO - case WARN: - return stringWARN - case ERROR: - return stringERROR - case FATAL: - return stringFATAL - default: - return fmt.Sprintf(stringUNDEFINED) - } -} - -func (s Severity) MarshalText() ([]byte, error) { - str := s.String() - if str == stringUNDEFINED { - return nil, fmt.Errorf("severity of value %q does not exists", s) - } - return []byte(str), nil -} - -func (s *Severity) UnmarshalText(text []byte) error { - switch string(text) { - case stringDEBUG: - *s = DEBUG - case stringINFO: - *s = INFO - case stringWARN: - *s = WARN - case stringERROR: - *s = ERROR - case stringFATAL: - *s = FATAL - default: - return fmt.Errorf("severity level %d does not exists", s) - } - return nil -}