feat(server/client): add windows pipe, pipe peer credentials support

Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
This commit is contained in:
2025-11-23 18:08:12 +01:00
parent 6829ab5bbc
commit e6fee24f40
17 changed files with 339 additions and 16 deletions

View File

@@ -3,6 +3,7 @@ package client
import (
"context"
"fmt"
"net/url"
"strings"
"google.golang.org/grpc"
@@ -19,7 +20,7 @@ type Client interface {
}
func New(opts ...Option) (Client, error) {
c := &client{opts: &options{}}
c := &client{opts: &options{dialOptions: []grpc.DialOption{grpc.WithContextDialer(dial)}}}
for _, o := range opts {
o(c.opts)
}
@@ -78,3 +79,32 @@ func (c *client) Invoke(ctx context.Context, method string, args interface{}, re
func (c *client) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
return c.cc.NewStream(ctx, desc, method, opts...)
}
func parseDialTarget(target string) (string, string) {
net := "tcp"
m1 := strings.Index(target, ":")
m2 := strings.Index(target, ":/")
// handle unix:addr which will fail with url.Parse
if m1 >= 0 && m2 < 0 {
if n := target[0:m1]; n == "unix" {
return n, target[m1+1:]
}
}
if strings.HasPrefix(target, `\\.\pipe\`) {
net = "pipe"
return net, target
}
if m2 >= 0 {
t, err := url.Parse(target)
if err != nil {
return net, target
}
scheme := t.Scheme
addr := t.Host
if scheme == "unix" {
addr += t.Path
}
return scheme, addr
}
return net, target
}

26
client/client_test.go Normal file
View File

@@ -0,0 +1,26 @@
package client
import (
"testing"
)
func TestParseDialTarget(t *testing.T) {
tests := []struct {
input string
expectedNet string
expectedAddr string
}{
{"tcp://localhost:50051", "tcp", "localhost:50051"},
{"localhost:50051", "tcp", "localhost:50051"},
{"unix:///tmp/socket", "unix", "/tmp/socket"},
{"unix://C:/path/to/socket", "unix", "C:/path/to/socket"},
{"unix:path/to/socket", "unix", "path/to/socket"},
{`\\.\pipe\example`, "pipe", `\\.\pipe\example`},
}
for _, test := range tests {
net, addr := parseDialTarget(test.input)
if net != test.expectedNet || addr != test.expectedAddr {
t.Errorf("parseDialTarget(%q) = (%q, %q); want (%q, %q)", test.input, net, addr, test.expectedNet, test.expectedAddr)
}
}
}

12
client/dialer.go Normal file
View File

@@ -0,0 +1,12 @@
//go:build !windows
package client
import (
"context"
"net"
)
func dial(ctx context.Context, addr string) (net.Conn, error) {
network, address := parseDialTarget(addr)
return (&net.Dialer{}).DialContext(ctx, network, address)
}

18
client/dialer_windows.go Normal file
View File

@@ -0,0 +1,18 @@
//go:build windows
package client
import (
"context"
"net"
"github.com/Microsoft/go-winio"
)
func dial(ctx context.Context, addr string) (net.Conn, error) {
network, address := parseDialTarget(addr)
if network == "pipe" {
return winio.DialPipeContext(ctx, address)
}
return (&net.Dialer{}).DialContext(ctx, network, address)
}