...

Source file src/github.com/aws/smithy-go/transport/http/client.go

Documentation: github.com/aws/smithy-go/transport/http

     1  package http
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	smithy "github.com/aws/smithy-go"
     9  	"github.com/aws/smithy-go/middleware"
    10  )
    11  
    12  // ClientDo provides the interface for custom HTTP client implementations.
    13  type ClientDo interface {
    14  	Do(*http.Request) (*http.Response, error)
    15  }
    16  
    17  // ClientDoFunc provides a helper to wrap a function as an HTTP client for
    18  // round tripping requests.
    19  type ClientDoFunc func(*http.Request) (*http.Response, error)
    20  
    21  // Do will invoke the underlying func, returning the result.
    22  func (fn ClientDoFunc) Do(r *http.Request) (*http.Response, error) {
    23  	return fn(r)
    24  }
    25  
    26  // ClientHandler wraps a client that implements the HTTP Do method. Standard
    27  // implementation is http.Client.
    28  type ClientHandler struct {
    29  	client ClientDo
    30  }
    31  
    32  // NewClientHandler returns an initialized middleware handler for the client.
    33  func NewClientHandler(client ClientDo) ClientHandler {
    34  	return ClientHandler{
    35  		client: client,
    36  	}
    37  }
    38  
    39  // Handle implements the middleware Handler interface, that will invoke the
    40  // underlying HTTP client. Requires the input to be a Smithy *Request. Returns
    41  // a smithy *Response, or error if the request failed.
    42  func (c ClientHandler) Handle(ctx context.Context, input interface{}) (
    43  	out interface{}, metadata middleware.Metadata, err error,
    44  ) {
    45  	req, ok := input.(*Request)
    46  	if !ok {
    47  		return nil, metadata, fmt.Errorf("expect Smithy http.Request value as input, got unsupported type %T", input)
    48  	}
    49  
    50  	builtRequest := req.Build(ctx)
    51  	if err := ValidateEndpointHost(builtRequest.Host); err != nil {
    52  		return nil, metadata, err
    53  	}
    54  
    55  	resp, err := c.client.Do(builtRequest)
    56  	if resp == nil {
    57  		// Ensure a http response value is always present to prevent unexpected
    58  		// panics.
    59  		resp = &http.Response{
    60  			Header: http.Header{},
    61  			Body:   http.NoBody,
    62  		}
    63  	}
    64  	if err != nil {
    65  		err = &RequestSendError{Err: err}
    66  
    67  		// Override the error with a context canceled error, if that was canceled.
    68  		select {
    69  		case <-ctx.Done():
    70  			err = &smithy.CanceledError{Err: ctx.Err()}
    71  		default:
    72  		}
    73  	}
    74  
    75  	// HTTP RoundTripper *should* close the request body. But this may not happen in a timely manner.
    76  	// So instead Smithy *Request Build wraps the body to be sent in a safe closer that will clear the
    77  	// stream reference so that it can be safely reused.
    78  	if builtRequest.Body != nil {
    79  		_ = builtRequest.Body.Close()
    80  	}
    81  
    82  	return &Response{Response: resp}, metadata, err
    83  }
    84  
    85  // RequestSendError provides a generic request transport error. This error
    86  // should wrap errors making HTTP client requests.
    87  //
    88  // The ClientHandler will wrap the HTTP client's error if the client request
    89  // fails, and did not fail because of context canceled.
    90  type RequestSendError struct {
    91  	Err error
    92  }
    93  
    94  // ConnectionError returns that the error is related to not being able to send
    95  // the request, or receive a response from the service.
    96  func (e *RequestSendError) ConnectionError() bool {
    97  	return true
    98  }
    99  
   100  // Unwrap returns the underlying error, if there was one.
   101  func (e *RequestSendError) Unwrap() error {
   102  	return e.Err
   103  }
   104  
   105  func (e *RequestSendError) Error() string {
   106  	return fmt.Sprintf("request send failed, %v", e.Err)
   107  }
   108  
   109  // NopClient provides a client that ignores the request, and returns an empty
   110  // successful HTTP response value.
   111  type NopClient struct{}
   112  
   113  // Do ignores the request and returns a 200 status empty response.
   114  func (NopClient) Do(r *http.Request) (*http.Response, error) {
   115  	return &http.Response{
   116  		StatusCode: 200,
   117  		Header:     http.Header{},
   118  		Body:       http.NoBody,
   119  	}, nil
   120  }
   121  

View as plain text