Commit d90adbb1 authored by Honfika's avatar Honfika

various fixes

parent 17a4efec
......@@ -119,7 +119,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
string hostname = e.HttpClient.Request.RequestUri.Host;
await writeToConsole("Tunnel to: " + hostname);
//await writeToConsole("Tunnel to: " + hostname);
if (hostname.Contains("dropbox.com"))
{
......@@ -138,8 +138,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
// intecept & cancel redirect or update requests
private async Task onRequest(object sender, SessionEventArgs e)
{
await writeToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
await writeToConsole(e.HttpClient.Request.Url);
//await writeToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
//await writeToConsole(e.HttpClient.Request.Url);
// store it in the UserData property
// It can be a simple integer, Guid, or any type
......@@ -189,7 +189,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task onResponse(object sender, SessionEventArgs e)
{
await writeToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
//await writeToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
string ext = System.IO.Path.GetExtension(e.HttpClient.Request.RequestUri.AbsolutePath);
......
......@@ -54,6 +54,7 @@
</TabItem>
</TabControl>
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" Orientation="Horizontal">
<ToggleButton IsChecked="True" Content="On/Off" Click="ButtonProxyOnOff_OnClick" />
<TextBlock Text="ClientConnectionCount:" />
<TextBlock Text="{Binding ClientConnectionCount}" Margin="10,0,20,0" />
<TextBlock Text="ServerConnectionCount:" />
......
......@@ -8,6 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using Titanium.Web.Proxy.EventArguments;
......@@ -95,7 +96,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
Dispatcher.Invoke(() => { ServerConnectionCount = proxyServer.ServerConnectionCount; });
};
proxyServer.Start();
proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp);
InitializeComponent();
......@@ -284,15 +285,26 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{
if (e.Key == Key.Delete)
{
bool isSelected = false;
var selectedItems = ((ListView)sender).SelectedItems;
Sessions.SuppressNotification = true;
foreach (var item in selectedItems.Cast<SessionListItem>().ToArray())
{
if (item == SelectedSession)
{
isSelected = true;
}
Sessions.Remove(item);
sessionDictionary.Remove(item.HttpClient);
}
Sessions.SuppressNotification = false;
if (isSelected)
{
SelectedSession = null;
}
}
}
......@@ -300,6 +312,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{
if (SelectedSession == null)
{
TextBoxRequest.Text = null;
TextBoxResponse.Text = string.Empty;
ImageResponse.Source = null;
return;
}
......@@ -307,7 +322,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
var session = SelectedSession.HttpClient;
var request = session.Request;
var fullData = (request.IsBodyRead ? request.Body : null) ?? new byte[0];
var fullData = (request.IsBodyRead ? request.Body : null) ?? Array.Empty<byte>();
var data = fullData;
bool truncated = data.Length > truncateLimit;
if (truncated)
......@@ -324,7 +339,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
TextBoxRequest.Text = sb.ToString();
var response = session.Response;
fullData = (response.IsBodyRead ? response.Body : null) ?? new byte[0];
fullData = (response.IsBodyRead ? response.Body : null) ?? Array.Empty<byte>();
data = fullData;
truncated = data.Length > truncateLimit;
if (truncated)
......@@ -348,10 +363,13 @@ namespace Titanium.Web.Proxy.Examples.Wpf
try
{
using (MemoryStream stream = new MemoryStream(fullData))
if (fullData.Length > 0)
{
ImageResponse.Source =
BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
using (var stream = new MemoryStream(fullData))
{
ImageResponse.Source =
BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
}
catch
......@@ -359,5 +377,19 @@ namespace Titanium.Web.Proxy.Examples.Wpf
ImageResponse.Source = null;
}
}
private void ButtonProxyOnOff_OnClick(object sender, RoutedEventArgs e)
{
var button = (ToggleButton)sender;
if (button.IsChecked == true)
{
proxyServer.SetAsSystemProxy((ExplicitProxyEndPoint)proxyServer.ProxyEndPoints[0],
ProxyProtocolType.AllHttp);
}
else
{
proxyServer.RestoreOriginalProxySettings();
}
}
}
}
......@@ -265,8 +265,8 @@ namespace Titanium.Web.Proxy
try
{
await clientStream.ReadAsync(data, 0, available, cancellationToken);
// clientStream.Available should be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available, cancellationToken);
await connection.StreamWriter.WriteAsync(data, 0, available, true, cancellationToken);
}
finally
......@@ -279,10 +279,13 @@ namespace Titanium.Web.Proxy
((ConnectResponse)connectArgs.HttpClient.Response).ServerHelloInfo = serverHelloInfo;
}
await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, ExceptionFunc);
if (!clientStream.IsClosed && !connection.Stream.IsClosed)
{
await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, ExceptionFunc);
}
}
finally
{
......
......@@ -481,6 +481,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
try
{
var cutOff = DateTime.Now.AddSeconds(-1 * Server.ConnectionTimeOutSeconds);
foreach (var item in cache)
{
var queue = item.Value;
......@@ -489,7 +490,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
if (queue.TryDequeue(out var connection))
{
var cutOff = DateTime.Now.AddSeconds(-1 * Server.ConnectionTimeOutSeconds);
if (!Server.EnableConnectionPool
|| connection.LastAccess < cutOff)
{
......@@ -512,7 +512,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
var emptyKeys = cache.Where(x => x.Value.Count == 0).Select(x => x.Key).ToList();
foreach (string key in emptyKeys)
{
cache.TryRemove(key, out var _);
cache.TryRemove(key, out _);
}
}
finally
......
......@@ -516,6 +516,20 @@ namespace Titanium.Web.Proxy
DisableSystemProxy(ProxyProtocolType.Https);
}
/// <summary>
/// Restores the original proxy settings.
/// </summary>
public void RestoreOriginalProxySettings()
{
if (!RunTime.IsWindows)
{
throw new NotSupportedException(@"Setting system proxy settings are only supported in Windows.
Please manually configure your operating system to use this proxy's port and address.");
}
systemProxySettingsManager.RestoreOriginalSettings();
}
/// <summary>
/// Clear the specified proxy setting for current machine.
/// </summary>
......@@ -524,7 +538,7 @@ namespace Titanium.Web.Proxy
if (!RunTime.IsWindows)
{
throw new NotSupportedException(@"Setting system proxy settings are only supported in Windows.
Please manually confugure you operating system to use this proxy's port and address.");
Please manually configure your operating system to use this proxy's port and address.");
}
systemProxySettingsManager.RemoveProxy(protocolType);
......
......@@ -65,6 +65,11 @@ namespace Titanium.Web.Proxy
// (assuming HTTP connection is kept alive by client)
while (true)
{
if (clientStream.IsClosed)
{
return;
}
// read the request line
string httpCmd = await clientStream.ReadLineAsync(cancellationToken);
......@@ -255,11 +260,11 @@ namespace Titanium.Web.Proxy
throw new Exception("Session was terminated by user.");
}
//Release server connection for each HTTP session instead of per client connection.
//This will be more efficient especially when client is idly holding server connection
//between sessions without using it.
//Do not release authenticated connections for performance reasons.
//Otherwise it will keep authenticating per session.
// Release server connection for each HTTP session instead of per client connection.
// This will be more efficient especially when client is idly holding server connection
// between sessions without using it.
// Do not release authenticated connections for performance reasons.
// Otherwise it will keep authenticating per session.
if (EnableConnectionPool && connection != null
&& !connection.IsWinAuthenticated)
{
......
using System.Collections.Concurrent;
using System.Buffers;
using System.Collections.Concurrent;
namespace Titanium.Web.Proxy.StreamExtended.BufferPool
{
......@@ -10,8 +11,6 @@ namespace Titanium.Web.Proxy.StreamExtended.BufferPool
/// </summary>
public class DefaultBufferPool : IBufferPool
{
private readonly ConcurrentStack<byte[]> buffers = new ConcurrentStack<byte[]>();
/// <summary>
/// Gets a buffer.
/// </summary>
......@@ -19,12 +18,7 @@ namespace Titanium.Web.Proxy.StreamExtended.BufferPool
/// <returns></returns>
public byte[] GetBuffer(int bufferSize)
{
if (!buffers.TryPop(out var buffer) || buffer.Length != bufferSize)
{
buffer = new byte[bufferSize];
}
return buffer;
return ArrayPool<byte>.Shared.Rent(bufferSize);
}
/// <summary>
......@@ -33,15 +27,11 @@ namespace Titanium.Web.Proxy.StreamExtended.BufferPool
/// <param name="buffer">The buffer.</param>
public void ReturnBuffer(byte[] buffer)
{
if (buffer != null)
{
buffers.Push(buffer);
}
ArrayPool<byte>.Shared.Return(buffer);
}
public void Dispose()
{
buffers.Clear();
}
}
}
......@@ -9,6 +9,7 @@ namespace Titanium.Web.Proxy.StreamExtended.BufferPool
public interface IBufferPool : IDisposable
{
byte[] GetBuffer(int bufferSize);
void ReturnBuffer(byte[] buffer);
}
}
......@@ -17,13 +17,14 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// <seealso cref="System.IO.Stream" />
public class CustomBufferedStream : Stream, ICustomStreamReader
{
private readonly Stream baseStream;
private readonly bool leaveOpen;
private byte[] streamBuffer;
// default to UTF-8
private static readonly Encoding encoding = Encoding.UTF8;
private static bool networkStreamHack = true;
private int bufferLength;
private int bufferPos;
......@@ -40,8 +41,28 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
public event EventHandler<DataEventArgs> DataWrite;
public Stream BaseStream { get; }
public bool IsClosed => closed;
static CustomBufferedStream()
{
// TODO: remove this hack when removing .NET 4.x support
try
{
var method = typeof(NetworkStream).GetMethod(nameof(Stream.ReadAsync),
new Type[] { typeof(byte[]), typeof(int), typeof(int), typeof(CancellationToken) });
if (method != null && method.DeclaringType != typeof(Stream))
{
networkStreamHack = false;
}
}
catch
{
// ignore
}
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomBufferedStream"/> class.
/// </summary>
......@@ -51,7 +72,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// <param name="leaveOpen"><see langword="true" /> to leave the stream open after disposing the <see cref="T:CustomBufferedStream" /> object; otherwise, <see langword="false" />.</param>
public CustomBufferedStream(Stream baseStream, IBufferPool bufferPool, int bufferSize, bool leaveOpen = false)
{
this.baseStream = baseStream;
BaseStream = baseStream;
BufferSize = bufferSize;
this.leaveOpen = leaveOpen;
streamBuffer = bufferPool.GetBuffer(bufferSize);
......@@ -63,7 +84,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// </summary>
public override void Flush()
{
baseStream.Flush();
BaseStream.Flush();
}
/// <summary>
......@@ -78,7 +99,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
{
bufferLength = 0;
bufferPos = 0;
return baseStream.Seek(offset, origin);
return BaseStream.Seek(offset, origin);
}
/// <summary>
......@@ -87,7 +108,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// <param name="value">The desired length of the current stream in bytes.</param>
public override void SetLength(long value)
{
baseStream.SetLength(value);
BaseStream.SetLength(value);
}
/// <summary>
......@@ -127,7 +148,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
public override void Write(byte[] buffer, int offset, int count)
{
OnDataWrite(buffer, offset, count);
baseStream.Write(buffer, offset, count);
BaseStream.Write(buffer, offset, count);
}
/// <summary>
......@@ -160,7 +181,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// </returns>
public override Task FlushAsync(CancellationToken cancellationToken = default)
{
return baseStream.FlushAsync(cancellationToken);
return BaseStream.FlushAsync(cancellationToken);
}
/// <summary>
......@@ -329,7 +350,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
{
OnDataWrite(buffer, offset, count);
await baseStream.WriteAsync(buffer, offset, count, cancellationToken);
await BaseStream.WriteAsync(buffer, offset, count, cancellationToken);
}
/// <summary>
......@@ -343,7 +364,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
{
buffer[0] = value;
OnDataWrite(buffer, 0, 1);
baseStream.Write(buffer, 0, 1);
BaseStream.Write(buffer, 0, 1);
}
finally
{
......@@ -373,7 +394,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
closed = true;
if (!leaveOpen)
{
baseStream.Dispose();
BaseStream.Dispose();
}
var buffer = streamBuffer;
......@@ -385,27 +406,27 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// <summary>
/// When overridden in a derived class, gets a value indicating whether the current stream supports reading.
/// </summary>
public override bool CanRead => baseStream.CanRead;
public override bool CanRead => BaseStream.CanRead;
/// <summary>
/// When overridden in a derived class, gets a value indicating whether the current stream supports seeking.
/// </summary>
public override bool CanSeek => baseStream.CanSeek;
public override bool CanSeek => BaseStream.CanSeek;
/// <summary>
/// When overridden in a derived class, gets a value indicating whether the current stream supports writing.
/// </summary>
public override bool CanWrite => baseStream.CanWrite;
public override bool CanWrite => BaseStream.CanWrite;
/// <summary>
/// Gets a value that determines whether the current stream can time out.
/// </summary>
public override bool CanTimeout => baseStream.CanTimeout;
public override bool CanTimeout => BaseStream.CanTimeout;
/// <summary>
/// When overridden in a derived class, gets the length in bytes of the stream.
/// </summary>
public override long Length => baseStream.Length;
public override long Length => BaseStream.Length;
/// <summary>
/// Gets a value indicating whether data is available.
......@@ -422,8 +443,8 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// </summary>
public override long Position
{
get => baseStream.Position;
set => baseStream.Position = value;
get => BaseStream.Position;
set => BaseStream.Position = value;
}
/// <summary>
......@@ -431,8 +452,8 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// </summary>
public override int ReadTimeout
{
get => baseStream.ReadTimeout;
set => baseStream.ReadTimeout = value;
get => BaseStream.ReadTimeout;
set => BaseStream.ReadTimeout = value;
}
/// <summary>
......@@ -440,8 +461,8 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// </summary>
public override int WriteTimeout
{
get => baseStream.WriteTimeout;
set => baseStream.WriteTimeout = value;
get => BaseStream.WriteTimeout;
set => BaseStream.WriteTimeout = value;
}
/// <summary>
......@@ -466,7 +487,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
bool result = false;
try
{
int readBytes = baseStream.Read(streamBuffer, bufferLength, streamBuffer.Length - bufferLength);
int readBytes = BaseStream.Read(streamBuffer, bufferLength, streamBuffer.Length - bufferLength);
result = readBytes > 0;
if (result)
{
......@@ -516,7 +537,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
bool result = false;
try
{
int readBytes = await baseStream.ReadAsync(streamBuffer, bufferLength, bytesToRead, cancellationToken);
int readBytes = await BaseStream.ReadAsync(streamBuffer, bufferLength, bytesToRead, cancellationToken);
result = readBytes > 0;
if (result)
{
......@@ -622,5 +643,81 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
buffer = newBuffer;
}
/// <summary>
/// Base Stream.BeginRead will call this.Read and block thread (we don't want this, Network stream handles async)
/// In order to really async Reading Launch this.ReadAsync as Task will fire NetworkStream.ReadAsync
/// See Threads here :
/// https://github.com/justcoding121/Stream-Extended/pull/43
/// https://github.com/justcoding121/Titanium-Web-Proxy/issues/575
/// </summary>
/// <returns></returns>
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
if (!networkStreamHack)
{
return base.BeginRead(buffer, offset, count, callback, state);
}
var vAsyncResult = this.ReadAsync(buffer, offset, count);
vAsyncResult.ContinueWith(pAsyncResult =>
{
//use TaskExtended to pass State as AsyncObject
//callback will call EndRead (otherwise, it will block)
callback?.Invoke(new TaskResult<int>(pAsyncResult, state));
});
return vAsyncResult;
}
/// <summary>
/// override EndRead to handle async Reading (see BeginRead comment)
/// </summary>
/// <returns></returns>
public override int EndRead(IAsyncResult asyncResult)
{
if (!networkStreamHack)
{
return base.EndRead(asyncResult);
}
return ((TaskResult<int>)asyncResult).Result;
}
/// <summary>
/// Fix the .net bug with SslStream slow WriteAsync
/// https://github.com/justcoding121/Titanium-Web-Proxy/issues/495
/// Stream.BeginWrite + Stream.BeginRead uses the same SemaphoreSlim(1)
/// That's why we need to call NetworkStream.BeginWrite only (while read is waiting SemaphoreSlim)
/// </summary>
/// <returns></returns>
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
if (!networkStreamHack)
{
return base.BeginWrite(buffer, offset, count, callback, state);
}
var vAsyncResult = this.WriteAsync(buffer, offset, count);
vAsyncResult.ContinueWith(pAsyncResult =>
{
callback?.Invoke(new TaskResult(pAsyncResult, state));
});
return vAsyncResult;
}
public override void EndWrite(IAsyncResult asyncResult)
{
if (!networkStreamHack)
{
base.EndWrite(asyncResult);
return;
}
((TaskResult)asyncResult).GetResult();
}
}
}
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.StreamExtended.Network
{
/// <summary>
/// Mimic a Task but you can set AsyncState
/// </summary>
/// <typeparam name="T"></typeparam>
public class TaskResult : IAsyncResult
{
Task Task;
readonly object asyncState;
public TaskResult(Task pTask, object state)
{
Task = pTask;
asyncState = state;
}
public object AsyncState => asyncState;
public WaitHandle AsyncWaitHandle => ((IAsyncResult)Task).AsyncWaitHandle;
public bool CompletedSynchronously => ((IAsyncResult)Task).CompletedSynchronously;
public bool IsCompleted => Task.IsCompleted;
public void GetResult() { this.Task.GetAwaiter().GetResult(); }
}
/// <summary>
/// Mimic a Task<T> but you can set AsyncState
/// </summary>
/// <typeparam name="T"></typeparam>
public class TaskResult<T> : IAsyncResult
{
Task<T> Task;
readonly object asyncState;
public TaskResult(Task<T> pTask, object state)
{
Task = pTask;
asyncState = state;
}
public object AsyncState => asyncState;
public WaitHandle AsyncWaitHandle => ((IAsyncResult)Task).AsyncWaitHandle;
public bool CompletedSynchronously => ((IAsyncResult)Task).CompletedSynchronously;
public bool IsCompleted => Task.IsCompleted;
public T Result => Task.Result;
}
}
......@@ -14,6 +14,7 @@
<ItemGroup>
<PackageReference Include="BrotliSharpLib" Version="0.3.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
......
......@@ -14,6 +14,7 @@
<ItemGroup>
<PackageReference Include="BrotliSharpLib" Version="0.3.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
......
......@@ -14,6 +14,7 @@
<ItemGroup>
<PackageReference Include="BrotliSharpLib" Version="0.3.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
......
......@@ -18,12 +18,14 @@
<dependency id="Portable.BouncyCastle" version="1.8.5" />
<dependency id="BrotliSharpLib" version="0.3.3" />
<dependency id="Microsoft.Win32.Registry" version="4.5.0" />
<dependency id="System.Buffers" version="4.5.0" />
<dependency id="System.Security.Principal.Windows" version="4.5.1" />
</group>
<group targetFramework="netcoreapp2.1">
<dependency id="Portable.BouncyCastle" version="1.8.5" />
<dependency id="BrotliSharpLib" version="0.3.3" />
<dependency id="Microsoft.Win32.Registry" version="4.5.0" />
<dependency id="System.Buffers" version="4.5.0" />
<dependency id="System.Security.Principal.Windows" version="4.5.1" />
</group>
</dependencies>
......
......@@ -103,7 +103,6 @@ namespace Titanium.Web.Proxy
try
{
CustomBufferedStream serverStream = null;
int available = clientStream.Available;
if (available > 0)
......@@ -114,9 +113,7 @@ namespace Titanium.Web.Proxy
{
// clientStream.Available should be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available, cancellationToken);
serverStream = connection.Stream;
await serverStream.WriteAsync(data, 0, available, cancellationToken);
await serverStream.FlushAsync(cancellationToken);
await connection.StreamWriter.WriteAsync(data, 0, available, true, cancellationToken);
}
finally
{
......@@ -124,8 +121,11 @@ namespace Titanium.Web.Proxy
}
}
await TcpHelper.SendRaw(clientStream, serverStream, BufferPool, BufferSize,
null, null, cancellationTokenSource, ExceptionFunc);
if (!clientStream.IsClosed && !connection.Stream.IsClosed)
{
await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
null, null, cancellationTokenSource, ExceptionFunc);
}
}
finally
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment