Example #1
0
// Notify the client of an error condition and log it for audit purposes.
func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, msg string, detail interface{}, code int) {
	problem := core.ProblemDetails{Detail: msg}
	switch code {
	case http.StatusPreconditionFailed:
		fallthrough
	case http.StatusForbidden:
		problem.Type = core.UnauthorizedProblem
	case http.StatusConflict:
		fallthrough
	case http.StatusMethodNotAllowed:
		fallthrough
	case http.StatusNotFound:
		fallthrough
	case http.StatusBadRequest:
		fallthrough
	case http.StatusLengthRequired:
		problem.Type = core.MalformedProblem
	default: // Either http.StatusInternalServerError or an unexpected code
		problem.Type = core.ServerInternalProblem
	}

	// Only audit log internal errors so users cannot purposefully cause
	// auditable events.
	if problem.Type == core.ServerInternalProblem {
		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		wfe.log.Audit(fmt.Sprintf("Internal error - %s - %s", msg, detail))
	} else if statusCodeFromError(detail) != http.StatusInternalServerError {
		// If not an internal error and problem is a custom error type
		problem.Detail += fmt.Sprintf(" :: %s", detail)
	}

	problemDoc, err := json.Marshal(problem)
	if err != nil {
		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		wfe.log.Audit(fmt.Sprintf("Could not marshal error message: %s - %+v", err, problem))
		problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}")
	}

	// Paraphrased from
	// https://golang.org/src/net/http/server.go#L1272
	response.Header().Set("Content-Type", "application/problem+json")
	response.WriteHeader(code)
	response.Write(problemDoc)

	wfe.stats.Inc(fmt.Sprintf("WFE.HTTP.ErrorCodes.%d", code), 1, 1.0)
	problemSegments := strings.Split(string(problem.Type), ":")
	if len(problemSegments) > 0 {
		wfe.stats.Inc(fmt.Sprintf("WFE.HTTP.ProblemTypes.%s", problemSegments[len(problemSegments)-1]), 1, 1.0)
	}
}