// 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) } }