// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !js // +build !js package net import ( "bytes" "runtime" "testing" "time" ) func TestRawConnReadWrite(t *testing.T) { switch runtime.GOOS { case "plan9": t.Skipf("not supported on %s", runtime.GOOS) } t.Run("TCP", func(t *testing.T) { handler := func(ls *localServer, ln Listener) { c, err := ln.Accept() if err != nil { t.Error(err) return } defer c.Close() cc, err := ln.(*TCPListener).SyscallConn() if err != nil { t.Fatal(err) } called := false op := func(uintptr) bool { called = true return true } err = cc.Write(op) if err == nil { t.Error("Write should return an error") } if called { t.Error("Write shouldn't call op") } called = false err = cc.Read(op) if err == nil { t.Error("Read should return an error") } if called { t.Error("Read shouldn't call op") } var b [32]byte n, err := c.Read(b[:]) if err != nil { t.Error(err) return } if _, err := c.Write(b[:n]); err != nil { t.Error(err) return } } ls, err := newLocalServer("tcp") if err != nil { t.Fatal(err) } defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) } c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String()) if err != nil { t.Fatal(err) } defer c.Close() cc, err := c.(*TCPConn).SyscallConn() if err != nil { t.Fatal(err) } data := []byte("HELLO-R-U-THERE") if err := writeRawConn(cc, data); err != nil { t.Fatal(err) } var b [32]byte n, err := readRawConn(cc, b[:]) if err != nil { t.Fatal(err) } if bytes.Compare(b[:n], data) != 0 { t.Fatalf("got %q; want %q", b[:n], data) } }) t.Run("Deadline", func(t *testing.T) { switch runtime.GOOS { case "windows": t.Skipf("not supported on %s", runtime.GOOS) } ln, err := newLocalListener("tcp") if err != nil { t.Fatal(err) } defer ln.Close() c, err := Dial(ln.Addr().Network(), ln.Addr().String()) if err != nil { t.Fatal(err) } defer c.Close() cc, err := c.(*TCPConn).SyscallConn() if err != nil { t.Fatal(err) } var b [1]byte c.SetDeadline(noDeadline) if err := c.SetDeadline(time.Now().Add(-1)); err != nil { t.Fatal(err) } if err = writeRawConn(cc, b[:]); err == nil { t.Fatal("Write should fail") } if perr := parseWriteError(err); perr != nil { t.Error(perr) } if !isDeadlineExceeded(err) { t.Errorf("got %v; want timeout", err) } if _, err = readRawConn(cc, b[:]); err == nil { t.Fatal("Read should fail") } if perr := parseReadError(err); perr != nil { t.Error(perr) } if !isDeadlineExceeded(err) { t.Errorf("got %v; want timeout", err) } c.SetReadDeadline(noDeadline) if err := c.SetReadDeadline(time.Now().Add(-1)); err != nil { t.Fatal(err) } if _, err = readRawConn(cc, b[:]); err == nil { t.Fatal("Read should fail") } if perr := parseReadError(err); perr != nil { t.Error(perr) } if !isDeadlineExceeded(err) { t.Errorf("got %v; want timeout", err) } c.SetWriteDeadline(noDeadline) if err := c.SetWriteDeadline(time.Now().Add(-1)); err != nil { t.Fatal(err) } if err = writeRawConn(cc, b[:]); err == nil { t.Fatal("Write should fail") } if perr := parseWriteError(err); perr != nil { t.Error(perr) } if !isDeadlineExceeded(err) { t.Errorf("got %v; want timeout", err) } }) } func TestRawConnControl(t *testing.T) { switch runtime.GOOS { case "plan9": t.Skipf("not supported on %s", runtime.GOOS) } t.Run("TCP", func(t *testing.T) { ln, err := newLocalListener("tcp") if err != nil { t.Fatal(err) } defer ln.Close() cc1, err := ln.(*TCPListener).SyscallConn() if err != nil { t.Fatal(err) } if err := controlRawConn(cc1, ln.Addr()); err != nil { t.Fatal(err) } c, err := Dial(ln.Addr().Network(), ln.Addr().String()) if err != nil { t.Fatal(err) } defer c.Close() cc2, err := c.(*TCPConn).SyscallConn() if err != nil { t.Fatal(err) } if err := controlRawConn(cc2, c.LocalAddr()); err != nil { t.Fatal(err) } ln.Close() if err := controlRawConn(cc1, ln.Addr()); err == nil { t.Fatal("Control after Close should fail") } c.Close() if err := controlRawConn(cc2, c.LocalAddr()); err == nil { t.Fatal("Control after Close should fail") } }) }