request.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. package request
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "net/url"
  8. "reflect"
  9. "strings"
  10. "time"
  11. "github.com/aws/aws-sdk-go/aws"
  12. "github.com/aws/aws-sdk-go/aws/awserr"
  13. "github.com/aws/aws-sdk-go/aws/client/metadata"
  14. )
  15. // A Request is the service request to be made.
  16. type Request struct {
  17. Config aws.Config
  18. ClientInfo metadata.ClientInfo
  19. Handlers Handlers
  20. Retryer
  21. Time time.Time
  22. ExpireTime time.Duration
  23. Operation *Operation
  24. HTTPRequest *http.Request
  25. HTTPResponse *http.Response
  26. Body io.ReadSeeker
  27. BodyStart int64 // offset from beginning of Body that the request body starts
  28. Params interface{}
  29. Error error
  30. Data interface{}
  31. RequestID string
  32. RetryCount int
  33. Retryable *bool
  34. RetryDelay time.Duration
  35. NotHoist bool
  36. SignedHeaderVals http.Header
  37. LastSignedAt time.Time
  38. built bool
  39. // Need to persist an intermideant body betweend the input Body and HTTP
  40. // request body because the HTTP Client's transport can maintain a reference
  41. // to the HTTP request's body after the client has returned. This value is
  42. // safe to use concurrently and rewraps the input Body for each HTTP request.
  43. safeBody *offsetReader
  44. }
  45. // An Operation is the service API operation to be made.
  46. type Operation struct {
  47. Name string
  48. HTTPMethod string
  49. HTTPPath string
  50. *Paginator
  51. }
  52. // Paginator keeps track of pagination configuration for an API operation.
  53. type Paginator struct {
  54. InputTokens []string
  55. OutputTokens []string
  56. LimitToken string
  57. TruncationToken string
  58. }
  59. // New returns a new Request pointer for the service API
  60. // operation and parameters.
  61. //
  62. // Params is any value of input parameters to be the request payload.
  63. // Data is pointer value to an object which the request's response
  64. // payload will be deserialized to.
  65. func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
  66. retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request {
  67. method := operation.HTTPMethod
  68. if method == "" {
  69. method = "POST"
  70. }
  71. httpReq, _ := http.NewRequest(method, "", nil)
  72. var err error
  73. httpReq.URL, err = url.Parse(clientInfo.Endpoint + operation.HTTPPath)
  74. if err != nil {
  75. httpReq.URL = &url.URL{}
  76. err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err)
  77. }
  78. r := &Request{
  79. Config: cfg,
  80. ClientInfo: clientInfo,
  81. Handlers: handlers.Copy(),
  82. Retryer: retryer,
  83. Time: time.Now(),
  84. ExpireTime: 0,
  85. Operation: operation,
  86. HTTPRequest: httpReq,
  87. Body: nil,
  88. Params: params,
  89. Error: err,
  90. Data: data,
  91. }
  92. r.SetBufferBody([]byte{})
  93. return r
  94. }
  95. // WillRetry returns if the request's can be retried.
  96. func (r *Request) WillRetry() bool {
  97. return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries()
  98. }
  99. // ParamsFilled returns if the request's parameters have been populated
  100. // and the parameters are valid. False is returned if no parameters are
  101. // provided or invalid.
  102. func (r *Request) ParamsFilled() bool {
  103. return r.Params != nil && reflect.ValueOf(r.Params).Elem().IsValid()
  104. }
  105. // DataFilled returns true if the request's data for response deserialization
  106. // target has been set and is a valid. False is returned if data is not
  107. // set, or is invalid.
  108. func (r *Request) DataFilled() bool {
  109. return r.Data != nil && reflect.ValueOf(r.Data).Elem().IsValid()
  110. }
  111. // SetBufferBody will set the request's body bytes that will be sent to
  112. // the service API.
  113. func (r *Request) SetBufferBody(buf []byte) {
  114. r.SetReaderBody(bytes.NewReader(buf))
  115. }
  116. // SetStringBody sets the body of the request to be backed by a string.
  117. func (r *Request) SetStringBody(s string) {
  118. r.SetReaderBody(strings.NewReader(s))
  119. }
  120. // SetReaderBody will set the request's body reader.
  121. func (r *Request) SetReaderBody(reader io.ReadSeeker) {
  122. r.Body = reader
  123. r.ResetBody()
  124. }
  125. // Presign returns the request's signed URL. Error will be returned
  126. // if the signing fails.
  127. func (r *Request) Presign(expireTime time.Duration) (string, error) {
  128. r.ExpireTime = expireTime
  129. r.NotHoist = false
  130. r.Sign()
  131. if r.Error != nil {
  132. return "", r.Error
  133. }
  134. return r.HTTPRequest.URL.String(), nil
  135. }
  136. // PresignRequest behaves just like presign, but hoists all headers and signs them.
  137. // Also returns the signed hash back to the user
  138. func (r *Request) PresignRequest(expireTime time.Duration) (string, http.Header, error) {
  139. r.ExpireTime = expireTime
  140. r.NotHoist = true
  141. r.Sign()
  142. if r.Error != nil {
  143. return "", nil, r.Error
  144. }
  145. return r.HTTPRequest.URL.String(), r.SignedHeaderVals, nil
  146. }
  147. func debugLogReqError(r *Request, stage string, retrying bool, err error) {
  148. if !r.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) {
  149. return
  150. }
  151. retryStr := "not retrying"
  152. if retrying {
  153. retryStr = "will retry"
  154. }
  155. r.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v",
  156. stage, r.ClientInfo.ServiceName, r.Operation.Name, retryStr, err))
  157. }
  158. // Build will build the request's object so it can be signed and sent
  159. // to the service. Build will also validate all the request's parameters.
  160. // Anny additional build Handlers set on this request will be run
  161. // in the order they were set.
  162. //
  163. // The request will only be built once. Multiple calls to build will have
  164. // no effect.
  165. //
  166. // If any Validate or Build errors occur the build will stop and the error
  167. // which occurred will be returned.
  168. func (r *Request) Build() error {
  169. if !r.built {
  170. r.Handlers.Validate.Run(r)
  171. if r.Error != nil {
  172. debugLogReqError(r, "Validate Request", false, r.Error)
  173. return r.Error
  174. }
  175. r.Handlers.Build.Run(r)
  176. if r.Error != nil {
  177. debugLogReqError(r, "Build Request", false, r.Error)
  178. return r.Error
  179. }
  180. r.built = true
  181. }
  182. return r.Error
  183. }
  184. // Sign will sign the request returning error if errors are encountered.
  185. //
  186. // Send will build the request prior to signing. All Sign Handlers will
  187. // be executed in the order they were set.
  188. func (r *Request) Sign() error {
  189. r.Build()
  190. if r.Error != nil {
  191. debugLogReqError(r, "Build Request", false, r.Error)
  192. return r.Error
  193. }
  194. r.Handlers.Sign.Run(r)
  195. return r.Error
  196. }
  197. // ResetBody rewinds the request body backto its starting position, and
  198. // set's the HTTP Request body reference. When the body is read prior
  199. // to being sent in the HTTP request it will need to be rewound.
  200. func (r *Request) ResetBody() {
  201. if r.safeBody != nil {
  202. r.safeBody.Close()
  203. }
  204. r.safeBody = newOffsetReader(r.Body, r.BodyStart)
  205. r.HTTPRequest.Body = r.safeBody
  206. }
  207. // GetBody will return an io.ReadSeeker of the Request's underlying
  208. // input body with a concurrency safe wrapper.
  209. func (r *Request) GetBody() io.ReadSeeker {
  210. return r.safeBody
  211. }
  212. // Send will send the request returning error if errors are encountered.
  213. //
  214. // Send will sign the request prior to sending. All Send Handlers will
  215. // be executed in the order they were set.
  216. //
  217. // Canceling a request is non-deterministic. If a request has been canceled,
  218. // then the transport will choose, randomly, one of the state channels during
  219. // reads or getting the connection.
  220. //
  221. // readLoop() and getConn(req *Request, cm connectMethod)
  222. // https://github.com/golang/go/blob/master/src/net/http/transport.go
  223. //
  224. // Send will not close the request.Request's body.
  225. func (r *Request) Send() error {
  226. for {
  227. if aws.BoolValue(r.Retryable) {
  228. if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
  229. r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
  230. r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount))
  231. }
  232. // The previous http.Request will have a reference to the r.Body
  233. // and the HTTP Client's Transport may still be reading from
  234. // the request's body even though the Client's Do returned.
  235. r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, nil)
  236. r.ResetBody()
  237. // Closing response body to ensure that no response body is leaked
  238. // between retry attempts.
  239. if r.HTTPResponse != nil && r.HTTPResponse.Body != nil {
  240. r.HTTPResponse.Body.Close()
  241. }
  242. }
  243. r.Sign()
  244. if r.Error != nil {
  245. return r.Error
  246. }
  247. r.Retryable = nil
  248. r.Handlers.Send.Run(r)
  249. if r.Error != nil {
  250. if strings.Contains(r.Error.Error(), "net/http: request canceled") {
  251. return r.Error
  252. }
  253. err := r.Error
  254. r.Handlers.Retry.Run(r)
  255. r.Handlers.AfterRetry.Run(r)
  256. if r.Error != nil {
  257. debugLogReqError(r, "Send Request", false, r.Error)
  258. return r.Error
  259. }
  260. debugLogReqError(r, "Send Request", true, err)
  261. continue
  262. }
  263. r.Handlers.UnmarshalMeta.Run(r)
  264. r.Handlers.ValidateResponse.Run(r)
  265. if r.Error != nil {
  266. err := r.Error
  267. r.Handlers.UnmarshalError.Run(r)
  268. r.Handlers.Retry.Run(r)
  269. r.Handlers.AfterRetry.Run(r)
  270. if r.Error != nil {
  271. debugLogReqError(r, "Validate Response", false, r.Error)
  272. return r.Error
  273. }
  274. debugLogReqError(r, "Validate Response", true, err)
  275. continue
  276. }
  277. r.Handlers.Unmarshal.Run(r)
  278. if r.Error != nil {
  279. err := r.Error
  280. r.Handlers.Retry.Run(r)
  281. r.Handlers.AfterRetry.Run(r)
  282. if r.Error != nil {
  283. debugLogReqError(r, "Unmarshal Response", false, r.Error)
  284. return r.Error
  285. }
  286. debugLogReqError(r, "Unmarshal Response", true, err)
  287. continue
  288. }
  289. break
  290. }
  291. return nil
  292. }
  293. // AddToUserAgent adds the string to the end of the request's current user agent.
  294. func AddToUserAgent(r *Request, s string) {
  295. curUA := r.HTTPRequest.Header.Get("User-Agent")
  296. if len(curUA) > 0 {
  297. s = curUA + " " + s
  298. }
  299. r.HTTPRequest.Header.Set("User-Agent", s)
  300. }