package forward import ( "crypto/tls" "fmt" "log" "net" "net/http" "net/http/httputil" "net/url" "strings" "time" ) func newReverseProxy(target, kind string) (*httputil.ReverseProxy, error) { targetURL, err := url.Parse(target) if err != nil { return nil, err } proxy := httputil.NewSingleHostReverseProxy(targetURL) proxy.Transport = &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // 政务网自签证书 } proxy.Director = func(req *http.Request) { req.URL.Scheme = targetURL.Scheme req.URL.Host = targetURL.Host req.URL.Path = targetURL.Path req.URL.RawPath = targetURL.RawPath req.URL.RawQuery = targetURL.RawQuery req.Host = targetURL.Host filtered := make(http.Header) copyAllowedHeaders(filtered, req.Header, kind) req.Header = filtered } return proxy, nil } func handleForward(w http.ResponseWriter, r *http.Request, kind string, proxy *httputil.ReverseProxy, target, allowIPs, forwardSecret string) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } ip := clientIP(r) if allowIPs != "" && !ipAllowed(ip, allowIPs) { appLogf("%s | %s | %s | forbidden | not in ALLOW_IPS", time.Now().Format(time.RFC3339), ip, kind) http.Error(w, "forbidden", http.StatusForbidden) return } if forwardSecret != "" && r.Header.Get("X-Forward-Token") != forwardSecret { appLogf("%s | %s | %s | forbidden | bad forward token", time.Now().Format(time.RFC3339), ip, kind) http.Error(w, "forbidden", http.StatusForbidden) return } start := time.Now() sw := &statusRecorder{ResponseWriter: w, status: http.StatusOK} proxy.ServeHTTP(sw, r) ms := time.Since(start).Milliseconds() line := fmt.Sprintf("%s | %s | %s | http=%d | %dms | %s", time.Now().Format(time.RFC3339), ip, kind, sw.status, ms, target) appLogf(line) log.Printf("forward %s %s http=%d %dms", ip, kind, sw.status, ms) } type statusRecorder struct { http.ResponseWriter status int } func (w *statusRecorder) WriteHeader(code int) { w.status = code w.ResponseWriter.WriteHeader(code) } func clientIP(r *http.Request) string { ip, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { return r.RemoteAddr } return ip } func ipAllowed(ip, allow string) bool { for _, part := range strings.Split(allow, ",") { if strings.TrimSpace(part) == ip { return true } } return false }