@ -13,6 +13,7 @@ import (
"io/ioutil"
"net"
"reflect"
"sync"
"testing"
"testing/iotest"
"time"
@ -47,6 +48,12 @@ func (a fakeAddr) String() string {
return "str"
}
// newTestConn creates a connnection backed by a fake network connection using
// default values for buffering.
func newTestConn ( r io . Reader , w io . Writer , isServer bool ) * Conn {
return newConn ( fakeNetConn { Reader : r , Writer : w } , isServer , 1024 , 1024 , nil , nil , nil )
}
func TestFraming ( t * testing . T ) {
frameSizes := [ ] int { 0 , 1 , 2 , 124 , 125 , 126 , 127 , 128 , 129 , 65534 , 65535 , 65536 , 65537 }
var readChunkers = [ ] struct {
@ -82,8 +89,8 @@ func TestFraming(t *testing.T) {
for _ , chunker := range readChunkers {
var connBuf bytes . Buffer
wc := newConn ( fakeNetConn { Reader : nil , Writer : & connBuf } , isServer , 1024 , 1024 )
rc := newConn ( fakeNetConn { Reader : chunker . f ( & connBuf ) , Writer : nil } , ! isServer , 1024 , 1024 )
wc := newTestConn ( nil , & connBuf , isServer )
rc := newTestConn ( chunker . f ( & connBuf ) , nil , ! isServer )
if compress {
wc . newCompressionWriter = compressNoContextTakeover
rc . newDecompressionReader = decompressNoContextTakeover
@ -143,8 +150,8 @@ func TestControl(t *testing.T) {
for _ , isWriteControl := range [ ] bool { true , false } {
name := fmt . Sprintf ( "s:%v, wc:%v" , isServer , isWriteControl )
var connBuf bytes . Buffer
wc := newConn ( fakeNetConn { Reader : nil , Writer : & connBuf } , isServer , 1024 , 1024 )
rc := newConn ( fakeNetConn { Reader : & connBuf , Writer : nil } , ! isServer , 1024 , 1024 )
wc := newTestConn ( nil , & connBuf , isServer )
rc := newTestConn ( & connBuf , nil , ! isServer )
if isWriteControl {
wc . WriteControl ( PongMessage , [ ] byte ( message ) , time . Now ( ) . Add ( time . Second ) )
} else {
@ -173,14 +180,124 @@ func TestControl(t *testing.T) {
}
}
// simpleBufferPool is an implementation of BufferPool for TestWriteBufferPool.
type simpleBufferPool struct {
v interface { }
}
func ( p * simpleBufferPool ) Get ( ) interface { } {
v := p . v
p . v = nil
return v
}
func ( p * simpleBufferPool ) Put ( v interface { } ) {
p . v = v
}
func TestWriteBufferPool ( t * testing . T ) {
var buf bytes . Buffer
var pool simpleBufferPool
wc := newConn ( fakeNetConn { Writer : & buf } , true , 1024 , 1024 , & pool , nil , nil )
rc := newTestConn ( & buf , nil , false )
if wc . writeBuf != nil {
t . Fatal ( "writeBuf not nil after create" )
}
// Part 1: test NextWriter/Write/Close
w , err := wc . NextWriter ( TextMessage )
if err != nil {
t . Fatalf ( "wc.NextWriter() returned %v" , err )
}
if wc . writeBuf == nil {
t . Fatal ( "writeBuf is nil after NextWriter" )
}
writeBufAddr := & wc . writeBuf [ 0 ]
const message = "Hello World!"
if _ , err := io . WriteString ( w , message ) ; err != nil {
t . Fatalf ( "io.WriteString(w, message) returned %v" , err )
}
if err := w . Close ( ) ; err != nil {
t . Fatalf ( "w.Close() returned %v" , err )
}
if wc . writeBuf != nil {
t . Fatal ( "writeBuf not nil after w.Close()" )
}
if wpd , ok := pool . v . ( writePoolData ) ; ! ok || len ( wpd . buf ) == 0 || & wpd . buf [ 0 ] != writeBufAddr {
t . Fatal ( "writeBuf not returned to pool" )
}
opCode , p , err := rc . ReadMessage ( )
if opCode != TextMessage || err != nil {
t . Fatalf ( "ReadMessage() returned %d, p, %v" , opCode , err )
}
if s := string ( p ) ; s != message {
t . Fatalf ( "message is %s, want %s" , s , message )
}
// Part 2: Test WriteMessage.
if err := wc . WriteMessage ( TextMessage , [ ] byte ( message ) ) ; err != nil {
t . Fatalf ( "wc.WriteMessage() returned %v" , err )
}
if wc . writeBuf != nil {
t . Fatal ( "writeBuf not nil after wc.WriteMessage()" )
}
if wpd , ok := pool . v . ( writePoolData ) ; ! ok || len ( wpd . buf ) == 0 || & wpd . buf [ 0 ] != writeBufAddr {
t . Fatal ( "writeBuf not returned to pool after WriteMessage" )
}
opCode , p , err = rc . ReadMessage ( )
if opCode != TextMessage || err != nil {
t . Fatalf ( "ReadMessage() returned %d, p, %v" , opCode , err )
}
if s := string ( p ) ; s != message {
t . Fatalf ( "message is %s, want %s" , s , message )
}
}
func TestWriteBufferPoolSync ( t * testing . T ) {
var buf bytes . Buffer
var pool sync . Pool
wc := newConn ( fakeNetConn { Writer : & buf } , true , 1024 , 1024 , & pool , nil , nil )
rc := newTestConn ( & buf , nil , false )
const message = "Hello World!"
for i := 0 ; i < 3 ; i ++ {
if err := wc . WriteMessage ( TextMessage , [ ] byte ( message ) ) ; err != nil {
t . Fatalf ( "wc.WriteMessage() returned %v" , err )
}
opCode , p , err := rc . ReadMessage ( )
if opCode != TextMessage || err != nil {
t . Fatalf ( "ReadMessage() returned %d, p, %v" , opCode , err )
}
if s := string ( p ) ; s != message {
t . Fatalf ( "message is %s, want %s" , s , message )
}
}
}
func TestCloseFrameBeforeFinalMessageFrame ( t * testing . T ) {
const bufSize = 512
expectedErr := & CloseError { Code : CloseNormalClosure , Text : "hello" }
var b1 , b2 bytes . Buffer
wc := newConn ( fakeNetConn { Reader : nil , Writer : & b1 } , false , 1024 , bufSize )
rc := newConn ( fakeNetConn { Reader : & b1 , Writer : & b2 } , true , 1024 , 1024 )
wc := newConn ( & fakeNetConn { Reader : nil , Writer : & b1 } , false , 1024 , bufSize , nil , nil , nil )
rc := newTestConn ( & b1 , & b2 , true )
w , _ := wc . NextWriter ( BinaryMessage )
w . Write ( make ( [ ] byte , bufSize + bufSize / 2 ) )
@ -206,8 +323,8 @@ func TestEOFWithinFrame(t *testing.T) {
for n := 0 ; ; n ++ {
var b bytes . Buffer
wc := newConn ( fakeNetConn { Reader : nil , Writer : & b } , false , 1024 , 1024 )
rc := newConn ( fakeNetConn { Reader : & b , Writer : nil } , true , 1024 , 1024 )
wc := newTestConn ( nil , & b , false )
rc := newTestConn ( & b , nil , true )
w , _ := wc . NextWriter ( BinaryMessage )
w . Write ( make ( [ ] byte , bufSize ) )
@ -240,8 +357,8 @@ func TestEOFBeforeFinalFrame(t *testing.T) {
const bufSize = 512
var b1 , b2 bytes . Buffer
wc := newConn ( fakeNetConn { Reader : nil , Writer : & b1 } , false , 1024 , bufSize )
rc := newConn ( fakeNetConn { Reader : & b1 , Writer : & b2 } , true , 1024 , 1024 )
wc := newConn ( & fakeNetConn { Writer : & b1 } , false , 1024 , bufSize , nil , nil , nil )
rc := newTestConn ( & b1 , & b2 , true )
w , _ := wc . NextWriter ( BinaryMessage )
w . Write ( make ( [ ] byte , bufSize + bufSize / 2 ) )
@ -261,7 +378,7 @@ func TestEOFBeforeFinalFrame(t *testing.T) {
}
func TestWriteAfterMessageWriterClose ( t * testing . T ) {
wc := newConn ( fakeNetConn { Reader : nil , Writer : & bytes . Buffer { } } , false , 1024 , 1024 )
wc := newTestConn ( nil , & bytes . Buffer { } , false )
w , _ := wc . NextWriter ( BinaryMessage )
io . WriteString ( w , "hello" )
if err := w . Close ( ) ; err != nil {
@ -292,8 +409,8 @@ func TestReadLimit(t *testing.T) {
message := make ( [ ] byte , readLimit + 1 )
var b1 , b2 bytes . Buffer
wc := newConn ( fakeNetConn { Reader : nil , Writer : & b1 } , false , 1024 , readLimit - 2 )
rc := newConn ( fakeNetConn { Reader : & b1 , Writer : & b2 } , true , 1024 , 1024 )
wc := newConn ( & fakeNetConn { Writer : & b1 } , false , 1024 , readLimit - 2 , nil , nil , nil )
rc := newTestConn ( & b1 , & b2 , true )
rc . SetReadLimit ( readLimit )
// Send message at the limit with interleaved pong.
@ -321,7 +438,7 @@ func TestReadLimit(t *testing.T) {
}
func TestAddrs ( t * testing . T ) {
c := newConn ( & fakeNetConn { } , true , 1024 , 1024 )
c := newTestConn ( nil , nil , true )
if c . LocalAddr ( ) != localAddr {
t . Errorf ( "LocalAddr = %v, want %v" , c . LocalAddr ( ) , localAddr )
}
@ -333,7 +450,7 @@ func TestAddrs(t *testing.T) {
func TestUnderlyingConn ( t * testing . T ) {
var b1 , b2 bytes . Buffer
fc := fakeNetConn { Reader : & b1 , Writer : & b2 }
c := newConn ( fc , true , 1024 , 1024 )
c := newConn ( fc , true , 1024 , 1024 , nil , nil , nil )
ul := c . UnderlyingConn ( )
if ul != fc {
t . Fatalf ( "Underlying conn is not what it should be." )
@ -347,8 +464,8 @@ func TestBufioReadBytes(t *testing.T) {
m [ len ( m ) - 1 ] = '\n'
var b1 , b2 bytes . Buffer
wc := newConn ( fakeNetConn { Reader : nil , Writer : & b1 } , false , len ( m ) + 64 , len ( m ) + 64 )
rc := newConn ( fakeNetConn { Reader : & b1 , Writer : & b2 } , true , len ( m ) - 64 , len ( m ) - 64 )
wc := newConn ( fakeNetConn { Writer : & b1 } , false , len ( m ) + 64 , len ( m ) + 64 , nil , nil , nil )
rc := newConn ( fakeNetConn { Reader : & b1 , Writer : & b2 } , true , len ( m ) - 64 , len ( m ) - 64 , nil , nil , nil )
w , _ := wc . NextWriter ( BinaryMessage )
w . Write ( m )
@ -423,7 +540,7 @@ func (w blockingWriter) Write(p []byte) (int, error) {
func TestConcurrentWritePanic ( t * testing . T ) {
w := blockingWriter { make ( chan struct { } ) , make ( chan struct { } ) }
c := newConn ( fakeNetConn { Reader : nil , Writer : w } , false , 1024 , 1024 )
c := newTestConn ( nil , w , false )
go func ( ) {
c . WriteMessage ( TextMessage , [ ] byte { } )
} ( )
@ -449,7 +566,7 @@ func (r failingReader) Read(p []byte) (int, error) {
}
func TestFailedConnectionReadPanic ( t * testing . T ) {
c := newConn ( fakeNetConn { Reader : failingReader { } , Writer : nil } , false , 1024 , 1024 )
c := newTestConn ( failingReader { } , nil , false )
defer func ( ) {
if v := recover ( ) ; v != nil {
@ -462,35 +579,3 @@ func TestFailedConnectionReadPanic(t *testing.T) {
}
t . Fatal ( "should not get here" )
}
func TestBufioReuse ( t * testing . T ) {
brw := bufio . NewReadWriter ( bufio . NewReader ( nil ) , bufio . NewWriter ( nil ) )
c := newConnBRW ( nil , false , 0 , 0 , brw )
if c . br != brw . Reader {
t . Error ( "connection did not reuse bufio.Reader" )
}
var wh writeHook
brw . Writer . Reset ( & wh )
brw . WriteByte ( 0 )
brw . Flush ( )
if & c . writeBuf [ 0 ] != & wh . p [ 0 ] {
t . Error ( "connection did not reuse bufio.Writer" )
}
brw = bufio . NewReadWriter ( bufio . NewReaderSize ( nil , 0 ) , bufio . NewWriterSize ( nil , 0 ) )
c = newConnBRW ( nil , false , 0 , 0 , brw )
if c . br == brw . Reader {
t . Error ( "connection used bufio.Reader with small size" )
}
brw . Writer . Reset ( & wh )
brw . WriteByte ( 0 )
brw . Flush ( )
if & c . writeBuf [ 0 ] != & wh . p [ 0 ] {
t . Error ( "connection used bufio.Writer with small size" )
}
}