http

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2021 License: MIT Imports: 10 Imported by: 0

README

go-http

Simple/Extensible http request library for go

GoDoc latest version code coverage

Installation

go get github.com/conradludgate/go-http

Usage

This library consists of a few basic parts.

Client

Clients are what make the HTTP requests. They are very simple to make

client := http.NewClient()

The NewClient function accepts some options to extend it.

For example:

client := http.NewClient(
    // Set the base url of the client
    http.URLString("https://example.com/api/"),
    // Set a header to be sent with every request
    http.AddHeader("Authorization", "Bearer ABC"),
)

You can create a temporary client scope using the With function

client := http.NewClient(...)

// Creates a copy of client with a new header
// The original client is unaffected
client2 := client.With(
    http.AddHeader("User-Agent", "googlebot"),
)
Requests

Once you have a client, you can create requests

// Create a GET request object to <base_url>/v1/healthz
req := client.NewRequest(http.Get, http.Path("v1", "healthz"))

You can add a lot of options here too:

// Create a POST request object
req := client.Post(
    // to https://example.com/api/v1/healthz
    http.URLString("https://example.com/api/"),
    http.Path("v1", "healthz"),
    // With a JSON body
    http.JSON(someData),
    // And a User-Agent header
    http.AddHeader("User-Agent", "googlebot"),
)
Responses

Once you have your request object, you can Send it

resp, err := req.Send(context.Background())

A repeating story... Send also accepts some options

// any type that deserializes JSON
respBody := make(map[string]interface{})

resp, err := req.Send(context.Background(),
    http.JSON(&respBody)
)

Examples

Simple usage
// Set the base url for the client
client := http.NewClient(http.URLString("https://hacker-news.firebaseio.com/"))

respBody := make(map[string]interface{})

// make GET request
resp, err := client.Get(
    // to https://hacker-news.firebaseio.com/v0/item/8863.json
    http.Path("v0", "item", "8863.json"),
).Send(context.Background(),
    // deserialising the json response into respBody
    http.JSON(&respBody),
)
if err != nil {
    panic(err)
}

// Output: 200: My YC app: Dropbox - Throw away your USB drive
fmt.Printf("%d: %s", resp.Status, respBody["title"])
Complex usage
type RequestBody struct {
    Foo string
    Bar int64
}
type ResponseBody struct {
    URL  string      `json:"url"`
    JSON RequestBody `json:"json"`
}

reqBody := RequestBody{
    Foo: "Hello World",
    Bar: 1234,
}
respBody := new(ResponseBody)

client := http.NewClient()
resp, err := client.NewRequest(http.Post,
    // Send post request to https://httpbin.org/anything/test1
    http.URLString("https://httpbin.org/anything"),
    http.Path("test1"),
    // Including a header
    http.AddHeader("X-Example", "wow"),
    // Sending json from reqBody
    http.JSON(reqBody),
).Send(context.Background(),
    // Receiving json response into respBody
    http.JSON(respBody),
)
if err != nil {
    panic(err)
}

// Output: 200: https://httpbin.org/anything/test1 {Hello World 1234}
fmt.Printf("%d: %s %v", resp.Status, respBody.URL, respBody.JSON)
Errors

Most Go HTTP clients return errors when making request objects as well as sending them.

I usually find it annoying to make a request object, check the error, send the request, check the error and in both cases just bubbling it up wrapped with some context.

req, err := http.NewRequest(http.MethodGet, ":invalid_url")
if err != nil {
    return nil, fmt.Errorf("request error: ", err)
}
resp, err := client.Do(req)
if err != nil {
    return nil, fmt.Errorf("response error: ", err)
}

This library does that for you in one go.

_, err := client.Get(http.URLString(":invalid_url")).Send(context.Background())

// Output: request error: parse \":invalid-url\": missing protocol scheme
fmt.Println(err)

If you want to, you can extract the error after creating the request

req := client.Get(http.URLString(":invalid_url"))

// Output: request error: parse \":invalid-url\": missing protocol scheme
fmt.Println(req.Error())

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func StatusText added in v0.3.0

