From 918b887517ab8d46e929e1c31b8714e333298fae Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sat, 29 Mar 2025 13:29:02 +0900 Subject: [PATCH] renamed server_ctl to ServerCtl and capitalized the first letter of inner fields for exposure --- client-ctl.go | 4 +- client.go | 72 +++++++++++++++++------------- cmd/main.go | 2 +- server-ctl.go | 92 +++++++++++++++++++------------------- server-pxy.go | 59 ++++++++++++------------ server.go | 121 ++++++++++++++++++++++++++++++++------------------ 6 files changed, 199 insertions(+), 151 deletions(-) diff --git a/client-ctl.go b/client-ctl.go index 1fbd60d..8eb086b 100644 --- a/client-ctl.go +++ b/client-ctl.go @@ -180,7 +180,7 @@ type client_ctl_ws struct { // ------------------------------------ -func (ctl *client_ctl) Id() string { +func (ctl *client_ctl) Identity() string { return ctl.id } @@ -532,9 +532,9 @@ func (ctl *client_ctl_client_conns_id_routes) ServeHTTP(w http.ResponseWriter, r Id: jcr.RId, PeerAddr: jcr.ClientPeerAddr, PeerName: jcr.ClientPeerName, - Option: server_peer_option, ServiceAddr: jcr.ServerPeerSvcAddr, ServiceNet: jcr.ServerPeerSvcNet, + ServiceOption: server_peer_option, Lifetime: lifetime, Static: false, } diff --git a/client.go b/client.go index ca434cb..53e6538 100644 --- a/client.go +++ b/client.go @@ -35,13 +35,13 @@ type ClientPeerCancelFuncMap map[PeerId]context.CancelFunc // -------------------------------------------------------------------- type ClientRouteConfig struct { Id RouteId // requested id to be assigned. 0 for automatic assignment - PeerAddr string - PeerName string - Option RouteOption - ServiceAddr string // server-peer-svc-addr - ServiceNet string // server-peer-svc-net - Lifetime time.Duration - Static bool + PeerAddr string + PeerName string + ServiceAddr string // server-peer-svc-addr + ServiceNet string // server-peer-svc-net + ServiceOption RouteOption + Lifetime time.Duration + Static bool } type ClientConnConfig struct { @@ -202,7 +202,7 @@ type ClientRoute struct { PeerAddr string PeerName string - PeerOption RouteOption + PeerOption RouteOption // internally used in connecting ReqServerPeerSvcAddr string // requested server-side service address ReqServerPeerSvcNet string // requested server-side service address @@ -841,7 +841,7 @@ func (cts *ClientConn) AddNewClientRoute(rc *ClientRouteConfig) (*ClientRoute, e assigned_id = rc.Id } - r = NewClientRoute(cts, assigned_id, rc.Static, rc.PeerAddr, rc.PeerName, rc.ServiceAddr, rc.ServiceNet, rc.Option, rc.Lifetime) + r = NewClientRoute(cts, assigned_id, rc.Static, rc.PeerAddr, rc.PeerName, rc.ServiceAddr, rc.ServiceNet, rc.ServiceOption, rc.Lifetime) cts.route_map[r.Id] = r cts.C.stats.routes.Add(1) if cts.C.route_persister != nil { cts.C.route_persister.Save(cts, r) } @@ -1454,19 +1454,19 @@ func (hlw *client_ctl_log_writer) Write(p []byte) (n int, err error) { } type ClientHttpHandler interface { - Id() string + Identity() string Cors(req *http.Request) bool Authenticate(req *http.Request) (int, string) ServeHTTP (w http.ResponseWriter, req *http.Request) (int, error) } type ClientWebsocketHandler interface { - Id() string + Identity() string ServeWebsocket(ws *websocket.Conn) (int, error) } -func (c *Client) wrap_http_handler(handler ClientHttpHandler) http.Handler { +func (c *Client) WrapHttpHandler(handler ClientHttpHandler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { var status_code int var err error @@ -1512,9 +1512,9 @@ func (c *Client) wrap_http_handler(handler ClientHttpHandler) http.Handler { if status_code > 0 { if err != nil { - c.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s %d %.9f - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds(), err.Error()) + c.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s %d %.9f - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds(), err.Error()) } else { - c.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s %d %.9f", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds()) + c.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s %d %.9f", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds()) } } }) @@ -1529,7 +1529,7 @@ func (s *Client) WrapWebsocketHandler(handler ClientWebsocketHandler) websocket. var req *http.Request req = ws.Request() - s.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s [ws]", req.RemoteAddr, req.Method, req.URL.String()) + s.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s [ws]", req.RemoteAddr, req.Method, req.URL.String()) start_time = time.Now() status_code, err = handler.ServeWebsocket(ws) @@ -1537,9 +1537,9 @@ func (s *Client) WrapWebsocketHandler(handler ClientWebsocketHandler) websocket. if status_code > 0 { if err != nil { - s.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s [ws] %d %.9f - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds(), err.Error()) + s.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s [ws] %d %.9f - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds(), err.Error()) } else { - s.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s [ws] %d %.9f", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds()) + s.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s [ws] %d %.9f", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds()) } } }) @@ -1573,36 +1573,36 @@ func NewClient(ctx context.Context, name string, logger Logger, cfg *ClientConfi c.ctl_cors = cfg.CtlCors c.ctl_mux = http.NewServeMux() c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-conns", - c.wrap_http_handler(&client_ctl_client_conns{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_conns{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-conns/{conn_id}", - c.wrap_http_handler(&client_ctl_client_conns_id{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_conns_id{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-conns/{conn_id}/routes", - c.wrap_http_handler(&client_ctl_client_conns_id_routes{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_conns_id_routes{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-conns/{conn_id}/routes/{route_id}", - c.wrap_http_handler(&client_ctl_client_conns_id_routes_id{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_conns_id_routes_id{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-conns/{conn_id}/routes-spsp/{port_id}", - c.wrap_http_handler(&client_ctl_client_conns_id_routes_spsp{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_conns_id_routes_spsp{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-conns/{conn_id}/routes/{route_id}/peers", - c.wrap_http_handler(&client_ctl_client_conns_id_routes_id_peers{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_conns_id_routes_id_peers{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-conns/{conn_id}/routes/{route_id}/peers/{peer_id}", - c.wrap_http_handler(&client_ctl_client_conns_id_routes_id_peers_id{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_conns_id_routes_id_peers_id{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-conns/{conn_id}/peers", - c.wrap_http_handler(&client_ctl_client_conns_id_peers{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_conns_id_peers{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-routes", - c.wrap_http_handler(&client_ctl_client_routes{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_routes{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/client-peers", - c.wrap_http_handler(&client_ctl_client_peers{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_client_peers{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/notices", - c.wrap_http_handler(&client_ctl_notices{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_notices{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/notices/{conn_id}", - c.wrap_http_handler(&client_ctl_notices_id{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_notices_id{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/stats", - c.wrap_http_handler(&client_ctl_stats{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_stats{client_ctl{c: &c, id: HS_ID_CTL}})) c.ctl_mux.Handle(c.ctl_prefix + "/_ctl/token", - c.wrap_http_handler(&client_ctl_token{client_ctl{c: &c, id: HS_ID_CTL}})) + c.WrapHttpHandler(&client_ctl_token{client_ctl{c: &c, id: HS_ID_CTL}})) // TODO: make this optional. add this endpoint only if it's enabled... c.promreg = prometheus.NewRegistry() @@ -2068,6 +2068,16 @@ func (c *Client) SetRoutePersister(persister ClientRoutePersister) { c.route_persister = persister } +func (c *Client) AddCtlHandler(path string, handler ClientHttpHandler) { + // parked under /_ctl + c.ctl_mux.Handle(c.ctl_prefix + "/_ctl" + path, c.WrapHttpHandler(handler)) +} + +func (c *Client) AddCtlRootHandler(path string, handler ClientHttpHandler) { + // parked at the root level. must avoid conflicting path + c.ctl_mux.Handle(c.ctl_prefix + path, c.WrapHttpHandler(handler)) +} + func (c *Client) AddCtlMetricsCollector(col prometheus.Collector) error { return c.promreg.Register(col) } diff --git a/cmd/main.go b/cmd/main.go index 82ec653..d743be5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -240,7 +240,7 @@ func parse_client_route_config(v string) (*hodu.ClientRouteConfig, error) { ptc_name = strings.TrimSpace(va[3]) } - return &hodu.ClientRouteConfig{PeerAddr: va[0], PeerName: ptc_name, Option: option, ServiceAddr: svc_addr}, nil // TODO: other fields + return &hodu.ClientRouteConfig{PeerAddr: va[0], PeerName: ptc_name, ServiceOption: option, ServiceAddr: svc_addr}, nil // TODO: other fields } func client_main(ctl_addrs []string, rpc_addrs []string, route_configs []string, logfile string, cfg *ClientConfig) error { diff --git a/server-ctl.go b/server-ctl.go index a43e31f..627e2c2 100644 --- a/server-ctl.go +++ b/server-ctl.go @@ -86,81 +86,81 @@ type json_in_server_notice struct { // ------------------------------------ -type server_ctl struct { - s *Server - id string - noauth bool // override the auth configuration if true +type ServerCtl struct { + S *Server + Id string + NoAuth bool // override the auth configuration if true } type server_ctl_token struct { - server_ctl + ServerCtl } type server_ctl_server_conns struct { - server_ctl + ServerCtl } type server_ctl_server_conns_id struct { - server_ctl + ServerCtl } type server_ctl_server_conns_id_routes struct { - server_ctl + ServerCtl } type server_ctl_server_conns_id_routes_id struct { - server_ctl + ServerCtl } type server_ctl_server_conns_id_routes_id_peers struct { - server_ctl + ServerCtl } type server_ctl_server_conns_id_routes_id_peers_id struct { - server_ctl + ServerCtl } type server_ctl_server_conns_id_peers struct { - server_ctl + ServerCtl } type server_ctl_server_routes struct { - server_ctl + ServerCtl } type server_ctl_server_peers struct { - server_ctl + ServerCtl } type server_ctl_notices struct { - server_ctl + ServerCtl } type server_ctl_notices_id struct { - server_ctl + ServerCtl } type server_ctl_stats struct { - server_ctl + ServerCtl } type server_ctl_ws struct { - server_ctl + ServerCtl } // ------------------------------------ -func (ctl *server_ctl) Id() string { - return ctl.id +func (ctl *ServerCtl) Identity() string { + return ctl.Id } -func (ctl *server_ctl) Cors(req *http.Request) bool { - return ctl.s.Cfg.CtlCors +func (ctl *ServerCtl) Cors(req *http.Request) bool { + return ctl.S.Cfg.CtlCors } -func (ctl *server_ctl) Authenticate(req *http.Request) (int, string) { - if ctl.noauth || ctl.s.Cfg.CtlAuth == nil { return http.StatusOK, "" } - return ctl.s.Cfg.CtlAuth.Authenticate(req) +func (ctl *ServerCtl) Authenticate(req *http.Request) (int, string) { + if ctl.NoAuth || ctl.S.Cfg.CtlAuth == nil { return http.StatusOK, "" } + return ctl.S.Cfg.CtlAuth.Authenticate(req) } // ------------------------------------ @@ -171,7 +171,7 @@ func (ctl *server_ctl_token) ServeHTTP(w http.ResponseWriter, req *http.Request) var je *json.Encoder var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) switch req.Method { @@ -224,7 +224,7 @@ func (ctl *server_ctl_server_conns) ServeHTTP(w http.ResponseWriter, req *http.R var routes bool var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) q = req.URL.Query() @@ -305,7 +305,7 @@ func (ctl *server_ctl_server_conns_id) ServeHTTP(w http.ResponseWriter, req *htt var routes bool var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) q = req.URL.Query() @@ -385,7 +385,7 @@ func (ctl *server_ctl_server_conns_id_routes) ServeHTTP(w http.ResponseWriter, r var je *json.Encoder var cts *ServerConn - s = ctl.s + s = ctl.S je = json.NewEncoder(w) conn_id = req.PathValue("conn_id") @@ -452,10 +452,10 @@ func (ctl *server_ctl_server_conns_id_routes_id) ServeHTTP(w http.ResponseWriter var r *ServerRoute var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) - if ctl.id == HS_ID_WPX && req.Method != http.MethodGet { + if ctl.Id == HS_ID_WPX && req.Method != http.MethodGet { // support the get method only, if invoked via the wpx endpoint status_code = WriteEmptyRespHeader(w, http.StatusBadRequest) goto done @@ -466,23 +466,23 @@ func (ctl *server_ctl_server_conns_id_routes_id) ServeHTTP(w http.ResponseWriter r, err = s.FindServerRouteByIdStr(conn_id, route_id) if err != nil { /* - if route_id == PORT_ID_MARKER && ctl.s.wpx_foreign_port_proxy_marker != nil { + if route_id == PORT_ID_MARKER && ctl.S.wpx_foreign_port_proxy_marker != nil { // don't care if the ctl call is from wpx or not. if the request // is by the port number(noted by route being PORT_ID_MARKER), // check if it's a foreign port var pi *ServerRouteProxyInfo // currenly, this is invoked via wpx only for ssh from xterm.html // ugly, but hard-code the type to "ssh" here for now... - pi, err = ctl.s.wpx_foreign_port_proxy_maker("ssh", conn_id) + pi, err = ctl.S.wpx_foreign_port_proxy_maker("ssh", conn_id) if err == nil { r = proxy_info_to_server_route(pi) } // fake route } */ - if ctl.id == HS_ID_WPX && route_id == PORT_ID_MARKER && ctl.s.wpx_foreign_port_proxy_maker != nil { + if ctl.Id == HS_ID_WPX && route_id == PORT_ID_MARKER && ctl.S.wpx_foreign_port_proxy_maker != nil { var pi *ServerRouteProxyInfo // currenly, this is invoked via wpx only for ssh from xterm.html // ugly, but hard-code the type to "ssh" here for now... - pi, err = ctl.s.wpx_foreign_port_proxy_maker("ssh", conn_id) + pi, err = ctl.S.wpx_foreign_port_proxy_maker("ssh", conn_id) if err == nil { r = proxy_info_to_server_route(pi) } // fake route } } @@ -511,7 +511,7 @@ func (ctl *server_ctl_server_conns_id_routes_id) ServeHTTP(w http.ResponseWriter case http.MethodDelete: /*if r is foreign { // foreign route - ctl.s.wpx_foreign_port_proxy_stopper(conn_id) + ctl.S.wpx_foreign_port_proxy_stopper(conn_id) } else {*/ // native route r.ReqStop() @@ -540,7 +540,7 @@ func (ctl *server_ctl_server_conns_id_routes_id_peers) ServeHTTP(w http.Response var r *ServerRoute var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) conn_id = req.PathValue("conn_id") @@ -605,7 +605,7 @@ func (ctl *server_ctl_server_conns_id_routes_id_peers_id) ServeHTTP(w http.Respo var p *ServerPeerConn var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) conn_id = req.PathValue("conn_id") @@ -660,7 +660,7 @@ func (ctl *server_ctl_server_conns_id_peers) ServeHTTP(w http.ResponseWriter, re var je *json.Encoder var cts *ServerConn - s = ctl.s + s = ctl.S je = json.NewEncoder(w) conn_id = req.PathValue("conn_id") @@ -716,7 +716,7 @@ func (ctl *server_ctl_server_routes) ServeHTTP(w http.ResponseWriter, req *http. var je *json.Encoder var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) switch req.Method { @@ -763,7 +763,7 @@ func (ctl *server_ctl_server_peers) ServeHTTP(w http.ResponseWriter, req *http.R var je *json.Encoder var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) switch req.Method { @@ -811,7 +811,7 @@ func (ctl *server_ctl_notices) ServeHTTP(w http.ResponseWriter, req *http.Reques var je *json.Encoder var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) switch req.Method { @@ -853,7 +853,7 @@ func (ctl *server_ctl_notices_id) ServeHTTP(w http.ResponseWriter, req *http.Req var je *json.Encoder var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) conn_id = req.PathValue("conn_id") // server connection @@ -896,7 +896,7 @@ func (ctl *server_ctl_stats) ServeHTTP(w http.ResponseWriter, req *http.Request) var je *json.Encoder var err error - s = ctl.s + s = ctl.S je = json.NewEncoder(w) switch req.Method { @@ -931,11 +931,11 @@ func (ctl *server_ctl_ws) ServeWebsocket(ws *websocket.Conn) (int, error) { var err error var xerr error - s = ctl.s + s = ctl.S // handle authentication using the first message. // end this task if authentication fails. - if !ctl.noauth && s.Cfg.CtlAuth != nil { + if !ctl.NoAuth && s.Cfg.CtlAuth != nil { var req *http.Request req = ws.Request() diff --git a/server-pxy.go b/server-pxy.go index 18fab22..62f2217 100644 --- a/server-pxy.go +++ b/server-pxy.go @@ -31,8 +31,8 @@ var xterm_css []byte var xterm_html string type server_pxy struct { - s *Server - id string + S *Server + Id string } type server_pxy_http_main struct { @@ -186,8 +186,8 @@ func mutate_proxy_req_headers(req *http.Request, newreq *http.Request, path_pref // ------------------------------------ -func (pxy *server_pxy) Id() string { - return pxy.id +func (pxy *server_pxy) Identity() string { + return pxy.Id } func (pxy *server_pxy) Cors(req *http.Request) bool { @@ -205,6 +205,7 @@ func prevent_follow_redirect (req *http.Request, via []*http.Request) error { } func (pxy *server_pxy_http_main) get_route_proxy_info(req *http.Request, in_wpx_mode bool) (*ServerRouteProxyInfo, error) { + var s *Server var conn_id string var route_id string var r *ServerRoute @@ -212,6 +213,8 @@ func (pxy *server_pxy_http_main) get_route_proxy_info(req *http.Request, in_wpx_ var path_prefix string var err error + s = pxy.S + if in_wpx_mode { // for wpx conn_id = req.PathValue("port_id") route_id = pxy.prefix // this is PORT_ID_MARKER @@ -225,12 +228,12 @@ func (pxy *server_pxy_http_main) get_route_proxy_info(req *http.Request, in_wpx_ path_prefix = fmt.Sprintf("%s/%s/%s", pxy.prefix, conn_id, route_id) } - r, err = pxy.s.FindServerRouteByIdStr(conn_id, route_id) + r, err = s.FindServerRouteByIdStr(conn_id, route_id) if err != nil { - if !in_wpx_mode || pxy.s.wpx_foreign_port_proxy_maker == nil { return nil, err } + if !in_wpx_mode || s.wpx_foreign_port_proxy_maker == nil { return nil, err } // call this callback only in the wpx mode - pi, err = pxy.s.wpx_foreign_port_proxy_maker("http", conn_id) + pi, err = s.wpx_foreign_port_proxy_maker("http", conn_id) if err != nil { return nil, err } pi.IsForeign = true // just to ensure this } else { @@ -304,7 +307,7 @@ func (pxy *server_pxy_http_main) addr_to_transport (ctx context.Context, addr *n // establish the connection. dialer = &net.Dialer{} - waitctx, cancel_wait = context.WithTimeout(ctx, 3 * time.Second) // TODO: make timeout configurable + waitctx, cancel_wait = context.WithTimeout(ctx, 5 * time.Second) // TODO: make timeout configurable conn, err = dialer.DialContext(waitctx, TcpAddrClass(addr), addr.String()) cancel_wait() if err != nil { return nil, err } @@ -357,7 +360,7 @@ func (pxy *server_pxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Requ var upgrade_required bool var err error - s = pxy.s + s = pxy.S in_wpx_mode = (pxy.prefix == PORT_ID_MARKER) pi, err = pxy.get_route_proxy_info(req, in_wpx_mode) @@ -381,7 +384,7 @@ func (pxy *server_pxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Requ } proxy_url = pxy.req_to_proxy_url(req, pi) - s.log.Write(pxy.id, LOG_INFO, "[%s] %s %s -> %+v", req.RemoteAddr, req.Method, req.URL.String(), proxy_url) + s.log.Write(pxy.Id, LOG_INFO, "[%s] %s %s -> %+v", req.RemoteAddr, req.Method, req.URL.String(), proxy_url) proxy_req, err = http.NewRequestWithContext(s.Ctx, req.Method, proxy_url.String(), req.Body) if err != nil { @@ -407,7 +410,7 @@ func (pxy *server_pxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Requ } else { status_code = resp.StatusCode if upgrade_required && resp.StatusCode == http.StatusSwitchingProtocols { - s.log.Write(pxy.id, LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) + s.log.Write(pxy.Id, LOG_INFO, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) err = pxy.serve_upgraded(w, req, resp) if err != nil { goto oops } return 0, nil// print the log mesage before calling serve_upgraded() and exit here @@ -432,7 +435,7 @@ func (pxy *server_pxy_http_main) ServeHTTP(w http.ResponseWriter, req *http.Requ _, err = io.Copy(w, resp_body) if err != nil { - s.log.Write(pxy.id, LOG_WARN, "[%s] %s %s %s", req.RemoteAddr, req.Method, req.URL.String(), err.Error()) + s.log.Write(pxy.Id, LOG_WARN, "[%s] %s %s %s", req.RemoteAddr, req.Method, req.URL.String(), err.Error()) } // TODO: handle trailers @@ -474,7 +477,7 @@ func (pxy *server_pxy_xterm_file) ServeHTTP(w http.ResponseWriter, req *http.Req var status_code int var err error - s = pxy.s + s = pxy.S switch pxy.file { case "xterm.js": @@ -493,12 +496,12 @@ func (pxy *server_pxy_xterm_file) ServeHTTP(w http.ResponseWriter, req *http.Req // this endpoint is registered for /_ssh/{conn_id}/{route_id}/ under pxy. // and for /_ssh/{port_id} under wpx. - if pxy.id == HS_ID_WPX { + if pxy.Id == HS_ID_WPX { conn_id = req.PathValue("port_id") route_id = PORT_ID_MARKER _, err = s.FindServerRouteByIdStr(conn_id, route_id) - if err != nil && pxy.s.wpx_foreign_port_proxy_maker != nil { - _, err = pxy.s.wpx_foreign_port_proxy_maker("ssh", conn_id) + if err != nil && s.wpx_foreign_port_proxy_maker != nil { + _, err = s.wpx_foreign_port_proxy_maker("ssh", conn_id) } } else { conn_id = req.PathValue("conn_id") @@ -555,9 +558,9 @@ oops: // ------------------------------------ type server_pxy_ssh_ws struct { - s *Server + S *Server ws *websocket.Conn - id string + Id string } type json_ssh_ws_event struct { @@ -565,8 +568,8 @@ type json_ssh_ws_event struct { Data []string `json:"data"` } -func (pxy *server_pxy_ssh_ws) Id() string { - return pxy.id +func (pxy *server_pxy_ssh_ws) Identity() string { + return pxy.Id } // TODO: put this task to sync group. @@ -664,16 +667,16 @@ func (pxy *server_pxy_ssh_ws) ServeWebsocket(ws *websocket.Conn) (int, error) { var connect_ssh_cancel Atom[context.CancelFunc] var err error - s = pxy.s + s = pxy.S req = ws.Request() conn_ready_chan = make(chan bool, 3) conn_id = req.PathValue("conn_id") route_id = req.PathValue("route_id") r, err = s.FindServerRouteByIdStr(conn_id, route_id) - if err != nil && route_id == PORT_ID_MARKER && pxy.s.wpx_foreign_port_proxy_maker != nil { + if err != nil && route_id == PORT_ID_MARKER && s.wpx_foreign_port_proxy_maker != nil { var pi *ServerRouteProxyInfo - pi, err = pxy.s.wpx_foreign_port_proxy_maker("ssh", conn_id) + pi, err = s.wpx_foreign_port_proxy_maker("ssh", conn_id) if err != nil { pxy.send_ws_data(ws, "error", err.Error()) goto done @@ -710,14 +713,14 @@ func (pxy *server_pxy_ssh_ws) ServeWebsocket(ws *websocket.Conn) (int, error) { n, err = out.Read(buf) if err != nil { if err != io.EOF { - s.log.Write(pxy.id, LOG_ERROR, "Read from SSH stdout error - %s", err.Error()) + s.log.Write(pxy.Id, LOG_ERROR, "Read from SSH stdout error - %s", err.Error()) } break } if n > 0 { err = pxy.send_ws_data(ws, "iov", string(buf[:n])) if err != nil { - s.log.Write(pxy.id, LOG_ERROR, "Failed to send to websocket - %s", err.Error()) + s.log.Write(pxy.Id, LOG_ERROR, "Failed to send to websocket - %s", err.Error()) break } } @@ -754,13 +757,13 @@ ws_recv_loop: defer wg.Done() c, sess, in, out, err = pxy.connect_ssh(connect_ssh_ctx, username, password, r) if err != nil { - s.log.Write(pxy.id, LOG_ERROR, "failed to connect ssh - %s", err.Error()) + s.log.Write(pxy.Id, LOG_ERROR, "failed to connect ssh - %s", err.Error()) pxy.send_ws_data(ws, "error", err.Error()) ws.Close() // dirty way to flag out the error } else { err = pxy.send_ws_data(ws, "status", "opened") if err != nil { - s.log.Write(pxy.id, LOG_ERROR, "Failed to write opened event to websocket - %s", err.Error()) + s.log.Write(pxy.Id, LOG_ERROR, "Failed to write opened event to websocket - %s", err.Error()) ws.Close() // dirty way to flag out the error } else { conn_ready_chan <- true @@ -792,7 +795,7 @@ ws_recv_loop: rows, _ = strconv.Atoi(ev.Data[0]) cols, _ = strconv.Atoi(ev.Data[1]) sess.WindowChange(rows, cols) - s.log.Write(pxy.id, LOG_DEBUG, "Resized terminal to %d,%d", rows, cols) + s.log.Write(pxy.Id, LOG_DEBUG, "Resized terminal to %d,%d", rows, cols) // ignore error } } diff --git a/server.go b/server.go index 121a16a..887ed8f 100644 --- a/server.go +++ b/server.go @@ -1153,14 +1153,14 @@ func (hlw *server_http_log_writer) Write(p []byte) (n int, err error) { } type ServerHttpHandler interface { - Id() string + Identity() string Cors(req *http.Request) bool Authenticate(req *http.Request) (int, string) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) } type ServerWebsocketHandler interface { - Id() string + Identity() string ServeWebsocket(ws *websocket.Conn) (int, error) } @@ -1208,9 +1208,9 @@ func (s *Server) WrapHttpHandler(handler ServerHttpHandler) http.Handler { if status_code > 0 { if err != nil { - s.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s %d %.9f - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds(), err.Error()) + s.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s %d %.9f - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds(), err.Error()) } else { - s.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s %d %.9f", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds()) + s.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s %d %.9f", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds()) } } }) @@ -1225,7 +1225,7 @@ func (s *Server) WrapWebsocketHandler(handler ServerWebsocketHandler) websocket. var req *http.Request req = ws.Request() - s.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s [ws]", req.RemoteAddr, req.Method, req.URL.String()) + s.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s [ws]", req.RemoteAddr, req.Method, req.URL.String()) start_time = time.Now() status_code, err = handler.ServeWebsocket(ws) @@ -1233,9 +1233,9 @@ func (s *Server) WrapWebsocketHandler(handler ServerWebsocketHandler) websocket. if status_code > 0 { if err != nil { - s.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s [ws] %d %.9f - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds(), err.Error()) + s.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s [ws] %d %.9f - %s", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds(), err.Error()) } else { - s.log.Write(handler.Id(), LOG_INFO, "[%s] %s %s [ws] %d %.9f", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds()) + s.log.Write(handler.Identity(), LOG_INFO, "[%s] %s %s [ws] %d %.9f", req.RemoteAddr, req.Method, req.URL.String(), status_code, time_taken.Seconds()) } } }) @@ -1305,31 +1305,31 @@ func NewServer(ctx context.Context, name string, logger Logger, cfg *ServerConfi s.ctl_mux = http.NewServeMux() s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/server-conns", - s.WrapHttpHandler(&server_ctl_server_conns{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_server_conns{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/server-conns/{conn_id}", - s.WrapHttpHandler(&server_ctl_server_conns_id{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_server_conns_id{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/server-conns/{conn_id}/routes", - s.WrapHttpHandler(&server_ctl_server_conns_id_routes{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_server_conns_id_routes{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/server-conns/{conn_id}/routes/{route_id}", - s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/server-conns/{conn_id}/routes/{route_id}/peers", - s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id_peers{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id_peers{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/server-conns/{conn_id}/routes/{route_id}/peers/{peer_id}", - s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id_peers_id{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id_peers_id{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/server-conns/{conn_id}/peers", - s.WrapHttpHandler(&server_ctl_server_conns_id_peers{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_server_conns_id_peers{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/server-routes", - s.WrapHttpHandler(&server_ctl_server_routes{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_server_routes{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/server-peers", - s.WrapHttpHandler(&server_ctl_server_peers{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_server_peers{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/notices", - s.WrapHttpHandler(&server_ctl_notices{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_notices{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/notices/{conn_id}", - s.WrapHttpHandler(&server_ctl_notices_id{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_notices_id{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/stats", - s.WrapHttpHandler(&server_ctl_stats{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_stats{ServerCtl{S: &s, Id: HS_ID_CTL}})) s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl/token", - s.WrapHttpHandler(&server_ctl_token{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapHttpHandler(&server_ctl_token{ServerCtl{S: &s, Id: HS_ID_CTL}})) // TODO: make this optional. add this endpoint only if it's enabled... s.promreg = prometheus.NewRegistry() @@ -1339,7 +1339,36 @@ func NewServer(ctx context.Context, name string, logger Logger, cfg *ServerConfi promhttp.HandlerFor(s.promreg, promhttp.HandlerOpts{ EnableOpenMetrics: true })) s.ctl_mux.Handle("/_ctl/events", - s.WrapWebsocketHandler(&server_ctl_ws{server_ctl{s: &s, id: HS_ID_CTL}})) + s.WrapWebsocketHandler(&server_ctl_ws{ServerCtl{S: &s, Id: HS_ID_CTL}})) + + // this part is duplcate of pxy_mux. + s.ctl_mux.Handle("/_ssh-ws/{conn_id}/{route_id}", + s.WrapWebsocketHandler(&server_pxy_ssh_ws{S: &s, Id: HS_ID_PXY_WS})) + s.ctl_mux.Handle("/_ssh/server-conns/{conn_id}/routes/{route_id}", + s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id{ServerCtl{S: &s, Id: HS_ID_CTL, NoAuth: true}})) + s.ctl_mux.Handle("/_ssh/xterm.js", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "xterm.js"})) + s.ctl_mux.Handle("/_ssh/xterm.js.map", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "_notfound"})) + s.ctl_mux.Handle("/_ssh/xterm-addon-fit.js", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "xterm-addon-fit.js"})) + s.ctl_mux.Handle("/_ssh/xterm-addon-fit.js.map", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "_notfound"})) + s.ctl_mux.Handle("/_ssh/xterm.css", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "xterm.css"})) + s.ctl_mux.Handle("/_ssh/{conn_id}/", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "_redirect"})) + s.ctl_mux.Handle("/_ssh/{conn_id}/{route_id}/", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "xterm.html"})) + s.ctl_mux.Handle("/_ssh/", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "_forbidden"})) + s.ctl_mux.Handle("/favicon.ico", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "_forbidden"})) + s.ctl_mux.Handle("/favicon.ico/", + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, file: "_forbidden"})) + + s.ctl_mux.Handle("/_http/{conn_id}/{route_id}/{trailer...}", + s.WrapHttpHandler(&server_pxy_http_main{server_pxy: server_pxy{S: &s, Id: HS_ID_CTL}, prefix: "/_http"})) s.ctl = make([]*http.Server, len(cfg.CtlAddrs)) for i = 0; i < len(cfg.CtlAddrs); i++ { @@ -1356,32 +1385,32 @@ func NewServer(ctx context.Context, name string, logger Logger, cfg *ServerConfi s.pxy_mux = http.NewServeMux() // TODO: make /_init,_ssh,_ssh_ws,_http configurable... s.pxy_mux.Handle("/_ssh-ws/{conn_id}/{route_id}", - s.WrapWebsocketHandler(&server_pxy_ssh_ws{s: &s, id: HS_ID_PXY_WS})) + s.WrapWebsocketHandler(&server_pxy_ssh_ws{S: &s, Id: HS_ID_PXY_WS})) s.pxy_mux.Handle("/_ssh/server-conns/{conn_id}/routes/{route_id}", - s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id{server_ctl{s: &s, id: HS_ID_PXY, noauth: true}})) + s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id{ServerCtl{S: &s, Id: HS_ID_PXY, NoAuth: true}})) s.pxy_mux.Handle("/_ssh/xterm.js", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "xterm.js"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "xterm.js"})) s.pxy_mux.Handle("/_ssh/xterm.js.map", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "_notfound"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "_notfound"})) s.pxy_mux.Handle("/_ssh/xterm-addon-fit.js", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "xterm-addon-fit.js"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "xterm-addon-fit.js"})) s.pxy_mux.Handle("/_ssh/xterm-addon-fit.js.map", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "_notfound"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "_notfound"})) s.pxy_mux.Handle("/_ssh/xterm.css", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "xterm.css"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "xterm.css"})) s.pxy_mux.Handle("/_ssh/{conn_id}/", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "_redirect"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "_redirect"})) s.pxy_mux.Handle("/_ssh/{conn_id}/{route_id}/", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "xterm.html"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "xterm.html"})) s.pxy_mux.Handle("/_ssh/", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "_forbidden"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "_forbidden"})) s.pxy_mux.Handle("/favicon.ico", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "_forbidden"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "_forbidden"})) s.pxy_mux.Handle("/favicon.ico/", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, file: "_forbidden"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, file: "_forbidden"})) s.pxy_mux.Handle("/_http/{conn_id}/{route_id}/{trailer...}", - s.WrapHttpHandler(&server_pxy_http_main{server_pxy: server_pxy{s: &s, id: HS_ID_PXY}, prefix: "/_http"})) + s.WrapHttpHandler(&server_pxy_http_main{server_pxy: server_pxy{S: &s, Id: HS_ID_PXY}, prefix: "/_http"})) s.pxy = make([]*http.Server, len(cfg.PxyAddrs)) @@ -1401,25 +1430,25 @@ func NewServer(ctx context.Context, name string, logger Logger, cfg *ServerConfi s.wpx_mux = http.NewServeMux() // TODO: make /_init,_ssh,_ssh_ws,_http configurable... s.wpx_mux.Handle("/_ssh-ws/{conn_id}/{route_id}", - s.WrapWebsocketHandler(&server_pxy_ssh_ws{s: &s, id: "wpx-ssh"})) + s.WrapWebsocketHandler(&server_pxy_ssh_ws{S: &s, Id: "wpx-ssh"})) s.wpx_mux.Handle("/_ssh/server-conns/{conn_id}/routes/{route_id}", - s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id{server_ctl{s: &s, id: HS_ID_WPX, noauth: true}})) + s.WrapHttpHandler(&server_ctl_server_conns_id_routes_id{ServerCtl{S: &s, Id: HS_ID_WPX, NoAuth: true}})) s.wpx_mux.Handle("/_ssh/xterm.js", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_WPX}, file: "xterm.js"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_WPX}, file: "xterm.js"})) s.wpx_mux.Handle("/_ssh/xterm-addon-fit.js", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_WPX}, file: "xterm-addon-fit.js"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_WPX}, file: "xterm-addon-fit.js"})) s.wpx_mux.Handle("/_ssh/xterm.css", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_WPX}, file: "xterm.css"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_WPX}, file: "xterm.css"})) s.wpx_mux.Handle("/_ssh/{port_id}", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_WPX}, file: "xterm.html"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_WPX}, file: "xterm.html"})) s.wpx_mux.Handle("/_ssh/", - s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{s: &s, id: HS_ID_WPX}, file: "_forbidden"})) + s.WrapHttpHandler(&server_pxy_xterm_file{server_pxy: server_pxy{S: &s, Id: HS_ID_WPX}, file: "_forbidden"})) s.wpx_mux.Handle("/{port_id}/{trailer...}", - s.WrapHttpHandler(&server_pxy_http_main{server_pxy: server_pxy{s: &s, id: HS_ID_WPX}, prefix: PORT_ID_MARKER})) + s.WrapHttpHandler(&server_pxy_http_main{server_pxy: server_pxy{S: &s, Id: HS_ID_WPX}, prefix: PORT_ID_MARKER})) s.wpx_mux.Handle("/", - s.WrapHttpHandler(&server_pxy_http_wpx{server_pxy: server_pxy{s: &s, id: HS_ID_WPX}})) + s.WrapHttpHandler(&server_pxy_http_wpx{server_pxy: server_pxy{S: &s, Id: HS_ID_WPX}})) s.wpx = make([]*http.Server, len(cfg.WpxAddrs)) @@ -2081,9 +2110,15 @@ func (s *Server) SetConnNoticeHandlers(handlers []ServerConnNoticeHandler) { } func (s *Server) AddCtlHandler(path string, handler ServerHttpHandler) { + // parked under /_ctl s.ctl_mux.Handle(s.Cfg.CtlPrefix + "/_ctl" + path, s.WrapHttpHandler(handler)) } +func (s *Server) AddCtlRootHandler(path string, handler ServerHttpHandler) { + // parked at the root level. must avoid conflicting path + s.ctl_mux.Handle(s.Cfg.CtlPrefix + path, s.WrapHttpHandler(handler)) +} + func (s *Server) AddCtlMetricsCollector(col prometheus.Collector) error { return s.promreg.Register(col) }