2020-11-04 20:12:13 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Client is the default http client instance used by the following methods.
|
|
|
|
var Client = http.DefaultClient
|
|
|
|
|
|
|
|
// RequestOption is a function which can be used to modify
|
|
|
|
// a request instance before Do.
|
|
|
|
type RequestOption func(*http.Request) error
|
|
|
|
|
|
|
|
// WithAccessToken sets the given "token" to the authorization request header.
|
|
|
|
func WithAccessToken(token []byte) RequestOption {
|
|
|
|
bearer := "Bearer " + string(token)
|
|
|
|
return func(req *http.Request) error {
|
|
|
|
req.Header.Add("Authorization", bearer)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithContentType sets the content-type request header.
|
|
|
|
func WithContentType(cType string) RequestOption {
|
|
|
|
return func(req *http.Request) error {
|
|
|
|
req.Header.Set("Content-Type", cType)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithContentLength sets the content-length request header.
|
|
|
|
func WithContentLength(length int) RequestOption {
|
|
|
|
return func(req *http.Request) error {
|
|
|
|
req.Header.Set("Content-Length", strconv.Itoa(length))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do fires a request to the server.
|
|
|
|
func Do(method, url string, body io.Reader, opts ...RequestOption) (*http.Response, error) {
|
|
|
|
req, err := http.NewRequest(method, url, body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, opt := range opts {
|
|
|
|
if err = opt(req); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Client.Do(req)
|
|
|
|
}
|
|
|
|
|
|
|
|
// JSON fires a request with "v" as client json data.
|
|
|
|
func JSON(method, url string, v interface{}, opts ...RequestOption) (*http.Response, error) {
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
err := json.NewEncoder(buf).Encode(v)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
opts = append(opts, WithContentType("application/json; charset=utf-8"))
|
|
|
|
return Do(method, url, buf, opts...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Form fires a request with "formData" as client form data.
|
|
|
|
func Form(method, url string, formData url.Values, opts ...RequestOption) (*http.Response, error) {
|
|
|
|
encoded := formData.Encode()
|
|
|
|
body := strings.NewReader(encoded)
|
|
|
|
|
|
|
|
opts = append([]RequestOption{
|
|
|
|
WithContentType("application/x-www-form-urlencoded"),
|
|
|
|
WithContentLength(len(encoded)),
|
|
|
|
}, opts...)
|
|
|
|
|
|
|
|
return Do(method, url, body, opts...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// BindResponse binds a response body to the "dest" pointer and closes the body.
|
|
|
|
func BindResponse(resp *http.Response, dest interface{}) error {
|
|
|
|
contentType := resp.Header.Get("Content-Type")
|
|
|
|
if idx := strings.IndexRune(contentType, ';'); idx > 0 {
|
|
|
|
contentType = contentType[0:idx]
|
|
|
|
}
|
|
|
|
|
|
|
|
switch contentType {
|
|
|
|
case "application/json":
|
|
|
|
defer resp.Body.Close()
|
|
|
|
return json.NewDecoder(resp.Body).Decode(dest)
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unsupported content type: %s", contentType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RawResponse simply returns the raw response body.
|
|
|
|
func RawResponse(resp *http.Response) ([]byte, error) {
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
2022-06-17 21:03:18 +02:00
|
|
|
return io.ReadAll(resp.Body)
|
2020-11-04 20:12:13 +01:00
|
|
|
}
|