github.com/decred/politeia@v1.4.0/politeiawww/legacy/records/error.go (about) 1 // Copyright (c) 2020-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 records 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "net/http" 12 "runtime/debug" 13 "time" 14 15 pdv2 "github.com/decred/politeia/politeiad/api/v2" 16 pdclient "github.com/decred/politeia/politeiad/client" 17 v1 "github.com/decred/politeia/politeiawww/api/records/v1" 18 "github.com/decred/politeia/util" 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 Records 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 func handlePDError(w http.ResponseWriter, r *http.Request, format string, pde pdclient.RespError) { 100 var ( 101 pluginID = pde.ErrorReply.PluginID 102 errCode = pde.ErrorReply.ErrorCode 103 errContext = pde.ErrorReply.ErrorContext 104 ) 105 e := convertPDErrorCode(errCode) 106 switch { 107 case pluginID != "": 108 // politeiad plugin error. Log it and return a 400. 109 m := fmt.Sprintf("%v Plugin error: %v %v", 110 util.RemoteAddr(r), pluginID, errCode) 111 if errContext != "" { 112 m += fmt.Sprintf(": %v", errContext) 113 } 114 log.Infof(m) 115 util.RespondWithJSON(w, http.StatusBadRequest, 116 v1.PluginErrorReply{ 117 PluginID: pluginID, 118 ErrorCode: errCode, 119 ErrorContext: errContext, 120 }) 121 return 122 123 case e != v1.ErrorCodeInvalid: 124 // User error from politeiad that corresponds to a records user 125 // error. Log it and return a 400. 126 m := fmt.Sprintf("%v Records user error: %v %v", 127 util.RemoteAddr(r), e, v1.ErrorCodes[e]) 128 if errContext != "" { 129 m += fmt.Sprintf(": %v", errContext) 130 } 131 log.Infof(m) 132 util.RespondWithJSON(w, http.StatusBadRequest, 133 v1.UserErrorReply{ 134 ErrorCode: e, 135 ErrorContext: errContext, 136 }) 137 return 138 139 default: 140 // politeiad error does not correspond to a user error. Log it 141 // and return a 500. 142 ts := time.Now().Unix() 143 log.Errorf("%v %v %v %v Internal error %v: error code "+ 144 "from politeiad: %v", util.RemoteAddr(r), r.Method, r.URL, 145 r.Proto, ts, errCode) 146 147 util.RespondWithJSON(w, http.StatusInternalServerError, 148 v1.ServerErrorReply{ 149 ErrorCode: ts, 150 }) 151 return 152 } 153 } 154 155 func convertPDErrorCode(errCode uint32) v1.ErrorCodeT { 156 // Any error statuses that are intentionally omitted means that 157 // politeiawww should 500. 158 switch pdv2.ErrorCodeT(errCode) { 159 case pdv2.ErrorCodeRequestPayloadInvalid: 160 // Intentionally omitted 161 case pdv2.ErrorCodeChallengeInvalid: 162 // Intentionally omitted 163 case pdv2.ErrorCodeMetadataStreamInvalid: 164 // Intentionally omitted 165 case pdv2.ErrorCodeMetadataStreamDuplicate: 166 // Intentionally omitted 167 case pdv2.ErrorCodeFilesEmpty: 168 return v1.ErrorCodeFilesEmpty 169 case pdv2.ErrorCodeFileNameInvalid: 170 return v1.ErrorCodeFileNameInvalid 171 case pdv2.ErrorCodeFileNameDuplicate: 172 return v1.ErrorCodeFileNameDuplicate 173 case pdv2.ErrorCodeFileDigestInvalid: 174 return v1.ErrorCodeFileDigestInvalid 175 case pdv2.ErrorCodeFilePayloadInvalid: 176 return v1.ErrorCodeFilePayloadInvalid 177 case pdv2.ErrorCodeFileMIMETypeInvalid: 178 return v1.ErrorCodeFileMIMETypeInvalid 179 case pdv2.ErrorCodeFileMIMETypeUnsupported: 180 return v1.ErrorCodeFileMIMETypeUnsupported 181 case pdv2.ErrorCodeTokenInvalid: 182 return v1.ErrorCodeRecordTokenInvalid 183 case pdv2.ErrorCodeRecordNotFound: 184 return v1.ErrorCodeRecordNotFound 185 case pdv2.ErrorCodeRecordLocked: 186 return v1.ErrorCodeRecordLocked 187 case pdv2.ErrorCodeNoRecordChanges: 188 return v1.ErrorCodeNoRecordChanges 189 case pdv2.ErrorCodeStatusChangeInvalid: 190 return v1.ErrorCodeStatusChangeInvalid 191 case pdv2.ErrorCodePluginIDInvalid: 192 // Intentionally omitted 193 case pdv2.ErrorCodePluginCmdInvalid: 194 // Intentionally omitted 195 } 196 return v1.ErrorCodeInvalid 197 }