func StatusText(code Status) string

StatusText returns a text for the HTTP status code. It returns the empty string if the code is unknown.

Types

type BaseClientOption added in v0.3.0

type BaseClientOption struct {
	// contains filtered or unexported fields
}

func BaseClient added in v0.3.0

func BaseClient(client *stdhttp.Client) BaseClientOption

func (BaseClientOption) ModifyClient added in v0.3.0

func (co BaseClientOption) ModifyClient(c *Client)

type BodyOption added in v0.1.4

type BodyOption struct {
	// contains filtered or unexported fields
}

func Body

func Body(r io.Reader) BodyOption

Body is an option to add a body to a request

func (BodyOption) ModifyRequest added in v0.1.4

func (b BodyOption) ModifyRequest(r *Request) error

type BodyWriteOption added in v0.3.0

type BodyWriteOption struct {
	// contains filtered or unexported fields
}

func WriteBodyTo added in v0.3.0

func WriteBodyTo(w io.Writer) BodyWriteOption

Body is an option to add a body to a request

func (BodyWriteOption) ProcessResponse added in v0.3.0

func (b BodyWriteOption) ProcessResponse(resp *Response) error

type Client

type Client struct {
	PreRequestMiddlewares  []RequestOption
	PostRequestMiddlewares []RequestOption

	PreResponseMiddlewares  []ResponseOption
	PostResponseMiddlewares []ResponseOption
	// contains filtered or unexported fields
}

func NewClient

func NewClient(options ...ClientOption) *Client

NewClient creates a new HTTP Client with the given options

func (*Client) Apply added in v0.3.0

func (c *Client) Apply(options ...ClientOption)

With clones the client, applying the new options ontop

func (*Client) BaseClient added in v0.3.0

func (c *Client) BaseClient() *stdhttp.Client

func (*Client) Delete added in v0.3.0

func (c *Client) Delete(options ...RequestOption) *Request

Delete creates a new HTTP Delete Request with the options provided

func (*Client) Get added in v0.2.2

func (c *Client) Get(options ...RequestOption) *Request

Get creates a new HTTP Get Request with the options provided

func (*Client) NewRequest

func (c *Client) NewRequest(method Method, options ...RequestOption) *Request

NewRequest creates a new HTTP Request with the method and options provided

func (*Client) Post added in v0.2.2

func (c *Client) Post(options ...RequestOption) *Request

Post creates a new HTTP Post Request with the options provided

func (*Client) Put added in v0.3.0

func (c *Client) Put(options ...RequestOption) *Request

Put creates a new HTTP Put Request with the options provided

func (*Client) With

func (c *Client) With(options ...ClientOption) *Client

With clones the client, applying the new options ontop

type ClientOption

type ClientOption interface {
	ModifyClient(*Client)
}

ClientOption is the option type for clients

type HeaderOption added in v0.1.4

type HeaderOption struct {
	// contains filtered or unexported fields
}

func AddHeader added in v0.2.0

func AddHeader(key string, values ...string) HeaderOption

AddHeader is an option to add a HTTP header to a request

func (HeaderOption) ModifyClient added in v0.2.1

func (h HeaderOption) ModifyClient(c *Client)

func (HeaderOption) ModifyRequest added in v0.1.4

func (h HeaderOption) ModifyRequest(r *Request) error

type JSONOption added in v0.1.4

type JSONOption struct {
	// contains filtered or unexported fields
}

func JSON

func JSON(v interface{}) JSONOption

JSON is an option to add a JSON Body to a request or to expect a JSON Body in a response

Example
type RequestBody struct {
	Foo string
	Bar int64
}
type ResponseBody struct {
	URL  string      `json:"url"`
	JSON RequestBody `json:"json"`
}

ctx := context.Background()

reqBody := RequestBody{
	Foo: "Hello World",
	Bar: 1234,
}
respBody := new(ResponseBody)

