e75c8b37c8
Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com> Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
88 lines
2.5 KiB
Go
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)
|
|
}
|