func StartReverseProxy(port string, remoteURL *url.URL, opts Options) error {
	remoteHost = remoteURL.Host
	requests := make(chan Event, 10)
	go process(requests, opts)
	remote := remoteURL.Host
	proxy := newSingleHostReverseProxy(remoteURL)
	http.HandleFunc("/", handler(proxy, requests, opts.Rewrite))
	log.Println("Staring reverse proxy")
	log.Printf("Proxying from http://localhost%s to %s", port, remote)
	return http.ListenAndServe(port, nil)
}
StartReverseProxy listens on the local port and forwards request to remote
while capturing the request/response pairs selected by opts.
package recorder
import (
	"bytes"
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"image"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"net/url"
	"os"
	"path"
	"regexp"
	"strings"
	"time"
	"golang.org/x/net/html"
	"github.com/andybalholm/cascadia"
	"github.com/vdobler/ht/fingerprint"
	"github.com/vdobler/ht/ht"
	"github.com/vdobler/ht/sanitize"
)
var Events []Event
type Event struct {
	Request      *http.Request               
	Response     *httptest.ResponseRecorder  
	RequestBody  string                      
	ResponseBody string
	Timestamp    time.Time  
	Name         string     
}
func (e Event) extractName() string {
	doc, err := html.Parse(bytes.NewBufferString(e.ResponseBody))
	if err != nil {
		return ""
	}
	if node := cascadia.MustCompile("head title").MatchFirst(doc); node != nil {
		title := ht.TextContent(node, false)
		if title != "" {
			return title
		}
	}
	if node := cascadia.MustCompile("h1").MatchFirst(doc); node != nil {
		title := ht.TextContent(node, false)
		if title != "" {
			return title
		}
	}
	p := e.Request.URL.Path
	p = p[strings.LastIndex(p, "/")+1:]
	if i := strings.Index(p, "."); i != -1 {
		p = p[:i]
		if p != "" {
			return p
		}
	}
	return ""
}
type Options struct {
	Disarm time.Duration
	IgnoredContentType *regexp.Regexp
	IgnoredPath *regexp.Regexp
	Rewrite Rewriter
}
func (o Options) ignore(e Event) bool {
	if o.IgnoredPath != nil && o.IgnoredPath.MatchString(e.Request.URL.Path) {
		log.Println("Ignoring path", e.Request.URL.Path)
		return true
	}
	if o.IgnoredContentType != nil &&
		o.IgnoredContentType.MatchString(e.Response.HeaderMap.Get("Content-Type")) {
		log.Println("Ignoring content type ", e.Response.HeaderMap.Get("Content-Type"))
		return true
	}
	return false
}
var (
	remoteHost string
)
type Rewriter struct {
	local  string
	remote string
	remoteRe  *regexp.Regexp
	remoteSub string
	localRe  *regexp.Regexp
	localSub string
	what uint32
}
const (
	RewriteNothing        uint32 = 0
	RewriteResponseHeader uint32 = 1 << (iota - 1)
	RewriteResponseBody
	RewriteRequestHeader
	RewriteRequestBody
)
func NewRewriter(local, remote string, what uint32) Rewriter {
	r := Rewriter{
		local:  local,
		remote: remote,
		what:   what,
	}
	r.remoteRe = regexp.MustCompile(
		`(^|[^a-zAZ0-9])` + regexp.QuoteMeta(remote) + `([^a-zAZ0-9]|$)`)
	r.remoteSub = "${1}" + local + "${2}"
	r.localRe = regexp.MustCompile(
		`(^|[^a-zAZ0-9])` + regexp.QuoteMeta(local) + `([^a-zAZ0-9]|$)`)
	r.localSub = "${1}" + remote + "${2}"
	return r
}
func (r Rewriter) Response(header http.Header, body []byte) (http.Header, []byte) {
	rheader := r.header(header, r.remoteRe, r.remoteSub, r.what&RewriteResponseHeader != 0)
	rbody := r.body(body, r.remoteRe, r.remoteSub, r.what&RewriteResponseBody != 0)
	return rheader, rbody
}
func (r Rewriter) Request(header http.Header, body []byte) (http.Header, []byte) {
	rheader := r.header(header, r.localRe, r.localSub, r.what&RewriteRequestHeader != 0)
	rbody := r.body(body, r.localRe, r.localSub, r.what&RewriteRequestBody != 0)
	return rheader, rbody
}
func (r Rewriter) header(header http.Header, re *regexp.Regexp, sub string, do bool) http.Header {
	rheader := http.Header{}
	for h, vv := range header {
		if h == "Content-Length" {
			continue
		}
		if do {
			for i, v := range vv {
				w := r.remoteRe.ReplaceAllString(v, r.remoteSub)
				if w != v {
					fmt.Printf("Rewrite Response Header %q\n    from: %q\n    to:   %q\n",
						h, v, w)
				}
				vv[i] = w
			}
		}
		rheader[h] = vv
	}
	return rheader
}
func (r Rewriter) body(body []byte, re *regexp.Regexp, sub string, do bool) []byte {
	if !do {
		return body
	}
	rbody := r.remoteRe.ReplaceAll(body, []byte(r.remoteSub))
	if !bytes.Equal(body, rbody) {
		n := len(r.remoteRe.FindAllIndex(body, -1))
		fmt.Printf("Rewrite Response Body: %d occurrences\n", n)
	}
	return rbody
}