client := http.NewClient()
resp, err := client.Post(
	http.URLString("https://httpbin.org/anything"),
	http.Path("test1"),
	http.JSON(reqBody),
).Send(ctx,
	http.JSON(respBody),
)
if err != nil {
	panic(err)
}

fmt.Printf("%d: %s %v", resp.StatusCode, respBody.URL, respBody.JSON)
Output:

200: https://httpbin.org/anything/test1 {Hello World 1234}

func (JSONOption) ModifyRequest added in v0.1.4

func (j JSONOption) ModifyRequest(r *Request) error

func (JSONOption) ProcessResponse added in v0.1.4

func (j JSONOption) ProcessResponse(resp *Response) error

type Method

type Method string
const (
	Get    Method = "GET"
	Post   Method = "POST"
	Delete Method = "DELETE"
	Put    Method = "PUT"
)

type ParamsOption added in v0.3.0

type ParamsOption struct {
	// contains filtered or unexported fields
}

func Param added in v0.3.0

func Param(key string, values ...string) ParamsOption

Param is an option to add a single query parameter onto a request

Example
type ResponseBody struct {
	Args map[string]string `json:"args"`
}

ctx := context.Background()

respBody := new(ResponseBody)

client := http.NewClient()
resp, err := client.Get(
	http.URLString("https://httpbin.org/get"),
	http.Param("foo", "abc"),
	http.Params(url.Values{
		"bar": []string{"def"},
	}),
).Send(ctx,
	http.JSON(respBody),
)
if err != nil {
	panic(err)
}

fmt.Printf("%d: %s %s", resp.StatusCode, respBody.Args["foo"], respBody.Args["bar"])
Output:

200: abc def

func Params added in v0.3.0

func Params(values url.Values) ParamsOption

Params is an option to add query parameters onto a request

func (ParamsOption) ModifyRequest added in v0.3.0

func (q ParamsOption) ModifyRequest(r *Request) error

type PathOption added in v0.1.4

type PathOption struct {
	// contains filtered or unexported fields
}

func Path

func Path(pathSegments ...string) PathOption

Path is an option to add a path onto the base url of the request

func (PathOption) ModifyRequest added in v0.1.4

func (p PathOption) ModifyRequest(r *Request) error

type PostRequestOptions added in v0.3.0

type PostRequestOptions struct {
	// contains filtered or unexported fields
}

func PostRequestMiddlewares added in v0.3.0

func PostRequestMiddlewares(options ...RequestOption) PostRequestOptions

func (PostRequestOptions) ModifyClient added in v0.3.0

func (r PostRequestOptions) ModifyClient(c *Client)

type PostResponseOptions added in v0.3.0

type PostResponseOptions struct {
	// contains filtered or unexported fields
}

func PostResponseMiddlewares added in v0.3.0

func PostResponseMiddlewares(options ...ResponseOption) PostResponseOptions

func (PostResponseOptions) ModifyClient added in v0.3.0

func (r PostResponseOptions) ModifyClient(c *Client)

type PreRequestOptions added in v0.3.0

type PreRequestOptions struct {
	// contains filtered or unexported fields
}

func PreRequestMiddlewares added in v0.3.0

func PreRequestMiddlewares(options ...RequestOption) PreRequestOptions

func (PreRequestOptions) ModifyClient added in v0.3.0

func (r PreRequestOptions) ModifyClient(c *Client)

type PreResponseOptions added in v0.3.0

type PreResponseOptions struct {
	// contains filtered or unexported fields
}

func PreResponseMiddlewares added in v0.3.0

func PreResponseMiddlewares(options ...ResponseOption) PreResponseOptions

func (PreResponseOptions) ModifyClient added in v0.3.0

func (r PreResponseOptions) ModifyClient(c *Client)

type Request

type Request struct {
	Client *Client

	Method  Method
	URL     *url.URL
	Body    io.ReadCloser
	Headers stdhttp.Header
	// contains filtered or unexported fields
}

func (*Request) Error

func (r *Request) Error() error

Extract any errors out of the request that may have occured when building Done automatically when Sending but provided for extensibility

func (*Request) Send

