package http

import (
	
	
	
	
	
	
	

	iointernal 
)

// Request provides the HTTP specific request structure for HTTP specific
// middleware steps to use to serialize input, and send an operation's request.
type Request struct {
	*http.Request
	stream           io.Reader
	isStreamSeekable bool
	streamStartPos   int64
}

// NewStackRequest returns an initialized request ready to be populated with the
// HTTP request details. Returns empty interface so the function can be used as
// a parameter to the Smithy middleware Stack constructor.
func () interface{} {
	return &Request{
		Request: &http.Request{
			URL:           &url.URL{},
			Header:        http.Header{},
			ContentLength: -1, // default to unknown length
		},
	}
}

// IsHTTPS returns if the request is HTTPS. Returns false if no endpoint URL is set.
func ( *Request) () bool {
	if .URL == nil {
		return false
	}
	return strings.EqualFold(.URL.Scheme, "https")
}

// Clone returns a deep copy of the Request for the new context. A reference to
// the Stream is copied, but the underlying stream is not copied.
func ( *Request) () *Request {
	 := *
	.Request = .Request.Clone(context.TODO())
	return &
}

// StreamLength returns the number of bytes of the serialized stream attached
// to the request and ok set. If the length cannot be determined, an error will
// be returned.
func ( *Request) () ( int64,  bool,  error) {
	return streamLength(.stream, .isStreamSeekable, .streamStartPos)
}

func ( io.Reader,  bool,  int64) ( int64,  bool,  error) {
	if  == nil {
		return 0, true, nil
	}

	if ,  := .(interface{ () int });  {
		return int64(.()), true, nil
	}

	if ! {
		return 0, false, nil
	}

	 := .(io.Seeker)
	,  := .Seek(0, io.SeekEnd)
	if  != nil {
		return 0, false, 
	}

	// The reason to seek to streamStartPos instead of 0 is to ensure that the
	// SDK only sends the stream from the starting position the user's
	// application provided it to the SDK at. For example application opens a
	// file, and wants to skip the first N bytes uploading the rest. The
	// application would move the file's offset N bytes, then hand it off to
	// the SDK to send the remaining. The SDK should respect that initial offset.
	_,  = .Seek(, io.SeekStart)
	if  != nil {
		return 0, false, 
	}

	return  - , true, nil
}

// RewindStream will rewind the io.Reader to the relative start position if it
// is an io.Seeker.
func ( *Request) () error {
	// If there is no stream there is nothing to rewind.
	if .stream == nil {
		return nil
	}

	if !.isStreamSeekable {
		return fmt.Errorf("request stream is not seekable")
	}
	,  := .stream.(io.Seeker).Seek(.streamStartPos, io.SeekStart)
	return 
}

// GetStream returns the request stream io.Reader if a stream is set. If no
// stream is present nil will be returned.
func ( *Request) () io.Reader {
	return .stream
}

// IsStreamSeekable returns whether the stream is seekable.
func ( *Request) () bool {
	return .isStreamSeekable
}

// SetStream returns a clone of the request with the stream set to the provided
// reader. May return an error if the provided reader is seekable but returns
// an error.
func ( *Request) ( io.Reader) ( *Request,  error) {
	 = .Clone()

	if  == http.NoBody {
		 = nil
	}

	var  bool
	var  int64
	switch v := .(type) {
	case io.Seeker:
		,  := .Seek(0, io.SeekCurrent)
		if  != nil {
			return , 
		}
		 = true
		 = 
	default:
		// If the stream length can be determined, and is determined to be empty,
		// use a nil stream to prevent confusion between empty vs not-empty
		// streams.
		, ,  := streamLength(, false, 0)
		if  != nil {
			return nil, 
		} else if  &&  == 0 {
			 = nil
		}
	}

	.stream = 
	.isStreamSeekable = 
	.streamStartPos = 

	return , 
}

// Build returns a build standard HTTP request value from the Smithy request.
// The request's stream is wrapped in a safe container that allows it to be
// reused for subsequent attempts.
func ( *Request) ( context.Context) *http.Request {
	 := .Request.Clone()

	if .stream == nil && .ContentLength == -1 {
		.ContentLength = 0
	}

	switch stream := .stream.(type) {
	case *io.PipeReader:
		.Body = ioutil.NopCloser()
		.ContentLength = -1
	default:
		// HTTP Client Request must only have a non-nil body if the
		// ContentLength is explicitly unknown (-1) or non-zero. The HTTP
		// Client will interpret a non-nil body and ContentLength 0 as
		// "unknown". This is unwanted behavior.
		if .ContentLength != 0 && .stream != nil {
			.Body = iointernal.NewSafeReadCloser(ioutil.NopCloser())
		}
	}

	return 
}

// RequestCloner is a function that can take an input request type and clone the request
// for use in a subsequent retry attempt.
func ( interface{}) interface{} {
	return .(*Request).Clone()
}