Go websocket error: close 1006 (abnormal closure): unexpected EOF - go

I'm trying to write a simple go websocket server with gorilla/websocket
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
if os.Getenv("env") == "development" {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("Websocket error: %s", err)
return
}
defer conn.Close()
// Register client
clients[conn] = true
for {
message := message.Message{}
_, msg, err := conn.ReadMessage()
if err != nil {
log.Printf("Websocket error: %s", err)
return
}
res, _ = json.Marshal(context.Game)
// Send to every client that is currently connected
for client := range clients {
err := client.WriteMessage(websocket.TextMessage, res)
if err != nil {
// Remove connection
client.Close()
delete(clients, client)
}
}
}
})
the _, msg, err := conn.ReadMessage() line is throwing an error and closing the websocket, but I'm not sure why.
The error is close 1006 (abnormal closure): unexpected EOF. How can I prevent this?

The error indicates that the peer closed the connection without sending a close message. The RFC calls this "abnormal closure", but the error is normal to receive.
Use IsUnexpectedCloseError to filter out expected close errors. The Gorilla Chat Example shows how use the function (view code here).
The application in the question has a data race on clients as pointed out by Adrian. The Gorilla Chat Example shows how to maintain a map of clients without a data race (hub).

Related

Golang Gorilla Websocket Client Test Cases [duplicate]

I need to write unit test cases for Gorilla Web-socket.
var upgrader = websocket.Upgrader{} // use default options
func socketHandler(w http.ResponseWriter, r *http.Request) {
// Upgrade our raw HTTP connection to a websocket based one
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("Error during connection upgradation:", err)
return
}
defer conn.Close()
// The event loop
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("Error during message reading:", err)
break
}
log.Printf("Received: %s", message)
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println("Error during message writing:", err)
break
}
}
}
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Index Page")
}
func main() {
http.HandleFunc("/socket", socketHandler)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
I want to write the Unit Test case for socketHandler function. How should I go about it?
Spin up an httptest.Server with the websocket endpoint. Dial the server with the websocket client. Send and receive messages as needed.
Here's a simple example:
func TestWs(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(socketHandler))
defer s.Close()
wsURL := "ws" + strings.TrimPrefix(s.URL, "http")
c, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
if err != nil {
t.Error(err)
}
defer c.Close()
// Write a message.
c.WriteMessage(websocket.TextMessage, []byte("hello"))
// Expect the server to echo the message back.
c.SetReadDeadline(time.Now().Add(time.Second * 2))
mt, msg, err := c.ReadMessage()
if err != nil {
t.Error(err)
}
if mt != websocket.TextMessage || string(msg) != "hello" {
t.Errorf("expected text hello, got %d: %s", mt, msg)
}
}
Run the example on the playground.
To test a client, write a server endpoint that sends as receives messages as needed for a test. Run your client code against the endpoint as above.

How to write Unit Test Cases for Gorilla Websocket

I need to write unit test cases for Gorilla Web-socket.
var upgrader = websocket.Upgrader{} // use default options
func socketHandler(w http.ResponseWriter, r *http.Request) {
// Upgrade our raw HTTP connection to a websocket based one
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("Error during connection upgradation:", err)
return
}
defer conn.Close()
// The event loop
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("Error during message reading:", err)
break
}
log.Printf("Received: %s", message)
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println("Error during message writing:", err)
break
}
}
}
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Index Page")
}
func main() {
http.HandleFunc("/socket", socketHandler)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
I want to write the Unit Test case for socketHandler function. How should I go about it?
Spin up an httptest.Server with the websocket endpoint. Dial the server with the websocket client. Send and receive messages as needed.
Here's a simple example:
func TestWs(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(socketHandler))
defer s.Close()
wsURL := "ws" + strings.TrimPrefix(s.URL, "http")
c, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
if err != nil {
t.Error(err)
}
defer c.Close()
// Write a message.
c.WriteMessage(websocket.TextMessage, []byte("hello"))
// Expect the server to echo the message back.
c.SetReadDeadline(time.Now().Add(time.Second * 2))
mt, msg, err := c.ReadMessage()
if err != nil {
t.Error(err)
}
if mt != websocket.TextMessage || string(msg) != "hello" {
t.Errorf("expected text hello, got %d: %s", mt, msg)
}
}
Run the example on the playground.
To test a client, write a server endpoint that sends as receives messages as needed for a test. Run your client code against the endpoint as above.

"context deadline exceeded" AFTER first client-to-server request & response

