2021-09-29 15:38:50 +00:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
2024-07-03 14:12:13 +00:00
|
|
|
"maps"
|
|
|
|
"net/http"
|
|
|
|
"slices"
|
|
|
|
|
|
|
|
builtinTemplates "gh.tarampamp.am/error-pages/templates"
|
2021-09-29 15:38:50 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
2024-07-03 14:12:13 +00:00
|
|
|
// Templates hold all templates, with the key being the template name and the value being the template content
|
|
|
|
// in HTML format (Go templates are supported here).
|
|
|
|
Templates templates
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// Formats contain alternative response formats (e.g., if a client requests a response in one of these formats,
|
|
|
|
// we will render the response using the specified format instead of HTML; Go templates are supported).
|
|
|
|
Formats struct {
|
|
|
|
JSON string
|
|
|
|
XML string
|
|
|
|
PlainText string
|
2022-01-27 12:29:49 +00:00
|
|
|
}
|
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// Codes hold descriptions for HTTP codes (e.g., 404: "Not Found / The server can not find the requested page").
|
|
|
|
Codes Codes
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// TemplateName is the name of the template to use for rendering error pages. The template must be present in the
|
|
|
|
// Templates map.
|
|
|
|
TemplateName string
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// ProxyHeaders contains a list of HTTP headers that will be proxied from the incoming request to the
|
|
|
|
// error page response.
|
|
|
|
ProxyHeaders []string
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// L10n contains localization settings.
|
|
|
|
L10n struct {
|
|
|
|
// Disable the localization of error pages.
|
|
|
|
Disable bool
|
2022-01-27 12:29:49 +00:00
|
|
|
}
|
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// DefaultCodeToRender is the code for the default error page to be displayed. It is used when the requested
|
|
|
|
// code is not defined in the incoming request (i.e., the code to render as the index page).
|
|
|
|
DefaultCodeToRender uint16
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// RespondWithSameHTTPCode determines whether the response should have the same HTTP status code as the requested
|
|
|
|
// error page.
|
|
|
|
// In other words, if set to true and the requested error page has a code of 404, the HTTP response will also have
|
|
|
|
// a status code of 404. If set to false, the HTTP response will have a status code of 200 regardless of the
|
|
|
|
// requested error page's status code.
|
|
|
|
RespondWithSameHTTPCode bool
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// RotationMode allows to set the rotation mode for templates to switch between them automatically on startup,
|
|
|
|
// on each request, daily, hourly and so on.
|
|
|
|
RotationMode RotationMode
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// ShowDetails determines whether to show additional details in the error response, extracted from the
|
|
|
|
// incoming request (if supported by the template).
|
|
|
|
ShowDetails bool
|
2024-07-05 14:59:07 +00:00
|
|
|
|
|
|
|
// DisableMinification determines whether to disable minification of the rendered content (e.g., HTML, CSS) or not.
|
|
|
|
DisableMinification bool
|
2022-01-27 12:29:49 +00:00
|
|
|
}
|
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
const defaultJSONFormat string = `{
|
|
|
|
"error": true,
|
|
|
|
"code": {{ code | json }},
|
|
|
|
"message": {{ message | json }},
|
|
|
|
"description": {{ description | json }}{{ if show_details }},
|
|
|
|
"details": {
|
|
|
|
"host": {{ host | json }},
|
|
|
|
"original_uri": {{ original_uri | json }},
|
|
|
|
"forwarded_for": {{ forwarded_for | json }},
|
|
|
|
"namespace": {{ namespace | json }},
|
|
|
|
"ingress_name": {{ ingress_name | json }},
|
|
|
|
"service_name": {{ service_name | json }},
|
|
|
|
"service_port": {{ service_port | json }},
|
|
|
|
"request_id": {{ request_id | json }},
|
|
|
|
"timestamp": {{ nowUnix }}
|
|
|
|
}{{ end }}
|
2022-01-27 12:29:49 +00:00
|
|
|
}
|
2024-07-03 14:12:13 +00:00
|
|
|
` // an empty line at the end is important for better UX
|
|
|
|
|
|
|
|
const defaultXMLFormat string = `<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
<error>
|
|
|
|
<code>{{ code }}</code>
|
|
|
|
<message>{{ message }}</message>
|
|
|
|
<description>{{ description }}</description>{{ if show_details }}
|
|
|
|
<details>
|
|
|
|
<host>{{ host }}</host>
|
|
|
|
<originalURI>{{ original_uri }}</originalURI>
|
|
|
|
<forwardedFor>{{ forwarded_for }}</forwardedFor>
|
|
|
|
<namespace>{{ namespace }}</namespace>
|
|
|
|
<ingressName>{{ ingress_name }}</ingressName>
|
|
|
|
<serviceName>{{ service_name }}</serviceName>
|
|
|
|
<servicePort>{{ service_port }}</servicePort>
|
|
|
|
<requestID>{{ request_id }}</requestID>
|
|
|
|
<timestamp>{{ nowUnix }}</timestamp>
|
|
|
|
</details>{{ end }}
|
|
|
|
</error>
|
|
|
|
` // an empty line at the end is important for better UX
|
|
|
|
|
|
|
|
const defaultPlainTextFormat string = `Error {{ code }}: {{ message }}{{ if description }}
|
|
|
|
{{ description }}{{ end }}{{ if show_details }}
|
|
|
|
|
|
|
|
Host: {{ host }}
|
|
|
|
Original URI: {{ original_uri }}
|
|
|
|
Forwarded For: {{ forwarded_for }}
|
|
|
|
Namespace: {{ namespace }}
|
|
|
|
Ingress Name: {{ ingress_name }}
|
|
|
|
Service Name: {{ service_name }}
|
|
|
|
Service Port: {{ service_port }}
|
|
|
|
Request ID: {{ request_id }}
|
|
|
|
Timestamp: {{ nowUnix }}{{ end }}
|
|
|
|
` // an empty line at the end is important for better UX
|
|
|
|
|
|
|
|
//nolint:lll
|
|
|
|
var defaultCodes = Codes{ //nolint:gochecknoglobals
|
|
|
|
"400": {"Bad Request", "The server did not understand the request"},
|
|
|
|
"401": {"Unauthorized", "The requested page needs a username and a password"},
|
|
|
|
"403": {"Forbidden", "Access is forbidden to the requested page"},
|
|
|
|
"404": {"Not Found", "The server can not find the requested page"},
|
|
|
|
"405": {"Method Not Allowed", "The method specified in the request is not allowed"},
|
|
|
|
"407": {"Proxy Authentication Required", "You must authenticate with a proxy server before this request can be served"},
|
|
|
|
"408": {"Request Timeout", "The request took longer than the server was prepared to wait"},
|
|
|
|
"409": {"Conflict", "The request could not be completed because of a conflict"},
|
|
|
|
"410": {"Gone", "The requested page is no longer available"},
|
|
|
|
"411": {"Length Required", "The \"Content-Length\" is not defined. The server will not accept the request without it"},
|
|
|
|
"412": {"Precondition Failed", "The pre condition given in the request evaluated to false by the server"},
|
|
|
|
"413": {"Payload Too Large", "The server will not accept the request, because the request entity is too large"},
|
|
|
|
"416": {"Requested Range Not Satisfiable", "The requested byte range is not available and is out of bounds"},
|
|
|
|
"418": {"I'm a teapot", "Attempt to brew coffee with a teapot is not supported"},
|
|
|
|
"429": {"Too Many Requests", "Too many requests in a given amount of time"},
|
|
|
|
"500": {"Internal Server Error", "The server met an unexpected condition"},
|
|
|
|
"502": {"Bad Gateway", "The server received an invalid response from the upstream server"},
|
|
|
|
"503": {"Service Unavailable", "The server is temporarily overloading or down"},
|
|
|
|
"504": {"Gateway Timeout", "The gateway has timed out"},
|
|
|
|
"505": {"HTTP Version Not Supported", "The server does not support the \"http protocol\" version"},
|
2022-01-27 12:29:49 +00:00
|
|
|
}
|
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
var defaultProxyHeaders = []string{ //nolint:gochecknoglobals
|
|
|
|
// "Traceparent", // W3C Trace Context
|
|
|
|
// "Tracestate", // W3C Trace Context
|
|
|
|
"X-Request-Id", // unofficial HTTP header, used to trace individual HTTP requests
|
|
|
|
"X-Trace-Id", // same as above
|
|
|
|
"X-Amzn-Trace-Id", // to track HTTP requests from clients to targets or other AWS services
|
2021-09-29 15:38:50 +00:00
|
|
|
}
|
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// New creates a new configuration with default values.
|
|
|
|
func New() Config {
|
|
|
|
var cfg = Config{
|
|
|
|
Templates: make(templates), // allocate memory for templates
|
|
|
|
Codes: maps.Clone(defaultCodes), // copy default codes
|
2021-09-29 15:38:50 +00:00
|
|
|
}
|
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
cfg.Formats.JSON = defaultJSONFormat
|
|
|
|
cfg.Formats.XML = defaultXMLFormat
|
|
|
|
cfg.Formats.PlainText = defaultPlainTextFormat
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// add built-in templates
|
|
|
|
for name, content := range builtinTemplates.BuiltIn() {
|
|
|
|
cfg.Templates[name] = content
|
2022-01-27 12:29:49 +00:00
|
|
|
}
|
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// set first template as default
|
|
|
|
for _, name := range cfg.Templates.Names() {
|
|
|
|
cfg.TemplateName = name
|
2021-09-29 15:38:50 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
break
|
2021-09-29 15:38:50 +00:00
|
|
|
}
|
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// set default HTTP headers to proxy
|
|
|
|
cfg.ProxyHeaders = slices.Clone(defaultProxyHeaders)
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
// set defaults
|
|
|
|
cfg.DefaultCodeToRender = http.StatusNotFound
|
2022-01-27 12:29:49 +00:00
|
|
|
|
2024-07-03 14:12:13 +00:00
|
|
|
return cfg
|
2021-09-29 15:38:50 +00:00
|
|
|
}
|