gitlab.com/ignitionrobotics/web/ign-go@v1.0.0-rc4/net/http.go (about)

     1  package net
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/url"
    10  	"strings"
    11  	"time"
    12  )
    13  
    14  // httpCaller is a Caller implementation using HTTP as transport layer.
    15  type httpCaller struct {
    16  	// client is the HTTP client used to create requests and receive responses from a certain web server.
    17  	client *http.Client
    18  
    19  	// baseURL is the base URL where all the requests should be routed to.
    20  	baseURL *url.URL
    21  
    22  	// endpoints contains a set of HTTP endpoints that this caller can communicate with.
    23  	endpoints map[string]EndpointHTTP
    24  }
    25  
    26  // Call establishes a connection with a certain endpoint sending the given slice of bytes as input,
    27  // it returns the response's body as a slice of bytes.
    28  func (h *httpCaller) Call(ctx context.Context, endpoint string, in []byte) ([]byte, error) {
    29  	e := h.resolveEndpoint(endpoint)
    30  
    31  	u, err := h.baseURL.Parse(e.Path)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	buff := bytes.NewBuffer(in)
    37  	req, err := http.NewRequestWithContext(ctx, e.Method, u.String(), buff)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	res, err := h.client.Do(req)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	var out []byte
    48  	out, err = ioutil.ReadAll(res.Body)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	if res.StatusCode < 200 || res.StatusCode > 299 {
    54  		return nil, errors.New(strings.TrimRight(string(out), "\r\n"))
    55  	}
    56  
    57  	return out, nil
    58  }
    59  
    60  // resolveEndpoint resolves if the given endpoint is a valid endpoint
    61  func (h *httpCaller) resolveEndpoint(endpoint string) EndpointHTTP {
    62  	e, ok := h.endpoints[endpoint]
    63  	if !ok {
    64  		return defaultEndpointHTTP
    65  	}
    66  	return e
    67  }
    68  
    69  // defaultEndpointHTTP is a default endpoint returned when no HTTP endpoint has been found.
    70  var defaultEndpointHTTP = EndpointHTTP{
    71  	Method: http.MethodGet,
    72  	Path:   "/",
    73  }
    74  
    75  // EndpointHTTP represents an HTTP endpoint.
    76  type EndpointHTTP struct {
    77  	// Method is the HTTP verb supported by this endpoint.
    78  	Method string
    79  	// Path is the relative path where this endpoint is located.
    80  	// Example: /example/test
    81  	Path string
    82  }
    83  
    84  // NewCallerHTTP initializes a new HTTP Caller.
    85  func NewCallerHTTP(baseURL *url.URL, endpoints map[string]EndpointHTTP, timeout time.Duration) Caller {
    86  	return &httpCaller{
    87  		baseURL:   baseURL,
    88  		endpoints: endpoints,
    89  		client:    &http.Client{Timeout: timeout},
    90  	}
    91  }