Codebase list golang-github-go-kit-kit / bcbfb94 transport / http / client.go
bcbfb94

Tree @bcbfb94 (Download .tar.gz)

client.go @bcbfb94raw · history · blame

package http

import (
	"fmt"
	"net/http"
	"net/url"

	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
)

// Client wraps a URL and provides a method that implements endpoint.Endpoint.
type Client struct {
	// If client is nil, http.DefaultClient will be used.
	*http.Client

	// Method must be provided.
	Method string

	// URL must be provided.
	URL *url.URL

	// A background context must be provided.
	context.Context

	// EncodeRequestFunc must be provided. The HTTP request passed to the
	// EncodeRequestFunc will have a nil body.
	EncodeRequestFunc

	// DecodeResponseFunc must be provided.
	DecodeResponseFunc

	// Before functions are executed on the outgoing request after it is
	// created, but before it's sent to the HTTP client. Clients have no After
	// ResponseFuncs, as they don't work with ResponseWriters.
	Before []RequestFunc
}

// Endpoint returns a usable endpoint that will invoke the RPC specified by
// the client.
func (c Client) Endpoint() endpoint.Endpoint {
	return func(ctx context.Context, request interface{}) (interface{}, error) {
		ctx, cancel := context.WithCancel(ctx)
		defer cancel()

		req, err := http.NewRequest(c.Method, c.URL.String(), nil)
		if err != nil {
			return nil, fmt.Errorf("NewRequest: %v", err)
		}

		if err = c.EncodeRequestFunc(req, request); err != nil {
			return nil, fmt.Errorf("Encode: %v", err)
		}

		for _, f := range c.Before {
			ctx = f(ctx, req)
		}

		var resp *http.Response
		if c.Client != nil {
			resp, err = c.Client.Do(req)
		} else {
			resp, err = http.DefaultClient.Do(req)
		}
		if err != nil {
			return nil, fmt.Errorf("Do: %v", err)
		}
		defer func() { _ = resp.Body.Close() }()

		response, err := c.DecodeResponseFunc(resp)
		if err != nil {
			return nil, fmt.Errorf("Decode: %v", err)
		}

		return response, nil
	}
}