github.com/decred/politeia@v1.4.0/politeiawww/legacy/pi/error.go (about) 1 // Copyright (c) 2021 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package pi 6 7 import ( 8 "context" 9 "fmt" 10 "net/http" 11 "runtime/debug" 12 "time" 13 14 pdv2 "github.com/decred/politeia/politeiad/api/v2" 15 pdclient "github.com/decred/politeia/politeiad/client" 16 v1 "github.com/decred/politeia/politeiawww/api/pi/v1" 17 "github.com/decred/politeia/util" 18 "github.com/pkg/errors" 19 ) 20 21 func respondWithError(w http.ResponseWriter, r *http.Request, format string, err error) { 22 // Check if the client dropped the connection 23 if err := r.Context().Err(); err == context.Canceled { 24 log.Infof("%v %v %v %v client aborted connection", 25 util.RemoteAddr(r), r.Method, r.URL, r.Proto) 26 27 // Client dropped the connection. There is no need to 28 // respond further. 29 return 30 } 31 32 // Check for expected error types 33 var ( 34 ue v1.UserErrorReply 35 pe v1.PluginErrorReply 36 pde pdclient.RespError 37 ) 38 switch { 39 case errors.As(err, &ue): 40 // Records user error 41 m := fmt.Sprintf("%v Pi user error: %v %v", 42 util.RemoteAddr(r), ue.ErrorCode, v1.ErrorCodes[ue.ErrorCode]) 43 if ue.ErrorContext != "" { 44 m += fmt.Sprintf(": %v", ue.ErrorContext) 45 } 46 log.Infof(m) 47 util.RespondWithJSON(w, http.StatusBadRequest, 48 v1.UserErrorReply{ 49 ErrorCode: ue.ErrorCode, 50 ErrorContext: ue.ErrorContext, 51 }) 52 return 53 54 case errors.As(err, &pe): 55 // politeiawww plugin error 56 m := fmt.Sprintf("%v Plugin error: %v %v", 57 util.RemoteAddr(r), pe.PluginID, pe.ErrorCode) 58 if pe.ErrorContext != "" { 59 m += fmt.Sprintf(": %v", pe.ErrorContext) 60 } 61 log.Infof(m) 62 util.RespondWithJSON(w, http.StatusBadRequest, 63 v1.PluginErrorReply{ 64 PluginID: pe.PluginID, 65 ErrorCode: pe.ErrorCode, 66 ErrorContext: pe.ErrorContext, 67 }) 68 return 69 70 case errors.As(err, &pde): 71 // Politeiad error 72 handlePDError(w, r, format, pde) 73 74 default: 75 // Internal server error. Log it and return a 500. 76 t := time.Now().Unix() 77 e := fmt.Sprintf(format, err) 78 log.Errorf("%v %v %v %v Internal error %v: %v", 79 util.RemoteAddr(r), r.Method, r.URL, r.Proto, t, e) 80 81 // If this is a pkg/errors error then we can pull the 82 // stack trace out of the error, otherwise, we use the 83 // stack trace for this function. 84 stack, ok := util.StackTrace(err) 85 if !ok { 86 stack = string(debug.Stack()) 87 } 88 89 log.Errorf("Stacktrace (NOT A REAL CRASH): %v", stack) 90 91 util.RespondWithJSON(w, http.StatusInternalServerError, 92 v1.ServerErrorReply{ 93 ErrorCode: t, 94 }) 95 return 96 } 97 } 98 99 // handlePDError handles errors from politeiad. 100 func handlePDError(w http.ResponseWriter, r *http.Request, format string, pde pdclient.RespError) { 101 var ( 102 pluginID = pde.ErrorReply.PluginID 103 errCode = pde.ErrorReply.ErrorCode 104 errContext = pde.ErrorReply.ErrorContext 105 ) 106 e := convertPDErrorCode(errCode) 107 switch { 108 case pluginID != "": 109 // politeiad plugin error. Log it and return a 400. 110 m := fmt.Sprintf("%v Plugin error: %v %v", 111 util.RemoteAddr(r), pluginID, errCode) 112 if errContext != "" { 113 m += fmt.Sprintf(": %v", errContext) 114 } 115 log.Infof(m) 116 util.RespondWithJSON(w, http.StatusBadRequest, 117 v1.PluginErrorReply{ 118 PluginID: pluginID, 119 ErrorCode: errCode, 120 ErrorContext: errContext, 121 }) 122 return 123 124 case e != v1.ErrorCodeInvalid: 125 // User error from politeiad that corresponds to a records user 126 // error. Log it and return a 400. 127 m := fmt.Sprintf("%v Records user error: %v %v", 128 util.RemoteAddr(r), e, v1.ErrorCodes[e]) 129 if errContext != "" { 130 m += fmt.Sprintf(": %v", errContext) 131 } 132 log.Infof(m) 133 util.RespondWithJSON(w, http.StatusBadRequest, 134 v1.UserErrorReply{ 135 ErrorCode: e, 136 ErrorContext: errContext, 137 }) 138 return 139 140 default: 141 // politeiad error does not correspond to a user error. Log it 142 // and return a 500. 143 ts := time.Now().Unix() 144 log.Errorf("%v %v %v %v Internal error %v: error code "+ 145 "from politeiad: %v", util.RemoteAddr(r), r.Method, r.URL, 146 r.Proto, ts, errCode) 147 148 util.RespondWithJSON(w, http.StatusInternalServerError, 149 v1.ServerErrorReply{ 150 ErrorCode: ts, 151 }) 152 return 153 } 154 } 155 156 // convertPDErrorCode converts user errors from politeiad into pi user errors. 157 func convertPDErrorCode(errCode uint32) v1.ErrorCodeT { 158 // Any error statuses that are intentionally omitted means that 159 // politeiawww should 500. 160 switch pdv2.ErrorCodeT(errCode) { 161 case pdv2.ErrorCodeTokenInvalid: 162 return v1.ErrorCodeRecordTokenInvalid 163 case pdv2.ErrorCodeRecordNotFound: 164 return v1.ErrorCodeRecordNotFound 165 case pdv2.ErrorCodeRequestPayloadInvalid: 166 // Intentionally omitted 167 case pdv2.ErrorCodeChallengeInvalid: 168 // Intentionally omitted 169 case pdv2.ErrorCodeMetadataStreamInvalid: 170 // Intentionally omitted 171 case pdv2.ErrorCodeMetadataStreamDuplicate: 172 // Intentionally omitted 173 case pdv2.ErrorCodeFilesEmpty: 174 // Intentionally omitted 175 case pdv2.ErrorCodeFileNameInvalid: 176 // Intentionally omitted 177 case pdv2.ErrorCodeFileNameDuplicate: 178 // Intentionally omitted 179 case pdv2.ErrorCodeFileDigestInvalid: 180 // Intentionally omitted 181 case pdv2.ErrorCodeFilePayloadInvalid: 182 // Intentionally omitted 183 case pdv2.ErrorCodeFileMIMETypeInvalid: 184 // Intentionally omitted 185 case pdv2.ErrorCodeFileMIMETypeUnsupported: 186 // Intentionally omitted 187 case pdv2.ErrorCodeRecordLocked: 188 // Intentionally omitted 189 case pdv2.ErrorCodeNoRecordChanges: 190 // Intentionally omitted 191 case pdv2.ErrorCodeStatusChangeInvalid: 192 // Intentionally omitted 193 case pdv2.ErrorCodePluginIDInvalid: 194 // Intentionally omitted 195 case pdv2.ErrorCodePluginCmdInvalid: 196 // Intentionally omitted 197 } 198 return v1.ErrorCodeInvalid 199 }