func (l *tlsHelloListener) Accept() (net.Conn, error) {
	conn, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	buf := bufpool.Get().(*bytes.Buffer)
	buf.Reset()
	helloConn := &clientHelloConn{Conn: conn, listener: l, buf: buf}
	return tls.Server(helloConn, l.config), nil
}
Accept waits for and returns the next connection to the listener.
After it accepts the underlying connection, it reads the
ClientHello message and stores the parsed data into a map on l.
package httpserver
import (
	"bytes"
	"context"
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"github.com/tmpim/casket/caskettls"
	"github.com/tmpim/casket/telemetry"
)
type tlsHandler struct {
	next        http.Handler
	listener    *tlsHelloListener
	closeOnMITM bool  
}
func (h *tlsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h.listener == nil {
		h.next.ServeHTTP(w, r)
		return
	}
	h.listener.helloInfosMu.RLock()
	info := h.listener.helloInfos[r.RemoteAddr]
	h.listener.helloInfosMu.RUnlock()
	ua := r.Header.Get("User-Agent")
	uaHash := telemetry.FastHash([]byte(ua))
	go telemetry.AppendUnique("tls_client_hello_ua:"+caskettls.ClientHelloInfo(info).Key(), uaHash)
	var checked, mitm bool
	if r.Header.Get("X-BlueCoat-Via") != "" ||  
		r.Header.Get("X-FCCKV2") != "" ||  
		info.advertisesHeartbeatSupport() {  
		checked = true
		mitm = true
	} else if strings.Contains(ua, "Edge") || strings.Contains(ua, "MSIE") ||
		strings.Contains(ua, "Trident") {
		checked = true
		mitm = !info.looksLikeEdge()
	} else if strings.Contains(ua, "Chrome") {
		checked = true
		mitm = !info.looksLikeChrome()
	} else if strings.Contains(ua, "CriOS") {
		checked = true
		mitm = !info.looksLikeChrome() && !info.looksLikeSafari()
	} else if strings.Contains(ua, "Firefox") {
		checked = true
		if strings.Contains(ua, "Windows") {
			ver := getVersion(ua, "Firefox")
			if ver == 45.0 || ver == 52.0 {
				mitm = !info.looksLikeTor()
			} else {
				mitm = !info.looksLikeFirefox()
			}
		} else {
			mitm = !info.looksLikeFirefox()
		}
	} else if strings.Contains(ua, "Safari") {
		checked = true
		mitm = !info.looksLikeSafari()
	}
	if checked {
		r = r.WithContext(context.WithValue(r.Context(), MitmCtxKey, mitm))
		if mitm {
			go telemetry.AppendUnique("http_mitm", "likely")
		} else {
			go telemetry.AppendUnique("http_mitm", "unlikely")
		}
	} else {
		go telemetry.AppendUnique("http_mitm", "unknown")
	}
	if mitm && h.closeOnMITM {
		r.Close = true
		return
	}
	h.next.ServeHTTP(w, r)
}
func getVersion(ua, softwareName string) float64 {
	search := softwareName + "/"
	start := strings.Index(ua, search)
	if start < 0 {
		return -1
	}
	start += len(search)
	end := strings.Index(ua[start:], " ")
	if end < 0 {
		end = len(ua)
	} else {
		end += start
	}
	strVer := strings.Replace(ua[start:end], "-", "", -1)
	firstDot := strings.Index(strVer, ".")
	if firstDot >= 0 {
		strVer = strVer[:firstDot+1] + strings.Replace(strVer[firstDot+1:], ".", "", -1)
	}
	ver, err := strconv.ParseFloat(strVer, 64)
	if err != nil {
		return -1
	}
	return ver
}
type clientHelloConn struct {
	net.Conn
	listener  *tlsHelloListener
	readHello bool  
	buf       *bytes.Buffer
}
func (c *clientHelloConn) Read(b []byte) (n int, err error) {
	if c.readHello {
		return c.Conn.Read(b)
	}
	tee := io.TeeReader(c.Conn, c.buf)
	n, err = tee.Read(b)
	if err != nil {
		return
	}
	if c.buf.Len() < 5 {
		return  
	}
	hdr := make([]byte, 5)
	_, err = io.ReadFull(c.buf, hdr)
	if err != nil {
		return  
	}
	length := int(uint16(hdr[3])<<8 | uint16(hdr[4]))
	if c.buf.Len() < length {
		return  
	}
	hello := make([]byte, length)
	_, err = io.ReadFull(c.buf, hello)
	if err != nil {
		return
	}
	bufpool.Put(c.buf)  
	rawParsed := parseRawClientHello(hello)
	c.listener.helloInfosMu.Lock()
	c.listener.helloInfos[c.Conn.RemoteAddr().String()] = rawParsed
	c.listener.helloInfosMu.Unlock()
	chKey := caskettls.ClientHelloInfo(rawParsed).Key()
	go telemetry.SetNested("tls_client_hello", chKey, rawParsed)
	go telemetry.AppendUnique("tls_client_hello_count", chKey)
	c.readHello = true
	return
}
func parseRawClientHello(data []byte) (info rawHelloInfo) {
	if len(data) < 42 {
		return
	}
	info.Version = uint16(data[4])<<8 | uint16(data[5])
	sessionIDLen := int(data[38])
	if sessionIDLen > 32 || len(data) < 39+sessionIDLen {
		return
	}
	data = data[39+sessionIDLen:]
	if len(data) < 2 {
		return
	}
	cipherSuiteLen := int(data[0])<<8 | int(data[1])
	if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
		return
	}
	numCipherSuites := cipherSuiteLen / 2
	info.CipherSuites = make([]uint16, numCipherSuites)
	for i := 0; i < numCipherSuites; i++ {
		info.CipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
	}
	data = data[2+cipherSuiteLen:]
	if len(data) < 1 {
		return
	}
	compressionMethodsLen := int(data[0])
	if len(data) < 1+compressionMethodsLen {
		return
	}
	info.CompressionMethods = data[1 : 1+compressionMethodsLen]
	data = data[1+compressionMethodsLen:]
	if len(data) < 2 {
		return
	}
	extensionsLength := int(data[0])<<8 | int(data[1])
	data = data[2:]
	if extensionsLength != len(data) {
		return
	}
	for len(data) != 0 {
		if len(data) < 4 {
			return
		}
		extension := uint16(data[0])<<8 | uint16(data[1])
		length := int(data[2])<<8 | int(data[3])
		data = data[4:]
		if len(data) < length {
			return
		}
		info.Extensions = append(info.Extensions, extension)
		switch extension {
		case extensionSupportedCurves:
			if length < 2 {
				return
			}
			l := int(data[0])<<8 | int(data[1])
			if l%2 == 1 || length != l+2 {
				return
			}
			numCurves := l / 2
			info.Curves = make([]tls.CurveID, numCurves)
			d := data[2:]
			for i := 0; i < numCurves; i++ {
				info.Curves[i] = tls.CurveID(d[0])<<8 | tls.CurveID(d[1])
				d = d[2:]
			}
		case extensionSupportedPoints:
			if length < 1 {
				return
			}
			l := int(data[0])
			if length != l+1 {
				return
			}
			info.Points = make([]uint8, l)
			copy(info.Points, data[1:])
		}
		data = data[length:]
	}
	return
}
func newTLSListener(ln net.Listener, config *tls.Config) *tlsHelloListener {
	return &tlsHelloListener{
		Listener:   ln,
		config:     config,
		helloInfos: make(map[string]rawHelloInfo),
	}
}
type tlsHelloListener struct {
	net.Listener
	config       *tls.Config
	helloInfos   map[string]rawHelloInfo
	helloInfosMu sync.RWMutex
}