|  | // 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. | 
|  |  | 
|  | // +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") | 
|  | } | 
|  | }) | 
|  | } |