src/internal/core/server/protocols/http/server.go
package http
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"os"
"time"
"github.com/kodflow/kitsune/src/config"
"github.com/kodflow/kitsune/src/internal/core/certs"
"github.com/kodflow/kitsune/src/internal/core/server/router"
"github.com/kodflow/kitsune/src/internal/core/server/transport"
"github.com/kodflow/kitsune/src/internal/kernel/errors"
"github.com/kodflow/kitsune/src/internal/kernel/observability/logger"
"golang.org/x/net/http2"
)
// Server represents an HTTP server that handles both standard and secure connections.
// It encapsulates the functionality of two engines, one for handling standard HTTP
// connections and the other for secure HTTPS connections, along with a router for API routing.
type Server struct {
standard *Engine // The engine for handling standard HTTP connections.
secure *Engine // The engine for handling secure HTTPS connections.
router *router.Router // The router for managing API endpoints.
}
// ServerCfg holds configuration data for the HTTP server.
// This structure includes details such as the server's domain, subdomains, and port numbers
// for both HTTP and HTTPS connections.
type ServerCfg struct {
DOMAIN string // The domain of the server.
SUBS []string // The subdomains of the server.
HTTP string // The port number for HTTP connections.
HTTPS string // The port number for HTTPS connections.
}
// Engine represents an HTTP engine.
// It is responsible for managing network listeners and the HTTP server for either standard
// or secure connections, keeping track of its running status and configuration details
// like port, domain, and subdomains.
type Engine struct {
PORT string // The port number for the engine.
DOMAIN string // The domain of the engine.
SUBS []string // The subdomains of the engine.
listener net.Listener // The network listener for the engine.
server *http.Server // The HTTP server instance.
running bool // Indicates if the engine is currently running.
}
// newServerConfig creates a new HTTP server configuration.
// It sets up various timeouts and limits for the server. If a TLS configuration is provided,
// it is applied to enable HTTPS.
//
// Parameters:
// - tls: *tls.Config Optional TLS configuration for HTTPS.
//
// Returns:
// - *http.Server: Configured HTTP server.
func newServerConfig(tls *tls.Config) *http.Server {
srv := &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: 120 * time.Second,
ReadHeaderTimeout: 2 * time.Second,
MaxHeaderBytes: 1 << 20,
}
if tls != nil {
srv.TLSConfig = tls
}
return srv
}
// NewServer creates a new HTTP server based on the provided configuration.
// It initializes a standard and, if specified, a secure engine based on the server configuration.
//
// Parameters:
// - cfg: *ServerCfg Configuration data for the HTTP server.
//
// Returns:
// - *Server: A new HTTP server.
// - error: An error if any.
func NewServer(cfg *ServerCfg) *Server {
server := &Server{
router: router.MakeRouter(),
standard: &Engine{
PORT: cfg.HTTP,
DOMAIN: cfg.DOMAIN,
SUBS: cfg.SUBS,
server: newServerConfig(nil),
},
}
server.standard.server.Handler = http.HandlerFunc(server.HTTPHandler)
if cfg.HTTPS == "" {
return server
}
server.secure = &Engine{
PORT: cfg.HTTPS,
DOMAIN: cfg.DOMAIN,
SUBS: cfg.SUBS,
server: newServerConfig(certs.TLSConfigFor(cfg.DOMAIN, cfg.SUBS...)),
}
server.secure.server.Handler = http.HandlerFunc(server.HTTPHandler)
http2.ConfigureServer(server.secure.server, &http2.Server{
IdleTimeout: config.DEFAULT_TIMEOUT * time.Second,
})
return server
}
func (s *Server) Register(api *router.EndPoint) {
s.router.Register(api)
}
// Start starts the HTTP server, allowing it to accept incoming connections.
// It checks for any running instances of the server and starts the standard and secure engines.
//
// Returns:
// - error: An error if any.
func (s *Server) Start() error {
multi := errors.NewMultiError()
if s.standard.running {
multi.Add(errors.New("standard server active"))
}
if s.secure != nil && s.secure.running {
multi.Add(errors.New("secure server active"))
}
if multi.Count() > 0 {
return multi
}
multi.Add(s.standard.Start())
if s.secure != nil {
multi.Add(s.secure.Start())
}
return multi.IsError()
}
// Stop stops the HTTP server.
// This method halts the server's operations and stops accepting incoming connections.
//
// Returns:
// - error: An error if any.
func (s *Server) Stop() error {
multi := errors.NewMultiError()
if !s.standard.running {
multi.Add(errors.New("standard server is not active"))
}
if s.secure != nil && !s.secure.running {
multi.Add(errors.New("secure server is not active"))
}
if multi.Count() > 0 {
return multi
}
multi.Add(s.standard.Stop())
if s.secure != nil {
multi.Add(s.secure.Stop())
}
return multi.IsError()
}
// Start starts the HTTP engine, allowing it to accept incoming connections.
// This method initiates the listening process on the specified port and handles incoming requests.
//
// Returns:
// - error: An error if any.
func (e *Engine) Start() error {
if e.running {
return errors.New("server already started")
}
listener, err := net.Listen("tcp", ":"+e.PORT)
if err != nil {
return err
}
e.listener = listener
if e.server.TLSConfig != nil {
e.listener = tls.NewListener(e.listener, e.server.TLSConfig)
}
e.running = true
logger.Info(fmt.Sprintf("server start on %v:%v with pid: %v", e.DOMAIN, e.PORT, os.Getpid()))
go e.server.Serve(e.listener)
return nil
}
// Stop stops the HTTP engine.
// It terminates the listening process and stops the server from accepting new connections.
//
// Returns:
// - error: An error if any.
func (e *Engine) Stop() error {
if !e.running {
return errors.New("server is not active")
}
err := e.listener.Close()
e.running = false
logger.Info(fmt.Sprintf("server stop on %v:%v with pid: %v", e.DOMAIN, e.PORT, os.Getpid()))
return err
}
// HTTPHandler handles HTTP requests and sends back HTTP responses.
// It processes incoming HTTP requests, creates a corresponding transport request,
// and uses the router to generate a response. The handler deals with various HTTP methods,
// reads request bodies if necessary, and writes back responses including headers and status codes.
//
// Parameters:
// - w: http.ResponseWriter Response writer to send back the HTTP response.
// - r: *http.Request The incoming HTTP request to be processed.
func (s *Server) HTTPHandler(w http.ResponseWriter, r *http.Request) {
logger.Infof("request: %v %v", r.Host, r.URL.String())
// Initialize a new transport request and response
exchange := transport.New()
exchange.RequestFromHTTP(r)
s.router.Resolve(exchange)
exchange.ResponseFromHTTP(w)
}