good morning!!!!

Skip to content
Snippets Groups Projects
conn_test.go 5.5 KiB
Newer Older
// +build !js

Anmol Sethi's avatar
Anmol Sethi committed
package websocket_test

import (
	"context"
Anmol Sethi's avatar
Anmol Sethi committed
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"sync"
Anmol Sethi's avatar
Anmol Sethi committed
	"testing"
	"time"

Anmol Sethi's avatar
Anmol Sethi committed
	"golang.org/x/xerrors"
Anmol Sethi's avatar
Anmol Sethi committed
	"nhooyr.io/websocket"
Anmol Sethi's avatar
Anmol Sethi committed
	"nhooyr.io/websocket/internal/test/cmp"
Anmol Sethi's avatar
Anmol Sethi committed
	"nhooyr.io/websocket/internal/test/wstest"
	"nhooyr.io/websocket/internal/test/xrand"
Anmol Sethi's avatar
Anmol Sethi committed
	"nhooyr.io/websocket/internal/xsync"
Anmol Sethi's avatar
Anmol Sethi committed
func TestConn(t *testing.T) {
	t.Parallel()

Anmol Sethi's avatar
Anmol Sethi committed
	t.Run("data", func(t *testing.T) {
Anmol Sethi's avatar
Anmol Sethi committed
		for i := 0; i < 5; i++ {
			t.Run("", func(t *testing.T) {
Anmol Sethi's avatar
Anmol Sethi committed
				t.Parallel()

Anmol Sethi's avatar
Anmol Sethi committed
				copts := &websocket.CompressionOptions{
Anmol Sethi's avatar
Anmol Sethi committed
					Mode:      websocket.CompressionMode(xrand.Int(int(websocket.CompressionDisabled) + 1)),
Anmol Sethi's avatar
Anmol Sethi committed
					Threshold: xrand.Int(9999),
				}

				c1, c2, err := wstest.Pipe(&websocket.DialOptions{
					CompressionOptions: copts,
				}, &websocket.AcceptOptions{
					CompressionOptions: copts,
				})
				if err != nil {
					t.Fatal(err)
				}
				defer c2.Close(websocket.StatusInternalError, "")
Anmol Sethi's avatar
Anmol Sethi committed
				defer c1.Close(websocket.StatusInternalError, "")

				ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
				defer cancel()
Anmol Sethi's avatar
Anmol Sethi committed

Anmol Sethi's avatar
Anmol Sethi committed
				echoLoopErr := xsync.Go(func() error {
Anmol Sethi's avatar
Anmol Sethi committed
					err := wstest.EchoLoop(ctx, c2)
Anmol Sethi's avatar
Anmol Sethi committed
					return assertCloseStatus(websocket.StatusNormalClosure, err)
Anmol Sethi's avatar
Anmol Sethi committed
				defer func() {
					err := <-echoLoopErr
					if err != nil {
						t.Errorf("echo loop error: %v", err)
					}
				}()
Anmol Sethi's avatar
Anmol Sethi committed
				defer cancel()
Anmol Sethi's avatar
Anmol Sethi committed
				c1.SetReadLimit(131072)
Anmol Sethi's avatar
Anmol Sethi committed
				for i := 0; i < 5; i++ {
Anmol Sethi's avatar
Anmol Sethi committed
					err := wstest.Echo(ctx, c1, 131072)
Anmol Sethi's avatar
Anmol Sethi committed
					if err != nil {
						t.Fatal(err)
					}
Anmol Sethi's avatar
Anmol Sethi committed
				err = c1.Close(websocket.StatusNormalClosure, "")
				if err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
Anmol Sethi's avatar
Anmol Sethi committed

	t.Run("badClose", func(t *testing.T) {
		t.Parallel()

		c1, c2, err := wstest.Pipe(nil, nil)
		if err != nil {
			t.Fatal(err)
		}
		defer c1.Close(websocket.StatusInternalError, "")
		defer c2.Close(websocket.StatusInternalError, "")

		err = c1.Close(-1, "")
		if !cmp.ErrorContains(err, "failed to marshal close frame: status code StatusCode(-1) cannot be set") {
			t.Fatalf("unexpected error: %v", err)
		}
	})

	t.Run("ping", func(t *testing.T) {
		t.Parallel()

		c1, c2, err := wstest.Pipe(nil, nil)
		if err != nil {
			t.Fatal(err)
		}
		defer c1.Close(websocket.StatusInternalError, "")
		defer c2.Close(websocket.StatusInternalError, "")

		ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
		defer cancel()

		c2.CloseRead(ctx)
		c1.CloseRead(ctx)

		for i := 0; i < 10; i++ {
			err = c1.Ping(ctx)
			if err != nil {
				t.Fatal(err)
			}
		}

		err = c1.Close(websocket.StatusNormalClosure, "")
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
	})

	t.Run("badPing", func(t *testing.T) {
		t.Parallel()

		c1, c2, err := wstest.Pipe(nil, nil)
		if err != nil {
			t.Fatal(err)
		}
		defer c1.Close(websocket.StatusInternalError, "")
		defer c2.Close(websocket.StatusInternalError, "")

		ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
		defer cancel()

		c2.CloseRead(ctx)

		err = c1.Ping(ctx)
		if !cmp.ErrorContains(err, "failed to wait for pong") {
			t.Fatalf("unexpected error: %v", err)
		}
	})

	t.Run("concurrentWrite", func(t *testing.T) {
		t.Parallel()

		c1, c2, err := wstest.Pipe(nil, nil)
		if err != nil {
			t.Fatal(err)
		}
		defer c2.Close(websocket.StatusInternalError, "")
		defer c1.Close(websocket.StatusInternalError, "")

		ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
		defer cancel()

		discardLoopErr := xsync.Go(func() error {
			for {
				_, _, err := c2.Read(ctx)
				if websocket.CloseStatus(err) == websocket.StatusNormalClosure {
					return nil
				}
				if err != nil {
					return err
				}
			}
		})
		defer func() {
			err := <-discardLoopErr
			if err != nil {
				t.Errorf("discard loop error: %v", err)
			}
		}()
		defer cancel()

		msg := xrand.Bytes(xrand.Int(9999))
		const count = 100
		errs := make(chan error, count)

		for i := 0; i < count; i++ {
			go func() {
				errs <- c1.Write(ctx, websocket.MessageBinary, msg)
			}()
		}

		for i := 0; i < count; i++ {
			err := <-errs
			if err != nil {
				t.Fatal(err)
			}
		}

		err = c1.Close(websocket.StatusNormalClosure, "")
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
	})
Anmol Sethi's avatar
Anmol Sethi committed

Anmol Sethi's avatar
Anmol Sethi committed
func TestWasm(t *testing.T) {
	t.Parallel()
Anmol Sethi's avatar
Anmol Sethi committed

Anmol Sethi's avatar
Anmol Sethi committed
	var wg sync.WaitGroup
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		wg.Add(1)
		defer wg.Done()
Anmol Sethi's avatar
Anmol Sethi committed

Anmol Sethi's avatar
Anmol Sethi committed
		c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
			Subprotocols:       []string{"echo"},
			InsecureSkipVerify: true,
		})
Anmol Sethi's avatar
Anmol Sethi committed
		if err != nil {
Anmol Sethi's avatar
Anmol Sethi committed
			t.Error(err)
			return
Anmol Sethi's avatar
Anmol Sethi committed
		}
Anmol Sethi's avatar
Anmol Sethi committed
		defer c.Close(websocket.StatusInternalError, "")
Anmol Sethi's avatar
Anmol Sethi committed

Anmol Sethi's avatar
Anmol Sethi committed
		err = wstest.EchoLoop(r.Context(), c)
		if websocket.CloseStatus(err) != websocket.StatusNormalClosure {
			t.Errorf("echoLoop: %v", err)
Anmol Sethi's avatar
Anmol Sethi committed
		}
Anmol Sethi's avatar
Anmol Sethi committed
	}))
	defer wg.Wait()
	defer s.Close()
Anmol Sethi's avatar
Anmol Sethi committed

Anmol Sethi's avatar
Anmol Sethi committed
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
	defer cancel()
Anmol Sethi's avatar
Anmol Sethi committed

Anmol Sethi's avatar
Anmol Sethi committed
	cmd := exec.CommandContext(ctx, "go", "test", "-exec=wasmbrowsertest", "./...")
	cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm", fmt.Sprintf("WS_ECHO_SERVER_URL=%v", wstest.URL(s)))

	b, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("wasm test binary failed: %v:\n%s", err, b)
Anmol Sethi's avatar
Anmol Sethi committed
	}
}
Anmol Sethi's avatar
Anmol Sethi committed

func assertCloseStatus(exp websocket.StatusCode, err error) error {
	if websocket.CloseStatus(err) == -1 {
		return xerrors.Errorf("expected websocket.CloseError: %T %v", err, err)
	}
	if websocket.CloseStatus(err) != exp {
		return xerrors.Errorf("unexpected close status (%v):%v", exp, err)
	}
	return nil
}