Commit 66671d10 authored by Honfika's avatar Honfika

Store the headers as byte arrays

parent fc2bbb80
...@@ -22,15 +22,10 @@ namespace Titanium.Web.Proxy ...@@ -22,15 +22,10 @@ namespace Titanium.Web.Proxy
// if user callback is registered then do it // if user callback is registered then do it
if (ServerCertificateValidationCallback != null) if (ServerCertificateValidationCallback != null)
{ {
var args = new CertificateValidationEventArgs var args = new CertificateValidationEventArgs(certificate, chain, sslPolicyErrors);
{
Certificate = certificate,
Chain = chain,
SslPolicyErrors = sslPolicyErrors
};
// why is the sender null? // why is the sender null?
ServerCertificateValidationCallback.InvokeAsync(this, args, exceptionFunc).Wait(); ServerCertificateValidationCallback.InvokeAsync(this, args, ExceptionFunc).Wait();
return args.IsValid; return args.IsValid;
} }
...@@ -90,7 +85,7 @@ namespace Titanium.Web.Proxy ...@@ -90,7 +85,7 @@ namespace Titanium.Web.Proxy
}; };
// why is the sender null? // why is the sender null?
ClientCertificateSelectionCallback.InvokeAsync(this, args, exceptionFunc).Wait(); ClientCertificateSelectionCallback.InvokeAsync(this, args, ExceptionFunc).Wait();
return args.ClientCertificate; return args.ClientCertificate;
} }
......
...@@ -10,20 +10,27 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -10,20 +10,27 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public class CertificateValidationEventArgs : EventArgs public class CertificateValidationEventArgs : EventArgs
{ {
public CertificateValidationEventArgs(X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
Certificate = certificate;
Chain = chain;
SslPolicyErrors = sslPolicyErrors;
}
/// <summary> /// <summary>
/// Server certificate. /// Server certificate.
/// </summary> /// </summary>
public X509Certificate Certificate { get; internal set; } public X509Certificate Certificate { get; }
/// <summary> /// <summary>
/// Certificate chain. /// Certificate chain.
/// </summary> /// </summary>
public X509Chain Chain { get; internal set; } public X509Chain Chain { get; }
/// <summary> /// <summary>
/// SSL policy errors. /// SSL policy errors.
/// </summary> /// </summary>
public SslPolicyErrors SslPolicyErrors { get; internal set; } public SslPolicyErrors SslPolicyErrors { get; }
/// <summary> /// <summary>
/// Is the given server certificate valid? /// Is the given server certificate valid?
......
...@@ -11,13 +11,13 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -11,13 +11,13 @@ namespace Titanium.Web.Proxy.EventArguments
internal class LimitedStream : Stream internal class LimitedStream : Stream
{ {
private readonly IBufferPool bufferPool; private readonly IBufferPool bufferPool;
private readonly ICustomStreamReader baseStream; private readonly CustomBufferedStream baseStream;
private readonly bool isChunked; private readonly bool isChunked;
private long bytesRemaining; private long bytesRemaining;
private bool readChunkTrail; private bool readChunkTrail;
internal LimitedStream(ICustomStreamReader baseStream, IBufferPool bufferPool, bool isChunked, internal LimitedStream(CustomBufferedStream baseStream, IBufferPool bufferPool, bool isChunked,
long contentLength) long contentLength)
{ {
this.baseStream = baseStream; this.baseStream = baseStream;
......
...@@ -9,6 +9,7 @@ using Titanium.Web.Proxy.Helpers; ...@@ -9,6 +9,7 @@ using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses; using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.StreamExtended.Network; using Titanium.Web.Proxy.StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
...@@ -36,15 +37,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -36,15 +37,8 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Constructor to initialize the proxy /// Constructor to initialize the proxy
/// </summary> /// </summary>
internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ProxyClient proxyClient, ConnectRequest? connectRequest, CancellationTokenSource cancellationTokenSource)
CancellationTokenSource cancellationTokenSource) : base(server, endPoint, proxyClient, connectRequest, null, cancellationTokenSource)
: this(server, endPoint, null, cancellationTokenSource)
{
}
protected SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint,
Request? request, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, cancellationTokenSource, request)
{ {
} }
...@@ -72,7 +66,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -72,7 +66,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs>? MultipartRequestPartSent; public event EventHandler<MultipartRequestPartSentEventArgs>? MultipartRequestPartSent;
private ICustomStreamReader getStreamReader(bool isRequest) private CustomBufferedStream getStreamReader(bool isRequest)
{ {
return isRequest ? ProxyClient.ClientStream : HttpClient.Connection.Stream; return isRequest ? ProxyClient.ClientStream : HttpClient.Connection.Stream;
} }
...@@ -333,7 +327,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -333,7 +327,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Read a line from the byte stream /// Read a line from the byte stream
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private async Task<long> readUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, ReadOnlyMemory<char> boundary, CancellationToken cancellationToken) private async Task<long> readUntilBoundaryAsync(ILineStream reader, long totalBytesToRead, ReadOnlyMemory<char> boundary, CancellationToken cancellationToken)
{ {
int bufferDataLength = 0; int bufferDataLength = 0;
......
...@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
private static bool isWindowsAuthenticationSupported => RunTime.IsWindows; private static bool isWindowsAuthenticationSupported => RunTime.IsWindows;
internal readonly CancellationTokenSource? CancellationTokenSource; internal readonly CancellationTokenSource CancellationTokenSource;
internal TcpServerConnection ServerConnection => HttpClient.Connection; internal TcpServerConnection ServerConnection => HttpClient.Connection;
...@@ -40,25 +40,19 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -40,25 +40,19 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class. /// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
/// </summary> /// </summary>
private SessionEventArgsBase(ProxyServer server) private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
ProxyClient proxyClient, ConnectRequest? connectRequest, Request? request, CancellationTokenSource cancellationTokenSource)
{ {
BufferPool = server.BufferPool; BufferPool = server.BufferPool;
ExceptionFunc = server.ExceptionFunc; ExceptionFunc = server.ExceptionFunc;
TimeLine["Session Created"] = DateTime.Now; TimeLine["Session Created"] = DateTime.Now;
}
protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource,
Request? request) : this(server)
{
CancellationTokenSource = cancellationTokenSource; CancellationTokenSource = cancellationTokenSource;
ProxyClient = new ProxyClient(); ProxyClient = proxyClient;
HttpClient = new HttpWebClient(request); HttpClient = new HttpWebClient(connectRequest, request, new Lazy<int>(() => ProxyClient.Connection.GetProcessId(endPoint)));
LocalEndPoint = endPoint; LocalEndPoint = endPoint;
EnableWinAuth = server.EnableWinAuth && isWindowsAuthenticationSupported; EnableWinAuth = server.EnableWinAuth && isWindowsAuthenticationSupported;
HttpClient.ProcessId = new Lazy<int>(() => ProxyClient.Connection.GetProcessId(endPoint));
} }
/// <summary> /// <summary>
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Threading; using System.Threading;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.StreamExtended.Network; using Titanium.Web.Proxy.StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
...@@ -14,10 +15,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -14,10 +15,9 @@ namespace Titanium.Web.Proxy.EventArguments
private bool? isHttpsConnect; private bool? isHttpsConnect;
internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest, internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest,
CancellationTokenSource cancellationTokenSource) ProxyClient proxyClient, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, cancellationTokenSource, connectRequest) : base(server, endPoint, proxyClient, connectRequest, connectRequest, cancellationTokenSource)
{ {
HttpClient.ConnectRequest = connectRequest;
} }
/// <summary> /// <summary>
......
...@@ -14,6 +14,7 @@ using Titanium.Web.Proxy.Helpers; ...@@ -14,6 +14,7 @@ using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http2; using Titanium.Web.Proxy.Http2;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.StreamExtended; using Titanium.Web.Proxy.StreamExtended;
using Titanium.Web.Proxy.StreamExtended.Network; using Titanium.Web.Proxy.StreamExtended.Network;
...@@ -67,18 +68,16 @@ namespace Titanium.Web.Proxy ...@@ -67,18 +68,16 @@ namespace Titanium.Web.Proxy
var connectRequest = new ConnectRequest var connectRequest = new ConnectRequest
{ {
RequestUri = httpRemoteUri, RequestUri = httpRemoteUri,
OriginalUrl = httpUrl, OriginalUrlData = HttpHeader.Encoding.GetBytes(httpUrl),
HttpVersion = version HttpVersion = version
}; };
await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken); await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);
connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest, connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest,
cancellationTokenSource); new ProxyClient(clientConnection, clientStream, clientStreamWriter), cancellationTokenSource);
clientStream.DataRead += (o, args) => connectArgs.OnDataSent(args.Buffer, args.Offset, args.Count); clientStream.DataRead += (o, args) => connectArgs.OnDataSent(args.Buffer, args.Offset, args.Count);
clientStream.DataWrite += (o, args) => connectArgs.OnDataReceived(args.Buffer, args.Offset, args.Count); clientStream.DataWrite += (o, args) => connectArgs.OnDataReceived(args.Buffer, args.Offset, args.Count);
connectArgs.ProxyClient.Connection = clientConnection;
connectArgs.ProxyClient.ClientStream = clientStream;
await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc); await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc);
...@@ -336,10 +335,8 @@ namespace Titanium.Web.Proxy ...@@ -336,10 +335,8 @@ namespace Titanium.Web.Proxy
var connectionPreface = new ReadOnlyMemory<byte>(Http2Helper.ConnectionPreface); var connectionPreface = new ReadOnlyMemory<byte>(Http2Helper.ConnectionPreface);
await connection.StreamWriter.WriteAsync(connectionPreface, cancellationToken); await connection.StreamWriter.WriteAsync(connectionPreface, cancellationToken);
await Http2Helper.SendHttp2(clientStream, connection.Stream, await Http2Helper.SendHttp2(clientStream, connection.Stream,
() => new SessionEventArgs(this, endPoint, cancellationTokenSource) () => new SessionEventArgs(this, endPoint, new ProxyClient(clientConnection, clientStream, clientStreamWriter), connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
{ {
ProxyClient = { Connection = clientConnection },
HttpClient = { ConnectRequest = connectArgs?.HttpClient.ConnectRequest },
UserData = connectArgs?.UserData UserData = connectArgs?.UserData
}, },
async args => { await onBeforeRequest(args); }, async args => { await onBeforeRequest(args); },
......
using System;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Extensions
{
internal static class HttpHeaderExtensions
{
internal static string GetString(this ByteString str)
{
return GetString(str.Span);
}
internal static string GetString(this ReadOnlySpan<byte> bytes)
{
#if NETSTANDARD2_1
return HttpHeader.Encoding.GetString(bytes);
#else
return HttpHeader.Encoding.GetString(bytes.ToArray());
#endif
}
internal static ByteString GetByteString(this string str)
{
return HttpHeader.Encoding.GetBytes(str);
}
}
}
...@@ -94,19 +94,19 @@ namespace System.Net.Security ...@@ -94,19 +94,19 @@ namespace System.Net.Security
{ {
internal bool AllowRenegotiation { get; set; } internal bool AllowRenegotiation { get; set; }
internal string TargetHost { get; set; } internal string? TargetHost { get; set; }
internal X509CertificateCollection ClientCertificates { get; set; } internal X509CertificateCollection? ClientCertificates { get; set; }
internal LocalCertificateSelectionCallback LocalCertificateSelectionCallback { get; set; } internal LocalCertificateSelectionCallback? LocalCertificateSelectionCallback { get; set; }
internal SslProtocols EnabledSslProtocols { get; set; } internal SslProtocols EnabledSslProtocols { get; set; }
internal X509RevocationMode CertificateRevocationCheckMode { get; set; } internal X509RevocationMode CertificateRevocationCheckMode { get; set; }
internal List<SslApplicationProtocol> ApplicationProtocols { get; set; } internal List<SslApplicationProtocol>? ApplicationProtocols { get; set; }
internal RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; } internal RemoteCertificateValidationCallback? RemoteCertificateValidationCallback { get; set; }
internal EncryptionPolicy EncryptionPolicy { get; set; } internal EncryptionPolicy EncryptionPolicy { get; set; }
} }
......
using System; using System;
using System.Globalization; using System.Globalization;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
......
using System; using System;
using System.Text;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
...@@ -12,5 +13,5 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -12,5 +13,5 @@ namespace Titanium.Web.Proxy.Extensions
return uri.IsWellFormedOriginalString() ? uri.PathAndQuery : uri.GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped); return uri.IsWellFormedOriginalString() ? uri.PathAndQuery : uri.GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
} }
} }
} }
...@@ -5,6 +5,7 @@ using System.Threading; ...@@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
using Titanium.Web.Proxy.StreamExtended.BufferPool; using Titanium.Web.Proxy.StreamExtended.BufferPool;
using Titanium.Web.Proxy.StreamExtended.Network; using Titanium.Web.Proxy.StreamExtended.Network;
...@@ -13,10 +14,6 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -13,10 +14,6 @@ namespace Titanium.Web.Proxy.Helpers
{ {
internal static class HttpHelper internal static class HttpHelper
{ {
private static readonly Encoding defaultEncoding = Encoding.GetEncoding("ISO-8859-1");
public static Encoding HeaderEncoding => defaultEncoding;
struct SemicolonSplitEnumerator struct SemicolonSplitEnumerator
{ {
private readonly ReadOnlyMemory<char> data; private readonly ReadOnlyMemory<char> data;
...@@ -73,7 +70,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -73,7 +70,7 @@ namespace Titanium.Web.Proxy.Helpers
// return default if not specified // return default if not specified
if (contentType == null) if (contentType == null)
{ {
return defaultEncoding; return HttpHeader.DefaultEncoding;
} }
// extract the encoding by finding the charset // extract the encoding by finding the charset
...@@ -105,7 +102,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -105,7 +102,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
// return default if not specified // return default if not specified
return defaultEncoding; return HttpHeader.DefaultEncoding;
} }
internal static ReadOnlyMemory<char> GetBoundaryFromContentType(string? contentType) internal static ReadOnlyMemory<char> GetBoundaryFromContentType(string? contentType)
...@@ -173,7 +170,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -173,7 +170,7 @@ namespace Titanium.Web.Proxy.Helpers
/// Determines whether is connect method. /// Determines whether is connect method.
/// </summary> /// </summary>
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns> /// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsConnectMethod(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, CancellationToken cancellationToken = default) internal static Task<int> IsConnectMethod(CustomBufferedStream clientStreamReader, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{ {
return startsWith(clientStreamReader, bufferPool, "CONNECT", cancellationToken); return startsWith(clientStreamReader, bufferPool, "CONNECT", cancellationToken);
} }
...@@ -182,7 +179,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -182,7 +179,7 @@ namespace Titanium.Web.Proxy.Helpers
/// Determines whether is pri method (HTTP/2). /// Determines whether is pri method (HTTP/2).
/// </summary> /// </summary>
/// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns> /// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, CancellationToken cancellationToken = default) internal static Task<int> IsPriMethod(CustomBufferedStream clientStreamReader, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{ {
return startsWith(clientStreamReader, bufferPool, "PRI", cancellationToken); return startsWith(clientStreamReader, bufferPool, "PRI", cancellationToken);
} }
...@@ -193,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -193,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns> /// <returns>
/// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise /// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
/// </returns> /// </returns>
private static async Task<int> startsWith(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, string expectedStart, CancellationToken cancellationToken = default) private static async Task<int> startsWith(CustomBufferedStream clientStreamReader, IBufferPool bufferPool, string expectedStart, CancellationToken cancellationToken = default)
{ {
const int lengthToCheck = 10; const int lengthToCheck = 10;
if (bufferPool.BufferSize < lengthToCheck) if (bufferPool.BufferSize < lengthToCheck)
......
...@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.Helpers
internal async Task WriteRequestAsync(Request request, CancellationToken cancellationToken = default) internal async Task WriteRequestAsync(Request request, CancellationToken cancellationToken = default)
{ {
var headerBuilder = new HeaderBuilder(); var headerBuilder = new HeaderBuilder();
headerBuilder.WriteRequestLine(request.Method, request.RequestUriString, request.HttpVersion); headerBuilder.WriteRequestLine(request.Method, request.Url, request.HttpVersion);
await WriteAsync(request, headerBuilder, cancellationToken); await WriteAsync(request, headerBuilder, cancellationToken);
} }
} }
......
...@@ -6,6 +6,7 @@ using System.Text; ...@@ -6,6 +6,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
using Titanium.Web.Proxy.StreamExtended.BufferPool; using Titanium.Web.Proxy.StreamExtended.BufferPool;
using Titanium.Web.Proxy.StreamExtended.Network; using Titanium.Web.Proxy.StreamExtended.Network;
...@@ -19,7 +20,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -19,7 +20,7 @@ namespace Titanium.Web.Proxy.Helpers
private static readonly byte[] newLine = ProxyConstants.NewLineBytes; private static readonly byte[] newLine = ProxyConstants.NewLineBytes;
private static Encoding encoding => HttpHelper.HeaderEncoding; private static Encoding encoding => HttpHeader.Encoding;
internal HttpWriter(Stream stream, IBufferPool bufferPool) internal HttpWriter(Stream stream, IBufferPool bufferPool)
{ {
...@@ -148,7 +149,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -148,7 +149,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
internal Task CopyBodyAsync(ICustomStreamReader streamReader, bool isChunked, long contentLength, internal Task CopyBodyAsync(CustomBufferedStream streamReader, bool isChunked, long contentLength,
Action<byte[], int, int>? onCopy, CancellationToken cancellationToken) Action<byte[], int, int>? onCopy, CancellationToken cancellationToken)
{ {
// For chunked request we need to read data as they arrive, until we reach a chunk end symbol // For chunked request we need to read data as they arrive, until we reach a chunk end symbol
...@@ -193,7 +194,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -193,7 +194,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
private async Task copyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int>? onCopy, CancellationToken cancellationToken) private async Task copyBodyChunkedAsync(CustomBufferedStream reader, Action<byte[], int, int>? onCopy, CancellationToken cancellationToken)
{ {
while (true) while (true)
{ {
...@@ -233,7 +234,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -233,7 +234,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
private async Task copyBytesFromStream(ICustomStreamReader reader, long count, Action<byte[], int, int>? onCopy, private async Task copyBytesFromStream(CustomBufferedStream reader, long count, Action<byte[], int, int>? onCopy,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var buffer = bufferPool.GetBuffer(); var buffer = bufferPool.GetBuffer();
......
...@@ -34,9 +34,9 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -34,9 +34,9 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
} }
} }
public ICredentials Credentials { get; set; } public ICredentials? Credentials { get; set; }
public ProxyInfo ProxyInfo { get; internal set; } public ProxyInfo? ProxyInfo { get; internal set; }
public bool BypassLoopback { get; internal set; } public bool BypassLoopback { get; internal set; }
...@@ -46,7 +46,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -46,7 +46,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public bool AutomaticallyDetectSettings { get; internal set; } public bool AutomaticallyDetectSettings { get; internal set; }
private WebProxy proxy { get; set; } private WebProxy? proxy { get; set; }
public void Dispose() public void Dispose()
{ {
......
...@@ -67,7 +67,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -67,7 +67,7 @@ namespace Titanium.Web.Proxy.Http
public void Write(string str) public void Write(string str)
{ {
var encoding = HttpHelper.HeaderEncoding; var encoding = HttpHeader.Encoding;
#if NETSTANDARD2_1 #if NETSTANDARD2_1
var buf = ArrayPool<byte>.Shared.Rent(str.Length * 4); var buf = ArrayPool<byte>.Shared.Rent(str.Length * 4);
......
...@@ -4,6 +4,7 @@ using System.Collections.Generic; ...@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
...@@ -292,7 +293,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -292,7 +293,7 @@ namespace Titanium.Web.Proxy.Http
if (headers.TryGetValue(headerName, out var header)) if (headers.TryGetValue(headerName, out var header))
{ {
header.Value = value; header.ValueData = value.GetByteString();
} }
else else
{ {
......
...@@ -7,7 +7,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -7,7 +7,7 @@ namespace Titanium.Web.Proxy.Http
{ {
internal static class HeaderParser internal static class HeaderParser
{ {
internal static async Task ReadHeaders(ICustomStreamReader reader, HeaderCollection headerCollection, internal static async Task ReadHeaders(ILineStream reader, HeaderCollection headerCollection,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
string? tmpLine; string? tmpLine;
...@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy.Http
} }
string headerName = tmpLine.AsSpan(0, colonIndex).ToString(); string headerName = tmpLine.AsSpan(0, colonIndex).ToString();
string headerValue = tmpLine.AsSpan(colonIndex + 1).ToString(); string headerValue = tmpLine.AsSpan(colonIndex + 1).TrimStart().ToString();
headerCollection.AddHeader(headerName, headerValue); headerCollection.AddHeader(headerName, headerValue);
} }
} }
......
...@@ -18,10 +18,12 @@ namespace Titanium.Web.Proxy.Http ...@@ -18,10 +18,12 @@ namespace Titanium.Web.Proxy.Http
{ {
private TcpServerConnection? connection; private TcpServerConnection? connection;
internal HttpWebClient(Request? request) internal HttpWebClient(ConnectRequest? connectRequest, Request? request, Lazy<int> processIdFunc)
{ {
ConnectRequest = connectRequest;
Request = request ?? new Request(); Request = request ?? new Request();
Response = new Response(); Response = new Response();
ProcessId = processIdFunc;
} }
/// <summary> /// <summary>
...@@ -65,7 +67,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -65,7 +67,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Headers passed with Connect. /// Headers passed with Connect.
/// </summary> /// </summary>
public ConnectRequest? ConnectRequest { get; internal set; } public ConnectRequest? ConnectRequest { get; }
/// <summary> /// <summary>
/// Web Request. /// Web Request.
...@@ -114,7 +116,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -114,7 +116,7 @@ namespace Titanium.Web.Proxy.Http
string url; string url;
if (useUpstreamProxy || isTransparent) if (useUpstreamProxy || isTransparent)
{ {
url = Request.RequestUriString; url = Request.Url;
} }
else else
{ {
......
...@@ -14,40 +14,54 @@ namespace Titanium.Web.Proxy.Http ...@@ -14,40 +14,54 @@ namespace Titanium.Web.Proxy.Http
[TypeConverter(typeof(ExpandableObjectConverter))] [TypeConverter(typeof(ExpandableObjectConverter))]
public class Request : RequestResponseBase public class Request : RequestResponseBase
{ {
private string originalUrl;
/// <summary> /// <summary>
/// Request Method. /// Request Method.
/// </summary> /// </summary>
public string Method { get; set; } public string Method { get; set; }
/// <summary>
/// Request HTTP Uri.
/// </summary>
public Uri RequestUri { get; set; }
/// <summary> /// <summary>
/// Is Https? /// Is Https?
/// </summary> /// </summary>
public bool IsHttps => RequestUri.Scheme == ProxyServer.UriSchemeHttps; public bool IsHttps => RequestUri.Scheme == ProxyServer.UriSchemeHttps;
/// <summary> private ByteString originalUrlData;
/// The original request Url. private ByteString urlData;
/// </summary>
public string OriginalUrl internal ByteString OriginalUrlData
{ {
get => originalUrl; get => originalUrlData;
internal set set
{ {
originalUrl = value; originalUrlData = value;
RequestUriString = value; urlData = value;
} }
} }
/// <summary> /// <summary>
/// The request uri as it is in the HTTP header /// The original request Url.
/// </summary> /// </summary>
public string RequestUriString { get; set; } public string OriginalUrl => originalUrlData.GetString();
/// <summary>
/// Request HTTP Uri.
/// </summary>
public Uri RequestUri { get; set; }
/// <summary>
/// The request url as it is in the HTTP header
/// </summary>
public string Url
{
get => urlData.GetString();
set => urlData = value.GetByteString();
}
[Obsolete("This property is obsolete. Use Url property instead")]
public string RequestUriString
{
get => Url;
set => Url = value;
}
/// <summary> /// <summary>
/// Has request body? /// Has request body?
...@@ -108,11 +122,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -108,11 +122,6 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public bool IsMultipartFormData => ContentType?.StartsWith("multipart/form-data") == true; public bool IsMultipartFormData => ContentType?.StartsWith("multipart/form-data") == true;
/// <summary>
/// Request Url.
/// </summary>
public string Url => RequestUri.OriginalString;
/// <summary> /// <summary>
/// Cancels the client HTTP request without sending to server. /// Cancels the client HTTP request without sending to server.
/// This should be set when API user responds with custom response. /// This should be set when API user responds with custom response.
...@@ -155,9 +164,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -155,9 +164,9 @@ namespace Titanium.Web.Proxy.Http
get get
{ {
var headerBuilder = new HeaderBuilder(); var headerBuilder = new HeaderBuilder();
headerBuilder.WriteRequestLine(Method, RequestUriString, HttpVersion); headerBuilder.WriteRequestLine(Method, Url, HttpVersion);
headerBuilder.WriteHeaders(Headers); headerBuilder.WriteHeaders(Headers);
return headerBuilder.GetString(HttpHelper.HeaderEncoding); return headerBuilder.GetString(HttpHeader.Encoding);
} }
} }
......
...@@ -57,7 +57,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -57,7 +57,7 @@ namespace Titanium.Web.Proxy.Http
internal bool Http2IgnoreBodyFrames; internal bool Http2IgnoreBodyFrames;
internal Task Http2BeforeHandlerTask; internal Task? Http2BeforeHandlerTask;
/// <summary> /// <summary>
/// Priority used only in HTTP/2 /// Priority used only in HTTP/2
......
...@@ -102,7 +102,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -102,7 +102,7 @@ namespace Titanium.Web.Proxy.Http
var headerBuilder = new HeaderBuilder(); var headerBuilder = new HeaderBuilder();
headerBuilder.WriteResponseLine(HttpVersion, StatusCode, StatusDescription); headerBuilder.WriteResponseLine(HttpVersion, StatusCode, StatusDescription);
headerBuilder.WriteHeaders(Headers); headerBuilder.WriteHeaders(Headers);
return headerBuilder.GetString(HttpHelper.HeaderEncoding); return headerBuilder.GetString(HttpHeader.Encoding);
} }
} }
......
...@@ -22,7 +22,7 @@ using Titanium.Web.Proxy.Models; ...@@ -22,7 +22,7 @@ using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http2.Hpack namespace Titanium.Web.Proxy.Http2.Hpack
{ {
public class Decoder internal class Decoder
{ {
private readonly DynamicTable dynamicTable; private readonly DynamicTable dynamicTable;
...@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
private int skipLength; private int skipLength;
private int nameLength; private int nameLength;
private int valueLength; private int valueLength;
private string name; private ByteString name;
private enum State private enum State
{ {
...@@ -248,7 +248,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -248,7 +248,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (indexType == HpackUtil.IndexType.None) if (indexType == HpackUtil.IndexType.None)
{ {
// Name is unused so skip bytes // Name is unused so skip bytes
name = string.Empty; name = ByteString.Empty;
skipLength = nameLength; skipLength = nameLength;
state = State.SkipLiteralHeaderName; state = State.SkipLiteralHeaderName;
break; break;
...@@ -258,7 +258,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -258,7 +258,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (nameLength + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity) if (nameLength + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity)
{ {
dynamicTable.Clear(); dynamicTable.Clear();
name = string.Empty; name = Array.Empty<byte>();
skipLength = nameLength; skipLength = nameLength;
state = State.SkipLiteralHeaderName; state = State.SkipLiteralHeaderName;
break; break;
...@@ -292,7 +292,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -292,7 +292,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (indexType == HpackUtil.IndexType.None) if (indexType == HpackUtil.IndexType.None)
{ {
// Name is unused so skip bytes // Name is unused so skip bytes
name = string.Empty; name = ByteString.Empty;
skipLength = nameLength; skipLength = nameLength;
state = State.SkipLiteralHeaderName; state = State.SkipLiteralHeaderName;
break; break;
...@@ -302,7 +302,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -302,7 +302,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (nameLength + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity) if (nameLength + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity)
{ {
dynamicTable.Clear(); dynamicTable.Clear();
name = string.Empty; name = ByteString.Empty;
skipLength = nameLength; skipLength = nameLength;
state = State.SkipLiteralHeaderName; state = State.SkipLiteralHeaderName;
break; break;
...@@ -375,7 +375,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -375,7 +375,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (valueLength == 0) if (valueLength == 0)
{ {
InsertHeader(headerListener, name, string.Empty, indexType); InsertHeader(headerListener, name, Array.Empty<byte>(), indexType);
state = State.ReadHeaderRepresentation; state = State.ReadHeaderRepresentation;
} }
else else
...@@ -531,16 +531,16 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -531,16 +531,16 @@ namespace Titanium.Web.Proxy.Http2.Hpack
private void ReadName(int index) private void ReadName(int index)
{ {
name = GetHeaderField(index).Name; name = GetHeaderField(index).NameData;
} }
private void IndexHeader(int index, IHeaderListener headerListener) private void IndexHeader(int index, IHeaderListener headerListener)
{ {
var headerField = GetHeaderField(index); var headerField = GetHeaderField(index);
AddHeader(headerListener, headerField.Name, headerField.Value, false); AddHeader(headerListener, headerField.NameData, headerField.ValueData, false);
} }
private void InsertHeader(IHeaderListener headerListener, string name, string value, HpackUtil.IndexType indexType) private void InsertHeader(IHeaderListener headerListener, ByteString name, ByteString value, HpackUtil.IndexType indexType)
{ {
AddHeader(headerListener, name, value, indexType == HpackUtil.IndexType.Never); AddHeader(headerListener, name, value, indexType == HpackUtil.IndexType.Never);
...@@ -559,7 +559,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -559,7 +559,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
} }
} }
private void AddHeader(IHeaderListener headerListener, string name, string value, bool sensitive) private void AddHeader(IHeaderListener headerListener, ByteString name, ByteString value, bool sensitive)
{ {
if (name.Length == 0) if (name.Length == 0)
{ {
...@@ -592,7 +592,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -592,7 +592,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
return true; return true;
} }
private string ReadStringLiteral(BinaryReader input, int length) private ByteString ReadStringLiteral(BinaryReader input, int length)
{ {
var buf = new byte[length]; var buf = new byte[length];
int lengthToRead = length; int lengthToRead = length;
...@@ -607,7 +607,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -607,7 +607,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
throw new IOException("decompression failure"); throw new IOException("decompression failure");
} }
return huffmanEncoded ? HuffmanDecoder.Instance.Decode(buf) : Encoding.UTF8.GetString(buf); return new ByteString(huffmanEncoded ? HuffmanDecoder.Instance.Decode(buf) : buf);
} }
// Unsigned Little Endian Base 128 Variable-Length Integer Encoding // Unsigned Little Endian Base 128 Variable-Length Integer Encoding
......
/* 
using Titanium.Web.Proxy.Extensions;
#if NETSTANDARD2_1
/*
* Copyright 2014 Twitter, Inc * Copyright 2014 Twitter, Inc
* This file is a derivative work modified by Ringo Leese * This file is a derivative work modified by Ringo Leese
* *
...@@ -14,7 +18,6 @@ ...@@ -14,7 +18,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
...@@ -22,13 +25,13 @@ using Titanium.Web.Proxy.Models; ...@@ -22,13 +25,13 @@ using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http2.Hpack namespace Titanium.Web.Proxy.Http2.Hpack
{ {
public class Encoder internal class Encoder
{ {
private const int bucketSize = 17; private const int bucketSize = 17;
// a linked hash map of header fields // a linked hash map of header fields
private readonly HeaderEntry?[] headerFields = new HeaderEntry[bucketSize]; private readonly HeaderEntry?[] headerFields = new HeaderEntry[bucketSize];
private readonly HeaderEntry head = new HeaderEntry(-1, string.Empty, string.Empty, int.MaxValue, null); private readonly HeaderEntry head = new HeaderEntry(-1, ByteString.Empty, ByteString.Empty, int.MaxValue, null);
private int size; private int size;
/// <summary> /// <summary>
...@@ -63,7 +66,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -63,7 +66,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="sensitive">If set to <c>true</c> sensitive.</param> /// <param name="sensitive">If set to <c>true</c> sensitive.</param>
/// <param name="indexType">Index type.</param> /// <param name="indexType">Index type.</param>
/// <param name="useStaticName">Use static name.</param> /// <param name="useStaticName">Use static name.</param>
public void EncodeHeader(BinaryWriter output, string name, string value, bool sensitive = false, HpackUtil.IndexType indexType = HpackUtil.IndexType.Incremental, bool useStaticName = true) public void EncodeHeader(BinaryWriter output, ByteString name, ByteString value, bool sensitive = false, HpackUtil.IndexType indexType = HpackUtil.IndexType.Incremental, bool useStaticName = true)
{ {
// If the header value is sensitive then it must never be indexed // If the header value is sensitive then it must never be indexed
if (sensitive) if (sensitive)
...@@ -190,12 +193,11 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -190,12 +193,11 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// Encode string literal according to Section 5.2. /// Encode string literal according to Section 5.2.
/// </summary> /// </summary>
/// <param name="output">Output.</param> /// <param name="output">Output.</param>
/// <param name="stringLiteral">String literal.</param> /// <param name="stringData">String data.</param>
private void encodeStringLiteral(BinaryWriter output, string stringLiteral) private void encodeStringLiteral(BinaryWriter output, ByteString stringData)
{ {
var stringData = Encoding.UTF8.GetBytes(stringLiteral);
int huffmanLength = HuffmanEncoder.Instance.GetEncodedLength(stringData); int huffmanLength = HuffmanEncoder.Instance.GetEncodedLength(stringData);
if (huffmanLength < stringLiteral.Length) if (huffmanLength < stringData.Length)
{ {
encodeInteger(output, 0x80, 7, huffmanLength); encodeInteger(output, 0x80, 7, huffmanLength);
HuffmanEncoder.Instance.Encode(output, stringData); HuffmanEncoder.Instance.Encode(output, stringData);
...@@ -203,7 +205,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -203,7 +205,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
else else
{ {
encodeInteger(output, 0x00, 7, stringData.Length); encodeInteger(output, 0x00, 7, stringData.Length);
output.Write(stringData, 0, stringData.Length); output.Write(stringData.Span);
} }
} }
...@@ -215,7 +217,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -215,7 +217,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="value">Value.</param> /// <param name="value">Value.</param>
/// <param name="indexType">Index type.</param> /// <param name="indexType">Index type.</param>
/// <param name="nameIndex">Name index.</param> /// <param name="nameIndex">Name index.</param>
private void encodeLiteral(BinaryWriter output, string name, string value, HpackUtil.IndexType indexType, private void encodeLiteral(BinaryWriter output, ByteString name, ByteString value, HpackUtil.IndexType indexType,
int nameIndex) int nameIndex)
{ {
int mask; int mask;
...@@ -250,7 +252,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -250,7 +252,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
encodeStringLiteral(output, value); encodeStringLiteral(output, value);
} }
private int getNameIndex(string name) private int getNameIndex(ByteString name)
{ {
int index = StaticTable.GetIndex(name); int index = StaticTable.GetIndex(name);
if (index == -1) if (index == -1)
...@@ -299,9 +301,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -299,9 +301,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <returns>The entry.</returns> /// <returns>The entry.</returns>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="value">Value.</param> /// <param name="value">Value.</param>
private HeaderEntry? getEntry(string name, string value) private HeaderEntry? getEntry(ByteString name, ByteString value)
{ {
if (length() == 0 || name == null || value == null) if (length() == 0 || name.Length == 0 || value.Length == 0)
{ {
return null; return null;
} }
...@@ -310,7 +312,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -310,7 +312,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int i = index(h); int i = index(h);
for (var e = headerFields[i]; e != null; e = e.Next) for (var e = headerFields[i]; e != null; e = e.Next)
{ {
if (e.Hash == h && name.Equals(e.Name, StringComparison.OrdinalIgnoreCase) && Equals(value, e.Value)) if (e.Hash == h && name.Equals(e.NameData) && Equals(value, e.ValueData))
{ {
return e; return e;
} }
...@@ -325,9 +327,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -325,9 +327,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary> /// </summary>
/// <returns>The index.</returns> /// <returns>The index.</returns>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
private int getIndex(string name) private int getIndex(ByteString name)
{ {
if (length() == 0 || name == null) if (length() == 0 || name.Length == 0)
{ {
return -1; return -1;
} }
...@@ -337,7 +339,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -337,7 +339,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int index = -1; int index = -1;
for (var e = headerFields[i]; e != null; e = e.Next) for (var e = headerFields[i]; e != null; e = e.Next)
{ {
if (e.Hash == h && name.Equals(e.Name, StringComparison.OrdinalIgnoreCase)) if (e.Hash == h && name.Equals(e.NameData))
{ {
index = e.Index; index = e.Index;
break; break;
...@@ -371,7 +373,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -371,7 +373,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="value">Value.</param> /// <param name="value">Value.</param>
private void add(string name, string value) private void add(ByteString name, ByteString value)
{ {
int headerSize = HttpHeader.SizeOf(name, value); int headerSize = HttpHeader.SizeOf(name, value);
...@@ -457,12 +459,12 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -457,12 +459,12 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary> /// </summary>
/// <returns><c>true</c> if hash name; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if hash name; otherwise, <c>false</c>.</returns>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
private static int hash(string name) private static int hash(ByteString name)
{ {
int h = 0; int h = 0;
for (int i = 0; i < name.Length; i++) for (int i = 0; i < name.Length; i++)
{ {
h = 31 * h + name[i]; h = 31 * h + name.Span[i];
} }
if (h > 0) if (h > 0)
...@@ -514,7 +516,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -514,7 +516,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="value">Value.</param> /// <param name="value">Value.</param>
/// <param name="index">Index.</param> /// <param name="index">Index.</param>
/// <param name="next">Next.</param> /// <param name="next">Next.</param>
public HeaderEntry(int hash, string name, string value, int index, HeaderEntry? next) : base(name, value, true) public HeaderEntry(int hash, ByteString name, ByteString value, int index, HeaderEntry? next) : base(name, value, true)
{ {
Index = index; Index = index;
Hash = hash; Hash = hash;
...@@ -544,3 +546,4 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -544,3 +546,4 @@ namespace Titanium.Web.Proxy.Http2.Hpack
} }
} }
} }
#endif
...@@ -54,7 +54,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -54,7 +54,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="buf">the string literal to be decoded</param> /// <param name="buf">the string literal to be decoded</param>
/// <returns>the output stream for the compressed data</returns> /// <returns>the output stream for the compressed data</returns>
/// <exception cref="IOException">throws IOException if an I/O error occurs. In particular, an <code>IOException</code> may be thrown if the output stream has been closed.</exception> /// <exception cref="IOException">throws IOException if an I/O error occurs. In particular, an <code>IOException</code> may be thrown if the output stream has been closed.</exception>
public string Decode(byte[] buf) public ReadOnlyMemory<byte> Decode(byte[] buf)
{ {
var resultBuf = new byte[buf.Length * 2]; var resultBuf = new byte[buf.Length * 2];
int resultSize = 0; int resultSize = 0;
...@@ -109,7 +109,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -109,7 +109,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
throw new IOException("Invalid Padding"); throw new IOException("Invalid Padding");
} }
return Encoding.UTF8.GetString(resultBuf, 0, resultSize); return resultBuf.AsMemory(0, resultSize);
} }
private class Node private class Node
......
...@@ -17,10 +17,11 @@ ...@@ -17,10 +17,11 @@
using System; using System;
using System.IO; using System.IO;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http2.Hpack namespace Titanium.Web.Proxy.Http2.Hpack
{ {
public class HuffmanEncoder internal class HuffmanEncoder
{ {
/// <summary> /// <summary>
/// Huffman Encoder /// Huffman Encoder
...@@ -42,39 +43,15 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -42,39 +43,15 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary> /// </summary>
/// <param name="output">the output stream for the compressed data</param> /// <param name="output">the output stream for the compressed data</param>
/// <param name="data">the string literal to be Huffman encoded</param> /// <param name="data">the string literal to be Huffman encoded</param>
/// <exception cref="IOException">if an I/O error occurs.</exception>
/// <see cref="Encode(BinaryWriter,byte[],int,int)"/>
public void Encode(BinaryWriter output, byte[] data)
{
Encode(output, data, 0, data.Length);
}
/// <summary>
/// Compresses the input string literal using the Huffman coding.
/// </summary>
/// <param name="output">the output stream for the compressed data</param>
/// <param name="data">the string literal to be Huffman encoded</param>
/// <param name="off">the start offset in the data</param>
/// <param name="len">the number of bytes to encode</param>
/// <exception cref="IOException">if an I/O error occurs. In particular, an <code>IOException</code> may be thrown if the output stream has been closed.</exception> /// <exception cref="IOException">if an I/O error occurs. In particular, an <code>IOException</code> may be thrown if the output stream has been closed.</exception>
public void Encode(BinaryWriter output, byte[] data, int off, int len) public void Encode(BinaryWriter output, ByteString data)
{ {
if (output == null) if (output == null)
{ {
throw new ArgumentNullException(nameof(output)); throw new ArgumentNullException(nameof(output));
} }
if (data == null) if (data.Length == 0)
{
throw new ArgumentNullException(nameof(data));
}
if (off < 0 || len < 0 || (off + len) < 0 || off > data.Length || (off + len) > data.Length)
{
throw new IndexOutOfRangeException();
}
if (len == 0)
{ {
return; return;
} }
...@@ -82,9 +59,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -82,9 +59,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack
long current = 0L; long current = 0L;
int n = 0; int n = 0;
for (int i = 0; i < len; i++) for (int i = 0; i < data.Length; i++)
{ {
int b = data[off + i] & 0xFF; int b = data.Span[i] & 0xFF;
uint code = (uint)codes[b]; uint code = (uint)codes[b];
int nbits = lengths[b]; int nbits = lengths[b];
...@@ -112,15 +89,10 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -112,15 +89,10 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary> /// </summary>
/// <returns>the number of bytes required to Huffman encode <code>data</code></returns> /// <returns>the number of bytes required to Huffman encode <code>data</code></returns>
/// <param name="data">the string literal to be Huffman encoded</param> /// <param name="data">the string literal to be Huffman encoded</param>
public int GetEncodedLength(byte[] data) public int GetEncodedLength(ByteString data)
{ {
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
long len = 0L; long len = 0L;
foreach (byte b in data) foreach (byte b in data.Span)
{ {
len += lengths[b]; len += lengths[b];
} }
......
...@@ -14,9 +14,13 @@ ...@@ -14,9 +14,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
using System;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http2.Hpack namespace Titanium.Web.Proxy.Http2.Hpack
{ {
public interface IHeaderListener internal interface IHeaderListener
{ {
/// <summary> /// <summary>
/// EmitHeader is called by the decoder during header field emission. /// EmitHeader is called by the decoder during header field emission.
...@@ -25,6 +29,6 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -25,6 +29,6 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="value">Value.</param> /// <param name="value">Value.</param>
/// <param name="sensitive">If set to <c>true</c> sensitive.</param> /// <param name="sensitive">If set to <c>true</c> sensitive.</param>
void AddHeader(string name, string value, bool sensitive); void AddHeader(ByteString name, ByteString value, bool sensitive);
} }
} }
#if NETSTANDARD2_1 
using Titanium.Web.Proxy.Extensions;
#if NETSTANDARD2_1
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
...@@ -13,6 +15,7 @@ using Titanium.Web.Proxy.EventArguments; ...@@ -13,6 +15,7 @@ using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http2.Hpack; using Titanium.Web.Proxy.Http2.Hpack;
using Titanium.Web.Proxy.Models;
using Decoder = Titanium.Web.Proxy.Http2.Hpack.Decoder; using Decoder = Titanium.Web.Proxy.Http2.Hpack.Decoder;
using Encoder = Titanium.Web.Proxy.Http2.Hpack.Encoder; using Encoder = Titanium.Web.Proxy.Http2.Hpack.Encoder;
...@@ -234,7 +237,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -234,7 +237,7 @@ namespace Titanium.Web.Proxy.Http2
(name, value) => (name, value) =>
{ {
var headers = isClient ? args.HttpClient.Request.Headers : args.HttpClient.Response.Headers; var headers = isClient ? args.HttpClient.Request.Headers : args.HttpClient.Response.Headers;
headers.AddHeader(name, value); headers.AddHeader(new HttpHeader(name, value));
}); });
try try
{ {
...@@ -252,16 +255,16 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -252,16 +255,16 @@ namespace Titanium.Web.Proxy.Http2
if (rr is Request request) if (rr is Request request)
{ {
string? method = headerListener.Method; var method = headerListener.Method;
string? path = headerListener.Path; var path = headerListener.Path;
if (method == null || path == null) if (method.Length == 0 || path.Length == 0)
{ {
throw new Exception("HTTP/2 Missing method or path"); throw new Exception("HTTP/2 Missing method or path");
} }
request.HttpVersion = HttpVersion.Version20; request.HttpVersion = HttpVersion.Version20;
request.Method = method; request.Method = method.GetString();
request.OriginalUrl = path; request.OriginalUrlData = path;
request.RequestUri = headerListener.GetUri(); request.RequestUri = headerListener.GetUri();
} }
...@@ -269,7 +272,10 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -269,7 +272,10 @@ namespace Titanium.Web.Proxy.Http2
{ {
var response = (Response)rr; var response = (Response)rr;
response.HttpVersion = HttpVersion.Version20; response.HttpVersion = HttpVersion.Version20;
int.TryParse(headerListener.Status, out int statusCode);
// todo: avoid string conversion
string statusHack = HttpHeader.Encoding.GetString(headerListener.Status.Span);
int.TryParse(statusHack, out int statusCode);
response.StatusCode = statusCode; response.StatusCode = statusCode;
response.StatusDescription = string.Empty; response.StatusDescription = string.Empty;
} }
...@@ -461,21 +467,21 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -461,21 +467,21 @@ namespace Titanium.Web.Proxy.Http2
if (rr is Request request) if (rr is Request request)
{ {
encoder.EncodeHeader(writer, ":method", request.Method); encoder.EncodeHeader(writer, StaticTable.KnownHeaderMethod, request.Method.GetByteString());
encoder.EncodeHeader(writer, ":authority", request.RequestUri.Host); encoder.EncodeHeader(writer, StaticTable.KnownHeaderAuhtority, request.RequestUri.Host.GetByteString());
encoder.EncodeHeader(writer, ":scheme", request.RequestUri.Scheme); encoder.EncodeHeader(writer, StaticTable.KnownHeaderScheme, request.RequestUri.Scheme.GetByteString());
encoder.EncodeHeader(writer, ":path", request.RequestUriString, false, encoder.EncodeHeader(writer, StaticTable.KnownHeaderPath, request.Url.GetByteString(), false,
HpackUtil.IndexType.None, false); HpackUtil.IndexType.None, false);
} }
else else
{ {
var response = (Response)rr; var response = (Response)rr;
encoder.EncodeHeader(writer, ":status", response.StatusCode.ToString()); encoder.EncodeHeader(writer, StaticTable.KnownHeaderStatus, response.StatusCode.ToString().GetByteString());
} }
foreach (var header in rr.Headers) foreach (var header in rr.Headers)
{ {
encoder.EncodeHeader(writer, header.Name.ToLower(), header.Value); encoder.EncodeHeader(writer, header.NameData, header.ValueData);
} }
var data = ms.ToArray(); var data = ms.ToArray();
...@@ -565,28 +571,29 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -565,28 +571,29 @@ namespace Titanium.Web.Proxy.Http2
class MyHeaderListener : IHeaderListener class MyHeaderListener : IHeaderListener
{ {
private readonly Action<string, string> addHeaderFunc; private readonly Action<ByteString, ByteString> addHeaderFunc;
public string? Method { get; private set; } public ByteString Method { get; private set; }
public string? Status { get; private set; } public ByteString Status { get; private set; }
private string? authority; private ByteString authority;
private string? scheme; private ByteString scheme;
public string? Path { get; private set; } public ByteString Path { get; private set; }
public MyHeaderListener(Action<string, string> addHeaderFunc) public MyHeaderListener(Action<ByteString, ByteString> addHeaderFunc)
{ {
this.addHeaderFunc = addHeaderFunc; this.addHeaderFunc = addHeaderFunc;
} }
public void AddHeader(string name, string value, bool sensitive) public void AddHeader(ByteString name, ByteString value, bool sensitive)
{ {
if (name[0] == ':') if (name.Span[0] == ':')
{ {
switch (name) string nameStr = Encoding.ASCII.GetString(name.Span);
switch (nameStr)
{ {
case ":method": case ":method":
Method = value; Method = value;
...@@ -611,13 +618,23 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -611,13 +618,23 @@ namespace Titanium.Web.Proxy.Http2
public Uri GetUri() public Uri GetUri()
{ {
if (authority == null) if (authority.Length == 0)
{ {
// todo // todo
authority = "abc.abc"; authority = HttpHeader.Encoding.GetBytes("abc.abc");
} }
return new Uri(scheme + "://" + authority + Path); var bytes = new byte[scheme.Length + 3 + authority.Length + Path.Length];
scheme.Span.CopyTo(bytes);
int idx = scheme.Length;
bytes[idx++] = (byte)':';
bytes[idx++] = (byte)'/';
bytes[idx++] = (byte)'/';
authority.Span.CopyTo(bytes.AsSpan(idx, authority.Length));
idx += authority.Length;
Path.Span.CopyTo(bytes.AsSpan(idx, Path.Length));
return new Uri(HttpHeader.Encoding.GetString(bytes));
} }
} }
} }
......
using System;
using System.Text;
namespace Titanium.Web.Proxy.Models
{
internal struct ByteString : IEquatable<ByteString>
{
public static ByteString Empty = new ByteString(ReadOnlyMemory<byte>.Empty);
public ReadOnlyMemory<byte> Data { get; }
public ReadOnlySpan<byte> Span => Data.Span;
public int Length => Data.Length;
public ByteString(ReadOnlyMemory<byte> data)
{
Data = data;
}
public override bool Equals(object? obj)
{
return obj is ByteString other && Equals(other);
}
public bool Equals(ByteString other)
{
return Data.Span.SequenceEqual(other.Data.Span);
}
public override int GetHashCode()
{
return Data.GetHashCode();
}
public static explicit operator ByteString(string str) => new ByteString(Encoding.ASCII.GetBytes(str));
public static implicit operator ByteString(byte[] data) => new ByteString(data);
public static implicit operator ByteString(ReadOnlyMemory<byte> data) => new ByteString(data);
}
}
...@@ -11,9 +11,9 @@ namespace Titanium.Web.Proxy.Models ...@@ -11,9 +11,9 @@ namespace Titanium.Web.Proxy.Models
private static readonly Lazy<NetworkCredential> defaultCredentials = private static readonly Lazy<NetworkCredential> defaultCredentials =
new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials); new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials);
private string password; private string? password;
private string userName; private string? userName;
/// <summary> /// <summary>
/// Use default windows credentials? /// Use default windows credentials?
...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// Username. /// Username.
/// </summary> /// </summary>
public string UserName public string? UserName
{ {
get => UseDefaultCredentials ? defaultCredentials.Value.UserName : userName; get => UseDefaultCredentials ? defaultCredentials.Value.UserName : userName;
set set
...@@ -45,7 +45,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -45,7 +45,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// Password. /// Password.
/// </summary> /// </summary>
public string Password public string? Password
{ {
get => UseDefaultCredentials ? defaultCredentials.Value.Password : password; get => UseDefaultCredentials ? defaultCredentials.Value.Password : password;
set set
...@@ -62,22 +62,13 @@ namespace Titanium.Web.Proxy.Models ...@@ -62,22 +62,13 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// Host name. /// Host name.
/// </summary> /// </summary>
public string HostName { get; set; } public string HostName { get; set; } = string.Empty;
/// <summary> /// <summary>
/// Port. /// Port.
/// </summary> /// </summary>
public int Port { get; set; } public int Port { get; set; }
/// <summary>
/// Get cache key for Tcp connection cache.
/// </summary>
/// <returns></returns>
internal string GetCacheKey()
{
return $"{HostName}-{Port}" + (UseDefaultCredentials ? $"-{UserName}-{Password}" : string.Empty);
}
/// <summary> /// <summary>
/// returns data in Hostname:port format. /// returns data in Hostname:port format.
/// </summary> /// </summary>
......
using System; using System;
using System.Net; using System.Net;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
...@@ -33,6 +35,10 @@ namespace Titanium.Web.Proxy.Models ...@@ -33,6 +35,10 @@ namespace Titanium.Web.Proxy.Models
internal static Version Version20 { get; } = new Version(2, 0); internal static Version Version20 { get; } = new Version(2, 0);
#endif #endif
internal static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ISO-8859-1");
public static Encoding Encoding => DefaultEncoding;
internal static readonly HttpHeader ProxyConnectionKeepAlive = new HttpHeader("Proxy-Connection", "keep-alive"); internal static readonly HttpHeader ProxyConnectionKeepAlive = new HttpHeader("Proxy-Connection", "keep-alive");
/// <summary> /// <summary>
...@@ -47,30 +53,45 @@ namespace Titanium.Web.Proxy.Models ...@@ -47,30 +53,45 @@ namespace Titanium.Web.Proxy.Models
throw new Exception("Name cannot be null or empty"); throw new Exception("Name cannot be null or empty");
} }
Name = name.Trim(); NameData = name.Trim().GetByteString();
Value = value.Trim(); ValueData = value.Trim().GetByteString();
}
internal HttpHeader(ByteString name, ByteString value)
{
if (name.Length == 0)
{
throw new Exception("Name cannot be empty");
}
NameData = name;
ValueData = value;
} }
protected HttpHeader(string name, string value, bool headerEntry) private protected HttpHeader(ByteString name, ByteString value, bool headerEntry)
{ {
// special header entry created in inherited class with empty name // special header entry created in inherited class with empty name
Name = name.Trim(); NameData = name;
Value = value.Trim(); ValueData = value;
} }
/// <summary> /// <summary>
/// Header Name. /// Header Name.
/// </summary> /// </summary>
public string Name { get; } public string Name => NameData.GetString();
internal ByteString NameData { get; }
/// <summary> /// <summary>
/// Header Value. /// Header Value.
/// </summary> /// </summary>
public string Value { get; set; } public string Value => ValueData.GetString();
internal ByteString ValueData { get; set; }
public int Size => Name.Length + Value.Length + HttpHeaderOverhead; public int Size => Name.Length + Value.Length + HttpHeaderOverhead;
public static int SizeOf(string name, string value) internal static int SizeOf(ByteString name, ByteString value)
{ {
return name.Length + value.Length + HttpHeaderOverhead; return name.Length + value.Length + HttpHeaderOverhead;
} }
...@@ -84,7 +105,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -84,7 +105,7 @@ namespace Titanium.Web.Proxy.Models
return $"{Name}: {Value}"; return $"{Name}: {Value}";
} }
internal static HttpHeader GetProxyAuthorizationHeader(string userName, string password) internal static HttpHeader GetProxyAuthorizationHeader(string? userName, string? password)
{ {
var result = new HttpHeader(KnownHeaders.ProxyAuthorization, var result = new HttpHeader(KnownHeaders.ProxyAuthorization,
"Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{password}"))); "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{password}")));
......
...@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// underlying TCP Listener object /// underlying TCP Listener object
/// </summary> /// </summary>
internal TcpListener Listener { get; set; } internal TcpListener? Listener { get; set; }
/// <summary> /// <summary>
/// Ip Address we are listening. /// Ip Address we are listening.
...@@ -45,6 +45,6 @@ namespace Titanium.Web.Proxy.Models ...@@ -45,6 +45,6 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// Generic certificate to use for SSL decryption. /// Generic certificate to use for SSL decryption.
/// </summary> /// </summary>
public X509Certificate2 GenericCertificate { get; set; } public X509Certificate2? GenericCertificate { get; set; }
} }
} }
...@@ -8,7 +8,12 @@ namespace Titanium.Web.Proxy.Network ...@@ -8,7 +8,12 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal sealed class CachedCertificate internal sealed class CachedCertificate
{ {
internal X509Certificate2 Certificate { get; set; } public CachedCertificate(X509Certificate2 certificate)
{
Certificate = certificate;
}
internal X509Certificate2 Certificate { get; }
/// <summary> /// <summary>
/// Last time this certificate was used. /// Last time this certificate was used.
......
...@@ -44,7 +44,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -44,7 +44,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
private readonly Type typeX509PrivateKey; private readonly Type typeX509PrivateKey;
private object sharedPrivateKey; private object? sharedPrivateKey;
/// <summary> /// <summary>
/// Constructor. /// Constructor.
......
...@@ -60,15 +60,30 @@ namespace Titanium.Web.Proxy.Network ...@@ -60,15 +60,30 @@ namespace Titanium.Web.Proxy.Network
private readonly object rootCertCreationLock = new object(); private readonly object rootCertCreationLock = new object();
private ICertificateMaker certEngine; private ICertificateMaker? certEngineValue;
private ICertificateMaker certEngine
{
get
{
if (certEngineValue == null)
{
certEngineValue = engine == CertificateEngine.BouncyCastle
? (ICertificateMaker)new BCCertificateMaker(ExceptionFunc)
: new WinCertificateMaker(ExceptionFunc);
}
return certEngineValue;
}
}
private CertificateEngine engine; private CertificateEngine engine;
private string issuer; private string? issuer;
private X509Certificate2? rootCertificate; private X509Certificate2? rootCertificate;
private string rootCertificateName; private string? rootCertificateName;
private ICertificateCache certificateCache = new DefaultCertificateDiskCache(); private ICertificateCache certificateCache = new DefaultCertificateDiskCache();
...@@ -156,16 +171,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -156,16 +171,9 @@ namespace Titanium.Web.Proxy.Network
if (value != engine) if (value != engine)
{ {
certEngine = null!; certEngineValue = null!;
engine = value; engine = value;
} }
if (certEngine == null)
{
certEngine = engine == CertificateEngine.BouncyCastle
? (ICertificateMaker)new BCCertificateMaker(ExceptionFunc)
: new WinCertificateMaker(ExceptionFunc);
}
} }
} }
...@@ -468,10 +476,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -468,10 +476,7 @@ namespace Titanium.Web.Proxy.Network
var result = CreateCertificate(certificateName, false); var result = CreateCertificate(certificateName, false);
if (result != null) if (result != null)
{ {
cachedCertificates.TryAdd(certificateName, new CachedCertificate cachedCertificates.TryAdd(certificateName, new CachedCertificate(result));
{
Certificate = result
});
} }
return result; return result;
......
...@@ -9,19 +9,26 @@ namespace Titanium.Web.Proxy.Network ...@@ -9,19 +9,26 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal class ProxyClient internal class ProxyClient
{ {
public ProxyClient(TcpClientConnection connection, CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter)
{
Connection = connection;
ClientStream = clientStream;
ClientStreamWriter = clientStreamWriter;
}
/// <summary> /// <summary>
/// TcpClient connection used to communicate with client /// TcpClient connection used to communicate with client
/// </summary> /// </summary>
internal TcpClientConnection Connection { get; set; } internal TcpClientConnection Connection { get; }
/// <summary> /// <summary>
/// Holds the stream to client /// Holds the stream to client
/// </summary> /// </summary>
internal CustomBufferedStream ClientStream { get; set; } internal CustomBufferedStream ClientStream { get; }
/// <summary> /// <summary>
/// Used to write line by line to client /// Used to write line by line to client
/// </summary> /// </summary>
internal HttpResponseWriter ClientStreamWriter { get; set; } internal HttpResponseWriter ClientStreamWriter { get; }
} }
} }
...@@ -49,27 +49,52 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -49,27 +49,52 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal string GetConnectionCacheKey(string remoteHostName, int remotePort, internal string GetConnectionCacheKey(string remoteHostName, int remotePort,
bool isHttps, List<SslApplicationProtocol>? applicationProtocols, bool isHttps, List<SslApplicationProtocol>? applicationProtocols,
IPEndPoint upStreamEndPoint, ExternalProxy? externalProxy) IPEndPoint? upStreamEndPoint, ExternalProxy? externalProxy)
{ {
// http version is ignored since its an application level decision b/w HTTP 1.0/1.1 // 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. // also when doing connect request MS Edge browser sends http 1.0 but uses 1.1 after server sends 1.1 its response.
// That can create cache miss for same server connection unnecessarily especially when prefetching with Connect. // That can create cache miss for same server connection unnecessarily especially when prefetching with Connect.
// http version 2 is separated using applicationProtocols below. // http version 2 is separated using applicationProtocols below.
var cacheKeyBuilder = new StringBuilder($"{remoteHostName}-{remotePort}-" + var cacheKeyBuilder = new StringBuilder();
// when creating Tcp client isConnect won't matter cacheKeyBuilder.Append(remoteHostName);
$"{isHttps}-"); cacheKeyBuilder.Append("-");
cacheKeyBuilder.Append(remotePort);
cacheKeyBuilder.Append("-");
// when creating Tcp client isConnect won't matter
cacheKeyBuilder.Append(isHttps);
if (applicationProtocols != null) if (applicationProtocols != null)
{ {
foreach (var protocol in applicationProtocols.OrderBy(x => x)) foreach (var protocol in applicationProtocols.OrderBy(x => x))
{ {
cacheKeyBuilder.Append($"{protocol}-"); cacheKeyBuilder.Append("-");
cacheKeyBuilder.Append(protocol);
} }
} }
cacheKeyBuilder.Append(upStreamEndPoint != null if (upStreamEndPoint != null)
? $"{upStreamEndPoint.Address}-{upStreamEndPoint.Port}-" {
: string.Empty); cacheKeyBuilder.Append("-");
cacheKeyBuilder.Append(externalProxy != null ? $"{externalProxy.GetCacheKey()}-" : string.Empty); cacheKeyBuilder.Append(upStreamEndPoint.Address);
cacheKeyBuilder.Append("-");
cacheKeyBuilder.Append(upStreamEndPoint.Port);
}
if (externalProxy != null)
{
cacheKeyBuilder.Append("-");
cacheKeyBuilder.Append(externalProxy.HostName);
cacheKeyBuilder.Append("-");
cacheKeyBuilder.Append(externalProxy.Port);
if (externalProxy.UseDefaultCredentials)
{
cacheKeyBuilder.Append("-");
cacheKeyBuilder.Append(externalProxy.UserName);
cacheKeyBuilder.Append("-");
cacheKeyBuilder.Append(externalProxy.Password);
}
}
return cacheKeyBuilder.ToString(); return cacheKeyBuilder.ToString();
} }
...@@ -181,7 +206,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -181,7 +206,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <returns></returns> /// <returns></returns>
internal async Task<TcpServerConnection> GetServerConnection(string remoteHostName, int remotePort, internal async Task<TcpServerConnection> GetServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol>? applicationProtocols, bool isConnect, Version httpVersion, bool isHttps, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint upStreamEndPoint, ExternalProxy? externalProxy, ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, ExternalProxy? externalProxy,
bool noCache, CancellationToken cancellationToken) bool noCache, CancellationToken cancellationToken)
{ {
var sslProtocol = session?.ProxyClient.Connection.SslProtocol ?? SslProtocols.None; var sslProtocol = session?.ProxyClient.Connection.SslProtocol ?? SslProtocols.None;
...@@ -211,9 +236,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -211,9 +236,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
} }
var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol,
applicationProtocols, isConnect, proxyServer, session, upStreamEndPoint, externalProxy, cancellationToken); applicationProtocols, isConnect, proxyServer, session, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
connection.CacheKey = cacheKey;
return connection; return connection;
} }
...@@ -232,11 +255,12 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -232,11 +255,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="session">The http session.</param> /// <param name="session">The http session.</param>
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param> /// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
/// <param name="externalProxy">The external proxy to make request via.</param> /// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="cacheKey">The connection cache key</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns> /// <returns></returns>
private async Task<TcpServerConnection> createServerConnection(string remoteHostName, int remotePort, private async Task<TcpServerConnection> createServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, SslProtocols sslProtocol, List<SslApplicationProtocol>? applicationProtocols, bool isConnect, Version httpVersion, bool isHttps, SslProtocols sslProtocol, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint upStreamEndPoint, ExternalProxy? externalProxy, ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, ExternalProxy? externalProxy, string cacheKey,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
// deny connection to proxy end points to avoid infinite connection loop. // deny connection to proxy end points to avoid infinite connection loop.
...@@ -281,8 +305,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -281,8 +305,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
retry: retry:
try try
{ {
var hostname = useUpstreamProxy ? externalProxy!.HostName : remoteHostName; string hostname = useUpstreamProxy ? externalProxy!.HostName : remoteHostName;
var port = useUpstreamProxy ? externalProxy!.Port : remotePort; int port = useUpstreamProxy ? externalProxy!.Port : remotePort;
var ipAddresses = await Dns.GetHostAddressesAsync(hostname); var ipAddresses = await Dns.GetHostAddressesAsync(hostname);
if (ipAddresses == null || ipAddresses.Length == 0) if (ipAddresses == null || ipAddresses.Length == 0)
...@@ -350,7 +374,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -350,7 +374,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
var writer = new HttpRequestWriter(stream, proxyServer.BufferPool); var writer = new HttpRequestWriter(stream, proxyServer.BufferPool);
var connectRequest = new ConnectRequest var connectRequest = new ConnectRequest
{ {
OriginalUrl = $"{remoteHostName}:{remotePort}", OriginalUrlData = HttpHeader.Encoding.GetBytes($"{remoteHostName}:{remotePort}"),
HttpVersion = httpVersion HttpVersion = httpVersion
}; };
...@@ -418,17 +442,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -418,17 +442,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
throw; throw;
} }
return new TcpServerConnection(proxyServer, tcpClient, stream) return new TcpServerConnection(proxyServer, tcpClient, stream, remoteHostName, remotePort, isHttps,
{ negotiatedApplicationProtocol, httpVersion, useUpstreamProxy, externalProxy, upStreamEndPoint, cacheKey);
UpStreamProxy = externalProxy,
UpStreamEndPoint = upStreamEndPoint,
HostName = remoteHostName,
Port = remotePort,
IsHttps = isHttps,
NegotiatedApplicationProtocol = negotiatedApplicationProtocol,
UseUpstreamProxy = useUpstreamProxy,
Version = httpVersion
};
} }
......
...@@ -15,7 +15,9 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -15,7 +15,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
internal class TcpServerConnection : IDisposable internal class TcpServerConnection : IDisposable
{ {
internal TcpServerConnection(ProxyServer proxyServer, TcpClient tcpClient, CustomBufferedStream stream) internal TcpServerConnection(ProxyServer proxyServer, TcpClient tcpClient, CustomBufferedStream stream,
string hostName, int port, bool isHttps, SslApplicationProtocol negotiatedApplicationProtocol,
Version version, bool useUpstreamProxy, ExternalProxy? upStreamProxy, IPEndPoint? upStreamEndPoint, string cacheKey)
{ {
this.tcpClient = tcpClient; this.tcpClient = tcpClient;
LastAccess = DateTime.Now; LastAccess = DateTime.Now;
...@@ -23,6 +25,16 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -23,6 +25,16 @@ namespace Titanium.Web.Proxy.Network.Tcp
this.proxyServer.UpdateServerConnectionCount(true); this.proxyServer.UpdateServerConnectionCount(true);
StreamWriter = new HttpRequestWriter(stream, proxyServer.BufferPool); StreamWriter = new HttpRequestWriter(stream, proxyServer.BufferPool);
Stream = stream; Stream = stream;
HostName = hostName;
Port = port;
IsHttps = isHttps;
NegotiatedApplicationProtocol = negotiatedApplicationProtocol;
Version = version;
UseUpstreamProxy = useUpstreamProxy;
UpStreamProxy = upStreamProxy;
UpStreamEndPoint = upStreamEndPoint;
CacheKey = cacheKey;
} }
private ProxyServer proxyServer { get; } private ProxyServer proxyServer { get; }
......
...@@ -47,24 +47,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -47,24 +47,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
internal Message(byte[] message) internal Message(byte[] message)
{ {
type = 3; type = 3;
Decode(message);
}
/// <summary>
/// Domain name
/// </summary>
internal string Domain { get; private set; }
/// <summary>
/// Username
/// </summary>
internal string? Username { get; private set; }
internal Common.NtlmFlags Flags { get; set; }
// methods
private void Decode(byte[] message)
{
if (message == null) if (message == null)
{ {
throw new ArgumentNullException(nameof(message)); throw new ArgumentNullException(nameof(message));
...@@ -108,6 +91,18 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -108,6 +91,18 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
Username = DecodeString(message, userOff, userLen); Username = DecodeString(message, userOff, userLen);
} }
/// <summary>
/// Domain name
/// </summary>
internal string Domain { get; private set; }
/// <summary>
/// Username
/// </summary>
internal string Username { get; private set; }
internal Common.NtlmFlags Flags { get; set; }
private string DecodeString(byte[] buffer, int offset, int len) private string DecodeString(byte[] buffer, int offset, int len)
{ {
if ((Flags & Common.NtlmFlags.NegotiateUnicode) != 0) if ((Flags & Common.NtlmFlags.NegotiateUnicode) != 0)
......
...@@ -70,10 +70,8 @@ namespace Titanium.Web.Proxy ...@@ -70,10 +70,8 @@ namespace Titanium.Web.Proxy
return; return;
} }
var args = new SessionEventArgs(this, endPoint, cancellationTokenSource) var args = new SessionEventArgs(this, endPoint, new ProxyClient(clientConnection, clientStream, clientStreamWriter), connectRequest, cancellationTokenSource)
{ {
ProxyClient = { Connection = clientConnection },
HttpClient = { ConnectRequest = connectRequest },
UserData = connectArgs?.UserData UserData = connectArgs?.UserData
}; };
...@@ -122,12 +120,10 @@ namespace Titanium.Web.Proxy ...@@ -122,12 +120,10 @@ namespace Titanium.Web.Proxy
var request = args.HttpClient.Request; var request = args.HttpClient.Request;
request.RequestUri = httpRemoteUri; request.RequestUri = httpRemoteUri;
request.OriginalUrl = httpUrl; request.OriginalUrlData = HttpHeader.Encoding.GetBytes(httpUrl);
request.Method = httpMethod; request.Method = httpMethod;
request.HttpVersion = version; request.HttpVersion = version;
args.ProxyClient.ClientStream = clientStream;
args.ProxyClient.ClientStreamWriter = clientStreamWriter;
if (!args.IsTransparent) if (!args.IsTransparent)
{ {
......
...@@ -93,7 +93,7 @@ namespace Titanium.Web.Proxy ...@@ -93,7 +93,7 @@ namespace Titanium.Web.Proxy
// clear current response // clear current response
await args.ClearResponse(cancellationToken); await args.ClearResponse(cancellationToken);
await handleHttpSessionRequest(args.HttpClient.Request.Method, args.HttpClient.Request.RequestUriString, args.HttpClient.Request.HttpVersion, await handleHttpSessionRequest(args.HttpClient.Request.Method, args.HttpClient.Request.Url, args.HttpClient.Request.HttpVersion,
args, null, args.ClientConnection.NegotiatedApplicationProtocol, args, null, args.ClientConnection.NegotiatedApplicationProtocol,
cancellationToken, args.CancellationTokenSource); cancellationToken, args.CancellationTokenSource);
return; return;
......
...@@ -17,19 +17,19 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -17,19 +17,19 @@ namespace Titanium.Web.Proxy.StreamExtended
"DEFLATE" "DEFLATE"
}; };
public int HandshakeVersion { get; set; } public int HandshakeVersion { get; }
public int MajorVersion { get; set; } public int MajorVersion { get; }
public int MinorVersion { get; set; } public int MinorVersion { get; }
public byte[] Random { get; set; } public byte[] Random { get; }
public DateTime Time public DateTime Time
{ {
get get
{ {
DateTime time = DateTime.MinValue; var time = DateTime.MinValue;
if (Random.Length > 3) if (Random.Length > 3)
{ {
time = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) time = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)
...@@ -42,13 +42,13 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -42,13 +42,13 @@ namespace Titanium.Web.Proxy.StreamExtended
public byte[] SessionId { get; } public byte[] SessionId { get; }
public int[] Ciphers { get; set; } public int[] Ciphers { get; }
public byte[] CompressionData { get; set; } public byte[]? CompressionData { get; internal set; }
internal int ClientHelloLength { get; set; } internal int ClientHelloLength { get; }
internal int EntensionsStartPosition { get; set; } internal int ExtensionsStartPosition { get; set; }
public Dictionary<string, SslExtension>? Extensions { get; set; } public Dictionary<string, SslExtension>? Extensions { get; set; }
...@@ -79,9 +79,15 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -79,9 +79,15 @@ namespace Titanium.Web.Proxy.StreamExtended
} }
} }
public ClientHelloInfo(byte[] sessionId) internal ClientHelloInfo(int handshakeVersion, int majorVersion, int minorVersion, byte[] random, byte[] sessionId, int[] ciphers, int clientHelloLength)
{ {
HandshakeVersion = handshakeVersion;
MajorVersion = majorVersion;
MinorVersion = minorVersion;
Random = random;
SessionId = sessionId; SessionId = sessionId;
Ciphers = ciphers;
ClientHelloLength = clientHelloLength;
} }
private static string SslVersionToString(int major, int minor) private static string SslVersionToString(int major, int minor)
......
...@@ -9,9 +9,9 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -9,9 +9,9 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// Copies the source stream to destination stream. /// Copies the source stream to destination stream.
/// But this let users to peek and read the copying process. /// But this let users to peek and read the copying process.
/// </summary> /// </summary>
public class CopyStream : ICustomStreamReader, IDisposable internal class CopyStream : ILineStream, IDisposable
{ {
private readonly ICustomStreamReader reader; private readonly CustomBufferedStream reader;
private readonly ICustomStreamWriter writer; private readonly ICustomStreamWriter writer;
...@@ -23,13 +23,11 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -23,13 +23,11 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
private bool disposed; private bool disposed;
public int Available => reader.Available;
public bool DataAvailable => reader.DataAvailable; public bool DataAvailable => reader.DataAvailable;
public long ReadBytes { get; private set; } public long ReadBytes { get; private set; }
public CopyStream(ICustomStreamReader reader, ICustomStreamWriter writer, IBufferPool bufferPool) public CopyStream(CustomBufferedStream reader, ICustomStreamWriter writer, IBufferPool bufferPool)
{ {
this.reader = reader; this.reader = reader;
this.writer = writer; this.writer = writer;
...@@ -43,31 +41,6 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -43,31 +41,6 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
return await reader.FillBufferAsync(cancellationToken); return await reader.FillBufferAsync(cancellationToken);
} }
public byte PeekByteFromBuffer(int index)
{
return reader.PeekByteFromBuffer(index);
}
public ValueTask<int> PeekByteAsync(int index, CancellationToken cancellationToken = default)
{
return reader.PeekByteAsync(index, cancellationToken);
}
public ValueTask<int> PeekBytesAsync(byte[] buffer, int offset, int index, int size, CancellationToken cancellationToken = default)
{
return reader.PeekBytesAsync(buffer, offset, index, size, cancellationToken);
}
public void Flush()
{
// send out the current data from from the buffer
if (bufferLength > 0)
{
writer.Write(buffer, 0, bufferLength);
bufferLength = 0;
}
}
public async Task FlushAsync(CancellationToken cancellationToken = default) public async Task FlushAsync(CancellationToken cancellationToken = default)
{ {
// send out the current data from from the buffer // send out the current data from from the buffer
...@@ -86,63 +59,6 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -86,63 +59,6 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
return b; return b;
} }
public int Read(byte[] buffer, int offset, int count)
{
int result = reader.Read(buffer, offset, count);
if (result > 0)
{
if (bufferLength + result > bufferPool.BufferSize)
{
Flush();
}
Buffer.BlockCopy(buffer, offset, this.buffer, bufferLength, result);
bufferLength += result;
ReadBytes += result;
Flush();
}
return result;
}
public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)
{
int result = await reader.ReadAsync(buffer, offset, count, cancellationToken);
if (result > 0)
{
if (bufferLength + result > bufferPool.BufferSize)
{
await FlushAsync(cancellationToken);
}
Buffer.BlockCopy(buffer, offset, this.buffer, bufferLength, result);
bufferLength += result;
ReadBytes += result;
await FlushAsync(cancellationToken);
}
return result;
}
public async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
int result = await reader.ReadAsync(buffer, cancellationToken);
if (result > 0)
{
if (bufferLength + result > bufferPool.BufferSize)
{
await FlushAsync(cancellationToken);
}
buffer.Span.Slice(0, result).CopyTo(new Span<byte>(this.buffer, bufferLength, result));
bufferLength += result;
ReadBytes += result;
await FlushAsync(cancellationToken);
}
return result;
}
public ValueTask<string?> ReadLineAsync(CancellationToken cancellationToken = default) public ValueTask<string?> ReadLineAsync(CancellationToken cancellationToken = default)
{ {
return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken); return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken);
......
using System;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.StreamExtended.BufferPool;
namespace Titanium.Web.Proxy.StreamExtended.Network
{
internal class CustomBufferedPeekStream : ICustomStreamReader
{
private readonly IBufferPool bufferPool;
private readonly ICustomStreamReader baseStream;
internal int Position { get; private set; }
internal CustomBufferedPeekStream(ICustomStreamReader baseStream, IBufferPool bufferPool, int startPosition = 0)
{
this.bufferPool = bufferPool;
this.baseStream = baseStream;
Position = startPosition;
}
/// <summary>
/// Gets a value indicating whether data is available.
/// </summary>
bool ICustomStreamReader.DataAvailable => Available > 0;
/// <summary>
/// Gets the available data size.
/// </summary>
public int Available => baseStream.Available - Position;
internal async Task<bool> EnsureBufferLength(int length, CancellationToken cancellationToken)
{
var val = await baseStream.PeekByteAsync(Position + length - 1, cancellationToken);
return val != -1;
}
internal byte ReadByte()
{
return baseStream.PeekByteFromBuffer(Position++);
}
internal int ReadInt16()
{
int i1 = ReadByte();
int i2 = ReadByte();
return (i1 << 8) + i2;
}
internal int ReadInt24()
{
int i1 = ReadByte();
int i2 = ReadByte();
int i3 = ReadByte();
return (i1 << 16) + (i2 << 8) + i3;
}
internal byte[] ReadBytes(int length)
{
var buffer = new byte[length];
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = ReadByte();
}
return buffer;
}
/// <summary>
/// Fills the buffer asynchronous.
/// </summary>
/// <returns></returns>
ValueTask<bool> ICustomStreamReader.FillBufferAsync(CancellationToken cancellationToken)
{
return baseStream.FillBufferAsync(cancellationToken);
}
/// <summary>
/// Peeks a byte from buffer.
/// </summary>
/// <param name="index">The index.</param>
/// <returns></returns>
byte ICustomStreamReader.PeekByteFromBuffer(int index)
{
return baseStream.PeekByteFromBuffer(index);
}
/// <summary>
/// Peeks bytes asynchronous.
/// </summary>
/// <param name="buffer">The buffer to copy.</param>
/// <param name="offset">The offset where copying.</param>
/// <param name="index">The index.</param>
/// <param name="count">The count.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
ValueTask<int> ICustomStreamReader.PeekBytesAsync(byte[] buffer, int offset, int index, int count, CancellationToken cancellationToken)
{
return baseStream.PeekBytesAsync(buffer, offset, index, count, cancellationToken);
}
/// <summary>
/// Peeks a byte asynchronous.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
ValueTask<int> ICustomStreamReader.PeekByteAsync(int index, CancellationToken cancellationToken)
{
return baseStream.PeekByteAsync(index, cancellationToken);
}
/// <summary>
/// Reads a byte from buffer.
/// </summary>
/// <returns></returns>
byte ICustomStreamReader.ReadByteFromBuffer()
{
return ReadByte();
}
int ICustomStreamReader.Read(byte[] buffer, int offset, int count)
{
return baseStream.Read(buffer, offset, count);
}
/// <summary>
/// Reads the asynchronous.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The offset.</param>
/// <param name="count">The count.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
Task<int> ICustomStreamReader.ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return baseStream.ReadAsync(buffer, offset, count, cancellationToken);
}
/// <summary>
/// Reads the asynchronous.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
public ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
return baseStream.ReadAsync(buffer, cancellationToken);
}
/// <summary>
/// Read a line from the byte stream
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
ValueTask<string?> ICustomStreamReader.ReadLineAsync(CancellationToken cancellationToken)
{
return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken);
}
}
}
...@@ -6,6 +6,7 @@ using System.Text; ...@@ -6,6 +6,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.StreamExtended.BufferPool; using Titanium.Web.Proxy.StreamExtended.BufferPool;
namespace Titanium.Web.Proxy.StreamExtended.Network namespace Titanium.Web.Proxy.StreamExtended.Network
...@@ -16,13 +17,12 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -16,13 +17,12 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// of UTF-8 encoded string or raw bytes asynchronously from last read position. /// of UTF-8 encoded string or raw bytes asynchronously from last read position.
/// </summary> /// </summary>
/// <seealso cref="System.IO.Stream" /> /// <seealso cref="System.IO.Stream" />
internal class CustomBufferedStream : Stream, ICustomStreamReader internal class CustomBufferedStream : Stream, IPeekStream, ILineStream
{ {
private readonly bool leaveOpen; private readonly bool leaveOpen;
private readonly byte[] streamBuffer; private readonly byte[] streamBuffer;
// default to UTF-8 private static Encoding encoding => HttpHeader.Encoding;
private static Encoding encoding => HttpHelper.HeaderEncoding;
private static readonly bool networkStreamHack = true; private static readonly bool networkStreamHack = true;
...@@ -606,7 +606,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -606,7 +606,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// Read a line from the byte stream /// Read a line from the byte stream
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
internal static async ValueTask<string?> ReadLineInternalAsync(ICustomStreamReader reader, IBufferPool bufferPool, CancellationToken cancellationToken = default) internal static async ValueTask<string?> ReadLineInternalAsync(ILineStream reader, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{ {
byte lastChar = default; byte lastChar = default;
......
using System.Threading;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.StreamExtended.Network
{
public interface ILineStream
{
bool DataAvailable { get; }
/// <summary>
/// Fills the buffer asynchronous.
/// </summary>
/// <returns></returns>
ValueTask<bool> FillBufferAsync(CancellationToken cancellationToken = default);
byte ReadByteFromBuffer();
/// <summary>
/// Read a line from the byte stream
/// </summary>
/// <returns></returns>
ValueTask<string?> ReadLineAsync(CancellationToken cancellationToken = default);
}
}
...@@ -4,21 +4,8 @@ using System.Threading.Tasks; ...@@ -4,21 +4,8 @@ using System.Threading.Tasks;
namespace Titanium.Web.Proxy.StreamExtended.Network namespace Titanium.Web.Proxy.StreamExtended.Network
{ {
/// <summary> public interface IPeekStream
/// This concrete implemetation of interface acts as the source stream for CopyStream class.
/// </summary>
public interface ICustomStreamReader
{ {
int Available { get; }
bool DataAvailable { get; }
/// <summary>
/// Fills the buffer asynchronous.
/// </summary>
/// <returns></returns>
ValueTask<bool> FillBufferAsync(CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Peeks a byte from buffer. /// Peeks a byte from buffer.
/// </summary> /// </summary>
...@@ -45,42 +32,5 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -45,42 +32,5 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns> /// <returns></returns>
ValueTask<int> PeekBytesAsync(byte[] buffer, int offset, int index, int count, CancellationToken cancellationToken = default); ValueTask<int> PeekBytesAsync(byte[] buffer, int offset, int index, int count, CancellationToken cancellationToken = default);
byte ReadByteFromBuffer();
/// <summary>
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
/// </summary>
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between <paramref name="offset" /> and (<paramref name="offset" /> + <paramref name="count" /> - 1) replaced by the bytes read from the current source.</param>
/// <param name="offset">The zero-based byte offset in <paramref name="buffer" /> at which to begin storing the data read from the current stream.</param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
/// </returns>
int Read(byte[] buffer, int offset, int count);
/// <summary>
/// Read the specified number (or less) of raw bytes from the base stream to the given buffer to the specified offset
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="bytesToRead"></param>
/// <param name="cancellationToken"></param>
/// <returns>The number of bytes read</returns>
Task<int> ReadAsync(byte[] buffer, int offset, int bytesToRead, CancellationToken cancellationToken = default);
/// <summary>
/// Read the specified number (or less) of raw bytes from the base stream to the given buffer to the specified offset
/// </summary>
/// <param name="buffer"></param>
/// <param name="cancellationToken"></param>
/// <returns>The number of bytes read</returns>
ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default);
/// <summary>
/// Read a line from the byte stream
/// </summary>
/// <returns></returns>
ValueTask<string?> ReadLineAsync(CancellationToken cancellationToken = default);
} }
} }
\ No newline at end of file
using System;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.StreamExtended.BufferPool;
namespace Titanium.Web.Proxy.StreamExtended.Network
{
internal class PeekStreamReader
{
private readonly IPeekStream baseStream;
public int Position { get; private set; }
public PeekStreamReader(IPeekStream baseStream, int startPosition = 0)
{
this.baseStream = baseStream;
Position = startPosition;
}
public async ValueTask<bool> EnsureBufferLength(int length, CancellationToken cancellationToken)
{
var val = await baseStream.PeekByteAsync(Position + length - 1, cancellationToken);
return val != -1;
}
public byte ReadByte()
{
return baseStream.PeekByteFromBuffer(Position++);
}
public int ReadInt16()
{
int i1 = ReadByte();
int i2 = ReadByte();
return (i1 << 8) + i2;
}
public int ReadInt24()
{
int i1 = ReadByte();
int i2 = ReadByte();
int i3 = ReadByte();
return (i1 << 16) + (i2 << 8) + i3;
}
public byte[] ReadBytes(int length)
{
var buffer = new byte[length];
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = ReadByte();
}
return buffer;
}
}
}
...@@ -16,13 +16,25 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -16,13 +16,25 @@ namespace Titanium.Web.Proxy.StreamExtended
"DEFLATE" "DEFLATE"
}; };
public int HandshakeVersion { get; set; } public ServerHelloInfo(int handshakeVersion, int majorVersion, int minorVersion, byte[] random,
byte[] sessionId, int cipherSuite, int serverHelloLength)
{
HandshakeVersion = handshakeVersion;
MajorVersion = majorVersion;
MinorVersion = minorVersion;
Random = random;
SessionId = sessionId;
CipherSuite = cipherSuite;
ServerHelloLength = serverHelloLength;
}
public int HandshakeVersion { get; }
public int MajorVersion { get; set; } public int MajorVersion { get; }
public int MinorVersion { get; set; } public int MinorVersion { get; }
public byte[] Random { get; set; } public byte[] Random { get; }
public DateTime Time public DateTime Time
{ {
...@@ -39,13 +51,13 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -39,13 +51,13 @@ namespace Titanium.Web.Proxy.StreamExtended
} }
} }
public byte[] SessionId { get; set; } public byte[] SessionId { get; }
public int CipherSuite { get; set; } public int CipherSuite { get; }
public byte CompressionMethod { get; set; } public byte CompressionMethod { get; set; }
internal int ServerHelloLength { get; set; } internal int ServerHelloLength { get; }
internal int EntensionsStartPosition { get; set; } internal int EntensionsStartPosition { get; set; }
......
...@@ -12,19 +12,6 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -12,19 +12,6 @@ namespace Titanium.Web.Proxy.StreamExtended
/// </summary> /// </summary>
internal class SslTools internal class SslTools
{ {
/// <summary>
/// Is the given stream starts with an SSL client hello?
/// </summary>
/// <param name="stream"></param>
/// <param name="bufferPool"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<bool> IsClientHello(CustomBufferedStream stream, IBufferPool bufferPool, CancellationToken cancellationToken)
{
var clientHello = await PeekClientHello(stream, bufferPool, cancellationToken);
return clientHello != null;
}
/// <summary> /// <summary>
/// Peek the SSL client hello information. /// Peek the SSL client hello information.
/// </summary> /// </summary>
...@@ -46,7 +33,7 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -46,7 +33,7 @@ namespace Titanium.Web.Proxy.StreamExtended
if ((recordType & 0x80) == 0x80) if ((recordType & 0x80) == 0x80)
{ {
// SSL 2 // SSL 2
var peekStream = new CustomBufferedPeekStream(clientStream, bufferPool, 1); var peekStream = new PeekStreamReader(clientStream, 1);
// length value + minimum length // length value + minimum length
if (!await peekStream.EnsureBufferLength(10, cancellationToken)) if (!await peekStream.EnsureBufferLength(10, cancellationToken))
...@@ -88,21 +75,14 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -88,21 +75,14 @@ namespace Titanium.Web.Proxy.StreamExtended
byte[] sessionId = peekStream.ReadBytes(sessionIdLength); byte[] sessionId = peekStream.ReadBytes(sessionIdLength);
byte[] random = peekStream.ReadBytes(randomLength); byte[] random = peekStream.ReadBytes(randomLength);
var clientHelloInfo = new ClientHelloInfo(sessionId) var clientHelloInfo = new ClientHelloInfo(2, majorVersion, minorVersion, random, sessionId, ciphers,
{ peekStream.Position);
HandshakeVersion = 2,
MajorVersion = majorVersion,
MinorVersion = minorVersion,
Random = random,
Ciphers = ciphers,
ClientHelloLength = peekStream.Position,
};
return clientHelloInfo; return clientHelloInfo;
} }
else if (recordType == 0x16) else if (recordType == 0x16)
{ {
var peekStream = new CustomBufferedPeekStream(clientStream, bufferPool, 1); var peekStream = new PeekStreamReader(clientStream, 1);
// should contain at least 43 bytes // should contain at least 43 bytes
// 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length // 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length
...@@ -168,25 +148,19 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -168,25 +148,19 @@ namespace Titanium.Web.Proxy.StreamExtended
byte[] compressionData = peekStream.ReadBytes(length); byte[] compressionData = peekStream.ReadBytes(length);
int extenstionsStartPosition = peekStream.Position; int extensionsStartPosition = peekStream.Position;
Dictionary<string, SslExtension>? extensions = null; Dictionary<string, SslExtension>? extensions = null;
if(extenstionsStartPosition < recordLength + 5) if(extensionsStartPosition < recordLength + 5)
{ {
extensions = await ReadExtensions(majorVersion, minorVersion, peekStream, bufferPool, cancellationToken); extensions = await ReadExtensions(majorVersion, minorVersion, peekStream, bufferPool, cancellationToken);
} }
var clientHelloInfo = new ClientHelloInfo(sessionId) var clientHelloInfo = new ClientHelloInfo(3, majorVersion, minorVersion, random, sessionId, ciphers, peekStream.Position)
{ {
HandshakeVersion = 3, ExtensionsStartPosition = extensionsStartPosition,
MajorVersion = majorVersion,
MinorVersion = minorVersion,
Random = random,
Ciphers = ciphers,
CompressionData = compressionData, CompressionData = compressionData,
ClientHelloLength = peekStream.Position,
EntensionsStartPosition = extenstionsStartPosition,
Extensions = extensions, Extensions = extensions,
}; };
...@@ -231,7 +205,7 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -231,7 +205,7 @@ namespace Titanium.Web.Proxy.StreamExtended
{ {
// SSL 2 // SSL 2
// not tested. SSL2 is deprecated // not tested. SSL2 is deprecated
var peekStream = new CustomBufferedPeekStream(serverStream, bufferPool, 1); var peekStream = new PeekStreamReader(serverStream, 1);
// length value + minimum length // length value + minimum length
if (!await peekStream.EnsureBufferLength(39, cancellationToken)) if (!await peekStream.EnsureBufferLength(39, cancellationToken))
...@@ -265,22 +239,14 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -265,22 +239,14 @@ namespace Titanium.Web.Proxy.StreamExtended
byte[] sessionId = peekStream.ReadBytes(1); byte[] sessionId = peekStream.ReadBytes(1);
int cipherSuite = peekStream.ReadInt16(); int cipherSuite = peekStream.ReadInt16();
var serverHelloInfo = new ServerHelloInfo var serverHelloInfo = new ServerHelloInfo(2, majorVersion, minorVersion, random, sessionId, cipherSuite,
{ peekStream.Position);
HandshakeVersion = 2,
MajorVersion = majorVersion,
MinorVersion = minorVersion,
Random = random,
SessionId = sessionId,
CipherSuite = cipherSuite,
ServerHelloLength = peekStream.Position,
};
return serverHelloInfo; return serverHelloInfo;
} }
else if (recordType == 0x16) else if (recordType == 0x16)
{ {
var peekStream = new CustomBufferedPeekStream(serverStream, bufferPool, 1); var peekStream = new PeekStreamReader(serverStream, 1);
// should contain at least 43 bytes // should contain at least 43 bytes
// 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length // 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length
...@@ -329,16 +295,9 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -329,16 +295,9 @@ namespace Titanium.Web.Proxy.StreamExtended
extensions = await ReadExtensions(majorVersion, minorVersion, peekStream, bufferPool, cancellationToken); extensions = await ReadExtensions(majorVersion, minorVersion, peekStream, bufferPool, cancellationToken);
} }
var serverHelloInfo = new ServerHelloInfo var serverHelloInfo = new ServerHelloInfo(3, majorVersion, minorVersion, random, sessionId, cipherSuite, peekStream.Position)
{ {
HandshakeVersion = 3,
MajorVersion = majorVersion,
MinorVersion = minorVersion,
Random = random,
SessionId = sessionId,
CipherSuite = cipherSuite,
CompressionMethod = compressionMethod, CompressionMethod = compressionMethod,
ServerHelloLength = peekStream.Position,
EntensionsStartPosition = extenstionsStartPosition, EntensionsStartPosition = extenstionsStartPosition,
Extensions = extensions, Extensions = extensions,
}; };
...@@ -349,24 +308,24 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -349,24 +308,24 @@ namespace Titanium.Web.Proxy.StreamExtended
return null; return null;
} }
private static async Task<Dictionary<string, SslExtension>?> ReadExtensions(int majorVersion, int minorVersion, CustomBufferedPeekStream peekStream, IBufferPool bufferPool, CancellationToken cancellationToken) private static async Task<Dictionary<string, SslExtension>?> ReadExtensions(int majorVersion, int minorVersion, PeekStreamReader peekStreamReader, IBufferPool bufferPool, CancellationToken cancellationToken)
{ {
Dictionary<string, SslExtension>? extensions = null; Dictionary<string, SslExtension>? extensions = null;
if (majorVersion > 3 || majorVersion == 3 && minorVersion >= 1) if (majorVersion > 3 || majorVersion == 3 && minorVersion >= 1)
{ {
if (await peekStream.EnsureBufferLength(2, cancellationToken)) if (await peekStreamReader.EnsureBufferLength(2, cancellationToken))
{ {
int extensionsLength = peekStream.ReadInt16(); int extensionsLength = peekStreamReader.ReadInt16();
if (await peekStream.EnsureBufferLength(extensionsLength, cancellationToken)) if (await peekStreamReader.EnsureBufferLength(extensionsLength, cancellationToken))
{ {
extensions = new Dictionary<string, SslExtension>(); extensions = new Dictionary<string, SslExtension>();
int idx = 0; int idx = 0;
while (extensionsLength > 3) while (extensionsLength > 3)
{ {
int id = peekStream.ReadInt16(); int id = peekStreamReader.ReadInt16();
int length = peekStream.ReadInt16(); int length = peekStreamReader.ReadInt16();
byte[] data = peekStream.ReadBytes(length); byte[] data = peekStreamReader.ReadBytes(length);
var extension = SslExtensions.GetExtension(id, data, idx++); var extension = SslExtensions.GetExtension(id, data, idx++);
extensions[extension.Name] = extension; extensions[extension.Name] = extension;
extensionsLength -= 4 + length; extensionsLength -= 4 + length;
......
...@@ -12,6 +12,7 @@ using Titanium.Web.Proxy.Exceptions; ...@@ -12,6 +12,7 @@ using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.StreamExtended; using Titanium.Web.Proxy.StreamExtended;
using Titanium.Web.Proxy.StreamExtended.Network; using Titanium.Web.Proxy.StreamExtended.Network;
...@@ -81,11 +82,8 @@ namespace Titanium.Web.Proxy ...@@ -81,11 +82,8 @@ namespace Titanium.Web.Proxy
catch (Exception e) catch (Exception e)
{ {
var certname = certificate?.GetNameInfo(X509NameType.SimpleName, false); var certname = certificate?.GetNameInfo(X509NameType.SimpleName, false);
var session = new SessionEventArgs(this, endPoint, cancellationTokenSource) var session = new SessionEventArgs(this, endPoint, new ProxyClient(clientConnection, clientStream, clientStreamWriter), null,
{ cancellationTokenSource);
ProxyClient = { Connection = clientConnection },
HttpClient = { ConnectRequest = null }
};
throw new ProxyConnectException( throw new ProxyConnectException(
$"Couldn't authenticate host '{httpsHostName}' with certificate '{certname}'.", e, session); $"Couldn't authenticate host '{httpsHostName}' with certificate '{certname}'.", e, session);
} }
......
...@@ -132,7 +132,7 @@ namespace Titanium.Web.Proxy ...@@ -132,7 +132,7 @@ namespace Titanium.Web.Proxy
authHeader.Value.Length > x.Length + 1); authHeader.Value.Length > x.Length + 1);
string serverToken = authHeader.Value.Substring(scheme.Length + 1); string serverToken = authHeader.Value.Substring(scheme.Length + 1);
string clientToken = WinAuthHandler.GetFinalAuthToken(request.Host, serverToken, args.HttpClient.Data); string clientToken = WinAuthHandler.GetFinalAuthToken(request.Host!, serverToken, args.HttpClient.Data);
string auth = string.Concat(scheme, clientToken); string auth = string.Concat(scheme, clientToken);
......
...@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
var request = new Request var request = new Request
{ {
Method = "POST", Method = "POST",
RequestUriString = "/", Url = "/",
HttpVersion = new Version(1, 1) HttpVersion = new Version(1, 1)
}; };
request.Headers.AddHeader(KnownHeaders.Host, server); request.Headers.AddHeader(KnownHeaders.Host, server);
......
...@@ -26,14 +26,14 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -26,14 +26,14 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
try try
{ {
Request.ParseRequestLine(line, out var method, out var url, out var version); Request.ParseRequestLine(line, out var method, out var url, out var version);
RequestResponseBase request = new Request var request = new Request
{ {
Method = method, RequestUriString = url, HttpVersion = version Method = method, Url = url, HttpVersion = version
}; };
while (!string.IsNullOrEmpty(line = reader.ReadLine())) while (!string.IsNullOrEmpty(line = reader.ReadLine()))
{ {
var header = line.Split(colonSplit, 2); var header = line.Split(colonSplit, 2);
request.Headers.AddHeader(header[0], header[1]); request.Headers.AddHeader(header[0].Trim(), header[1].Trim());
} }
// First zero-length line denotes end of headers. If we // First zero-length line denotes end of headers. If we
...@@ -42,10 +42,10 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -42,10 +42,10 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
return null; return null;
if (!requireBody) if (!requireBody)
return request as Request; return request;
if (parseBody(reader, ref request)) if (parseBody(reader, request))
return request as Request; return request;
} }
catch catch
{ {
...@@ -71,7 +71,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -71,7 +71,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
try try
{ {
Response.ParseResponseLine(line, out var version, out var status, out var desc); Response.ParseResponseLine(line, out var version, out var status, out var desc);
RequestResponseBase response = new Response var response = new Response
{ {
HttpVersion = version, StatusCode = status, StatusDescription = desc HttpVersion = version, StatusCode = status, StatusDescription = desc
}; };
...@@ -87,8 +87,8 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -87,8 +87,8 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
if (line?.Length != 0) if (line?.Length != 0)
return null; return null;
if (parseBody(reader, ref response)) if (parseBody(reader, response))
return response as Response; return response;
} }
catch catch
{ {
...@@ -98,7 +98,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -98,7 +98,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
return null; return null;
} }
private static bool parseBody(StringReader reader, ref RequestResponseBase obj) private static bool parseBody(StringReader reader, RequestResponseBase obj)
{ {
obj.OriginalContentLength = obj.ContentLength; obj.OriginalContentLength = obj.ContentLength;
if (obj.ContentLength <= 0) if (obj.ContentLength <= 0)
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" 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.Extensions.DependencyInjection.Abstractions" Version="3.0.0" />
......
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