-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Open
Labels
Status: Requires Reporter ClarificationType: FeatureNew features or improvements in behaviorNew features or improvements in behavior
Description
Use case(s) - what problem will this feature solve?
gRPC fails to connect to Unix domain sockets on Windows when using unix:// URLs with
absolute paths. This seems expected as of today, since its only supported on Unix. Windows 10+ natively supports Unix domain sockets (AF_UNIX), but the URL
parser misinterprets the colon in drive letters (e.g., C:) as a port delimiter, causing
connection attempts to fail with "too many colons in address".
// This fails on Windows
conn, err := grpc.NewClient(
"unix://C:\\Users\\user\\socket.sock",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
// Error: invalid target address unix://C:\Users\...:443: too many colons in addressProposed Solution
Accept the standard file URL format for Windows paths: unix:///C:/path/to/socket (with
three slashes and forward slashes, similar to file:// URLs).
The unix resolver should strip the leading slash from paths like /C:/... to produce the
correct Windows path C:/....
Alternatives Considered
- Custom dialer workaround (current): Use
grpc.WithContextDialer()withpassthrough:///
scheme. This works but is verbose and non-obvious. - Opaque URI form: Convert
unix://C:\pathtounix:C:\pathbefore parsing. Less standard
than the file URL approach.
Additional Context
- Windows Unix socket support: Windows 10 build 17063+ (April 2018)
- Standard Go net.Dial("unix", path) works correctly on Windows
- Only gRPC's URL parsing is affected
- Fix implemented in
internal/resolver/unix/unix.go
Repro
// Minimal reproduction for unix:// URL parsing issue on Windows
//
// Problem: unix:// URLs with Windows absolute paths fail because the colon
// in drive letters (e.g., "C:") is misinterpreted as a port delimiter.
//
// Run: go run minimal_repro.go
package main
import (
"context"
"fmt"
"log"
"net"
"os"
"runtime"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
type greeterServer struct {
pb.UnimplementedGreeterServer
}
func (s *greeterServer) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
if runtime.GOOS != "windows" {
log.Fatal("This reproduction is specific to Windows")
}
// Create Unix socket with Windows absolute path
socketPath := os.TempDir() + "\\grpc-test.sock"
defer os.Remove(socketPath)
// Start server
lis, err := net.Listen("unix", socketPath)
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
defer lis.Close()
srv := grpc.NewServer()
pb.RegisterGreeterServer(srv, &greeterServer{})
go srv.Serve(lis)
defer srv.Stop()
time.Sleep(100 * time.Millisecond)
fmt.Printf("Socket path: %s\n\n", socketPath)
// Try to connect using unix:// URL with Windows path
target := "unix://" + socketPath
fmt.Printf("Connecting to: %s\n", target)
conn, err := grpc.NewClient(target, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Printf("❌ NewClient error: %v\n", err)
return
}
defer conn.Close()
client := pb.NewGreeterClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_, err = client.SayHello(ctx, &pb.HelloRequest{Name: "Test"})
if err != nil {
fmt.Printf("❌ RPC error: %v\n", err)
} else {
fmt.Println("✅ Success!")
}
}Metadata
Metadata
Assignees
Labels
Status: Requires Reporter ClarificationType: FeatureNew features or improvements in behaviorNew features or improvements in behavior