package http

import (
	
	
	
	
	
	
	
)

// Defaults for the HTTPTransportBuilder.
var (
	// Default connection pool options
	DefaultHTTPTransportMaxIdleConns        = 100
	DefaultHTTPTransportMaxIdleConnsPerHost = 10

	// Default connection timeouts
	DefaultHTTPTransportIdleConnTimeout       = 90 * time.Second
	DefaultHTTPTransportTLSHandleshakeTimeout = 10 * time.Second
	DefaultHTTPTransportExpectContinueTimeout = 1 * time.Second

	// Default to TLS 1.2 for all HTTPS requests.
	DefaultHTTPTransportTLSMinVersion uint16 = tls.VersionTLS12
)

// Timeouts for net.Dialer's network connection.
var (
	DefaultDialConnectTimeout   = 30 * time.Second
	DefaultDialKeepAliveTimeout = 30 * time.Second
)

// BuildableClient provides a HTTPClient implementation with options to
// create copies of the HTTPClient when additional configuration is provided.
//
// The client's methods will not share the http.Transport value between copies
// of the BuildableClient. Only exported member values of the Transport and
// optional Dialer will be copied between copies of BuildableClient.
type BuildableClient struct {
	transport *http.Transport
	dialer    *net.Dialer

	initOnce sync.Once

	clientTimeout time.Duration
	client        *http.Client
}

// NewBuildableClient returns an initialized client for invoking HTTP
// requests.
func () *BuildableClient {
	return &BuildableClient{}
}

// Do implements the HTTPClient interface's Do method to invoke a HTTP request,
// and receive the response. Uses the BuildableClient's current
// configuration to invoke the http.Request.
//
// If connection pooling is enabled (aka HTTP KeepAlive) the client will only
// share pooled connections with its own instance. Copies of the
// BuildableClient will have their own connection pools.
//
// Redirect (3xx) responses will not be followed, the HTTP response received
// will returned instead.
func ( *BuildableClient) ( *http.Request) (*http.Response, error) {
	.initOnce.Do(.build)

	return .client.Do()
}

// Freeze returns a frozen aws.HTTPClient implementation that is no longer a BuildableClient.
// Use this to prevent the SDK from applying DefaultMode configuration values to a buildable client.
func ( *BuildableClient) () aws.HTTPClient {
	 := .clone()
	.build()
	return .client
}

func ( *BuildableClient) () {
	.client = wrapWithLimitedRedirect(&http.Client{
		Timeout:   .clientTimeout,
		Transport: .GetTransport(),
	})
}

func ( *BuildableClient) () *BuildableClient {
	 := NewBuildableClient()
	.transport = .GetTransport()
	.dialer = .GetDialer()
	.clientTimeout = .clientTimeout

	return 
}

// WithTransportOptions copies the BuildableClient and returns it with the
// http.Transport options applied.
//
// If a non (*http.Transport) was set as the round tripper, the round tripper
// will be replaced with a default Transport value before invoking the option
// functions.
func ( *BuildableClient) ( ...func(*http.Transport)) *BuildableClient {
	 := .clone()

	 := .GetTransport()
	for ,  := range  {
		()
	}
	.transport = 

	return 
}

// WithDialerOptions copies the BuildableClient and returns it with the
// net.Dialer options applied. Will set the client's http.Transport DialContext
// member.
func ( *BuildableClient) ( ...func(*net.Dialer)) *BuildableClient {
	 := .clone()

	 := .GetDialer()
	for ,  := range  {
		()
	}
	.dialer = 

	 := .GetTransport()
	.DialContext = .dialer.DialContext
	.transport = 

	return 
}

// WithTimeout Sets the timeout used by the client for all requests.
func ( *BuildableClient) ( time.Duration) *BuildableClient {
	 := .clone()
	.clientTimeout = 
	return 
}

// GetTransport returns a copy of the client's HTTP Transport.
func ( *BuildableClient) () *http.Transport {
	var  *http.Transport
	if .transport != nil {
		 = .transport.Clone()
	} else {
		 = defaultHTTPTransport()
	}

	return 
}

// GetDialer returns a copy of the client's network dialer.
func ( *BuildableClient) () *net.Dialer {
	var  *net.Dialer
	if .dialer != nil {
		 = shallowCopyStruct(.dialer).(*net.Dialer)
	} else {
		 = defaultDialer()
	}

	return 
}

// GetTimeout returns a copy of the client's timeout to cancel requests with.
func ( *BuildableClient) () time.Duration {
	return .clientTimeout
}

