bf51655a7b
Signed-off-by: Jacek J. Łakis <jacek.lakis@intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
1171 lines
27 KiB
Go
1171 lines
27 KiB
Go
package spdystream
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/docker/spdystream/spdy"
|
|
)
|
|
|
|
func TestSpdyStreams(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
authenticated = true
|
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if streamErr != nil {
|
|
t.Fatalf("Error creating stream: %s", streamErr)
|
|
}
|
|
|
|
waitErr := stream.Wait()
|
|
if waitErr != nil {
|
|
t.Fatalf("Error waiting for stream: %s", waitErr)
|
|
}
|
|
|
|
message := []byte("hello")
|
|
writeErr := stream.WriteData(message, false)
|
|
if writeErr != nil {
|
|
t.Fatalf("Error writing data")
|
|
}
|
|
|
|
buf := make([]byte, 10)
|
|
n, readErr := stream.Read(buf)
|
|
if readErr != nil {
|
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
|
}
|
|
if n != 5 {
|
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n)
|
|
}
|
|
if bytes.Compare(buf[:n], message) != 0 {
|
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message)
|
|
}
|
|
|
|
headers := http.Header{
|
|
"TestKey": []string{"TestVal"},
|
|
}
|
|
sendErr := stream.SendHeader(headers, false)
|
|
if sendErr != nil {
|
|
t.Fatalf("Error sending headers: %s", sendErr)
|
|
}
|
|
receiveHeaders, receiveErr := stream.ReceiveHeader()
|
|
if receiveErr != nil {
|
|
t.Fatalf("Error receiving headers: %s", receiveErr)
|
|
}
|
|
if len(receiveHeaders) != 1 {
|
|
t.Fatalf("Unexpected number of headers:\nActual: %d\nExpecting:%d", len(receiveHeaders), 1)
|
|
}
|
|
testVal := receiveHeaders.Get("TestKey")
|
|
if testVal != "TestVal" {
|
|
t.Fatalf("Wrong test value:\nActual: %q\nExpecting: %q", testVal, "TestVal")
|
|
}
|
|
|
|
writeErr = stream.WriteData(message, true)
|
|
if writeErr != nil {
|
|
t.Fatalf("Error writing data")
|
|
}
|
|
|
|
smallBuf := make([]byte, 3)
|
|
n, readErr = stream.Read(smallBuf)
|
|
if readErr != nil {
|
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
|
}
|
|
if n != 3 {
|
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 3", n)
|
|
}
|
|
if bytes.Compare(smallBuf[:n], []byte("hel")) != 0 {
|
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", smallBuf[:n], message)
|
|
}
|
|
n, readErr = stream.Read(smallBuf)
|
|
if readErr != nil {
|
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
|
}
|
|
if n != 2 {
|
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 2", n)
|
|
}
|
|
if bytes.Compare(smallBuf[:n], []byte("lo")) != 0 {
|
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpected: lo", smallBuf[:n])
|
|
}
|
|
|
|
n, readErr = stream.Read(buf)
|
|
if readErr != io.EOF {
|
|
t.Fatalf("Expected EOF reading from finished stream, read %d bytes", n)
|
|
}
|
|
|
|
// Closing again should return error since stream is already closed
|
|
streamCloseErr := stream.Close()
|
|
if streamCloseErr == nil {
|
|
t.Fatalf("No error closing finished stream")
|
|
}
|
|
if streamCloseErr != ErrWriteClosedStream {
|
|
t.Fatalf("Unexpected error closing stream: %s", streamCloseErr)
|
|
}
|
|
|
|
streamResetErr := stream.Reset()
|
|
if streamResetErr != nil {
|
|
t.Fatalf("Error reseting stream: %s", streamResetErr)
|
|
}
|
|
|
|
authenticated = false
|
|
badStream, badStreamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if badStreamErr != nil {
|
|
t.Fatalf("Error creating stream: %s", badStreamErr)
|
|
}
|
|
|
|
waitErr = badStream.Wait()
|
|
if waitErr == nil {
|
|
t.Fatalf("Did not receive error creating stream")
|
|
}
|
|
if waitErr != ErrReset {
|
|
t.Fatalf("Unexpected error creating stream: %s", waitErr)
|
|
}
|
|
streamCloseErr = badStream.Close()
|
|
if streamCloseErr == nil {
|
|
t.Fatalf("No error closing bad stream")
|
|
}
|
|
|
|
spdyCloseErr := spdyConn.Close()
|
|
if spdyCloseErr != nil {
|
|
t.Fatalf("Error closing spdy connection: %s", spdyCloseErr)
|
|
}
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestPing(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
pingTime, pingErr := spdyConn.Ping()
|
|
if pingErr != nil {
|
|
t.Fatalf("Error pinging server: %s", pingErr)
|
|
}
|
|
if pingTime == time.Duration(0) {
|
|
t.Fatalf("Expecting non-zero ping time")
|
|
}
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestHalfClose(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
authenticated = true
|
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if streamErr != nil {
|
|
t.Fatalf("Error creating stream: %s", streamErr)
|
|
}
|
|
|
|
waitErr := stream.Wait()
|
|
if waitErr != nil {
|
|
t.Fatalf("Error waiting for stream: %s", waitErr)
|
|
}
|
|
|
|
message := []byte("hello and will read after close")
|
|
writeErr := stream.WriteData(message, false)
|
|
if writeErr != nil {
|
|
t.Fatalf("Error writing data")
|
|
}
|
|
|
|
streamCloseErr := stream.Close()
|
|
if streamCloseErr != nil {
|
|
t.Fatalf("Error closing stream: %s", streamCloseErr)
|
|
}
|
|
|
|
buf := make([]byte, 40)
|
|
n, readErr := stream.Read(buf)
|
|
if readErr != nil {
|
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
|
}
|
|
if n != 31 {
|
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n)
|
|
}
|
|
if bytes.Compare(buf[:n], message) != 0 {
|
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message)
|
|
}
|
|
|
|
spdyCloseErr := spdyConn.Close()
|
|
if spdyCloseErr != nil {
|
|
t.Fatalf("Error closing spdy connection: %s", spdyCloseErr)
|
|
}
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestUnexpectedRemoteConnectionClosed(t *testing.T) {
|
|
tt := []struct {
|
|
closeReceiver bool
|
|
closeSender bool
|
|
}{
|
|
{closeReceiver: true, closeSender: false},
|
|
{closeReceiver: false, closeSender: true},
|
|
{closeReceiver: false, closeSender: false},
|
|
}
|
|
for tix, tc := range tt {
|
|
listener, listenErr := net.Listen("tcp", "localhost:0")
|
|
if listenErr != nil {
|
|
t.Fatalf("Error listening: %v", listenErr)
|
|
}
|
|
|
|
var serverConn net.Conn
|
|
var connErr error
|
|
go func() {
|
|
serverConn, connErr = listener.Accept()
|
|
if connErr != nil {
|
|
t.Fatalf("Error accepting: %v", connErr)
|
|
}
|
|
|
|
serverSpdyConn, _ := NewConnection(serverConn, true)
|
|
go serverSpdyConn.Serve(func(stream *Stream) {
|
|
stream.SendReply(http.Header{}, tc.closeSender)
|
|
})
|
|
}()
|
|
|
|
conn, dialErr := net.Dial("tcp", listener.Addr().String())
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
authenticated = true
|
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if streamErr != nil {
|
|
t.Fatalf("Error creating stream: %s", streamErr)
|
|
}
|
|
|
|
waitErr := stream.Wait()
|
|
if waitErr != nil {
|
|
t.Fatalf("Error waiting for stream: %s", waitErr)
|
|
}
|
|
|
|
if tc.closeReceiver {
|
|
// make stream half closed, receive only
|
|
stream.Close()
|
|
}
|
|
|
|
streamch := make(chan error, 1)
|
|
go func() {
|
|
b := make([]byte, 1)
|
|
_, err := stream.Read(b)
|
|
streamch <- err
|
|
}()
|
|
|
|
closeErr := serverConn.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
|
|
select {
|
|
case e := <-streamch:
|
|
if e == nil || e != io.EOF {
|
|
t.Fatalf("(%d) Expected to get an EOF stream error", tix)
|
|
}
|
|
}
|
|
|
|
closeErr = conn.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error closing client connection: %s", closeErr)
|
|
}
|
|
|
|
listenErr = listener.Close()
|
|
if listenErr != nil {
|
|
t.Fatalf("Error closing listener: %s", listenErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCloseNotification(t *testing.T) {
|
|
listener, listenErr := net.Listen("tcp", "localhost:0")
|
|
if listenErr != nil {
|
|
t.Fatalf("Error listening: %v", listenErr)
|
|
}
|
|
listen := listener.Addr().String()
|
|
|
|
serverConnChan := make(chan net.Conn)
|
|
go func() {
|
|
serverConn, err := listener.Accept()
|
|
if err != nil {
|
|
t.Fatalf("Error accepting: %v", err)
|
|
}
|
|
|
|
serverSpdyConn, err := NewConnection(serverConn, true)
|
|
if err != nil {
|
|
t.Fatalf("Error creating server connection: %v", err)
|
|
}
|
|
go serverSpdyConn.Serve(NoOpStreamHandler)
|
|
<-serverSpdyConn.CloseChan()
|
|
serverConnChan <- serverConn
|
|
}()
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
// close client conn
|
|
err := conn.Close()
|
|
if err != nil {
|
|
t.Fatalf("Error closing client connection: %v", err)
|
|
}
|
|
|
|
var serverConn net.Conn
|
|
select {
|
|
case serverConn = <-serverConnChan:
|
|
}
|
|
|
|
err = serverConn.Close()
|
|
if err != nil {
|
|
t.Fatalf("Error closing serverConn: %v", err)
|
|
}
|
|
|
|
listenErr = listener.Close()
|
|
if listenErr != nil {
|
|
t.Fatalf("Error closing listener: %s", listenErr)
|
|
}
|
|
}
|
|
|
|
func TestIdleShutdownRace(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
authenticated = true
|
|
stream, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if err != nil {
|
|
t.Fatalf("Error creating stream: %v", err)
|
|
}
|
|
|
|
spdyConn.SetIdleTimeout(5 * time.Millisecond)
|
|
go func() {
|
|
time.Sleep(5 * time.Millisecond)
|
|
stream.Reset()
|
|
}()
|
|
|
|
select {
|
|
case <-spdyConn.CloseChan():
|
|
case <-time.After(20 * time.Millisecond):
|
|
t.Fatal("Timed out waiting for idle connection closure")
|
|
}
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestIdleNoTimeoutSet(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
select {
|
|
case <-spdyConn.CloseChan():
|
|
t.Fatal("Unexpected connection closure")
|
|
case <-time.After(10 * time.Millisecond):
|
|
}
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestIdleClearTimeout(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
spdyConn.SetIdleTimeout(10 * time.Millisecond)
|
|
spdyConn.SetIdleTimeout(0)
|
|
select {
|
|
case <-spdyConn.CloseChan():
|
|
t.Fatal("Unexpected connection closure")
|
|
case <-time.After(20 * time.Millisecond):
|
|
}
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestIdleNoData(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
spdyConn.SetIdleTimeout(10 * time.Millisecond)
|
|
<-spdyConn.CloseChan()
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestIdleWithData(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
spdyConn.SetIdleTimeout(25 * time.Millisecond)
|
|
|
|
authenticated = true
|
|
stream, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if err != nil {
|
|
t.Fatalf("Error creating stream: %v", err)
|
|
}
|
|
|
|
writeCh := make(chan struct{})
|
|
|
|
go func() {
|
|
b := []byte{1, 2, 3, 4, 5}
|
|
for i := 0; i < 10; i++ {
|
|
_, err = stream.Write(b)
|
|
if err != nil {
|
|
t.Fatalf("Error writing to stream: %v", err)
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
close(writeCh)
|
|
}()
|
|
|
|
writesFinished := false
|
|
|
|
Loop:
|
|
for {
|
|
select {
|
|
case <-writeCh:
|
|
writesFinished = true
|
|
case <-spdyConn.CloseChan():
|
|
if !writesFinished {
|
|
t.Fatal("Connection closed before all writes finished")
|
|
}
|
|
break Loop
|
|
}
|
|
}
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestIdleRace(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
spdyConn.SetIdleTimeout(10 * time.Millisecond)
|
|
|
|
authenticated = true
|
|
|
|
for i := 0; i < 10; i++ {
|
|
_, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if err != nil {
|
|
t.Fatalf("Error creating stream: %v", err)
|
|
}
|
|
}
|
|
|
|
<-spdyConn.CloseChan()
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestHalfClosedIdleTimeout(t *testing.T) {
|
|
listener, listenErr := net.Listen("tcp", "localhost:0")
|
|
if listenErr != nil {
|
|
t.Fatalf("Error listening: %v", listenErr)
|
|
}
|
|
listen := listener.Addr().String()
|
|
|
|
go func() {
|
|
serverConn, err := listener.Accept()
|
|
if err != nil {
|
|
t.Fatalf("Error accepting: %v", err)
|
|
}
|
|
|
|
serverSpdyConn, err := NewConnection(serverConn, true)
|
|
if err != nil {
|
|
t.Fatalf("Error creating server connection: %v", err)
|
|
}
|
|
go serverSpdyConn.Serve(func(s *Stream) {
|
|
s.SendReply(http.Header{}, true)
|
|
})
|
|
serverSpdyConn.SetIdleTimeout(10 * time.Millisecond)
|
|
}()
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
stream, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if err != nil {
|
|
t.Fatalf("Error creating stream: %v", err)
|
|
}
|
|
|
|
time.Sleep(20 * time.Millisecond)
|
|
|
|
stream.Reset()
|
|
|
|
err = spdyConn.Close()
|
|
if err != nil {
|
|
t.Fatalf("Error closing client spdy conn: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStreamReset(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
authenticated = true
|
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if streamErr != nil {
|
|
t.Fatalf("Error creating stream: %s", streamErr)
|
|
}
|
|
|
|
buf := []byte("dskjahfkdusahfkdsahfkdsafdkas")
|
|
for i := 0; i < 10; i++ {
|
|
if _, err := stream.Write(buf); err != nil {
|
|
t.Fatalf("Error writing to stream: %s", err)
|
|
}
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
if _, err := stream.Read(buf); err != nil {
|
|
t.Fatalf("Error reading from stream: %s", err)
|
|
}
|
|
}
|
|
|
|
// fmt.Printf("Resetting...\n")
|
|
if err := stream.Reset(); err != nil {
|
|
t.Fatalf("Error reseting stream: %s", err)
|
|
}
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestStreamResetWithDataRemaining(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
server, listen, serverErr := runServer(&wg)
|
|
if serverErr != nil {
|
|
t.Fatalf("Error initializing server: %s", serverErr)
|
|
}
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
authenticated = true
|
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if streamErr != nil {
|
|
t.Fatalf("Error creating stream: %s", streamErr)
|
|
}
|
|
|
|
buf := []byte("dskjahfkdusahfkdsahfkdsafdkas")
|
|
for i := 0; i < 10; i++ {
|
|
if _, err := stream.Write(buf); err != nil {
|
|
t.Fatalf("Error writing to stream: %s", err)
|
|
}
|
|
}
|
|
|
|
// read a bit to make sure a goroutine gets to <-dataChan
|
|
if _, err := stream.Read(buf); err != nil {
|
|
t.Fatalf("Error reading from stream: %s", err)
|
|
}
|
|
|
|
// fmt.Printf("Resetting...\n")
|
|
if err := stream.Reset(); err != nil {
|
|
t.Fatalf("Error reseting stream: %s", err)
|
|
}
|
|
|
|
closeErr := server.Close()
|
|
if closeErr != nil {
|
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
type roundTripper struct {
|
|
conn net.Conn
|
|
}
|
|
|
|
func (s *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
r := *req
|
|
req = &r
|
|
|
|
conn, err := net.Dial("tcp", req.URL.Host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = req.Write(conn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.conn = conn
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// see https://github.com/GoogleCloudPlatform/kubernetes/issues/4882
|
|
func TestFramingAfterRemoteConnectionClosed(t *testing.T) {
|
|
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
streamCh := make(chan *Stream)
|
|
|
|
w.WriteHeader(http.StatusSwitchingProtocols)
|
|
|
|
netconn, _, _ := w.(http.Hijacker).Hijack()
|
|
conn, _ := NewConnection(netconn, true)
|
|
go conn.Serve(func(s *Stream) {
|
|
s.SendReply(http.Header{}, false)
|
|
streamCh <- s
|
|
})
|
|
|
|
stream := <-streamCh
|
|
io.Copy(stream, stream)
|
|
|
|
closeChan := make(chan struct{})
|
|
go func() {
|
|
stream.Reset()
|
|
conn.Close()
|
|
close(closeChan)
|
|
}()
|
|
|
|
<-closeChan
|
|
}))
|
|
|
|
server.Start()
|
|
defer server.Close()
|
|
|
|
req, err := http.NewRequest("GET", server.URL, nil)
|
|
if err != nil {
|
|
t.Fatalf("Error creating request: %s", err)
|
|
}
|
|
|
|
rt := &roundTripper{}
|
|
client := &http.Client{Transport: rt}
|
|
|
|
_, err = client.Do(req)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error from client.Do: %s", err)
|
|
}
|
|
|
|
conn, err := NewConnection(rt.conn, false)
|
|
go conn.Serve(NoOpStreamHandler)
|
|
|
|
stream, err := conn.CreateStream(http.Header{}, nil, false)
|
|
if err != nil {
|
|
t.Fatalf("error creating client stream: %s", err)
|
|
}
|
|
|
|
n, err := stream.Write([]byte("hello"))
|
|
if err != nil {
|
|
t.Fatalf("error writing to stream: %s", err)
|
|
}
|
|
if n != 5 {
|
|
t.Fatalf("Expected to write 5 bytes, but actually wrote %d", n)
|
|
}
|
|
|
|
b := make([]byte, 5)
|
|
n, err = stream.Read(b)
|
|
if err != nil {
|
|
t.Fatalf("error reading from stream: %s", err)
|
|
}
|
|
if n != 5 {
|
|
t.Fatalf("Expected to read 5 bytes, but actually read %d", n)
|
|
}
|
|
if e, a := "hello", string(b[0:n]); e != a {
|
|
t.Fatalf("expected '%s', got '%s'", e, a)
|
|
}
|
|
|
|
stream.Reset()
|
|
conn.Close()
|
|
}
|
|
|
|
func TestGoAwayRace(t *testing.T) {
|
|
var done sync.WaitGroup
|
|
listener, err := net.Listen("tcp", "localhost:0")
|
|
if err != nil {
|
|
t.Fatalf("Error listening: %v", err)
|
|
}
|
|
listen := listener.Addr().String()
|
|
|
|
processDataFrame := make(chan struct{})
|
|
serverClosed := make(chan struct{})
|
|
|
|
done.Add(1)
|
|
go func() {
|
|
defer done.Done()
|
|
serverConn, err := listener.Accept()
|
|
if err != nil {
|
|
t.Fatalf("Error accepting: %v", err)
|
|
}
|
|
|
|
serverSpdyConn, err := NewConnection(serverConn, true)
|
|
if err != nil {
|
|
t.Fatalf("Error creating server connection: %v", err)
|
|
}
|
|
go func() {
|
|
<-serverSpdyConn.CloseChan()
|
|
close(serverClosed)
|
|
}()
|
|
|
|
// force the data frame handler to sleep before delivering the frame
|
|
serverSpdyConn.dataFrameHandler = func(frame *spdy.DataFrame) error {
|
|
<-processDataFrame
|
|
return serverSpdyConn.handleDataFrame(frame)
|
|
}
|
|
|
|
streamCh := make(chan *Stream)
|
|
go serverSpdyConn.Serve(func(s *Stream) {
|
|
s.SendReply(http.Header{}, false)
|
|
streamCh <- s
|
|
})
|
|
|
|
stream, ok := <-streamCh
|
|
if !ok {
|
|
t.Fatalf("didn't get a stream")
|
|
}
|
|
stream.Close()
|
|
data, err := ioutil.ReadAll(stream)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if e, a := "hello1hello2hello3hello4hello5", string(data); e != a {
|
|
t.Errorf("Expected %q, got %q", e, a)
|
|
}
|
|
}()
|
|
|
|
dialConn, err := net.Dial("tcp", listen)
|
|
if err != nil {
|
|
t.Fatalf("Error dialing server: %s", err)
|
|
}
|
|
conn, err := NewConnection(dialConn, false)
|
|
if err != nil {
|
|
t.Fatalf("Error creating client connectin: %v", err)
|
|
}
|
|
go conn.Serve(NoOpStreamHandler)
|
|
|
|
stream, err := conn.CreateStream(http.Header{}, nil, false)
|
|
if err != nil {
|
|
t.Fatalf("error creating client stream: %s", err)
|
|
}
|
|
if err := stream.Wait(); err != nil {
|
|
t.Fatalf("error waiting for stream creation: %v", err)
|
|
}
|
|
|
|
fmt.Fprint(stream, "hello1")
|
|
fmt.Fprint(stream, "hello2")
|
|
fmt.Fprint(stream, "hello3")
|
|
fmt.Fprint(stream, "hello4")
|
|
fmt.Fprint(stream, "hello5")
|
|
|
|
stream.Close()
|
|
conn.Close()
|
|
|
|
// wait for the server to get the go away frame
|
|
<-serverClosed
|
|
|
|
// allow the data frames to be delivered to the server's stream
|
|
close(processDataFrame)
|
|
|
|
done.Wait()
|
|
}
|
|
|
|
func TestSetIdleTimeoutAfterRemoteConnectionClosed(t *testing.T) {
|
|
listener, err := net.Listen("tcp", "localhost:0")
|
|
if err != nil {
|
|
t.Fatalf("Error listening: %v", err)
|
|
}
|
|
listen := listener.Addr().String()
|
|
|
|
serverConns := make(chan *Connection, 1)
|
|
go func() {
|
|
conn, connErr := listener.Accept()
|
|
if connErr != nil {
|
|
t.Fatal(connErr)
|
|
}
|
|
serverSpdyConn, err := NewConnection(conn, true)
|
|
if err != nil {
|
|
t.Fatalf("Error creating server connection: %v", err)
|
|
}
|
|
go serverSpdyConn.Serve(NoOpStreamHandler)
|
|
serverConns <- serverSpdyConn
|
|
}()
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
if err := spdyConn.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
serverConn := <-serverConns
|
|
defer serverConn.Close()
|
|
<-serverConn.closeChan
|
|
|
|
serverConn.SetIdleTimeout(10 * time.Second)
|
|
}
|
|
|
|
func TestClientConnectionStopsServingAfterGoAway(t *testing.T) {
|
|
listener, err := net.Listen("tcp", "localhost:0")
|
|
if err != nil {
|
|
t.Fatalf("Error listening: %v", err)
|
|
}
|
|
listen := listener.Addr().String()
|
|
|
|
serverConns := make(chan *Connection, 1)
|
|
go func() {
|
|
conn, connErr := listener.Accept()
|
|
if connErr != nil {
|
|
t.Fatal(connErr)
|
|
}
|
|
serverSpdyConn, err := NewConnection(conn, true)
|
|
if err != nil {
|
|
t.Fatalf("Error creating server connection: %v", err)
|
|
}
|
|
go serverSpdyConn.Serve(NoOpStreamHandler)
|
|
serverConns <- serverSpdyConn
|
|
}()
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
stream, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if err != nil {
|
|
t.Fatalf("Error creating stream: %v", err)
|
|
}
|
|
if err := stream.WaitTimeout(30 * time.Second); err != nil {
|
|
t.Fatalf("Timed out waiting for stream: %v", err)
|
|
}
|
|
|
|
readChan := make(chan struct{})
|
|
go func() {
|
|
_, err := ioutil.ReadAll(stream)
|
|
if err != nil {
|
|
t.Fatalf("Error reading stream: %v", err)
|
|
}
|
|
close(readChan)
|
|
}()
|
|
|
|
serverConn := <-serverConns
|
|
serverConn.Close()
|
|
|
|
// make sure the client conn breaks out of the main loop in Serve()
|
|
<-spdyConn.closeChan
|
|
// make sure the remote channels are closed and the stream read is unblocked
|
|
<-readChan
|
|
}
|
|
|
|
func TestStreamReadUnblocksAfterCloseThenReset(t *testing.T) {
|
|
listener, err := net.Listen("tcp", "localhost:0")
|
|
if err != nil {
|
|
t.Fatalf("Error listening: %v", err)
|
|
}
|
|
listen := listener.Addr().String()
|
|
|
|
serverConns := make(chan *Connection, 1)
|
|
go func() {
|
|
conn, connErr := listener.Accept()
|
|
if connErr != nil {
|
|
t.Fatal(connErr)
|
|
}
|
|
serverSpdyConn, err := NewConnection(conn, true)
|
|
if err != nil {
|
|
t.Fatalf("Error creating server connection: %v", err)
|
|
}
|
|
go serverSpdyConn.Serve(NoOpStreamHandler)
|
|
serverConns <- serverSpdyConn
|
|
}()
|
|
|
|
conn, dialErr := net.Dial("tcp", listen)
|
|
if dialErr != nil {
|
|
t.Fatalf("Error dialing server: %s", dialErr)
|
|
}
|
|
|
|
spdyConn, spdyErr := NewConnection(conn, false)
|
|
if spdyErr != nil {
|
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
|
}
|
|
go spdyConn.Serve(NoOpStreamHandler)
|
|
|
|
stream, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
|
if err != nil {
|
|
t.Fatalf("Error creating stream: %v", err)
|
|
}
|
|
if err := stream.WaitTimeout(30 * time.Second); err != nil {
|
|
t.Fatalf("Timed out waiting for stream: %v", err)
|
|
}
|
|
|
|
readChan := make(chan struct{})
|
|
go func() {
|
|
_, err := ioutil.ReadAll(stream)
|
|
if err != nil {
|
|
t.Fatalf("Error reading stream: %v", err)
|
|
}
|
|
close(readChan)
|
|
}()
|
|
|
|
serverConn := <-serverConns
|
|
defer serverConn.Close()
|
|
|
|
if err := stream.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := stream.Reset(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// make sure close followed by reset unblocks stream.Read()
|
|
select {
|
|
case <-readChan:
|
|
case <-time.After(10 * time.Second):
|
|
t.Fatal("Timed out waiting for stream read to unblock")
|
|
}
|
|
}
|
|
|
|
var authenticated bool
|
|
|
|
func authStreamHandler(stream *Stream) {
|
|
if !authenticated {
|
|
stream.Refuse()
|
|
}
|
|
MirrorStreamHandler(stream)
|
|
}
|
|
|
|
func runServer(wg *sync.WaitGroup) (io.Closer, string, error) {
|
|
listener, listenErr := net.Listen("tcp", "localhost:0")
|
|
if listenErr != nil {
|
|
return nil, "", listenErr
|
|
}
|
|
wg.Add(1)
|
|
go func() {
|
|
for {
|
|
conn, connErr := listener.Accept()
|
|
if connErr != nil {
|
|
break
|
|
}
|
|
|
|
spdyConn, _ := NewConnection(conn, true)
|
|
go spdyConn.Serve(authStreamHandler)
|
|
|
|
}
|
|
wg.Done()
|
|
}()
|
|
return listener, listener.Addr().String(), nil
|
|
}
|