1package autorest 2 3import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "log" 9 "net/http" 10 "net/http/cookiejar" 11 "time" 12) 13 14const ( 15 // DefaultPollingDelay is a reasonable delay between polling requests. 16 DefaultPollingDelay = 60 * time.Second 17 18 // DefaultPollingDuration is a reasonable total polling duration. 19 DefaultPollingDuration = 15 * time.Minute 20 21 // DefaultRetryAttempts is number of attempts for retry status codes (5xx). 22 DefaultRetryAttempts = 3 23) 24 25var statusCodesForRetry = []int{ 26 http.StatusRequestTimeout, // 408 27 http.StatusInternalServerError, // 500 28 http.StatusBadGateway, // 502 29 http.StatusServiceUnavailable, // 503 30 http.StatusGatewayTimeout, // 504 31} 32 33const ( 34 requestFormat = `HTTP Request Begin =================================================== 35%s 36===================================================== HTTP Request End 37` 38 responseFormat = `HTTP Response Begin =================================================== 39%s 40===================================================== HTTP Response End 41` 42) 43 44// Response serves as the base for all responses from generated clients. It provides access to the 45// last http.Response. 46type Response struct { 47 *http.Response `json:"-"` 48} 49 50// LoggingInspector implements request and response inspectors that log the full request and 51// response to a supplied log. 52type LoggingInspector struct { 53 Logger *log.Logger 54} 55 56// WithInspection returns a PrepareDecorator that emits the http.Request to the supplied logger. The 57// body is restored after being emitted. 58// 59// Note: Since it reads the entire Body, this decorator should not be used where body streaming is 60// important. It is best used to trace JSON or similar body values. 61func (li LoggingInspector) WithInspection() PrepareDecorator { 62 return func(p Preparer) Preparer { 63 return PreparerFunc(func(r *http.Request) (*http.Request, error) { 64 var body, b bytes.Buffer 65 66 defer r.Body.Close() 67 68 r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body)) 69 if err := r.Write(&b); err != nil { 70 return nil, fmt.Errorf("Failed to write response: %v", err) 71 } 72 73 li.Logger.Printf(requestFormat, b.String()) 74 75 r.Body = ioutil.NopCloser(&body) 76 return p.Prepare(r) 77 }) 78 } 79} 80 81// ByInspecting returns a RespondDecorator that emits the http.Response to the supplied logger. The 82// body is restored after being emitted. 83// 84// Note: Since it reads the entire Body, this decorator should not be used where body streaming is 85// important. It is best used to trace JSON or similar body values. 86func (li LoggingInspector) ByInspecting() RespondDecorator { 87 return func(r Responder) Responder { 88 return ResponderFunc(func(resp *http.Response) error { 89 var body, b bytes.Buffer 90 defer resp.Body.Close() 91 resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body)) 92 if err := resp.Write(&b); err != nil { 93 return fmt.Errorf("Failed to write response: %v", err) 94 } 95 96 li.Logger.Printf(responseFormat, b.String()) 97 98 resp.Body = ioutil.NopCloser(&body) 99 return r.Respond(resp) 100 }) 101 } 102} 103 104// Client is the base for autorest generated clients. It provides default, "do nothing" 105// implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the 106// standard, undecorated http.Client as a default Sender. 107// 108// Generated clients should also use Error (see NewError and NewErrorWithError) for errors and 109// return responses that compose with Response. 110// 111// Most customization of generated clients is best achieved by supplying a custom Authorizer, custom 112// RequestInspector, and / or custom ResponseInspector. Users may log requests, implement circuit 113// breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) or otherwise influence 114// sending the request by providing a decorated Sender. 115type Client struct { 116 Authorizer Authorizer 117 Sender Sender 118 RequestInspector PrepareDecorator 119 ResponseInspector RespondDecorator 120 121 // PollingDelay sets the polling frequency used in absence of a Retry-After HTTP header 122 PollingDelay time.Duration 123 124 // PollingDuration sets the maximum polling time after which an error is returned. 125 PollingDuration time.Duration 126 127 // RetryAttempts sets the default number of retry attempts for client. 128 RetryAttempts int 129 130 // RetryDuration sets the delay duration for retries. 131 RetryDuration time.Duration 132 133 // UserAgent, if not empty, will be set as the HTTP User-Agent header on all requests sent 134 // through the Do method. 135 UserAgent string 136 137 Jar http.CookieJar 138} 139 140// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed 141// string. 142func NewClientWithUserAgent(ua string) Client { 143 return Client{ 144 PollingDelay: DefaultPollingDelay, 145 PollingDuration: DefaultPollingDuration, 146 RetryAttempts: DefaultRetryAttempts, 147 RetryDuration: 30 * time.Second, 148 UserAgent: ua, 149 } 150} 151 152// Do implements the Sender interface by invoking the active Sender after applying authorization. 153// If Sender is not set, it uses a new instance of http.Client. In both cases it will, if UserAgent 154// is set, apply set the User-Agent header. 155func (c Client) Do(r *http.Request) (*http.Response, error) { 156 if r.UserAgent() == "" { 157 r, _ = Prepare(r, 158 WithUserAgent(c.UserAgent)) 159 } 160 r, err := Prepare(r, 161 c.WithInspection(), 162 c.WithAuthorization()) 163 if err != nil { 164 return nil, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed") 165 } 166 resp, err := SendWithSender(c.sender(), r, 167 DoRetryForStatusCodes(c.RetryAttempts, c.RetryDuration, statusCodesForRetry...)) 168 Respond(resp, 169 c.ByInspecting()) 170 return resp, err 171} 172 173// sender returns the Sender to which to send requests. 174func (c Client) sender() Sender { 175 if c.Sender == nil { 176 j, _ := cookiejar.New(nil) 177 return &http.Client{Jar: j} 178 } 179 return c.Sender 180} 181 182// WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator 183// from the current Authorizer. If not Authorizer is set, it uses the NullAuthorizer. 184func (c Client) WithAuthorization() PrepareDecorator { 185 return c.authorizer().WithAuthorization() 186} 187 188// authorizer returns the Authorizer to use. 189func (c Client) authorizer() Authorizer { 190 if c.Authorizer == nil { 191 return NullAuthorizer{} 192 } 193 return c.Authorizer 194} 195 196// WithInspection is a convenience method that passes the request to the supplied RequestInspector, 197// if present, or returns the WithNothing PrepareDecorator otherwise. 198func (c Client) WithInspection() PrepareDecorator { 199 if c.RequestInspector == nil { 200 return WithNothing() 201 } 202 return c.RequestInspector 203} 204 205// ByInspecting is a convenience method that passes the response to the supplied ResponseInspector, 206// if present, or returns the ByIgnoring RespondDecorator otherwise. 207func (c Client) ByInspecting() RespondDecorator { 208 if c.ResponseInspector == nil { 209 return ByIgnoring() 210 } 211 return c.ResponseInspector 212} 213