func (r *Request) Send(ctx context.Context, options ...ResponseOption) (*Response, error)

Send the HTTP Request, processing the response with the options provided

Example
type HNItem struct {
	ID         int64  `json:"id"`
	Type       string `json:"type"`
	Decendants int64  `json:"decendants"`

	By    string `json:"by"`
	Time  int64  `json:"time"`
	Score int64  `json:"score"`

	URL   string `json:"url"`
	Title string `json:"title"`

	Kids []int64 `json:"kids"`
}

ctx := context.Background()

// Set the base url for the client
client := http.NewClient(http.URLString("https://hacker-news.firebaseio.com/"))

// make GET request to https://hacker-news.firebaseio.com/v0/item/8863.json
req := client.Get(http.Path("v0", "item", "8863.json"))

respBody := new(HNItem)
resp, err := req.Send(ctx, http.JSON(respBody))
if err != nil {
	panic(err)
}

fmt.Printf("%d: %s", resp.StatusCode, respBody.Title)
Output:

200: My YC app: Dropbox - Throw away your USB drive

type RequestOption

type RequestOption interface {
	ModifyRequest(*Request) error
}

RequestOption is the option type for requests

type Response added in v0.1.2

type Response struct {
	Headers    stdhttp.Header
	StatusCode Status
	// contains filtered or unexported fields
}

func (*Response) Close added in v0.2.0

func (r *Response) Close() error

func (*Response) Read added in v0.2.0

func (r *Response) Read(p []byte) (n int, err error)

func (*Response) Reset added in v0.2.0

func (r *Response) Reset()

type ResponseOption added in v0.1.2

type ResponseOption interface {
	ProcessResponse(*Response) error
}

ResponseOption is the option type for responses

type Status added in v0.3.0

