Files
kandev/apps/backend/internal/agentctl/server/api/vscode_proxy.go
T

88 lines
2.5 KiB
Go

package api
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// handleVscodeProxy reverse-proxies requests to the local code-server process.
// It assumes VS Code is running according to procMgr status and routes all paths
// under /api/v1/vscode/proxy/*path to code-server root.
func (s *Server) handleVscodeProxy(c *gin.Context) {
info := s.procMgr.VscodeInfo()
incomingPath := c.Request.URL.Path
s.logger.Debug("vscode proxy request received",
zap.String("method", c.Request.Method),
zap.String("incoming_path", incomingPath),
zap.String("status", string(info.Status)),
zap.Int("port", info.Port))
if info.Status != "running" || info.Port == 0 {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "code-server is not running"})
return
}
target, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", info.Port))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid vscode target"})
return
}
proxy := &httputil.ReverseProxy{}
proxy.Rewrite = func(r *httputil.ProxyRequest) {
r.SetURL(target)
// Strip /api/v1/vscode/proxy prefix and forward remaining path.
path := strings.TrimPrefix(r.Out.URL.Path, "/api/v1/vscode/proxy")
if path == "" {
path = "/"
}
s.logger.Debug("rewrote vscode proxy path",
zap.String("incoming_path", r.In.URL.Path),
zap.String("forwarded_path", path))
r.Out.URL.Path = path
r.Out.URL.RawPath = ""
// Rewrite Origin to match code-server's host so ensureOrigin check passes.
r.Out.Header.Set("Origin", target.String())
if r.Out.Header.Get("Upgrade") != "" {
r.Out.Header.Set("Connection", "Upgrade")
}
}
proxy.ModifyResponse = func(resp *http.Response) error {
s.logger.Debug("vscode upstream response",
zap.Int("status", resp.StatusCode),
zap.String("request_path", resp.Request.URL.Path))
if resp.StatusCode == http.StatusSwitchingProtocols {
resp.Header.Set("Connection", "Upgrade")
}
return nil
}
proxy.ErrorHandler = func(w http.ResponseWriter, _ *http.Request, proxyErr error) {
s.logger.Warn("vscode proxy upstream error",
zap.Int("port", info.Port),
zap.Error(proxyErr))
http.Error(w, "code-server proxy error", http.StatusBadGateway)
}
defer func() {
if r := recover(); r != nil {
if r == http.ErrAbortHandler {
s.logger.Debug("vscode proxy: client disconnected")
return
}
panic(r)
}
}()
request, releaseFencing := credentialScopedRequest(c)
defer releaseFencing()
proxy.ServeHTTP(c.Writer, request)
}