1package api 2 3import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/http" 9 10 "github.com/hashicorp/vault/sdk/helper/jsonutil" 11) 12 13// Response is a raw response that wraps an HTTP response. 14type Response struct { 15 *http.Response 16} 17 18// DecodeJSON will decode the response body to a JSON structure. This 19// will consume the response body, but will not close it. Close must 20// still be called. 21func (r *Response) DecodeJSON(out interface{}) error { 22 return jsonutil.DecodeJSONFromReader(r.Body, out) 23} 24 25// Error returns an error response if there is one. If there is an error, 26// this will fully consume the response body, but will not close it. The 27// body must still be closed manually. 28func (r *Response) Error() error { 29 // 200 to 399 are okay status codes. 429 is the code for health status of 30 // standby nodes. 31 if (r.StatusCode >= 200 && r.StatusCode < 400) || r.StatusCode == 429 { 32 return nil 33 } 34 35 // We have an error. Let's copy the body into our own buffer first, 36 // so that if we can't decode JSON, we can at least copy it raw. 37 bodyBuf := &bytes.Buffer{} 38 if _, err := io.Copy(bodyBuf, r.Body); err != nil { 39 return err 40 } 41 42 r.Body.Close() 43 r.Body = ioutil.NopCloser(bodyBuf) 44 45 // Build up the error object 46 respErr := &ResponseError{ 47 HTTPMethod: r.Request.Method, 48 URL: r.Request.URL.String(), 49 StatusCode: r.StatusCode, 50 } 51 52 // Decode the error response if we can. Note that we wrap the bodyBuf 53 // in a bytes.Reader here so that the JSON decoder doesn't move the 54 // read pointer for the original buffer. 55 var resp ErrorResponse 56 if err := jsonutil.DecodeJSON(bodyBuf.Bytes(), &resp); err != nil { 57 // Store the fact that we couldn't decode the errors 58 respErr.RawError = true 59 respErr.Errors = []string{bodyBuf.String()} 60 } else { 61 // Store the decoded errors 62 respErr.Errors = resp.Errors 63 } 64 65 return respErr 66} 67 68// ErrorResponse is the raw structure of errors when they're returned by the 69// HTTP API. 70type ErrorResponse struct { 71 Errors []string 72} 73 74// ResponseError is the error returned when Vault responds with an error or 75// non-success HTTP status code. If a request to Vault fails because of a 76// network error a different error message will be returned. ResponseError gives 77// access to the underlying errors and status code. 78type ResponseError struct { 79 // HTTPMethod is the HTTP method for the request (PUT, GET, etc). 80 HTTPMethod string 81 82 // URL is the URL of the request. 83 URL string 84 85 // StatusCode is the HTTP status code. 86 StatusCode int 87 88 // RawError marks that the underlying error messages returned by Vault were 89 // not parsable. The Errors slice will contain the raw response body as the 90 // first and only error string if this value is set to true. 91 RawError bool 92 93 // Errors are the underlying errors returned by Vault. 94 Errors []string 95} 96 97// Error returns a human-readable error string for the response error. 98func (r *ResponseError) Error() string { 99 errString := "Errors" 100 if r.RawError { 101 errString = "Raw Message" 102 } 103 104 var errBody bytes.Buffer 105 errBody.WriteString(fmt.Sprintf( 106 "Error making API request.\n\n"+ 107 "URL: %s %s\n"+ 108 "Code: %d. %s:\n\n", 109 r.HTTPMethod, r.URL, r.StatusCode, errString)) 110 111 if r.RawError && len(r.Errors) == 1 { 112 errBody.WriteString(r.Errors[0]) 113 } else { 114 for _, err := range r.Errors { 115 errBody.WriteString(fmt.Sprintf("* %s", err)) 116 } 117 } 118 119 return errBody.String() 120} 121