mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-08-29 05:16:31 -04:00
This change adds support for message mode pipes and uses them to support CloseWrite() to better match TCP and UNIX sockets. Message mode pipes support writing and (optionally) reading data in message-sized chunks. This is useful for us because when in this mode a zero-sized message can be read. We use this zero-sized message to signal that no more writes will arrive. This is not standard practice in Windows, but it is a reasonable compromise.
263 lines
4.6 KiB
Go
263 lines
4.6 KiB
Go
package winio
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
var testPipeName = `\\.\pipe\winiotestpipe`
|
|
|
|
func TestDialUnknownFailsImmediately(t *testing.T) {
|
|
_, err := DialPipe(testPipeName, nil)
|
|
if err.(*os.PathError).Err != syscall.ENOENT {
|
|
t.Fatalf("expected ENOENT got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDialListenerTimesOut(t *testing.T) {
|
|
l, err := ListenPipe(testPipeName, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer l.Close()
|
|
var d = time.Duration(10 * time.Millisecond)
|
|
_, err = DialPipe(testPipeName, &d)
|
|
if err != ErrTimeout {
|
|
t.Fatalf("expected ErrTimeout, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDialAccessDeniedWithRestrictedSD(t *testing.T) {
|
|
c := PipeConfig{
|
|
SecurityDescriptor: "D:P(A;;0x1200FF;;;WD)",
|
|
}
|
|
l, err := ListenPipe(testPipeName, &c)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer l.Close()
|
|
_, err = DialPipe(testPipeName, nil)
|
|
if err.(*os.PathError).Err != syscall.ERROR_ACCESS_DENIED {
|
|
t.Fatalf("expected ERROR_ACCESS_DENIED, got %v", err)
|
|
}
|
|
}
|
|
|
|
func getConnection(cfg *PipeConfig) (client net.Conn, server net.Conn, err error) {
|
|
l, err := ListenPipe(testPipeName, cfg)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer l.Close()
|
|
|
|
type response struct {
|
|
c net.Conn
|
|
err error
|
|
}
|
|
ch := make(chan response)
|
|
go func() {
|
|
c, err := l.Accept()
|
|
ch <- response{c, err}
|
|
}()
|
|
|
|
c, err := DialPipe(testPipeName, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
r := <-ch
|
|
if err = r.err; err != nil {
|
|
c.Close()
|
|
return
|
|
}
|
|
|
|
client = c
|
|
server = r.c
|
|
return
|
|
}
|
|
|
|
func TestReadTimeout(t *testing.T) {
|
|
c, s, err := getConnection(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
defer s.Close()
|
|
|
|
c.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
|
|
|
|
buf := make([]byte, 10)
|
|
_, err = c.Read(buf)
|
|
if err != ErrTimeout {
|
|
t.Fatalf("expected ErrTimeout, got %v", err)
|
|
}
|
|
}
|
|
|
|
func server(l net.Listener, ch chan int) {
|
|
c, err := l.Accept()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
rw := bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c))
|
|
s, err := rw.ReadString('\n')
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
_, err = rw.WriteString("got " + s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
err = rw.Flush()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
c.Close()
|
|
ch <- 1
|
|
}
|
|
|
|
func TestFullListenDialReadWrite(t *testing.T) {
|
|
l, err := ListenPipe(testPipeName, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer l.Close()
|
|
|
|
ch := make(chan int)
|
|
go server(l, ch)
|
|
|
|
c, err := DialPipe(testPipeName, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
rw := bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c))
|
|
_, err = rw.WriteString("hello world\n")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = rw.Flush()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s, err := rw.ReadString('\n')
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ms := "got hello world\n"
|
|
if s != ms {
|
|
t.Errorf("expected '%s', got '%s'", ms, s)
|
|
}
|
|
|
|
<-ch
|
|
}
|
|
|
|
func TestCloseAbortsListen(t *testing.T) {
|
|
l, err := ListenPipe(testPipeName, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ch := make(chan error)
|
|
go func() {
|
|
_, err := l.Accept()
|
|
ch <- err
|
|
}()
|
|
|
|
time.Sleep(30 * time.Millisecond)
|
|
l.Close()
|
|
|
|
err = <-ch
|
|
if err != ErrPipeListenerClosed {
|
|
t.Fatalf("expected ErrPipeListenerClosed, got %v", err)
|
|
}
|
|
}
|
|
|
|
func ensureEOFOnClose(t *testing.T, r io.Reader, w io.Closer) {
|
|
b := make([]byte, 10)
|
|
w.Close()
|
|
n, err := r.Read(b)
|
|
if n > 0 {
|
|
t.Errorf("unexpected byte count %d", n)
|
|
}
|
|
if err != io.EOF {
|
|
t.Errorf("expected EOF: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCloseClientEOFServer(t *testing.T) {
|
|
c, s, err := getConnection(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
defer s.Close()
|
|
ensureEOFOnClose(t, c, s)
|
|
}
|
|
|
|
func TestCloseServerEOFClient(t *testing.T) {
|
|
c, s, err := getConnection(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
defer s.Close()
|
|
ensureEOFOnClose(t, s, c)
|
|
}
|
|
|
|
func TestCloseWriteEOF(t *testing.T) {
|
|
cfg := &PipeConfig{
|
|
MessageMode: true,
|
|
}
|
|
c, s, err := getConnection(cfg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
defer s.Close()
|
|
|
|
type closeWriter interface {
|
|
CloseWrite() error
|
|
}
|
|
|
|
err = c.(closeWriter).CloseWrite()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
b := make([]byte, 10)
|
|
_, err = s.Read(b)
|
|
if err != io.EOF {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestAcceptAfterCloseFails(t *testing.T) {
|
|
l, err := ListenPipe(testPipeName, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
l.Close()
|
|
_, err = l.Accept()
|
|
if err != ErrPipeListenerClosed {
|
|
t.Fatalf("expected ErrPipeListenerClosed, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDialTimesOutByDefault(t *testing.T) {
|
|
l, err := ListenPipe(testPipeName, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer l.Close()
|
|
_, err = DialPipe(testPipeName, nil)
|
|
if err != ErrTimeout {
|
|
t.Fatalf("expected ErrTimeout, got %v", err)
|
|
}
|
|
}
|