mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-01-10 04:18:14 +02:00
Add request builder to simplify request handling
This commit is contained in:
parent
9d39816709
commit
0bc0feb4bb
173
pkg/requests/builder.go
Normal file
173
pkg/requests/builder.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package requests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/bitly/go-simplejson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Builder allows users to construct a request and then either get the requests
|
||||||
|
// response via Do(), parse the response into a simplejson.Json via JSON(),
|
||||||
|
// or to parse the json response into an object via UnmarshalInto().
|
||||||
|
type Builder interface {
|
||||||
|
WithContext(context.Context) Builder
|
||||||
|
WithBody(io.Reader) Builder
|
||||||
|
WithMethod(string) Builder
|
||||||
|
WithHeaders(http.Header) Builder
|
||||||
|
SetHeader(key, value string) Builder
|
||||||
|
Do() (*http.Response, error)
|
||||||
|
UnmarshalInto(interface{}) error
|
||||||
|
UnmarshalJSON() (*simplejson.Json, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type builder struct {
|
||||||
|
context context.Context
|
||||||
|
method string
|
||||||
|
endpoint string
|
||||||
|
body io.Reader
|
||||||
|
header http.Header
|
||||||
|
response *http.Response
|
||||||
|
}
|
||||||
|
|
||||||
|
// New provides a new Builder for the given endpoint.
|
||||||
|
func New(endpoint string) Builder {
|
||||||
|
return &builder{
|
||||||
|
endpoint: endpoint,
|
||||||
|
method: "GET",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext adds a context to the request.
|
||||||
|
// If no context is provided, context.Background() is used instead.
|
||||||
|
func (r *builder) WithContext(ctx context.Context) Builder {
|
||||||
|
r.context = ctx
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithBody adds a body to the request.
|
||||||
|
func (r *builder) WithBody(body io.Reader) Builder {
|
||||||
|
r.body = body
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMethod sets the request method. Defaults to "GET".
|
||||||
|
func (r *builder) WithMethod(method string) Builder {
|
||||||
|
r.method = method
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHeaders replaces the request header map with the given header map.
|
||||||
|
func (r *builder) WithHeaders(header http.Header) Builder {
|
||||||
|
r.header = header
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHeader sets a single header to the given value.
|
||||||
|
// May be used to add multiple headers.
|
||||||
|
func (r *builder) SetHeader(key, value string) Builder {
|
||||||
|
if r.header == nil {
|
||||||
|
r.header = make(http.Header)
|
||||||
|
}
|
||||||
|
r.header.Set(key, value)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do performs the request and returns the response in its raw form.
|
||||||
|
// If the request has already been performed, returns the previous result.
|
||||||
|
// This will not allow you to repeat a request.
|
||||||
|
func (r *builder) Do() (*http.Response, error) {
|
||||||
|
if r.response != nil {
|
||||||
|
// Request has already been done
|
||||||
|
return r.response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must provide a non-nil context to NewRequestWithContext
|
||||||
|
if r.context == nil {
|
||||||
|
r.context = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(r.context, r.method, r.endpoint, r.body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating request: %v", err)
|
||||||
|
}
|
||||||
|
req.Header = r.header
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error performing request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.response = resp
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalInto performs the request and attempts to unmarshal the response into the
|
||||||
|
// the given interface. The response body is assumed to be JSON.
|
||||||
|
// The response must have a 200 status otherwise an error will be returned.
|
||||||
|
func (r *builder) UnmarshalInto(into interface{}) error {
|
||||||
|
resp, err := r.Do()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnmarshalInto(resp, into)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON performs the request and attempts to unmarshal the response into a
|
||||||
|
// simplejson.Json. The response body is assume to be JSON.
|
||||||
|
// The response must have a 200 status otherwise an error will be returned.
|
||||||
|
func (r *builder) UnmarshalJSON() (*simplejson.Json, error) {
|
||||||
|
resp, err := r.Do()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := getResponseBody(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := simplejson.NewJson(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading json: %v", err)
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalInto attempts to unmarshal the response into the the given interface.
|
||||||
|
// The response body is assumed to be JSON.
|
||||||
|
// The response must have a 200 status otherwise an error will be returned.
|
||||||
|
func UnmarshalInto(resp *http.Response, into interface{}) error {
|
||||||
|
body, err := getResponseBody(resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(body, into); err != nil {
|
||||||
|
return fmt.Errorf("error unmarshalling body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getResponseBody extracts the response body, but will only return the body
|
||||||
|
// if the response was successful.
|
||||||
|
func getResponseBody(resp *http.Response) ([]byte, error) {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading response body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only unmarshal body if the response was successful
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("unexpected status \"%d\": %s", resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return body, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user