1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build unix
6
7package net
8
9import (
10	"internal/syscall/unix"
11	"testing"
12)
13
14// For backward compatibility, opening a net.Conn, turning it into an os.File,
15// and calling the Fd method should return a blocking descriptor.
16func TestFileFdBlocks(t *testing.T) {
17	if !testableNetwork("unix") {
18		t.Skipf("skipping: unix sockets not supported")
19	}
20
21	ls := newLocalServer(t, "unix")
22	defer ls.teardown()
23
24	errc := make(chan error, 1)
25	done := make(chan bool)
26	handler := func(ls *localServer, ln Listener) {
27		server, err := ln.Accept()
28		errc <- err
29		if err != nil {
30			return
31		}
32		defer server.Close()
33		<-done
34	}
35	if err := ls.buildup(handler); err != nil {
36		t.Fatal(err)
37	}
38	defer close(done)
39
40	client, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
41	if err != nil {
42		t.Fatal(err)
43	}
44	defer client.Close()
45
46	if err := <-errc; err != nil {
47		t.Fatalf("server error: %v", err)
48	}
49
50	// The socket should be non-blocking.
51	rawconn, err := client.(*UnixConn).SyscallConn()
52	if err != nil {
53		t.Fatal(err)
54	}
55	err = rawconn.Control(func(fd uintptr) {
56		nonblock, err := unix.IsNonblock(int(fd))
57		if err != nil {
58			t.Fatal(err)
59		}
60		if !nonblock {
61			t.Fatal("unix socket is in blocking mode")
62		}
63	})
64	if err != nil {
65		t.Fatal(err)
66	}
67
68	file, err := client.(*UnixConn).File()
69	if err != nil {
70		t.Fatal(err)
71	}
72
73	// At this point the descriptor should still be non-blocking.
74	rawconn, err = file.SyscallConn()
75	if err != nil {
76		t.Fatal(err)
77	}
78	err = rawconn.Control(func(fd uintptr) {
79		nonblock, err := unix.IsNonblock(int(fd))
80		if err != nil {
81			t.Fatal(err)
82		}
83		if !nonblock {
84			t.Fatal("unix socket as os.File is in blocking mode")
85		}
86	})
87	if err != nil {
88		t.Fatal(err)
89	}
90
91	fd := file.Fd()
92
93	// Calling Fd should have put the descriptor into blocking mode.
94	nonblock, err := unix.IsNonblock(int(fd))
95	if err != nil {
96		t.Fatal(err)
97	}
98	if nonblock {
99		t.Error("unix socket through os.File.Fd is non-blocking")
100	}
101}
102