1package api 2 3import ( 4 "bytes" 5 "encoding/json" 6 "io" 7 "io/ioutil" 8 "net/http" 9 "net/url" 10 11 "github.com/hashicorp/vault/sdk/helper/consts" 12 13 retryablehttp "github.com/hashicorp/go-retryablehttp" 14) 15 16// Request is a raw request configuration structure used to initiate 17// API requests to the Vault server. 18type Request struct { 19 Method string 20 URL *url.URL 21 Params url.Values 22 Headers http.Header 23 ClientToken string 24 MFAHeaderVals []string 25 WrapTTL string 26 Obj interface{} 27 28 // When possible, use BodyBytes as it is more efficient due to how the 29 // retry logic works 30 BodyBytes []byte 31 32 // Fallback 33 Body io.Reader 34 BodySize int64 35 36 // Whether to request overriding soft-mandatory Sentinel policies (RGPs and 37 // EGPs). If set, the override flag will take effect for all policies 38 // evaluated during the request. 39 PolicyOverride bool 40} 41 42// SetJSONBody is used to set a request body that is a JSON-encoded value. 43func (r *Request) SetJSONBody(val interface{}) error { 44 buf, err := json.Marshal(val) 45 if err != nil { 46 return err 47 } 48 49 r.Obj = val 50 r.BodyBytes = buf 51 return nil 52} 53 54// ResetJSONBody is used to reset the body for a redirect 55func (r *Request) ResetJSONBody() error { 56 if r.BodyBytes == nil { 57 return nil 58 } 59 return r.SetJSONBody(r.Obj) 60} 61 62// DEPRECATED: ToHTTP turns this request into a valid *http.Request for use 63// with the net/http package. 64func (r *Request) ToHTTP() (*http.Request, error) { 65 req, err := r.toRetryableHTTP() 66 if err != nil { 67 return nil, err 68 } 69 70 switch { 71 case r.BodyBytes == nil && r.Body == nil: 72 // No body 73 74 case r.BodyBytes != nil: 75 req.Request.Body = ioutil.NopCloser(bytes.NewReader(r.BodyBytes)) 76 77 default: 78 if c, ok := r.Body.(io.ReadCloser); ok { 79 req.Request.Body = c 80 } else { 81 req.Request.Body = ioutil.NopCloser(r.Body) 82 } 83 } 84 85 return req.Request, nil 86} 87 88func (r *Request) toRetryableHTTP() (*retryablehttp.Request, error) { 89 // Encode the query parameters 90 r.URL.RawQuery = r.Params.Encode() 91 92 // Create the HTTP request, defaulting to retryable 93 var req *retryablehttp.Request 94 95 var err error 96 var body interface{} 97 98 switch { 99 case r.BodyBytes == nil && r.Body == nil: 100 // No body 101 102 case r.BodyBytes != nil: 103 // Use bytes, it's more efficient 104 body = r.BodyBytes 105 106 default: 107 body = r.Body 108 } 109 110 req, err = retryablehttp.NewRequest(r.Method, r.URL.RequestURI(), body) 111 if err != nil { 112 return nil, err 113 } 114 115 req.URL.User = r.URL.User 116 req.URL.Scheme = r.URL.Scheme 117 req.URL.Host = r.URL.Host 118 req.Host = r.URL.Host 119 120 if r.Headers != nil { 121 for header, vals := range r.Headers { 122 for _, val := range vals { 123 req.Header.Add(header, val) 124 } 125 } 126 } 127 128 if len(r.ClientToken) != 0 { 129 req.Header.Set(consts.AuthHeaderName, r.ClientToken) 130 } 131 132 if len(r.WrapTTL) != 0 { 133 req.Header.Set("X-Vault-Wrap-TTL", r.WrapTTL) 134 } 135 136 if len(r.MFAHeaderVals) != 0 { 137 for _, mfaHeaderVal := range r.MFAHeaderVals { 138 req.Header.Add("X-Vault-MFA", mfaHeaderVal) 139 } 140 } 141 142 if r.PolicyOverride { 143 req.Header.Set("X-Vault-Policy-Override", "true") 144 } 145 146 return req, nil 147} 148