add DialPipeContext

This commit is contained in:
Simeone
2019-03-26 09:43:43 +01:00
parent dd3d7fa178
commit 5fdbdcc2ae
2 changed files with 54 additions and 15 deletions
+33 -15
View File
@@ -3,6 +3,7 @@
package winio
import (
"context"
"errors"
"io"
"net"
@@ -137,9 +138,30 @@ func (s pipeAddress) String() string {
return string(s)
}
func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
for {
select {
case <-ctx.Done():
err := ctx.Err()
if err == context.DeadlineExceeded {
err = ErrTimeout
} else {
err = &os.PathError{Op: "open", Path: *path, Err: err}
}
return syscall.Handle(0), err
default:
h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
if err != cERROR_PIPE_BUSY {
return h, &os.PathError{Op: "open", Path: *path, Err: err}
}
time.Sleep(time.Millisecond * 10)
}
}
}
// DialPipe connects to a named pipe by path, timing out if the connection
// takes longer than the specified duration. If timeout is nil, then we use
// a default timeout of 5 seconds. (We do not use WaitNamedPipe.)
// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
var absTimeout time.Time
if timeout != nil {
@@ -147,23 +169,19 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
} else {
absTimeout = time.Now().Add(time.Second * 2)
}
ctx, _ := context.WithDeadline(context.Background(), absTimeout)
conn, err := DialPipeContext(ctx, path)
return conn, err
}
//DialPipeContext connects to a named pipe. ctx can be used to cancel or
//expire the pending connection ( We do not use WaitNamedPipe.)
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
var err error
var h syscall.Handle
for {
h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
if err != cERROR_PIPE_BUSY {
break
}
if time.Now().After(absTimeout) {
return nil, ErrTimeout
}
// Wait 10 msec and try again. This is a rather simplistic
// view, as we always try each 10 milliseconds.
time.Sleep(time.Millisecond * 10)
}
h, err = tryDialPipe(ctx, &path)
if err != nil {
return nil, &os.PathError{Op: "open", Path: path, Err: err}
return nil, err
}
var flags uint32
+21
View File
@@ -3,6 +3,7 @@ package winio
import (
"bufio"
"bytes"
"context"
"io"
"net"
"os"
@@ -37,6 +38,26 @@ func TestDialListenerTimesOut(t *testing.T) {
}
}
func TestDialListenerGetsCancelled(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
l, err := ListenPipe(testPipeName, nil)
if err != nil {
t.Fatal(err)
}
ch := make(chan error)
defer l.Close()
go func(ctx context.Context, ch chan error) {
_, err := DialPipeContext(ctx, testPipeName)
ch <- err
}(ctx, ch)
time.Sleep(time.Millisecond * 30)
cancel()
err = <-ch
if err == nil {
t.Fatalf("expected ErrTimeout, got %v", err)
}
}
func TestDialAccessDeniedWithRestrictedSD(t *testing.T) {
c := PipeConfig{
SecurityDescriptor: "D:P(A;;0x1200FF;;;WD)",