I am trying to make an application that periodically sends data to a client's browser, unitl the client's browser closes. My problem is, that I am unable to detect client closing the browser.
I have following code writen in GO (minimal code where the problem is pressent):
import (
"fmt"
"net/http"
"time"
)
func test(w http.ResponseWriter, r *http.Request) {
f, f_err := w.(http.Flusher)
if !f_err {
fmt.Printf("%s\n", "flush error")
return
}
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Content-Type", "text/event-stream")
w.WriteHeader(http.StatusOK)
for timeout := 0; timeout < 1000; timeout++ {
writ, err := w.Write([]byte("data: {\"result\": \"success\"}\n\n"))
f.Flush()
fmt.Printf("%d %d %s\n", timeout, writ, err)
time.Sleep(time.Duration(1) * time.Second)
}
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
test(w, r)
})
srv := &http.Server{
Addr: ":3001",
Handler: mux,
}
err := srv.ListenAndServe()
if err != nil {
panic(err)
}
}
The code above is my server and I connect to this server via ssh tunel (from port 3001 to port 3001) with:
$ curl localhost:3001/test
When I kill the curl, the server is still trying to send data. This results in server overload after some time.
This is what is happening on client and server:
Server:
$ ./test
0 29 %!s(<nil>)
1 29 %!s(<nil>)
2 29 %!s(<nil>)
3 29 %!s(<nil>)
4 29 %!s(<nil>)
5 29 %!s(<nil>)
6 29 %!s(<nil>)
7 29 %!s(<nil>)
...
...
...
Client:
$ curl localhost:3001/test
data: {"result": "success"}
data: {"result": "success"}
data: {"result": "success"}
data: {"result": "success"}
^C
What I want to achive is the ability to detect kill of curl
.
When I try to run and later kill curl
on same machine where server is running, I end up with:
Server:
$ ./test
0 29 %!s(<nil>)
1 29 %!s(<nil>)
2 29 %!s(<nil>)
3 29 %!s(<nil>)
4 29 %!s(<nil>)
5 29 %!s(<nil>)
6 29 %!s(<nil>)
7 0 write tcp 127.0.0.1:3001->127.0.0.1:51058: write: broken pipe
8 0 write tcp 127.0.0.1:3001->127.0.0.1:51058: write: broken pipe
9 0 write tcp 127.0.0.1:3001->127.0.0.1:51058: write: broken pipe
10 0 write tcp 127.0.0.1:3001->127.0.0.1:51058: write: broken pipe
11 0 write tcp 127.0.0.1:3001->127.0.0.1:51058: write: broken pipe
12 0 write tcp 127.0.0.1:3001->127.0.0.1:51058: write: broken pipe
13 0 write tcp 127.0.0.1:3001->127.0.0.1:51058: write: broken pipe
...
...
...
Client:
$ curl localhost:3001/test
data: {"result": "success"}
data: {"result": "success"}
data: {"result": "success"}
data: {"result": "success"}
^C
This is exactly somethnig I would like to have, when I am using the ssh tunnel.
According to this article (Section About streaming). Go net/http
cannot detect ungraceful disconnects. Therefore, connection over SSH tunnel, lost wifi, unplugged cable, browser crash, etc., cannot be detected with net/http
.
Note: This may change in the future since underlying net.Conn
is capable of detecting these types of error.
UPDATE: I just retried the experiment and it now works. You get error write: broken pipe
.