api.go 28 KB

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