diff --git a/examples/helloworld/client/main.go b/examples/helloworld/client/main.go index 1a6a39811..1be1c779b 100644 --- a/examples/helloworld/client/main.go +++ b/examples/helloworld/client/main.go @@ -27,6 +27,7 @@ func callHTTP() { if err != nil { panic(err) } + defer conn.Close() client := pb.NewGreeterHTTPClient(conn) reply, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "kratos"}) if err != nil { @@ -55,6 +56,7 @@ func callGRPC() { if err != nil { panic(err) } + defer conn.Close() client := pb.NewGreeterClient(conn) reply, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "kratos"}) if err != nil { diff --git a/examples/helloworld/server/main.go b/examples/helloworld/server/main.go index a9d7dc1a7..f60cdfcb5 100644 --- a/examples/helloworld/server/main.go +++ b/examples/helloworld/server/main.go @@ -39,18 +39,18 @@ func (s *server) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*he func main() { s := &server{} - grpcSrv := grpc.NewServer( - grpc.Address(":9000"), - grpc.Middleware( - recovery.Recovery(), - ), - ) httpSrv := http.NewServer( http.Address(":8000"), http.Middleware( recovery.Recovery(), ), ) + grpcSrv := grpc.NewServer( + grpc.Address(":9000"), + grpc.Middleware( + recovery.Recovery(), + ), + ) helloworld.RegisterGreeterServer(grpcSrv, s) helloworld.RegisterGreeterHTTPServer(httpSrv, s) diff --git a/examples/metadata/client/main.go b/examples/metadata/client/main.go index 89018110f..643a2a489 100644 --- a/examples/metadata/client/main.go +++ b/examples/metadata/client/main.go @@ -27,6 +27,7 @@ func callHTTP() { if err != nil { panic(err) } + defer conn.Close() client := helloworld.NewGreeterHTTPClient(conn) ctx := context.Background() ctx = metadata.AppendToClientContext(ctx, "x-md-global-extra", "2233") @@ -48,6 +49,7 @@ func callGRPC() { if err != nil { log.Fatal(err) } + defer conn.Close() client := helloworld.NewGreeterClient(conn) ctx := context.Background() ctx = metadata.AppendToClientContext(ctx, "x-md-global-extra", "2233") diff --git a/examples/tls/README.md b/examples/tls/README.md new file mode 100644 index 000000000..be39d4fee --- /dev/null +++ b/examples/tls/README.md @@ -0,0 +1,10 @@ +# Run the example + +1、Compile and execute the server code: +```shell +$ go run server/main.go +``` +2、From a different terminal, compile and execute the client code: +```shell +$ go run client/main.go +``` \ No newline at end of file diff --git a/examples/tls/cert/server.crt b/examples/tls/cert/server.crt new file mode 100644 index 000000000..d4b1047d5 --- /dev/null +++ b/examples/tls/cert/server.crt @@ -0,0 +1,64 @@ + +-----BEGIN CERTIFICATE----- +MIIFZTCCA02gAwIBAgIBATANBgkqhkiG9w0BAQUFADB8MQswCQYDVQQGEwJDTjEL +MAkGA1UECAwCU0gxCzAJBgNVBAcMAlNIMQ8wDQYDVQQKDAZrcmF0b3MxCzAJBgNV +BAsMAklUMRMwEQYDVQQDDAprcmF0b3MuY29tMSAwHgYJKoZIhvcNAQkBFhFrcmF0 +b3NAa3JhdG9zLmNvbTAeFw0yMTA3MjcxNTM1MjJaFw0yMjA3MjcxNTM1MjJaME0x +CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJTSDELMAkGA1UEBwwCU0gxDzANBgNVBAoM +BmtyYXRvczETMBEGA1UEAwwKa3JhdG9zLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBALfEWAuPqUHTwTiOT8dtiCM3aEm1D7I6K/PY2mSDMMOI4f5z +TYi7LsKLLItMQR44cEDein/kl0U0QNYJRHqVCr/3IXA4ds0maiq+npY/S2KABDiI +Z38TZ1PZ2bcD3Jb0o1gw0GcSokOGzVtuNKSiASc3D711AepGerChD5UrbixrZdg2 +wAeWZj39Tl93+zCcldVrCHMSM7LmDluHJ5KZ8T6auuK6ypQPVhqLz8VseHhB/IRw +s9/o9OlJ3kv4wB/CWlFIvU6ZGVnshZAGOk3Brq25kw600RRDr2MpxNhFY1Xvrt7D +tAJ7fK3/3VaV+I2C4OzzmztK7T1WlL8XqZj6I3t1ZCZryMvDIUPzC4mgiRqEJiF7 +VzuVm1sjyTKkD4oX6ZjJYXJ/6pbvd8+AexuwVAsQJKqaF1iQC9jThg55RUTkGo8F +DFErW7XHKHe8vKXuRLG5k3xZBiHK7gsVyHzx0ouuSuZMFHg7L9ACeeqtqxqKNd+0 +fIo4N0vNb3GEj+YaTLoadhDSBEsynyQNTfrDf+oFRmQUg3q0W4VJHJFhkmTHRRcq +Qj6xEAJDHC1xr8yr3jif9BKWG2+zEbvpiTcRXKhycv2OUI11dK72aMnOycusJjRe +8pOqcYhSVQZnz31WWlkmX9TRiQtEeUUkFCYAnIArSKm5rNwOddLCzC8Z4js1AgMB +AAGjITAfMB0GA1UdEQQWMBSCDCoua3JhdG9zLmNvbYcEfwAAATANBgkqhkiG9w0B +AQUFAAOCAgEAjr3SXzNOcN8+JQuroS6hKHadrcp6djepd3r5YKSEjKBNxVAU0gj6 +QGl0zjSqhxSFwN4wCqXU/4JJVOyAJCV+t992j5wNdaGI+Tcu4whK2LtPi0O68ttq +Nn5H/8bmotW0IZ/YDcq1V8EVWiTZPECk4QLx26S2sjG4HOKNUAs8o+PoUmQE5bKJ +XBFWmjsOfPnI0WBGnuCvGUw5wP1ipLiuK+OhoTNKA6SXPopm5KMDv7gjYPlcmPyI +sJcpve75m9EXQxrDvJtvws8MIZnkWvWi7bW6uQ7274S7YjMZL09/sQTolQOtwl59 +pvEbkQNPzgdvQYAfrlJjSBtbq3OtHF+j+p2K+7R0TY2F1OW3LeV8vkcq7IOt38Er +FK5feNEL3t9GlrF8ASmJp/JvhWQiJo5tZJxWZ68CjKfLmVd406ehsK5XNlHJemnl +hNpuAegeV6WDglvMNavvQCJfK5mKokn73HujtQveZ8vwPKV4f6Wg3sHJ2yyP7ou2 +2UZ10Qbp6UvFYfYuMvLuq8kznapWqQ0dIJzPDs+CzVtjk1eINUF9UjsSvqX4oCvy ++ZvaM9k4kU/fJMRkLstc/Dx2G13T/moI/l5sw0qFtck12zs/SQ7xgcW3b0ruGX9S +11opfl5R86GpXXbz+vNL9fWP+8cIvoGZK8RAC872bcMPEoJPjPIwAbI= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFdDCCA1wCCQDHq+cGa349DzANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJD +TjELMAkGA1UECAwCU0gxCzAJBgNVBAcMAlNIMQ8wDQYDVQQKDAZrcmF0b3MxCzAJ +BgNVBAsMAklUMRMwEQYDVQQDDAprcmF0b3MuY29tMSAwHgYJKoZIhvcNAQkBFhFr +cmF0b3NAa3JhdG9zLmNvbTAeFw0yMTA3MjcxNTMwMzlaFw0zMTA3MjUxNTMwMzla +MHwxCzAJBgNVBAYTAkNOMQswCQYDVQQIDAJTSDELMAkGA1UEBwwCU0gxDzANBgNV +BAoMBmtyYXRvczELMAkGA1UECwwCSVQxEzARBgNVBAMMCmtyYXRvcy5jb20xIDAe +BgkqhkiG9w0BCQEWEWtyYXRvc0BrcmF0b3MuY29tMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEApy2GV9MiECuelNk3fz1Qwh6+wj8Ip11NG+LxEGK4/MLD +JRJtbgAg/7s3vzrm4WDKATDO27W6wewNFOvEnGWyh9wyjAtSgnAcJreq7F2DMbpO ++E2guIQHSCCzfa10s4BgwXKdBRPPvwTADIHXPtlbq4BItJqzt/AhLQbdDAp93mHX +NCzFdlIr4wflT2OW7EO24K2LgMZLWCzaESei9fL6AYm7jEvfaFYZksI3rjJNAj1q +wccMu1o6TvdWRA5fvBi6h15Z0ekR8C2LbM1A54zziZwd+YjcwdQHJJgWJFH7yNSt +Oe/AJZzP1nRk/5H0EvxBnF7du6vfeSjZJytp8cXMlbYg4NGGkSy782tBaUaDIb43 +iLqSjfHVZDLzbDGNy/u/mzfo4xS8lxZ92zE7z21d0WyUAJ75Z72v4kaNTr2tnMuE +NTTG1787e3NB0CaV6gjeP8XbMV8gwNTrJmTW3dS+DT6sKtTIISOvsUCZ6h5qFvKU +RWqJ7MiaSxg31DPg51caYDjiVLkkES8GRpPM/Njsg9WpFTQeqcecKbbOdw0ihvoV +fq0FgHpp+jbjm0KkmcNdSX7Ld5XeHp2rBPkA283IdXIAvjjthlyWJTmSP6kDDdEI +km4Did1Bg0wcXNnFlbHHavCfeRTQbVoIYVkWH7I323Vp8itvKobz1GirSOK0Hr0C +AwEAATANBgkqhkiG9w0BAQsFAAOCAgEAFBYIOOyABIbUUOjjjvx2FSDRBNLpee5O +45KmznuCGerhR7ad3rUNhaakA9HJzmLMUXtmyzy6+ej0HzdqZE7RRzdDVftBGaOf +thwEzUAiHfqeX0o039sTQvJSqUY2sBko+tyDRmeNtmd3TPE5VZZSWG+TUtrOofr7 +K28UquMthJrmtSC8IJQOvA78Nc/FCPNaGZrcS8ZvrgbrkCLPN4dIJvY9I38xaWQ+ +G0gNQazxPzdp89/UMzkczyJAKjYj4JyLCbrjzTZXjtu+rYJezxyS+3QSf0F0xVBr +/8HCeXX6xG16WZY54Z6AijqI3sjiRBSQ25rLJJ00sWa9k1oH0Poyiv4pQG3RHgIs +jJEQh7RQE5zAsBx2NHZ8FcsUGX2oOjSS+vtX//Bg37kN7oYx5HEtK9c+a0wWUxo8 +8cqIzqrQSWOd1CipxbE5CUUNKdGQzNCLTfLO68KitLdaSOCGoU/PaoAncbr6EjdB +kzqJcHooqq8asl6fu1DVnYqCpEEp30ldU3p4MdclcMV0XZUq6bXFq1ylmfZXfC0t +zSGnBUjxB5lohn5fs1S/DxyoUR8LbIFVWCljEf4jJtMRnCQV3bR3xeVTbGh3ti81 +21uhQKjIP/X2BRNAvRo1qUbrzEeHAJG3EbIG78rRFvSz6MkWVTZzeH1SgCr9Onw7 +djovWE7E6ic= +-----END CERTIFICATE----- diff --git a/examples/tls/cert/server.key b/examples/tls/cert/server.key new file mode 100644 index 000000000..cd1259397 --- /dev/null +++ b/examples/tls/cert/server.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/examples/tls/client/main.go b/examples/tls/client/main.go new file mode 100644 index 000000000..3c436d50c --- /dev/null +++ b/examples/tls/client/main.go @@ -0,0 +1,63 @@ +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + "io/ioutil" + "log" + + pb "github.com/go-kratos/kratos/examples/helloworld/helloworld" + "github.com/go-kratos/kratos/v2/transport/grpc" + "github.com/go-kratos/kratos/v2/transport/http" +) + +func main() { + b, err := ioutil.ReadFile("../cert/server.crt") + if err != nil { + panic(err) + } + cp := x509.NewCertPool() + if !cp.AppendCertsFromPEM(b) { + panic(err) + } + tlsConf := &tls.Config{ServerName: "www.kratos.com", RootCAs: cp} + callHTTP(tlsConf) + callGRPC(tlsConf) +} + +func callHTTP(tlsConf *tls.Config) { + conn, err := http.NewClient( + context.Background(), + http.WithEndpoint("https://127.0.0.1:8000"), + http.WithTLSConfig(tlsConf), + ) + if err != nil { + panic(err) + } + defer conn.Close() + client := pb.NewGreeterHTTPClient(conn) + reply, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "kratos"}) + if err != nil { + log.Fatal(err) + } + log.Printf("[http] SayHello %s\n", reply.Message) +} + +func callGRPC(tlsConf *tls.Config) { + conn, err := grpc.Dial( + context.Background(), + grpc.WithEndpoint("127.0.0.1:9000"), + grpc.WithTLSConfig(tlsConf), + ) + if err != nil { + panic(err) + } + defer conn.Close() + client := pb.NewGreeterClient(conn) + reply, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "kratos"}) + if err != nil { + log.Fatal(err) + } + log.Printf("[grpc] SayHello %+v\n", reply) +} diff --git a/examples/tls/server/main.go b/examples/tls/server/main.go new file mode 100644 index 000000000..e954938b1 --- /dev/null +++ b/examples/tls/server/main.go @@ -0,0 +1,63 @@ +package main + +import ( + "context" + "crypto/tls" + "fmt" + "log" + + "github.com/go-kratos/kratos/examples/helloworld/helloworld" + "github.com/go-kratos/kratos/v2" + "github.com/go-kratos/kratos/v2/transport/grpc" + "github.com/go-kratos/kratos/v2/transport/http" +) + +// go build -ldflags "-X main.Version=x.y.z" +var ( + // Name is the name of the compiled software. + Name = "helloworld" + // Version is the version of the compiled software. + Version = "v1.0.0" +) + +// server is used to implement helloworld.GreeterServer. +type server struct { + helloworld.UnimplementedGreeterServer +} + +// SayHello implements helloworld.GreeterServer +func (s *server) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) { + return &helloworld.HelloReply{Message: fmt.Sprintf("Hello %+v", in.Name)}, nil +} + +func main() { + cert, err := tls.LoadX509KeyPair("../cert/server.crt", "../cert/server.key") + if err != nil { + panic(err) + } + tlsConf := &tls.Config{Certificates: []tls.Certificate{cert}} + + s := &server{} + httpSrv := http.NewServer( + http.Address(":8000"), + http.TLSConfig(tlsConf), + ) + grpcSrv := grpc.NewServer( + grpc.Address(":9000"), + grpc.TLSConfig(tlsConf), + ) + helloworld.RegisterGreeterServer(grpcSrv, s) + helloworld.RegisterGreeterHTTPServer(httpSrv, s) + + app := kratos.New( + kratos.Name(Name), + kratos.Server( + httpSrv, + grpcSrv, + ), + ) + + if err := app.Run(); err != nil { + log.Fatal(err) + } +} diff --git a/transport/grpc/client.go b/transport/grpc/client.go index ab307419a..d60e46684 100644 --- a/transport/grpc/client.go +++ b/transport/grpc/client.go @@ -2,6 +2,7 @@ package grpc import ( "context" + "crypto/tls" "time" "github.com/go-kratos/kratos/v2/middleware" @@ -14,6 +15,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/balancer/roundrobin" + "google.golang.org/grpc/credentials" grpcmd "google.golang.org/grpc/metadata" ) @@ -48,6 +50,13 @@ func WithDiscovery(d registry.Discovery) ClientOption { } } +// WithTLSConfig with TLS config. +func WithTLSConfig(c *tls.Config) ClientOption { + return func(o *clientOptions) { + o.tlsConf = c + } +} + // WithUnaryInterceptor returns a DialOption that specifies the interceptor for unary RPCs. func WithUnaryInterceptor(in ...grpc.UnaryClientInterceptor) ClientOption { return func(o *clientOptions) { @@ -65,6 +74,7 @@ func WithOptions(opts ...grpc.DialOption) ClientOption { // clientOptions is gRPC Client type clientOptions struct { endpoint string + tlsConf *tls.Config timeout time.Duration discovery registry.Discovery middleware []middleware.Middleware @@ -106,6 +116,9 @@ func dial(ctx context.Context, insecure bool, opts ...ClientOption) (*grpc.Clien if insecure { grpcOpts = append(grpcOpts, grpc.WithInsecure()) } + if options.tlsConf != nil { + grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(credentials.NewTLS(options.tlsConf))) + } if len(options.grpcOpts) > 0 { grpcOpts = append(grpcOpts, options.grpcOpts...) } diff --git a/transport/grpc/server.go b/transport/grpc/server.go index 6d39a2813..ac893b9e1 100644 --- a/transport/grpc/server.go +++ b/transport/grpc/server.go @@ -2,6 +2,7 @@ package grpc import ( "context" + "crypto/tls" "net" "net/url" "sync" @@ -15,6 +16,7 @@ import ( "github.com/go-kratos/kratos/v2/transport" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/health" "google.golang.org/grpc/health/grpc_health_v1" grpcmd "google.golang.org/grpc/metadata" @@ -62,6 +64,13 @@ func Middleware(m ...middleware.Middleware) ServerOption { } } +// TLSConfig with TLS config. +func TLSConfig(c *tls.Config) ServerOption { + return func(s *Server) { + s.tlsConf = c + } +} + // UnaryInterceptor returns a ServerOption that sets the UnaryServerInterceptor for the server. func UnaryInterceptor(in ...grpc.UnaryServerInterceptor) ServerOption { return func(s *Server) { @@ -80,6 +89,7 @@ func Options(opts ...grpc.ServerOption) ServerOption { type Server struct { *grpc.Server ctx context.Context + tlsConf *tls.Config lis net.Listener once sync.Once err error @@ -116,6 +126,9 @@ func NewServer(opts ...ServerOption) *Server { var grpcOpts = []grpc.ServerOption{ grpc.ChainUnaryInterceptor(ints...), } + if srv.tlsConf != nil { + grpcOpts = append(grpcOpts, grpc.Creds(credentials.NewTLS(srv.tlsConf))) + } if len(srv.grpcOpts) > 0 { grpcOpts = append(grpcOpts, srv.grpcOpts...) } diff --git a/transport/http/client.go b/transport/http/client.go index 0aca5850b..0efb92857 100644 --- a/transport/http/client.go +++ b/transport/http/client.go @@ -3,6 +3,7 @@ package http import ( "bytes" "context" + "crypto/tls" "fmt" "io" "io/ioutil" @@ -35,6 +36,7 @@ type ClientOption func(*clientOptions) // Client is an HTTP transport client. type clientOptions struct { ctx context.Context + tlsConf *tls.Config timeout time.Duration endpoint string userAgent string @@ -127,6 +129,13 @@ func WithBlock() ClientOption { } } +// WithTLSConfig with tls config. +func WithTLSConfig(c *tls.Config) ClientOption { + return func(o *clientOptions) { + o.tlsConf = c + } +} + // Client is an HTTP client. type Client struct { opts clientOptions @@ -149,7 +158,16 @@ func NewClient(ctx context.Context, opts ...ClientOption) (*Client, error) { for _, o := range opts { o(&options) } - target, err := parseTarget(options.endpoint) + if options.tlsConf != nil { + if tr, ok := options.transport.(*http.Transport); ok { + tr.TLSClientConfig = options.tlsConf + } + } + var isSecure bool + if options.tlsConf != nil { + isSecure = true + } + target, err := parseTarget(options.endpoint, isSecure) if err != nil { return nil, err } @@ -285,7 +303,10 @@ func (client *Client) do(ctx context.Context, req *http.Request, c callInfo) (*h // Close tears down the Transport and all underlying connections. func (client *Client) Close() error { - return client.r.Close() + if client.r != nil { + return client.r.Close() + } + return nil } // DefaultRequestEncoder is an HTTP request encoder. diff --git a/transport/http/resolver.go b/transport/http/resolver.go index 56eb496be..9f24acc4c 100644 --- a/transport/http/resolver.go +++ b/transport/http/resolver.go @@ -4,6 +4,8 @@ import ( "context" "errors" "net/url" + "strconv" + "strings" "sync" "time" @@ -23,12 +25,17 @@ type Target struct { Endpoint string } -func parseTarget(endpoint string) (*Target, error) { +func parseTarget(endpoint string, isSecure bool) (*Target, error) { + if !strings.Contains(endpoint, "://") { + if isSecure { + endpoint = "https://" + endpoint + } else { + endpoint = "http://" + endpoint + } + } u, err := url.Parse(endpoint) if err != nil { - if u, err = url.Parse("http://" + endpoint); err != nil { - return nil, err - } + return nil, err } target := &Target{Scheme: u.Scheme, Authority: u.Host} if len(u.Path) > 1 { @@ -139,7 +146,12 @@ func parseEndpoint(endpoints []string) (string, string, error) { return "", "", err } if u.Scheme == "http" { - return u.Scheme, u.Host, nil + isSecure, _ := strconv.ParseBool(u.Query().Get("isSecure")) + scheme := "http" + if isSecure { + scheme = "https" + } + return scheme, u.Host, nil } } return "", "", nil diff --git a/transport/http/resolver_test.go b/transport/http/resolver_test.go new file mode 100644 index 000000000..b2af82bab --- /dev/null +++ b/transport/http/resolver_test.go @@ -0,0 +1,29 @@ +package http + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseTarget(t *testing.T) { + target, err := parseTarget("localhost:8000", false) + assert.Nil(t, err) + assert.Equal(t, &Target{Scheme: "http", Authority: "localhost:8000"}, target) + + target, err = parseTarget("discovery:///demo", false) + assert.Nil(t, err) + assert.Equal(t, &Target{Scheme: "discovery", Authority: "", Endpoint: "demo"}, target) + + target, err = parseTarget("127.0.0.1:8000", false) + assert.Nil(t, err) + assert.Equal(t, &Target{Scheme: "http", Authority: "127.0.0.1:8000"}, target) + + target, err = parseTarget("https://127.0.0.1:8000", false) + assert.Nil(t, err) + assert.Equal(t, &Target{Scheme: "https", Authority: "127.0.0.1:8000"}, target) + + target, err = parseTarget("127.0.0.1:8000", true) + assert.Nil(t, err) + assert.Equal(t, &Target{Scheme: "https", Authority: "127.0.0.1:8000"}, target) +} diff --git a/transport/http/server.go b/transport/http/server.go index 8a4f8a20b..96d57e3c4 100644 --- a/transport/http/server.go +++ b/transport/http/server.go @@ -2,6 +2,7 @@ package http import ( "context" + "crypto/tls" "errors" "net" "net/http" @@ -93,10 +94,18 @@ func Endpoint(endpoint *url.URL) ServerOption { } } +// TLSConfig with TLS config. +func TLSConfig(c *tls.Config) ServerOption { + return func(o *Server) { + o.tlsConf = c + } +} + // Server is an HTTP server wrapper. type Server struct { *http.Server lis net.Listener + tlsConf *tls.Config once sync.Once endpoint *url.URL err error @@ -127,7 +136,8 @@ func NewServer(opts ...ServerOption) *Server { o(srv) } srv.Server = &http.Server{ - Handler: FilterChain(srv.filters...)(srv), + Handler: FilterChain(srv.filters...)(srv), + TLSConfig: srv.tlsConf, } srv.router = mux.NewRouter() srv.router.Use(srv.filter()) @@ -207,7 +217,11 @@ func (s *Server) Endpoint() (*url.URL, error) { return } s.lis = lis - s.endpoint = &url.URL{Scheme: "http", Host: addr} + var query string + if s.tlsConf != nil { + query = "isSecure=true" + } + s.endpoint = &url.URL{Scheme: "http", Host: addr, RawQuery: query} }) if s.err != nil { return nil, s.err @@ -224,7 +238,13 @@ func (s *Server) Start(ctx context.Context) error { return ctx } s.log.Infof("[HTTP] server listening on: %s", s.lis.Addr().String()) - if err := s.Serve(s.lis); !errors.Is(err, http.ErrServerClosed) { + var err error + if s.tlsConf != nil { + err = s.ServeTLS(s.lis, "", "") + } else { + err = s.Serve(s.lis) + } + if !errors.Is(err, http.ErrServerClosed) { return err } return nil