In a simple gRPC example, I connect to the server, request to send a text message, and in return a success message is sent. Once the server is live, the first client request is successful, with no errors.
However, after the first try, each subsequent request (identical to the first) return this error, and does not return a response as the results (the text message is still sent, but the generated ID is not sent back):
rpc error: code = DeadlineExceeded desc = context deadline exceeded
After debugging a little bit, I found that the error
func (c *messageSchedulerClient) SendText(ctx context.Context, in *TextRequest, opts ...grpc.CallOption) (*MessageReply, error) {
...
err := c.cc.Invoke(ctx, "/communication.MessageScheduler/SendText", in, out, opts...)
...
return nil, err
}
returns
rpc error: code = DeadlineExceeded desc = context deadline exceeded
Here is my client code:
func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
c := pb.NewMessageSchedulerClient(conn)
var r *pb.MessageReply
r, err = pbSendText(c, false)
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetId())
err = conn.Close()
if err != nil {
log.Printf("Connection Close Error: %v", err)
}
return
}
func pbSendText(c pb.MessageSchedulerClient, instant bool) (*pb.MessageReply, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second * 5)
minute := timestamppb.New(time.Now().Add(time.Second * 30))
r, err := c.SendText(ctx, &pb.TextRequest{})
if err != nil {
log.Printf("DEBUG MESSAGE: Error after c.SendText(ctx, in): %v", err)
}
cancel()
return r, err
}
and the server code is...
func (s *MessageServer) SendText(ctx context.Context, in *pb.TextRequest) (*pb.MessageReply, error) {
return &pb.MessageReply{Id: GeneratePublicId()}, nil
}
func GrpcServe() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterMessageSchedulerServer(s, &MessageServer{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
return
}
const Alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHJKMNOPQRSTUVWXYZ0123457"
// executes instantly, O(n)
func GeneratePublicId() string {
return id.NewWithAlphabet(Alphabet)
}
I've tried changing the context to .TODO from .Background. Doesn't work. I am SURE it's something so simple that I'm missing.
Again, the first time I execute the application from the client side, it works. But after the first client application execution, meaning application execution and beyond -- until I restart the gRPC Server, it gives me the error.
The GeneratePublicId function executes almost instantly, as it is O(n) and uses a random number function.

Synchronizing a Test Server During Tests

Summary: I'm running into a race condition during testing where my server is not reliably ready to serve requests before making client requests against it. How can I block only until the listener is ready, and still maintain composable public APIs without requiring users to BYO net.Listener?
We see the following error as the goroutine that spins up our (blocking) server in the background isn't listening before we call client.Do(req) in the TestRun test function.
--- FAIL: TestRun/Server_accepts_HTTP_requests (0.00s)
/home/matt/repos/admission-control/server_test.go:64: failed to make a request: Get https://127.0.0.1:37877: dial tcp 127.0.0.1:37877: connect: connection refused
I'm not using httptest.Server directly as I'm attempting to test the blocking & cancellation characteristics of my own server componenent.
I create an httptest.NewUnstartedServer, clone its *tls.Config into a new http.Server after starting it with StartTLS(), and then close it, before calling *AdmissionServer.Run(). This also has the benefit of giving me a *http.Client with the matching RootCAs configured.
Testing TLS is important here as the daemon this exposes lives in a TLS-only environment.
func newTestServer(ctx context.Context, t *testing.T) *httptest.Server {
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "OK")
})
testSrv := httptest.NewUnstartedServer(testHandler)
admissionServer, err := NewServer(nil, &noopLogger{})
if err != nil {
t.Fatalf("admission server creation failed: %s", err)
return nil
}
// We start the test server, copy its config out, and close it down so we can
// start our own server. This is because httptest.Server only generates a
// self-signed TLS config after starting it.
testSrv.StartTLS()
admissionServer.srv = &http.Server{
Addr: testSrv.Listener.Addr().String(),
Handler: testHandler,
TLSConfig: testSrv.TLS.Clone(),
}
testSrv.Close()
// We need a better synchronization primitive here that doesn't block
// but allows the underlying listener to be ready before
// serving client requests.
go func() {
if err := admissionServer.Run(ctx); err != nil {
t.Fatalf("server returned unexpectedly: %s", err)
}
}()
return testSrv
}
// Test that we can start a minimal AdmissionServer and handle a request.
func TestRun(t *testing.T) {
testSrv := newTestServer(context.TODO(), t)
t.Run("Server accepts HTTP requests", func(t *testing.T) {
client := testSrv.Client()
req, err := http.NewRequest(http.MethodGet, testSrv.URL, nil)
if err != nil {
t.Fatalf("request creation failed: %s", err)
}
resp, err := client.Do(req)
if err != nil {
t.Fatalf("failed to make a request: %s", err)
}
// Later sub-tests will test cancellation propagation, signal handling, etc.
For posterity, this is our composable Run function, that listens in a goroutine and then blocks on our cancellation & error channels in a for-select:
type AdmissionServer struct {
srv *http.Server
logger log.Logger
GracePeriod time.Duration
}
func (as *AdmissionServer) Run(ctx context.Context) error {
sigChan := make(chan os.Signal, 1)
defer close(sigChan)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
// run in goroutine
errs := make(chan error)
defer close(errs)
go func() {
as.logger.Log(
"msg", fmt.Sprintf("admission control listening on '%s'", as.srv.Addr),
)
if err := as.srv.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
errs <- err
as.logger.Log(
"err", err.Error(),
"msg", "the server exited",
)
return
}
return
}()
// Block indefinitely until we receive an interrupt, cancellation or error
// signal.
for {
select {
case sig := <-sigChan:
as.logger.Log(
"msg", fmt.Sprintf("signal received: %s", sig),
)
return as.shutdown(ctx, as.GracePeriod)
case err := <-errs:
as.logger.Log(
"msg", fmt.Sprintf("listener error: %s", err),
)
// We don't need to explictly call shutdown here, as
// *http.Server.ListenAndServe closes the listener when returning an error.
return err
case <-ctx.Done():
as.logger.Log(
"msg", fmt.Sprintf("cancellation received: %s", ctx.Err()),
)
return as.shutdown(ctx, as.GracePeriod)
}
}
}
Notes:
There is a (simple) constructor for an *AdmissionServer: I've left it out for brevity. The AdmissionServer is composable and accepts a *http.Server so that it can be plugged into existing applications easily.
The wrapped http.Server type that we create a listener from doesn't itself expose any way to tell if its listening; at best we can try to listen again and catch the error (e.g. port already bound to another listener), which does not seem robust as the net package doesn't expose a useful typed error for this.
You can just attempt to connect to the server before starting the test suite, as part of the initialization process.
For example, I usually have a function like this in my tests:
// waitForServer attempts to establish a TCP connection to localhost:<port>
// in a given amount of time. It returns upon a successful connection;
// ptherwise exits with an error.
func waitForServer(port string) {
backoff := 50 * time.Millisecond
for i := 0; i < 10; i++ {
conn, err := net.DialTimeout("tcp", ":"+port, 1*time.Second)
if err != nil {
time.Sleep(backoff)
continue
}
err = conn.Close()
if err != nil {
log.Fatal(err)
}
return
}
log.Fatalf("Server on port %s not up after 10 attempts", port)
}
Then in my TestMain() I do:
func TestMain(m *testing.M) {
go startServer()
waitForServer(serverPort)
// run the suite
os.Exit(m.Run())
}

How to shut down a golang service safely after receiving parameters - Best practices

I have been implementing server using golang. I need to shutdown my server after receiving the expected parameter 'code'. Before shutting down the sever I need to redirect to a another web page. I have implemented as give below. This code is working. I need to know whether it is the best way of doing it ? Your suggestions are appreciated..
func main() {
var code string
const port int = 8888
httpPortString := ":" + strconv.Itoa(port)
mux := http.NewServeMux()
fmt.Printf("Http Server initialized on Port %s", httpPortString)
server := http.Server{Addr: httpPortString, Handler: mux}
var timer *time.Timer
mux.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
fmt.Printf("Error parsing the code: %s", err)
}
code = r.Form.Get("code")
if err != nil {
log.Printf("Error occurred while establishing the server: %s", err)
}
http.Redirect(w, r, "https://cloud.google.com/sdk/auth_success", http.StatusMovedPermanently)
timer = time.NewTimer(2 * time.Second)
go func() {
<-timer.C
server.Shutdown(context.Background())
}()
})
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Printf("Error while establishing the service: %s", err)
}
fmt.Println("Finished executing the the service")
}
Thank you ..!
Taking #Peter flushing suggestion and ideas from the example cited here:
f, ok := w.(http.Flusher)
if !ok {
http.Error(w, "no flush support", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "https://cloud.google.com/sdk/auth_success", http.StatusSeeOther)
f.Flush() // <-- ensures client gets all writes
// this is done implicitly on http handler returns, but...
// we're shutting down the server now!
go func() {
server.Shutdown(context.Background())
close(idleConnsClosed)
}()
See full playground version for idleConnsClosed setup/cleanup: https://play.golang.org/p/UBmLfyhKT0B
P.S. Don't use http.StatusMovedPermanently unless you really want users never to use the source URL again. Users' browsers will cache this (301) code - and not hit your server - which may not be what you want. If you want temporary redirects use http.StatusSeeOther (code 303).

Resources