Skip to content

feat(gm): support GM #164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0
github.com/tjfoc/gmsm v1.4.2-0.20220114090716-36b992c51540
golang.org/x/net v0.17.0
golang.org/x/sync v0.4.0
golang.org/x/sys v0.14.0
Expand Down Expand Up @@ -54,6 +55,7 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/afero v1.8.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/oauth2 v0.11.0 // indirect
golang.org/x/term v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tjfoc/gmsm v1.4.2-0.20220114090716-36b992c51540 h1:Q7nxhP4rDahaXbLofX2fRX1dcEoQRvlJA0Hd2hGgh9k=
github.com/tjfoc/gmsm v1.4.2-0.20220114090716-36b992c51540/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand All @@ -340,8 +342,11 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -405,6 +410,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
Expand Down
43 changes: 43 additions & 0 deletions pkg/common/transport/gm_round_tripper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package transport

import (
"context"
"crypto/tls"
"net"
"net/http"
"time"

"github.com/tjfoc/gmsm/gmtls"
)

func NewGMRoundTripper(cfg *gmtls.Config) WrapperFunc {
return func(inner http.RoundTripper) http.RoundTripper {
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: cfg.InsecureSkipVerify,
},
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dialer := &net.Dialer{}
conn, err := gmtls.DialWithDialer(dialer, network, addr, cfg)
if err != nil {
return nil, err
}
return conn, nil
},
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dialer := &net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 60 * time.Second,
}
conn, err := gmtls.DialWithDialer(dialer, network, addr, cfg)
if err != nil {
return nil, err
}
return conn, nil
},
TLSHandshakeTimeout: 15 * time.Second,
IdleConnTimeout: 30 * time.Second,
}
}
}
159 changes: 159 additions & 0 deletions pkg/common/transport/gm_round_tripper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package transport

import (
"net/http"
"testing"

"github.com/tjfoc/gmsm/gmtls"
"github.com/tjfoc/gmsm/x509"
)

func TestNewGMRoundTripper(t *testing.T) {
tests := []struct {
name string
description string
cfg *gmtls.Config
request *http.Request
expected bool
}{
{
name: "test1",
description: "support both GM and no-GM encryption algorithm",
cfg: &gmtls.Config{
InsecureSkipVerify: true,
},
request: httpNewRequest("GET", "https://sm2test.ovssl.cn", nil, t),
expected: false,
},
{
name: "test2",
description: "support both GM and no-GM encryption algorithm",
cfg: &gmtls.Config{
GMSupport: &gmtls.GMSupport{WorkMode: gmtls.ModeAutoSwitch},
InsecureSkipVerify: true,
},
request: httpNewRequest("GET", "https://sm2test.ovssl.cn", nil, t),
expected: false,
},
{
name: "test3",
description: "support both GM and no-GM encryption algorithm",
cfg: &gmtls.Config{
GMSupport: &gmtls.GMSupport{WorkMode: gmtls.ModeGMSSLOnly},
InsecureSkipVerify: true,
},
request: httpNewRequest("GET", "https://sm2test.ovssl.cn", nil, t),
expected: false,
},
{
name: "test4",
description: "support both GM and no-GM encryption algorithm",
cfg: &gmtls.Config{
GMSupport: &gmtls.GMSupport{WorkMode: gmtls.ModeAutoSwitch},
InsecureSkipVerify: true,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
for _, v := range rawCerts {
_, err := x509.ParseCertificate(v)
if err != nil {
return err
}
}
return nil
},
},
request: httpNewRequest("GET", "https://sm2test.ovssl.cn", nil, t),
expected: false,
},
{
name: "test5",
description: "support both GM and no-GM encryption algorithm",
cfg: &gmtls.Config{
GMSupport: &gmtls.GMSupport{WorkMode: gmtls.ModeGMSSLOnly},
InsecureSkipVerify: true,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
for _, v := range rawCerts {
_, err := x509.ParseCertificate(v)
if err != nil {
return err
}
}
return nil
},
},
request: httpNewRequest("GET", "https://sm2test.ovssl.cn", nil, t),
expected: false,
},

{
name: "test6",
description: "don not support GM",
cfg: &gmtls.Config{
InsecureSkipVerify: true,
},
request: httpNewRequest("GET", "https://baidu.com", nil, t),
expected: true,
},
{
name: "test7",
description: "don not support GM",
cfg: &gmtls.Config{
GMSupport: &gmtls.GMSupport{WorkMode: gmtls.ModeAutoSwitch},
InsecureSkipVerify: true,
},
request: httpNewRequest("GET", "https://baidu.com", nil, t),
expected: true,
},
{
name: "test8",
description: "don not support GM",
cfg: &gmtls.Config{
GMSupport: &gmtls.GMSupport{WorkMode: gmtls.ModeGMSSLOnly},
InsecureSkipVerify: true,
},
request: httpNewRequest("GET", "https://baidu.com", nil, t),
expected: true,
},
{
name: "test9",
description: "don not support GM",
cfg: &gmtls.Config{
GMSupport: &gmtls.GMSupport{WorkMode: gmtls.ModeAutoSwitch},
InsecureSkipVerify: true,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
for _, v := range rawCerts {
_, err := x509.ParseCertificate(v)
if err != nil {
return err
}
}
return nil
},
},
request: httpNewRequest("GET", "https://baidu.com", nil, t),
expected: true,
},
}

