func maybePrettyPrintJSON(d []byte) []byte {
	if d2, ok := asHex(d); ok {
		return d2
	}
	var m map[string]interface{}
	err := json.Unmarshal(d, &m)
	if err != nil {
		return d
	}
	d2, err := json.MarshalIndent(m, "", "  ")
	if err != nil {
		return d
	}
	return d2
}
if d is a valid json, pretty-print it
package httplogproxy
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"
	"github.com/elazarl/goproxy"
	"github.com/elazarl/goproxy/transport"
)
var (
	tr               = transport.Transport{Proxy: transport.ProxyFromEnvironment}
	proxyLogFilePath string
	proxyLogFile     *os.File
	sessionID        int32
	muLog            sync.Mutex
)
func getNextSessionID() int {
	v := atomic.AddInt32(&sessionID, 1)
	return int(v)
}
func clearSessionID() {
	atomic.StoreInt32(&sessionID, 0)
}
func must(err error) {
	if err != nil {
		panic(err.Error())
	}
}
func panicIf(cond bool, format string, args ...interface{}) {
	if cond {
		s := fmt.Sprintf(format, args...)
		panic(s)
	}
}
func valueOrDefault(value, def string) string {
	if value != "" {
		return value
	}
	return def
}
func openLogFile(logPath string) {
	if logPath == "" {
		return
	}
	dir := filepath.Dir(logPath)
	err := os.MkdirAll(dir, 0755)
	must(err)
	f, err := os.Create(logPath)
	must(err)
	proxyLogFile = f
	proxyLogFilePath = logPath
	fmt.Printf("Logging to %s\n", logPath)
	clearSessionID()
}
func closeLogFile() {
	if proxyLogFile != nil {
		proxyLogFile.Close()
		proxyLogFile = nil
	}
}
func CloseLogFile() {
	lgShort("")
	muLog.Lock()
	defer muLog.Unlock()
	closeLogFile()
}
func ChangeLogFile(logPath string) {
	muLog.Lock()
	defer muLog.Unlock()
	if proxyLogFilePath == logPath {
		return
	}
	closeLogFile()
	openLogFile(logPath)
}
func lg(d []byte) {
	muLog.Lock()
	defer muLog.Unlock()
	if proxyLogFile != nil {
		proxyLogFile.Write(d)
		proxyLogFile.Sync()
	}
}
var (
	previousLogLine string
	logLineCount    int
)
func lgShort(s string) {
	muLog.Lock()
	defer muLog.Unlock()
	if s == previousLogLine {
		logLineCount++
		return
	}
	if previousLogLine != "" {
		if logLineCount <= 1 {
			os.Stdout.WriteString(previousLogLine)
		} else {
			s = strings.TrimSpace(previousLogLine)
			s = fmt.Sprintf("%s x %d\n", s, logLineCount)
			os.Stdout.WriteString(s)
		}
	}
	previousLogLine = s
	logLineCount = 1
}
type stoppableListener struct {
	net.Listener
	sync.WaitGroup
}
type stoppableConn struct {
	net.Conn
	wg *sync.WaitGroup
}
func newStoppableListener(l net.Listener) *stoppableListener {
	return &stoppableListener{l, sync.WaitGroup{}}
}
func (sl *stoppableListener) Accept() (net.Conn, error) {
	c, err := sl.Listener.Accept()
	if err != nil {
		return c, err
	}
	sl.Add(1)
	return &stoppableConn{c, &sl.WaitGroup}, nil
}
func (sc *stoppableConn) Close() error {
	sc.wg.Done()
	return sc.Conn.Close()
}
type BufferCloser struct {
	*bytes.Buffer
}
func NewBufferCloser(buf *bytes.Buffer) *BufferCloser {
	if buf == nil {
		buf = &bytes.Buffer{}
	}
	return &BufferCloser{
		Buffer: buf,
	}
}
func (b *BufferCloser) Close() error {
	return nil
}
type SessionData struct {
	reqBody  bytes.Buffer
	respBody *BufferCloser
}
func NewSessionData() *SessionData {
	return &SessionData{}
}
func handleOnRequest(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	panicIf(req == nil, "req == nil")
	sd := NewSessionData()
	ctx.UserData = sd
	if req.Body != nil {
		req.Body = ioutil.NopCloser(io.TeeReader(req.Body, &sd.reqBody))
	}
	return req, nil
}
func isUnprintable(c byte) bool {
	if c < 32 {
		if c == 9 || c == 10 || c == 13 {
			return false
		}
		return true
	}
	return c >= 127
}
func isBinaryData(d []byte) bool {
	for _, b := range d {
		if isUnprintable(b) {
			return true
		}
	}
	return false
}
func asHex(d []byte) ([]byte, bool) {
	if !isBinaryData(d) {
		return d, false
	}
	var res []byte
	for i, c := range d {
		if i > 2048 {
			break
		}
		if isUnprintable(c) {
			s := fmt.Sprintf("x%02x ", c)
			res = append(res, s...)
		} else {
			res = append(res, c)
		}
	}
	return res, true
}