func (req *RequestData) Forward(client *http.Client, config BasketConfig, basket string) (*http.Response, error) {
	forwardURL, err := url.ParseRequestURI(config.ForwardURL)
	if err != nil {
		return nil, fmt.Errorf("invalid forward URL: %s - %s", config.ForwardURL, err)
	}
	if config.ExpandPath && len(req.Path) > len(basket)+1 {
		forwardURL.Path = expandURL(forwardURL.Path, req.Path, basket)
	}
	if len(req.Query) > 0 {
		if len(forwardURL.RawQuery) > 0 {
			forwardURL.RawQuery += "&" + req.Query
		} else {
			forwardURL.RawQuery = req.Query
		}
	}
	forwardReq, err := http.NewRequest(req.Method, forwardURL.String(), strings.NewReader(req.Body))
	if err != nil {
		return nil, fmt.Errorf("failed to create forward request: %s", err)
	}
	for header, vals := range req.Header {
		for _, val := range vals {
			forwardReq.Header.Add(header, val)
		}
	}
	forwardHeadersCleanup(forwardReq)
	forwardReq.Header.Set(DoNotForwardHeader, "1")
	response, err := client.Do(forwardReq)
	if err != nil {
		log.Printf("[warn] failed to forward request for basket: %s - %s", basket, err)
		badGatewayResp := &http.Response{
			StatusCode: http.StatusBadGateway,
			Header:     http.Header{},
			Body:       ioutil.NopCloser(strings.NewReader(fmt.Sprintf("Failed to forward request: %s", err)))}
		badGatewayResp.Header.Set("Content-Type", "text/plain")
		return badGatewayResp, nil
	}
	return response, nil
}
Forward forwards request data to specified URL
package main
import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)
const toMs = int64(time.Millisecond) / int64(time.Nanosecond)
const DoNotForwardHeader = "X-Do-Not-Forward"
type BasketConfig struct {
	ForwardURL    string `json:"forward_url"`
	ProxyResponse bool   `json:"proxy_response"`
	InsecureTLS   bool   `json:"insecure_tls"`
	ExpandPath    bool   `json:"expand_path"`
	Capacity      int    `json:"capacity"`
}
type ResponseConfig struct {
	Status     int         `json:"status"`
	Headers    http.Header `json:"headers"`
	Body       string      `json:"body"`
	IsTemplate bool        `json:"is_template"`
}
type BasketAuth struct {
	Token string `json:"token"`
}
type RequestData struct {
	Date          int64       `json:"date"`
	Header        http.Header `json:"headers"`
	ContentLength int64       `json:"content_length"`
	Body          string      `json:"body"`
	Method        string      `json:"method"`
	Path          string      `json:"path"`
	Query         string      `json:"query"`
}
type RequestsPage struct {
	Requests   []*RequestData `json:"requests"`
	Count      int            `json:"count"`
	TotalCount int            `json:"total_count"`
	HasMore    bool           `json:"has_more"`
}
type RequestsQueryPage struct {
	Requests []*RequestData `json:"requests"`
	HasMore  bool           `json:"has_more"`
}
type BasketNamesPage struct {
	Names   []string `json:"names"`
	Count   int      `json:"count"`
	HasMore bool     `json:"has_more"`
}
type BasketNamesQueryPage struct {
	Names   []string `json:"names"`
	HasMore bool     `json:"has_more"`
}
type DatabaseStats struct {
	BasketsCount       int           `json:"baskets_count"`
	EmptyBasketsCount  int           `json:"empty_baskets_count"`
	RequestsCount      int           `json:"requests_count"`
	RequestsTotalCount int           `json:"requests_total_count"`
	MaxBasketSize      int           `json:"max_basket_size"`
	AvgBasketSize      int           `json:"avg_basket_size"`
	TopBasketsBySize   []*BasketInfo `json:"top_baskets_size"`
	TopBasketsByDate   []*BasketInfo `json:"top_baskets_recent"`
}
type BasketInfo struct {
	Name               string `json:"name"`
	RequestsCount      int    `json:"requests_count"`
	RequestsTotalCount int    `json:"requests_total_count"`
	LastRequestDate    int64  `json:"last_request_date"`
}
type Basket interface {
	Config() BasketConfig
	Update(config BasketConfig)
	Authorize(token string) bool
	GetResponse(method string) *ResponseConfig
	SetResponse(method string, response ResponseConfig)
	Add(req *http.Request) *RequestData
	Clear()
	Size() int
	GetRequests(max int, skip int) RequestsPage
	FindRequests(query string, in string, max int, skip int) RequestsQueryPage
}
type BasketsDatabase interface {
	Create(name string, config BasketConfig) (BasketAuth, error)
	Get(name string) Basket
	Delete(name string)
	Size() int
	GetNames(max int, skip int) BasketNamesPage
	FindNames(query string, max int, skip int) BasketNamesQueryPage
	GetStats(max int) DatabaseStats
	Release()
}
func ToRequestData(req *http.Request) *RequestData {
	data := new(RequestData)
	data.Date = time.Now().UnixNano() / toMs
	data.Header = make(http.Header)
	for k, v := range req.Header {
		data.Header[k] = v
	}
	data.ContentLength = req.ContentLength
	data.Method = req.Method
	data.Path = req.URL.Path
	data.Query = req.URL.RawQuery
	body, _ := ioutil.ReadAll(req.Body)
	data.Body = string(body)
	return data
}