for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
wrapper := NewGMRoundTripper(testCase.cfg)
wrappedTransport := wrapper(http.DefaultTransport)
client := &http.Client{
Transport: wrappedTransport,
}

resp, err := client.Do(testCase.request)

if testCase.expected && err == nil {
t.Errorf("expected error but got none")
}
if !testCase.expected && err != nil {
t.Errorf("expected: %v, but got err: %v", testCase.expected, err)
}

if resp != nil && resp.Body != nil {
resp.Body.Close()
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package utils
package transport

import (
"bytes"
Expand All @@ -37,6 +37,12 @@ var (
serverGRPCEndpointCallCounter uint64
)

func NewLogRoundTripper() WrapperFunc {
return func(rt http.RoundTripper) http.RoundTripper {
return &LogRoundTripper{Rt: rt}
}
}

// LogRoundTripper satisfies the http.RoundTripper interface and is used to
// customize the default http client RoundTripper to allow for logging.
type LogRoundTripper struct {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package utils
package transport

import (
"io"
Expand All @@ -20,9 +20,9 @@ func TestRoundTrip(t *testing.T) {
response: &http.Response{
Status: "200 OK",
StatusCode: http.StatusOK,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Proto: "HTTP/2.0",
ProtoMajor: 2,
ProtoMinor: 0,
},
expected: true,
description: "request and response are real entities",
Expand Down Expand Up @@ -67,11 +67,11 @@ func TestRoundTrip(t *testing.T) {
name: "test6",
request: httpNewRequest("POST", "https://hub.docker.com/", nil, t),
response: &http.Response{
Status: "404 Not Found",
StatusCode: http.StatusNotFound,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Status: "405 Method Not Allowed",
StatusCode: http.StatusMethodNotAllowed,
Proto: "HTTP/2.0",
ProtoMajor: 2,
ProtoMinor: 0,
},
expected: true,
description: "the method of request is POST and url is real entity",
Expand All @@ -80,11 +80,11 @@ func TestRoundTrip(t *testing.T) {
name: "test7",
request: httpNewRequest("PUT", "https://hub.docker.com/", nil, t),
response: &http.Response{
Status: "404 Not Found",
StatusCode: http.StatusNotFound,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Status: "405 Method Not Allowed",
StatusCode: http.StatusMethodNotAllowed,
Proto: "HTTP/2.0",
ProtoMajor: 2,
ProtoMinor: 0,
},
expected: true,
description: "the method of request is PUT and url is real entity",
Expand All @@ -93,11 +93,11 @@ func TestRoundTrip(t *testing.T) {
name: "test8",
request: httpNewRequest("DELETE", "https://hub.docker.com/", nil, t),
response: &http.Response{
Status: "404 Not Found",
StatusCode: http.StatusNotFound,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Status: "405 Method Not Allowed",
StatusCode: http.StatusMethodNotAllowed,
Proto: "HTTP/2.0",
ProtoMajor: 2,
ProtoMinor: 0,
},
expected: true,
description: "the method of request is DELETE and url is real entity",
Expand Down Expand Up @@ -130,9 +130,9 @@ func TestRoundTrip(t *testing.T) {
response: &http.Response{
Status: "200 OK",
StatusCode: http.StatusOK,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Proto: "HTTP/2.0",
ProtoMajor: 2,
ProtoMinor: 0,
},
expected: true,
description: "the ProtoMajor of customized response is 1",
Expand All @@ -143,9 +143,9 @@ func TestRoundTrip(t *testing.T) {
response: &http.Response{
Status: "200 OK",
StatusCode: http.StatusOK,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Proto: "HTTP/2.0",
ProtoMajor: 2,
ProtoMinor: 0,
},
expected: true,
description: "the ProtoMinor of customized response is 1",
Expand Down
27 changes: 27 additions & 0 deletions pkg/common/transport/transport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package transport

import (
"net/http"
)

type WrapperFunc func(rt http.RoundTripper) http.RoundTripper

func Wrappers(fns ...WrapperFunc) WrapperFunc {
if len(fns) == 0 {
return nil
}
// optimize the common case of wrapping a possibly nil tr wrapper
// with an additional wrapper
if len(fns) == 2 && fns[0] == nil {
return fns[1]
}
return func(rt http.RoundTripper) http.RoundTripper {
base := rt
for _, fn := range fns {
if fn != nil {
base = fn(base)
}
}
return base
}
}
Loading