diff --git a/src/server/webapp/handlers.go b/src/server/webapp/handlers.go
index 2fee4ca..8a97db6 100644
--- a/src/server/webapp/handlers.go
+++ b/src/server/webapp/handlers.go
@@ -14,6 +14,10 @@ import (
"slices"
"time"
+ "io"
+ "bytes"
+ "fmt"
+
"gontrol/src/logger"
"gontrol/src/server/api"
)
@@ -173,60 +177,240 @@ func (a *App) getAgentIds(w http.ResponseWriter, r *http.Request) {
return
}
-// proxyAgentHandler tunnels HTTP and WebSocket traffic to an agent
-// selected by ?ip=…&port=… . It keeps the path *after* /proxyAgent,
-// removes the two query parameters, disables HTTP/2 (mandatory for WS),
-// and streams with no buffering.
-func (a *App) proxyAgentHandler(w http.ResponseWriter, r *http.Request) {
- ip := r.URL.Query().Get("ip")
- port := r.URL.Query().Get("port")
- if ip == "" || port == "" {
- http.Error(w, "ip and port query parameters are required", http.StatusBadRequest)
- return
- }
+// func readCookie(r *http.Request, name string) (string, bool){
+// c, err := r.Cookie(name)
+// if err != nil { return "", false }
+// v, _ := url.QueryUnescape(c.Value)
+// return v, true
+// }
- // We leave the scheme "http" even for WebSockets – the Upgrade
- // header does the rest. (Only cosmetic to change it to "ws”.)
- target, err := url.Parse("http://" + ip + ":" + port)
- if err != nil {
- http.Error(w, "invalid ip/port: "+err.Error(), http.StatusBadRequest)
- return
- }
+// func setCookie(h http.Header, name, val string) {
+// h.Add("Set-Cookie", (&http.Cookie{
+// Name: name,
+// Value: url.QueryEscape(val),
+// Path: "/proxyAgent",
+// SameSite: http.SameSiteLaxMode,
+// }).String())
+// }
- proxy := &httputil.ReverseProxy{
- Director: func(req *http.Request) {
- // Point to the agent
- req.URL.Scheme = target.Scheme
- req.URL.Host = target.Host
+// // proxyAgentHandler tunnels HTTP and WebSocket traffic to an agent
+// // selected by ?ip=…&port=… . It keeps the path *after* /proxyAgent,
+// // removes the two query parameters, disables HTTP/2 (mandatory for WS),
+// // and streams with no buffering.
+// func (a *App) proxyAgentHandler(w http.ResponseWriter, r *http.Request) {
+// ip := r.URL.Query().Get("ip")
+// port := r.URL.Query().Get("port")
+// if ip == "" || port == "" {
+// http.Error(w, "ip and port query parameters are required", http.StatusBadRequest)
+// return
+// }
- // Trim the first "/proxyAgent" prefix
- if strings.HasPrefix(req.URL.Path, "/proxyAgent") {
- req.URL.Path = strings.TrimPrefix(req.URL.Path, "/proxyAgent")
- if req.URL.Path == "" {
- req.URL.Path = "/"
- }
- }
+// // We leave the scheme "http" even for WebSockets – the Upgrade
+// // header does the rest. (Only cosmetic to change it to "ws”.)
+// target, err := url.Parse("http://" + ip + ":" + port)
+// if err != nil {
+// http.Error(w, "invalid ip/port: "+err.Error(), http.StatusBadRequest)
+// return
+// }
- // Scrub ip/port from downstream query
- q := req.URL.Query()
- q.Del("ip")
- q.Del("port")
- req.URL.RawQuery = q.Encode()
+// proxy := &httputil.ReverseProxy{
+// Director: func(req *http.Request) {
+// // Point to the agent
+// req.URL.Scheme = target.Scheme
+// req.URL.Host = target.Host
- // Preserve Host (many CLI-style servers care)
- req.Host = target.Host
- },
+// // Trim the first "/proxyAgent" prefix
+// if strings.HasPrefix(req.URL.Path, "/proxyAgent") {
+// req.URL.Path = strings.TrimPrefix(req.URL.Path, "/proxyAgent")
+// if req.URL.Path == "" {
+// req.URL.Path = "/"
+// }
+// }
- // Critical tweaks for WebSockets
- Transport: &http.Transport{
- Proxy: http.ProxyFromEnvironment,
- DialContext: (&net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}).DialContext,
- ForceAttemptHTTP2: false, // MUST be HTTP/1.1 for WS
- ResponseHeaderTimeout: 0,
- },
+// // Scrub ip/port from downstream query
+// q := req.URL.Query()
+// q.Del("ip")
+// q.Del("port")
+// req.URL.RawQuery = q.Encode()
- FlushInterval: -1, // stream bytes immediately
- }
+// // Preserve Host (many CLI-style servers care)
+// req.Host = target.Host
+// },
- proxy.ServeHTTP(w, r)
+// // Critical tweaks for WebSockets
+// Transport: &http.Transport{
+// Proxy: http.ProxyFromEnvironment,
+// DialContext: (&net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}).DialContext,
+// ForceAttemptHTTP2: false, // MUST be HTTP/1.1 for WS
+// ResponseHeaderTimeout: 0,
+// },
+
+// FlushInterval: -1, // stream bytes immediately
+// }
+
+// proxy.ServeHTTP(w, r)
+// }
+
+// Registration (keep the slash so sub‑paths are included):
+// webMux.HandleFunc("/proxyAgent/", app.proxyAgentHandler)
+
+//func (app *App) proxyAgentHandler(w http.ResponseWriter, r *http.Request) {
+
+// //------------------------------------------------------------------
+// // 0. Helpers for cookie handling
+// //------------------------------------------------------------------
+// readCookie := func(name string) (string, bool) {
+// c, err := r.Cookie(name)
+// if err != nil { return "", false }
+// v, _ := url.QueryUnescape(c.Value)
+// return v, true
+// }
+// writeCookie := func(h http.Header, name, val string) {
+// h.Add("Set-Cookie", (&http.Cookie{
+// Name: name,
+// Value: url.QueryEscape(val),
+// Path: "/proxyAgent", // cookie visible only here
+// SameSite: http.SameSiteLaxMode,
+// }).String())
+// }
+
+// //------------------------------------------------------------------
+// // 1. Decide which agent to talk to
+// //------------------------------------------------------------------
+// ip, port := r.URL.Query().Get("ip"), r.URL.Query().Get("port")
+// // fallback to cookies if query params are absent
+// if ip == "" || port == "" {
+// if v, ok := readCookie("proxyagent_ip"); ok { ip = v }
+// if v, ok := readCookie("proxyagent_port"); ok { port = v }
+// }
+// if ip == "" || port == "" {
+// http.Error(w, "ip and port query parameters are required", http.StatusBadRequest)
+// return
+// }
+
+// target, err := url.Parse("http://" + ip + ":" + port)
+// if err != nil {
+// http.Error(w, "invalid ip/port: "+err.Error(), http.StatusBadRequest)
+// return
+// }
+
+// // keep copies for ModifyResponse (Director will delete them)
+// ipForCookie, portForCookie := ip, port
+
+// //------------------------------------------------------------------
+// // 2. Reverse proxy
+// //------------------------------------------------------------------
+// proxy := &httputil.ReverseProxy{
+
+// Director: func(req *http.Request) {
+// // 2.1 upstream address
+// req.URL.Scheme = target.Scheme
+// req.URL.Host = target.Host
+
+// // 2.2 strip ONE leading /proxyAgent
+// if strings.HasPrefix(req.URL.Path, "/proxyAgent") {
+// req.URL.Path = strings.TrimPrefix(req.URL.Path, "/proxyAgent")
+// if req.URL.Path == "" { req.URL.Path = "/" }
+// }
+
+// // 2.3 remove ip/port from outgoing query
+// q := req.URL.Query()
+// q.Del("ip"); q.Del("port")
+// req.URL.RawQuery = q.Encode()
+
+// // 2.4 keep original Host header
+// req.Host = target.Host
+
+// // 2.5 force uncompressed HTML so we can patch it
+// req.Header.Del("Accept-Encoding")
+// },
+
+// Transport: &http.Transport{
+// Proxy: http.ProxyFromEnvironment,
+// DialContext: (&net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}).DialContext,
+// ForceAttemptHTTP2: false, // must stay on h1 for WebSocket upgrade
+// ResponseHeaderTimeout: 0,
+// },
+
+// FlushInterval: -1, // instant streaming for xterm.js
+
+// //------------------------------------------------------------------
+// // 3. Response rewrite & cookie injection
+// //------------------------------------------------------------------
+// ModifyResponse: func(resp *http.Response) error {
+
+// // 3.1 always (once per agent) set the cookies
+// writeCookie(resp.Header, "proxyagent_ip", ipForCookie)
+// writeCookie(resp.Header, "proxyagent_port", portForCookie)
+
+// // 3.2 if the payload is HTML, inject
Hostname: {{.HostName}}
Connect via Proxy + + + + Connect without Proxy