144 lines
4.3 KiB
Go
144 lines
4.3 KiB
Go
package probs
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
)
|
|
|
|
// Error types that can be used in ACME payloads
|
|
const (
|
|
ConnectionProblem = ProblemType("urn:acme:error:connection")
|
|
MalformedProblem = ProblemType("urn:acme:error:malformed")
|
|
ServerInternalProblem = ProblemType("urn:acme:error:serverInternal")
|
|
TLSProblem = ProblemType("urn:acme:error:tls")
|
|
UnauthorizedProblem = ProblemType("urn:acme:error:unauthorized")
|
|
UnknownHostProblem = ProblemType("urn:acme:error:unknownHost")
|
|
RateLimitedProblem = ProblemType("urn:acme:error:rateLimited")
|
|
BadNonceProblem = ProblemType("urn:acme:error:badNonce")
|
|
)
|
|
|
|
// ProblemType defines the error types in the ACME protocol
|
|
type ProblemType string
|
|
|
|
// ProblemDetails objects represent problem documents
|
|
// https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
|
|
type ProblemDetails struct {
|
|
Type ProblemType `json:"type,omitempty"`
|
|
Detail string `json:"detail,omitempty"`
|
|
// HTTPStatus is the HTTP status code the ProblemDetails should probably be sent
|
|
// as.
|
|
HTTPStatus int `json:"status,omitempty"`
|
|
}
|
|
|
|
func (pd *ProblemDetails) Error() string {
|
|
return fmt.Sprintf("%s :: %s", pd.Type, pd.Detail)
|
|
}
|
|
|
|
// statusTooManyRequests is the HTTP status code meant for rate limiting
|
|
// errors. It's not currently in the net/http library so we add it here.
|
|
const statusTooManyRequests = 429
|
|
|
|
// ProblemDetailsToStatusCode inspects the given ProblemDetails to figure out
|
|
// what HTTP status code it should represent. It should only be used by the WFE
|
|
// but is included in this package because of its reliance on ProblemTypes.
|
|
func ProblemDetailsToStatusCode(prob *ProblemDetails) int {
|
|
if prob.HTTPStatus != 0 {
|
|
return prob.HTTPStatus
|
|
}
|
|
switch prob.Type {
|
|
case ConnectionProblem, MalformedProblem, TLSProblem, UnknownHostProblem, BadNonceProblem:
|
|
return http.StatusBadRequest
|
|
case ServerInternalProblem:
|
|
return http.StatusInternalServerError
|
|
case UnauthorizedProblem:
|
|
return http.StatusForbidden
|
|
case RateLimitedProblem:
|
|
return statusTooManyRequests
|
|
default:
|
|
return http.StatusInternalServerError
|
|
}
|
|
}
|
|
|
|
// BadNonce returns a ProblemDetails with a BadNonceProblem and a 400 Bad
|
|
// Request status code.
|
|
func BadNonce(detail string) *ProblemDetails {
|
|
return &ProblemDetails{
|
|
Type: BadNonceProblem,
|
|
Detail: detail,
|
|
HTTPStatus: http.StatusBadRequest,
|
|
}
|
|
}
|
|
|
|
// Conflict returns a ProblemDetails with a MalformedProblem and a 409 Conflict
|
|
// status code.
|
|
func Conflict(detail string) *ProblemDetails {
|
|
return &ProblemDetails{
|
|
Type: MalformedProblem,
|
|
Detail: detail,
|
|
HTTPStatus: http.StatusConflict,
|
|
}
|
|
}
|
|
|
|
// Malformed returns a ProblemDetails with a MalformedProblem and a 400 Bad
|
|
// Request status code.
|
|
func Malformed(detail string, args ...interface{}) *ProblemDetails {
|
|
if len(args) > 0 {
|
|
detail = fmt.Sprintf(detail, args...)
|
|
}
|
|
return &ProblemDetails{
|
|
Type: MalformedProblem,
|
|
Detail: detail,
|
|
HTTPStatus: http.StatusBadRequest,
|
|
}
|
|
}
|
|
|
|
// NotFound returns a ProblemDetails with a MalformedProblem and a 404 Not Found
|
|
// status code.
|
|
func NotFound(detail string) *ProblemDetails {
|
|
return &ProblemDetails{
|
|
Type: MalformedProblem,
|
|
Detail: detail,
|
|
HTTPStatus: http.StatusNotFound,
|
|
}
|
|
}
|
|
|
|
// ServerInternal returns a ProblemDetails with a ServerInternalProblem and a
|
|
// 500 Internal Server Failure status code.
|
|
func ServerInternal(detail string) *ProblemDetails {
|
|
return &ProblemDetails{
|
|
Type: ServerInternalProblem,
|
|
Detail: detail,
|
|
HTTPStatus: http.StatusInternalServerError,
|
|
}
|
|
}
|
|
|
|
// Unauthorized returns a ProblemDetails with an UnauthorizedProblem and a 403
|
|
// Forbidden status code.
|
|
func Unauthorized(detail string) *ProblemDetails {
|
|
return &ProblemDetails{
|
|
Type: UnauthorizedProblem,
|
|
Detail: detail,
|
|
HTTPStatus: http.StatusForbidden,
|
|
}
|
|
}
|
|
|
|
// MethodNotAllowed returns a ProblemDetails representing a disallowed HTTP
|
|
// method error.
|
|
func MethodNotAllowed() *ProblemDetails {
|
|
return &ProblemDetails{
|
|
Type: MalformedProblem,
|
|
Detail: "Method not allowed",
|
|
HTTPStatus: http.StatusMethodNotAllowed,
|
|
}
|
|
}
|
|
|
|
// ContentLengthRequired returns a ProblemDetails representing a missing
|
|
// Content-Length header error
|
|
func ContentLengthRequired() *ProblemDetails {
|
|
return &ProblemDetails{
|
|
Type: MalformedProblem,
|
|
Detail: "missing Content-Length header",
|
|
HTTPStatus: http.StatusLengthRequired,
|
|
}
|
|
}
|