added test
This commit is contained in:
@@ -7,77 +7,78 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Shared global variables used across all files
|
||||
var (
|
||||
config Config
|
||||
configMux sync.RWMutex
|
||||
cert *tls.Certificate
|
||||
baseDir string
|
||||
certDir string
|
||||
certPath string
|
||||
keyPath string
|
||||
configPath string
|
||||
config Config // Holds the proxy configuration loaded from config.yaml
|
||||
configMux sync.RWMutex // Mutex for thread-safe access to config
|
||||
cert *tls.Certificate // TLS certificate for HTTPS server
|
||||
baseDir string // Base directory (working dir for go run, executable dir for binary)
|
||||
certDir string // Directory for certificates
|
||||
certPath string // Path to certificate file
|
||||
keyPath string // Path to private key file
|
||||
configPath string // Path to config.yaml
|
||||
|
||||
// Loggers
|
||||
errorLogger *log.Logger
|
||||
refreshLogger *log.Logger
|
||||
trafficLogger *log.Logger
|
||||
// Loggers for different types of events
|
||||
errorLogger *log.Logger // Logs errors to errors.log and console
|
||||
refreshLogger *log.Logger // Logs config/certificate refreshes to refresh.log and console
|
||||
trafficLogger *log.Logger // Logs traffic to traffic-YYYY-MM-DD.log and console
|
||||
)
|
||||
|
||||
// init sets up the base directory and initializes logging
|
||||
func init() {
|
||||
// Get the absolute path of the running executable (or main.go in `go run`)
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get executable path: %v", err)
|
||||
}
|
||||
baseDir = filepath.Dir(exePath)
|
||||
/*
|
||||
if runtime.GOOS == "windows" && len(os.Args) > 0 && filepath.Ext(os.Args[0]) == ".go" {
|
||||
var err error
|
||||
baseDir, err = os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get working directory: %v", err)
|
||||
}
|
||||
} else {
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get executable path: %v", err)
|
||||
}
|
||||
baseDir = filepath.Dir(exePath)
|
||||
// Determine base directory based on execution context
|
||||
if runtime.GOOS == "windows" && len(os.Args) > 0 && filepath.Ext(os.Args[0]) == ".go" {
|
||||
// For "go run", use current working directory
|
||||
var err error
|
||||
baseDir, err = os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get working directory: %v", err)
|
||||
}
|
||||
*/
|
||||
// Setup logging
|
||||
} else {
|
||||
// For compiled binary, use executable's directory
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get executable path: %v", err)
|
||||
}
|
||||
baseDir = filepath.Dir(exePath)
|
||||
}
|
||||
|
||||
// Initialize logging to files and console
|
||||
setupLogging()
|
||||
}
|
||||
|
||||
// setupLogging configures loggers for errors, refreshes, and traffic
|
||||
func setupLogging() {
|
||||
logsDir := filepath.Join(baseDir, "logs")
|
||||
if err := os.MkdirAll(logsDir, 0755); err != nil {
|
||||
log.Fatalf("Failed to create logs directory: %v", err)
|
||||
}
|
||||
|
||||
// Error log
|
||||
// Error log: writes to errors.log and stdout
|
||||
errorFile, err := os.OpenFile(filepath.Join(logsDir, "errors.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open errors.log: %v", err)
|
||||
}
|
||||
errorLogger = log.New(io.MultiWriter(os.Stdout, errorFile), "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
// Refresh log
|
||||
// Refresh log: writes to refresh.log and stdout
|
||||
refreshFile, err := os.OpenFile(filepath.Join(logsDir, "refresh.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open refresh.log: %v", err)
|
||||
}
|
||||
refreshLogger = log.New(io.MultiWriter(os.Stdout, refreshFile), "REFRESH: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
// Traffic log (daily rotation)
|
||||
// Traffic log: managed in a goroutine for daily rotation
|
||||
go manageTrafficLogs(logsDir)
|
||||
}
|
||||
|
||||
// manageTrafficLogs handles daily rotation of traffic logs with 7-day retention
|
||||
func manageTrafficLogs(logsDir string) {
|
||||
for {
|
||||
dateStr := time.Now().Format("2006-01-02")
|
||||
@@ -89,7 +90,7 @@ func manageTrafficLogs(logsDir string) {
|
||||
}
|
||||
trafficLogger = log.New(io.MultiWriter(os.Stdout, trafficFile), "TRAFFIC: ", log.Ldate|log.Ltime)
|
||||
|
||||
// Cleanup old logs
|
||||
// Cleanup logs older than 7 days
|
||||
cleanupOldLogs(logsDir)
|
||||
|
||||
// Wait until next day
|
||||
@@ -99,6 +100,7 @@ func manageTrafficLogs(logsDir string) {
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupOldLogs removes traffic logs older than 7 days
|
||||
func cleanupOldLogs(logsDir string) {
|
||||
files, err := os.ReadDir(logsDir)
|
||||
if err != nil {
|
||||
@@ -106,7 +108,7 @@ func cleanupOldLogs(logsDir string) {
|
||||
return
|
||||
}
|
||||
|
||||
cutoff := time.Now().AddDate(0, 0, -7) // 7 days ago
|
||||
cutoff := time.Now().AddDate(0, 0, -7)
|
||||
for _, file := range files {
|
||||
if strings.HasPrefix(file.Name(), "traffic-") && file.Name() != "traffic-"+time.Now().Format("2006-01-02")+".log" {
|
||||
info, err := file.Info()
|
||||
@@ -124,7 +126,9 @@ func cleanupOldLogs(logsDir string) {
|
||||
}
|
||||
}
|
||||
|
||||
// main is the entry point, setting up and running the HTTP/HTTPS servers
|
||||
func main() {
|
||||
// Set config file path and load or generate initial config
|
||||
configPath = filepath.Join(baseDir, "config.yaml")
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
config = generateDefaultConfig()
|
||||
@@ -140,8 +144,10 @@ func main() {
|
||||
config = cfg
|
||||
}
|
||||
|
||||
// Update certificate and config paths based on loaded config
|
||||
updatePaths()
|
||||
|
||||
// Generate or load TLS certificate
|
||||
_, certErr := os.Stat(certPath)
|
||||
_, keyErr := os.Stat(keyPath)
|
||||
if os.IsNotExist(certErr) || os.IsNotExist(keyErr) {
|
||||
@@ -149,22 +155,24 @@ func main() {
|
||||
errorLogger.Fatalf("Failed to generate self-signed certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := loadCertificate(); err != nil {
|
||||
errorLogger.Fatalf("Failed to load certificate: %v", err)
|
||||
}
|
||||
|
||||
// Start background monitoring for config and certificate changes
|
||||
go monitorCertificates()
|
||||
go monitorConfig()
|
||||
|
||||
// Configure HTTP server with timeouts for robustness
|
||||
httpServer := &http.Server{
|
||||
Addr: config.ListenHTTP,
|
||||
Handler: http.HandlerFunc(handler),
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
MaxHeaderBytes: 1 << 20, // 1 MB max header size
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
// Configure HTTPS server with TLS and certificate fetching
|
||||
tlsConfig := &tls.Config{
|
||||
GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
configMux.RLock()
|
||||
@@ -181,16 +189,16 @@ func main() {
|
||||
WriteTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
// Start HTTP server in a goroutine
|
||||
go func() {
|
||||
log.Printf("Starting HTTP server on %s", config.ListenHTTP)
|
||||
trafficLogger.Printf("Starting HTTP server on %s", config.ListenHTTP)
|
||||
refreshLogger.Printf("Starting HTTP server on %s", config.ListenHTTP)
|
||||
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
errorLogger.Fatalf("HTTP server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf("Starting HTTPS server on %s", config.ListenHTTPS)
|
||||
trafficLogger.Printf("Starting HTTPS server on %s", config.ListenHTTPS)
|
||||
// Start HTTPS server in the main goroutine
|
||||
refreshLogger.Printf("Starting HTTPS server on %s", config.ListenHTTPS)
|
||||
if err := httpsServer.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
|
||||
errorLogger.Fatalf("HTTPS server error: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user