Files
Wormhole/internal/proxy/http_tunnel.go
T
rose_cat707 0d84b07f68
CI / docker (push) Successful in 1m58s
feat: Wormhole 内网穿透与反向代理网关
Go + Vue 管理台:Agent 隧道、域名反代、IP 安全、访客验证与监控大盘。
Gitea Actions CI 多架构镜像;纯 Go SQLite、无 CGO Docker 构建。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 17:52:43 +08:00

75 lines
1.7 KiB
Go

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
}