|
|
|
@ -256,6 +256,12 @@ func (s *Server) Start() error {
|
|
|
|
IdleTimeout: 120 * time.Second,
|
|
|
|
IdleTimeout: 120 * time.Second,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Start server in a goroutine so we can return from this function
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
|
|
s.logger.Info("Starting web server", "port", s.port, "http2", s.useHTTP2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if s.useHTTP2 {
|
|
|
|
// Check if certificate files exist
|
|
|
|
// Check if certificate files exist
|
|
|
|
certFile := "server.crt"
|
|
|
|
certFile := "server.crt"
|
|
|
|
keyFile := "server.key"
|
|
|
|
keyFile := "server.key"
|
|
|
|
@ -263,39 +269,48 @@ func (s *Server) Start() error {
|
|
|
|
certExists := fileExists(certFile)
|
|
|
|
certExists := fileExists(certFile)
|
|
|
|
keyExists := fileExists(keyFile)
|
|
|
|
keyExists := fileExists(keyFile)
|
|
|
|
|
|
|
|
|
|
|
|
s.logger.Info("Starting web server", "port", s.port, "http2", true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if certExists && keyExists {
|
|
|
|
if certExists && keyExists {
|
|
|
|
// Start HTTPS server with HTTP/2 support
|
|
|
|
// Start HTTPS server with HTTP/2 support
|
|
|
|
s.logger.Info("Using TLS with HTTP/2", "certFile", certFile, "keyFile", keyFile)
|
|
|
|
s.logger.Info("Using TLS with HTTP/2", "certFile", certFile, "keyFile", keyFile)
|
|
|
|
return s.server.ListenAndServeTLS(certFile, keyFile)
|
|
|
|
err = s.server.ListenAndServeTLS(certFile, keyFile)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// Generate self-signed certificate for development
|
|
|
|
// Generate self-signed certificate for development
|
|
|
|
s.logger.Info("Generating self-signed certificate for HTTP/2")
|
|
|
|
s.logger.Info("Generating self-signed certificate for HTTP/2")
|
|
|
|
cert, key, err := generateSelfSignedCert()
|
|
|
|
cert, key, genErr := generateSelfSignedCert()
|
|
|
|
if err != nil {
|
|
|
|
if genErr != nil {
|
|
|
|
s.logger.Error("Failed to generate self-signed certificate", "error", err)
|
|
|
|
s.logger.Error("Failed to generate self-signed certificate", "error", genErr)
|
|
|
|
|
|
|
|
|
|
|
|
// Fall back to HTTP/1.1
|
|
|
|
// Fall back to HTTP/1.1
|
|
|
|
s.logger.Info("Falling back to HTTP/1.1 (no TLS)")
|
|
|
|
s.logger.Info("Falling back to HTTP/1.1 (no TLS)")
|
|
|
|
s.server.TLSConfig = nil
|
|
|
|
s.server.TLSConfig = nil
|
|
|
|
return s.server.ListenAndServe()
|
|
|
|
err = s.server.ListenAndServe()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
|
|
// Write certificate files
|
|
|
|
// Write certificate files
|
|
|
|
if err := os.WriteFile(certFile, cert, 0600); err != nil {
|
|
|
|
if writeErr := os.WriteFile(certFile, cert, 0600); writeErr != nil {
|
|
|
|
s.logger.Error("Failed to write certificate file", "error", err)
|
|
|
|
s.logger.Error("Failed to write certificate file", "error", writeErr)
|
|
|
|
return err
|
|
|
|
err = writeErr
|
|
|
|
|
|
|
|
} else if writeErr := os.WriteFile(keyFile, key, 0600); writeErr != nil {
|
|
|
|
|
|
|
|
s.logger.Error("Failed to write key file", "error", writeErr)
|
|
|
|
|
|
|
|
err = writeErr
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
s.logger.Info("Self-signed certificate generated", "certFile", certFile, "keyFile", keyFile)
|
|
|
|
|
|
|
|
err = s.server.ListenAndServeTLS(certFile, keyFile)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if err := os.WriteFile(keyFile, key, 0600); err != nil {
|
|
|
|
}
|
|
|
|
s.logger.Error("Failed to write key file", "error", err)
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
// Start HTTP/1.1 server
|
|
|
|
|
|
|
|
s.logger.Info("Using HTTP/1.1 (no TLS)")
|
|
|
|
|
|
|
|
s.server.TLSConfig = nil
|
|
|
|
|
|
|
|
err = s.server.ListenAndServe()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
s.logger.Info("Self-signed certificate generated", "certFile", certFile, "keyFile", keyFile)
|
|
|
|
if err != nil && err != http.ErrServerClosed {
|
|
|
|
return s.server.ListenAndServeTLS(certFile, keyFile)
|
|
|
|
s.logger.Error("HTTP server error", "error", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Helper function to check if a file exists
|
|
|
|
// Helper function to check if a file exists
|
|
|
|
@ -347,6 +362,10 @@ func (s *Server) Shutdown(ctx context.Context) error {
|
|
|
|
// Signal event forwarder to stop
|
|
|
|
// Signal event forwarder to stop
|
|
|
|
close(s.shutdown)
|
|
|
|
close(s.shutdown)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Close all client connections with a timeout
|
|
|
|
|
|
|
|
shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
|
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
|
|
// Close all client connections
|
|
|
|
// Close all client connections
|
|
|
|
s.clientsMux.Lock()
|
|
|
|
s.clientsMux.Lock()
|
|
|
|
for clientChan := range s.clients {
|
|
|
|
for clientChan := range s.clients {
|
|
|
|
@ -363,7 +382,15 @@ func (s *Server) Shutdown(ctx context.Context) error {
|
|
|
|
s.adminclientsMux.Unlock()
|
|
|
|
s.adminclientsMux.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
// Shutdown the HTTP server
|
|
|
|
// Shutdown the HTTP server
|
|
|
|
return s.server.Shutdown(ctx)
|
|
|
|
err := s.server.Shutdown(shutdownCtx)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
// If shutdown times out, force close
|
|
|
|
|
|
|
|
s.logger.Warn("Server shutdown timed out, forcing close", "error", err)
|
|
|
|
|
|
|
|
return s.server.Close()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s.logger.Info("Web server shutdown complete")
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// forwardEvents forwards derby events to SSE clients
|
|
|
|
// forwardEvents forwards derby events to SSE clients
|
|
|
|
|