package main import ( "crypto/tls" "fmt" "io" "net/http" "sync/atomic" "time" ) // Backend represents a backend service that can handle proxied requests type Backend struct { WorkerID string URL string Healthy bool } // BackendPool manages a pool of backend services for load balancing type BackendPool struct { workerType WorkerType store *WorkerStore current atomic.Uint64 // For round-robin } // NewBackendPool creates a new backend pool for a specific worker type func NewBackendPool(workerType WorkerType, store *WorkerStore) *BackendPool { return &BackendPool{ workerType: workerType, store: store, } } // GetBackends returns all healthy backends of this pool's type func (bp *BackendPool) GetBackends() []Backend { workers := bp.store.List() backends := make([]Backend, 0) for _, worker := range workers { if worker.Type == bp.workerType && worker.Healthy { backends = append(backends, Backend{ WorkerID: worker.ID, URL: worker.URL, Healthy: worker.Healthy, }) } } return backends } // NextBackend returns the next healthy backend using round-robin func (bp *BackendPool) NextBackend() (*Backend, error) { backends := bp.GetBackends() if len(backends) == 0 { return nil, fmt.Errorf("no healthy %s backends available", bp.workerType) } // Round-robin selection idx := bp.current.Add(1) % uint64(len(backends)) return &backends[idx], nil } // ProxyManager manages multiple backend pools type ProxyManager struct { inputPool *BackendPool outputPool *BackendPool client *http.Client } // NewProxyManager creates a new proxy manager func NewProxyManager(store *WorkerStore) *ProxyManager { // Create HTTP client that accepts self-signed certs (for internal services) transport := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, } return &ProxyManager{ inputPool: NewBackendPool(WorkerTypeInput, store), outputPool: NewBackendPool(WorkerTypeOutput, store), client: &http.Client{ Timeout: 30 * time.Second, Transport: transport, }, } } // ProxyGetTarget forwards a GET request to an input service to get next target IP func (pm *ProxyManager) ProxyGetTarget(w http.ResponseWriter, r *http.Request) error { backend, err := pm.inputPool.NextBackend() if err != nil { return err } // Forward GET /target request targetURL := fmt.Sprintf("%s/target", backend.URL) req, err := http.NewRequest("GET", targetURL, nil) if err != nil { return err } // Copy headers if needed req.Header.Set("User-Agent", "PingServiceManager-Gateway/1.0") resp, err := pm.client.Do(req) if err != nil { return fmt.Errorf("backend request failed: %v", err) } defer resp.Body.Close() // Copy response status and headers w.WriteHeader(resp.StatusCode) for key, values := range resp.Header { for _, value := range values { w.Header().Add(key, value) } } // Copy response body _, err = io.Copy(w, resp.Body) return err } // ProxyPostResult forwards a POST request to an output service to submit results func (pm *ProxyManager) ProxyPostResult(w http.ResponseWriter, r *http.Request) error { backend, err := pm.outputPool.NextBackend() if err != nil { return err } // Forward POST /result request targetURL := fmt.Sprintf("%s/result", backend.URL) req, err := http.NewRequest("POST", targetURL, r.Body) if err != nil { return err } // Copy content type req.Header.Set("Content-Type", r.Header.Get("Content-Type")) req.Header.Set("User-Agent", "PingServiceManager-Gateway/1.0") resp, err := pm.client.Do(req) if err != nil { return fmt.Errorf("backend request failed: %v", err) } defer resp.Body.Close() // Copy response status and headers w.WriteHeader(resp.StatusCode) for key, values := range resp.Header { for _, value := range values { w.Header().Add(key, value) } } // Copy response body _, err = io.Copy(w, resp.Body) return err } // GetPoolStats returns statistics about backend pools func (pm *ProxyManager) GetPoolStats() map[string]interface{} { inputBackends := pm.inputPool.GetBackends() outputBackends := pm.outputPool.GetBackends() return map[string]interface{}{ "input_backends": len(inputBackends), "output_backends": len(outputBackends), "total_backends": len(inputBackends) + len(outputBackends), } }