func marshal(w io.Writer, v interface{}, mediaType string) (string, error) {
	if v == nil {
		return mediaType, nil
	}
	switch mediaType {
	case ContentTypeJSON:
		return ContentTypeJSON, MarshalJSON(w, v, mediaType)
	case ContentTypeYAML:
		return ContentTypeYAML, MarshalYAML(w, v, mediaType)
	case ContentTypeText:
		_, err := fmt.Fprint(w, v)
		return ContentTypeText, err
	default:
		return mediaType, errors.Wrap(ErrUnknownContentType, mediaType)
	}
}
marshal is the default marshaler
package httpclient
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"time"
	"golang.org/x/time/rate"
	yaml "gopkg.in/yaml.v2"
	"github.com/google/go-querystring/query"
	"github.com/pkg/errors"
)
const (
	ContentTypeText = "text/plain"
	ContentTypeJSON = "application/json"
	ContentTypeYAML = "application/yaml"
)
var (
	ErrUnknownContentType = errors.New("unknown media type")
	ErrTooManyRequest     = errors.New("too many requests")
)
type Client struct {
	client *http.Client
	limiter *rate.Limiter
	BaseURL *url.URL
	ContentType string
	username string
	password string
	header http.Header
	Marshaler   MarshalerFunc
	Unmarshaler UnmarshalerFunc
	RequestCallback  RequestCallbackFunc
	ResponseCallback ResponseCallbackFunc
}
type Opt func(*Client) error
type RequestCallbackFunc func(*http.Request) *http.Request
type ResponseCallbackFunc func(*http.Response) (*http.Response, error)
type MarshalerFunc func(io.Writer, interface{}, string) (string, error)
type UnmarshalerFunc func(io.Reader, interface{}, string) error
func QueryOptions(u string, opt interface{}) (string, error) {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return u, nil
	}
	origURL, err := url.Parse(u)
	if err != nil {
		return u, err
	}
	origValues := origURL.Query()
	newValues, err := query.Values(opt)
	if err != nil {
		return u, err
	}
	for k, v := range newValues {
		origValues[k] = v
	}
	origURL.RawQuery = origValues.Encode()
	return origURL.String(), nil
}
func New(baseURL string, opts ...Opt) (*Client, error) {
	u, err := url.Parse(baseURL)
	if err != nil {
		return nil, err
	}
	c := &Client{
		client: &http.Client{
			Timeout: 30 * time.Second,
		},
		BaseURL:          u,
		ContentType:      ContentTypeJSON,
		Marshaler:        marshal,
		Unmarshaler:      unmarshal,
		RequestCallback:  requestCallback,
		ResponseCallback: responseCallback,
	}
	for _, opt := range opts {
		if err := opt(c); err != nil {
			return nil, err
		}
	}
	return c, nil
}
func WithPassword(p string) Opt {
	return func(c *Client) error {
		if p == "" {
			return errors.New("password cannot be empty")
		}
		c.password = p
		return nil
	}
}
func WithUsername(u string) Opt {
	return func(c *Client) error {
		if u == "" {
			return errors.New("username cannot be empty")
		}
		c.username = u
		return nil
	}
}
func WithHTTPClient(c *http.Client) Opt {
	return func(cli *Client) error {
		cli.client = c
		return nil
	}
}
func WithRateLimiter(l *rate.Limiter) Opt {
	return func(cli *Client) error {
		cli.limiter = l
		return nil
	}
}
func WithContentType(ct string) Opt {
	return func(c *Client) error {
		if ct == "" {
			return errors.New("content type cannot be empty")
		}
		c.ContentType = ct
		return nil
	}
}
func WithHeader(header http.Header) Opt {
	return func(c *Client) error {
		c.header = header
		return nil
	}
}
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}
	u := c.BaseURL.ResolveReference(rel)
	if c.Marshaler == nil {
		panic("Marshaler is nil")
	}
	buf := new(bytes.Buffer)
	contentType, err := c.Marshaler(buf, body, c.ContentType)
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}
	if c.header != nil {
		req.Header = c.header
	}
	if len(c.username) > 0 && len(c.password) > 0 {
		req.SetBasicAuth(c.username, c.password)
	}
	req.Header.Add("Content-Type", contentType)
	req.Header.Add("Accept", contentType)
	if c.RequestCallback == nil {
		panic("RequestCallback is nil")
	}
	return c.RequestCallback(req), nil
}