Files
zeekay 7052d7f267 rebrand: openziti → hanzozt (Hanzo Zero Trust)
Global rebrand of all OpenZiti references to Hanzo ZT:
- Go module paths: github.com/openziti/* → github.com/hanzozt/*
- Docker images: openziti/* → hanzozt/*
- Java packages: org.openziti.* → org.hanzozt.*
- Brand: OpenZiti → Hanzo ZT
- URLs: openziti.io → hanzozt.dev
2026-02-18 16:13:46 -08:00

220 lines
6.5 KiB
Go

/*
Copyright NetFoundry Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package xweb
import (
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"log"
"net"
"net/http"
"sync"
"github.com/michaelquigley/pfxlog"
"github.com/hanzozt/foundation/v2/debugz"
"github.com/hanzozt/xweb/v3/middleware"
)
type ContextKey string
type ServerContext struct {
BindPoint BindPoint
ServerConfig *ServerConfig
Config *InstanceConfig
}
type namedHttpServer struct {
*http.Server
ApiBindingList []string
BindPointConfig BindPoint
ServerConfig *ServerConfig
InstanceConfig *InstanceConfig
}
func (s namedHttpServer) NewBaseContext(_ net.Listener) context.Context {
serverContext := &ServerContext{
BindPoint: s.BindPointConfig,
ServerConfig: s.ServerConfig,
Config: s.InstanceConfig,
}
ctx := context.Background()
ctx = context.WithValue(ctx, ServerContextKey, serverContext)
return ctx
}
// Server represents all the http.Server's and http.Handler's necessary to run a single xweb.ServerConfig
type Server struct {
DefaultHttpHandlerProviderImpl
HttpServers []*namedHttpServer
logWriter *io.PipeWriter
options *ServerConfigOptions
config interface{}
Handle http.Handler
OnHandlerPanic func(writer http.ResponseWriter, request *http.Request, panicVal interface{})
ServerConfig *ServerConfig
}
// NewServer creates a new Server from a ServerConfig. All necessary http.Handler's will be created from the supplied
// DemuxFactory and Registry.
func NewServer(instance Instance, serverConfig *ServerConfig) (*Server, error) {
logWriter := pfxlog.Logger().Writer()
tlsConfig := serverConfig.Identity.ServerTLSConfig()
tlsConfig.ClientAuth = tls.RequestClientCert
tlsConfig.MinVersion = uint16(serverConfig.Options.MinTLSVersion)
tlsConfig.MaxVersion = uint16(serverConfig.Options.MaxTLSVersion)
server := &Server{
logWriter: logWriter,
config: &serverConfig,
HttpServers: []*namedHttpServer{},
ServerConfig: serverConfig,
}
server.SetParent(instance)
var handlers []ApiHandler
var apiBindingList []string
for _, api := range serverConfig.APIs {
if apiFactory := instance.GetRegistry().Get(api.Binding()); apiFactory != nil {
if handler, err := apiFactory.New(serverConfig, api.Options()); err != nil {
pfxlog.Logger().Fatalf("encountered error building handler for api binding [%s]: %v", api.Binding(), err)
} else {
handlers = append(handlers, handler)
apiBindingList = append(apiBindingList, api.binding)
}
} else {
pfxlog.Logger().Fatalf("encountered api binding [%s] which has no associated factory registered", api.Binding())
}
}
demuxHandler, err := instance.GetDemuxFactory().Build(handlers)
if err != nil {
return nil, fmt.Errorf("error creating server: %v", err)
}
demuxHandler.SetParent(server)
for _, bindPoint := range serverConfig.BindPoints {
namedServer := &namedHttpServer{
ApiBindingList: apiBindingList,
ServerConfig: serverConfig,
BindPointConfig: bindPoint,
InstanceConfig: instance.GetConfig(),
Server: &http.Server{
Addr: bindPoint.ServerAddress(),
WriteTimeout: serverConfig.Options.WriteTimeout,
ReadTimeout: serverConfig.Options.ReadTimeout,
IdleTimeout: serverConfig.Options.IdleTimeout,
Handler: server.wrapHandler(serverConfig, bindPoint, demuxHandler),
TLSConfig: tlsConfig,
ErrorLog: log.New(logWriter, "", 0),
},
}
namedServer.BaseContext = namedServer.NewBaseContext
server.HttpServers = append(server.HttpServers, namedServer)
}
for _, mutator := range instance.GetConfig().Options.ServerMutators {
if err = mutator(instance, serverConfig, server); err != nil {
return nil, fmt.Errorf("encountered error mutating server instance: %v", err)
}
}
return server, nil
}
func (server *Server) wrapHandler(_ *ServerConfig, bp BindPoint, handler http.Handler) http.Handler {
//innermost/bottom -> outermost/top
handler = bp.BeforeHandler(handler)
handler = server.wrapPanicRecovery(handler)
handler = middleware.NewCompressionHandler(handler)
return handler
}
// wrapPanicRecovery wraps a http.Handler with another http.Handler that provides recovery.
func (server *Server) wrapPanicRecovery(handler http.Handler) http.Handler {
wrappedHandler := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
defer func() {
if panicVal := recover(); panicVal != nil {
if server.OnHandlerPanic != nil {
server.OnHandlerPanic(writer, request, panicVal)
return
}
pfxlog.Logger().Errorf("panic caught by server handler: %v\n%v", panicVal, debugz.GenerateLocalStack())
}
}()
handler.ServeHTTP(writer, request)
})
return wrappedHandler
}
// Start the server and all underlying http.Server's
func (server *Server) Start() error {
logger := pfxlog.Logger()
var wg sync.WaitGroup
for _, httpServer := range server.HttpServers {
wg.Add(1)
svr := httpServer
go func() {
defer wg.Done()
logger.Infof("starting ApiConfig to listen and serve tls on %s for server %s with APIs: %v", svr.Addr, svr.ServerConfig.Name, svr.ApiBindingList)
cfg := svr.TLSConfig
// make sure to listen to the expected protocols
cfg.NextProtos = append(cfg.NextProtos, "h2", "http/1.1", "")
l, err := svr.BindPointConfig.Listener(svr.ServerConfig.Name, svr.Server.TLSConfig)
if err != nil {
logger.Errorf("error creating listener for server: %s at %s, %v", svr.ServerConfig.Name, svr.BindPointConfig.ServerAddress(), err)
}
err = svr.Serve(l)
if !errors.Is(err, http.ErrServerClosed) {
logger.Errorf("error running server: %s at %s, %v", svr.ServerConfig.Name, svr.BindPointConfig.ServerAddress(), err)
}
}()
}
logger.Infof("all servers running")
wg.Wait()
logger.Infof("all servers no longer running")
return nil
}
// Shutdown stops the server and all underlying http.Server's
func (server *Server) Shutdown(ctx context.Context) {
_ = server.logWriter.Close()
for _, httpServer := range server.HttpServers {
localServer := httpServer
func() {
_ = localServer.Shutdown(ctx)
}()
}
}