From f524fffe6caacfd4e56477d2ef3c403be16949ab Mon Sep 17 00:00:00 2001 From: Shinku <17696928+Shinku-Chen@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:25:20 +0800 Subject: [PATCH 1/5] SetTLSFingerprint can uTLSConnApply --- client.go | 7 ++++++- client_test.go | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index e9db4b8..172f4a4 100644 --- a/client.go +++ b/client.go @@ -1224,7 +1224,7 @@ func (conn *uTLSConn) ConnectionState() tls.ConnectionState { // (https://github.com/refraction-networking/utls) to perform the tls handshake, // which uses the specified clientHelloID to simulate the tls fingerprint. // Note this is valid for HTTP1 and HTTP2, not HTTP3. -func (c *Client) SetTLSFingerprint(clientHelloID utls.ClientHelloID) *Client { +func (c *Client) SetTLSFingerprint(clientHelloID utls.ClientHelloID, uTLSConnApply ...func(*uTLSConn) error) *Client { fn := func(ctx context.Context, addr string, plainConn net.Conn) (conn net.Conn, tlsState *tls.ConnectionState, err error) { colonPos := strings.LastIndex(addr, ":") if colonPos == -1 { @@ -1248,6 +1248,11 @@ func (c *Client) SetTLSFingerprint(clientHelloID utls.ClientHelloID) *Client { KeyLogWriter: tlsConfig.KeyLogWriter, } uconn := &uTLSConn{utls.UClient(plainConn, utlsConfig, clientHelloID)} + for _, fnApply := range uTLSConnApply { + if err = fnApply(uconn); err != nil { + return + } + } err = uconn.HandshakeContext(ctx) if err != nil { return diff --git a/client_test.go b/client_test.go index 8f7bc9b..cfd8483 100644 --- a/client_test.go +++ b/client_test.go @@ -19,6 +19,7 @@ import ( "github.com/imroc/req/v3/internal/header" "github.com/imroc/req/v3/internal/tests" + utls "github.com/refraction-networking/utls" "golang.org/x/net/publicsuffix" ) @@ -720,3 +721,21 @@ func TestCloneCookieJar(t *testing.T) { tests.AssertEqual(t, true, c2.cookiejarFactory == nil) tests.AssertEqual(t, true, c2.httpClient.Jar == nil) } +func TestUTLSConnApply(t *testing.T) { + c1 := C() + + c1.SetTLSFingerprint(utls.HelloCustom, func(conn *uTLSConn) error { + tt, _ := utls.UTLSIdToSpec(utls.HelloQQ_Auto) + //cycletls.StringToSpec("771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-5-10-11-13-16-18-21-23-27-35-43-45-51-17513-65281,29-23-24,0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:120.0) Gecko/20100101 Firefox/120.0", false) + err := conn.ApplyPreset(&tt) + return err + }) + + //c1.SetTLSFingerprintQQ() + bodyJson := &struct { + Ja3NText string `json:"ja3n_text"` + }{} + _ = c1.Get("https://tls.browserleaks.com/json").Do().Into(bodyJson) + //println(string(bodyJson.Ja3NText)) + tests.AssertEqual(t, true, bodyJson.Ja3NText == "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-5-10-11-13-16-18-21-23-27-35-43-45-51-17513-65281,29-23-24,0") +} From fc4d80439c6ae3e08242c5d0d42a6ebcd09cba61 Mon Sep 17 00:00:00 2001 From: Shinku <17696928+Shinku-Chen@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:25:59 +0800 Subject: [PATCH 2/5] SetTLSFingerprint can uTLSConnApply --- client_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client_test.go b/client_test.go index cfd8483..6cb44ec 100644 --- a/client_test.go +++ b/client_test.go @@ -726,6 +726,7 @@ func TestUTLSConnApply(t *testing.T) { c1.SetTLSFingerprint(utls.HelloCustom, func(conn *uTLSConn) error { tt, _ := utls.UTLSIdToSpec(utls.HelloQQ_Auto) + //"github.com/Danny-Dasilva/CycleTLS/cycletls" //cycletls.StringToSpec("771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-5-10-11-13-16-18-21-23-27-35-43-45-51-17513-65281,29-23-24,0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:120.0) Gecko/20100101 Firefox/120.0", false) err := conn.ApplyPreset(&tt) return err From de2a62b357ca3645e26caf783c9852ac5a69ba95 Mon Sep 17 00:00:00 2001 From: Shinku <17696928+Shinku-Chen@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:32:52 +0800 Subject: [PATCH 3/5] SetTLSFingerprint can uTLSConnApply --- client.go | 7 +++++++ client_test.go | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/client.go b/client.go index 172f4a4..571a139 100644 --- a/client.go +++ b/client.go @@ -1278,6 +1278,13 @@ func (c *Client) SetTLSFingerprint(clientHelloID utls.ClientHelloID, uTLSConnApp c.Transport.SetTLSHandshake(fn) return c } +func (c *Client) SetTLSFingerprintSpec(clientHelloID *utls.ClientHelloSpec) *Client { + c.SetTLSFingerprint(utls.HelloCustom, func(conn *uTLSConn) error { + err := conn.ApplyPreset(clientHelloID) + return err + }) + return c +} // SetTLSHandshake set the custom tls handshake function, only valid for HTTP1 and HTTP2, not HTTP3, // it specifies an optional dial function for tls handshake, it works even if a proxy is set, can be diff --git a/client_test.go b/client_test.go index 6cb44ec..0402c1b 100644 --- a/client_test.go +++ b/client_test.go @@ -740,3 +740,14 @@ func TestUTLSConnApply(t *testing.T) { //println(string(bodyJson.Ja3NText)) tests.AssertEqual(t, true, bodyJson.Ja3NText == "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-5-10-11-13-16-18-21-23-27-35-43-45-51-17513-65281,29-23-24,0") } +func TestSetTLSFingerprintSpec(t *testing.T) { + c1 := C() + tt, _ := utls.UTLSIdToSpec(utls.HelloQQ_Auto) + c1.SetTLSFingerprintSpec(&tt) + bodyJson := &struct { + Ja3NText string `json:"ja3n_text"` + }{} + _ = c1.Get("https://tls.browserleaks.com/json").Do().Into(bodyJson) + //println(string(bodyJson.Ja3NText)) + tests.AssertEqual(t, true, bodyJson.Ja3NText == "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-5-10-11-13-16-18-21-23-27-35-43-45-51-17513-65281,29-23-24,0") +} From ddf9504d05540048afac22dcf4e3b0c36ff8b2cf Mon Sep 17 00:00:00 2001 From: Shinku <17696928+Shinku-Chen@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:33:25 +0800 Subject: [PATCH 4/5] SetTLSFingerprint can uTLSConnApply --- client.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client.go b/client.go index 571a139..89a687a 100644 --- a/client.go +++ b/client.go @@ -1278,6 +1278,10 @@ func (c *Client) SetTLSFingerprint(clientHelloID utls.ClientHelloID, uTLSConnApp c.Transport.SetTLSHandshake(fn) return c } + +// SetTLSFingerprintSpec set the tls fingerprint for tls handshake, will use utls +// (https://github.com/refraction-networking/utls) to perform the tls handshake, +// which uses the specified clientHelloID to simulate the tls fingerprint. func (c *Client) SetTLSFingerprintSpec(clientHelloID *utls.ClientHelloSpec) *Client { c.SetTLSFingerprint(utls.HelloCustom, func(conn *uTLSConn) error { err := conn.ApplyPreset(clientHelloID) From d6dc3399d4933052f31789f0afa507567e927f33 Mon Sep 17 00:00:00 2001 From: Shinku <17696928+Shinku-Chen@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:40:21 +0800 Subject: [PATCH 5/5] SetTLSFingerprint can uTLSConnApply --- client.go | 12 ++++++++---- client_test.go | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/client.go b/client.go index 89a687a..21accd0 100644 --- a/client.go +++ b/client.go @@ -1224,7 +1224,11 @@ func (conn *uTLSConn) ConnectionState() tls.ConnectionState { // (https://github.com/refraction-networking/utls) to perform the tls handshake, // which uses the specified clientHelloID to simulate the tls fingerprint. // Note this is valid for HTTP1 and HTTP2, not HTTP3. -func (c *Client) SetTLSFingerprint(clientHelloID utls.ClientHelloID, uTLSConnApply ...func(*uTLSConn) error) *Client { +func (c *Client) SetTLSFingerprint(clientHelloID utls.ClientHelloID) *Client { + c.setTLSFingerprint(clientHelloID, nil) + return c +} +func (c *Client) setTLSFingerprint(clientHelloID utls.ClientHelloID, uTLSConnApply func(*uTLSConn) error) *Client { fn := func(ctx context.Context, addr string, plainConn net.Conn) (conn net.Conn, tlsState *tls.ConnectionState, err error) { colonPos := strings.LastIndex(addr, ":") if colonPos == -1 { @@ -1248,8 +1252,8 @@ func (c *Client) SetTLSFingerprint(clientHelloID utls.ClientHelloID, uTLSConnApp KeyLogWriter: tlsConfig.KeyLogWriter, } uconn := &uTLSConn{utls.UClient(plainConn, utlsConfig, clientHelloID)} - for _, fnApply := range uTLSConnApply { - if err = fnApply(uconn); err != nil { + if uTLSConnApply != nil { + if err = uTLSConnApply(uconn); err != nil { return } } @@ -1283,7 +1287,7 @@ func (c *Client) SetTLSFingerprint(clientHelloID utls.ClientHelloID, uTLSConnApp // (https://github.com/refraction-networking/utls) to perform the tls handshake, // which uses the specified clientHelloID to simulate the tls fingerprint. func (c *Client) SetTLSFingerprintSpec(clientHelloID *utls.ClientHelloSpec) *Client { - c.SetTLSFingerprint(utls.HelloCustom, func(conn *uTLSConn) error { + c.setTLSFingerprint(utls.HelloCustom, func(conn *uTLSConn) error { err := conn.ApplyPreset(clientHelloID) return err }) diff --git a/client_test.go b/client_test.go index 0402c1b..de3f000 100644 --- a/client_test.go +++ b/client_test.go @@ -724,7 +724,7 @@ func TestCloneCookieJar(t *testing.T) { func TestUTLSConnApply(t *testing.T) { c1 := C() - c1.SetTLSFingerprint(utls.HelloCustom, func(conn *uTLSConn) error { + c1.setTLSFingerprint(utls.HelloCustom, func(conn *uTLSConn) error { tt, _ := utls.UTLSIdToSpec(utls.HelloQQ_Auto) //"github.com/Danny-Dasilva/CycleTLS/cycletls" //cycletls.StringToSpec("771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-5-10-11-13-16-18-21-23-27-35-43-45-51-17513-65281,29-23-24,0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:120.0) Gecko/20100101 Firefox/120.0", false)