ctxclient

package module
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 16, 2023 License: BSD-3-Clause Imports: 7 Imported by: 24

README

ctxclient package

GoDoc BSD 3-Clause License

Package ctxclient offers utilities for easing the making of http calls by handing boilerplate functions and providing for the selection of an http.Client/Transport based upon the current context. A ctxclient.Func provides the basis of the functionality via the Client and Do methods. A nil ctxclient simply returns the default client while a Func that return the ErrUseDefault will also return the default on a Client call.

The Do func simplifies making an http request by handling context timeouts and checking for non 2xx response. In the case of a non 2xx response a NotSuccess error is returned which contains the body, header and status code of the response.

var f ctxclient.Func

res, err = f.Do(ctx, req)
if err != nil {
    ...
}

Funcs may also be used to global defaults by registering a Func via the ctxclient.Register function.

Please review examples_test.go.

This package borrows from ideas found in golang.org/x/oauth2.

Documentation

Overview

Package ctxclient offers utilities for handling the selection and creation of http.Clients based on the context. This borrows from ideas found in golang.org/x/oauth2.

Usage example:

import (
    "github.com/jfcote87/ctxclient"
)
...
var clf *ctxclient.Func
req, _ := http.NewRequest("GET","http://example.com",nil)
res, err := clf.Do(req)
...
Example (Func)
package main

import (
	"context"
	"errors"
	"log"
	"net/http"

	"github.com/jfcote87/ctxclient"
)

func main() {
	var clfunc ctxclient.Func = func(ctx context.Context) (*http.Client, error) {
		k, _ := ctx.Value(UserKey).(string)
		if k == "" {

			return nil, errors.New("no user key provided in context")
			// or to use the default client instead:
			// return nil, ctxclient.ErrUseDefault
		}
		return &http.Client{Transport: &UserKeyTransport{UserKey: k}}, nil
	}
	ctx := context.WithValue(context.Background(), UserKey, "USER_GUID")
	req, _ := http.NewRequest("GET", "http://example.com", nil)

	res, err := clfunc.Do(ctx, req)
	switch ex := err.(type) {
	case *ctxclient.NotSuccess:
		log.Printf("server returned %s status: %s", ex.StatusMessage, ex.Body)
	case nil:
		log.Printf("successful server response %s", res.Status)
	default:
		log.Printf("Transport error: %v", err)
	}
}

var UserKey userKey

type userKey struct{}

type UserKeyTransport struct {
	UserKey string
}

func (t *UserKeyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	h := make(http.Header)
	for k, v := range r.Header {
		h[k] = v
	}
	newReq := *r
	h.Set("X-USERKEY", t.UserKey)
	newReq.Header = h
	return http.DefaultTransport.RoundTrip(&newReq)
}
Output:

Example (Func_nil)
package main

import (
	"context"
	"log"
	"net/http"

	"github.com/jfcote87/ctxclient"
)

func main() {
	ctx := context.Background()
	var clfunc ctxclient.Func

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	res, err := clfunc.Do(ctx, req)
	switch ex := err.(type) {
	case *ctxclient.NotSuccess:
		log.Printf("server returned %s status: %s", ex.StatusMessage, ex.Body)
	case nil:
		log.Printf("successful server response %s", res.Status)
	default:
		log.Printf("transport error: %v", err)
	}
}
Output:

Example (Register)
package main

import (
	"context"
	"net/http"

	"github.com/jfcote87/ctxclient"
)

func main() {
	// should be done during a package init()
	var clfunc ctxclient.Func = func(ctx context.Context) (*http.Client, error) {
		k, _ := ctx.Value(UserKey).(string)
		if k == "" {
			return nil, ctxclient.ErrUseDefault // use default instead
		}
		return &http.Client{Transport: &UserKeyTransport{UserKey: k}}, nil
	}
	ctxclient.RegisterFunc(clfunc)
}

var UserKey userKey

type userKey struct{}

type UserKeyTransport struct {
	UserKey string
}

func (t *UserKeyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	h := make(http.Header)
	for k, v := range r.Header {
		h[k] = v
	}
	newReq := *r
	h.Set("X-USERKEY", t.UserKey)
	newReq.Header = h
	return http.DefaultTransport.RoundTrip(&newReq)
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrUseDefault *useDefault

ErrUseDefault should be returned as the error from a ctxclient.Func when wishing to use the default client determined by the ctxclient package.

Functions

func Client

func Client(ctx context.Context) *http.Client

Client retrieves the default client. If an error occurs, the error will be stored as an ErrorTransport in the client. The error will be returned on all calls the client makes.

func Do

func Do(ctx context.Context, req *http.Request) (*http.Response, error)

Do sends the request using the default client and checks for timeout/cancellation. Returns *NotSuccess error if response status is not 2xx. ctx must be non-nil

func Error

func Error(cl *http.Client) error

Error checks the passed client for an ErrorTransport and returns the embedded error.

func PostForm

func PostForm(ctx context.Context, url string, payload url.Values) (*http.Response, error)

PostForm issues a POST request through the default http.Client

func RegisterFunc

func RegisterFunc(f Func)

RegisterFunc adds f to the list of Funcs checked by the Default Func. This should only be called during init as it is not thread safe.

func RequestError

func RequestError(req *http.Request, err error) (*http.Response, error)

RequestError is a helper func to use in RoundTripper interfaces. Closes request body, checking for nils to you don't have to.

func Transport

func Transport(ctx context.Context) http.RoundTripper

Transport returns the transport from the context's default client

Types

type ErrorTransport

type ErrorTransport struct{ Err error }

ErrorTransport returns the pass error on RoundTrip call. This RoundTripper should be used in cases where error handling can be postponed due to short response handling time.

func (*ErrorTransport) RoundTrip

func (t *ErrorTransport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip always return the embedded err. The error will be wrapped in an url.Error by http.Client

type Func

type Func func(ctx context.Context) (*http.Client, error)

Func returns an http.Client pointer.

func (Func) Client

func (f Func) Client(ctx context.Context) *http.Client

Client retrieves the Func's client. If an error occurs, the error will be stored as an ErrorTransport in the client. The error will be returned on all calls the client makes.

func (Func) Do

func (f Func) Do(ctx context.Context, req *http.Request) (*http.Response, error)

Do sends the request using the calculated client and checks for timeout/cancellation. Returns *NotSuccess if response status is not 2xx. ctx must be non-nil

func (Func) Get added in v0.6.0

func (f Func) Get(ctx context.Context, url string) (*http.Response, error)

Get creates a GET request from the passed url and return Response if 200-2XX response

func (Func) PostForm

func (f Func) PostForm(ctx context.Context, url string, payload url.Values) (*http.Response, error)

PostForm issues a POST request through the http.Client determined by f

type NotSuccess

type NotSuccess struct {
	Response      *http.Response
	StatusCode    int
	StatusMessage string
	Body          []byte
	Header        http.Header
}

NotSuccess contains body of a non 2xx http response

func (NotSuccess) Error

func (re NotSuccess) Error() string

Error fulfills error interface

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL