From 4ecd01bee59e220ac2908244bd0e291f9bf0f91a Mon Sep 17 00:00:00 2001 From: maoren <747293336.com> Date: Fri, 20 Mar 2026 11:42:27 +0000 Subject: [PATCH] feat: add client remote address display support - Add SEND_SERVER_REMOTE env var and X-Send-Server-Remote heade r - Add RemoteAddr, RemoteIP, RemotePort to echo response - Make WebSocket UI responsive for mobile devices --- README.md | 34 +++++++++++++ cmd/echo-server/html/frontend.tmpl.html | 66 +++++++++++++++++++++++++ cmd/echo-server/main.go | 45 +++++++++++++---- 3 files changed, 136 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 798b509..3c2c414 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,23 @@ information about HTTP request headers and bodies back to the client. The `PORT` environment variable sets the server port, which defaults to `8080`. +### SEND_SERVER_REMOTE + +The `SEND_SERVER_REMOTE` variable set remote address display or + +```bash +curl -H '' -H 'X-Send-Server-Remote: true' http://dnsname.domain +``` + +Add RemoteAddr, RemoteIP, RemotePort to echo response + +``` +RemoteAddr: xx:yy +RemoteIP: xx +RemotePort: yy +``` + + ### Logging Set the `LOG_HTTP_HEADERS` environment variable to print request headers to @@ -62,6 +79,23 @@ go get -u github.com/jmalloc/echo-server/... PORT=10000 echo-server ``` +``` +export GOPROXY=https://goproxy.cn,direct && SEND_SERVER_REMOTE=false PORT=10001 go run cmd/echo-server/main.go +``` + +## build and package + +```bash +go build -o artifacts/echo-server ./cmd/echo-server +# build +GOOS=linux GOARCH=amd64 go build -o artifacts/echo-server_linux_amd64 ./cmd/echo-server +GOOS=linux GOARCH=arm64 go build -o artifacts/echo-server_linux_arm64 ./cmd/echo-server +GOOS=darwin GOARCH=amd64 go build -o artifacts/echo-server_mac_x86_64 ./cmd/echo-server +GOOS=darwin GOARCH=arm64 go build -o artifacts/echo-server_mac_arm64 ./cmd/echo-server +GOOS=windows GOARCH=amd64 go build -o artifacts/echo-server_windows_amd64 ./cmd/echo-server +ls -lah artifacts/* +``` + ### Running under Docker To run the latest version as a container: diff --git a/cmd/echo-server/html/frontend.tmpl.html b/cmd/echo-server/html/frontend.tmpl.html index 657f239..5e108fe 100644 --- a/cmd/echo-server/html/frontend.tmpl.html +++ b/cmd/echo-server/html/frontend.tmpl.html @@ -1,6 +1,7 @@ websocket + diff --git a/cmd/echo-server/main.go b/cmd/echo-server/main.go index 16596e8..a800a4b 100644 --- a/cmd/echo-server/main.go +++ b/cmd/echo-server/main.go @@ -89,6 +89,16 @@ func handler(wr http.ResponseWriter, req *http.Request) { "false", ) + sendServerRemoteString := os.Getenv("SEND_SERVER_REMOTE") + if v := req.Header.Get("X-Send-Server-Remote"); v != "" { + sendServerRemoteString = v + } + + sendServerRemote := strings.EqualFold( + sendServerRemoteString, + "true", + ) + for _, line := range os.Environ() { parts := strings.SplitN(line, "=", 2) key, value := parts[0], parts[1] @@ -102,17 +112,17 @@ func handler(wr http.ResponseWriter, req *http.Request) { } if websocket.IsWebSocketUpgrade(req) { - serveWebSocket(wr, req, sendServerHostname) + serveWebSocket(wr, req, sendServerHostname, sendServerRemote) } else if path.Base(req.URL.Path) == ".ws" { serveFrontend(wr, req) } else if path.Base(req.URL.Path) == ".sse" { - serveSSE(wr, req, sendServerHostname) + serveSSE(wr, req, sendServerHostname, sendServerRemote) } else { - serveHTTP(wr, req, sendServerHostname) + serveHTTP(wr, req, sendServerHostname, sendServerRemote) } } -func serveWebSocket(wr http.ResponseWriter, req *http.Request, sendServerHostname bool) { +func serveWebSocket(wr http.ResponseWriter, req *http.Request, sendServerHostname bool, sendServerRemote bool) { connection, err := upgrader.Upgrade(wr, req, nil) if err != nil { fmt.Printf("%s | %s\n", req.RemoteAddr, err) @@ -188,7 +198,7 @@ func serveFrontend(wr http.ResponseWriter, req *http.Request) { wr.WriteHeader(200) } -func serveHTTP(wr http.ResponseWriter, req *http.Request, sendServerHostname bool) { +func serveHTTP(wr http.ResponseWriter, req *http.Request, sendServerHostname bool, sendServerRemote bool) { wr.Header().Add("Content-Type", "text/plain") wr.WriteHeader(200) @@ -201,17 +211,17 @@ func serveHTTP(wr http.ResponseWriter, req *http.Request, sendServerHostname boo } } - writeRequest(wr, req) + writeRequest(wr, req, sendServerRemote) } -func serveSSE(wr http.ResponseWriter, req *http.Request, sendServerHostname bool) { +func serveSSE(wr http.ResponseWriter, req *http.Request, sendServerHostname bool, sendServerRemote bool) { if _, ok := wr.(http.Flusher); !ok { http.Error(wr, "Streaming unsupported!", http.StatusInternalServerError) return } var echo strings.Builder - writeRequest(&echo, req) + writeRequest(&echo, req, sendServerRemote) wr.Header().Set("Content-Type", "text/event-stream") wr.Header().Set("Cache-Control", "no-cache") @@ -290,11 +300,28 @@ func writeSSEField( } // writeRequest writes request headers to w. -func writeRequest(w io.Writer, req *http.Request) { +func writeRequest(w io.Writer, req *http.Request, sendServerRemote bool) { fmt.Fprintf(w, "%s %s %s\n", req.Method, req.URL, req.Proto) fmt.Fprintln(w, "") fmt.Fprintf(w, "Host: %s\n", req.Host) + + if sendServerRemote { + fmt.Fprintf(w, "RemoteAddr: %s\n", req.RemoteAddr) + + // 解析IP地址和端口 + ip := req.RemoteAddr + port := "" + if idx := strings.LastIndex(req.RemoteAddr, ":"); idx != -1 { + ip = req.RemoteAddr[:idx] + port = req.RemoteAddr[idx+1:] + // 移除IPv6地址的方括号 + ip = strings.TrimPrefix(strings.TrimSuffix(ip, "]"), "[") + } + fmt.Fprintf(w, "RemoteIP: %s\n", ip) + fmt.Fprintf(w, "RemotePort: %s\n", port) + } + printHeaders(w, req.Header) var body bytes.Buffer