|
9 | 9 | using System.Net.Security; |
10 | 10 | using System.Net.Sockets; |
11 | 11 | using System.Net.Test.Common; |
| 12 | +using System.Runtime.CompilerServices; |
12 | 13 | using System.Security.Authentication; |
13 | 14 | using System.Security.Cryptography.X509Certificates; |
14 | 15 | using System.Text; |
@@ -940,6 +941,48 @@ await server.AcceptConnectionAsync(async connection2 => |
940 | 941 | } |
941 | 942 | } |
942 | 943 | } |
| 944 | + |
| 945 | + [OuterLoop] |
| 946 | + [Fact] |
| 947 | + public void HandlerDroppedWithoutDisposal_NotKeptAlive() |
| 948 | + { |
| 949 | + var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); |
| 950 | + HandlerDroppedWithoutDisposal_NotKeptAliveCore(tcs); |
| 951 | + for (int i = 0; i < 10; i++) |
| 952 | + { |
| 953 | + GC.Collect(); |
| 954 | + GC.WaitForPendingFinalizers(); |
| 955 | + } |
| 956 | + Assert.True(tcs.Task.IsCompleted); |
| 957 | + } |
| 958 | + |
| 959 | + [MethodImpl(MethodImplOptions.NoInlining)] |
| 960 | + private void HandlerDroppedWithoutDisposal_NotKeptAliveCore(TaskCompletionSource<bool> setOnFinalized) |
| 961 | + { |
| 962 | + // This relies on knowing that in order for the connection pool to operate, it needs |
| 963 | + // to maintain a reference to the supplied IWebProxy. As such, we provide a proxy |
| 964 | + // that when finalized will set our event, so that we can determine the state associated |
| 965 | + // with a handler has gone away. |
| 966 | + IWebProxy p = new PassthroughProxyWithFinalizerCallback(() => setOnFinalized.TrySetResult(true)); |
| 967 | + |
| 968 | + // Make a bunch of requests and drop the associated HttpClient instances after making them, without disposal. |
| 969 | + Task.WaitAll((from i in Enumerable.Range(0, 10) |
| 970 | + select LoopbackServer.CreateClientAndServerAsync( |
| 971 | + url => new HttpClient(new StandardSocketsHttpHandler { Proxy = p }).GetStringAsync(url), |
| 972 | + server => server.AcceptConnectionSendResponseAndCloseAsync())).ToArray()); |
| 973 | + } |
| 974 | + |
| 975 | + private sealed class PassthroughProxyWithFinalizerCallback : IWebProxy |
| 976 | + { |
| 977 | + private readonly Action _callback; |
| 978 | + |
| 979 | + public PassthroughProxyWithFinalizerCallback(Action callback) => _callback = callback; |
| 980 | + ~PassthroughProxyWithFinalizerCallback() => _callback(); |
| 981 | + |
| 982 | + public ICredentials Credentials { get; set; } |
| 983 | + public Uri GetProxy(Uri destination) => destination; |
| 984 | + public bool IsBypassed(Uri host) => true; |
| 985 | + } |
943 | 986 | } |
944 | 987 |
|
945 | 988 | public sealed class SocketsHttpHandler_PublicAPIBehavior_Test |
|
0 commit comments