package proxy import ( "context" "log" "net" "net/http" "sync" "time" "github.com/wormhole/wormhole/internal/auth" "github.com/wormhole/wormhole/internal/store" ) type httpTunnelServer struct { manager *Manager tunnel *store.Tunnel srv *http.Server stop chan struct{} wg sync.WaitGroup } func newHTTPTunnelServer(m *Manager, t *store.Tunnel) Service { return &httpTunnelServer{ manager: m, tunnel: t, stop: make(chan struct{}), } } func (s *httpTunnelServer) ID() uint { return s.tunnel.ID } func (s *httpTunnelServer) Start() error { addr := net.JoinHostPort("0.0.0.0", itoa(s.tunnel.ListenPort)) s.srv = &http.Server{ Addr: addr, Handler: http.HandlerFunc(s.handleHTTP), } log.Printf("[proxy] http tunnel %d (visitor auth) listening on %s -> %s", s.tunnel.ID, addr, s.tunnel.TargetAddr) s.wg.Add(1) go func() { defer s.wg.Done() if err := s.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Printf("[proxy] http tunnel %d error: %v", s.tunnel.ID, err) } }() return nil } func (s *httpTunnelServer) handleHTTP(w http.ResponseWriter, r *http.Request) { t := s.tunnel ip := auth.ExtractRemoteIP(r) directIP := auth.DirectRemoteIP(r) s.manager.metrics.RecordRequestIP("tunnel", t.ID, ip, directIP) if !s.manager.security.Allow(directIP, t.ClientID, "tunnel", t.ID) { http.Error(w, "forbidden", http.StatusForbidden) return } if !s.manager.resAuth.CheckHTTP(w, r, "tunnel", t.ID) { return } s.manager.proxyTunnelHTTP(w, r, t) } func (s *httpTunnelServer) Stop() error { close(s.stop) if s.srv != nil { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _ = s.srv.Shutdown(ctx) } s.wg.Wait() return nil }