api.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. /*
  2. * Minio Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2018 Minio, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package minio
  18. import (
  19. "bytes"
  20. "context"
  21. "crypto/md5"
  22. "crypto/sha256"
  23. "errors"
  24. "fmt"
  25. "hash"
  26. "io"
  27. "io/ioutil"
  28. "math/rand"
  29. "net"
  30. "net/http"
  31. "net/http/cookiejar"
  32. "net/http/httputil"
  33. "net/url"
  34. "os"
  35. "runtime"
  36. "strings"
  37. "sync"
  38. "time"
  39. "golang.org/x/net/publicsuffix"
  40. "github.com/minio/minio-go/pkg/credentials"
  41. "github.com/minio/minio-go/pkg/s3signer"
  42. "github.com/minio/minio-go/pkg/s3utils"
  43. )
  44. // Client implements Amazon S3 compatible methods.
  45. type Client struct {
  46. /// Standard options.
  47. // Parsed endpoint url provided by the user.
  48. endpointURL *url.URL
  49. // Holds various credential providers.
  50. credsProvider *credentials.Credentials
  51. // Custom signerType value overrides all credentials.
  52. overrideSignerType credentials.SignatureType
  53. // User supplied.
  54. appInfo struct {
  55. appName string
  56. appVersion string
  57. }
  58. // Indicate whether we are using https or not
  59. secure bool
  60. // Needs allocation.
  61. httpClient *http.Client
  62. bucketLocCache *bucketLocationCache
  63. // Advanced functionality.
  64. isTraceEnabled bool
  65. traceOutput io.Writer
  66. // S3 specific accelerated endpoint.
  67. s3AccelerateEndpoint string
  68. // Region endpoint
  69. region string
  70. // Random seed.
  71. random *rand.Rand
  72. // lookup indicates type of url lookup supported by server. If not specified,
  73. // default to Auto.
  74. lookup BucketLookupType
  75. }
  76. // Options for New method
  77. type Options struct {
  78. Creds *credentials.Credentials
  79. Secure bool
  80. Region string
  81. BucketLookup BucketLookupType
  82. // Add future fields here
  83. }
  84. // Global constants.
  85. const (
  86. libraryName = "minio-go"
  87. libraryVersion = "v6.0.14"
  88. )
  89. // User Agent should always following the below style.
  90. // Please open an issue to discuss any new changes here.
  91. //
  92. // Minio (OS; ARCH) LIB/VER APP/VER
  93. const (
  94. libraryUserAgentPrefix = "Minio (" + runtime.GOOS + "; " + runtime.GOARCH + ") "
  95. libraryUserAgent = libraryUserAgentPrefix + libraryName + "/" + libraryVersion
  96. )
  97. // BucketLookupType is type of url lookup supported by server.
  98. type BucketLookupType int
  99. // Different types of url lookup supported by the server.Initialized to BucketLookupAuto
  100. const (
  101. BucketLookupAuto BucketLookupType = iota
  102. BucketLookupDNS
  103. BucketLookupPath
  104. )
  105. // NewV2 - instantiate minio client with Amazon S3 signature version
  106. // '2' compatibility.
  107. func NewV2(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
  108. creds := credentials.NewStaticV2(accessKeyID, secretAccessKey, "")
  109. clnt, err := privateNew(endpoint, creds, secure, "", BucketLookupAuto)
  110. if err != nil {
  111. return nil, err
  112. }
  113. clnt.overrideSignerType = credentials.SignatureV2
  114. return clnt, nil
  115. }
  116. // NewV4 - instantiate minio client with Amazon S3 signature version
  117. // '4' compatibility.
  118. func NewV4(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
  119. creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
  120. clnt, err := privateNew(endpoint, creds, secure, "", BucketLookupAuto)
  121. if err != nil {
  122. return nil, err
  123. }
  124. clnt.overrideSignerType = credentials.SignatureV4
  125. return clnt, nil
  126. }
  127. // New - instantiate minio client, adds automatic verification of signature.
  128. func New(endpoint, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
  129. creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
  130. clnt, err := privateNew(endpoint, creds, secure, "", BucketLookupAuto)
  131. if err != nil {
  132. return nil, err
  133. }
  134. // Google cloud storage should be set to signature V2, force it if not.
  135. if s3utils.IsGoogleEndpoint(*clnt.endpointURL) {
  136. clnt.overrideSignerType = credentials.SignatureV2
  137. }
  138. // If Amazon S3 set to signature v4.
  139. if s3utils.IsAmazonEndpoint(*clnt.endpointURL) {
  140. clnt.overrideSignerType = credentials.SignatureV4
  141. }
  142. return clnt, nil
  143. }
  144. // NewWithCredentials - instantiate minio client with credentials provider
  145. // for retrieving credentials from various credentials provider such as
  146. // IAM, File, Env etc.
  147. func NewWithCredentials(endpoint string, creds *credentials.Credentials, secure bool, region string) (*Client, error) {
  148. return privateNew(endpoint, creds, secure, region, BucketLookupAuto)
  149. }
  150. // NewWithRegion - instantiate minio client, with region configured. Unlike New(),
  151. // NewWithRegion avoids bucket-location lookup operations and it is slightly faster.
  152. // Use this function when if your application deals with single region.
  153. func NewWithRegion(endpoint, accessKeyID, secretAccessKey string, secure bool, region string) (*Client, error) {
  154. creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
  155. return privateNew(endpoint, creds, secure, region, BucketLookupAuto)
  156. }
  157. // NewWithOptions - instantiate minio client with options
  158. func NewWithOptions(endpoint string, opts *Options) (*Client, error) {
  159. return privateNew(endpoint, opts.Creds, opts.Secure, opts.Region, opts.BucketLookup)
  160. }
  161. // lockedRandSource provides protected rand source, implements rand.Source interface.
  162. type lockedRandSource struct {
  163. lk sync.Mutex
  164. src rand.Source
  165. }
  166. // Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
  167. func (r *lockedRandSource) Int63() (n int64) {
  168. r.lk.Lock()
  169. n = r.src.Int63()
  170. r.lk.Unlock()
  171. return
  172. }
  173. // Seed uses the provided seed value to initialize the generator to a
  174. // deterministic state.
  175. func (r *lockedRandSource) Seed(seed int64) {
  176. r.lk.Lock()
  177. r.src.Seed(seed)
  178. r.lk.Unlock()
  179. }
  180. // Redirect requests by re signing the request.
  181. func (c *Client) redirectHeaders(req *http.Request, via []*http.Request) error {
  182. if len(via) >= 5 {
  183. return errors.New("stopped after 5 redirects")
  184. }
  185. if len(via) == 0 {
  186. return nil
  187. }
  188. lastRequest := via[len(via)-1]
  189. var reAuth bool
  190. for attr, val := range lastRequest.Header {
  191. // if hosts do not match do not copy Authorization header
  192. if attr == "Authorization" && req.Host != lastRequest.Host {
  193. reAuth = true
  194. continue
  195. }
  196. if _, ok := req.Header[attr]; !ok {
  197. req.Header[attr] = val
  198. }
  199. }
  200. *c.endpointURL = *req.URL
  201. value, err := c.credsProvider.Get()
  202. if err != nil {
  203. return err
  204. }
  205. var (
  206. signerType = value.SignerType
  207. accessKeyID = value.AccessKeyID
  208. secretAccessKey = value.SecretAccessKey
  209. sessionToken = value.SessionToken
  210. region = c.region
  211. )
  212. // Custom signer set then override the behavior.
  213. if c.overrideSignerType != credentials.SignatureDefault {
  214. signerType = c.overrideSignerType
  215. }
  216. // If signerType returned by credentials helper is anonymous,
  217. // then do not sign regardless of signerType override.
  218. if value.SignerType == credentials.SignatureAnonymous {
  219. signerType = credentials.SignatureAnonymous
  220. }
  221. if reAuth {
  222. // Check if there is no region override, if not get it from the URL if possible.
  223. if region == "" {
  224. region = s3utils.GetRegionFromURL(*c.endpointURL)
  225. }
  226. switch {
  227. case signerType.IsV2():
  228. return errors.New("signature V2 cannot support redirection")
  229. case signerType.IsV4():
  230. req = s3signer.SignV4(*req, accessKeyID, secretAccessKey, sessionToken, getDefaultLocation(*c.endpointURL, region))
  231. }
  232. }
  233. return nil
  234. }
  235. func privateNew(endpoint string, creds *credentials.Credentials, secure bool, region string, lookup BucketLookupType) (*Client, error) {
  236. // construct endpoint.
  237. endpointURL, err := getEndpointURL(endpoint, secure)
  238. if err != nil {
  239. return nil, err
  240. }
  241. // Initialize cookies to preserve server sent cookies if any and replay
  242. // them upon each request.
  243. jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
  244. if err != nil {
  245. return nil, err
  246. }
  247. // instantiate new Client.
  248. clnt := new(Client)
  249. // Save the credentials.
  250. clnt.credsProvider = creds
  251. // Remember whether we are using https or not
  252. clnt.secure = secure
  253. // Save endpoint URL, user agent for future uses.
  254. clnt.endpointURL = endpointURL
  255. // Instantiate http client and bucket location cache.
  256. clnt.httpClient = &http.Client{
  257. Jar: jar,
  258. Transport: DefaultTransport,
  259. CheckRedirect: clnt.redirectHeaders,
  260. }
  261. // Sets custom region, if region is empty bucket location cache is used automatically.
  262. if region == "" {
  263. region = s3utils.GetRegionFromURL(*clnt.endpointURL)
  264. }
  265. clnt.region = region
  266. // Instantiate bucket location cache.
  267. clnt.bucketLocCache = newBucketLocationCache()
  268. // Introduce a new locked random seed.
  269. clnt.random = rand.New(&lockedRandSource{src: rand.NewSource(time.Now().UTC().UnixNano())})
  270. // Sets bucket lookup style, whether server accepts DNS or Path lookup. Default is Auto - determined
  271. // by the SDK. When Auto is specified, DNS lookup is used for Amazon/Google cloud endpoints and Path for all other endpoints.
  272. clnt.lookup = lookup
  273. // Return.
  274. return clnt, nil
  275. }
  276. // SetAppInfo - add application details to user agent.
  277. func (c *Client) SetAppInfo(appName string, appVersion string) {
  278. // if app name and version not set, we do not set a new user agent.
  279. if appName != "" && appVersion != "" {
  280. c.appInfo = struct {
  281. appName string
  282. appVersion string
  283. }{}
  284. c.appInfo.appName = appName
  285. c.appInfo.appVersion = appVersion
  286. }
  287. }
  288. // SetCustomTransport - set new custom transport.
  289. func (c *Client) SetCustomTransport(customHTTPTransport http.RoundTripper) {
  290. // Set this to override default transport
  291. // ``http.DefaultTransport``.
  292. //
  293. // This transport is usually needed for debugging OR to add your
  294. // own custom TLS certificates on the client transport, for custom
  295. // CA's and certs which are not part of standard certificate
  296. // authority follow this example :-
  297. //
  298. // tr := &http.Transport{
  299. // TLSClientConfig: &tls.Config{RootCAs: pool},
  300. // DisableCompression: true,
  301. // }
  302. // api.SetCustomTransport(tr)
  303. //
  304. if c.httpClient != nil {
  305. c.httpClient.Transport = customHTTPTransport
  306. }
  307. }
  308. // TraceOn - enable HTTP tracing.
  309. func (c *Client) TraceOn(outputStream io.Writer) {
  310. // if outputStream is nil then default to os.Stdout.
  311. if outputStream == nil {
  312. outputStream = os.Stdout
  313. }
  314. // Sets a new output stream.
  315. c.traceOutput = outputStream
  316. // Enable tracing.
  317. c.isTraceEnabled = true
  318. }
  319. // TraceOff - disable HTTP tracing.
  320. func (c *Client) TraceOff() {
  321. // Disable tracing.
  322. c.isTraceEnabled = false
  323. }
  324. // SetS3TransferAccelerate - turns s3 accelerated endpoint on or off for all your
  325. // requests. This feature is only specific to S3 for all other endpoints this
  326. // function does nothing. To read further details on s3 transfer acceleration
  327. // please vist -
  328. // http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
  329. func (c *Client) SetS3TransferAccelerate(accelerateEndpoint string) {
  330. if s3utils.IsAmazonEndpoint(*c.endpointURL) {
  331. c.s3AccelerateEndpoint = accelerateEndpoint
  332. }
  333. }
  334. // Hash materials provides relevant initialized hash algo writers
  335. // based on the expected signature type.
  336. //
  337. // - For signature v4 request if the connection is insecure compute only sha256.
  338. // - For signature v4 request if the connection is secure compute only md5.
  339. // - For anonymous request compute md5.
  340. func (c *Client) hashMaterials() (hashAlgos map[string]hash.Hash, hashSums map[string][]byte) {
  341. hashSums = make(map[string][]byte)
  342. hashAlgos = make(map[string]hash.Hash)
  343. if c.overrideSignerType.IsV4() {
  344. if c.secure {
  345. hashAlgos["md5"] = md5.New()
  346. } else {
  347. hashAlgos["sha256"] = sha256.New()
  348. }
  349. } else {
  350. if c.overrideSignerType.IsAnonymous() {
  351. hashAlgos["md5"] = md5.New()
  352. }
  353. }
  354. return hashAlgos, hashSums
  355. }
  356. // requestMetadata - is container for all the values to make a request.
  357. type requestMetadata struct {
  358. // If set newRequest presigns the URL.
  359. presignURL bool
  360. // User supplied.
  361. bucketName string
  362. objectName string
  363. queryValues url.Values
  364. customHeader http.Header
  365. expires int64
  366. // Generated by our internal code.
  367. bucketLocation string
  368. contentBody io.Reader
  369. contentLength int64
  370. contentMD5Base64 string // carries base64 encoded md5sum
  371. contentSHA256Hex string // carries hex encoded sha256sum
  372. }
  373. // dumpHTTP - dump HTTP request and response.
  374. func (c Client) dumpHTTP(req *http.Request, resp *http.Response) error {
  375. // Starts http dump.
  376. _, err := fmt.Fprintln(c.traceOutput, "---------START-HTTP---------")
  377. if err != nil {
  378. return err
  379. }
  380. // Filter out Signature field from Authorization header.
  381. origAuth := req.Header.Get("Authorization")
  382. if origAuth != "" {
  383. req.Header.Set("Authorization", redactSignature(origAuth))
  384. }
  385. // Only display request header.
  386. reqTrace, err := httputil.DumpRequestOut(req, false)
  387. if err != nil {
  388. return err
  389. }
  390. // Write request to trace output.
  391. _, err = fmt.Fprint(c.traceOutput, string(reqTrace))
  392. if err != nil {
  393. return err
  394. }
  395. // Only display response header.
  396. var respTrace []byte
  397. // For errors we make sure to dump response body as well.
  398. if resp.StatusCode != http.StatusOK &&
  399. resp.StatusCode != http.StatusPartialContent &&
  400. resp.StatusCode != http.StatusNoContent {
  401. respTrace, err = httputil.DumpResponse(resp, true)
  402. if err != nil {
  403. return err
  404. }
  405. } else {
  406. respTrace, err = httputil.DumpResponse(resp, false)
  407. if err != nil {
  408. return err
  409. }
  410. }
  411. // Write response to trace output.
  412. _, err = fmt.Fprint(c.traceOutput, strings.TrimSuffix(string(respTrace), "\r\n"))
  413. if err != nil {
  414. return err
  415. }
  416. // Ends the http dump.
  417. _, err = fmt.Fprintln(c.traceOutput, "---------END-HTTP---------")
  418. if err != nil {
  419. return err
  420. }
  421. // Returns success.
  422. return nil
  423. }
  424. // do - execute http request.
  425. func (c Client) do(req *http.Request) (*http.Response, error) {
  426. resp, err := c.httpClient.Do(req)
  427. if err != nil {
  428. // Handle this specifically for now until future Golang versions fix this issue properly.
  429. if urlErr, ok := err.(*url.Error); ok {
  430. if strings.Contains(urlErr.Err.Error(), "EOF") {
  431. return nil, &url.Error{
  432. Op: urlErr.Op,
  433. URL: urlErr.URL,
  434. Err: errors.New("Connection closed by foreign host " + urlErr.URL + ". Retry again."),
  435. }
  436. }
  437. }
  438. return nil, err
  439. }
  440. // Response cannot be non-nil, report error if thats the case.
  441. if resp == nil {
  442. msg := "Response is empty. " + reportIssue
  443. return nil, ErrInvalidArgument(msg)
  444. }
  445. // If trace is enabled, dump http request and response.
  446. if c.isTraceEnabled {
  447. err = c.dumpHTTP(req, resp)
  448. if err != nil {
  449. return nil, err
  450. }
  451. }
  452. return resp, nil
  453. }
  454. // List of success status.
  455. var successStatus = []int{
  456. http.StatusOK,
  457. http.StatusNoContent,
  458. http.StatusPartialContent,
  459. }
  460. // executeMethod - instantiates a given method, and retries the
  461. // request upon any error up to maxRetries attempts in a binomially
  462. // delayed manner using a standard back off algorithm.
  463. func (c Client) executeMethod(ctx context.Context, method string, metadata requestMetadata) (res *http.Response, err error) {
  464. var isRetryable bool // Indicates if request can be retried.
  465. var bodySeeker io.Seeker // Extracted seeker from io.Reader.
  466. var reqRetry = MaxRetry // Indicates how many times we can retry the request
  467. if metadata.contentBody != nil {
  468. // Check if body is seekable then it is retryable.
  469. bodySeeker, isRetryable = metadata.contentBody.(io.Seeker)
  470. switch bodySeeker {
  471. case os.Stdin, os.Stdout, os.Stderr:
  472. isRetryable = false
  473. }
  474. // Retry only when reader is seekable
  475. if !isRetryable {
  476. reqRetry = 1
  477. }
  478. // Figure out if the body can be closed - if yes
  479. // we will definitely close it upon the function
  480. // return.
  481. bodyCloser, ok := metadata.contentBody.(io.Closer)
  482. if ok {
  483. defer bodyCloser.Close()
  484. }
  485. }
  486. // Create a done channel to control 'newRetryTimer' go routine.
  487. doneCh := make(chan struct{}, 1)
  488. // Indicate to our routine to exit cleanly upon return.
  489. defer close(doneCh)
  490. // Blank indentifier is kept here on purpose since 'range' without
  491. // blank identifiers is only supported since go1.4
  492. // https://golang.org/doc/go1.4#forrange.
  493. for range c.newRetryTimer(reqRetry, DefaultRetryUnit, DefaultRetryCap, MaxJitter, doneCh) {
  494. // Retry executes the following function body if request has an
  495. // error until maxRetries have been exhausted, retry attempts are
  496. // performed after waiting for a given period of time in a
  497. // binomial fashion.
  498. if isRetryable {
  499. // Seek back to beginning for each attempt.
  500. if _, err = bodySeeker.Seek(0, 0); err != nil {
  501. // If seek failed, no need to retry.
  502. return nil, err
  503. }
  504. }
  505. // Instantiate a new request.
  506. var req *http.Request
  507. req, err = c.newRequest(method, metadata)
  508. if err != nil {
  509. errResponse := ToErrorResponse(err)
  510. if isS3CodeRetryable(errResponse.Code) {
  511. continue // Retry.
  512. }
  513. return nil, err
  514. }
  515. // Add context to request
  516. req = req.WithContext(ctx)
  517. // Initiate the request.
  518. res, err = c.do(req)
  519. if err != nil {
  520. // For supported http requests errors verify.
  521. if isHTTPReqErrorRetryable(err) {
  522. continue // Retry.
  523. }
  524. // For other errors, return here no need to retry.
  525. return nil, err
  526. }
  527. // For any known successful http status, return quickly.
  528. for _, httpStatus := range successStatus {
  529. if httpStatus == res.StatusCode {
  530. return res, nil
  531. }
  532. }
  533. // Read the body to be saved later.
  534. errBodyBytes, err := ioutil.ReadAll(res.Body)
  535. // res.Body should be closed
  536. closeResponse(res)
  537. if err != nil {
  538. return nil, err
  539. }
  540. // Save the body.
  541. errBodySeeker := bytes.NewReader(errBodyBytes)
  542. res.Body = ioutil.NopCloser(errBodySeeker)
  543. // For errors verify if its retryable otherwise fail quickly.
  544. errResponse := ToErrorResponse(httpRespToErrorResponse(res, metadata.bucketName, metadata.objectName))
  545. // Save the body back again.
  546. errBodySeeker.Seek(0, 0) // Seek back to starting point.
  547. res.Body = ioutil.NopCloser(errBodySeeker)
  548. // Bucket region if set in error response and the error
  549. // code dictates invalid region, we can retry the request
  550. // with the new region.
  551. //
  552. // Additionally we should only retry if bucketLocation and custom
  553. // region is empty.
  554. if metadata.bucketLocation == "" && c.region == "" {
  555. if errResponse.Code == "AuthorizationHeaderMalformed" || errResponse.Code == "InvalidRegion" {
  556. if metadata.bucketName != "" && errResponse.Region != "" {
  557. // Gather Cached location only if bucketName is present.
  558. if _, cachedLocationError := c.bucketLocCache.Get(metadata.bucketName); cachedLocationError != false {
  559. c.bucketLocCache.Set(metadata.bucketName, errResponse.Region)
  560. continue // Retry.
  561. }
  562. }
  563. }
  564. }
  565. // Verify if error response code is retryable.
  566. if isS3CodeRetryable(errResponse.Code) {
  567. continue // Retry.
  568. }
  569. // Verify if http status code is retryable.
  570. if isHTTPStatusRetryable(res.StatusCode) {
  571. continue // Retry.
  572. }
  573. // For all other cases break out of the retry loop.
  574. break
  575. }
  576. return res, err
  577. }
  578. // newRequest - instantiate a new HTTP request for a given method.
  579. func (c Client) newRequest(method string, metadata requestMetadata) (req *http.Request, err error) {
  580. // If no method is supplied default to 'POST'.
  581. if method == "" {
  582. method = "POST"
  583. }
  584. location := metadata.bucketLocation
  585. if location == "" {
  586. if metadata.bucketName != "" {
  587. // Gather location only if bucketName is present.
  588. location, err = c.getBucketLocation(metadata.bucketName)
  589. if err != nil {
  590. if ToErrorResponse(err).Code != "AccessDenied" {
  591. return nil, err
  592. }
  593. }
  594. // Upon AccessDenied error on fetching bucket location, default
  595. // to possible locations based on endpoint URL. This can usually
  596. // happen when GetBucketLocation() is disabled using IAM policies.
  597. }
  598. if location == "" {
  599. location = getDefaultLocation(*c.endpointURL, c.region)
  600. }
  601. }
  602. // Look if target url supports virtual host.
  603. isVirtualHost := c.isVirtualHostStyleRequest(*c.endpointURL, metadata.bucketName)
  604. // Construct a new target URL.
  605. targetURL, err := c.makeTargetURL(metadata.bucketName, metadata.objectName, location, isVirtualHost, metadata.queryValues)
  606. if err != nil {
  607. return nil, err
  608. }
  609. // Initialize a new HTTP request for the method.
  610. req, err = http.NewRequest(method, targetURL.String(), nil)
  611. if err != nil {
  612. return nil, err
  613. }
  614. // Get credentials from the configured credentials provider.
  615. value, err := c.credsProvider.Get()
  616. if err != nil {
  617. return nil, err
  618. }
  619. var (
  620. signerType = value.SignerType
  621. accessKeyID = value.AccessKeyID
  622. secretAccessKey = value.SecretAccessKey
  623. sessionToken = value.SessionToken
  624. )
  625. // Custom signer set then override the behavior.
  626. if c.overrideSignerType != credentials.SignatureDefault {
  627. signerType = c.overrideSignerType
  628. }
  629. // If signerType returned by credentials helper is anonymous,
  630. // then do not sign regardless of signerType override.
  631. if value.SignerType == credentials.SignatureAnonymous {
  632. signerType = credentials.SignatureAnonymous
  633. }
  634. // Generate presign url if needed, return right here.
  635. if metadata.expires != 0 && metadata.presignURL {
  636. if signerType.IsAnonymous() {
  637. return nil, ErrInvalidArgument("Presigned URLs cannot be generated with anonymous credentials.")
  638. }
  639. if signerType.IsV2() {
  640. // Presign URL with signature v2.
  641. req = s3signer.PreSignV2(*req, accessKeyID, secretAccessKey, metadata.expires, isVirtualHost)
  642. } else if signerType.IsV4() {
  643. // Presign URL with signature v4.
  644. req = s3signer.PreSignV4(*req, accessKeyID, secretAccessKey, sessionToken, location, metadata.expires)
  645. }
  646. return req, nil
  647. }
  648. // Set 'User-Agent' header for the request.
  649. c.setUserAgent(req)
  650. // Set all headers.
  651. for k, v := range metadata.customHeader {
  652. req.Header.Set(k, v[0])
  653. }
  654. // Go net/http notoriously closes the request body.
  655. // - The request Body, if non-nil, will be closed by the underlying Transport, even on errors.
  656. // This can cause underlying *os.File seekers to fail, avoid that
  657. // by making sure to wrap the closer as a nop.
  658. if metadata.contentLength == 0 {
  659. req.Body = nil
  660. } else {
  661. req.Body = ioutil.NopCloser(metadata.contentBody)
  662. }
  663. // Set incoming content-length.
  664. req.ContentLength = metadata.contentLength
  665. if req.ContentLength <= -1 {
  666. // For unknown content length, we upload using transfer-encoding: chunked.
  667. req.TransferEncoding = []string{"chunked"}
  668. }
  669. // set md5Sum for content protection.
  670. if len(metadata.contentMD5Base64) > 0 {
  671. req.Header.Set("Content-Md5", metadata.contentMD5Base64)
  672. }
  673. // For anonymous requests just return.
  674. if signerType.IsAnonymous() {
  675. return req, nil
  676. }
  677. switch {
  678. case signerType.IsV2():
  679. // Add signature version '2' authorization header.
  680. req = s3signer.SignV2(*req, accessKeyID, secretAccessKey, isVirtualHost)
  681. case metadata.objectName != "" && method == "PUT" && metadata.customHeader.Get("X-Amz-Copy-Source") == "" && !c.secure:
  682. // Streaming signature is used by default for a PUT object request. Additionally we also
  683. // look if the initialized client is secure, if yes then we don't need to perform
  684. // streaming signature.
  685. req = s3signer.StreamingSignV4(req, accessKeyID,
  686. secretAccessKey, sessionToken, location, metadata.contentLength, time.Now().UTC())
  687. default:
  688. // Set sha256 sum for signature calculation only with signature version '4'.
  689. shaHeader := unsignedPayload
  690. if metadata.contentSHA256Hex != "" {
  691. shaHeader = metadata.contentSHA256Hex
  692. }
  693. req.Header.Set("X-Amz-Content-Sha256", shaHeader)
  694. // Add signature version '4' authorization header.
  695. req = s3signer.SignV4(*req, accessKeyID, secretAccessKey, sessionToken, location)
  696. }
  697. // Return request.
  698. return req, nil
  699. }
  700. // set User agent.
  701. func (c Client) setUserAgent(req *http.Request) {
  702. req.Header.Set("User-Agent", libraryUserAgent)
  703. if c.appInfo.appName != "" && c.appInfo.appVersion != "" {
  704. req.Header.Set("User-Agent", libraryUserAgent+" "+c.appInfo.appName+"/"+c.appInfo.appVersion)
  705. }
  706. }
  707. // makeTargetURL make a new target url.
  708. func (c Client) makeTargetURL(bucketName, objectName, bucketLocation string, isVirtualHostStyle bool, queryValues url.Values) (*url.URL, error) {
  709. host := c.endpointURL.Host
  710. // For Amazon S3 endpoint, try to fetch location based endpoint.
  711. if s3utils.IsAmazonEndpoint(*c.endpointURL) {
  712. if c.s3AccelerateEndpoint != "" && bucketName != "" {
  713. // http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
  714. // Disable transfer acceleration for non-compliant bucket names.
  715. if strings.Contains(bucketName, ".") {
  716. return nil, ErrTransferAccelerationBucket(bucketName)
  717. }
  718. // If transfer acceleration is requested set new host.
  719. // For more details about enabling transfer acceleration read here.
  720. // http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
  721. host = c.s3AccelerateEndpoint
  722. } else {
  723. // Do not change the host if the endpoint URL is a FIPS S3 endpoint.
  724. if !s3utils.IsAmazonFIPSEndpoint(*c.endpointURL) {
  725. // Fetch new host based on the bucket location.
  726. host = getS3Endpoint(bucketLocation)
  727. }
  728. }
  729. }
  730. // Save scheme.
  731. scheme := c.endpointURL.Scheme
  732. // Strip port 80 and 443 so we won't send these ports in Host header.
  733. // The reason is that browsers and curl automatically remove :80 and :443
  734. // with the generated presigned urls, then a signature mismatch error.
  735. if h, p, err := net.SplitHostPort(host); err == nil {
  736. if scheme == "http" && p == "80" || scheme == "https" && p == "443" {
  737. host = h
  738. }
  739. }
  740. urlStr := scheme + "://" + host + "/"
  741. // Make URL only if bucketName is available, otherwise use the
  742. // endpoint URL.
  743. if bucketName != "" {
  744. // If endpoint supports virtual host style use that always.
  745. // Currently only S3 and Google Cloud Storage would support
  746. // virtual host style.
  747. if isVirtualHostStyle {
  748. urlStr = scheme + "://" + bucketName + "." + host + "/"
  749. if objectName != "" {
  750. urlStr = urlStr + s3utils.EncodePath(objectName)
  751. }
  752. } else {
  753. // If not fall back to using path style.
  754. urlStr = urlStr + bucketName + "/"
  755. if objectName != "" {
  756. urlStr = urlStr + s3utils.EncodePath(objectName)
  757. }
  758. }
  759. }
  760. // If there are any query values, add them to the end.
  761. if len(queryValues) > 0 {
  762. urlStr = urlStr + "?" + s3utils.QueryEncode(queryValues)
  763. }
  764. return url.Parse(urlStr)
  765. }
  766. // returns true if virtual hosted style requests are to be used.
  767. func (c *Client) isVirtualHostStyleRequest(url url.URL, bucketName string) bool {
  768. if bucketName == "" {
  769. return false
  770. }
  771. if c.lookup == BucketLookupDNS {
  772. return true
  773. }
  774. if c.lookup == BucketLookupPath {
  775. return false
  776. }
  777. // default to virtual only for Amazon/Google storage. In all other cases use
  778. // path style requests
  779. return s3utils.IsVirtualHostSupported(url, bucketName)
  780. }