|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license.
|
3 | 3 | // See the LICENSE file in the project root for more information.
|
4 | 4 |
|
| 5 | +using System.Diagnostics; |
| 6 | +using System.IO; |
| 7 | +using System.IO.Pipes; |
| 8 | +using System.Threading.Tasks; |
5 | 9 | using Xunit;
|
6 | 10 |
|
7 | 11 | namespace System.Net.Sockets.Tests
|
8 | 12 | {
|
9 |
| - public class CreateSocket |
| 13 | + public class CreateSocket : RemoteExecutorTestBase |
10 | 14 | {
|
11 | 15 | public static object[][] DualModeSuccessInputs = {
|
12 | 16 | new object[] { SocketType.Stream, ProtocolType.Tcp },
|
@@ -89,9 +93,94 @@ public void Ctor_Failure(AddressFamily addressFamily, SocketType socketType, Pro
|
89 | 93 | [ConditionalTheory(nameof(SupportsRawSockets))]
|
90 | 94 | public void Ctor_Raw_Success(AddressFamily addressFamily, ProtocolType protocolType)
|
91 | 95 | {
|
92 |
| - using (new Socket(addressFamily, SocketType.Raw, protocolType)) |
93 |
| - { |
94 |
| - } |
| 96 | + using (new Socket(addressFamily, SocketType.Raw, protocolType)) |
| 97 | + { |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + [Theory] |
| 102 | + [InlineData(true, 0)] // Accept |
| 103 | + [InlineData(false, 0)] |
| 104 | + [InlineData(true, 1)] // AcceptAsync |
| 105 | + [InlineData(false, 1)] |
| 106 | + [InlineData(true, 2)] // Begin/EndAccept |
| 107 | + [InlineData(false, 2)] |
| 108 | + public void CtorAndAccept_SocketNotKeptAliveViaInheritance(bool validateClientOuter, int acceptApiOuter) |
| 109 | + { |
| 110 | + // Run the test in another process so as to not have trouble with other tests |
| 111 | + // launching child processes that might impact inheritance. |
| 112 | + RemoteInvoke((validateClientString, acceptApiString) => |
| 113 | + { |
| 114 | + bool validateClient = bool.Parse(validateClientString); |
| 115 | + int acceptApi = int.Parse(acceptApiString); |
| 116 | + |
| 117 | + // Create a listening server. |
| 118 | + using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) |
| 119 | + { |
| 120 | + listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); |
| 121 | + listener.Listen(int.MaxValue); |
| 122 | + EndPoint ep = listener.LocalEndPoint; |
| 123 | + |
| 124 | + // Create a client and connect to that listener. |
| 125 | + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) |
| 126 | + { |
| 127 | + client.Connect(ep); |
| 128 | + |
| 129 | + // Accept the connection using one of multiple accept mechanisms. |
| 130 | + Socket server = |
| 131 | + acceptApi == 0 ? listener.Accept() : |
| 132 | + acceptApi == 1 ? listener.AcceptAsync().GetAwaiter().GetResult() : |
| 133 | + acceptApi == 2 ? Task.Factory.FromAsync(listener.BeginAccept, listener.EndAccept, null).GetAwaiter().GetResult() : |
| 134 | + throw new Exception($"Unexpected {nameof(acceptApi)}: {acceptApi}"); |
| 135 | + |
| 136 | + // Get streams for the client and server, and create a pipe that we'll use |
| 137 | + // to communicate with a child process. |
| 138 | + using (var serverStream = new NetworkStream(server, ownsSocket: true)) |
| 139 | + using (var clientStream = new NetworkStream(client, ownsSocket: true)) |
| 140 | + using (var serverPipe = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)) |
| 141 | + { |
| 142 | + // Create a child process that blocks waiting to receive a signal on the anonymous pipe. |
| 143 | + // The whole purpose of the child is to test whether handles are inherited, so we |
| 144 | + // keep the child process alive until we're done validating that handles close as expected. |
| 145 | + using (RemoteInvoke(clientPipeHandle => |
| 146 | + { |
| 147 | + using (var clientPipe = new AnonymousPipeClientStream(PipeDirection.In, clientPipeHandle)) |
| 148 | + { |
| 149 | + Assert.Equal(42, clientPipe.ReadByte()); |
| 150 | + } |
| 151 | + }, serverPipe.GetClientHandleAsString())) |
| 152 | + { |
| 153 | + if (validateClient) // Validate that the child isn't keeping alive the "new Socket" for the client |
| 154 | + { |
| 155 | + // Send data from the server to client, then validate the client gets EOF when the server closes. |
| 156 | + serverStream.WriteByte(84); |
| 157 | + Assert.Equal(84, clientStream.ReadByte()); |
| 158 | + serverStream.Close(); |
| 159 | + Assert.Equal(-1, clientStream.ReadByte()); |
| 160 | + } |
| 161 | + else // Validate that the child isn't keeping alive the "listener.Accept" for the server |
| 162 | + { |
| 163 | + // Send data from the client to server, then validate the server gets EOF when the client closes. |
| 164 | + clientStream.WriteByte(84); |
| 165 | + Assert.Equal(84, serverStream.ReadByte()); |
| 166 | + clientStream.Close(); |
| 167 | + Assert.Equal(-1, serverStream.ReadByte()); |
| 168 | + } |
| 169 | + |
| 170 | + // And validate that we after closing the listening socket, we're not able to connect. |
| 171 | + listener.Dispose(); |
| 172 | + using (var tmpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) |
| 173 | + { |
| 174 | + Assert.ThrowsAny<SocketException>(() => tmpClient.Connect(ep)); |
| 175 | + } |
| 176 | + |
| 177 | + // Let the child process terminate. |
| 178 | + serverPipe.WriteByte(42); |
| 179 | + } |
| 180 | + } |
| 181 | + } |
| 182 | + } |
| 183 | + }, validateClientOuter.ToString(), acceptApiOuter.ToString()).Dispose(); |
95 | 184 | }
|
96 | 185 | }
|
97 | 186 | }
|
0 commit comments