gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

api.go (28799B)


      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 
     18 package minio
     19 
     20 import (
     21 	"bytes"
     22 	"context"
     23 	"encoding/base64"
     24 	"errors"
     25 	"fmt"
     26 	"hash/crc32"
     27 	"io"
     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 	"sync/atomic"
     39 	"time"
     40 
     41 	md5simd "github.com/minio/md5-simd"
     42 	"github.com/minio/minio-go/v7/pkg/credentials"
     43 	"github.com/minio/minio-go/v7/pkg/s3utils"
     44 	"github.com/minio/minio-go/v7/pkg/signer"
     45 	"golang.org/x/net/publicsuffix"
     46 )
     47 
     48 // Client implements Amazon S3 compatible methods.
     49 type Client struct {
     50 	//  Standard options.
     51 
     52 	// Parsed endpoint url provided by the user.
     53 	endpointURL *url.URL
     54 
     55 	// Holds various credential providers.
     56 	credsProvider *credentials.Credentials
     57 
     58 	// Custom signerType value overrides all credentials.
     59 	overrideSignerType credentials.SignatureType
     60 
     61 	// User supplied.
     62 	appInfo struct {
     63 		appName    string
     64 		appVersion string
     65 	}
     66 
     67 	// Indicate whether we are using https or not
     68 	secure bool
     69 
     70 	// Needs allocation.
     71 	httpClient     *http.Client
     72 	bucketLocCache *bucketLocationCache
     73 
     74 	// Advanced functionality.
     75 	isTraceEnabled  bool
     76 	traceErrorsOnly bool
     77 	traceOutput     io.Writer
     78 
     79 	// S3 specific accelerated endpoint.
     80 	s3AccelerateEndpoint string
     81 
     82 	// Region endpoint
     83 	region string
     84 
     85 	// Random seed.
     86 	random *rand.Rand
     87 
     88 	// lookup indicates type of url lookup supported by server. If not specified,
     89 	// default to Auto.
     90 	lookup BucketLookupType
     91 
     92 	// Factory for MD5 hash functions.
     93 	md5Hasher    func() md5simd.Hasher
     94 	sha256Hasher func() md5simd.Hasher
     95 
     96 	healthStatus int32
     97 
     98 	trailingHeaderSupport bool
     99 }
    100 
    101 // Options for New method
    102 type Options struct {
    103 	Creds        *credentials.Credentials
    104 	Secure       bool
    105 	Transport    http.RoundTripper
    106 	Region       string
    107 	BucketLookup BucketLookupType
    108 
    109 	// Allows setting a custom region lookup based on URL pattern
    110 	// not all URL patterns are covered by this library so if you
    111 	// have a custom endpoints with many regions you can use this
    112 	// function to perform region lookups appropriately.
    113 	CustomRegionViaURL func(u url.URL) string
    114 
    115 	// TrailingHeaders indicates server support of trailing headers.
    116 	// Only supported for v4 signatures.
    117 	TrailingHeaders bool
    118 
    119 	// Custom hash routines. Leave nil to use standard.
    120 	CustomMD5    func() md5simd.Hasher
    121 	CustomSHA256 func() md5simd.Hasher
    122 }
    123 
    124 // Global constants.
    125 const (
    126 	libraryName    = "minio-go"
    127 	libraryVersion = "v7.0.56"
    128 )
    129 
    130 // User Agent should always following the below style.
    131 // Please open an issue to discuss any new changes here.
    132 //
    133 //	MinIO (OS; ARCH) LIB/VER APP/VER
    134 const (
    135 	libraryUserAgentPrefix = "MinIO (" + runtime.GOOS + "; " + runtime.GOARCH + ") "
    136 	libraryUserAgent       = libraryUserAgentPrefix + libraryName + "/" + libraryVersion
    137 )
    138 
    139 // BucketLookupType is type of url lookup supported by server.
    140 type BucketLookupType int
    141 
    142 // Different types of url lookup supported by the server.Initialized to BucketLookupAuto
    143 const (
    144 	BucketLookupAuto BucketLookupType = iota
    145 	BucketLookupDNS
    146 	BucketLookupPath
    147 )
    148 
    149 // New - instantiate minio client with options
    150 func New(endpoint string, opts *Options) (*Client, error) {
    151 	if opts == nil {
    152 		return nil, errors.New("no options provided")
    153 	}
    154 	clnt, err := privateNew(endpoint, opts)
    155 	if err != nil {
    156 		return nil, err
    157 	}
    158 	// Google cloud storage should be set to signature V2, force it if not.
    159 	if s3utils.IsGoogleEndpoint(*clnt.endpointURL) {
    160 		clnt.overrideSignerType = credentials.SignatureV2
    161 	}
    162 	// If Amazon S3 set to signature v4.
    163 	if s3utils.IsAmazonEndpoint(*clnt.endpointURL) {
    164 		clnt.overrideSignerType = credentials.SignatureV4
    165 	}
    166 
    167 	return clnt, nil
    168 }
    169 
    170 // EndpointURL returns the URL of the S3 endpoint.
    171 func (c *Client) EndpointURL() *url.URL {
    172 	endpoint := *c.endpointURL // copy to prevent callers from modifying internal state
    173 	return &endpoint
    174 }
    175 
    176 // lockedRandSource provides protected rand source, implements rand.Source interface.
    177 type lockedRandSource struct {
    178 	lk  sync.Mutex
    179 	src rand.Source
    180 }
    181 
    182 // Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
    183 func (r *lockedRandSource) Int63() (n int64) {
    184 	r.lk.Lock()
    185 	n = r.src.Int63()
    186 	r.lk.Unlock()
    187 	return
    188 }
    189 
    190 // Seed uses the provided seed value to initialize the generator to a
    191 // deterministic state.
    192 func (r *lockedRandSource) Seed(seed int64) {
    193 	r.lk.Lock()
    194 	r.src.Seed(seed)
    195 	r.lk.Unlock()
    196 }
    197 
    198 func privateNew(endpoint string, opts *Options) (*Client, error) {
    199 	// construct endpoint.
    200 	endpointURL, err := getEndpointURL(endpoint, opts.Secure)
    201 	if err != nil {
    202 		return nil, err
    203 	}
    204 
    205 	// Initialize cookies to preserve server sent cookies if any and replay
    206 	// them upon each request.
    207 	jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
    208 	if err != nil {
    209 		return nil, err
    210 	}
    211 
    212 	// instantiate new Client.
    213 	clnt := new(Client)
    214 
    215 	// Save the credentials.
    216 	clnt.credsProvider = opts.Creds
    217 
    218 	// Remember whether we are using https or not
    219 	clnt.secure = opts.Secure
    220 
    221 	// Save endpoint URL, user agent for future uses.
    222 	clnt.endpointURL = endpointURL
    223 
    224 	transport := opts.Transport
    225 	if transport == nil {
    226 		transport, err = DefaultTransport(opts.Secure)
    227 		if err != nil {
    228 			return nil, err
    229 		}
    230 	}
    231 
    232 	// Instantiate http client and bucket location cache.
    233 	clnt.httpClient = &http.Client{
    234 		Jar:       jar,
    235 		Transport: transport,
    236 		CheckRedirect: func(req *http.Request, via []*http.Request) error {
    237 			return http.ErrUseLastResponse
    238 		},
    239 	}
    240 
    241 	// Sets custom region, if region is empty bucket location cache is used automatically.
    242 	if opts.Region == "" {
    243 		if opts.CustomRegionViaURL != nil {
    244 			opts.Region = opts.CustomRegionViaURL(*clnt.endpointURL)
    245 		} else {
    246 			opts.Region = s3utils.GetRegionFromURL(*clnt.endpointURL)
    247 		}
    248 	}
    249 	clnt.region = opts.Region
    250 
    251 	// Instantiate bucket location cache.
    252 	clnt.bucketLocCache = newBucketLocationCache()
    253 
    254 	// Introduce a new locked random seed.
    255 	clnt.random = rand.New(&lockedRandSource{src: rand.NewSource(time.Now().UTC().UnixNano())})
    256 
    257 	// Add default md5 hasher.
    258 	clnt.md5Hasher = opts.CustomMD5
    259 	clnt.sha256Hasher = opts.CustomSHA256
    260 	if clnt.md5Hasher == nil {
    261 		clnt.md5Hasher = newMd5Hasher
    262 	}
    263 	if clnt.sha256Hasher == nil {
    264 		clnt.sha256Hasher = newSHA256Hasher
    265 	}
    266 
    267 	clnt.trailingHeaderSupport = opts.TrailingHeaders && clnt.overrideSignerType.IsV4()
    268 
    269 	// Sets bucket lookup style, whether server accepts DNS or Path lookup. Default is Auto - determined
    270 	// by the SDK. When Auto is specified, DNS lookup is used for Amazon/Google cloud endpoints and Path for all other endpoints.
    271 	clnt.lookup = opts.BucketLookup
    272 
    273 	// healthcheck is not initialized
    274 	clnt.healthStatus = unknown
    275 
    276 	// Return.
    277 	return clnt, nil
    278 }
    279 
    280 // SetAppInfo - add application details to user agent.
    281 func (c *Client) SetAppInfo(appName string, appVersion string) {
    282 	// if app name and version not set, we do not set a new user agent.
    283 	if appName != "" && appVersion != "" {
    284 		c.appInfo.appName = appName
    285 		c.appInfo.appVersion = appVersion
    286 	}
    287 }
    288 
    289 // TraceOn - enable HTTP tracing.
    290 func (c *Client) TraceOn(outputStream io.Writer) {
    291 	// if outputStream is nil then default to os.Stdout.
    292 	if outputStream == nil {
    293 		outputStream = os.Stdout
    294 	}
    295 	// Sets a new output stream.
    296 	c.traceOutput = outputStream
    297 
    298 	// Enable tracing.
    299 	c.isTraceEnabled = true
    300 }
    301 
    302 // TraceErrorsOnlyOn - same as TraceOn, but only errors will be traced.
    303 func (c *Client) TraceErrorsOnlyOn(outputStream io.Writer) {
    304 	c.TraceOn(outputStream)
    305 	c.traceErrorsOnly = true
    306 }
    307 
    308 // TraceErrorsOnlyOff - Turns off the errors only tracing and everything will be traced after this call.
    309 // If all tracing needs to be turned off, call TraceOff().
    310 func (c *Client) TraceErrorsOnlyOff() {
    311 	c.traceErrorsOnly = false
    312 }
    313 
    314 // TraceOff - disable HTTP tracing.
    315 func (c *Client) TraceOff() {
    316 	// Disable tracing.
    317 	c.isTraceEnabled = false
    318 	c.traceErrorsOnly = false
    319 }
    320 
    321 // SetS3TransferAccelerate - turns s3 accelerated endpoint on or off for all your
    322 // requests. This feature is only specific to S3 for all other endpoints this
    323 // function does nothing. To read further details on s3 transfer acceleration
    324 // please vist -
    325 // http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
    326 func (c *Client) SetS3TransferAccelerate(accelerateEndpoint string) {
    327 	if s3utils.IsAmazonEndpoint(*c.endpointURL) {
    328 		c.s3AccelerateEndpoint = accelerateEndpoint
    329 	}
    330 }
    331 
    332 // Hash materials provides relevant initialized hash algo writers
    333 // based on the expected signature type.
    334 //
    335 //   - For signature v4 request if the connection is insecure compute only sha256.
    336 //   - For signature v4 request if the connection is secure compute only md5.
    337 //   - For anonymous request compute md5.
    338 func (c *Client) hashMaterials(isMd5Requested, isSha256Requested bool) (hashAlgos map[string]md5simd.Hasher, hashSums map[string][]byte) {
    339 	hashSums = make(map[string][]byte)
    340 	hashAlgos = make(map[string]md5simd.Hasher)
    341 	if c.overrideSignerType.IsV4() {
    342 		if c.secure {
    343 			hashAlgos["md5"] = c.md5Hasher()
    344 		} else {
    345 			if isSha256Requested {
    346 				hashAlgos["sha256"] = c.sha256Hasher()
    347 			}
    348 		}
    349 	} else {
    350 		if c.overrideSignerType.IsAnonymous() {
    351 			hashAlgos["md5"] = c.md5Hasher()
    352 		}
    353 	}
    354 	if isMd5Requested {
    355 		hashAlgos["md5"] = c.md5Hasher()
    356 	}
    357 	return hashAlgos, hashSums
    358 }
    359 
    360 const (
    361 	unknown = -1
    362 	offline = 0
    363 	online  = 1
    364 )
    365 
    366 // IsOnline returns true if healthcheck enabled and client is online
    367 func (c *Client) IsOnline() bool {
    368 	return !c.IsOffline()
    369 }
    370 
    371 // sets online healthStatus to offline
    372 func (c *Client) markOffline() {
    373 	atomic.CompareAndSwapInt32(&c.healthStatus, online, offline)
    374 }
    375 
    376 // IsOffline returns true if healthcheck enabled and client is offline
    377 func (c *Client) IsOffline() bool {
    378 	return atomic.LoadInt32(&c.healthStatus) == offline
    379 }
    380 
    381 // HealthCheck starts a healthcheck to see if endpoint is up. Returns a context cancellation function
    382 // and and error if health check is already started
    383 func (c *Client) HealthCheck(hcDuration time.Duration) (context.CancelFunc, error) {
    384 	if atomic.LoadInt32(&c.healthStatus) == online {
    385 		return nil, fmt.Errorf("health check is running")
    386 	}
    387 	if hcDuration < 1*time.Second {
    388 		return nil, fmt.Errorf("health check duration should be atleast 1 second")
    389 	}
    390 	ctx, cancelFn := context.WithCancel(context.Background())
    391 	atomic.StoreInt32(&c.healthStatus, online)
    392 	probeBucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "probe-health-")
    393 	go func(duration time.Duration) {
    394 		timer := time.NewTimer(duration)
    395 		defer timer.Stop()
    396 		for {
    397 			select {
    398 			case <-ctx.Done():
    399 				atomic.StoreInt32(&c.healthStatus, unknown)
    400 				return
    401 			case <-timer.C:
    402 				// Do health check the first time and ONLY if the connection is marked offline
    403 				if c.IsOffline() {
    404 					gctx, gcancel := context.WithTimeout(context.Background(), 3*time.Second)
    405 					_, err := c.getBucketLocation(gctx, probeBucketName)
    406 					gcancel()
    407 					if !IsNetworkOrHostDown(err, false) {
    408 						switch ToErrorResponse(err).Code {
    409 						case "NoSuchBucket", "AccessDenied", "":
    410 							atomic.CompareAndSwapInt32(&c.healthStatus, offline, online)
    411 						}
    412 					}
    413 				}
    414 
    415 				timer.Reset(duration)
    416 			}
    417 		}
    418 	}(hcDuration)
    419 	return cancelFn, nil
    420 }
    421 
    422 // requestMetadata - is container for all the values to make a request.
    423 type requestMetadata struct {
    424 	// If set newRequest presigns the URL.
    425 	presignURL bool
    426 
    427 	// User supplied.
    428 	bucketName         string
    429 	objectName         string
    430 	queryValues        url.Values
    431 	customHeader       http.Header
    432 	extraPresignHeader http.Header
    433 	expires            int64
    434 
    435 	// Generated by our internal code.
    436 	bucketLocation   string
    437 	contentBody      io.Reader
    438 	contentLength    int64
    439 	contentMD5Base64 string // carries base64 encoded md5sum
    440 	contentSHA256Hex string // carries hex encoded sha256sum
    441 	streamSha256     bool
    442 	addCrc           bool
    443 	trailer          http.Header // (http.Request).Trailer. Requires v4 signature.
    444 }
    445 
    446 // dumpHTTP - dump HTTP request and response.
    447 func (c *Client) dumpHTTP(req *http.Request, resp *http.Response) error {
    448 	// Starts http dump.
    449 	_, err := fmt.Fprintln(c.traceOutput, "---------START-HTTP---------")
    450 	if err != nil {
    451 		return err
    452 	}
    453 
    454 	// Filter out Signature field from Authorization header.
    455 	origAuth := req.Header.Get("Authorization")
    456 	if origAuth != "" {
    457 		req.Header.Set("Authorization", redactSignature(origAuth))
    458 	}
    459 
    460 	// Only display request header.
    461 	reqTrace, err := httputil.DumpRequestOut(req, false)
    462 	if err != nil {
    463 		return err
    464 	}
    465 
    466 	// Write request to trace output.
    467 	_, err = fmt.Fprint(c.traceOutput, string(reqTrace))
    468 	if err != nil {
    469 		return err
    470 	}
    471 
    472 	// Only display response header.
    473 	var respTrace []byte
    474 
    475 	// For errors we make sure to dump response body as well.
    476 	if resp.StatusCode != http.StatusOK &&
    477 		resp.StatusCode != http.StatusPartialContent &&
    478 		resp.StatusCode != http.StatusNoContent {
    479 		respTrace, err = httputil.DumpResponse(resp, true)
    480 		if err != nil {
    481 			return err
    482 		}
    483 	} else {
    484 		respTrace, err = httputil.DumpResponse(resp, false)
    485 		if err != nil {
    486 			return err
    487 		}
    488 	}
    489 
    490 	// Write response to trace output.
    491 	_, err = fmt.Fprint(c.traceOutput, strings.TrimSuffix(string(respTrace), "\r\n"))
    492 	if err != nil {
    493 		return err
    494 	}
    495 
    496 	// Ends the http dump.
    497 	_, err = fmt.Fprintln(c.traceOutput, "---------END-HTTP---------")
    498 	if err != nil {
    499 		return err
    500 	}
    501 
    502 	// Returns success.
    503 	return nil
    504 }
    505 
    506 // do - execute http request.
    507 func (c *Client) do(req *http.Request) (resp *http.Response, err error) {
    508 	defer func() {
    509 		if IsNetworkOrHostDown(err, false) {
    510 			c.markOffline()
    511 		}
    512 	}()
    513 
    514 	resp, err = c.httpClient.Do(req)
    515 	if err != nil {
    516 		// Handle this specifically for now until future Golang versions fix this issue properly.
    517 		if urlErr, ok := err.(*url.Error); ok {
    518 			if strings.Contains(urlErr.Err.Error(), "EOF") {
    519 				return nil, &url.Error{
    520 					Op:  urlErr.Op,
    521 					URL: urlErr.URL,
    522 					Err: errors.New("Connection closed by foreign host " + urlErr.URL + ". Retry again."),
    523 				}
    524 			}
    525 		}
    526 		return nil, err
    527 	}
    528 
    529 	// Response cannot be non-nil, report error if thats the case.
    530 	if resp == nil {
    531 		msg := "Response is empty. " + reportIssue
    532 		return nil, errInvalidArgument(msg)
    533 	}
    534 
    535 	// If trace is enabled, dump http request and response,
    536 	// except when the traceErrorsOnly enabled and the response's status code is ok
    537 	if c.isTraceEnabled && !(c.traceErrorsOnly && resp.StatusCode == http.StatusOK) {
    538 		err = c.dumpHTTP(req, resp)
    539 		if err != nil {
    540 			return nil, err
    541 		}
    542 	}
    543 
    544 	return resp, nil
    545 }
    546 
    547 // List of success status.
    548 var successStatus = []int{
    549 	http.StatusOK,
    550 	http.StatusNoContent,
    551 	http.StatusPartialContent,
    552 }
    553 
    554 // executeMethod - instantiates a given method, and retries the
    555 // request upon any error up to maxRetries attempts in a binomially
    556 // delayed manner using a standard back off algorithm.
    557 func (c *Client) executeMethod(ctx context.Context, method string, metadata requestMetadata) (res *http.Response, err error) {
    558 	if c.IsOffline() {
    559 		return nil, errors.New(c.endpointURL.String() + " is offline.")
    560 	}
    561 
    562 	var retryable bool       // Indicates if request can be retried.
    563 	var bodySeeker io.Seeker // Extracted seeker from io.Reader.
    564 	reqRetry := MaxRetry     // Indicates how many times we can retry the request
    565 
    566 	if metadata.contentBody != nil {
    567 		// Check if body is seekable then it is retryable.
    568 		bodySeeker, retryable = metadata.contentBody.(io.Seeker)
    569 		switch bodySeeker {
    570 		case os.Stdin, os.Stdout, os.Stderr:
    571 			retryable = false
    572 		}
    573 		// Retry only when reader is seekable
    574 		if !retryable {
    575 			reqRetry = 1
    576 		}
    577 
    578 		// Figure out if the body can be closed - if yes
    579 		// we will definitely close it upon the function
    580 		// return.
    581 		bodyCloser, ok := metadata.contentBody.(io.Closer)
    582 		if ok {
    583 			defer bodyCloser.Close()
    584 		}
    585 	}
    586 
    587 	// Create cancel context to control 'newRetryTimer' go routine.
    588 	retryCtx, cancel := context.WithCancel(ctx)
    589 
    590 	// Indicate to our routine to exit cleanly upon return.
    591 	defer cancel()
    592 
    593 	for range c.newRetryTimer(retryCtx, reqRetry, DefaultRetryUnit, DefaultRetryCap, MaxJitter) {
    594 		// Retry executes the following function body if request has an
    595 		// error until maxRetries have been exhausted, retry attempts are
    596 		// performed after waiting for a given period of time in a
    597 		// binomial fashion.
    598 		if retryable {
    599 			// Seek back to beginning for each attempt.
    600 			if _, err = bodySeeker.Seek(0, 0); err != nil {
    601 				// If seek failed, no need to retry.
    602 				return nil, err
    603 			}
    604 		}
    605 
    606 		if metadata.addCrc {
    607 			if metadata.trailer == nil {
    608 				metadata.trailer = make(http.Header, 1)
    609 			}
    610 			crc := crc32.New(crc32.MakeTable(crc32.Castagnoli))
    611 			metadata.contentBody = newHashReaderWrapper(metadata.contentBody, crc, func(hash []byte) {
    612 				// Update trailer when done.
    613 				metadata.trailer.Set("x-amz-checksum-crc32c", base64.StdEncoding.EncodeToString(hash))
    614 			})
    615 			metadata.trailer.Set("x-amz-checksum-crc32c", base64.StdEncoding.EncodeToString(crc.Sum(nil)))
    616 		}
    617 		// Instantiate a new request.
    618 		var req *http.Request
    619 		req, err = c.newRequest(ctx, method, metadata)
    620 		if err != nil {
    621 			errResponse := ToErrorResponse(err)
    622 			if isS3CodeRetryable(errResponse.Code) {
    623 				continue // Retry.
    624 			}
    625 
    626 			return nil, err
    627 		}
    628 
    629 		// Initiate the request.
    630 		res, err = c.do(req)
    631 		if err != nil {
    632 			if isRequestErrorRetryable(err) {
    633 				// Retry the request
    634 				continue
    635 			}
    636 			return nil, err
    637 		}
    638 
    639 		// For any known successful http status, return quickly.
    640 		for _, httpStatus := range successStatus {
    641 			if httpStatus == res.StatusCode {
    642 				return res, nil
    643 			}
    644 		}
    645 
    646 		// Read the body to be saved later.
    647 		errBodyBytes, err := io.ReadAll(res.Body)
    648 		// res.Body should be closed
    649 		closeResponse(res)
    650 		if err != nil {
    651 			return nil, err
    652 		}
    653 
    654 		// Save the body.
    655 		errBodySeeker := bytes.NewReader(errBodyBytes)
    656 		res.Body = io.NopCloser(errBodySeeker)
    657 
    658 		// For errors verify if its retryable otherwise fail quickly.
    659 		errResponse := ToErrorResponse(httpRespToErrorResponse(res, metadata.bucketName, metadata.objectName))
    660 
    661 		// Save the body back again.
    662 		errBodySeeker.Seek(0, 0) // Seek back to starting point.
    663 		res.Body = io.NopCloser(errBodySeeker)
    664 
    665 		// Bucket region if set in error response and the error
    666 		// code dictates invalid region, we can retry the request
    667 		// with the new region.
    668 		//
    669 		// Additionally, we should only retry if bucketLocation and custom
    670 		// region is empty.
    671 		if c.region == "" {
    672 			switch errResponse.Code {
    673 			case "AuthorizationHeaderMalformed":
    674 				fallthrough
    675 			case "InvalidRegion":
    676 				fallthrough
    677 			case "AccessDenied":
    678 				if errResponse.Region == "" {
    679 					// Region is empty we simply return the error.
    680 					return res, err
    681 				}
    682 				// Region is not empty figure out a way to
    683 				// handle this appropriately.
    684 				if metadata.bucketName != "" {
    685 					// Gather Cached location only if bucketName is present.
    686 					if location, cachedOk := c.bucketLocCache.Get(metadata.bucketName); cachedOk && location != errResponse.Region {
    687 						c.bucketLocCache.Set(metadata.bucketName, errResponse.Region)
    688 						continue // Retry.
    689 					}
    690 				} else {
    691 					// This is for ListBuckets() fallback.
    692 					if errResponse.Region != metadata.bucketLocation {
    693 						// Retry if the error response has a different region
    694 						// than the request we just made.
    695 						metadata.bucketLocation = errResponse.Region
    696 						continue // Retry
    697 					}
    698 				}
    699 			}
    700 		}
    701 
    702 		// Verify if error response code is retryable.
    703 		if isS3CodeRetryable(errResponse.Code) {
    704 			continue // Retry.
    705 		}
    706 
    707 		// Verify if http status code is retryable.
    708 		if isHTTPStatusRetryable(res.StatusCode) {
    709 			continue // Retry.
    710 		}
    711 
    712 		// For all other cases break out of the retry loop.
    713 		break
    714 	}
    715 
    716 	// Return an error when retry is canceled or deadlined
    717 	if e := retryCtx.Err(); e != nil {
    718 		return nil, e
    719 	}
    720 
    721 	return res, err
    722 }
    723 
    724 // newRequest - instantiate a new HTTP request for a given method.
    725 func (c *Client) newRequest(ctx context.Context, method string, metadata requestMetadata) (req *http.Request, err error) {
    726 	// If no method is supplied default to 'POST'.
    727 	if method == "" {
    728 		method = http.MethodPost
    729 	}
    730 
    731 	location := metadata.bucketLocation
    732 	if location == "" {
    733 		if metadata.bucketName != "" {
    734 			// Gather location only if bucketName is present.
    735 			location, err = c.getBucketLocation(ctx, metadata.bucketName)
    736 			if err != nil {
    737 				return nil, err
    738 			}
    739 		}
    740 		if location == "" {
    741 			location = getDefaultLocation(*c.endpointURL, c.region)
    742 		}
    743 	}
    744 
    745 	// Look if target url supports virtual host.
    746 	// We explicitly disallow MakeBucket calls to not use virtual DNS style,
    747 	// since the resolution may fail.
    748 	isMakeBucket := (metadata.objectName == "" && method == http.MethodPut && len(metadata.queryValues) == 0)
    749 	isVirtualHost := c.isVirtualHostStyleRequest(*c.endpointURL, metadata.bucketName) && !isMakeBucket
    750 
    751 	// Construct a new target URL.
    752 	targetURL, err := c.makeTargetURL(metadata.bucketName, metadata.objectName, location,
    753 		isVirtualHost, metadata.queryValues)
    754 	if err != nil {
    755 		return nil, err
    756 	}
    757 
    758 	// Initialize a new HTTP request for the method.
    759 	req, err = http.NewRequestWithContext(ctx, method, targetURL.String(), nil)
    760 	if err != nil {
    761 		return nil, err
    762 	}
    763 
    764 	// Get credentials from the configured credentials provider.
    765 	value, err := c.credsProvider.Get()
    766 	if err != nil {
    767 		return nil, err
    768 	}
    769 
    770 	var (
    771 		signerType      = value.SignerType
    772 		accessKeyID     = value.AccessKeyID
    773 		secretAccessKey = value.SecretAccessKey
    774 		sessionToken    = value.SessionToken
    775 	)
    776 
    777 	// Custom signer set then override the behavior.
    778 	if c.overrideSignerType != credentials.SignatureDefault {
    779 		signerType = c.overrideSignerType
    780 	}
    781 
    782 	// If signerType returned by credentials helper is anonymous,
    783 	// then do not sign regardless of signerType override.
    784 	if value.SignerType == credentials.SignatureAnonymous {
    785 		signerType = credentials.SignatureAnonymous
    786 	}
    787 
    788 	// Generate presign url if needed, return right here.
    789 	if metadata.expires != 0 && metadata.presignURL {
    790 		if signerType.IsAnonymous() {
    791 			return nil, errInvalidArgument("Presigned URLs cannot be generated with anonymous credentials.")
    792 		}
    793 		if metadata.extraPresignHeader != nil {
    794 			if signerType.IsV2() {
    795 				return nil, errInvalidArgument("Extra signed headers for Presign with Signature V2 is not supported.")
    796 			}
    797 			for k, v := range metadata.extraPresignHeader {
    798 				req.Header.Set(k, v[0])
    799 			}
    800 		}
    801 		if signerType.IsV2() {
    802 			// Presign URL with signature v2.
    803 			req = signer.PreSignV2(*req, accessKeyID, secretAccessKey, metadata.expires, isVirtualHost)
    804 		} else if signerType.IsV4() {
    805 			// Presign URL with signature v4.
    806 			req = signer.PreSignV4(*req, accessKeyID, secretAccessKey, sessionToken, location, metadata.expires)
    807 		}
    808 		return req, nil
    809 	}
    810 
    811 	// Set 'User-Agent' header for the request.
    812 	c.setUserAgent(req)
    813 
    814 	// Set all headers.
    815 	for k, v := range metadata.customHeader {
    816 		req.Header.Set(k, v[0])
    817 	}
    818 
    819 	// Go net/http notoriously closes the request body.
    820 	// - The request Body, if non-nil, will be closed by the underlying Transport, even on errors.
    821 	// This can cause underlying *os.File seekers to fail, avoid that
    822 	// by making sure to wrap the closer as a nop.
    823 	if metadata.contentLength == 0 {
    824 		req.Body = nil
    825 	} else {
    826 		req.Body = io.NopCloser(metadata.contentBody)
    827 	}
    828 
    829 	// Set incoming content-length.
    830 	req.ContentLength = metadata.contentLength
    831 	if req.ContentLength <= -1 {
    832 		// For unknown content length, we upload using transfer-encoding: chunked.
    833 		req.TransferEncoding = []string{"chunked"}
    834 	}
    835 
    836 	// set md5Sum for content protection.
    837 	if len(metadata.contentMD5Base64) > 0 {
    838 		req.Header.Set("Content-Md5", metadata.contentMD5Base64)
    839 	}
    840 
    841 	// For anonymous requests just return.
    842 	if signerType.IsAnonymous() {
    843 		return req, nil
    844 	}
    845 
    846 	switch {
    847 	case signerType.IsV2():
    848 		// Add signature version '2' authorization header.
    849 		req = signer.SignV2(*req, accessKeyID, secretAccessKey, isVirtualHost)
    850 	case metadata.streamSha256 && !c.secure:
    851 		if len(metadata.trailer) > 0 {
    852 			req.Trailer = metadata.trailer
    853 		}
    854 		// Streaming signature is used by default for a PUT object request.
    855 		// Additionally, we also look if the initialized client is secure,
    856 		// if yes then we don't need to perform streaming signature.
    857 		req = signer.StreamingSignV4(req, accessKeyID,
    858 			secretAccessKey, sessionToken, location, metadata.contentLength, time.Now().UTC(), c.sha256Hasher())
    859 	default:
    860 		// Set sha256 sum for signature calculation only with signature version '4'.
    861 		shaHeader := unsignedPayload
    862 		if metadata.contentSHA256Hex != "" {
    863 			shaHeader = metadata.contentSHA256Hex
    864 			if len(metadata.trailer) > 0 {
    865 				// Sanity check, we should not end up here if upstream is sane.
    866 				return nil, errors.New("internal error: contentSHA256Hex with trailer not supported")
    867 			}
    868 		} else if len(metadata.trailer) > 0 {
    869 			shaHeader = unsignedPayloadTrailer
    870 		}
    871 		req.Header.Set("X-Amz-Content-Sha256", shaHeader)
    872 
    873 		// Add signature version '4' authorization header.
    874 		req = signer.SignV4Trailer(*req, accessKeyID, secretAccessKey, sessionToken, location, metadata.trailer)
    875 	}
    876 
    877 	// Return request.
    878 	return req, nil
    879 }
    880 
    881 // set User agent.
    882 func (c *Client) setUserAgent(req *http.Request) {
    883 	req.Header.Set("User-Agent", libraryUserAgent)
    884 	if c.appInfo.appName != "" && c.appInfo.appVersion != "" {
    885 		req.Header.Set("User-Agent", libraryUserAgent+" "+c.appInfo.appName+"/"+c.appInfo.appVersion)
    886 	}
    887 }
    888 
    889 // makeTargetURL make a new target url.
    890 func (c *Client) makeTargetURL(bucketName, objectName, bucketLocation string, isVirtualHostStyle bool, queryValues url.Values) (*url.URL, error) {
    891 	host := c.endpointURL.Host
    892 	// For Amazon S3 endpoint, try to fetch location based endpoint.
    893 	if s3utils.IsAmazonEndpoint(*c.endpointURL) {
    894 		if c.s3AccelerateEndpoint != "" && bucketName != "" {
    895 			// http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
    896 			// Disable transfer acceleration for non-compliant bucket names.
    897 			if strings.Contains(bucketName, ".") {
    898 				return nil, errTransferAccelerationBucket(bucketName)
    899 			}
    900 			// If transfer acceleration is requested set new host.
    901 			// For more details about enabling transfer acceleration read here.
    902 			// http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
    903 			host = c.s3AccelerateEndpoint
    904 		} else {
    905 			// Do not change the host if the endpoint URL is a FIPS S3 endpoint or a S3 PrivateLink interface endpoint
    906 			if !s3utils.IsAmazonFIPSEndpoint(*c.endpointURL) && !s3utils.IsAmazonPrivateLinkEndpoint(*c.endpointURL) {
    907 				// Fetch new host based on the bucket location.
    908 				host = getS3Endpoint(bucketLocation)
    909 			}
    910 		}
    911 	}
    912 
    913 	// Save scheme.
    914 	scheme := c.endpointURL.Scheme
    915 
    916 	// Strip port 80 and 443 so we won't send these ports in Host header.
    917 	// The reason is that browsers and curl automatically remove :80 and :443
    918 	// with the generated presigned urls, then a signature mismatch error.
    919 	if h, p, err := net.SplitHostPort(host); err == nil {
    920 		if scheme == "http" && p == "80" || scheme == "https" && p == "443" {
    921 			host = h
    922 			if ip := net.ParseIP(h); ip != nil && ip.To4() == nil {
    923 				host = "[" + h + "]"
    924 			}
    925 		}
    926 	}
    927 
    928 	urlStr := scheme + "://" + host + "/"
    929 
    930 	// Make URL only if bucketName is available, otherwise use the
    931 	// endpoint URL.
    932 	if bucketName != "" {
    933 		// If endpoint supports virtual host style use that always.
    934 		// Currently only S3 and Google Cloud Storage would support
    935 		// virtual host style.
    936 		if isVirtualHostStyle {
    937 			urlStr = scheme + "://" + bucketName + "." + host + "/"
    938 			if objectName != "" {
    939 				urlStr += s3utils.EncodePath(objectName)
    940 			}
    941 		} else {
    942 			// If not fall back to using path style.
    943 			urlStr = urlStr + bucketName + "/"
    944 			if objectName != "" {
    945 				urlStr += s3utils.EncodePath(objectName)
    946 			}
    947 		}
    948 	}
    949 
    950 	// If there are any query values, add them to the end.
    951 	if len(queryValues) > 0 {
    952 		urlStr = urlStr + "?" + s3utils.QueryEncode(queryValues)
    953 	}
    954 
    955 	return url.Parse(urlStr)
    956 }
    957 
    958 // returns true if virtual hosted style requests are to be used.
    959 func (c *Client) isVirtualHostStyleRequest(url url.URL, bucketName string) bool {
    960 	if bucketName == "" {
    961 		return false
    962 	}
    963 
    964 	if c.lookup == BucketLookupDNS {
    965 		return true
    966 	}
    967 	if c.lookup == BucketLookupPath {
    968 		return false
    969 	}
    970 
    971 	// default to virtual only for Amazon/Google  storage. In all other cases use
    972 	// path style requests
    973 	return s3utils.IsVirtualHostSupported(url, bucketName)
    974 }