func WithQueryParams(qp map[string]string) HTTPOption {
	return func(r *http.Request) {
		q := r.URL.Query()
		for k, v := range qp {
			q.Add(k, v)
		}
		r.URL.RawQuery = q.Encode()
	}
}
WithQueryParams creates an HTTPOption that will set all the entries of qp as query parameters
on the request.
package internal
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"net/http"
	"strconv"
	"time"
	"google.golang.org/api/option"
	"google.golang.org/api/transport"
)
type HTTPClient struct {
	Client      *http.Client
	RetryConfig *RetryConfig
	CreateErrFn CreateErrFn
	SuccessFn   SuccessFn
	Opts        []HTTPOption
}
type SuccessFn func(r *Response) bool
type CreateErrFn func(r *Response) error
func NewHTTPClient(ctx context.Context, opts ...option.ClientOption) (*HTTPClient, string, error) {
	hc, endpoint, err := transport.NewHTTPClient(ctx, opts...)
	if err != nil {
		return nil, "", err
	}
	return WithDefaultRetryConfig(hc), endpoint, nil
}
func WithDefaultRetryConfig(hc *http.Client) *HTTPClient {
	twoMinutes := time.Duration(2) * time.Minute
	return &HTTPClient{
		Client: hc,
		RetryConfig: &RetryConfig{
			MaxRetries: 4,
			CheckForRetry: retryNetworkAndHTTPErrors(
				http.StatusInternalServerError,
				http.StatusServiceUnavailable,
			),
			ExpBackoffFactor: 0.5,
			MaxDelay:         &twoMinutes,
		},
	}
}
type Request struct {
	Method      string
	URL         string
	Body        HTTPEntity
	Opts        []HTTPOption
	SuccessFn   SuccessFn
	CreateErrFn CreateErrFn
}
type Response struct {
	Status int
	Header http.Header
	Body   []byte
	resp   *http.Response
}
func (r *Response) LowLevelResponse() *http.Response {
	if r.resp == nil {
		return nil
	}
	resp := *r.resp
	resp.Body = ioutil.NopCloser(bytes.NewBuffer(r.Body))
	return &resp
}
func (c *HTTPClient) Do(ctx context.Context, req *Request) (*Response, error) {
	var result *attemptResult
	for retries := 0; ; retries++ {
		hr, err := req.buildHTTPRequest(c.Opts)
		if err != nil {
			return nil, err
		}
		result = c.attempt(ctx, hr, retries)
		if !result.Retry {
			break
		}
		if err = result.waitForRetry(ctx); err != nil {
			return nil, err
		}
	}
	return c.handleResult(req, result)
}
func (c *HTTPClient) DoAndUnmarshal(ctx context.Context, req *Request, v interface{}) (*Response, error) {
	resp, err := c.Do(ctx, req)
	if err != nil {
		return nil, err
	}
	if v != nil {
		if err := json.Unmarshal(resp.Body, v); err != nil {
			return nil, fmt.Errorf("error while parsing response: %v", err)
		}
	}
	return resp, nil
}
func (c *HTTPClient) attempt(ctx context.Context, hr *http.Request, retries int) *attemptResult {
	resp, err := c.Client.Do(hr.WithContext(ctx))
	result := &attemptResult{}
	if err != nil {
		result.Err = err
	} else {
		ir, err := newResponse(resp)
		result.Resp = ir
		result.Err = err
	}
	if c.RetryConfig != nil {
		delay, retry := c.RetryConfig.retryDelay(retries, resp, result.Err)
		result.RetryAfter = delay
		result.Retry = retry
	}
	return result
}
func (c *HTTPClient) handleResult(req *Request, result *attemptResult) (*Response, error) {
	if result.Err != nil {
		return nil, newFirebaseErrorTransport(result.Err)
	}
	if !c.success(req, result.Resp) {
		return nil, c.newError(req, result.Resp)
	}
	return result.Resp, nil
}
func (c *HTTPClient) success(req *Request, resp *Response) bool {
	var successFn SuccessFn
	if req.SuccessFn != nil {
		successFn = req.SuccessFn
	} else if c.SuccessFn != nil {
		successFn = c.SuccessFn
	} else {
		successFn = HasSuccessStatus
	}
	return successFn(resp)
}
func (c *HTTPClient) newError(req *Request, resp *Response) error {
	createErr := func(r *Response) error {
		return NewFirebaseErrorOnePlatform(r)
	}
	if req.CreateErrFn != nil {
		createErr = req.CreateErrFn
	} else if c.CreateErrFn != nil {
		createErr = c.CreateErrFn
	}
	return createErr(resp)
}
type attemptResult struct {
	Resp       *Response
	Err        error
	Retry      bool
	RetryAfter time.Duration
}
func (r *attemptResult) waitForRetry(ctx context.Context) error {
	if r.RetryAfter > 0 {
		select {
		case <-ctx.Done():
		case <-time.After(r.RetryAfter):
		}
	}
	return ctx.Err()
}
func (r *Request) buildHTTPRequest(opts []HTTPOption) (*http.Request, error) {
	var data io.Reader
	if r.Body != nil {
		b, err := r.Body.Bytes()
		if err != nil {
			return nil, err
		}
		data = bytes.NewBuffer(b)
		opts = append(opts, WithHeader("Content-Type", r.Body.Mime()))
	}
	req, err := http.NewRequest(r.Method, r.URL, data)
	if err != nil {
		return nil, err
	}
	opts = append(opts, r.Opts...)
	for _, o := range opts {
		o(req)
	}
	return req, nil
}
type HTTPEntity interface {
	Bytes() ([]byte, error)
	Mime() string
}
type jsonEntity struct {
	Val interface{}
}
func NewJSONEntity(v interface{}) HTTPEntity {
	return &jsonEntity{Val: v}
}
func (e *jsonEntity) Bytes() ([]byte, error) {
	return json.Marshal(e.Val)
}
func (e *jsonEntity) Mime() string {
	return "application/json"
}
func newResponse(resp *http.Response) (*Response, error) {
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return &Response{
		Status: resp.StatusCode,
		Body:   b,
		Header: resp.Header,
		resp:   resp,
	}, nil
}
type HTTPOption func(*http.Request)
func WithHeader(key, value string) HTTPOption {
	return func(r *http.Request) {
		r.Header.Set(key, value)
	}
}
func WithQueryParam(key, value string) HTTPOption {
	return func(r *http.Request) {
		q := r.URL.Query()
		q.Add(key, value)
		r.URL.RawQuery = q.Encode()
	}
}