diff --git a/conn.go b/conn.go index 7afb796..80f60c4 100644 --- a/conn.go +++ b/conn.go @@ -115,6 +115,20 @@ func IsCloseError(err error, codes ...int) bool { return false } +// IsUnexpectedCloseError returns boolean indicating whether the error is a +// *CloseError with a code not in the list of expected codes. +func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { + if e, ok := err.(*CloseError); ok { + for _, code := range expectedCodes { + if e.Code == code { + return false + } + } + return true + } + return false +} + var ( errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} @@ -321,9 +335,6 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er // // There can be at most one open writer on a connection. NextWriter closes the // previous writer if the application has not already done so. -// -// The NextWriter method and the writers returned from the method cannot be -// accessed by more than one goroutine at a time. func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { if c.writeErr != nil { return nil, c.writeErr @@ -708,7 +719,8 @@ func (c *Conn) handleProtocolError(message string) error { // the previous message if the application has not already consumed it. // // Errors returned from NextReader are permanent. If NextReader returns a -// non-nil error, then all subsequent calls to NextReader will the same error. +// non-nil error, then all subsequent calls to NextReader return the same +// error. func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { c.readSeq++ diff --git a/conn_test.go b/conn_test.go index 62fc210..251254e 100644 --- a/conn_test.go +++ b/conn_test.go @@ -291,3 +291,23 @@ func TestCloseError(t *testing.T) { } } } + +var unexpectedCloseErrorTests = []struct { + err error + codes []int + ok bool +}{ + {&CloseError{Code: CloseNormalClosure}, []int{CloseNormalClosure}, false}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived}, true}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived, CloseNormalClosure}, false}, + {errors.New("hello"), []int{CloseNormalClosure}, false}, +} + +func TestUnexpectedCloseErrors(t *testing.T) { + for _, tt := range unexpectedCloseErrorTests { + ok := IsUnexpectedCloseError(tt.err, tt.codes...) + if ok != tt.ok { + t.Errorf("IsUnexpectedCloseError(%#v, %#v) returned %v, want %v", tt.err, tt.codes, ok, tt.ok) + } + } +} diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..43f8f9a --- /dev/null +++ b/example_test.go @@ -0,0 +1,40 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket_test + +import ( + "log" + "net/http" + "testing" + + "github.com/gorilla/websocket" +) + +// The websocket.IsUnexpectedCloseError function is useful for identifying +// application and protocol errors. +// +// This server application works with a client application running in the +// browser. The client application does not explicitly close the websocket. The +// only expected close message from the client has the code +// websocket.CloseGoingAway. All other other close messages are likely the +// result of an application or protocol error and are logged to aid debugging. +func ExampleIsUnexpectedCloseError(err error, c *websocket.Conn, req *http.Request) { + for { + messageType, p, err := c.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { + log.Println("error: %v, user-agent: %v", err, req.Header.Get("User-Agent")) + } + return + } + processMesage(messageType, p) + } +} + +func processMesage(mt int, p []byte) {} + +// TestX prevents godoc from showing this entire file in the example. Remove +// this function when a second example is added. +func TestX(t *testing.T) {}