mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-04 15:49:49 +00:00
101 lines
2.3 KiB
Go
101 lines
2.3 KiB
Go
package httpclienttest
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
type Profile string
|
|
|
|
const (
|
|
ProfileNative Profile = "native"
|
|
ProfileChrome Profile = "chrome"
|
|
ProfileFirefox Profile = "firefox"
|
|
ProfileSafari Profile = "safari"
|
|
ProfileAnyBrowser Profile = "browser"
|
|
)
|
|
|
|
type FingerprintServerOptions struct {
|
|
RequiredProfile Profile
|
|
Handler http.Handler
|
|
}
|
|
|
|
type FingerprintServer struct {
|
|
*httptest.Server
|
|
|
|
mu sync.Mutex
|
|
profiles []Profile
|
|
}
|
|
|
|
func NewFingerprintServer(t testing.TB, options FingerprintServerOptions) *FingerprintServer {
|
|
t.Helper()
|
|
|
|
handler := options.Handler
|
|
if handler == nil {
|
|
handler = http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
}
|
|
server := &FingerprintServer{
|
|
Server: httptest.NewUnstartedServer(handler),
|
|
}
|
|
server.EnableHTTP2 = true
|
|
server.Config.ErrorLog = log.New(io.Discard, "", 0)
|
|
server.TLS = &tls.Config{
|
|
GetConfigForClient: func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
|
|
profile := classifyProfile(hello.CipherSuites)
|
|
server.mu.Lock()
|
|
server.profiles = append(server.profiles, profile)
|
|
server.mu.Unlock()
|
|
if profileMatches(profile, options.RequiredProfile) {
|
|
return nil, nil
|
|
}
|
|
return nil, errors.New("required TLS fingerprint was not presented")
|
|
},
|
|
}
|
|
server.StartTLS()
|
|
t.Cleanup(server.Close)
|
|
return server
|
|
}
|
|
|
|
func (s *FingerprintServer) Profiles() []Profile {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
return append([]Profile(nil), s.profiles...)
|
|
}
|
|
|
|
func profileMatches(actual, required Profile) bool {
|
|
if required == ProfileAnyBrowser {
|
|
return actual != ProfileNative
|
|
}
|
|
return actual == required
|
|
}
|
|
|
|
func classifyProfile(cipherSuites []uint16) Profile {
|
|
if len(cipherSuites) >= 5 && isGREASE(cipherSuites[0]) {
|
|
switch cipherSuites[4] {
|
|
case tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
|
|
return ProfileChrome
|
|
case tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
|
|
return ProfileSafari
|
|
}
|
|
}
|
|
if len(cipherSuites) >= 3 &&
|
|
cipherSuites[0] == tls.TLS_AES_128_GCM_SHA256 &&
|
|
cipherSuites[1] == tls.TLS_CHACHA20_POLY1305_SHA256 &&
|
|
cipherSuites[2] == tls.TLS_AES_256_GCM_SHA384 {
|
|
return ProfileFirefox
|
|
}
|
|
return ProfileNative
|
|
}
|
|
|
|
func isGREASE(value uint16) bool {
|
|
return value&0x0f0f == 0x0a0a && byte(value) == byte(value>>8)
|
|
}
|