Files
wehub-resource-sync e0e362d700
SDK Tests / SDK CI (push) Blocked by required conditions
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
SDK Tests / Python SDK Tests (sandbox) (push) Waiting to run
SDK Tests / CLI Quality (push) Waiting to run
SDK Tests / CLI Tests (push) Waiting to run
SDK Tests / Python SDK Quality (code-interpreter) (push) Waiting to run
SDK Tests / Python SDK Quality (sandbox) (push) Waiting to run
SDK Tests / Python SDK Tests (code-interpreter) (push) Waiting to run
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Waiting to run
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Waiting to run
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Waiting to run
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Waiting to run
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Waiting to run
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Waiting to run
SDK Tests / Go SDK Quality And Tests (push) Waiting to run
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:33 +08:00

119 lines
3.1 KiB
Go

// Copyright 2025 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package web
import (
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/alibaba/opensandbox/execd/pkg/log"
)
func ProxyMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if !strings.HasPrefix(c.Request.URL.Path, "/proxy/") {
c.Next()
return
}
r := c.Request
w := c.Writer
rest := strings.TrimPrefix(r.URL.Path, "/proxy/")
parts := strings.SplitN(rest, "/", 2)
if len(parts) == 0 || parts[0] == "" {
http.Error(w, "port is required", http.StatusBadRequest)
c.Abort()
return
}
port := parts[0]
path := "/"
if len(parts) == 2 && parts[1] != "" {
path += parts[1]
}
target := &url.URL{
Scheme: "http",
Host: "127.0.0.1:" + port,
Path: path,
}
isWebSocket := strings.ToLower(r.Header.Get("Upgrade")) == "websocket"
proxy := httputil.NewSingleHostReverseProxy(target)
// Flush SSE chunks promptly; a small interval avoids buffering breaks chunked streams.
proxy.FlushInterval = 200 * time.Millisecond
proxy.Director = func(req *http.Request) {
req.URL.Scheme = "http"
req.URL.Host = "127.0.0.1:" + port
req.URL.Path = path
req.URL.RawQuery = r.URL.RawQuery
req.URL.RawPath = ""
req.RequestURI = ""
req.Header.Set("X-Forwarded-For", getClientIP(r))
req.Header.Set("X-Forwarded-Proto", "http")
req.Header.Del("X-Forwarded-Host")
if isWebSocket {
req.Header.Set("Connection", "Upgrade")
req.Header.Set("Upgrade", "websocket")
req.Header.Set("Sec-WebSocket-Version", "13")
if key := r.Header.Get("Sec-WebSocket-Key"); key != "" {
req.Header.Set("Sec-WebSocket-Key", key)
}
}
}
proxy.Transport = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 600 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 600 * time.Second,
}
proxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, err error) {
log.Error("Proxy error: %v, request: %s %s", err, req.Method, req.RequestURI)
http.Error(rw, "Bad Gateway", http.StatusBadGateway)
}
log.Info("Proxy: %s %s -> %s (WebSocket: %v)", r.Method, r.RequestURI, target.Host, isWebSocket)
proxy.ServeHTTP(w, r)
c.Abort()
}
}
func getClientIP(r *http.Request) string {
if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
return strings.Split(ip, ",")[0]
}
if ip := r.Header.Get("X-Real-IP"); ip != "" {
return ip
}
return r.RemoteAddr
}