package multistream import ( "crypto/rand" "io" "net" "testing" "time" ) func TestProtocolNegotiation(t *testing.T) { a, b := net.Pipe() mux := NewMultistreamMuxer() mux.AddHandler("/a", nil) mux.AddHandler("/b", nil) mux.AddHandler("/c", nil) done := make(chan struct{}) go func() { selected, _, err := mux.Negotiate(a) if err != nil { t.Fatal(err) } if selected != "/a" { t.Fatal("incorrect protocol selected") } close(done) }() err := SelectProtoOrFail("/a", b) if err != nil { t.Fatal(err) } select { case <-time.After(time.Second): t.Fatal("protocol negotiation didnt complete") case <-done: } verifyPipe(t, a, b) } func TestSelectOne(t *testing.T) { a, b := net.Pipe() mux := NewMultistreamMuxer() mux.AddHandler("/a", nil) mux.AddHandler("/b", nil) mux.AddHandler("/c", nil) done := make(chan struct{}) go func() { selected, _, err := mux.Negotiate(a) if err != nil { t.Fatal(err) } if selected != "/c" { t.Fatal("incorrect protocol selected") } close(done) }() sel, err := SelectOneOf([]string{"/d", "/e", "/c"}, b) if err != nil { t.Fatal(err) } if sel != "/c" { t.Fatal("selected wrong protocol") } select { case <-time.After(time.Second): t.Fatal("protocol negotiation didnt complete") case <-done: } verifyPipe(t, a, b) } func TestSelectOneAndWrite(t *testing.T) { a, b := net.Pipe() mux := NewMultistreamMuxer() mux.AddHandler("/a", nil) mux.AddHandler("/b", nil) mux.AddHandler("/c", nil) done := make(chan struct{}) go func() { selected, _, err := mux.Negotiate(a) if err != nil { t.Fatal(err) } if selected != "/c" { t.Fatal("incorrect protocol selected") } close(done) }() sel, err := SelectOneOf([]string{"/d", "/e", "/c"}, b) if err != nil { t.Fatal(err) } if sel != "/c" { t.Fatal("selected wrong protocol") } select { case <-time.After(time.Second): t.Fatal("protocol negotiation didnt complete") case <-done: } verifyPipe(t, a, b) } func TestLazyConns(t *testing.T) { a, b := net.Pipe() mux := NewMultistreamMuxer() mux.AddHandler("/a", nil) mux.AddHandler("/b", nil) mux.AddHandler("/c", nil) la := NewMSSelect(a, "/c") lb := NewMSSelect(b, "/c") verifyPipe(t, la, lb) } func TestLazyAndMux(t *testing.T) { a, b := net.Pipe() mux := NewMultistreamMuxer() mux.AddHandler("/a", nil) mux.AddHandler("/b", nil) mux.AddHandler("/c", nil) done := make(chan struct{}) go func() { selected, _, err := mux.Negotiate(a) if err != nil { t.Fatal(err) } if selected != "/c" { t.Fatal("incorrect protocol selected") } msg := make([]byte, 5) _, err = a.Read(msg) if err != nil { t.Fatal(err) } close(done) }() lb := NewMSSelect(b, "/c") // do a write to push the handshake through _, err := lb.Write([]byte("hello")) if err != nil { t.Fatal(err) } select { case <-time.After(time.Second): t.Fatal("failed to complete in time") case <-done: } verifyPipe(t, a, lb) } func TestLazyAndMuxWrite(t *testing.T) { a, b := net.Pipe() mux := NewMultistreamMuxer() mux.AddHandler("/a", nil) mux.AddHandler("/b", nil) mux.AddHandler("/c", nil) done := make(chan struct{}) go func() { selected, _, err := mux.Negotiate(a) if err != nil { t.Fatal(err) } if selected != "/c" { t.Fatal("incorrect protocol selected") } _, err = a.Write([]byte("hello")) if err != nil { t.Fatal(err) } close(done) }() lb := NewMSSelect(b, "/c") // do a write to push the handshake through msg := make([]byte, 5) _, err := lb.Read(msg) if err != nil { t.Fatal(err) } if string(msg) != "hello" { t.Fatal("wrong!") } select { case <-time.After(time.Second): t.Fatal("failed to complete in time") case <-done: } verifyPipe(t, a, lb) } func verifyPipe(t *testing.T, a, b io.ReadWriter) { mes := make([]byte, 1024) rand.Read(mes) go func() { b.Write(mes) a.Write(mes) }() buf := make([]byte, len(mes)) n, err := a.Read(buf) if err != nil { t.Fatal(err) } if n != len(buf) { t.Fatal("failed to read enough") } if string(buf) != string(mes) { t.Fatal("somehow read wrong message") } n, err = b.Read(buf) if err != nil { t.Fatal(err) } if n != len(buf) { t.Fatal("failed to read enough") } if string(buf) != string(mes) { t.Fatal("somehow read wrong message") } }