feat: web service support TLS

This commit is contained in:
zhuyasen
2025-11-15 17:32:50 +08:00
parent 42f5a14dff
commit 64e4ffbbe0
14 changed files with 189 additions and 47 deletions

View File

@@ -18,6 +18,7 @@ func CreateServices() []app.IServer {
// case 1, create a http service without registry
httpServer := server.NewHTTPServer_pbExample(httpAddr,
server.WithHTTPIsProd(cfg.App.Env == "prod"),
server.WithHTTPTLS(cfg.HTTP.TLS),
)
// case 2, Create a http service and register it with consul or etcd or nacos
@@ -25,6 +26,7 @@ func CreateServices() []app.IServer {
//httpServer := server.NewHTTPServer_pbExample(httpAddr,
// server.WithHTTPRegistry(httpRegistry, httpInstance),
// server.WithHTTPIsProd(cfg.App.Env == "prod"),
// server.WithHTTPTLS(cfg.HTTP.TLS),
//)
servers = append(servers, httpServer)

View File

@@ -19,6 +19,7 @@ func CreateServices() []app.IServer {
// case 1, create http and grpc services without registry
httpServer := server.NewHTTPServer(httpAddr,
server.WithHTTPIsProd(cfg.App.Env == "prod"),
server.WithHTTPTLS(cfg.HTTP.TLS),
)
grpcServer := server.NewGRPCServer(grpcAddr)
@@ -27,6 +28,7 @@ func CreateServices() []app.IServer {
//httpServer := server.NewHTTPServer(httpAddr,
// server.WithHTTPRegistry(httpRegistry, httpInstance),
// server.WithHTTPIsProd(cfg.App.Env == "prod"),
// server.WithHTTPTLS(cfg.HTTP.TLS),
//)
//grpcRegistry, grpcInstance := registerService("grpc", cfg.App.Host, cfg.Grpc.Port)
//grpcServer := server.NewGRPCServer(grpcAddr,

View File

@@ -18,6 +18,7 @@ func CreateServices() []app.IServer {
httpAddr := ":" + strconv.Itoa(cfg.HTTP.Port)
httpServer := server.NewHTTPServer(httpAddr,
server.WithHTTPIsProd(cfg.App.Env == "prod"),
server.WithHTTPTLS(cfg.HTTP.TLS),
)
servers = append(servers, httpServer)

View File

@@ -18,6 +18,7 @@ func CreateServices() []app.IServer {
httpAddr := ":" + strconv.Itoa(cfg.HTTP.Port)
httpServer := server.NewHTTPServer_pbExample(httpAddr,
server.WithHTTPIsProd(cfg.App.Env == "prod"),
server.WithHTTPTLS(cfg.HTTP.TLS),
)
servers = append(servers, httpServer)

View File

@@ -26,6 +26,7 @@ func CreateServices() []app.IServer {
httpServer := server.NewHTTPServer(httpAddr,
server.WithHTTPRegistry(httpRegistry, httpInstance),
server.WithHTTPIsProd(cfg.App.Env == "prod"),
server.WithHTTPTLS(cfg.HTTP.TLS),
)
servers = append(servers, httpServer)

View File

@@ -212,10 +212,6 @@ func (g *rpcGwPbGenerator) addFields(r replacer.Replacer) []replacer.Field {
Old: appConfigFileMark2,
New: getDBConfigCode(""), // no db config
},
{ // replace the configuration of the *.yml file
Old: appConfigFileMark,
New: "",
},
//{ // replace the contents of the model/init.go file
// Old: modelInitDBFileMark,
// New: getInitDBCode(DBDriverMysql), // default is mysql

View File

@@ -370,7 +370,17 @@ func NewCenter(configFile string) (*Center, error) {
httpServerConfigCode = `# http server settings
http:
port: 8080 # listen port
timeout: 0 # request timeout, unit(second), if 0 means not set, if greater than 0 means set timeout, if enableHTTPProfile is true, it needs to set 0 or greater than 60s`
timeout: 0 # request timeout, unit(second), if 0 means not set, if greater than 0 means set timeout, if enableHTTPProfile is true, it needs to set 0 or greater than 60s
tls:
# TLS mode options:
# self-signed - Use localhost self-signed certificate
# encrypt - Use Let's Encrypt (requires domain & email)
# external - Use external certificates (requires certFile & keyFile)
enableMode: ""
domain: "" # Required if enableMode = encrypt
email: "" # Required if enableMode = encrypt
certFile: "" # Required if enableMode = external, absolute path of cert file
keyFile: "" # Required if enableMode = external, absolute path of key file`
rpcServerConfigCode = `# grpc server settings
grpc:
@@ -414,6 +424,16 @@ grpcClient:
http:
port: 8080 # listen port
timeout: 0 # request timeout, unit(second), if 0 means not set, if greater than 0 means set timeout, if enableHTTPProfile is true, it needs to set 0 or greater than 60s
tls:
# TLS mode options:
# self-signed - Use localhost self-signed certificate
# encrypt - Use Let's Encrypt (requires domain & email)
# external - Use external certificates (requires certFile & keyFile)
enableMode: ""
domain: "" # Required if enableMode = encrypt
email: "" # Required if enableMode = encrypt
certFile: "" # Required if enableMode = external, absolute path of cert file
keyFile: "" # Required if enableMode = external, absolute path of key file
# grpc client-side settings, support for setting up multiple grpc clients.
@@ -441,7 +461,17 @@ grpcClient:
grpcAndHTTPServerConfigCode = `# http server settings
http:
port: 8080 # listen port
timeout: 0 # request timeout, unit(second), if 0 means not set, if greater than 0 means set timeout, if enableHTTPProfile is true, it needs to set 0 or greater than 60s
timeout: 0 # request timeout, unit(second), if 0 means not set, if greater than 0 means set timeout, if enableHTTPProfile is true, it needs to set 0 or greater than 60s
tls:
# TLS mode options:
# self-signed - Use localhost self-signed certificate
# encrypt - Use Let's Encrypt (requires domain & email)
# external - Use external certificates (requires certFile & keyFile)
enableMode: ""
domain: "" # Required if enableMode = encrypt
email: "" # Required if enableMode = encrypt
certFile: "" # Required if enableMode = external, absolute path of cert file
keyFile: "" # Required if enableMode = external, absolute path of key file
# grpc server settings

View File

@@ -23,7 +23,16 @@ app:
http:
port: 8080 # listen port
timeout: 0 # request timeout, unit(second), if 0 means not set, if greater than 0 means set timeout, if enableHTTPProfile is true, it needs to set 0 or greater than 60s
tls:
# TLS mode options:
# self-signed - Use localhost self-signed certificate
# encrypt - Use Let's Encrypt (requires domain & email)
# external - Use external certificates (requires certFile & keyFile)
enableMode: ""
domain: "" # Required if enableMode = encrypt
email: "" # Required if enableMode = encrypt
certFile: "" # Required if enableMode = external, absolute path of cert file
keyFile: "" # Required if enableMode = external, absolute path of key file
# grpc server settings
grpc:

View File

@@ -76,6 +76,14 @@ type ServerSecure struct {
Type string `yaml:"type" json:"type"`
}
type TLS struct {
CertFile string `yaml:"certFile" json:"certFile"`
Domain string `yaml:"domain" json:"domain"`
Email string `yaml:"email" json:"email"`
EnableMode string `yaml:"enableMode" json:"enableMode"`
KeyFile string `yaml:"keyFile" json:"keyFile"`
}
type App struct {
CacheType string `yaml:"cacheType" json:"cacheType"`
EnableCircuitBreaker bool `yaml:"enableCircuitBreaker" json:"enableCircuitBreaker"`
@@ -169,4 +177,5 @@ type NacosRd struct {
type HTTP struct {
Port int `yaml:"port" json:"port"`
Timeout int `yaml:"timeout" json:"timeout"`
TLS TLS `yaml:"tls" json:"tls"`
}

View File

@@ -9,8 +9,10 @@ import (
"github.com/gin-gonic/gin"
"github.com/go-dev-frame/sponge/pkg/app"
"github.com/go-dev-frame/sponge/pkg/httpsrv"
"github.com/go-dev-frame/sponge/pkg/servicerd/registry"
"github.com/go-dev-frame/sponge/internal/config"
"github.com/go-dev-frame/sponge/internal/routers"
)
@@ -18,7 +20,7 @@ var _ app.IServer = (*httpServer)(nil)
type httpServer struct {
addr string
server *http.Server
server *httpsrv.Server
instance *registry.ServiceInstance
iRegistry registry.Registry
@@ -33,8 +35,8 @@ func (s *httpServer) Start() error {
}
}
if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return fmt.Errorf("listen server error: %v", err)
if err := s.server.Run(); err != nil {
return fmt.Errorf("run %s service error: %v", s.server.Scheme(), err)
}
return nil
}
@@ -56,7 +58,29 @@ func (s *httpServer) Stop() error {
// String comment
func (s *httpServer) String() string {
return "http service address " + s.addr
return s.server.Scheme() + " service address is " + s.addr
}
func newServer(server *http.Server, tls config.TLS) *httpsrv.Server {
var c *httpsrv.Server
switch httpsrv.Mode(tls.EnableMode) {
case httpsrv.ModeTLSSelfSigned:
c = httpsrv.New(server, httpsrv.NewTLSSelfSignedConfig())
case httpsrv.ModeTLSEncrypt:
c = httpsrv.New(server,
httpsrv.NewTLSEAutoEncryptConfig(
tls.Domain,
tls.Email,
// enable http redirect to https, port 80 to 443, default is false
//httpsrv.WithTLSEncryptEnableRedirect(),
),
)
case httpsrv.ModeTLSExternal:
c = httpsrv.New(server, httpsrv.NewTLSExternalConfig(tls.CertFile, tls.KeyFile))
default:
c = httpsrv.New(server) // default is http, no tls
}
return c
}
// NewHTTPServer creates a new http server
@@ -82,7 +106,7 @@ func NewHTTPServer(addr string, opts ...HTTPOption) app.IServer {
return &httpServer{
addr: addr,
server: server,
server: newServer(server, o.tls),
iRegistry: o.iRegistry,
instance: o.instance,
}
@@ -113,7 +137,7 @@ func NewHTTPServer_pbExample(addr string, opts ...HTTPOption) app.IServer { //no
return &httpServer{
addr: addr,
server: server,
server: newServer(server, o.tls),
iRegistry: o.iRegistry,
instance: o.instance,
}

View File

@@ -9,7 +9,9 @@ import (
"github.com/gin-gonic/gin"
"github.com/go-dev-frame/sponge/pkg/app"
"github.com/go-dev-frame/sponge/pkg/httpsrv"
"github.com/go-dev-frame/sponge/internal/config"
"github.com/go-dev-frame/sponge/internal/routers"
)
@@ -17,13 +19,13 @@ var _ app.IServer = (*httpServer)(nil)
type httpServer struct {
addr string
server *http.Server
server *httpsrv.Server
}
// Start http service
func (s *httpServer) Start() error {
if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return fmt.Errorf("listen server error: %v", err)
if err := s.server.Run(); err != nil {
return fmt.Errorf("run %s service error: %v", s.server.Scheme(), err)
}
return nil
}
@@ -36,7 +38,29 @@ func (s *httpServer) Stop() error {
// String comment
func (s *httpServer) String() string {
return "http service address " + s.addr
return s.server.Scheme() + " service address is " + s.addr
}
func newServer(server *http.Server, tls config.TLS) *httpsrv.Server {
var c *httpsrv.Server
switch httpsrv.Mode(tls.EnableMode) {
case httpsrv.ModeTLSSelfSigned:
c = httpsrv.New(server, httpsrv.NewTLSSelfSignedConfig())
case httpsrv.ModeTLSEncrypt:
c = httpsrv.New(server,
httpsrv.NewTLSEAutoEncryptConfig(
tls.Domain,
tls.Email,
// enable http redirect to https, port 80 to 443, default is false
//httpsrv.WithTLSEncryptEnableRedirect(),
),
)
case httpsrv.ModeTLSExternal:
c = httpsrv.New(server, httpsrv.NewTLSExternalConfig(tls.CertFile, tls.KeyFile))
default:
c = httpsrv.New(server)
}
return c
}
// NewHTTPServer creates a new http server
@@ -54,39 +78,14 @@ func NewHTTPServer(addr string, opts ...HTTPOption) app.IServer {
server := &http.Server{
Addr: addr,
Handler: router,
//ReadTimeout: time.Second*30,
//WriteTimeout: time.Second*60,
IdleTimeout: time.Second * 60,
MaxHeaderBytes: 1 << 20,
}
return &httpServer{
addr: addr,
server: server,
server: newServer(server, o.tls),
}
}
// delete the templates code start
// NewHTTPServer_pbExample creates a new web server
func NewHTTPServer_pbExample(addr string, opts ...HTTPOption) app.IServer { //nolint
o := defaultHTTPOptions()
o.apply(opts...)
if o.isProd {
gin.SetMode(gin.ReleaseMode)
} else {
gin.SetMode(gin.DebugMode)
}
router := routers.NewRouter_pbExample()
server := &http.Server{
Addr: addr,
Handler: router,
MaxHeaderBytes: 1 << 20,
}
return &httpServer{
addr: addr,
server: server,
}
}
// delete the templates code end

View File

@@ -2,6 +2,8 @@ package server
import (
"github.com/go-dev-frame/sponge/pkg/servicerd/registry"
"github.com/go-dev-frame/sponge/internal/config"
)
// HTTPOption setting up http
@@ -11,6 +13,7 @@ type httpOptions struct {
isProd bool
instance *registry.ServiceInstance
iRegistry registry.Registry
tls config.TLS
}
func defaultHTTPOptions() *httpOptions {
@@ -41,3 +44,10 @@ func WithHTTPRegistry(iRegistry registry.Registry, instance *registry.ServiceIns
o.instance = instance
}
}
// WithHTTPTLS setting up tls
func WithHTTPTLS(tls config.TLS) HTTPOption {
return func(o *httpOptions) {
o.tls = tls
}
}

View File

@@ -1,10 +1,15 @@
package server
import (
"github.com/go-dev-frame/sponge/internal/config"
)
// HTTPOption setting up http
type HTTPOption func(*httpOptions)
type httpOptions struct {
isProd bool
tls config.TLS
}
func defaultHTTPOptions() *httpOptions {
@@ -25,3 +30,10 @@ func WithHTTPIsProd(isProd bool) HTTPOption {
o.isProd = isProd
}
}
// WithHTTPTLS setting up tls
func WithHTTPTLS(tls config.TLS) HTTPOption {
return func(o *httpOptions) {
o.tls = tls
}
}

View File

@@ -85,11 +85,12 @@ func TestHTTPServerMock(t *testing.T) {
instance: &registry.ServiceInstance{},
iRegistry: &iRegistry{},
}
s.server = &http.Server{
server := &http.Server{
Addr: addr,
Handler: http.NewServeMux(),
MaxHeaderBytes: 1 << 20,
}
s.server = newServer(server, config.Get().HTTP.TLS)
go func() {
time.Sleep(time.Second * 3)
@@ -113,3 +114,48 @@ func (i *iRegistry) Register(ctx context.Context, service *registry.ServiceInsta
func (i *iRegistry) Deregister(ctx context.Context, service *registry.ServiceInstance) error {
return nil
}
func Test_newServer(t *testing.T) {
tests := []struct {
name string
tls config.TLS
scheme string
}{
{
name: "no_tls",
tls: config.TLS{},
scheme: "http",
},
{
name: "tls_self_signed",
tls: config.TLS{
EnableMode: "self-signed",
},
scheme: "https",
},
{
name: "tls_encrypt",
tls: config.TLS{
EnableMode: "encrypt",
Domain: "example.com",
Email: "admin@example.com",
},
scheme: "https",
},
{
name: "tls_external",
tls: config.TLS{
EnableMode: "external",
CertFile: "cert.pem",
KeyFile: "key.pem",
},
scheme: "https",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := newServer(&http.Server{}, tt.tls)
assert.Equal(t, tt.scheme, server.Scheme())
})
}
}