Files

266 lines
5.5 KiB
Go
Raw Permalink Normal View History

2026-03-30 22:05:57 +08:00
package server
import (
"bufio"
"computer-network/admin"
"computer-network/config"
"fmt"
"net"
"sync"
)
type Server struct {
listener net.Listener
config *config.Config
handler *Handler
mu sync.RWMutex
running bool
stopChan chan struct{}
}
func NewServer(cfg *config.Config) *Server {
return &Server{
config: cfg,
handler: NewHandler(cfg.GetRootDir()),
stopChan: make(chan struct{}),
}
}
func (s *Server) Start() error {
s.mu.Lock()
defer s.mu.Unlock()
if s.running {
return fmt.Errorf("server is already running")
}
var err error
addr := fmt.Sprintf(":%d", s.config.GetPort())
s.listener, err = net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf("failed to listen on port %d: %v", s.config.GetPort(), err)
}
s.running = true
fmt.Printf("Web Server started on port %d\n", s.config.GetPort())
fmt.Printf("Root directory: %s\n", s.config.GetRootDir())
fmt.Printf("Admin console: http://localhost:%d\n", s.config.GetAdminPort())
go s.acceptConnections()
return nil
}
func (s *Server) acceptConnections() {
for {
select {
case <-s.stopChan:
return
default:
conn, err := s.listener.Accept()
if err != nil {
select {
case <-s.stopChan:
return
default:
fmt.Printf("Error accepting connection: %v\n", err)
continue
}
}
go s.handler.HandleConnection(conn)
}
}
}
func (s *Server) Stop() error {
s.mu.Lock()
defer s.mu.Unlock()
if !s.running {
return fmt.Errorf("server is not running")
}
close(s.stopChan)
s.running = false
if s.listener != nil {
err := s.listener.Close()
if err != nil {
fmt.Printf("Error closing listener: %v\n", err)
}
}
fmt.Println("Web Server stopped")
return nil
}
func (s *Server) IsRunning() bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.running
}
func (s *Server) GetPort() int {
return s.config.GetPort()
}
func (s *Server) UpdateConfig(cfg *config.Config) {
s.mu.Lock()
defer s.mu.Unlock()
s.config = cfg
s.handler = NewHandler(cfg.GetRootDir())
}
type AdminServer struct {
listener net.Listener
config *config.Config
adminHandler *admin.AdminHandler
webServer *Server
mu sync.RWMutex
running bool
stopChan chan struct{}
}
func NewAdminServer(cfg *config.Config) *AdminServer {
return &AdminServer{
config: cfg,
adminHandler: admin.NewAdminHandler(),
stopChan: make(chan struct{}),
}
}
func (as *AdminServer) SetWebServer(ws *Server) {
as.mu.Lock()
defer as.mu.Unlock()
as.webServer = ws
}
func (as *AdminServer) Start() error {
as.mu.Lock()
defer as.mu.Unlock()
if as.running {
return fmt.Errorf("admin server is already running")
}
var err error
addr := fmt.Sprintf(":%d", as.config.GetAdminPort())
as.listener, err = net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf("failed to listen on admin port %d: %v", as.config.GetAdminPort(), err)
}
as.running = true
fmt.Printf("Admin server started on port %d\n", as.config.GetAdminPort())
go as.acceptConnections()
return nil
}
func (as *AdminServer) acceptConnections() {
for {
select {
case <-as.stopChan:
return
default:
conn, err := as.listener.Accept()
if err != nil {
select {
case <-as.stopChan:
return
default:
fmt.Printf("Error accepting admin connection: %v\n", err)
continue
}
}
go as.handleAdminConnection(conn)
}
}
}
func (as *AdminServer) handleAdminConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
req, err := ParseRequest(reader)
if err != nil {
response := BuildErrorResponse(StatusBadRequest, "", as.config.GetRootDir())
SendResponse(conn, response)
return
}
if !req.IsGET() && req.Method != "DELETE" {
response := BuildErrorResponse(StatusNotImplemented, "", as.config.GetRootDir())
SendResponse(conn, response)
return
}
path := req.GetPath()
var response *Response
switch path {
case "/", "/index.html":
httpResp := as.adminHandler.GetIndexPage()
response = convertAdminResponse(httpResp)
case "/api/logs":
if req.IsGET() {
httpResp := as.adminHandler.GetLogsJSON()
response = convertAdminResponse(httpResp)
} else if req.Method == "DELETE" {
httpResp := as.adminHandler.ClearLogs()
response = convertAdminResponse(httpResp)
} else {
response = BuildErrorResponse(StatusNotImplemented, "", as.config.GetRootDir())
}
case "/logs":
httpResp := as.adminHandler.GetLogsPage()
response = convertAdminResponse(httpResp)
case "/api/status":
webServerRunning := as.webServer != nil && as.webServer.IsRunning()
webServerPort := 9000
if as.webServer != nil {
webServerPort = as.webServer.GetPort()
}
statusBody := fmt.Sprintf(`{"running":%t,"port":%d}`, webServerRunning, webServerPort)
response = NewResponse("200 OK", statusBody, "application/json")
default:
response = BuildErrorResponse(StatusNotFound, "", as.config.GetRootDir())
}
SendResponse(conn, response)
}
func (as *AdminServer) Stop() error {
as.mu.Lock()
defer as.mu.Unlock()
if !as.running {
return fmt.Errorf("admin server is not running")
}
close(as.stopChan)
as.running = false
if as.listener != nil {
err := as.listener.Close()
if err != nil {
fmt.Printf("Error closing admin listener: %v\n", err)
}
}
fmt.Println("Admin server stopped")
return nil
}
func (as *AdminServer) IsRunning() bool {
as.mu.RLock()
defer as.mu.RUnlock()
return as.running
}
func convertAdminResponse(resp *admin.HTTPResponse) *Response {
return NewResponse(resp.StatusCode, resp.Body, resp.ContentType)
}