Codebase list golang-github-go-kit-kit / d61f875 transport / http / binding.go
d61f875

Tree @d61f875 (Download .tar.gz)

binding.go @d61f875raw · history · blame

package http

import (
	"net/http"

	"golang.org/x/net/context"

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

type binding struct {
	context.Context
	makeRequest func() interface{}
	codec.Codec
	endpoint.Endpoint
	before []BeforeFunc
	after  []AfterFunc
}

// NewBinding returns an HTTP handler that wraps the given endpoint.
func NewBinding(ctx context.Context, makeRequest func() interface{}, cdc codec.Codec, e endpoint.Endpoint, options ...BindingOption) http.Handler {
	b := &binding{
		Context:     ctx,
		makeRequest: makeRequest,
		Codec:       cdc,
		Endpoint:    e,
	}
	for _, option := range options {
		option(b)
	}
	return b
}

func (b *binding) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Per-request context.
	ctx, cancel := context.WithCancel(b.Context)
	defer cancel()

	// Prepare the RPC's context with details from the request.
	for _, f := range b.before {
		ctx = f(ctx, r)
	}

	// Decode request.
	req := b.makeRequest()
	ctx, err := b.Codec.Decode(ctx, r.Body, req)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Execute RPC.
	resp, err := b.Endpoint(ctx, req)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Prepare the ResponseWriter.
	for _, f := range b.after {
		f(ctx, w)
	}

	// Encode response.
	if err := b.Codec.Encode(w, resp); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// BindingOption sets a parameter for the HTTP binding.
type BindingOption func(*binding)

// BindingBefore adds pre-RPC BeforeFuncs to the HTTP binding.
func BindingBefore(funcs ...BeforeFunc) BindingOption {
	return func(b *binding) { b.before = append(b.before, funcs...) }
}

// BindingAfter adds post-RPC AfterFuncs to the HTTP binding.
func BindingAfter(funcs ...AfterFunc) BindingOption {
	return func(b *binding) { b.after = append(b.after, funcs...) }
}