type Status int
const (
	StatusContinue           Status = 100 // RFC 7231, 6.2.1
	StatusSwitchingProtocols Status = 101 // RFC 7231, 6.2.2
	StatusProcessing         Status = 102 // RFC 2518, 10.1
	StatusEarlyHints         Status = 103 // RFC 8297

	StatusOK                   Status = 200 // RFC 7231, 6.3.1
	StatusCreated              Status = 201 // RFC 7231, 6.3.2
	StatusAccepted             Status = 202 // RFC 7231, 6.3.3
	StatusNonAuthoritativeInfo Status = 203 // RFC 7231, 6.3.4
	StatusNoContent            Status = 204 // RFC 7231, 6.3.5
	StatusResetContent         Status = 205 // RFC 7231, 6.3.6
	StatusPartialContent       Status = 206 // RFC 7233, 4.1
	StatusMultiStatus          Status = 207 // RFC 4918, 11.1
	StatusAlreadyReported      Status = 208 // RFC 5842, 7.1
	StatusIMUsed               Status = 226 // RFC 3229, 10.4.1

	StatusMultipleChoices  Status = 300 // RFC 7231, 6.4.1
	StatusMovedPermanently Status = 301 // RFC 7231, 6.4.2
	StatusFound            Status = 302 // RFC 7231, 6.4.3
	StatusSeeOther         Status = 303 // RFC 7231, 6.4.4
	StatusNotModified      Status = 304 // RFC 7232, 4.1
	StatusUseProxy         Status = 305 // RFC 7231, 6.4.5

	StatusTemporaryRedirect Status = 307 // RFC 7231, 6.4.7
	StatusPermanentRedirect Status = 308 // RFC 7538, 3

	StatusBadRequest                   Status = 400 // RFC 7231, 6.5.1
	StatusUnauthorized                 Status = 401 // RFC 7235, 3.1
	StatusPaymentRequired              Status = 402 // RFC 7231, 6.5.2
	StatusForbidden                    Status = 403 // RFC 7231, 6.5.3
	StatusNotFound                     Status = 404 // RFC 7231, 6.5.4
	StatusMethodNotAllowed             Status = 405 // RFC 7231, 6.5.5
	StatusNotAcceptable                Status = 406 // RFC 7231, 6.5.6
	StatusProxyAuthRequired            Status = 407 // RFC 7235, 3.2
	StatusRequestTimeout               Status = 408 // RFC 7231, 6.5.7
	StatusConflict                     Status = 409 // RFC 7231, 6.5.8
	StatusGone                         Status = 410 // RFC 7231, 6.5.9
	StatusLengthRequired               Status = 411 // RFC 7231, 6.5.10
	StatusPreconditionFailed           Status = 412 // RFC 7232, 4.2
	StatusRequestEntityTooLarge        Status = 413 // RFC 7231, 6.5.11
	StatusRequestURITooLong            Status = 414 // RFC 7231, 6.5.12
	StatusUnsupportedMediaType         Status = 415 // RFC 7231, 6.5.13
	StatusRequestedRangeNotSatisfiable Status = 416 // RFC 7233, 4.4
	StatusExpectationFailed            Status = 417 // RFC 7231, 6.5.14
	StatusTeapot                       Status = 418 // RFC 7168, 2.3.3
	StatusMisdirectedRequest           Status = 421 // RFC 7540, 9.1.2
	StatusUnprocessableEntity          Status = 422 // RFC 4918, 11.2
	StatusLocked                       Status = 423 // RFC 4918, 11.3
	StatusFailedDependency             Status = 424 // RFC 4918, 11.4
	StatusTooEarly                     Status = 425 // RFC 8470, 5.2.
	StatusUpgradeRequired              Status = 426 // RFC 7231, 6.5.15
	StatusPreconditionRequired         Status = 428 // RFC 6585, 3
	StatusTooManyRequests              Status = 429 // RFC 6585, 4
	StatusRequestHeaderFieldsTooLarge  Status = 431 // RFC 6585, 5
	StatusUnavailableForLegalReasons   Status = 451 // RFC 7725, 3

	StatusInternalServerError           Status = 500 // RFC 7231, 6.6.1
	StatusNotImplemented                Status = 501 // RFC 7231, 6.6.2
	StatusBadGateway                    Status = 502 // RFC 7231, 6.6.3
	StatusServiceUnavailable            Status = 503 // RFC 7231, 6.6.4
	StatusGatewayTimeout                Status = 504 // RFC 7231, 6.6.5
	StatusHTTPVersionNotSupported       Status = 505 // RFC 7231, 6.6.6
	StatusVariantAlsoNegotiates         Status = 506 // RFC 2295, 8.1
	StatusInsufficientStorage           Status = 507 // RFC 4918, 11.5
	StatusLoopDetected                  Status = 508 // RFC 5842, 7.2
	StatusNotExtended                   Status = 510 // RFC 2774, 7
	StatusNetworkAuthenticationRequired Status = 511 // RFC 6585, 6
)

HTTP status codes as registered with IANA. See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml

func (Status) String added in v0.3.0

func (s Status) String() string

func (Status) Type added in v0.3.0

func (s Status) Type() StatusType

type StatusType added in v0.3.0

type StatusType int
const (
	StatusTypeInformational StatusType = iota + 1
	StatusTypeSuccess
	StatusTypeRedirection
	StatusTypeClientError
	StatusTypeServerError
)

type URLOption added in v0.1.4

type URLOption struct {
	// contains filtered or unexported fields
}

func URL

func URL(url *url.URL) URLOption

URL is an option to set the url of the request

func (URLOption) ModifyClient added in v0.1.4

func (u URLOption) ModifyClient(c *Client)

func (URLOption) ModifyRequest added in v0.1.4

func (u URLOption) ModifyRequest(r *Request) error

type URLStringOption added in v0.1.4

type URLStringOption struct {
	// contains filtered or unexported fields
}

func URLString

func URLString(url string) URLStringOption

URLString is an option to parse and set the url of the request

func (URLStringOption) ModifyClient added in v0.1.4

func (u URLStringOption) ModifyClient(c *Client)

func (URLStringOption) ModifyRequest added in v0.1.4

func (u URLStringOption) ModifyRequest(r *Request) error

Jump to

Keyboard shortcuts

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