github.com/decred/politeia@v1.4.0/politeiawww/legacy/ticketvote/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 ticketvote 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/ticketvote/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 pde pdclient.RespError 36 ) 37 switch { 38 case errors.As(err, &ue): 39 // Ticketvote user error 40 m := fmt.Sprintf("Ticketvote user error: %v %v %v", 41 util.RemoteAddr(r), ue.ErrorCode, v1.ErrorCodes[ue.ErrorCode]) 42 if ue.ErrorContext != "" { 43 m += fmt.Sprintf(": %v", ue.ErrorContext) 44 } 45 log.Infof(m) 46 util.RespondWithJSON(w, http.StatusBadRequest, 47 v1.UserErrorReply{ 48 ErrorCode: ue.ErrorCode, 49 ErrorContext: ue.ErrorContext, 50 }) 51 return 52 53 case errors.As(err, &pde): 54 // Politeiad error 55 handlePDError(w, r, format, pde) 56 57 default: 58 // Internal server error. Log it and return a 500. 59 t := time.Now().Unix() 60 e := fmt.Sprintf(format, err) 61 log.Errorf("%v %v %v %v Internal error %v: %v", 62 util.RemoteAddr(r), r.Method, r.URL, r.Proto, t, e) 63 64 // If this is a pkg/errors error then we can pull the 65 // stack trace out of the error, otherwise, we use the 66 // stack trace for this function. 67 stack, ok := util.StackTrace(err) 68 if !ok { 69 stack = string(debug.Stack()) 70 } 71 72 log.Errorf("Stacktrace (NOT A REAL CRASH): %v", stack) 73 74 util.RespondWithJSON(w, http.StatusInternalServerError, 75 v1.ServerErrorReply{ 76 ErrorCode: t, 77 }) 78 return 79 } 80 } 81 82 func handlePDError(w http.ResponseWriter, r *http.Request, format string, pde pdclient.RespError) { 83 var ( 84 pluginID = pde.ErrorReply.PluginID 85 errCode = pde.ErrorReply.ErrorCode 86 errContext = pde.ErrorReply.ErrorContext 87 ) 88 e := convertPDErrorCode(errCode) 89 switch { 90 case pluginID != "": 91 // politeiad plugin error. Log it and return a 400. 92 m := fmt.Sprintf("%v Plugin error: %v %v", 93 util.RemoteAddr(r), pluginID, errCode) 94 if errContext != "" { 95 m += fmt.Sprintf(": %v", errContext) 96 } 97 log.Infof(m) 98 util.RespondWithJSON(w, http.StatusBadRequest, 99 v1.PluginErrorReply{ 100 PluginID: pluginID, 101 ErrorCode: errCode, 102 ErrorContext: errContext, 103 }) 104 return 105 106 case e != v1.ErrorCodeInvalid: 107 // User error from politeiad that corresponds to a records user 108 // error. Log it and return a 400. 109 m := fmt.Sprintf("%v Ticketvote user error: %v %v", 110 util.RemoteAddr(r), e, v1.ErrorCodes[e]) 111 if errContext != "" { 112 m += fmt.Sprintf(": %v", errContext) 113 } 114 log.Infof(m) 115 util.RespondWithJSON(w, http.StatusBadRequest, 116 v1.UserErrorReply{ 117 ErrorCode: e, 118 ErrorContext: errContext, 119 }) 120 return 121 122 default: 123 // politeiad error does not correspond to a user error. Log it 124 // and return a 500. 125 ts := time.Now().Unix() 126 log.Errorf("%v %v %v %v Internal error %v: error code "+ 127 "from politeiad: %v", util.RemoteAddr(r), r.Method, r.URL, 128 r.Proto, ts, errCode) 129 130 util.RespondWithJSON(w, http.StatusInternalServerError, 131 v1.ServerErrorReply{ 132 ErrorCode: ts, 133 }) 134 return 135 } 136 } 137 138 func convertPDErrorCode(errCode uint32) v1.ErrorCodeT { 139 // This list is only populated with politeiad errors that we expect 140 // for the ticketvote plugin commands. Any politeiad errors not 141 // included in this list will cause politeiawww to 500. 142 switch pdv2.ErrorCodeT(errCode) { 143 case pdv2.ErrorCodeRecordNotFound: 144 return v1.ErrorCodeRecordNotFound 145 case pdv2.ErrorCodeTokenInvalid: 146 return v1.ErrorCodeTokenInvalid 147 case pdv2.ErrorCodeRecordLocked: 148 return v1.ErrorCodeRecordLocked 149 case pdv2.ErrorCodeDuplicatePayload: 150 return v1.ErrorCodeDuplicatePayload 151 } 152 return v1.ErrorCodeInvalid 153 }