func () *net.Dialer {
	return &net.Dialer{
		Timeout:   DefaultDialConnectTimeout,
		KeepAlive: DefaultDialKeepAliveTimeout,
		DualStack: true,
	}
}

func () *http.Transport {
	 := defaultDialer()

	 := &http.Transport{
		Proxy:                 http.ProxyFromEnvironment,
		DialContext:           .DialContext,
		TLSHandshakeTimeout:   DefaultHTTPTransportTLSHandleshakeTimeout,
		MaxIdleConns:          DefaultHTTPTransportMaxIdleConns,
		MaxIdleConnsPerHost:   DefaultHTTPTransportMaxIdleConnsPerHost,
		IdleConnTimeout:       DefaultHTTPTransportIdleConnTimeout,
		ExpectContinueTimeout: DefaultHTTPTransportExpectContinueTimeout,
		ForceAttemptHTTP2:     true,
		TLSClientConfig: &tls.Config{
			MinVersion: DefaultHTTPTransportTLSMinVersion,
		},
	}

	return 
}

// shallowCopyStruct creates a shallow copy of the passed in source struct, and
// returns that copy of the same struct type.
func ( interface{}) interface{} {
	 := reflect.ValueOf()
	 := .Type()

	var  bool
	if .Kind() == reflect.Ptr {
		 = .Elem()
		 = .Elem()
		 = true
	}
	 := reflect.New().Elem()

	for  := 0;  < .NumField(); ++ {
		 := .Field()
		if len(.PkgPath) != 0 {
			// unexported fields have a PkgPath
			continue
		}

		.Field().Set(.Field())
	}

	if  {
		 = .Addr()
	}

	return .Interface()
}

// wrapWithLimitedRedirect updates the Client's Transport and CheckRedirect to
// not follow any redirect other than 307 and 308. No other redirect will be
// followed.
//
// If the client does not have a Transport defined will use a new SDK default
// http.Transport configuration.
func ( *http.Client) *http.Client {
	 := .Transport
	if  == nil {
		 = defaultHTTPTransport()
	}

	 := *
	.CheckRedirect = limitedRedirect
	.Transport = suppressBadHTTPRedirectTransport{
		tr: ,
	}

	return &
}

// limitedRedirect is a CheckRedirect that prevents the client from following
// any non 307/308 HTTP status code redirects.
//
// The 307 and 308 redirects are allowed because the client must use the
// original HTTP method for the redirected to location. Whereas 301 and 302
// allow the client to switch to GET for the redirect.
//
// Suppresses all redirect requests with a URL of badHTTPRedirectLocation.
func ( *http.Request,  []*http.Request) error {
	// Request.Response, in CheckRedirect is the response that is triggering
	// the redirect.
	 := .Response
	if .URL.String() == badHTTPRedirectLocation {
		.Header.Del(badHTTPRedirectLocation)
		return http.ErrUseLastResponse
	}

	switch .StatusCode {
	case 307, 308:
		// Only allow 307 and 308 redirects as they preserve the method.
		return nil
	}

	return http.ErrUseLastResponse
}

// suppressBadHTTPRedirectTransport provides an http.RoundTripper
// implementation that wraps another http.RoundTripper to prevent HTTP client
// receiving 301 and 302 HTTP responses redirects without the required location
// header.
//
// Clients using this utility must have a CheckRedirect, e.g. limitedRedirect,
// that check for responses with having a URL of baseHTTPRedirectLocation, and
// suppress the redirect.
type suppressBadHTTPRedirectTransport struct {
	tr http.RoundTripper
}

const badHTTPRedirectLocation = `https://amazonaws.com/badhttpredirectlocation`

// RoundTrip backfills a stub location when a 301/302 response is received
// without a location. This stub location is used by limitedRedirect to prevent
// the HTTP client from failing attempting to use follow a redirect without a
// location value.
func ( suppressBadHTTPRedirectTransport) ( *http.Request) (*http.Response, error) {
	,  := .tr.RoundTrip()
	if  != nil {
		return , 
	}

	// S3 is the only known service to return 301 without location header.
	// The Go standard library HTTP client will return an opaque error if it
	// tries to follow a 301/302 response missing the location header.
	switch .StatusCode {
	case 301, 302:
		if  := .Header.Get("Location"); len() == 0 {
			.Header.Set("Location", badHTTPRedirectLocation)
		}
	}

	return , 
}