protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancel) { Guard.NotNull(nameof(request), request); if (cancel == null) { cancel = CancellationToken.None; } // https://tools.ietf.org/html/rfc7230#section-2.7.1 // A sender MUST NOT generate an "http" URI with an empty host identifier. var host = Guard.NotNullOrEmptyOrWhitespace($"{nameof(request)}.{nameof(request.RequestUri)}.{nameof(request.RequestUri.DnsSafeHost)}", request.RequestUri.DnsSafeHost, trim: true); using (await DisposeRequestLock.ReaderLockAsync(cancel).ConfigureAwait(false)) using (var connectLockTask = await ConnectLock.LockAsync(cancel).ConfigureAwait(false)) // this makes sure clients with the same host don't try to connect concurrently, it gets released after connection established { KeyValuePair <TorSocks5Client, AsyncLock> clientLockPair = TryFindClientLockPair(host, request.RequestUri.Port); AsyncLock clientLock = clientLockPair.Value ?? new AsyncLock(); // this makes sure clients with the same host don't work concurrently using (await clientLock.LockAsync(cancel).ConfigureAwait(false)) { TorSocks5Client client = await SendAsync(request, host, connectLockTask, clientLockPair, clientLock, cancel); try { return(await new HttpResponseMessage().CreateNewAsync(client.Stream, request.Method).ConfigureAwait(false)); } catch (IOException) { // the connection is lost, reconnect client = await SendAsync(request, host, connectLockTask, clientLockPair, clientLock, cancel); return(await new HttpResponseMessage().CreateNewAsync(client.Stream, request.Method).ConfigureAwait(false)); } } } }
protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancel) { Guard.NotNull(nameof(request), request); if (cancel == null) { cancel = CancellationToken.None; } // https://tools.ietf.org/html/rfc7230#section-2.7.1 // A sender MUST NOT generate an "http" URI with an empty host identifier. var host = Guard.NotNullOrEmptyOrWhitespace($"{nameof(request)}.{nameof(request.RequestUri)}.{nameof(request.RequestUri.DnsSafeHost)}", request.RequestUri.DnsSafeHost, trim: true); using (var linuxOsxLock = await LinuxOsxLock.LockAsync(cancel).ConfigureAwait(false)) // Linux and OSX is terrible, rather do everything in sync, if windows, it'll be released right away, this must be in effect to bypass this linuxosxbug: if (client != null && (!client.IsConnected || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))) using (await DisposeRequestLock.ReaderLockAsync(cancel).ConfigureAwait(false)) using (var connectLockTask = await ConnectLock.LockAsync(cancel).ConfigureAwait(false)) // this makes sure clients with the same host don't try to connect concurrently, it gets released after connection established { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { linuxOsxLock.Dispose(); } KeyValuePair <TorSocks5Client, AsyncLock> clientLockPair = TryFindClientLockPair(host, request.RequestUri.Port); AsyncLock clientLock = clientLockPair.Value ?? new AsyncLock(); // this makes sure clients with the same host don't work concurrently using (await clientLock.LockAsync(cancel).ConfigureAwait(false)) { TorSocks5Client client = await SendAsync(request, host, connectLockTask, clientLockPair, clientLock, cancel); try { return(await new HttpResponseMessage().CreateNewAsync(client.Stream, request.Method).ConfigureAwait(false)); } catch (IOException) { // the connection is lost, reconnect client = await SendAsync(request, host, connectLockTask, clientLockPair, clientLock, cancel); return(await new HttpResponseMessage().CreateNewAsync(client.Stream, request.Method).ConfigureAwait(false)); } } } }