github.com/letsencrypt/boulder@v0.20251208.0/web/send_error.go (about) 1 package web 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strings" 8 9 blog "github.com/letsencrypt/boulder/log" 10 "github.com/letsencrypt/boulder/probs" 11 ) 12 13 // SendError does a few things that we want for each error response: 14 // - Adds both the external and the internal error to a RequestEvent. 15 // - If the ProblemDetails provided is a ServerInternalProblem, audit logs the 16 // internal error. 17 // - Prefixes the Type field of the ProblemDetails with the RFC8555 namespace. 18 // - Sends an HTTP response containing the error and an error code to the user. 19 // 20 // The internal error (ierr) may be nil if no information beyond the 21 // ProblemDetails is needed for internal debugging. 22 func SendError( 23 log blog.Logger, 24 response http.ResponseWriter, 25 logEvent *RequestEvent, 26 prob *probs.ProblemDetails, 27 ierr error, 28 ) { 29 // Write the JSON problem response 30 response.Header().Set("Content-Type", "application/problem+json") 31 if prob.HTTPStatus != 0 { 32 response.WriteHeader(prob.HTTPStatus) 33 } else { 34 // All problems should have an HTTPStatus set, because all of the functions 35 // in the probs package which construct a problem set one. A problem details 36 // object getting to this point without a status set is an error. 37 response.WriteHeader(http.StatusInternalServerError) 38 } 39 40 // Suppress logging of the "Your account is temporarily prevented from 41 // requesting certificates" error. 42 var primaryDetail = prob.Detail 43 if prob.Type == probs.PausedProblem { 44 primaryDetail = "account/ident pair is paused" 45 } 46 47 // Record details to the log event 48 logEvent.Error = fmt.Sprintf("%d :: %s :: %s", prob.HTTPStatus, prob.Type, primaryDetail) 49 if len(prob.SubProblems) > 0 { 50 subDetails := make([]string, len(prob.SubProblems)) 51 for i, sub := range prob.SubProblems { 52 subDetails[i] = fmt.Sprintf("\"%s :: %s :: %s\"", sub.Identifier.Value, sub.Type, sub.Detail) 53 } 54 logEvent.Error += fmt.Sprintf(" [%s]", strings.Join(subDetails, ", ")) 55 } 56 if ierr != nil { 57 logEvent.AddError("%s", ierr) 58 } 59 60 // Set the proper namespace for the problem and any sub-problems. 61 prob.Type = probs.ProblemType(probs.ErrorNS) + prob.Type 62 for i := range prob.SubProblems { 63 prob.SubProblems[i].Type = probs.ProblemType(probs.ErrorNS) + prob.SubProblems[i].Type 64 } 65 66 problemDoc, err := json.MarshalIndent(prob, "", " ") 67 if err != nil { 68 log.AuditErrf("Could not marshal error message: %s - %+v", err, prob) 69 problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}") 70 } 71 72 response.Write(problemDoc) 73 }