github.com/decred/politeia@v1.4.0/politeiawww/legacy/comments/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 comments 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/comments/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 // Comments 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 user 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 comments 125 // user error. Log it and return a 400. 126 m := fmt.Sprintf("%v Comments 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 default: 139 // politeiad error does not correspond to a user error. Log it 140 // and return a 500. 141 ts := time.Now().Unix() 142 log.Errorf("%v %v %v %v Internal error %v: error code "+ 143 "from politeiad: %v", util.RemoteAddr(r), r.Method, r.URL, 144 r.Proto, ts, errCode) 145 146 util.RespondWithJSON(w, http.StatusInternalServerError, 147 v1.ServerErrorReply{ 148 ErrorCode: ts, 149 }) 150 return 151 } 152 } 153 154 func convertPDErrorCode(errCode uint32) v1.ErrorCodeT { 155 // These are the only politeiad user errors that the comments 156 // API expects to encounter. 157 switch pdv2.ErrorCodeT(errCode) { 158 case pdv2.ErrorCodeTokenInvalid: 159 return v1.ErrorCodeTokenInvalid 160 case pdv2.ErrorCodeRecordNotFound: 161 return v1.ErrorCodeRecordNotFound 162 case pdv2.ErrorCodeRecordLocked: 163 return v1.ErrorCodeRecordLocked 164 case pdv2.ErrorCodeDuplicatePayload: 165 return v1.ErrorCodeDuplicatePayload 166 } 167 return v1.ErrorCodeInvalid 168 }