mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 10:37:56 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			139 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) HashiCorp, Inc.
 | |
| // SPDX-License-Identifier: MPL-2.0
 | |
| 
 | |
| package api
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"io/ioutil"
 | |
| 	"net/http"
 | |
| )
 | |
| 
 | |
| // Response is a raw response that wraps an HTTP response.
 | |
| type Response struct {
 | |
| 	*http.Response
 | |
| }
 | |
| 
 | |
| // DecodeJSON will decode the response body to a JSON structure. This
 | |
| // will consume the response body, but will not close it. Close must
 | |
| // still be called.
 | |
| func (r *Response) DecodeJSON(out interface{}) error {
 | |
| 	dec := json.NewDecoder(r.Body)
 | |
| 	dec.UseNumber()
 | |
| 	return dec.Decode(out)
 | |
| }
 | |
| 
 | |
| // Error returns an error response if there is one. If there is an error,
 | |
| // this will fully consume the response body, but will not close it. The
 | |
| // body must still be closed manually.
 | |
| func (r *Response) Error() error {
 | |
| 	// 200 to 399 are okay status codes. 429 is the code for health status of
 | |
| 	// standby nodes, otherwise, 429 is treated as quota limit reached.
 | |
| 	if (r.StatusCode >= 200 && r.StatusCode < 400) || (r.StatusCode == 429 && r.Request.URL.Path == "/v1/sys/health") {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	// We have an error. Let's copy the body into our own buffer first,
 | |
| 	// so that if we can't decode JSON, we can at least copy it raw.
 | |
| 	bodyBuf := &bytes.Buffer{}
 | |
| 	if _, err := io.Copy(bodyBuf, r.Body); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	r.Body.Close()
 | |
| 	r.Body = ioutil.NopCloser(bodyBuf)
 | |
| 	ns := r.Header.Get(NamespaceHeaderName)
 | |
| 
 | |
| 	// Build up the error object
 | |
| 	respErr := &ResponseError{
 | |
| 		HTTPMethod:    r.Request.Method,
 | |
| 		URL:           r.Request.URL.String(),
 | |
| 		StatusCode:    r.StatusCode,
 | |
| 		NamespacePath: ns,
 | |
| 	}
 | |
| 
 | |
| 	// Decode the error response if we can. Note that we wrap the bodyBuf
 | |
| 	// in a bytes.Reader here so that the JSON decoder doesn't move the
 | |
| 	// read pointer for the original buffer.
 | |
| 	var resp ErrorResponse
 | |
| 	dec := json.NewDecoder(bytes.NewReader(bodyBuf.Bytes()))
 | |
| 	dec.UseNumber()
 | |
| 	if err := dec.Decode(&resp); err != nil {
 | |
| 		// Store the fact that we couldn't decode the errors
 | |
| 		respErr.RawError = true
 | |
| 		respErr.Errors = []string{bodyBuf.String()}
 | |
| 	} else {
 | |
| 		// Store the decoded errors
 | |
| 		respErr.Errors = resp.Errors
 | |
| 	}
 | |
| 
 | |
| 	return respErr
 | |
| }
 | |
| 
 | |
| // ErrorResponse is the raw structure of errors when they're returned by the
 | |
| // HTTP API.
 | |
| type ErrorResponse struct {
 | |
| 	Errors []string
 | |
| }
 | |
| 
 | |
| // ResponseError is the error returned when Vault responds with an error or
 | |
| // non-success HTTP status code. If a request to Vault fails because of a
 | |
| // network error a different error message will be returned. ResponseError gives
 | |
| // access to the underlying errors and status code.
 | |
| type ResponseError struct {
 | |
| 	// HTTPMethod is the HTTP method for the request (PUT, GET, etc).
 | |
| 	HTTPMethod string
 | |
| 
 | |
| 	// URL is the URL of the request.
 | |
| 	URL string
 | |
| 
 | |
| 	// StatusCode is the HTTP status code.
 | |
| 	StatusCode int
 | |
| 
 | |
| 	// RawError marks that the underlying error messages returned by Vault were
 | |
| 	// not parsable. The Errors slice will contain the raw response body as the
 | |
| 	// first and only error string if this value is set to true.
 | |
| 	RawError bool
 | |
| 
 | |
| 	// Errors are the underlying errors returned by Vault.
 | |
| 	Errors []string
 | |
| 
 | |
| 	// Namespace path to be reported to the client if it is set to anything other
 | |
| 	// than root
 | |
| 	NamespacePath string
 | |
| }
 | |
| 
 | |
| // Error returns a human-readable error string for the response error.
 | |
| func (r *ResponseError) Error() string {
 | |
| 	errString := "Errors"
 | |
| 	if r.RawError {
 | |
| 		errString = "Raw Message"
 | |
| 	}
 | |
| 
 | |
| 	var ns string
 | |
| 	if r.NamespacePath != "" && r.NamespacePath != "root/" {
 | |
| 		ns = "Namespace: " + r.NamespacePath + "\n"
 | |
| 	}
 | |
| 
 | |
| 	var errBody bytes.Buffer
 | |
| 	errBody.WriteString(fmt.Sprintf(
 | |
| 		"Error making API request.\n\n"+
 | |
| 			ns+
 | |
| 			"URL: %s %s\n"+
 | |
| 			"Code: %d. %s:\n\n",
 | |
| 		r.HTTPMethod, r.URL, r.StatusCode, errString))
 | |
| 
 | |
| 	if r.RawError && len(r.Errors) == 1 {
 | |
| 		errBody.WriteString(r.Errors[0])
 | |
| 	} else {
 | |
| 		for _, err := range r.Errors {
 | |
| 			errBody.WriteString(fmt.Sprintf("* %s", err))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return errBody.String()
 | |
| }
 | 
