Unverified Commit 3b807dad authored by honfika's avatar honfika Committed by GitHub

Merge pull request #664 from justcoding121/master

beta
parents 108c0401 9b4e3dca
......@@ -3,6 +3,7 @@ using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.StreamExtended.BufferPool;
using Titanium.Web.Proxy.StreamExtended.Network;
......@@ -11,16 +12,16 @@ namespace Titanium.Web.Proxy.EventArguments
internal class LimitedStream : Stream
{
private readonly IBufferPool bufferPool;
private readonly CustomBufferedStream baseStream;
private readonly IHttpStreamReader baseReader;
private readonly bool isChunked;
private long bytesRemaining;
private bool readChunkTrail;
internal LimitedStream(CustomBufferedStream baseStream, IBufferPool bufferPool, bool isChunked,
internal LimitedStream(IHttpStreamReader baseStream, IBufferPool bufferPool, bool isChunked,
long contentLength)
{
this.baseStream = baseStream;
this.baseReader = baseStream;
this.bufferPool = bufferPool;
this.isChunked = isChunked;
bytesRemaining = isChunked
......@@ -49,12 +50,12 @@ namespace Titanium.Web.Proxy.EventArguments
if (readChunkTrail)
{
// read the chunk trail of the previous chunk
string? s = baseStream.ReadLineAsync().Result;
string? s = baseReader.ReadLineAsync().Result;
}
readChunkTrail = true;
string? chunkHead = baseStream.ReadLineAsync().Result!;
string? chunkHead = baseReader.ReadLineAsync().Result!;
int idx = chunkHead.IndexOf(";", StringComparison.Ordinal);
if (idx >= 0)
{
......@@ -73,7 +74,7 @@ namespace Titanium.Web.Proxy.EventArguments
bytesRemaining = -1;
// chunk trail
var task = baseStream.ReadLineAsync();
var task = baseReader.ReadLineAsync();
if (!task.IsCompleted)
task.AsTask().Wait();
}
......@@ -119,7 +120,7 @@ namespace Titanium.Web.Proxy.EventArguments
}
int toRead = (int)Math.Min(count, bytesRemaining);
int res = baseStream.Read(buffer, offset, toRead);
int res = baseReader.Read(buffer, offset, toRead);
bytesRemaining -= res;
if (res == 0)
......
......@@ -10,6 +10,7 @@ using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments
......@@ -35,8 +36,8 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ProxyClient proxyClient, ConnectRequest? connectRequest, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, proxyClient, connectRequest, new Request(), cancellationTokenSource)
internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest? connectRequest, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, clientConnection, clientStream, connectRequest, new Request(), cancellationTokenSource)
{
}
......@@ -64,14 +65,9 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs>? MultipartRequestPartSent;
private CustomBufferedStream getStreamReader(bool isRequest)
private HttpStream getStream(bool isRequest)
{
return isRequest ? ProxyClient.ClientStream : HttpClient.Connection.Stream;
}
private HttpWriter getStreamWriter(bool isRequest)
{
return isRequest ? (HttpWriter)ProxyClient.ClientStreamWriter : HttpClient.Connection.StreamWriter;
return isRequest ? (HttpStream)ClientStream : HttpClient.Connection.Stream;
}
/// <summary>
......@@ -197,21 +193,19 @@ namespace Titanium.Web.Proxy.EventArguments
private async Task<byte[]> readBodyAsync(bool isRequest, CancellationToken cancellationToken)
{
using (var bodyStream = new MemoryStream())
{
var writer = new HttpWriter(bodyStream, BufferPool);
if (isRequest)
{
await CopyRequestBodyAsync(writer, TransformationMode.Uncompress, cancellationToken);
}
else
{
await CopyResponseBodyAsync(writer, TransformationMode.Uncompress, cancellationToken);
}
using var bodyStream = new MemoryStream();
using var http = new HttpStream(bodyStream, BufferPool);
return bodyStream.ToArray();
if (isRequest)
{
await CopyRequestBodyAsync(http, TransformationMode.Uncompress, cancellationToken);
}
else
{
await CopyResponseBodyAsync(http, TransformationMode.Uncompress, cancellationToken);
}
return bodyStream.ToArray();
}
/// <summary>
......@@ -229,18 +223,16 @@ namespace Titanium.Web.Proxy.EventArguments
return;
}
using (var bodyStream = new MemoryStream())
{
var writer = new HttpWriter(bodyStream, BufferPool);
await copyBodyAsync(isRequest, true, writer, TransformationMode.None, null, cancellationToken);
}
using var bodyStream = new MemoryStream();
using var http = new HttpStream(bodyStream, BufferPool);
await copyBodyAsync(isRequest, true, http, TransformationMode.None, null, cancellationToken);
}
/// <summary>
/// This is called when the request is PUT/POST/PATCH to read the body
/// </summary>
/// <returns></returns>
internal async Task CopyRequestBodyAsync(HttpWriter writer, TransformationMode transformation, CancellationToken cancellationToken)
internal async Task CopyRequestBodyAsync(IHttpStreamWriter writer, TransformationMode transformation, CancellationToken cancellationToken)
{
var request = HttpClient.Request;
......@@ -249,7 +241,7 @@ namespace Titanium.Web.Proxy.EventArguments
// send the request body bytes to server
if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData)
{
var reader = getStreamReader(true);
var reader = getStream(true);
var boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
using (var copyStream = new CopyStream(reader, writer, BufferPool))
......@@ -279,14 +271,14 @@ namespace Titanium.Web.Proxy.EventArguments
}
}
internal async Task CopyResponseBodyAsync(HttpWriter writer, TransformationMode transformation, CancellationToken cancellationToken)
internal async Task CopyResponseBodyAsync(IHttpStreamWriter writer, TransformationMode transformation, CancellationToken cancellationToken)
{
await copyBodyAsync(false, false, writer, transformation, OnDataReceived, cancellationToken);
}
private async Task copyBodyAsync(bool isRequest, bool useOriginalHeaderValues, HttpWriter writer, TransformationMode transformation, Action<byte[], int, int>? onCopy, CancellationToken cancellationToken)
private async Task copyBodyAsync(bool isRequest, bool useOriginalHeaderValues, IHttpStreamWriter writer, TransformationMode transformation, Action<byte[], int, int>? onCopy, CancellationToken cancellationToken)
{
var stream = getStreamReader(isRequest);
var stream = getStream(isRequest);
var requestResponse = isRequest ? (RequestResponseBase)HttpClient.Request : HttpClient.Response;
......@@ -313,10 +305,8 @@ namespace Titanium.Web.Proxy.EventArguments
try
{
using (var bufStream = new CustomBufferedStream(s, BufferPool, true))
{
await writer.CopyBodyAsync(bufStream, false, -1, onCopy, cancellationToken);
}
var http = new HttpStream(s, BufferPool, true);
await writer.CopyBodyAsync(http, false, -1, onCopy, cancellationToken);
}
finally
{
......
......@@ -26,7 +26,12 @@ namespace Titanium.Web.Proxy.EventArguments
internal TcpServerConnection ServerConnection => HttpClient.Connection;
internal TcpClientConnection ClientConnection => ProxyClient.Connection;
/// <summary>
/// Holds a reference to client
/// </summary>
internal TcpClientConnection ClientConnection { get; }
internal HttpClientStream ClientStream { get; }
protected readonly IBufferPool BufferPool;
protected readonly ExceptionHandler ExceptionFunc;
......@@ -41,7 +46,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
/// </summary>
private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
ProxyClient proxyClient, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
{
BufferPool = server.BufferPool;
ExceptionFunc = server.ExceptionFunc;
......@@ -49,17 +54,13 @@ namespace Titanium.Web.Proxy.EventArguments
CancellationTokenSource = cancellationTokenSource;
ProxyClient = proxyClient;
HttpClient = new HttpWebClient(connectRequest, request, new Lazy<int>(() => ProxyClient.Connection.GetProcessId(endPoint)));
ClientConnection = clientConnection;
ClientStream = clientStream;
HttpClient = new HttpWebClient(connectRequest, request, new Lazy<int>(() => clientConnection.GetProcessId(endPoint)));
LocalEndPoint = endPoint;
EnableWinAuth = server.EnableWinAuth && isWindowsAuthenticationSupported;
}
/// <summary>
/// Holds a reference to client
/// </summary>
internal ProxyClient ProxyClient { get; }
/// <summary>
/// Returns a user data for this request/response session which is
/// same as the user data of HttpClient.
......@@ -93,7 +94,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Client End Point.
/// </summary>
public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.Connection.RemoteEndPoint;
public IPEndPoint ClientEndPoint => (IPEndPoint)ClientConnection.RemoteEndPoint;
/// <summary>
/// The web client used to communicate with server for this session.
......@@ -106,7 +107,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Are we using a custom upstream HTTP(S) proxy?
/// </summary>
public ExternalProxy? CustomUpStreamProxyUsed { get; internal set; }
public IExternalProxy? CustomUpStreamProxyUsed { get; internal set; }
/// <summary>
/// Local endpoint via which we make the request.
......
using System;
using System.Threading;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments
......@@ -15,8 +17,8 @@ namespace Titanium.Web.Proxy.EventArguments
private bool? isHttpsConnect;
internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest,
ProxyClient proxyClient, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, proxyClient, connectRequest, connectRequest, cancellationTokenSource)
TcpClientConnection clientConnection, HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, clientConnection, clientStream, connectRequest, connectRequest, cancellationTokenSource)
{
}
......
......@@ -36,8 +36,7 @@ namespace Titanium.Web.Proxy
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool);
var clientStream = new HttpClientStream(clientConnection.GetStream(), BufferPool);
Task<TcpServerConnection>? prefetchConnectionTask = null;
bool closeServerConnection = false;
......@@ -70,7 +69,7 @@ namespace Titanium.Web.Proxy
await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);
connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest,
new ProxyClient(clientConnection, clientStream, clientStreamWriter), cancellationTokenSource);
clientConnection, clientStream, cancellationTokenSource);
clientStream.DataRead += (o, args) => connectArgs.OnDataSent(args.Buffer, args.Offset, args.Count);
clientStream.DataWrite += (o, args) => connectArgs.OnDataReceived(args.Buffer, args.Offset, args.Count);
......@@ -92,7 +91,7 @@ namespace Titanium.Web.Proxy
}
// send the response
await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
await clientStream.WriteResponseAsync(connectArgs.HttpClient.Response,
cancellationToken: cancellationToken);
return;
}
......@@ -102,7 +101,7 @@ namespace Titanium.Web.Proxy
await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc);
// send the response
await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
await clientStream.WriteResponseAsync(connectArgs.HttpClient.Response,
cancellationToken: cancellationToken);
return;
}
......@@ -115,14 +114,13 @@ namespace Titanium.Web.Proxy
response.Headers.FixProxyHeaders();
connectArgs.HttpClient.Response = response;
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken);
await clientStream.WriteResponseAsync(response, cancellationToken: cancellationToken);
var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);
bool isClientHello = clientHelloInfo != null;
if (clientHelloInfo != null)
{
connectRequest.IsHttps = true;
connectRequest.TunnelType = TunnelType.Https;
connectRequest.ClientHelloInfo = clientHelloInfo;
}
......@@ -131,6 +129,7 @@ namespace Titanium.Web.Proxy
if (decryptSsl && clientHelloInfo != null)
{
connectRequest.IsHttps = true; // todo: move this line to the previous "if"
clientConnection.SslProtocol = clientHelloInfo.SslProtocol;
bool http2Supported = false;
......@@ -216,10 +215,9 @@ namespace Titanium.Web.Proxy
#endif
// HTTPS server created - we can now decrypt the client's traffic
clientStream = new CustomBufferedStream(sslStream, BufferPool);
clientStream = new HttpClientStream(sslStream, BufferPool);
clientStream.DataRead += (o, args) => connectArgs.OnDecryptedDataSent(args.Buffer, args.Offset, args.Count);
clientStream.DataWrite += (o, args) => connectArgs.OnDecryptedDataReceived(args.Buffer, args.Offset, args.Count);
clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool);
}
catch (Exception e)
{
......@@ -274,7 +272,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);
await connection.StreamWriter.WriteAsync(data, 0, available, true, cancellationToken);
await connection.Stream.WriteAsync(data, 0, available, true, cancellationToken);
}
finally
{
......@@ -335,9 +333,9 @@ namespace Titanium.Web.Proxy
{
#if NETSTANDARD2_1
var connectionPreface = new ReadOnlyMemory<byte>(Http2Helper.ConnectionPreface);
await connection.StreamWriter.WriteAsync(connectionPreface, cancellationToken);
await connection.Stream.WriteAsync(connectionPreface, cancellationToken);
await Http2Helper.SendHttp2(clientStream, connection.Stream,
() => new SessionEventArgs(this, endPoint, new ProxyClient(clientConnection, clientStream, clientStreamWriter), connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
() => new SessionEventArgs(this, endPoint, clientConnection, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
{
UserData = connectArgs?.UserData
},
......@@ -356,8 +354,7 @@ namespace Titanium.Web.Proxy
calledRequestHandler = true;
// Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
cancellationTokenSource, connectArgs, prefetchConnectionTask);
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, cancellationTokenSource, connectArgs, prefetchConnectionTask);
}
catch (ProxyException e)
{
......@@ -388,11 +385,6 @@ namespace Titanium.Web.Proxy
sslStream?.Dispose();
clientStream.Dispose();
if (!cancellationTokenSource.IsCancellationRequested)
{
cancellationTokenSource.Cancel();
}
}
}
}
......
......@@ -7,9 +7,9 @@ using Titanium.Web.Proxy.StreamExtended.BufferPool;
namespace Titanium.Web.Proxy.Helpers
{
internal sealed class HttpResponseWriter : HttpWriter
internal sealed class HttpClientStream : HttpStream
{
internal HttpResponseWriter(Stream stream, IBufferPool bufferPool)
internal HttpClientStream(Stream stream, IBufferPool bufferPool)
: base(stream, bufferPool)
{
}
......
......@@ -170,18 +170,18 @@ namespace Titanium.Web.Proxy.Helpers
/// Determines whether is connect method.
/// </summary>
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsConnectMethod(CustomBufferedStream clientStreamReader, IBufferPool bufferPool, CancellationToken cancellationToken = default)
internal static Task<int> IsConnectMethod(IPeekStream httpReader, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{
return startsWith(clientStreamReader, bufferPool, "CONNECT", cancellationToken);
return startsWith(httpReader, bufferPool, "CONNECT", cancellationToken);
}
/// <summary>
/// Determines whether is pri method (HTTP/2).
/// </summary>
/// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsPriMethod(CustomBufferedStream clientStreamReader, IBufferPool bufferPool, CancellationToken cancellationToken = default)
internal static Task<int> IsPriMethod(IPeekStream httpReader, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{
return startsWith(clientStreamReader, bufferPool, "PRI", cancellationToken);
return startsWith(httpReader, bufferPool, "PRI", cancellationToken);
}
/// <summary>
......@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns>
/// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
/// </returns>
private static async Task<int> startsWith(CustomBufferedStream clientStreamReader, IBufferPool bufferPool, string expectedStart, CancellationToken cancellationToken = default)
private static async Task<int> startsWith(IPeekStream httpReader, IBufferPool bufferPool, string expectedStart, CancellationToken cancellationToken = default)
{
const int lengthToCheck = 10;
if (bufferPool.BufferSize < lengthToCheck)
......@@ -205,7 +205,7 @@ namespace Titanium.Web.Proxy.Helpers
int i = 0;
while (i < lengthToCheck)
{
int peeked = await clientStreamReader.PeekBytesAsync(buffer, i, i, lengthToCheck - i, cancellationToken);
int peeked = await httpReader.PeekBytesAsync(buffer, i, i, lengthToCheck - i, cancellationToken);
if (peeked <= 0)
return -1;
......
......@@ -6,9 +6,9 @@ using Titanium.Web.Proxy.StreamExtended.BufferPool;
namespace Titanium.Web.Proxy.Helpers
{
internal sealed class HttpRequestWriter : HttpWriter
internal sealed class HttpServerStream : HttpStream
{
internal HttpRequestWriter(Stream stream, IBufferPool bufferPool)
internal HttpServerStream(Stream stream, IBufferPool bufferPool)
: base(stream, bufferPool)
{
}
......
This diff is collapsed.
......@@ -95,7 +95,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return true;
}
public ExternalProxy? GetProxy(Uri destination)
public IExternalProxy? GetProxy(Uri destination)
{
if (GetAutoProxies(destination, out var proxies))
{
......
......@@ -111,7 +111,7 @@ namespace Titanium.Web.Proxy.Http
bool useUpstreamProxy = upstreamProxy != null && Connection.IsHttps == false;
var writer = Connection.StreamWriter;
var serverStream = Connection.Stream;
string url;
if (useUpstreamProxy || isTransparent)
......@@ -153,7 +153,7 @@ namespace Titanium.Web.Proxy.Http
headerBuilder.WriteLine();
await writer.WriteHeadersAsync(headerBuilder, cancellationToken);
await serverStream.WriteHeadersAsync(headerBuilder, cancellationToken);
if (enable100ContinueBehaviour && Request.ExpectContinue)
{
......
......@@ -6,7 +6,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary>
/// An upstream proxy this proxy uses if any.
/// </summary>
public class ExternalProxy
public class ExternalProxy : IExternalProxy
{
private static readonly Lazy<NetworkCredential> defaultCredentials =
new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials);
......
namespace Titanium.Web.Proxy.Models
{
public interface IExternalProxy
{
/// <summary>
/// Use default windows credentials?
/// </summary>
bool UseDefaultCredentials { get; set; }
/// <summary>
/// Bypass this proxy for connections to localhost?
/// </summary>
bool BypassLocalhost { get; set; }
/// <summary>
/// Username.
/// </summary>
string? UserName { get; set; }
/// <summary>
/// Password.
/// </summary>
string? Password { get; set; }
/// <summary>
/// Host name.
/// </summary>
string HostName { get; set; }
/// <summary>
/// Port.
/// </summary>
int Port { get; set; }
string ToString();
}
}

using Titanium.Web.Proxy.StreamExtended.BufferPool;
using Titanium.Web.Proxy.StreamExtended.Network;
#if DEBUG
using System;
using System.IO;
using System.Text;
using System.Threading;
namespace Titanium.Web.Proxy.Network
{
internal class DebugCustomBufferedStream : CustomBufferedStream
{
private const string basePath = @".";
private static int counter;
private readonly FileStream fileStreamReceived;
private readonly FileStream fileStreamSent;
public DebugCustomBufferedStream(Guid connectionId, string type, Stream baseStream, IBufferPool bufferPool, bool leaveOpen = false)
: base(baseStream, bufferPool, leaveOpen)
{
Counter = Interlocked.Increment(ref counter);
fileStreamSent = new FileStream(Path.Combine(basePath, $"{connectionId}_{type}_{Counter}_sent.dat"), FileMode.Create);
fileStreamReceived = new FileStream(Path.Combine(basePath, $"{connectionId}_{type}_{Counter}_received.dat"), FileMode.Create);
}
public int Counter { get; }
protected override void OnDataWrite(byte[] buffer, int offset, int count)
{
fileStreamSent.Write(buffer, offset, count);
Flush();
}
protected override void OnDataRead(byte[] buffer, int offset, int count)
{
fileStreamReceived.Write(buffer, offset, count);
Flush();
}
public void LogException(Exception ex)
{
var data = Encoding.UTF8.GetBytes("EXCEPTION: " + ex);
fileStreamReceived.Write(data, 0, data.Length);
fileStreamReceived.Flush();
}
public override void Flush()
{
fileStreamSent.Flush(true);
fileStreamReceived.Flush(true);
if (CanWrite)
{
base.Flush();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Flush();
fileStreamSent.Dispose();
fileStreamReceived.Dispose();
}
base.Dispose(disposing);
}
}
}
#endif
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.StreamExtended.Network;
namespace Titanium.Web.Proxy.Network
{
/// <summary>
/// This class wraps Tcp connection to client
/// </summary>
internal class ProxyClient
{
public ProxyClient(TcpClientConnection connection, CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter)
{
Connection = connection;
ClientStream = clientStream;
ClientStreamWriter = clientStreamWriter;
}
/// <summary>
/// TcpClient connection used to communicate with client
/// </summary>
internal TcpClientConnection Connection { get; }
/// <summary>
/// Holds the stream to client
/// </summary>
internal CustomBufferedStream ClientStream { get; }
/// <summary>
/// Used to write line by line to client
/// </summary>
internal HttpResponseWriter ClientStreamWriter { get; }
}
}
......@@ -86,7 +86,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
proxyServer.UpdateClientConnectionCount(false);
tcpClient.CloseSocket();
});
}
}
}
......@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal string GetConnectionCacheKey(string remoteHostName, int remotePort,
bool isHttps, List<SslApplicationProtocol>? applicationProtocols,
IPEndPoint? upStreamEndPoint, ExternalProxy? externalProxy)
IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy)
{
// http version is ignored since its an application level decision b/w HTTP 1.0/1.1
// also when doing connect request MS Edge browser sends http 1.0 but uses 1.1 after server sends 1.1 its response.
......@@ -115,7 +115,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
}
ExternalProxy? customUpStreamProxy = null;
IExternalProxy? customUpStreamProxy = null;
bool isHttps = session.IsHttps;
if (server.GetCustomUpStreamProxyFunc != null)
......@@ -170,7 +170,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect,
List<SslApplicationProtocol>? applicationProtocols, bool noCache, CancellationToken cancellationToken)
{
ExternalProxy? customUpStreamProxy = null;
IExternalProxy? customUpStreamProxy = null;
bool isHttps = session.IsHttps;
if (server.GetCustomUpStreamProxyFunc != null)
......@@ -208,10 +208,10 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <returns></returns>
internal async Task<TcpServerConnection> GetServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, ExternalProxy? externalProxy,
ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy,
bool noCache, CancellationToken cancellationToken)
{
var sslProtocol = session?.ProxyClient.Connection.SslProtocol ?? SslProtocols.None;
var sslProtocol = session?.ClientConnection.SslProtocol ?? SslProtocols.None;
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
isHttps, applicationProtocols, upStreamEndPoint, externalProxy);
......@@ -262,7 +262,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <returns></returns>
private async Task<TcpServerConnection> createServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, SslProtocols sslProtocol, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, ExternalProxy? externalProxy, string cacheKey,
ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy, string cacheKey,
CancellationToken cancellationToken)
{
// deny connection to proxy end points to avoid infinite connection loop.
......@@ -297,14 +297,14 @@ namespace Titanium.Web.Proxy.Network.Tcp
}
TcpClient? tcpClient = null;
CustomBufferedStream? stream = null;
HttpServerStream? stream = null;
SslApplicationProtocol negotiatedApplicationProtocol = default;
bool retry = true;
var enabledSslProtocols = sslProtocol;
retry:
retry:
try
{
string hostname = useUpstreamProxy ? externalProxy!.HostName : remoteHostName;
......@@ -323,6 +323,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
Array.Sort(ipAddresses, (x, y) => x.AddressFamily.CompareTo(y.AddressFamily));
Exception lastException = null;
for (int i = 0; i < ipAddresses.Length; i++)
{
try
......@@ -352,28 +353,29 @@ namespace Titanium.Web.Proxy.Network.Tcp
}
catch (Exception e)
{
if (i == ipAddresses.Length - 1)
{
throw new Exception($"Could not establish connection to {hostname}", e);
}
// dispose the current TcpClient and try the next address
lastException = e;
tcpClient?.Dispose();
tcpClient = null;
}
}
if (tcpClient == null)
{
throw new Exception($"Could not establish connection to {hostname}", lastException);
}
if (session != null)
{
session.TimeLine["Connection Established"] = DateTime.Now;
}
await proxyServer.InvokeConnectionCreateEvent(tcpClient!, false);
await proxyServer.InvokeConnectionCreateEvent(tcpClient, false);
stream = new CustomBufferedStream(tcpClient!.GetStream(), proxyServer.BufferPool);
stream = new HttpServerStream(tcpClient.GetStream(), proxyServer.BufferPool);
if (useUpstreamProxy && (isConnect || isHttps))
{
var writer = new HttpRequestWriter(stream, proxyServer.BufferPool);
string authority = $"{remoteHostName}:{remotePort}";
var connectRequest = new ConnectRequest(authority)
{
......@@ -391,7 +393,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
HttpHeader.GetProxyAuthorizationHeader(externalProxy.UserName, externalProxy.Password));
}
await writer.WriteRequestAsync(connectRequest, cancellationToken: cancellationToken);
await stream.WriteRequestAsync(connectRequest, cancellationToken: cancellationToken);
string httpStatus = await stream.ReadLineAsync(cancellationToken)
?? throw new ServerConnectionException("Server connection was closed.");
......@@ -411,7 +413,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate,
proxyServer.SelectClientCertificate);
stream = new CustomBufferedStream(sslStream, proxyServer.BufferPool);
stream = new HttpServerStream(sslStream, proxyServer.BufferPool);
var options = new SslClientAuthenticationOptions
{
......@@ -430,11 +432,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
session.TimeLine["HTTPS Established"] = DateTime.Now;
}
}
}
catch (IOException ex) when (ex.HResult == unchecked((int)0x80131620) && retry && enabledSslProtocols >= SslProtocols.Tls11)
{
stream?.Dispose();
tcpClient?.Close();
enabledSslProtocols = SslProtocols.Tls;
retry = false;
goto retry;
......
......@@ -15,15 +15,14 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal class TcpServerConnection : IDisposable
{
internal TcpServerConnection(ProxyServer proxyServer, TcpClient tcpClient, CustomBufferedStream stream,
internal TcpServerConnection(ProxyServer proxyServer, TcpClient tcpClient, HttpServerStream stream,
string hostName, int port, bool isHttps, SslApplicationProtocol negotiatedApplicationProtocol,
Version version, bool useUpstreamProxy, ExternalProxy? upStreamProxy, IPEndPoint? upStreamEndPoint, string cacheKey)
Version version, bool useUpstreamProxy, IExternalProxy? upStreamProxy, IPEndPoint? upStreamEndPoint, string cacheKey)
{
this.tcpClient = tcpClient;
TcpClient = tcpClient;
LastAccess = DateTime.Now;
this.proxyServer = proxyServer;
this.proxyServer.UpdateServerConnectionCount(true);
StreamWriter = new HttpRequestWriter(stream, proxyServer.BufferPool);
Stream = stream;
HostName = hostName;
Port = port;
......@@ -41,7 +40,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal bool IsClosed => Stream.IsClosed;
internal ExternalProxy? UpStreamProxy { get; set; }
internal IExternalProxy? UpStreamProxy { get; set; }
internal string HostName { get; set; }
......@@ -63,22 +62,15 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal Version Version { get; set; } = HttpHeader.VersionUnknown;
private readonly TcpClient tcpClient;
/// <summary>
/// The TcpClient.
/// </summary>
internal TcpClient TcpClient => tcpClient;
internal TcpClient TcpClient { get; }
/// <summary>
/// Used to write lines to server
/// </summary>
internal HttpRequestWriter StreamWriter { get; }
/// <summary>
/// Server stream
/// </summary>
internal CustomBufferedStream Stream { get; }
internal HttpServerStream Stream { get; }
/// <summary>
/// Last time this connection was used
......@@ -107,8 +99,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
// This way we can push tcp Time_Wait to server side when possible.
await Task.Delay(1000);
proxyServer.UpdateServerConnectionCount(false);
Stream?.Dispose();
tcpClient.CloseSocket();
Stream.Dispose();
TcpClient.CloseSocket();
});
}
......
......@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy
/// </summary>
private WinHttpWebProxyFinder? systemProxyResolver;
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of ProxyServer class with provided parameters.
......@@ -145,7 +145,7 @@ namespace Titanium.Web.Proxy
/// Defaults to false.
/// </summary>
public bool EnableWinAuth { get; set; }
/// <summary>
/// Enable disable HTTP/2 support.
/// Warning: HTTP/2 support is very limited
......@@ -253,12 +253,12 @@ namespace Titanium.Web.Proxy
/// <summary>
/// External proxy used for Http requests.
/// </summary>
public ExternalProxy? UpStreamHttpProxy { get; set; }
public IExternalProxy? UpStreamHttpProxy { get; set; }
/// <summary>
/// External proxy used for Https requests.
/// </summary>
public ExternalProxy? UpStreamHttpsProxy { get; set; }
public IExternalProxy? UpStreamHttpsProxy { get; set; }
/// <summary>
/// Local adapter/NIC endpoint where proxy makes request via.
......@@ -275,7 +275,7 @@ namespace Titanium.Web.Proxy
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests.
/// User should return the ExternalProxy object with valid credentials.
/// </summary>
public Func<SessionEventArgsBase, Task<ExternalProxy?>>? GetCustomUpStreamProxyFunc { get; set; }
public Func<SessionEventArgsBase, Task<IExternalProxy?>>? GetCustomUpStreamProxyFunc { get; set; }
/// <summary>
/// Callback for error events in this proxy instance.
......@@ -709,7 +709,7 @@ namespace Titanium.Web.Proxy
/// </summary>
/// <param name="sessionEventArgs">The session.</param>
/// <returns>The external proxy as task result.</returns>
private Task<ExternalProxy?> getSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs)
private Task<IExternalProxy?> getSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs)
{
var proxy = systemProxyResolver!.GetProxy(sessionEventArgs.HttpClient.Request.RequestUri);
return Task.FromResult(proxy);
......@@ -803,15 +803,8 @@ namespace Titanium.Web.Proxy
/// </summary>
/// <param name="clientStream">The client stream.</param>
/// <param name="exception">The exception.</param>
private void onException(CustomBufferedStream clientStream, Exception exception)
private void onException(HttpClientStream clientStream, Exception exception)
{
#if DEBUG
if (clientStream is DebugCustomBufferedStream debugStream)
{
debugStream.LogException(exception);
}
#endif
ExceptionFunc(exception);
}
......
......@@ -31,13 +31,11 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint">The proxy endpoint.</param>
/// <param name="clientConnection">The client connection.</param>
/// <param name="clientStream">The client stream.</param>
/// <param name="clientStreamWriter">The client stream writer.</param>
/// <param name="cancellationTokenSource">The cancellation token source for this async task.</param>
/// <param name="connectArgs">The Connect request if this is a HTTPS request from explicit endpoint.</param>
/// <param name="prefetchConnectionTask">Prefetched server connection for current client using Connect/SNI headers.</param>
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null,
HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null,
Task<TcpServerConnection>? prefetchConnectionTask = null)
{
var connectRequest = connectArgs?.HttpClient.ConnectRequest;
......@@ -66,7 +64,7 @@ namespace Titanium.Web.Proxy
return;
}
var args = new SessionEventArgs(this, endPoint, new ProxyClient(clientConnection, clientStream, clientStreamWriter), connectRequest, cancellationTokenSource)
var args = new SessionEventArgs(this, endPoint, clientConnection, clientStream, connectRequest, cancellationTokenSource)
{
UserData = connectArgs?.UserData
};
......@@ -101,7 +99,7 @@ namespace Titanium.Web.Proxy
await onBeforeResponse(args);
// send the response
await clientStreamWriter.WriteResponseAsync(args.HttpClient.Response,
await clientStream.WriteResponseAsync(args.HttpClient.Response,
cancellationToken: cancellationToken);
return;
}
......@@ -276,7 +274,7 @@ namespace Titanium.Web.Proxy
// if upgrading to websocket then relay the request without reading the contents
await handleWebSocketUpgrade(requestHttpMethod, requestHttpUrl, requestVersion, args, args.HttpClient.Request,
args.HttpClient.Response, args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamWriter,
args.HttpClient.Response, args.ClientStream,
connection, cancellationTokenSource, cancellationToken);
return false;
}
......@@ -304,13 +302,13 @@ namespace Titanium.Web.Proxy
// If a successful 100 continue request was made, inform that to the client and reset response
if (request.ExpectationSucceeded)
{
var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
var writer = args.ClientStream;
var response = args.HttpClient.Response;
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
headerBuilder.WriteHeaders(response.Headers);
await clientStreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
await writer.WriteHeadersAsync(headerBuilder, cancellationToken);
await args.ClearResponse(cancellationToken);
}
......@@ -320,14 +318,12 @@ namespace Titanium.Web.Proxy
{
if (request.IsBodyRead)
{
var writer = args.HttpClient.Connection.StreamWriter;
await writer.WriteBodyAsync(body!, request.IsChunked, cancellationToken);
await args.HttpClient.Connection.Stream.WriteBodyAsync(body!, request.IsChunked, cancellationToken);
}
else if (!request.ExpectationFailed)
{
// get the request body unless an unsuccessful 100 continue request was made
HttpWriter writer = args.HttpClient.Connection.StreamWriter!;
await args.CopyRequestBodyAsync(writer, TransformationMode.None, cancellationToken);
await args.CopyRequestBodyAsync(args.HttpClient.Connection.Stream, TransformationMode.None, cancellationToken);
}
}
......@@ -357,7 +353,7 @@ namespace Titanium.Web.Proxy
supportedAcceptEncoding.Add("identity");
requestHeaders.SetOrAddHeaderValue(KnownHeaders.AcceptEncoding,
string.Join(",", supportedAcceptEncoding));
string.Join(", ", supportedAcceptEncoding));
}
requestHeaders.FixProxyHeaders();
......
......@@ -64,13 +64,13 @@ namespace Titanium.Web.Proxy
// it may changed in the user event
response = args.HttpClient.Response;
var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
var clientStream = args.ClientStream;
// user set custom response by ignoring original response from server.
if (response.Locked)
{
// write custom user response with body and return.
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken);
await clientStream.WriteResponseAsync(response, cancellationToken: cancellationToken);
if (args.HttpClient.HasConnection && !args.HttpClient.CloseServerConnection)
{
......@@ -108,7 +108,7 @@ namespace Titanium.Web.Proxy
if (response.IsBodyRead)
{
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken);
await clientStream.WriteResponseAsync(response, cancellationToken: cancellationToken);
}
else
{
......@@ -116,12 +116,12 @@ namespace Titanium.Web.Proxy
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
headerBuilder.WriteHeaders(response.Headers);
await clientStreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
await clientStream.WriteHeadersAsync(headerBuilder, cancellationToken);
// Write body if exists
if (response.HasBody)
{
await args.CopyResponseBodyAsync(clientStreamWriter, TransformationMode.None,
await args.CopyResponseBodyAsync(clientStream, TransformationMode.None,
cancellationToken);
}
}
......
using System;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.StreamExtended.BufferPool;
namespace Titanium.Web.Proxy.StreamExtended.Network
......@@ -11,9 +12,9 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// </summary>
internal class CopyStream : ILineStream, IDisposable
{
private readonly CustomBufferedStream reader;
private readonly IHttpStreamReader reader;
private readonly ICustomStreamWriter writer;
private readonly IHttpStreamWriter writer;
private readonly IBufferPool bufferPool;
......@@ -27,7 +28,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
public long ReadBytes { get; private set; }
public CopyStream(CustomBufferedStream reader, ICustomStreamWriter writer, IBufferPool bufferPool)
public CopyStream(IHttpStreamReader reader, IHttpStreamWriter writer, IBufferPool bufferPool)
{
this.reader = reader;
this.writer = writer;
......@@ -61,7 +62,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
public ValueTask<string?> ReadLineAsync(CancellationToken cancellationToken = default)
{
return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken);
return HttpStream.ReadLineInternalAsync(this, bufferPool, cancellationToken);
}
public void Dispose()
......
using System.Threading;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.StreamExtended.Network
{
public interface IHttpStreamReader : ILineStream
{
int Read(byte[] buffer, int offset, int count);
Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
}
}
using System.Threading;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.StreamExtended.Network
......@@ -6,10 +7,13 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// <summary>
/// A concrete implementation of this interface is required when calling CopyStream.
/// </summary>
public interface ICustomStreamWriter
internal interface IHttpStreamWriter
{
void Write(byte[] buffer, int i, int bufferLength);
Task WriteAsync(byte[] buffer, int i, int bufferLength, CancellationToken cancellationToken);
Task CopyBodyAsync(IHttpStreamReader streamReader, bool isChunked, long contentLength,
Action<byte[], int, int>? onCopy, CancellationToken cancellationToken);
}
}
\ No newline at end of file
}
......@@ -53,5 +53,5 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
return buffer;
}
}
}
}
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.StreamExtended.BufferPool;
using Titanium.Web.Proxy.StreamExtended.Models;
using Titanium.Web.Proxy.StreamExtended.Network;
......@@ -19,7 +20,7 @@ namespace Titanium.Web.Proxy.StreamExtended
/// <param name="bufferPool"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<ClientHelloInfo?> PeekClientHello(CustomBufferedStream clientStream, IBufferPool bufferPool, CancellationToken cancellationToken = default)
public static async Task<ClientHelloInfo?> PeekClientHello(IPeekStream clientStream, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{
// detects the HTTPS ClientHello message as it is described in the following url:
// https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format
......@@ -127,11 +128,10 @@ namespace Titanium.Web.Proxy.StreamExtended
return null;
}
byte[] ciphersData = peekStream.ReadBytes(length);
int[] ciphers = new int[ciphersData.Length / 2];
int[] ciphers = new int[length / 2];
for (int i = 0; i < ciphers.Length; i++)
{
ciphers[i] = (ciphersData[2 * i] << 8) + ciphersData[2 * i + 1];
ciphers[i] = peekStream.ReadInt16();
}
length = peekStream.ReadByte();
......@@ -178,7 +178,7 @@ namespace Titanium.Web.Proxy.StreamExtended
/// <param name="bufferPool"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<bool> IsServerHello(CustomBufferedStream stream, IBufferPool bufferPool, CancellationToken cancellationToken)
public static async Task<bool> IsServerHello(IPeekStream stream, IBufferPool bufferPool, CancellationToken cancellationToken)
{
var serverHello = await PeekServerHello(stream, bufferPool, cancellationToken);
return serverHello != null;
......@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.StreamExtended
/// <param name="bufferPool"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<ServerHelloInfo?> PeekServerHello(CustomBufferedStream serverStream, IBufferPool bufferPool, CancellationToken cancellationToken = default)
public static async Task<ServerHelloInfo?> PeekServerHello(IPeekStream serverStream, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{
// detects the HTTPS ClientHello message as it is described in the following url:
// https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format
......@@ -286,11 +286,11 @@ namespace Titanium.Web.Proxy.StreamExtended
int cipherSuite = peekStream.ReadInt16();
byte compressionMethod = peekStream.ReadByte();
int extenstionsStartPosition = peekStream.Position;
int extensionsStartPosition = peekStream.Position;
Dictionary<string, SslExtension>? extensions = null;
if (extenstionsStartPosition < recordLength + 5)
if (extensionsStartPosition < recordLength + 5)
{
extensions = await ReadExtensions(majorVersion, minorVersion, peekStream, bufferPool, cancellationToken);
}
......@@ -298,7 +298,7 @@ namespace Titanium.Web.Proxy.StreamExtended
var serverHelloInfo = new ServerHelloInfo(3, majorVersion, minorVersion, random, sessionId, cipherSuite, peekStream.Position)
{
CompressionMethod = compressionMethod,
EntensionsStartPosition = extenstionsStartPosition,
EntensionsStartPosition = extensionsStartPosition,
Extensions = extensions,
};
......
......@@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="BrotliSharpLib" Version="0.3.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5.2" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup>
......
......@@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="BrotliSharpLib" Version="0.3.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5.2" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup>
......
......@@ -14,7 +14,7 @@
<ItemGroup>
<PackageReference Include="BrotliSharpLib" Version="0.3.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5.2" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.3" />
......
......@@ -15,7 +15,7 @@
<tags></tags>
<dependencies>
<group targetFramework="netstandard2.0">
<dependency id="Portable.BouncyCastle" version="1.8.5" />
<dependency id="Portable.BouncyCastle" version="1.8.5.2" />
<dependency id="BrotliSharpLib" version="0.3.3" />
<dependency id="Microsoft.Win32.Registry" version="4.6.0" />
<dependency id="System.Buffers" version="4.5.0" />
......@@ -24,7 +24,7 @@
<dependency id="System.Security.Principal.Windows" version="4.6.0" />
</group>
<group targetFramework="netstandard2.1">
<dependency id="Portable.BouncyCastle" version="1.8.5" />
<dependency id="Portable.BouncyCastle" version="1.8.5.2" />
<dependency id="BrotliSharpLib" version="0.3.3" />
<dependency id="Microsoft.Win32.Registry" version="4.6.0" />
<dependency id="System.Buffers" version="4.5.0" />
......
......@@ -33,8 +33,7 @@ namespace Titanium.Web.Proxy
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool);
var clientStream = new HttpClientStream(clientConnection.GetStream(), BufferPool);
SslStream? sslStream = null;
......@@ -75,14 +74,12 @@ namespace Titanium.Web.Proxy
await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);
// HTTPS server created - we can now decrypt the client's traffic
clientStream = new CustomBufferedStream(sslStream, BufferPool);
clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool);
clientStream = new HttpClientStream(sslStream, BufferPool);
}
catch (Exception e)
{
var certname = certificate?.GetNameInfo(X509NameType.SimpleName, false);
var session = new SessionEventArgs(this, endPoint, new ProxyClient(clientConnection, clientStream, clientStreamWriter), null,
var session = new SessionEventArgs(this, endPoint, clientConnection, clientStream, null,
cancellationTokenSource);
throw new ProxyConnectException(
$"Couldn't authenticate host '{httpsHostName}' with certificate '{certname}'.", e, session);
......@@ -108,7 +105,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);
await connection.StreamWriter.WriteAsync(data, 0, available, true, cancellationToken);
await connection.Stream.WriteAsync(data, 0, available, true, cancellationToken);
}
finally
{
......@@ -133,7 +130,7 @@ namespace Titanium.Web.Proxy
// HTTPS server created - we can now decrypt the client's traffic
// Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter, cancellationTokenSource);
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, cancellationTokenSource);
}
catch (ProxyException e)
{
......@@ -155,11 +152,6 @@ namespace Titanium.Web.Proxy
{
sslStream?.Dispose();
clientStream.Dispose();
if (!cancellationTokenSource.IsCancellationRequested)
{
cancellationTokenSource.Cancel();
}
}
}
}
......
......@@ -18,15 +18,14 @@ namespace Titanium.Web.Proxy
/// </summary>
private async Task handleWebSocketUpgrade(string requestHttpMethod, string requestHttpUrl, Version requestVersion,
SessionEventArgs args, Request request, Response response,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
TcpServerConnection serverConnection,
HttpClientStream clientStream, TcpServerConnection serverConnection,
CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken)
{
// prepare the prefix content
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteRequestLine(requestHttpMethod, requestHttpUrl, requestVersion);
headerBuilder.WriteHeaders(request.Headers);
await serverConnection.StreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
await serverConnection.Stream.WriteHeadersAsync(headerBuilder, cancellationToken);
string httpStatus;
try
......@@ -51,7 +50,7 @@ namespace Titanium.Web.Proxy
if (!args.IsTransparent)
{
await clientStreamWriter.WriteResponseAsync(response,
await clientStream.WriteResponseAsync(response,
cancellationToken: cancellationToken);
}
......
......@@ -175,7 +175,7 @@ namespace Titanium.Web.Proxy
// Add custom div to body to clarify that the proxy (not the client browser) failed authentication
string authErrorMessage =
"<div class=\"inserted-by-proxy\"><h2>NTLM authentication through Titanium.Web.Proxy (" +
args.ProxyClient.Connection.LocalEndPoint +
args.ClientConnection.LocalEndPoint +
") failed. Please check credentials.</h2></div>";
string originalErrorMessage =
"<div class=\"inserted-by-proxy\"><h3>Response from remote web server below.</h3></div><br/>";
......
......@@ -69,7 +69,7 @@ namespace Titanium.Web.Proxy.IntegrationTests
};
var client = testSuite.GetClient(proxy1, true);
var response = await client.PostAsync(new Uri(server.ListeningHttpsUrl),
new StringContent("hello server. I am a client."));
......
......@@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.0.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.0.0" />
</ItemGroup>
......
......@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.0.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.0.0" />
</ItemGroup>
......
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