Unverified Commit ed0800f4 authored by honfika's avatar honfika Committed by GitHub

Merge pull request #645 from justcoding121/master

beta
parents ef2019e3 d8dce115
...@@ -53,11 +53,11 @@ namespace Titanium.Web.Proxy ...@@ -53,11 +53,11 @@ namespace Titanium.Web.Proxy
/// <param name="remoteCertificate">The remote certificate of server.</param> /// <param name="remoteCertificate">The remote certificate of server.</param>
/// <param name="acceptableIssuers">The acceptable issues for client certificate as listed by server.</param> /// <param name="acceptableIssuers">The acceptable issues for client certificate as listed by server.</param>
/// <returns></returns> /// <returns></returns>
internal X509Certificate SelectClientCertificate(object sender, string targetHost, internal X509Certificate? SelectClientCertificate(object sender, string targetHost,
X509CertificateCollection localCertificates, X509CertificateCollection localCertificates,
X509Certificate remoteCertificate, string[] acceptableIssuers) X509Certificate remoteCertificate, string[] acceptableIssuers)
{ {
X509Certificate clientCertificate = null; X509Certificate? clientCertificate = null;
if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null && if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null &&
localCertificates.Count > 0) localCertificates.Count > 0)
......
using System; using System;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression namespace Titanium.Web.Proxy.Compression
......
using System; using System;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression namespace Titanium.Web.Proxy.Compression
......
...@@ -10,16 +10,17 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -10,16 +10,17 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
internal readonly CancellationTokenSource TaskCancellationSource; internal readonly CancellationTokenSource TaskCancellationSource;
internal BeforeSslAuthenticateEventArgs(CancellationTokenSource taskCancellationSource) internal BeforeSslAuthenticateEventArgs(CancellationTokenSource taskCancellationSource, string sniHostName)
{ {
TaskCancellationSource = taskCancellationSource; TaskCancellationSource = taskCancellationSource;
SniHostName = sniHostName;
} }
/// <summary> /// <summary>
/// The server name indication hostname if available. Otherwise the generic certificate hostname of /// The server name indication hostname if available. Otherwise the generic certificate hostname of
/// TransparentEndPoint. /// TransparentEndPoint.
/// </summary> /// </summary>
public string SniHostName { get; internal set; } public string SniHostName { get; }
/// <summary> /// <summary>
/// Should we decrypt the SSL request? /// Should we decrypt the SSL request?
......
...@@ -11,31 +11,31 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -11,31 +11,31 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// The proxy server instance. /// The proxy server instance.
/// </summary> /// </summary>
public object Sender { get; internal set; } public object? Sender { get; internal set; }
/// <summary> /// <summary>
/// The remote hostname to which we are authenticating against. /// The remote hostname to which we are authenticating against.
/// </summary> /// </summary>
public string TargetHost { get; internal set; } public string? TargetHost { get; internal set; }
/// <summary> /// <summary>
/// Local certificates in store with matching issuers requested by TargetHost website. /// Local certificates in store with matching issuers requested by TargetHost website.
/// </summary> /// </summary>
public X509CertificateCollection LocalCertificates { get; internal set; } public X509CertificateCollection? LocalCertificates { get; internal set; }
/// <summary> /// <summary>
/// Certificate of the remote server. /// Certificate of the remote server.
/// </summary> /// </summary>
public X509Certificate RemoteCertificate { get; internal set; } public X509Certificate? RemoteCertificate { get; internal set; }
/// <summary> /// <summary>
/// Acceptable issuers as listed by remote server. /// Acceptable issuers as listed by remote server.
/// </summary> /// </summary>
public string[] AcceptableIssuers { get; internal set; } public string[]? AcceptableIssuers { get; internal set; }
/// <summary> /// <summary>
/// Client Certificate we selected. Set this value to override. /// Client Certificate we selected. Set this value to override.
/// </summary> /// </summary>
public X509Certificate ClientCertificate { get; set; } public X509Certificate? ClientCertificate { get; set; }
} }
} }
...@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy.EventArguments
} }
protected SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, protected SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint,
Request request, CancellationTokenSource cancellationTokenSource) Request? request, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, cancellationTokenSource, request) : base(server, endPoint, cancellationTokenSource, request)
{ {
} }
...@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Occurs when multipart request part sent. /// Occurs when multipart request part sent.
/// </summary> /// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent; public event EventHandler<MultipartRequestPartSentEventArgs>? MultipartRequestPartSent;
private ICustomStreamReader getStreamReader(bool isRequest) private ICustomStreamReader getStreamReader(bool isRequest)
{ {
...@@ -105,7 +105,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -105,7 +105,7 @@ namespace Titanium.Web.Proxy.EventArguments
request.ReadHttp2BodyTaskCompletionSource = tcs; request.ReadHttp2BodyTaskCompletionSource = tcs;
// signal to HTTP/2 copy frame method to continue // signal to HTTP/2 copy frame method to continue
request.ReadHttp2BeforeHandlerTaskCompletionSource.SetResult(true); request.ReadHttp2BeforeHandlerTaskCompletionSource!.SetResult(true);
await tcs.Task; await tcs.Task;
...@@ -135,11 +135,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -135,11 +135,11 @@ namespace Titanium.Web.Proxy.EventArguments
HttpClient.Response = new Response(); HttpClient.Response = new Response();
} }
internal void OnMultipartRequestPartSent(string boundary, HeaderCollection headers) internal void OnMultipartRequestPartSent(ReadOnlySpan<char> boundary, HeaderCollection headers)
{ {
try try
{ {
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary, headers)); MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary.ToString(), headers));
} }
catch (Exception ex) catch (Exception ex)
{ {
...@@ -177,7 +177,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -177,7 +177,7 @@ namespace Titanium.Web.Proxy.EventArguments
response.ReadHttp2BodyTaskCompletionSource = tcs; response.ReadHttp2BodyTaskCompletionSource = tcs;
// signal to HTTP/2 copy frame method to continue // signal to HTTP/2 copy frame method to continue
response.ReadHttp2BeforeHandlerTaskCompletionSource.SetResult(true); response.ReadHttp2BeforeHandlerTaskCompletionSource!.SetResult(true);
await tcs.Task; await tcs.Task;
...@@ -252,7 +252,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -252,7 +252,7 @@ namespace Titanium.Web.Proxy.EventArguments
if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData) if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData)
{ {
var reader = getStreamReader(true); var reader = getStreamReader(true);
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType); var boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
using (var copyStream = new CopyStream(reader, writer, BufferPool)) using (var copyStream = new CopyStream(reader, writer, BufferPool))
{ {
...@@ -268,7 +268,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -268,7 +268,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
var headers = new HeaderCollection(); var headers = new HeaderCollection();
await HeaderParser.ReadHeaders(copyStream, headers, cancellationToken); await HeaderParser.ReadHeaders(copyStream, headers, cancellationToken);
OnMultipartRequestPartSent(boundary, headers); OnMultipartRequestPartSent(boundary.Span, headers);
} }
} }
...@@ -286,7 +286,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -286,7 +286,7 @@ namespace Titanium.Web.Proxy.EventArguments
await copyBodyAsync(false, false, writer, transformation, OnDataReceived, cancellationToken); await copyBodyAsync(false, false, writer, transformation, OnDataReceived, cancellationToken);
} }
private async Task copyBodyAsync(bool isRequest, bool useOriginalHeaderValues, HttpWriter writer, TransformationMode transformation, Action<byte[], int, int> onCopy, CancellationToken cancellationToken) private async Task copyBodyAsync(bool isRequest, bool useOriginalHeaderValues, HttpWriter writer, TransformationMode transformation, Action<byte[], int, int>? onCopy, CancellationToken cancellationToken)
{ {
var stream = getStreamReader(isRequest); var stream = getStreamReader(isRequest);
...@@ -302,9 +302,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -302,9 +302,9 @@ namespace Titanium.Web.Proxy.EventArguments
} }
LimitedStream limitedStream; LimitedStream limitedStream;
Stream decompressStream = null; Stream? decompressStream = null;
string contentEncoding = useOriginalHeaderValues ? requestResponse.OriginalContentEncoding : requestResponse.ContentEncoding; string? contentEncoding = useOriginalHeaderValues ? requestResponse.OriginalContentEncoding : requestResponse.ContentEncoding;
Stream s = limitedStream = new LimitedStream(stream, BufferPool, isChunked, contentLength); Stream s = limitedStream = new LimitedStream(stream, BufferPool, isChunked, contentLength);
...@@ -333,7 +333,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -333,7 +333,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, string boundary, CancellationToken cancellationToken) private async Task<long> readUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, ReadOnlyMemory<char> boundary, CancellationToken cancellationToken)
{ {
int bufferDataLength = 0; int bufferDataLength = 0;
...@@ -360,7 +360,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -360,7 +360,7 @@ namespace Titanium.Web.Proxy.EventArguments
bool ok = true; bool ok = true;
for (int i = 0; i < boundary.Length; i++) for (int i = 0; i < boundary.Length; i++)
{ {
if (buffer[startIdx + i] != boundary[i]) if (buffer[startIdx + i] != boundary.Span[i])
{ {
ok = false; ok = false;
break; break;
...@@ -519,7 +519,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -519,7 +519,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <param name="html">HTML content to sent.</param> /// <param name="html">HTML content to sent.</param>
/// <param name="headers">HTTP response headers.</param> /// <param name="headers">HTTP response headers.</param>
/// <param name="closeServerConnection">Close the server connection used by request if any?</param> /// <param name="closeServerConnection">Close the server connection used by request if any?</param>
public void Ok(string html, Dictionary<string, HttpHeader> headers = null, public void Ok(string html, Dictionary<string, HttpHeader>? headers = null,
bool closeServerConnection = false) bool closeServerConnection = false)
{ {
var response = new OkResponse(); var response = new OkResponse();
...@@ -541,7 +541,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -541,7 +541,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <param name="result">The html content bytes.</param> /// <param name="result">The html content bytes.</param>
/// <param name="headers">The HTTP headers.</param> /// <param name="headers">The HTTP headers.</param>
/// <param name="closeServerConnection">Close the server connection used by request if any?</param> /// <param name="closeServerConnection">Close the server connection used by request if any?</param>
public void Ok(byte[] result, Dictionary<string, HttpHeader> headers = null, public void Ok(byte[] result, Dictionary<string, HttpHeader>? headers = null,
bool closeServerConnection = false) bool closeServerConnection = false)
{ {
var response = new OkResponse(); var response = new OkResponse();
...@@ -562,7 +562,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -562,7 +562,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <param name="headers">The HTTP headers.</param> /// <param name="headers">The HTTP headers.</param>
/// <param name="closeServerConnection">Close the server connection used by request if any?</param> /// <param name="closeServerConnection">Close the server connection used by request if any?</param>
public void GenericResponse(string html, HttpStatusCode status, public void GenericResponse(string html, HttpStatusCode status,
Dictionary<string, HttpHeader> headers = null, bool closeServerConnection = false) Dictionary<string, HttpHeader>? headers = null, bool closeServerConnection = false)
{ {
var response = new GenericResponse(status); var response = new GenericResponse(status);
response.HttpVersion = HttpClient.Request.HttpVersion; response.HttpVersion = HttpClient.Request.HttpVersion;
......
...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.EventArguments
protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint, protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
Request request) : this(server) Request? request) : this(server)
{ {
CancellationTokenSource = cancellationTokenSource; CancellationTokenSource = cancellationTokenSource;
...@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Returns a user data for this request/response session which is /// Returns a user data for this request/response session which is
/// same as the user data of HttpClient. /// same as the user data of HttpClient.
/// </summary> /// </summary>
public object UserData public object? UserData
{ {
get => HttpClient.UserData; get => HttpClient.UserData;
set => HttpClient.UserData = value; set => HttpClient.UserData = value;
...@@ -112,7 +112,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -112,7 +112,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Are we using a custom upstream HTTP(S) proxy? /// Are we using a custom upstream HTTP(S) proxy?
/// </summary> /// </summary>
public ExternalProxy CustomUpStreamProxyUsed { get; internal set; } public ExternalProxy? CustomUpStreamProxyUsed { get; internal set; }
/// <summary> /// <summary>
/// Local endpoint via which we make the request. /// Local endpoint via which we make the request.
...@@ -127,7 +127,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -127,7 +127,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// The last exception that happened. /// The last exception that happened.
/// </summary> /// </summary>
public Exception Exception { get; internal set; } public Exception? Exception { get; internal set; }
/// <summary> /// <summary>
/// Implements cleanup here. /// Implements cleanup here.
...@@ -146,12 +146,12 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -146,12 +146,12 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Fired when data is sent within this session to server/client. /// Fired when data is sent within this session to server/client.
/// </summary> /// </summary>
public event EventHandler<DataEventArgs> DataSent; public event EventHandler<DataEventArgs>? DataSent;
/// <summary> /// <summary>
/// Fired when data is received within this session from client/server. /// Fired when data is received within this session from client/server.
/// </summary> /// </summary>
public event EventHandler<DataEventArgs> DataReceived; public event EventHandler<DataEventArgs>? DataReceived;
internal void OnDataSent(byte[] buffer, int offset, int count) internal void OnDataSent(byte[] buffer, int offset, int count)
{ {
......
...@@ -45,12 +45,12 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -45,12 +45,12 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Fired when decrypted data is sent within this session to server/client. /// Fired when decrypted data is sent within this session to server/client.
/// </summary> /// </summary>
public event EventHandler<DataEventArgs> DecryptedDataSent; public event EventHandler<DataEventArgs>? DecryptedDataSent;
/// <summary> /// <summary>
/// Fired when decrypted data is received within this session from client/server. /// Fired when decrypted data is received within this session from client/server.
/// </summary> /// </summary>
public event EventHandler<DataEventArgs> DecryptedDataReceived; public event EventHandler<DataEventArgs>? DecryptedDataReceived;
internal void OnDecryptedDataSent(byte[] buffer, int offset, int count) internal void OnDecryptedDataSent(byte[] buffer, int offset, int count)
{ {
......
using System; using System;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
......
...@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.Exceptions ...@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.Exceptions
/// </summary> /// </summary>
/// <param name="message">Exception message</param> /// <param name="message">Exception message</param>
/// <param name="innerException">Inner exception associated</param> /// <param name="innerException">Inner exception associated</param>
protected ProxyException(string message, Exception innerException) : base(message, innerException) protected ProxyException(string message, Exception? innerException) : base(message, innerException)
{ {
} }
} }
......
...@@ -14,7 +14,7 @@ namespace Titanium.Web.Proxy.Exceptions ...@@ -14,7 +14,7 @@ namespace Titanium.Web.Proxy.Exceptions
/// <param name="message">Message for this exception</param> /// <param name="message">Message for this exception</param>
/// <param name="innerException">Associated inner exception</param> /// <param name="innerException">Associated inner exception</param>
/// <param name="session">Instance of <see cref="EventArguments.SessionEventArgs" /> associated to the exception</param> /// <param name="session">Instance of <see cref="EventArguments.SessionEventArgs" /> associated to the exception</param>
internal ProxyHttpException(string message, Exception innerException, SessionEventArgs session) : base( internal ProxyHttpException(string message, Exception? innerException, SessionEventArgs? session) : base(
message, innerException) message, innerException)
{ {
Session = session; Session = session;
...@@ -26,6 +26,6 @@ namespace Titanium.Web.Proxy.Exceptions ...@@ -26,6 +26,6 @@ namespace Titanium.Web.Proxy.Exceptions
/// <remarks> /// <remarks>
/// This object properties should not be edited. /// This object properties should not be edited.
/// </remarks> /// </remarks>
public SessionEventArgs Session { get; } public SessionEventArgs? Session { get; }
} }
} }
...@@ -38,16 +38,16 @@ namespace Titanium.Web.Proxy ...@@ -38,16 +38,16 @@ namespace Titanium.Web.Proxy
var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool); var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool); var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool);
Task<TcpServerConnection> prefetchConnectionTask = null; Task<TcpServerConnection>? prefetchConnectionTask = null;
bool closeServerConnection = false; bool closeServerConnection = false;
bool calledRequestHandler = false; bool calledRequestHandler = false;
SslStream sslStream = null; SslStream? sslStream = null;
try try
{ {
string connectHostname = null; string? connectHostname = null;
TunnelConnectSessionEventArgs connectArgs = null; TunnelConnectSessionEventArgs? connectArgs = null;
// Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request) // Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
if (await HttpHelper.IsConnectMethod(clientStream, BufferPool, cancellationToken) == 1) if (await HttpHelper.IsConnectMethod(clientStream, BufferPool, cancellationToken) == 1)
...@@ -126,7 +126,7 @@ namespace Titanium.Web.Proxy ...@@ -126,7 +126,7 @@ namespace Titanium.Web.Proxy
var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken); var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);
bool isClientHello = clientHelloInfo != null; bool isClientHello = clientHelloInfo != null;
if (isClientHello) if (clientHelloInfo != null)
{ {
connectRequest.TunnelType = TunnelType.Https; connectRequest.TunnelType = TunnelType.Https;
connectRequest.ClientHelloInfo = clientHelloInfo; connectRequest.ClientHelloInfo = clientHelloInfo;
...@@ -134,7 +134,7 @@ namespace Titanium.Web.Proxy ...@@ -134,7 +134,7 @@ namespace Titanium.Web.Proxy
await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc, isClientHello); await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);
if (decryptSsl && isClientHello) if (decryptSsl && clientHelloInfo != null)
{ {
clientConnection.SslProtocol = clientHelloInfo.SslProtocol; clientConnection.SslProtocol = clientHelloInfo.SslProtocol;
connectRequest.RequestUri = new Uri("https://" + httpUrl); connectRequest.RequestUri = new Uri("https://" + httpUrl);
...@@ -166,7 +166,7 @@ namespace Titanium.Web.Proxy ...@@ -166,7 +166,7 @@ namespace Titanium.Web.Proxy
if (EnableTcpServerConnectionPrefetch) if (EnableTcpServerConnectionPrefetch)
{ {
IPAddress[] ipAddresses = null; IPAddress[]? ipAddresses = null;
try try
{ {
// make sure the host can be resolved before creating the prefetch task // make sure the host can be resolved before creating the prefetch task
...@@ -184,7 +184,7 @@ namespace Titanium.Web.Proxy ...@@ -184,7 +184,7 @@ namespace Titanium.Web.Proxy
} }
} }
X509Certificate2 certificate = null; X509Certificate2? certificate = null;
try try
{ {
sslStream = new SslStream(clientStream, false); sslStream = new SslStream(clientStream, false);
...@@ -306,10 +306,10 @@ namespace Titanium.Web.Proxy ...@@ -306,10 +306,10 @@ namespace Titanium.Web.Proxy
string httpCmd = await clientStream.ReadLineAsync(cancellationToken); string httpCmd = await clientStream.ReadLineAsync(cancellationToken);
if (httpCmd == "PRI * HTTP/2.0") if (httpCmd == "PRI * HTTP/2.0")
{ {
connectArgs.HttpClient.ConnectRequest.TunnelType = TunnelType.Http2; connectArgs.HttpClient.ConnectRequest!.TunnelType = TunnelType.Http2;
// HTTP/2 Connection Preface // HTTP/2 Connection Preface
string line = await clientStream.ReadLineAsync(cancellationToken); string? line = await clientStream.ReadLineAsync(cancellationToken);
if (line != string.Empty) if (line != string.Empty)
{ {
throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received"); throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
...@@ -332,11 +332,9 @@ namespace Titanium.Web.Proxy ...@@ -332,11 +332,9 @@ namespace Titanium.Web.Proxy
noCache: true, cancellationToken: cancellationToken); noCache: true, cancellationToken: cancellationToken);
try try
{ {
await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken);
await connection.StreamWriter.WriteLineAsync(cancellationToken);
await connection.StreamWriter.WriteLineAsync("SM", cancellationToken);
await connection.StreamWriter.WriteLineAsync(cancellationToken);
#if NETSTANDARD2_1 #if NETSTANDARD2_1
var connectionPreface = new ReadOnlyMemory<byte>(Http2Helper.ConnectionPreface);
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, cancellationTokenSource)
{ {
......
...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.Extensions
internal static readonly List<SslApplicationProtocol> Http2ProtocolAsList = internal static readonly List<SslApplicationProtocol> Http2ProtocolAsList =
new List<SslApplicationProtocol> { SslApplicationProtocol.Http2 }; new List<SslApplicationProtocol> { SslApplicationProtocol.Http2 };
internal static string GetServerName(this ClientHelloInfo clientHelloInfo) internal static string? GetServerName(this ClientHelloInfo clientHelloInfo)
{ {
if (clientHelloInfo.Extensions != null && if (clientHelloInfo.Extensions != null &&
clientHelloInfo.Extensions.TryGetValue("server_name", out var serverNameExtension)) clientHelloInfo.Extensions.TryGetValue("server_name", out var serverNameExtension))
...@@ -29,7 +29,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -29,7 +29,7 @@ namespace Titanium.Web.Proxy.Extensions
} }
#if NETSTANDARD2_1 #if NETSTANDARD2_1
internal static List<SslApplicationProtocol> GetAlpn(this ClientHelloInfo clientHelloInfo) internal static List<SslApplicationProtocol>? GetAlpn(this ClientHelloInfo clientHelloInfo)
{ {
if (clientHelloInfo.Extensions != null && clientHelloInfo.Extensions.TryGetValue("ALPN", out var alpnExtension)) if (clientHelloInfo.Extensions != null && clientHelloInfo.Extensions.TryGetValue("ALPN", out var alpnExtension))
{ {
...@@ -77,15 +77,19 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -77,15 +77,19 @@ namespace Titanium.Web.Proxy.Extensions
} }
#endif #endif
} }
}
#if !NETSTANDARD2_1 #if !NETSTANDARD2_1
namespace System.Net.Security
{
internal enum SslApplicationProtocol internal enum SslApplicationProtocol
{ {
Http11, Http11,
Http2 Http2
} }
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "Reviewed.")] [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification =
"Reviewed.")]
internal class SslClientAuthenticationOptions internal class SslClientAuthenticationOptions
{ {
internal bool AllowRenegotiation { get; set; } internal bool AllowRenegotiation { get; set; }
...@@ -111,7 +115,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -111,7 +115,7 @@ namespace Titanium.Web.Proxy.Extensions
{ {
internal bool AllowRenegotiation { get; set; } internal bool AllowRenegotiation { get; set; }
internal X509Certificate ServerCertificate { get; set; } internal X509Certificate? ServerCertificate { get; set; }
internal bool ClientCertificateRequired { get; set; } internal bool ClientCertificateRequired { get; set; }
...@@ -119,11 +123,11 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -119,11 +123,11 @@ namespace Titanium.Web.Proxy.Extensions
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; }
} }
#endif
} }
#endif
...@@ -32,7 +32,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -32,7 +32,7 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="bufferPool"></param> /// <param name="bufferPool"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int>? onCopy,
IBufferPool bufferPool, CancellationToken cancellationToken) IBufferPool bufferPool, CancellationToken cancellationToken)
{ {
var buffer = bufferPool.GetBuffer(); var buffer = bufferPool.GetBuffer();
......
...@@ -10,6 +10,11 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -10,6 +10,11 @@ namespace Titanium.Web.Proxy.Extensions
return str.Equals(value, StringComparison.CurrentCultureIgnoreCase); return str.Equals(value, StringComparison.CurrentCultureIgnoreCase);
} }
internal static bool EqualsIgnoreCase(this ReadOnlySpan<char> str, ReadOnlySpan<char> value)
{
return str.Equals(value, StringComparison.CurrentCultureIgnoreCase);
}
internal static bool ContainsIgnoreCase(this string str, string value) internal static bool ContainsIgnoreCase(this string str, string value)
{ {
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0; return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0;
......
using System; using System;
using System.Net.Sockets; using System.Net.Sockets;
using System.Reflection;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
......
using System; using System;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -16,12 +15,58 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -16,12 +15,58 @@ namespace Titanium.Web.Proxy.Helpers
{ {
private static readonly Encoding defaultEncoding = Encoding.GetEncoding("ISO-8859-1"); private static readonly Encoding defaultEncoding = Encoding.GetEncoding("ISO-8859-1");
public static Encoding HeaderEncoding => defaultEncoding;
struct SemicolonSplitEnumerator
{
private readonly ReadOnlyMemory<char> data;
private ReadOnlyMemory<char> current;
private int idx;
public SemicolonSplitEnumerator(string str) : this(str.AsMemory())
{
}
public SemicolonSplitEnumerator(ReadOnlyMemory<char> data)
{
this.data = data;
current = null;
idx = 0;
}
public SemicolonSplitEnumerator GetEnumerator() { return this; }
public bool MoveNext()
{
if (this.idx > data.Length) return false;
int idx = data.Span.Slice(this.idx).IndexOf(';');
if (idx == -1)
{
idx = data.Length;
}
else
{
idx += this.idx;
}
current = data.Slice(this.idx, idx - this.idx);
this.idx = idx + 1;
return true;
}
public ReadOnlyMemory<char> Current => current;
}
/// <summary> /// <summary>
/// Gets the character encoding of request/response from content-type header /// Gets the character encoding of request/response from content-type header
/// </summary> /// </summary>
/// <param name="contentType"></param> /// <param name="contentType"></param>
/// <returns></returns> /// <returns></returns>
internal static Encoding GetEncodingFromContentType(string contentType) internal static Encoding GetEncodingFromContentType(string? contentType)
{ {
try try
{ {
...@@ -32,24 +77,24 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -32,24 +77,24 @@ namespace Titanium.Web.Proxy.Helpers
} }
// extract the encoding by finding the charset // extract the encoding by finding the charset
var parameters = contentType.Split(ProxyConstants.SemiColonSplit); foreach (var p in new SemicolonSplitEnumerator(contentType))
foreach (string parameter in parameters)
{ {
var split = parameter.Split(ProxyConstants.EqualSplit, 2); var parameter = p.Span;
if (split.Length == 2 && split[0].Trim().EqualsIgnoreCase(KnownHeaders.ContentTypeCharset)) int equalsIndex = parameter.IndexOf('=');
if (equalsIndex != -1 && parameter.Slice(0, equalsIndex).TrimStart().EqualsIgnoreCase(KnownHeaders.ContentTypeCharset.AsSpan()))
{ {
string value = split[1]; var value = parameter.Slice(equalsIndex + 1);
if (value.EqualsIgnoreCase("x-user-defined")) if (value.EqualsIgnoreCase("x-user-defined".AsSpan()))
{ {
continue; continue;
} }
if (value.Length > 2 && value[0] == '"' && value[value.Length - 1] == '"') if (value.Length > 2 && value[0] == '"' && value[value.Length - 1] == '"')
{ {
value = value.Substring(1, value.Length - 2); value = value.Slice(1, value.Length - 2);
} }
return Encoding.GetEncoding(value); return Encoding.GetEncoding(value.ToString());
} }
} }
} }
...@@ -63,21 +108,20 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -63,21 +108,20 @@ namespace Titanium.Web.Proxy.Helpers
return defaultEncoding; return defaultEncoding;
} }
internal static string GetBoundaryFromContentType(string contentType) internal static ReadOnlyMemory<char> GetBoundaryFromContentType(string? contentType)
{ {
if (contentType != null) if (contentType != null)
{ {
// extract the boundary // extract the boundary
var parameters = contentType.Split(ProxyConstants.SemiColonSplit); foreach (var parameter in new SemicolonSplitEnumerator(contentType))
foreach (string parameter in parameters)
{ {
var split = parameter.Split(ProxyConstants.EqualSplit, 2); int equalsIndex = parameter.Span.IndexOf('=');
if (split.Length == 2 && split[0].Trim().EqualsIgnoreCase(KnownHeaders.ContentTypeBoundary)) if (equalsIndex != -1 && parameter.Span.Slice(0, equalsIndex).TrimStart().EqualsIgnoreCase(KnownHeaders.ContentTypeBoundary.AsSpan()))
{ {
string value = split[1]; var value = parameter.Slice(equalsIndex + 1);
if (value.Length > 2 && value[0] == '"' && value[value.Length - 1] == '"') if (value.Length > 2 && value.Span[0] == '"' && value.Span[value.Length - 1] == '"')
{ {
value = value.Substring(1, value.Length - 2); value = value.Slice(1, value.Length - 2);
} }
return value; return value;
...@@ -152,16 +196,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -152,16 +196,14 @@ namespace Titanium.Web.Proxy.Helpers
private static async Task<int> startsWith(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, string expectedStart, CancellationToken cancellationToken = default) private static async Task<int> startsWith(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, string expectedStart, CancellationToken cancellationToken = default)
{ {
const int lengthToCheck = 10; const int lengthToCheck = 10;
byte[] buffer = null; if (bufferPool.BufferSize < lengthToCheck)
try
{ {
if (bufferPool.BufferSize < lengthToCheck) throw new Exception($"Buffer is too small. Minimum size is {lengthToCheck} bytes");
{ }
throw new Exception($"Buffer is too small. Minimum size is {lengthToCheck} bytes");
}
buffer = bufferPool.GetBuffer(bufferPool.BufferSize);
byte[] buffer = bufferPool.GetBuffer(bufferPool.BufferSize);
try
{
bool isExpected = true; bool isExpected = true;
int i = 0; int i = 0;
while (i < lengthToCheck) while (i < lengthToCheck)
......
...@@ -17,15 +17,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -17,15 +17,13 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the request. /// Writes the request.
/// </summary> /// </summary>
/// <param name="request">The request object.</param> /// <param name="request">The request object.</param>
/// <param name="flush">Should we flush after write?</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param> /// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns></returns> /// <returns></returns>
internal async Task WriteRequestAsync(Request request, bool flush = true, internal async Task WriteRequestAsync(Request request, CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
{ {
await WriteLineAsync(Request.CreateRequestLine(request.Method, request.RequestUriString, request.HttpVersion), var headerBuilder = new HeaderBuilder();
cancellationToken); headerBuilder.WriteRequestLine(request.Method, request.RequestUriString, request.HttpVersion);
await WriteAsync(request, flush, cancellationToken); await WriteAsync(request, headerBuilder, cancellationToken);
} }
} }
} }
...@@ -18,29 +18,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -18,29 +18,13 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the response. /// Writes the response.
/// </summary> /// </summary>
/// <param name="response">The response object.</param> /// <param name="response">The response object.</param>
/// <param name="flush">Should we flush after write?</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param> /// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The Task.</returns> /// <returns>The Task.</returns>
internal async Task WriteResponseAsync(Response response, bool flush = true, internal async Task WriteResponseAsync(Response response, CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
{ {
await WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, response.StatusDescription, var headerBuilder = new HeaderBuilder();
cancellationToken); headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
await WriteAsync(response, flush, cancellationToken); await WriteAsync(response, headerBuilder, cancellationToken);
}
/// <summary>
/// Write response status
/// </summary>
/// <param name="version">The Http version.</param>
/// <param name="code">The HTTP status code.</param>
/// <param name="description">The HTTP status description.</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The Task.</returns>
internal Task WriteResponseStatusAsync(Version version, int code, string description,
CancellationToken cancellationToken)
{
return WriteLineAsync(Response.CreateResponseLine(version, code, description), cancellationToken);
} }
} }
} }
using System; using System;
using System.Buffers;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
...@@ -18,7 +19,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -18,7 +19,7 @@ namespace Titanium.Web.Proxy.Helpers
private static readonly byte[] newLine = ProxyConstants.NewLineBytes; private static readonly byte[] newLine = ProxyConstants.NewLineBytes;
private static readonly Encoding encoder = Encoding.ASCII; private static Encoding encoding => HttpHelper.HeaderEncoding;
internal HttpWriter(Stream stream, IBufferPool bufferPool) internal HttpWriter(Stream stream, IBufferPool bufferPool)
{ {
...@@ -50,7 +51,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -50,7 +51,7 @@ namespace Titanium.Web.Proxy.Helpers
var buffer = bufferPool.GetBuffer(); var buffer = bufferPool.GetBuffer();
try try
{ {
int idx = encoder.GetBytes(value, 0, charCount, buffer, 0); int idx = encoding.GetBytes(value, 0, charCount, buffer, 0);
if (newLineChars > 0) if (newLineChars > 0)
{ {
Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars); Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
...@@ -67,7 +68,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -67,7 +68,7 @@ namespace Titanium.Web.Proxy.Helpers
else else
{ {
var buffer = new byte[charCount + newLineChars + 1]; var buffer = new byte[charCount + newLineChars + 1];
int idx = encoder.GetBytes(value, 0, charCount, buffer, 0); int idx = encoding.GetBytes(value, 0, charCount, buffer, 0);
if (newLineChars > 0) if (newLineChars > 0)
{ {
Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars); Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
...@@ -86,28 +87,20 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -86,28 +87,20 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary> /// <summary>
/// Write the headers to client /// Write the headers to client
/// </summary> /// </summary>
/// <param name="headers"></param> /// <param name="header"></param>
/// <param name="flush"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
internal async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true, internal async Task WriteHeadersAsync(HeaderBuilder header, CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
{ {
var headerBuilder = new StringBuilder(); await WriteAsync(header.GetBytes(), true, cancellationToken);
foreach (var header in headers)
{
headerBuilder.Append($"{header.ToString()}{ProxyConstants.NewLine}");
}
headerBuilder.Append(ProxyConstants.NewLine);
await WriteAsync(headerBuilder.ToString(), cancellationToken);
if (flush)
{
await stream.FlushAsync(cancellationToken);
}
} }
/// <summary>
/// Writes the data to the stream.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="flush">Should we flush after write?</param>
/// <param name="cancellationToken">The cancellation token.</param>
internal async Task WriteAsync(byte[] data, bool flush = false, CancellationToken cancellationToken = default) internal async Task WriteAsync(byte[] data, bool flush = false, CancellationToken cancellationToken = default)
{ {
await stream.WriteAsync(data, 0, data.Length, cancellationToken); await stream.WriteAsync(data, 0, data.Length, cancellationToken);
...@@ -155,7 +148,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -155,7 +148,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
internal Task CopyBodyAsync(ICustomStreamReader streamReader, bool isChunked, long contentLength, internal Task CopyBodyAsync(ICustomStreamReader 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
if (isChunked) if (isChunked)
...@@ -199,8 +192,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -199,8 +192,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, private async Task copyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int>? onCopy, CancellationToken cancellationToken)
CancellationToken cancellationToken)
{ {
while (true) while (true)
{ {
...@@ -240,7 +232,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -240,7 +232,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(ICustomStreamReader reader, long count, Action<byte[], int, int>? onCopy,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var buffer = bufferPool.GetBuffer(); var buffer = bufferPool.GetBuffer();
...@@ -280,14 +272,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -280,14 +272,14 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the request/response headers and body. /// Writes the request/response headers and body.
/// </summary> /// </summary>
/// <param name="requestResponse"></param> /// <param name="requestResponse"></param>
/// <param name="flush"></param> /// <param name="headerBuilder"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
protected async Task WriteAsync(RequestResponseBase requestResponse, bool flush = true, protected async Task WriteAsync(RequestResponseBase requestResponse, HeaderBuilder headerBuilder, CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
{ {
var body = requestResponse.CompressBodyAndUpdateContentLength(); var body = requestResponse.CompressBodyAndUpdateContentLength();
await WriteHeadersAsync(requestResponse.Headers, flush, cancellationToken); headerBuilder.WriteHeaders(requestResponse.Headers);
await WriteAsync(headerBuilder.GetBytes(), true, cancellationToken);
if (body != null) if (body != null)
{ {
...@@ -322,5 +314,31 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -322,5 +314,31 @@ namespace Titanium.Web.Proxy.Helpers
{ {
return stream.WriteAsync(buffer, offset, count, cancellationToken); return stream.WriteAsync(buffer, offset, count, cancellationToken);
} }
#if NETSTANDARD2_1
/// <summary>
/// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number of bytes written, and monitors cancellation requests.
/// </summary>
/// <param name="buffer">The buffer to write data from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="P:System.Threading.CancellationToken.None" />.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
return stream.WriteAsync(buffer, cancellationToken);
}
#else
/// <summary>
/// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number of bytes written, and monitors cancellation requests.
/// </summary>
/// <param name="buffer">The buffer to write data from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="P:System.Threading.CancellationToken.None" />.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public Task WriteAsync2(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
var buf = ArrayPool<byte>.Shared.Rent(buffer.Length);
buffer.CopyTo(buf);
return stream.WriteAsync(buf, 0, buf.Length, cancellationToken);
}
#endif
} }
} }
using System; using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
......
...@@ -9,8 +9,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -9,8 +9,8 @@ namespace Titanium.Web.Proxy.Helpers
{ {
internal class ProxyInfo internal class ProxyInfo
{ {
internal ProxyInfo(bool? autoDetect, string autoConfigUrl, int? proxyEnable, string proxyServer, internal ProxyInfo(bool? autoDetect, string? autoConfigUrl, int? proxyEnable, string? proxyServer,
string proxyOverride) string? proxyOverride)
{ {
AutoDetect = autoDetect; AutoDetect = autoDetect;
AutoConfigUrl = autoConfigUrl; AutoConfigUrl = autoConfigUrl;
...@@ -54,13 +54,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -54,13 +54,13 @@ namespace Titanium.Web.Proxy.Helpers
internal bool? AutoDetect { get; } internal bool? AutoDetect { get; }
internal string AutoConfigUrl { get; } internal string? AutoConfigUrl { get; }
internal int? ProxyEnable { get; } internal int? ProxyEnable { get; }
internal string ProxyServer { get; } internal string? ProxyServer { get; }
internal string ProxyOverride { get; } internal string? ProxyOverride { get; }
internal bool BypassLoopback { get; } internal bool BypassLoopback { get; }
...@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
/// <param name="proxyServerValues"></param> /// <param name="proxyServerValues"></param>
/// <returns></returns> /// <returns></returns>
internal static List<HttpSystemProxyValue> GetSystemProxyValues(string proxyServerValues) internal static List<HttpSystemProxyValue> GetSystemProxyValues(string? proxyServerValues)
{ {
var result = new List<HttpSystemProxyValue>(); var result = new List<HttpSystemProxyValue>();
...@@ -167,7 +167,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -167,7 +167,7 @@ namespace Titanium.Web.Proxy.Helpers
return result; return result;
} }
var proxyValues = proxyServerValues.Split(';'); var proxyValues = proxyServerValues!.Split(';');
if (proxyValues.Length > 0) if (proxyValues.Length > 0)
{ {
...@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
private static HttpSystemProxyValue parseProxyValue(string value) private static HttpSystemProxyValue? parseProxyValue(string value)
{ {
string tmp = Regex.Replace(value, @"\s+", " ").Trim(); string tmp = Regex.Replace(value, @"\s+", " ").Trim();
......
...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.Helpers
internal const int InternetOptionSettingsChanged = 39; internal const int InternetOptionSettingsChanged = 39;
internal const int InternetOptionRefresh = 37; internal const int InternetOptionRefresh = 37;
private ProxyInfo originalValues; private ProxyInfo? originalValues;
public SystemProxyManager() public SystemProxyManager()
{ {
...@@ -90,8 +90,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -90,8 +90,8 @@ namespace Titanium.Web.Proxy.Helpers
saveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
prepareRegistry(reg); prepareRegistry(reg);
string exisitingContent = reg.GetValue(regProxyServer) as string; string? existingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent); var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(existingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0); existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if ((protocolType & ProxyProtocolType.Http) != 0) if ((protocolType & ProxyProtocolType.Http) != 0)
{ {
...@@ -141,9 +141,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -141,9 +141,9 @@ namespace Titanium.Web.Proxy.Helpers
if (reg.GetValue(regProxyServer) != null) if (reg.GetValue(regProxyServer) != null)
{ {
string exisitingContent = reg.GetValue(regProxyServer) as string; string? existingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent); var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(existingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0); existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if (existingSystemProxyValues.Count != 0) if (existingSystemProxyValues.Count != 0)
...@@ -284,7 +284,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -284,7 +284,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
internal ProxyInfo GetProxyInfoFromRegistry() internal ProxyInfo? GetProxyInfoFromRegistry()
{ {
using (var reg = openInternetSettingsKey()) using (var reg = openInternetSettingsKey())
{ {
......
...@@ -103,7 +103,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -103,7 +103,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
private static async Task sendRawTap(Stream clientStream, Stream serverStream, IBufferPool bufferPool, private static async Task sendRawTap(Stream clientStream, Stream serverStream, IBufferPool bufferPool,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<byte[], int, int>? onDataSend, Action<byte[], int, int>? onDataReceive,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
...@@ -133,7 +133,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -133,7 +133,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
internal static Task SendRaw(Stream clientStream, Stream serverStream, IBufferPool bufferPool, internal static Task SendRaw(Stream clientStream, Stream serverStream, IBufferPool bufferPool,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<byte[], int, int>? onDataSend, Action<byte[], int, int>? onDataReceive,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
......
...@@ -13,8 +13,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -13,8 +13,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
ref WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig); ref WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern WinHttpHandle WinHttpOpen(string userAgent, AccessType accessType, string proxyName, internal static extern WinHttpHandle WinHttpOpen(string? userAgent, AccessType accessType, string? proxyName,
string proxyBypass, int dwFlags); string? proxyBypass, int dwFlags);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool WinHttpSetTimeouts(WinHttpHandle session, int resolveTimeout, internal static extern bool WinHttpSetTimeouts(WinHttpHandle session, int resolveTimeout,
...@@ -66,7 +66,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -66,7 +66,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
{ {
public AutoProxyFlags Flags; public AutoProxyFlags Flags;
public AutoDetectType AutoDetectFlags; public AutoDetectType AutoDetectFlags;
[MarshalAs(UnmanagedType.LPWStr)] public string AutoConfigUrl; [MarshalAs(UnmanagedType.LPWStr)] public string? AutoConfigUrl;
private readonly IntPtr lpvReserved; private readonly IntPtr lpvReserved;
private readonly int dwReserved; private readonly int dwReserved;
public bool AutoLogonIfChallenged; public bool AutoLogonIfChallenged;
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
...@@ -42,7 +42,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -42,7 +42,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public bool BypassOnLocal { get; internal set; } public bool BypassOnLocal { get; internal set; }
public Uri AutomaticConfigurationScript { get; internal set; } public Uri? AutomaticConfigurationScript { get; internal set; }
public bool AutomaticallyDetectSettings { get; internal set; } public bool AutomaticallyDetectSettings { get; internal set; }
...@@ -53,7 +53,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -53,7 +53,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
dispose(true); dispose(true);
} }
public bool GetAutoProxies(Uri destination, out IList<string> proxyList) public bool GetAutoProxies(Uri destination, out IList<string>? proxyList)
{ {
proxyList = null; proxyList = null;
if (session == null || session.IsInvalid || state == AutoWebProxyState.UnrecognizedScheme) if (session == null || session.IsInvalid || state == AutoWebProxyState.UnrecognizedScheme)
...@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return false; return false;
} }
string proxyListString = null; string? proxyListString = null;
var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed; var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed;
if (AutomaticallyDetectSettings && !autoDetectFailed) if (AutomaticallyDetectSettings && !autoDetectFailed)
{ {
...@@ -88,14 +88,14 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -88,14 +88,14 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
if (!string.IsNullOrEmpty(proxyListString)) if (!string.IsNullOrEmpty(proxyListString))
{ {
proxyListString = removeWhitespaces(proxyListString); proxyListString = removeWhitespaces(proxyListString!);
proxyList = proxyListString.Split(';'); proxyList = proxyListString.Split(';');
} }
return true; return true;
} }
public ExternalProxy GetProxy(Uri destination) public ExternalProxy? GetProxy(Uri destination)
{ {
if (GetAutoProxies(destination, out var proxies)) if (GetAutoProxies(destination, out var proxies))
{ {
...@@ -131,12 +131,12 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -131,12 +131,12 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
var protocolType = ProxyInfo.ParseProtocolType(destination.Scheme); var protocolType = ProxyInfo.ParseProtocolType(destination.Scheme);
if (protocolType.HasValue) if (protocolType.HasValue)
{ {
HttpSystemProxyValue value = null; HttpSystemProxyValue? value = null;
if (ProxyInfo?.Proxies?.TryGetValue(protocolType.Value, out value) == true) if (ProxyInfo?.Proxies?.TryGetValue(protocolType.Value, out value) == true)
{ {
var systemProxy = new ExternalProxy var systemProxy = new ExternalProxy
{ {
HostName = value.HostName, HostName = value!.HostName,
Port = value.Port Port = value.Port
}; };
...@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
session.Close(); session.Close();
} }
private int getAutoProxies(Uri destination, Uri scriptLocation, out string proxyListString) private int getAutoProxies(Uri destination, Uri? scriptLocation, out string? proxyListString)
{ {
int num = 0; int num = 0;
var autoProxyOptions = new NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS(); var autoProxyOptions = new NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS();
...@@ -247,7 +247,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -247,7 +247,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
} }
private bool winHttpGetProxyForUrl(string destination, private bool winHttpGetProxyForUrl(string destination,
ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string proxyListString) ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string? proxyListString)
{ {
proxyListString = null; proxyListString = null;
bool flag; bool flag;
......
...@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public class ConnectResponse : Response public class ConnectResponse : Response
{ {
public ServerHelloInfo ServerHelloInfo { get; set; } public ServerHelloInfo? ServerHelloInfo { get; set; }
/// <summary> /// <summary>
/// Creates a successful CONNECT response /// Creates a successful CONNECT response
......
using System;
using System.Buffers;
using System.IO;
using System.Text;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http
{
internal class HeaderBuilder
{
private MemoryStream stream = new MemoryStream();
public void WriteRequestLine(string httpMethod, string httpUrl, Version version)
{
// "{httpMethod} {httpUrl} HTTP/{version.Major}.{version.Minor}";
Write(httpMethod);
Write(" ");
Write(httpUrl);
Write(" HTTP/");
Write(version.Major.ToString());
Write(".");
Write(version.Minor.ToString());
WriteLine();
}
public void WriteResponseLine(Version version, int statusCode, string statusDescription)
{
// "HTTP/{version.Major}.{version.Minor} {statusCode} {statusDescription}";
Write("HTTP/");
Write(version.Major.ToString());
Write(".");
Write(version.Minor.ToString());
Write(" ");
Write(statusCode.ToString());
Write(" ");
Write(statusDescription);
WriteLine();
}
public void WriteHeaders(HeaderCollection headers)
{
foreach (var header in headers)
{
WriteHeader(header);
}
WriteLine();
}
public void WriteHeader(HttpHeader header)
{
Write(header.Name);
Write(": ");
Write(header.Value);
WriteLine();
}
public void WriteLine()
{
var data = ProxyConstants.NewLineBytes;
stream.Write(data, 0, data.Length);
}
public void Write(string str)
{
var encoding = HttpHelper.HeaderEncoding;
#if NETSTANDARD2_1
var buf = ArrayPool<byte>.Shared.Rent(str.Length * 4);
var span = new Span<byte>(buf);
int bytes = encoding.GetBytes(str.AsSpan(), span);
stream.Write(span.Slice(0, bytes));
ArrayPool<byte>.Shared.Return(buf);
#else
var data = encoding.GetBytes(str);
stream.Write(data, 0, data.Length);
#endif
}
public byte[] GetBytes()
{
return stream.ToArray();
}
}
}
...@@ -71,7 +71,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -71,7 +71,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
/// <param name="name"></param> /// <param name="name"></param>
/// <returns></returns> /// <returns></returns>
public List<HttpHeader> GetHeaders(string name) public List<HttpHeader>? GetHeaders(string name)
{ {
if (headers.ContainsKey(name)) if (headers.ContainsKey(name))
{ {
...@@ -89,7 +89,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -89,7 +89,7 @@ namespace Titanium.Web.Proxy.Http
return null; return null;
} }
public HttpHeader GetFirstHeader(string name) public HttpHeader? GetFirstHeader(string name)
{ {
if (headers.TryGetValue(name, out var header)) if (headers.TryGetValue(name, out var header))
{ {
...@@ -198,7 +198,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -198,7 +198,7 @@ namespace Titanium.Web.Proxy.Http
/// Adds the given header objects to Request /// Adds the given header objects to Request
/// </summary> /// </summary>
/// <param name="newHeaders"></param> /// <param name="newHeaders"></param>
public void AddHeaders(IEnumerable<KeyValuePair<string, HttpHeader>> newHeaders) public void AddHeaders(IEnumerable<KeyValuePair<string, HttpHeader>>? newHeaders)
{ {
if (newHeaders == null) if (newHeaders == null)
{ {
...@@ -272,7 +272,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -272,7 +272,7 @@ namespace Titanium.Web.Proxy.Http
nonUniqueHeaders.Clear(); nonUniqueHeaders.Clear();
} }
internal string GetHeaderValueOrNull(string headerName) internal string? GetHeaderValueOrNull(string headerName)
{ {
if (headers.TryGetValue(headerName, out var header)) if (headers.TryGetValue(headerName, out var header))
{ {
...@@ -282,8 +282,14 @@ namespace Titanium.Web.Proxy.Http ...@@ -282,8 +282,14 @@ namespace Titanium.Web.Proxy.Http
return null; return null;
} }
internal void SetOrAddHeaderValue(string headerName, string value) internal void SetOrAddHeaderValue(string headerName, string? value)
{ {
if (value == null)
{
RemoveHeader(headerName);
return;
}
if (headers.TryGetValue(headerName, out var header)) if (headers.TryGetValue(headerName, out var header))
{ {
header.Value = value; header.Value = value;
...@@ -300,7 +306,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -300,7 +306,7 @@ namespace Titanium.Web.Proxy.Http
internal void FixProxyHeaders() internal void FixProxyHeaders()
{ {
// If proxy-connection close was returned inform to close the connection // If proxy-connection close was returned inform to close the connection
string proxyHeader = GetHeaderValueOrNull(KnownHeaders.ProxyConnection); string? proxyHeader = GetHeaderValueOrNull(KnownHeaders.ProxyConnection);
RemoveHeader(KnownHeaders.ProxyConnection); RemoveHeader(KnownHeaders.ProxyConnection);
if (proxyHeader != null) if (proxyHeader != null)
......
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
using Titanium.Web.Proxy.StreamExtended.Network; using Titanium.Web.Proxy.StreamExtended.Network;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
...@@ -11,11 +10,18 @@ namespace Titanium.Web.Proxy.Http ...@@ -11,11 +10,18 @@ namespace Titanium.Web.Proxy.Http
internal static async Task ReadHeaders(ICustomStreamReader reader, HeaderCollection headerCollection, internal static async Task ReadHeaders(ICustomStreamReader reader, HeaderCollection headerCollection,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
string tmpLine; string? tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync(cancellationToken))) while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync(cancellationToken)))
{ {
var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); int colonIndex = tmpLine!.IndexOf(':');
headerCollection.AddHeader(header[0], header[1]); if (colonIndex == -1)
{
throw new Exception("Header line should contain a colon character.");
}
string headerName = tmpLine.AsSpan(0, colonIndex).ToString();
string headerValue = tmpLine.AsSpan(colonIndex + 1).ToString();
headerCollection.AddHeader(headerName, headerValue);
} }
} }
......
...@@ -8,7 +8,6 @@ using Titanium.Web.Proxy.Exceptions; ...@@ -8,7 +8,6 @@ using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
...@@ -17,7 +16,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -17,7 +16,9 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public class HttpWebClient public class HttpWebClient
{ {
internal HttpWebClient(Request request) private TcpServerConnection? connection;
internal HttpWebClient(Request? request)
{ {
Request = request ?? new Request(); Request = request ?? new Request();
Response = new Response(); Response = new Response();
...@@ -26,7 +27,20 @@ namespace Titanium.Web.Proxy.Http ...@@ -26,7 +27,20 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Connection to server /// Connection to server
/// </summary> /// </summary>
internal TcpServerConnection Connection { get; set; } internal TcpServerConnection Connection
{
get
{
if (connection == null)
{
throw new Exception("Connection is null");
}
return connection;
}
}
internal bool HasConnection => connection != null;
/// <summary> /// <summary>
/// Should we close the server connection at the end of this HTTP request/response session. /// Should we close the server connection at the end of this HTTP request/response session.
...@@ -41,17 +55,17 @@ namespace Titanium.Web.Proxy.Http ...@@ -41,17 +55,17 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Gets or sets the user data. /// Gets or sets the user data.
/// </summary> /// </summary>
public object UserData { get; set; } public object? UserData { get; set; }
/// <summary> /// <summary>
/// Override UpStreamEndPoint for this request; Local NIC via request is made /// Override UpStreamEndPoint for this request; Local NIC via request is made
/// </summary> /// </summary>
public IPEndPoint UpStreamEndPoint { get; set; } public IPEndPoint? UpStreamEndPoint { get; set; }
/// <summary> /// <summary>
/// Headers passed with Connect. /// Headers passed with Connect.
/// </summary> /// </summary>
public ConnectRequest ConnectRequest { get; internal set; } public ConnectRequest? ConnectRequest { get; internal set; }
/// <summary> /// <summary>
/// Web Request. /// Web Request.
...@@ -81,7 +95,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -81,7 +95,7 @@ namespace Titanium.Web.Proxy.Http
internal void SetConnection(TcpServerConnection serverConnection) internal void SetConnection(TcpServerConnection serverConnection)
{ {
serverConnection.LastAccess = DateTime.Now; serverConnection.LastAccess = DateTime.Now;
Connection = serverConnection; connection = serverConnection;
} }
/// <summary> /// <summary>
...@@ -107,19 +121,19 @@ namespace Titanium.Web.Proxy.Http ...@@ -107,19 +121,19 @@ namespace Titanium.Web.Proxy.Http
url = Request.RequestUri.GetOriginalPathAndQuery(); url = Request.RequestUri.GetOriginalPathAndQuery();
} }
var headerBuilder = new HeaderBuilder();
// prepare the request & headers // prepare the request & headers
await writer.WriteLineAsync(Request.CreateRequestLine(Request.Method, url, Request.HttpVersion), cancellationToken); headerBuilder.WriteRequestLine(Request.Method, url, Request.HttpVersion);
var headerBuilder = new StringBuilder();
// Send Authentication to Upstream proxy if needed // Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null if (!isTransparent && upstreamProxy != null
&& Connection.IsHttps == false && Connection.IsHttps == false
&& !string.IsNullOrEmpty(upstreamProxy.UserName) && !string.IsNullOrEmpty(upstreamProxy.UserName)
&& upstreamProxy.Password != null) && upstreamProxy.Password != null)
{ {
headerBuilder.Append($"{HttpHeader.ProxyConnectionKeepAlive}{ProxyConstants.NewLine}"); headerBuilder.WriteHeader(HttpHeader.ProxyConnectionKeepAlive);
headerBuilder.Append($"{HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password)}{ProxyConstants.NewLine}"); headerBuilder.WriteHeader(HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password));
} }
// write request headers // write request headers
...@@ -127,13 +141,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -127,13 +141,15 @@ namespace Titanium.Web.Proxy.Http
{ {
if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization) if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization)
{ {
headerBuilder.Append($"{header}{ProxyConstants.NewLine}"); headerBuilder.WriteHeader(header);
} }
} }
headerBuilder.Append(ProxyConstants.NewLine); headerBuilder.WriteLine();
await writer.WriteAsync(headerBuilder.ToString(), cancellationToken); var data = headerBuilder.GetBytes();
await writer.WriteAsync(data, 0, data.Length, cancellationToken);
if (enable100ContinueBehaviour && Request.ExpectContinue) if (enable100ContinueBehaviour && Request.ExpectContinue)
{ {
...@@ -166,11 +182,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -166,11 +182,8 @@ namespace Titanium.Web.Proxy.Http
string httpStatus; string httpStatus;
try try
{ {
httpStatus = await Connection.Stream.ReadLineAsync(cancellationToken); httpStatus = await Connection.Stream.ReadLineAsync(cancellationToken) ??
if (httpStatus == null) throw new ServerConnectionException("Server connection was closed.");
{
throw new ServerConnectionException("Server connection was closed.");
}
} }
catch (Exception e) when (!(e is ServerConnectionException)) catch (Exception e) when (!(e is ServerConnectionException))
{ {
...@@ -179,7 +192,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -179,7 +192,8 @@ namespace Titanium.Web.Proxy.Http
if (httpStatus == string.Empty) if (httpStatus == string.Empty)
{ {
httpStatus = await Connection.Stream.ReadLineAsync(cancellationToken); httpStatus = await Connection.Stream.ReadLineAsync(cancellationToken) ??
throw new ServerConnectionException("Server connection was closed.");
} }
Response.ParseResponseLine(httpStatus, out var version, out int statusCode, out string statusDescription); Response.ParseResponseLine(httpStatus, out var version, out int statusCode, out string statusDescription);
...@@ -197,7 +211,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -197,7 +211,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
internal void FinishSession() internal void FinishSession()
{ {
Connection = null; connection = null;
ConnectRequest?.FinishSession(); ConnectRequest?.FinishSession();
Request?.FinishSession(); Request?.FinishSession();
......
using System.Collections.Generic; using System.Collections.Generic;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
...@@ -13,7 +13,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -13,7 +13,8 @@ namespace Titanium.Web.Proxy.Http
} }
else else
{ {
value = default; // hack: https://stackoverflow.com/questions/54593923/nullable-reference-types-with-generic-return-type
value = default!;
} }
return result; return result;
...@@ -24,4 +25,4 @@ namespace Titanium.Web.Proxy.Http ...@@ -24,4 +25,4 @@ namespace Titanium.Web.Proxy.Http
return (T)this[key]; return (T)this[key];
} }
} }
} }
\ No newline at end of file
...@@ -3,8 +3,8 @@ using System.ComponentModel; ...@@ -3,8 +3,8 @@ using System.ComponentModel;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
...@@ -85,7 +85,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -85,7 +85,7 @@ namespace Titanium.Web.Proxy.Http
/// Note: Changing this does NOT change host in RequestUri. /// Note: Changing this does NOT change host in RequestUri.
/// Users can set new RequestUri separately. /// Users can set new RequestUri separately.
/// </summary> /// </summary>
public string Host public string? Host
{ {
get => Headers.GetHeaderValueOrNull(KnownHeaders.Host); get => Headers.GetHeaderValueOrNull(KnownHeaders.Host);
set => Headers.SetOrAddHeaderValue(KnownHeaders.Host, value); set => Headers.SetOrAddHeaderValue(KnownHeaders.Host, value);
...@@ -98,7 +98,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -98,7 +98,7 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Expect); string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Expect);
return headerValue != null && headerValue.Equals(KnownHeaders.Expect100Continue); return headerValue != null && headerValue.Equals(KnownHeaders.Expect100Continue);
} }
} }
...@@ -126,7 +126,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -126,7 +126,7 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Upgrade); string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Upgrade);
if (headerValue == null) if (headerValue == null)
{ {
...@@ -154,15 +154,10 @@ namespace Titanium.Web.Proxy.Http ...@@ -154,15 +154,10 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var sb = new StringBuilder(); var headerBuilder = new HeaderBuilder();
sb.Append($"{CreateRequestLine(Method, RequestUriString, HttpVersion)}{ProxyConstants.NewLine}"); headerBuilder.WriteRequestLine(Method, RequestUriString, HttpVersion);
foreach (var header in Headers) headerBuilder.WriteHeaders(Headers);
{ return HttpHelper.HeaderEncoding.GetString(headerBuilder.GetBytes());
sb.Append($"{header.ToString()}{ProxyConstants.NewLine}");
}
sb.Append(ProxyConstants.NewLine);
return sb.ToString();
} }
} }
...@@ -197,38 +192,41 @@ namespace Titanium.Web.Proxy.Http ...@@ -197,38 +192,41 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal static string CreateRequestLine(string httpMethod, string httpUrl, Version version)
{
return $"{httpMethod} {httpUrl} HTTP/{version.Major}.{version.Minor}";
}
internal static void ParseRequestLine(string httpCmd, out string httpMethod, out string httpUrl, internal static void ParseRequestLine(string httpCmd, out string httpMethod, out string httpUrl,
out Version version) out Version version)
{ {
// break up the line into three components (method, remote URL & Http Version) int firstSpace = httpCmd.IndexOf(' ');
var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3); if (firstSpace == -1)
if (httpCmdSplit.Length < 2)
{ {
// does not contain at least 2 parts
throw new Exception("Invalid HTTP request line: " + httpCmd); throw new Exception("Invalid HTTP request line: " + httpCmd);
} }
int lastSpace = httpCmd.LastIndexOf(' ');
// break up the line into three components (method, remote URL & Http Version)
// Find the request Verb // Find the request Verb
httpMethod = httpCmdSplit[0]; httpMethod = httpCmd.Substring(0, firstSpace);
if (!isAllUpper(httpMethod)) if (!isAllUpper(httpMethod))
{ {
httpMethod = httpMethod.ToUpper(); httpMethod = httpMethod.ToUpper();
} }
httpUrl = httpCmdSplit[1];
// parse the HTTP version
version = HttpHeader.Version11; version = HttpHeader.Version11;
if (httpCmdSplit.Length == 3)
if (firstSpace == lastSpace)
{
httpUrl = httpCmd.AsSpan(firstSpace + 1).ToString();
}
else
{ {
string httpVersion = httpCmdSplit[2].Trim(); httpUrl = httpCmd.AsSpan(firstSpace + 1, lastSpace - firstSpace - 1).ToString();
// parse the HTTP version
var httpVersion = httpCmd.AsSpan(lastSpace + 1);
if (httpVersion.EqualsIgnoreCase("HTTP/1.0")) if (httpVersion.EqualsIgnoreCase("HTTP/1.0".AsSpan(0)))
{ {
version = HttpHeader.Version10; version = HttpHeader.Version10;
} }
......
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -19,12 +18,12 @@ namespace Titanium.Web.Proxy.Http ...@@ -19,12 +18,12 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Cached body content as byte array. /// Cached body content as byte array.
/// </summary> /// </summary>
protected byte[] BodyInternal { get; private set; } protected byte[]? BodyInternal { get; private set; }
/// <summary> /// <summary>
/// Cached body as string. /// Cached body as string.
/// </summary> /// </summary>
private string bodyString; private string? bodyString;
/// <summary> /// <summary>
/// Store whether the original request/response has body or not, since the user may change the parameters. /// Store whether the original request/response has body or not, since the user may change the parameters.
...@@ -48,13 +47,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -48,13 +47,13 @@ namespace Titanium.Web.Proxy.Http
/// Store whether the original request/response content-encoding, since the user may change the parameters. /// Store whether the original request/response content-encoding, since the user may change the parameters.
/// We need this detail to syphon out attached tcp connection for reuse. /// We need this detail to syphon out attached tcp connection for reuse.
/// </summary> /// </summary>
internal string OriginalContentEncoding { get; set; } internal string? OriginalContentEncoding { get; set; }
internal TaskCompletionSource<bool> ReadHttp2BeforeHandlerTaskCompletionSource; internal TaskCompletionSource<bool>? ReadHttp2BeforeHandlerTaskCompletionSource;
internal TaskCompletionSource<bool> ReadHttp2BodyTaskCompletionSource; internal TaskCompletionSource<bool>? ReadHttp2BodyTaskCompletionSource;
internal MemoryStream Http2BodyData; internal MemoryStream? Http2BodyData;
internal bool Http2IgnoreBodyFrames; internal bool Http2IgnoreBodyFrames;
...@@ -87,7 +86,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -87,7 +86,7 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.ContentLength); string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.ContentLength);
if (headerValue == null) if (headerValue == null)
{ {
...@@ -119,7 +118,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -119,7 +118,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Content encoding for this request/response. /// Content encoding for this request/response.
/// </summary> /// </summary>
public string ContentEncoding => Headers.GetHeaderValueOrNull(KnownHeaders.ContentEncoding)?.Trim(); public string? ContentEncoding => Headers.GetHeaderValueOrNull(KnownHeaders.ContentEncoding)?.Trim();
/// <summary> /// <summary>
/// Encoding for this request/response. /// Encoding for this request/response.
...@@ -129,7 +128,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -129,7 +128,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Content-type of the request/response. /// Content-type of the request/response.
/// </summary> /// </summary>
public string ContentType public string? ContentType
{ {
get => Headers.GetHeaderValueOrNull(KnownHeaders.ContentType); get => Headers.GetHeaderValueOrNull(KnownHeaders.ContentType);
set => Headers.SetOrAddHeaderValue(KnownHeaders.ContentType, value); set => Headers.SetOrAddHeaderValue(KnownHeaders.ContentType, value);
...@@ -142,7 +141,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -142,7 +141,7 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.TransferEncoding); string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.TransferEncoding);
return headerValue != null && headerValue.ContainsIgnoreCase(KnownHeaders.TransferEncodingChunked); return headerValue != null && headerValue.ContainsIgnoreCase(KnownHeaders.TransferEncodingChunked);
} }
...@@ -174,7 +173,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -174,7 +173,7 @@ namespace Titanium.Web.Proxy.Http
get get
{ {
EnsureBodyAvailable(); EnsureBodyAvailable();
return BodyInternal; return BodyInternal!;
} }
internal set internal set
...@@ -233,7 +232,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -233,7 +232,7 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal byte[] CompressBodyAndUpdateContentLength() internal byte[]? CompressBodyAndUpdateContentLength()
{ {
if (!IsBodyRead && BodyInternal == null) if (!IsBodyRead && BodyInternal == null)
{ {
...@@ -241,7 +240,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -241,7 +240,7 @@ namespace Titanium.Web.Proxy.Http
} }
bool isChunked = IsChunked; bool isChunked = IsChunked;
string contentEncoding = ContentEncoding; string? contentEncoding = ContentEncoding;
if (HasBody) if (HasBody)
{ {
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
using System.ComponentModel; using System.ComponentModel;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
...@@ -36,7 +36,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -36,7 +36,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Response Status description. /// Response Status description.
/// </summary> /// </summary>
public string StatusDescription { get; set; } public string StatusDescription { get; set; } = string.Empty;
/// <summary> /// <summary>
/// Has response body? /// Has response body?
...@@ -78,7 +78,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -78,7 +78,7 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Connection); string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Connection);
if (headerValue != null) if (headerValue != null)
{ {
...@@ -99,15 +99,10 @@ namespace Titanium.Web.Proxy.Http ...@@ -99,15 +99,10 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var sb = new StringBuilder(); var headerBuilder = new HeaderBuilder();
sb.Append($"{CreateResponseLine(HttpVersion, StatusCode, StatusDescription)}{ProxyConstants.NewLine}"); headerBuilder.WriteResponseLine(HttpVersion, StatusCode, StatusDescription);
foreach (var header in Headers) headerBuilder.WriteHeaders(Headers);
{ return HttpHelper.HeaderEncoding.GetString(headerBuilder.GetBytes());
sb.Append($"{header.ToString()}{ProxyConstants.NewLine}");
}
sb.Append(ProxyConstants.NewLine);
return sb.ToString();
} }
} }
...@@ -126,30 +121,42 @@ namespace Titanium.Web.Proxy.Http ...@@ -126,30 +121,42 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal static string CreateResponseLine(Version version, int statusCode, string statusDescription)
{
return $"HTTP/{version.Major}.{version.Minor} {statusCode} {statusDescription}";
}
internal static void ParseResponseLine(string httpStatus, out Version version, out int statusCode, internal static void ParseResponseLine(string httpStatus, out Version version, out int statusCode,
out string statusDescription) out string statusDescription)
{ {
var httpResult = httpStatus.Split(ProxyConstants.SpaceSplit, 3); int firstSpace = httpStatus.IndexOf(' ');
if (httpResult.Length <= 1) if (firstSpace == -1)
{ {
throw new Exception("Invalid HTTP status line: " + httpStatus); throw new Exception("Invalid HTTP status line: " + httpStatus);
} }
string httpVersion = httpResult[0]; var httpVersion = httpStatus.AsSpan(0, firstSpace);
version = HttpHeader.Version11; version = HttpHeader.Version11;
if (httpVersion.EqualsIgnoreCase("HTTP/1.0")) if (httpVersion.EqualsIgnoreCase("HTTP/1.0".AsSpan()))
{ {
version = HttpHeader.Version10; version = HttpHeader.Version10;
} }
statusCode = int.Parse(httpResult[1]); int secondSpace = httpStatus.IndexOf(' ', firstSpace + 1);
statusDescription = httpResult.Length > 2 ? httpResult[2] : string.Empty; if (secondSpace != -1)
{
#if NETSTANDARD2_1
statusCode = int.Parse(httpStatus.AsSpan(firstSpace + 1, secondSpace - firstSpace - 1));
#else
statusCode = int.Parse(httpStatus.AsSpan(firstSpace + 1, secondSpace - firstSpace - 1).ToString());
#endif
statusDescription = httpStatus.AsSpan(secondSpace + 1).ToString();
}
else
{
#if NETSTANDARD2_1
statusCode = int.Parse(httpStatus.AsSpan(firstSpace + 1));
#else
statusCode = int.Parse(httpStatus.AsSpan(firstSpace + 1).ToString());
#endif
statusDescription = string.Empty;
}
} }
} }
} }
using System.Net; using System.Net;
using System.Web;
namespace Titanium.Web.Proxy.Http.Responses namespace Titanium.Web.Proxy.Http.Responses
{ {
...@@ -15,7 +14,7 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -15,7 +14,7 @@ namespace Titanium.Web.Proxy.Http.Responses
public GenericResponse(HttpStatusCode status) public GenericResponse(HttpStatusCode status)
{ {
StatusCode = (int)status; StatusCode = (int)status;
StatusDescription = Get(StatusCode); StatusDescription = Get(StatusCode) ?? string.Empty;
} }
/// <summary> /// <summary>
...@@ -29,7 +28,7 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -29,7 +28,7 @@ namespace Titanium.Web.Proxy.Http.Responses
StatusDescription = statusDescription; StatusDescription = statusDescription;
} }
internal static string Get(int code) internal static string? Get(int code)
{ {
switch (code) switch (code)
{ {
...@@ -98,6 +97,7 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -98,6 +97,7 @@ namespace Titanium.Web.Proxy.Http.Responses
case 510: return "Not Extended"; case 510: return "Not Extended";
case 511: return "Network Authentication Required"; case 511: return "Network Authentication Required";
} }
return null; return null;
} }
} }
......
...@@ -519,15 +519,14 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -519,15 +519,14 @@ namespace Titanium.Web.Proxy.Http2.Hpack
var headerField = StaticTable.Get(index); var headerField = StaticTable.Get(index);
return headerField; return headerField;
} }
else if (index - StaticTable.Length <= dynamicTable.Length())
if (index - StaticTable.Length <= dynamicTable.Length())
{ {
var headerField = dynamicTable.GetEntry(index - StaticTable.Length); var headerField = dynamicTable.GetEntry(index - StaticTable.Length);
return headerField; return headerField;
} }
else
{ throw new IOException("illegal index value (" + index + ")");
throw new IOException("illegal index value (" + index + ")");
}
} }
private void ReadName(int index) private void ReadName(int index)
......
...@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
public class DynamicTable public class DynamicTable
{ {
// a circular queue of header fields // a circular queue of header fields
HttpHeader[] headerFields; HttpHeader?[] headerFields;
int head; int head;
int tail; int tail;
...@@ -89,10 +89,10 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -89,10 +89,10 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int i = head - index; int i = head - index;
if (i < 0) if (i < 0)
{ {
return headerFields[i + headerFields.Length]; return headerFields[i + headerFields.Length]!;
} }
return headerFields[i]; return headerFields[i]!;
} }
/// <summary> /// <summary>
...@@ -128,7 +128,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -128,7 +128,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <summary> /// <summary>
/// Remove and return the oldest header field from the dynamic table. /// Remove and return the oldest header field from the dynamic table.
/// </summary> /// </summary>
public HttpHeader Remove() public HttpHeader? Remove()
{ {
var removed = headerFields[tail]; var removed = headerFields[tail];
if (removed == null) if (removed == null)
...@@ -218,8 +218,8 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -218,8 +218,8 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int cursor = tail; int cursor = tail;
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
{ {
var entry = headerFields[cursor++]; var entry = headerFields![cursor++];
tmp[i] = entry; tmp[i] = entry!;
if (cursor == headerFields.Length) if (cursor == headerFields.Length)
{ {
cursor = 0; cursor = 0;
......
...@@ -27,7 +27,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -27,7 +27,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
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, string.Empty, string.Empty, int.MaxValue, null);
private int size; private int size;
...@@ -299,7 +299,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -299,7 +299,7 @@ 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(string name, string value)
{ {
if (length() == 0 || name == null || value == null) if (length() == 0 || name == null || value == null)
{ {
...@@ -400,7 +400,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -400,7 +400,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <summary> /// <summary>
/// Remove and return the oldest header field from the dynamic table. /// Remove and return the oldest header field from the dynamic table.
/// </summary> /// </summary>
private HttpHeader remove() private HttpHeader? remove()
{ {
if (size == 0) if (size == 0)
{ {
...@@ -423,7 +423,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -423,7 +423,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
} }
else else
{ {
prev.Next = next; prev!.Next = next;
} }
eldest.Remove(); eldest.Remove();
...@@ -500,7 +500,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -500,7 +500,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
public HeaderEntry After { get; set; } public HeaderEntry After { get; set; }
// These fields comprise the chained list for header fields with the same hash. // These fields comprise the chained list for header fields with the same hash.
public HeaderEntry Next { get; set; } public HeaderEntry? Next { get; set; }
public int Hash { get; } public int Hash { get; }
...@@ -514,7 +514,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -514,7 +514,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, string name, string value, int index, HeaderEntry? next) : base(name, value, true)
{ {
Index = index; Index = index;
Hash = hash; Hash = hash;
......
...@@ -69,7 +69,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -69,7 +69,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
while (bits >= 8) while (bits >= 8)
{ {
int c = (current >> (bits - 8)) & 0xFF; int c = (current >> (bits - 8)) & 0xFF;
node = node.Children[c]; node = node.Children![c];
bits -= node.Bits; bits -= node.Bits;
if (node.IsTerminal) if (node.IsTerminal)
{ {
...@@ -87,7 +87,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -87,7 +87,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
while (bits > 0) while (bits > 0)
{ {
int c = (current << (8 - bits)) & 0xFF; int c = (current << (8 - bits)) & 0xFF;
node = node.Children[c]; node = node.Children![c];
if (node.IsTerminal && node.Bits <= bits) if (node.IsTerminal && node.Bits <= bits)
{ {
bits -= node.Bits; bits -= node.Bits;
...@@ -121,7 +121,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -121,7 +121,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
public int Bits { get; } public int Bits { get; }
// internal nodes have children // internal nodes have children
public Node[] Children { get; } public Node[]? Children { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HuffmanDecoder"/> class. /// Initializes a new instance of the <see cref="HuffmanDecoder"/> class.
...@@ -173,7 +173,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -173,7 +173,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
length -= 8; length -= 8;
int i = (code >> length) & 0xFF; int i = (code >> length) & 0xFF;
if (current.Children[i] == null) if (current.Children![i] == null)
{ {
current.Children[i] = new Node(); current.Children[i] = new Node();
} }
...@@ -187,7 +187,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -187,7 +187,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int end = 1 << shift; int end = 1 << shift;
for (int i = start; i < start + end; i++) for (int i = start; i < start + end; i++)
{ {
current.Children[i] = terminal; current.Children![i] = terminal;
} }
} }
} }
......
...@@ -5,6 +5,7 @@ using System.Collections.Generic; ...@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Compression; using Titanium.Web.Proxy.Compression;
...@@ -12,11 +13,15 @@ using Titanium.Web.Proxy.EventArguments; ...@@ -12,11 +13,15 @@ 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 Decoder = Titanium.Web.Proxy.Http2.Hpack.Decoder;
using Encoder = Titanium.Web.Proxy.Http2.Hpack.Encoder;
namespace Titanium.Web.Proxy.Http2 namespace Titanium.Web.Proxy.Http2
{ {
internal class Http2Helper internal class Http2Helper
{ {
public static readonly byte[] ConnectionPreface = Encoding.ASCII.GetBytes("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
/// <summary> /// <summary>
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers /// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers
/// as prefix /// as prefix
...@@ -59,11 +64,11 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -59,11 +64,11 @@ namespace Titanium.Web.Proxy.Http2
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
int headerTableSize = 0; int headerTableSize = 0;
Decoder decoder = null; Decoder? decoder = null;
var frameHeader = new Http2FrameHeader(); var frameHeader = new Http2FrameHeader();
frameHeader.Buffer = new byte[9]; frameHeader.Buffer = new byte[9];
byte[] buffer = null; byte[]? buffer = null;
while (true) while (true)
{ {
var frameHeaderBuffer = frameHeader.Buffer; var frameHeaderBuffer = frameHeader.Buffer;
...@@ -98,8 +103,8 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -98,8 +103,8 @@ namespace Titanium.Web.Proxy.Http2
bool sendPacket = true; bool sendPacket = true;
bool endStream = false; bool endStream = false;
SessionEventArgs args = null; SessionEventArgs? args = null;
RequestResponseBase rr = null; RequestResponseBase? rr = null;
if (type == Http2FrameType.Data || type == Http2FrameType.Headers/* || type == Http2FrameType.PushPromise*/) if (type == Http2FrameType.Data || type == Http2FrameType.Headers/* || type == Http2FrameType.PushPromise*/)
{ {
if (!sessions.TryGetValue(streamId, out args)) if (!sessions.TryGetValue(streamId, out args))
...@@ -156,7 +161,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -156,7 +161,7 @@ namespace Titanium.Web.Proxy.Http2
length -= buffer[0]; length -= buffer[0];
} }
data.Write(buffer, offset, length); data!.Write(buffer, offset, length);
} }
} }
else if (type == Http2FrameType.Headers/* || type == Http2FrameType.PushPromise*/) else if (type == Http2FrameType.Headers/* || type == Http2FrameType.PushPromise*/)
...@@ -247,9 +252,16 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -247,9 +252,16 @@ namespace Titanium.Web.Proxy.Http2
if (rr is Request request) if (rr is Request request)
{ {
string? method = headerListener.Method;
string? path = headerListener.Path;
if (method == null || path == null)
{
throw new Exception("HTTP/2 Missing method or path");
}
request.HttpVersion = HttpVersion.Version20; request.HttpVersion = HttpVersion.Version20;
request.Method = headerListener.Method; request.Method = method;
request.OriginalUrl = headerListener.Path; request.OriginalUrl = path;
request.RequestUri = headerListener.GetUri(); request.RequestUri = headerListener.GetUri();
} }
...@@ -259,6 +271,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -259,6 +271,7 @@ namespace Titanium.Web.Proxy.Http2
response.HttpVersion = HttpVersion.Version20; response.HttpVersion = HttpVersion.Version20;
int.TryParse(headerListener.Status, out int statusCode); int.TryParse(headerListener.Status, out int statusCode);
response.StatusCode = statusCode; response.StatusCode = statusCode;
response.StatusDescription = string.Empty;
} }
} }
catch (Exception ex) catch (Exception ex)
...@@ -348,12 +361,12 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -348,12 +361,12 @@ namespace Titanium.Web.Proxy.Http2
} }
} }
if (endStream && rr.ReadHttp2BodyTaskCompletionSource != null) if (endStream && rr!.ReadHttp2BodyTaskCompletionSource != null)
{ {
if (!rr.BodyAvailable) if (!rr.BodyAvailable)
{ {
var data = rr.Http2BodyData; var data = rr.Http2BodyData;
var body = data.ToArray(); var body = data!.ToArray();
if (rr.ContentEncoding != null) if (rr.ContentEncoding != null)
{ {
...@@ -389,7 +402,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -389,7 +402,7 @@ namespace Titanium.Web.Proxy.Http2
await rr.Http2BeforeHandlerTask; await rr.Http2BeforeHandlerTask;
} }
if (args.IsPromise) if (args!.IsPromise)
{ {
breakpoint(); breakpoint();
} }
...@@ -501,7 +514,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -501,7 +514,7 @@ namespace Titanium.Web.Proxy.Http2
if (rr.HasBody && rr.IsBodyRead) if (rr.HasBody && rr.IsBodyRead)
{ {
int pos = 0; int pos = 0;
while (pos < body.Length) while (pos < body!.Length)
{ {
int bodyFrameLength = Math.Min(buffer.Length, body.Length - pos); int bodyFrameLength = Math.Min(buffer.Length, body.Length - pos);
Buffer.BlockCopy(body, pos, buffer, 0, bodyFrameLength); Buffer.BlockCopy(body, pos, buffer, 0, bodyFrameLength);
...@@ -554,15 +567,15 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -554,15 +567,15 @@ namespace Titanium.Web.Proxy.Http2
{ {
private readonly Action<string, string> addHeaderFunc; private readonly Action<string, string> addHeaderFunc;
public string Method { get; private set; } public string? Method { get; private set; }
public string Status { get; private set; } public string? Status { get; private set; }
private string authority; private string? authority;
private string scheme; private string? scheme;
public string Path { get; private set; } public string? Path { get; private set; }
public MyHeaderListener(Action<string, string> addHeaderFunc) public MyHeaderListener(Action<string, string> addHeaderFunc)
{ {
......
using System.Net; using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -33,13 +32,13 @@ namespace Titanium.Web.Proxy.Models ...@@ -33,13 +32,13 @@ namespace Titanium.Web.Proxy.Models
/// Set the <see cref="TunnelConnectSessionEventArgs.DecryptSsl" /> property to false if this HTTP connect request /// Set the <see cref="TunnelConnectSessionEventArgs.DecryptSsl" /> property to false if this HTTP connect request
/// shouldn't be decrypted and instead be relayed. /// shouldn't be decrypted and instead be relayed.
/// </summary> /// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectRequest; public event AsyncEventHandler<TunnelConnectSessionEventArgs>? BeforeTunnelConnectRequest;
/// <summary> /// <summary>
/// Intercept tunnel connect response. /// Intercept tunnel connect response.
/// Valid only for explicit endpoints. /// Valid only for explicit endpoints.
/// </summary> /// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectResponse; public event AsyncEventHandler<TunnelConnectSessionEventArgs>? BeforeTunnelConnectResponse;
internal async Task InvokeBeforeTunnelConnectRequest(ProxyServer proxyServer, internal async Task InvokeBeforeTunnelConnectRequest(ProxyServer proxyServer,
TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc) TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc)
......
using System; using System;
using System.Net;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
...@@ -16,13 +17,21 @@ namespace Titanium.Web.Proxy.Models ...@@ -16,13 +17,21 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public const int HttpHeaderOverhead = 32; public const int HttpHeaderOverhead = 32;
internal static readonly Version VersionUnknown = new Version(0, 0); #if NETSTANDARD2_1
internal static Version VersionUnknown => HttpVersion.Unknown;
#else
internal static Version VersionUnknown { get; } = new Version(0, 0);
#endif
internal static readonly Version Version10 = new Version(1, 0); internal static Version Version10 => HttpVersion.Version10;
internal static readonly Version Version11 = new Version(1, 1); internal static Version Version11 => HttpVersion.Version11;
internal static readonly Version Version20 = new Version(2, 0); #if NETSTANDARD2_1
internal static Version Version20 => HttpVersion.Version20;
#else
internal static Version Version20 { get; } = new Version(2, 0);
#endif
internal static readonly HttpHeader ProxyConnectionKeepAlive = new HttpHeader("Proxy-Connection", "keep-alive"); internal static readonly HttpHeader ProxyConnectionKeepAlive = new HttpHeader("Proxy-Connection", "keep-alive");
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
/// <summary> /// <summary>
/// An optional continuation token to return to the caller if set /// An optional continuation token to return to the caller if set
/// </summary> /// </summary>
public string Continuation { get; set; } public string? Continuation { get; set; }
public static ProxyAuthenticationContext Failed() public static ProxyAuthenticationContext Failed()
{ {
......
...@@ -32,7 +32,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -32,7 +32,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// Before Ssl authentication this event is fired. /// Before Ssl authentication this event is fired.
/// </summary> /// </summary>
public event AsyncEventHandler<BeforeSslAuthenticateEventArgs> BeforeSslAuthenticate; public event AsyncEventHandler<BeforeSslAuthenticateEventArgs>? BeforeSslAuthenticate;
internal async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer, internal async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer,
BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc) BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc)
......
using System; using System;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
......
...@@ -48,9 +48,9 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -48,9 +48,9 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="isRoot">if set to <c>true</c> [is root].</param> /// <param name="isRoot">if set to <c>true</c> [is root].</param>
/// <param name="signingCert">The signing cert.</param> /// <param name="signingCert">The signing cert.</param>
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
public X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert = null) public X509Certificate2 MakeCertificate(string sSubjectCn, X509Certificate2? signingCert = null)
{ {
return makeCertificateInternal(sSubjectCn, isRoot, true, signingCert); return makeCertificateInternal(sSubjectCn, true, signingCert);
} }
/// <summary> /// <summary>
...@@ -66,12 +66,12 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -66,12 +66,12 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="hostName">The host name</param> /// <param name="hostName">The host name</param>
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
/// <exception cref="PemException">Malformed sequence in RSA private key</exception> /// <exception cref="PemException">Malformed sequence in RSA private key</exception>
private static X509Certificate2 generateCertificate(string hostName, private static X509Certificate2 generateCertificate(string? hostName,
string subjectName, string subjectName,
string issuerName, DateTime validFrom, string issuerName, DateTime validFrom,
DateTime validTo, int keyStrength = 2048, DateTime validTo, int keyStrength = 2048,
string signatureAlgorithm = "SHA256WithRSA", string signatureAlgorithm = "SHA256WithRSA",
AsymmetricKeyParameter issuerPrivateKey = null) AsymmetricKeyParameter? issuerPrivateKey = null)
{ {
// Generating Random Numbers // Generating Random Numbers
var randomGenerator = new CryptoApiRandomGenerator(); var randomGenerator = new CryptoApiRandomGenerator();
...@@ -162,11 +162,11 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -162,11 +162,11 @@ namespace Titanium.Web.Proxy.Network.Certificate
private static X509Certificate2 withPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey) private static X509Certificate2 withPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey)
{ {
const string password = "password"; const string password = "password";
Pkcs12Store store = null; Pkcs12Store store;
if(RunTime.IsRunningOnMono) if(RunTime.IsRunningOnMono)
{ {
Pkcs12StoreBuilder builder = new Pkcs12StoreBuilder(); var builder = new Pkcs12StoreBuilder();
builder.SetUseDerEncoding(true); builder.SetUseDerEncoding(true);
store = builder.Build(); store = builder.Build();
} }
...@@ -190,7 +190,6 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -190,7 +190,6 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <summary> /// <summary>
/// Makes the certificate internal. /// Makes the certificate internal.
/// </summary> /// </summary>
/// <param name="isRoot">if set to <c>true</c> [is root].</param>
/// <param name="hostName">hostname for certificate</param> /// <param name="hostName">hostname for certificate</param>
/// <param name="subjectName">The full subject.</param> /// <param name="subjectName">The full subject.</param>
/// <param name="validFrom">The valid from.</param> /// <param name="validFrom">The valid from.</param>
...@@ -201,18 +200,10 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -201,18 +200,10 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// You must specify a Signing Certificate if and only if you are not creating a /// You must specify a Signing Certificate if and only if you are not creating a
/// root. /// root.
/// </exception> /// </exception>
private X509Certificate2 makeCertificateInternal(bool isRoot, private X509Certificate2 makeCertificateInternal(string hostName, string subjectName,
string hostName, string subjectName, DateTime validFrom, DateTime validTo, X509Certificate2? signingCertificate)
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{ {
if (isRoot != (null == signingCertificate)) if (signingCertificate == null)
{
throw new ArgumentException(
"You must specify a Signing Certificate if and only if you are not creating a root.",
nameof(signingCertificate));
}
if (isRoot)
{ {
return generateCertificate(null, subjectName, subjectName, validFrom, validTo); return generateCertificate(null, subjectName, subjectName, validFrom, validTo);
} }
...@@ -226,18 +217,17 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -226,18 +217,17 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// Makes the certificate internal. /// Makes the certificate internal.
/// </summary> /// </summary>
/// <param name="subject">The s subject cn.</param> /// <param name="subject">The s subject cn.</param>
/// <param name="isRoot">if set to <c>true</c> [is root].</param>
/// <param name="switchToMtaIfNeeded">if set to <c>true</c> [switch to MTA if needed].</param> /// <param name="switchToMtaIfNeeded">if set to <c>true</c> [switch to MTA if needed].</param>
/// <param name="signingCert">The signing cert.</param> /// <param name="signingCert">The signing cert.</param>
/// <param name="cancellationToken">Task cancellation token</param> /// <param name="cancellationToken">Task cancellation token</param>
/// <returns>X509Certificate2.</returns> /// <returns>X509Certificate2.</returns>
private X509Certificate2 makeCertificateInternal(string subject, bool isRoot, private X509Certificate2 makeCertificateInternal(string subject,
bool switchToMtaIfNeeded, X509Certificate2 signingCert = null, bool switchToMtaIfNeeded, X509Certificate2? signingCert = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return makeCertificateInternal(isRoot, subject, $"CN={subject}", return makeCertificateInternal(subject, $"CN={subject}",
DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays),
isRoot ? null : signingCert); signingCert);
} }
} }
} }
...@@ -7,6 +7,6 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -7,6 +7,6 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// </summary> /// </summary>
internal interface ICertificateMaker internal interface ICertificateMaker
{ {
X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert); X509Certificate2 MakeCertificate(string sSubjectCn, X509Certificate2? signingCert);
} }
} }
...@@ -75,18 +75,18 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -75,18 +75,18 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <summary> /// <summary>
/// Make certificate. /// Make certificate.
/// </summary> /// </summary>
public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot, X509Certificate2 signingCert = null) public X509Certificate2 MakeCertificate(string sSubjectCN, X509Certificate2? signingCert = null)
{ {
return makeCertificate(sSubjectCN, isRoot, true, signingCert); return makeCertificate(sSubjectCN, true, signingCert);
} }
private X509Certificate2 makeCertificate(string sSubjectCN, bool isRoot, private X509Certificate2 makeCertificate(string sSubjectCN,
bool switchToMTAIfNeeded, X509Certificate2 signingCert = null, bool switchToMTAIfNeeded, X509Certificate2? signingCertificate = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA) if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{ {
return Task.Run(() => makeCertificate(sSubjectCN, isRoot, false, signingCert), return Task.Run(() => makeCertificate(sSubjectCN, false, signingCertificate),
cancellationToken).Result; cancellationToken).Result;
} }
...@@ -107,37 +107,30 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -107,37 +107,30 @@ namespace Titanium.Web.Proxy.Network.Certificate
var now = DateTime.Now; var now = DateTime.Now;
var graceTime = now.AddDays(graceDays); var graceTime = now.AddDays(graceDays);
var certificate = makeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, hashAlgo, graceTime, var certificate = makeCertificate(sSubjectCN, fullSubject, keyLength, hashAlgo, graceTime,
now.AddDays(validDays), isRoot ? null : signingCert); now.AddDays(validDays), signingCertificate);
return certificate; return certificate;
} }
private X509Certificate2 makeCertificate(bool isRoot, string subject, string fullSubject, private X509Certificate2 makeCertificate(string subject, string fullSubject,
int privateKeyLength, string hashAlg, DateTime validFrom, DateTime validTo, int privateKeyLength, string hashAlg, DateTime validFrom, DateTime validTo,
X509Certificate2 signingCertificate) X509Certificate2? signingCertificate)
{ {
if (isRoot != (null == signingCertificate))
{
throw new ArgumentException(
"You must specify a Signing Certificate if and only if you are not creating a root.",
nameof(isRoot));
}
var x500CertDN = Activator.CreateInstance(typeX500DN); var x500CertDN = Activator.CreateInstance(typeX500DN);
var typeValue = new object[] { fullSubject, 0 }; var typeValue = new object[] { fullSubject, 0 };
typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500CertDN, typeValue); typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500CertDN, typeValue);
var x500RootCertDN = Activator.CreateInstance(typeX500DN); var x500RootCertDN = Activator.CreateInstance(typeX500DN);
if (!isRoot) if (signingCertificate != null)
{ {
typeValue[0] = signingCertificate.Subject; typeValue[0] = signingCertificate.Subject;
} }
typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500RootCertDN, typeValue); typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500RootCertDN, typeValue);
object sharedPrivateKey = null; object? sharedPrivateKey = null;
if (!isRoot) if (signingCertificate != null)
{ {
sharedPrivateKey = this.sharedPrivateKey; sharedPrivateKey = this.sharedPrivateKey;
} }
...@@ -151,11 +144,11 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -151,11 +144,11 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeValue[0] = 2; typeValue[0] = 2;
typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey,
typeValue); typeValue);
typeValue = new object[] { isRoot ? 2 : 1 }; typeValue = new object[] { signingCertificate == null ? 2 : 1 };
typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey,
typeValue); typeValue);
if (!isRoot) if (signingCertificate != null)
{ {
typeValue = new object[] { 176 }; typeValue = new object[] { 176 };
typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey,
...@@ -167,7 +160,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -167,7 +160,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeValue); typeValue);
typeX509PrivateKey.InvokeMember("Create", BindingFlags.InvokeMethod, null, sharedPrivateKey, null); typeX509PrivateKey.InvokeMember("Create", BindingFlags.InvokeMethod, null, sharedPrivateKey, null);
if (!isRoot) if (signingCertificate != null)
{ {
this.sharedPrivateKey = sharedPrivateKey; this.sharedPrivateKey = sharedPrivateKey;
} }
...@@ -210,7 +203,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -210,7 +203,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, requestCert, null); typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, requestCert, null);
typeValue = new object[1]; typeValue = new object[1];
if (!isRoot) if (signingCertificate != null)
{ {
typeValue[0] = kuExt; typeValue[0] = kuExt;
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue);
...@@ -219,7 +212,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -219,7 +212,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeValue[0] = ekuExt; typeValue[0] = ekuExt;
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue);
if (!isRoot) if (signingCertificate != null)
{ {
// add alternative names // add alternative names
// https://forums.iis.net/t/1180823.aspx // https://forums.iis.net/t/1180823.aspx
...@@ -244,7 +237,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -244,7 +237,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue);
} }
if (!isRoot) if (signingCertificate != null)
{ {
var signerCertificate = Activator.CreateInstance(typeSignerCertificate); var signerCertificate = Activator.CreateInstance(typeSignerCertificate);
...@@ -281,7 +274,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -281,7 +274,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, x509Enrollment,
typeValue); typeValue);
if (isRoot) if (signingCertificate == null)
{ {
typeValue[0] = fullSubject; typeValue[0] = fullSubject;
typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null, typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null,
...@@ -296,7 +289,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -296,7 +289,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment, typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment,
typeValue); typeValue);
typeValue = new object[] { null, 0, 1 }; typeValue = new object[] { null!, 0, 1 };
string empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, string empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null,
x509Enrollment, typeValue); x509Enrollment, typeValue);
......
...@@ -52,8 +52,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -52,8 +52,8 @@ namespace Titanium.Web.Proxy.Network
/// Useful to prevent multiple threads working on same certificate generation /// Useful to prevent multiple threads working on same certificate generation
/// when burst certificate generation requests happen for same certificate. /// when burst certificate generation requests happen for same certificate.
/// </summary> /// </summary>
private readonly ConcurrentDictionary<string, Task<X509Certificate2>> pendingCertificateCreationTasks private readonly ConcurrentDictionary<string, Task<X509Certificate2?>> pendingCertificateCreationTasks
= new ConcurrentDictionary<string, Task<X509Certificate2>>(); = new ConcurrentDictionary<string, Task<X509Certificate2?>>();
private readonly CancellationTokenSource clearCertificatesTokenSource private readonly CancellationTokenSource clearCertificatesTokenSource
= new CancellationTokenSource(); = new CancellationTokenSource();
...@@ -66,7 +66,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -66,7 +66,7 @@ namespace Titanium.Web.Proxy.Network
private string issuer; private string issuer;
private X509Certificate2 rootCertificate; private X509Certificate2? rootCertificate;
private string rootCertificateName; private string rootCertificateName;
...@@ -87,7 +87,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -87,7 +87,7 @@ namespace Titanium.Web.Proxy.Network
/// prompting for UAC if required? /// prompting for UAC if required?
/// </param> /// </param>
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
internal CertificateManager(string rootCertificateName, string rootCertificateIssuerName, internal CertificateManager(string? rootCertificateName, string? rootCertificateIssuerName,
bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin, bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
...@@ -156,7 +156,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -156,7 +156,7 @@ namespace Titanium.Web.Proxy.Network
if (value != engine) if (value != engine)
{ {
certEngine = null; certEngine = null!;
engine = value; engine = value;
} }
...@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// The root certificate. /// The root certificate.
/// </summary> /// </summary>
public X509Certificate2 RootCertificate public X509Certificate2? RootCertificate
{ {
get => rootCertificate; get => rootCertificate;
set set
...@@ -268,6 +268,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -268,6 +268,11 @@ namespace Titanium.Web.Proxy.Network
/// <returns></returns> /// <returns></returns>
private bool rootCertificateInstalled(StoreLocation storeLocation) private bool rootCertificateInstalled(StoreLocation storeLocation)
{ {
if (RootCertificate == null)
{
throw new Exception("Root certificate is null.");
}
string value = $"{RootCertificate.Issuer}"; string value = $"{RootCertificate.Issuer}";
return findCertificates(StoreName.Root, storeLocation, value).Count > 0 return findCertificates(StoreName.Root, storeLocation, value).Count > 0
&& (CertificateEngine != CertificateEngine.DefaultWindows && (CertificateEngine != CertificateEngine.DefaultWindows
...@@ -298,8 +303,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -298,8 +303,7 @@ namespace Titanium.Web.Proxy.Network
{ {
if (RootCertificate == null) if (RootCertificate == null)
{ {
ExceptionFunc(new Exception("Could not install certificate as it is null or empty.")); throw new Exception("Could not install certificate as it is null or empty.");
return;
} }
var x509Store = new X509Store(storeName, storeLocation); var x509Store = new X509Store(storeName, storeLocation);
...@@ -330,8 +334,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -330,8 +334,7 @@ namespace Titanium.Web.Proxy.Network
/// <param name="storeName"></param> /// <param name="storeName"></param>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <param name="certificate"></param> /// <param name="certificate"></param>
private void uninstallCertificate(StoreName storeName, StoreLocation storeLocation, private void uninstallCertificate(StoreName storeName, StoreLocation storeLocation, X509Certificate2? certificate)
X509Certificate2 certificate)
{ {
if (certificate == null) if (certificate == null)
{ {
...@@ -361,12 +364,19 @@ namespace Titanium.Web.Proxy.Network ...@@ -361,12 +364,19 @@ namespace Titanium.Web.Proxy.Network
private X509Certificate2 makeCertificate(string certificateName, bool isRootCertificate) private X509Certificate2 makeCertificate(string certificateName, bool isRootCertificate)
{ {
//if (isRoot != (null == signingCertificate))
//{
// throw new ArgumentException(
// "You must specify a Signing Certificate if and only if you are not creating a root.",
// nameof(signingCertificate));
//}
if (!isRootCertificate && RootCertificate == null) if (!isRootCertificate && RootCertificate == null)
{ {
CreateRootCertificate(); CreateRootCertificate();
} }
var certificate = certEngine.MakeCertificate(certificateName, isRootCertificate, RootCertificate); var certificate = certEngine.MakeCertificate(certificateName, isRootCertificate ? null : RootCertificate);
if (CertificateEngine == CertificateEngine.DefaultWindows) if (CertificateEngine == CertificateEngine.DefaultWindows)
{ {
...@@ -382,9 +392,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -382,9 +392,9 @@ namespace Titanium.Web.Proxy.Network
/// <param name="certificateName"></param> /// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param> /// <param name="isRootCertificate"></param>
/// <returns></returns> /// <returns></returns>
internal X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) internal X509Certificate2? CreateCertificate(string certificateName, bool isRootCertificate)
{ {
X509Certificate2 certificate; X509Certificate2? certificate;
try try
{ {
if (!isRootCertificate && SaveFakeCertificates) if (!isRootCertificate && SaveFakeCertificates)
...@@ -436,7 +446,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -436,7 +446,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
/// <param name="certificateName"></param> /// <param name="certificateName"></param>
/// <returns></returns> /// <returns></returns>
public async Task<X509Certificate2> CreateServerCertificate(string certificateName) public async Task<X509Certificate2?> CreateServerCertificate(string certificateName)
{ {
// check in cache first // check in cache first
if (cachedCertificates.TryGetValue(certificateName, out var cached)) if (cachedCertificates.TryGetValue(certificateName, out var cached))
...@@ -589,7 +599,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -589,7 +599,7 @@ namespace Titanium.Web.Proxy.Network
/// Loads root certificate from current executing assembly location with expected name rootCert.pfx. /// Loads root certificate from current executing assembly location with expected name rootCert.pfx.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public X509Certificate2 LoadRootCertificate() public X509Certificate2? LoadRootCertificate()
{ {
try try
{ {
...@@ -671,7 +681,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -671,7 +681,7 @@ namespace Titanium.Web.Proxy.Network
installCertificate(StoreName.My, StoreLocation.CurrentUser); installCertificate(StoreName.My, StoreLocation.CurrentUser);
string pfxFileName = Path.GetTempFileName(); string pfxFileName = Path.GetTempFileName();
File.WriteAllBytes(pfxFileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword)); File.WriteAllBytes(pfxFileName, RootCertificate!.Export(X509ContentType.Pkcs12, PfxPassword));
// currentUser\Root, currentMachine\Personal & currentMachine\Root // currentUser\Root, currentMachine\Personal & currentMachine\Root
var info = new ProcessStartInfo var info = new ProcessStartInfo
......
...@@ -11,10 +11,10 @@ namespace Titanium.Web.Proxy.Network ...@@ -11,10 +11,10 @@ namespace Titanium.Web.Proxy.Network
private const string defaultCertificateDirectoryName = "crts"; private const string defaultCertificateDirectoryName = "crts";
private const string defaultCertificateFileExtension = ".pfx"; private const string defaultCertificateFileExtension = ".pfx";
private const string defaultRootCertificateFileName = "rootCert" + defaultCertificateFileExtension; private const string defaultRootCertificateFileName = "rootCert" + defaultCertificateFileExtension;
private string rootCertificatePath; private string? rootCertificatePath;
private string certificatePath; private string? certificatePath;
public X509Certificate2 LoadRootCertificate(string pathOrName, string password, X509KeyStorageFlags storageFlags) public X509Certificate2? LoadRootCertificate(string pathOrName, string password, X509KeyStorageFlags storageFlags)
{ {
string path = getRootCertificatePath(pathOrName); string path = getRootCertificatePath(pathOrName);
return loadCertificate(path, password, storageFlags); return loadCertificate(path, password, storageFlags);
...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Network
} }
/// <inheritdoc /> /// <inheritdoc />
public X509Certificate2 LoadCertificate(string subjectName, X509KeyStorageFlags storageFlags) public X509Certificate2? LoadCertificate(string subjectName, X509KeyStorageFlags storageFlags)
{ {
string path = Path.Combine(getCertificatePath(), subjectName + defaultCertificateFileExtension); string path = Path.Combine(getCertificatePath(), subjectName + defaultCertificateFileExtension);
return loadCertificate(path, string.Empty, storageFlags); return loadCertificate(path, string.Empty, storageFlags);
...@@ -56,7 +56,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -56,7 +56,7 @@ namespace Titanium.Web.Proxy.Network
certificatePath = null; certificatePath = null;
} }
private X509Certificate2 loadCertificate(string path, string password, X509KeyStorageFlags storageFlags) private X509Certificate2? loadCertificate(string path, string password, X509KeyStorageFlags storageFlags)
{ {
byte[] exported; byte[] exported;
......
...@@ -7,7 +7,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -7,7 +7,7 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// Loads the root certificate from the storage. /// Loads the root certificate from the storage.
/// </summary> /// </summary>
X509Certificate2 LoadRootCertificate(string pathOrName, string password, X509KeyStorageFlags storageFlags); X509Certificate2? LoadRootCertificate(string pathOrName, string password, X509KeyStorageFlags storageFlags);
/// <summary> /// <summary>
/// Saves the root certificate to the storage. /// Saves the root certificate to the storage.
...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// Loads certificate from the storage. Returns true if certificate does not exist. /// Loads certificate from the storage. Returns true if certificate does not exist.
/// </summary> /// </summary>
X509Certificate2 LoadCertificate(string subjectName, X509KeyStorageFlags storageFlags); X509Certificate2? LoadCertificate(string subjectName, X509KeyStorageFlags storageFlags);
/// <summary> /// <summary>
/// Stores certificate into the storage. /// Stores certificate into the storage.
......
...@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Network
private readonly int retries; private readonly int retries;
private readonly TcpConnectionFactory tcpConnectionFactory; private readonly TcpConnectionFactory tcpConnectionFactory;
private TcpServerConnection currentConnection; private TcpServerConnection? currentConnection;
internal RetryPolicy(int retries, TcpConnectionFactory tcpConnectionFactory) internal RetryPolicy(int retries, TcpConnectionFactory tcpConnectionFactory)
{ {
...@@ -25,11 +25,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -25,11 +25,11 @@ namespace Titanium.Web.Proxy.Network
/// <param name="initialConnection">Initial Tcp connection to use.</param> /// <param name="initialConnection">Initial Tcp connection to use.</param>
/// <returns>Returns the latest connection used and the latest exception if any.</returns> /// <returns>Returns the latest connection used and the latest exception if any.</returns>
internal async Task<RetryResult> ExecuteAsync(Func<TcpServerConnection, Task<bool>> action, internal async Task<RetryResult> ExecuteAsync(Func<TcpServerConnection, Task<bool>> action,
Func<Task<TcpServerConnection>> generator, TcpServerConnection initialConnection) Func<Task<TcpServerConnection>> generator, TcpServerConnection? initialConnection)
{ {
currentConnection = initialConnection; currentConnection = initialConnection;
bool @continue = true; bool @continue = true;
Exception exception = null; Exception? exception = null;
var attempts = retries; var attempts = retries;
...@@ -79,12 +79,13 @@ namespace Titanium.Web.Proxy.Network ...@@ -79,12 +79,13 @@ namespace Titanium.Web.Proxy.Network
internal class RetryResult internal class RetryResult
{ {
internal bool IsSuccess => Exception == null; internal TcpServerConnection? LatestConnection { get; }
internal TcpServerConnection LatestConnection { get; }
internal Exception Exception { get; } internal Exception? Exception { get; }
internal bool Continue { get; } internal bool Continue { get; }
internal RetryResult(TcpServerConnection lastConnection, Exception exception, bool @continue) internal RetryResult(TcpServerConnection? lastConnection, Exception? exception, bool @continue)
{ {
LatestConnection = lastConnection; LatestConnection = lastConnection;
Exception = exception; Exception = exception;
......
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
#if NETSTANDARD2_1
using System.Net.Security; using System.Net.Security;
#endif
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication; using System.Security.Authentication;
using System.Threading.Tasks; using System.Threading.Tasks;
......
...@@ -47,8 +47,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -47,8 +47,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal ProxyServer Server { get; } internal ProxyServer Server { get; }
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.
...@@ -83,13 +83,13 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -83,13 +83,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal async Task<string> GetConnectionCacheKey(ProxyServer server, SessionEventArgsBase session, internal async Task<string> GetConnectionCacheKey(ProxyServer server, SessionEventArgsBase session,
SslApplicationProtocol applicationProtocol) SslApplicationProtocol applicationProtocol)
{ {
List<SslApplicationProtocol> applicationProtocols = null; List<SslApplicationProtocol>? applicationProtocols = null;
if (applicationProtocol != default) if (applicationProtocol != default)
{ {
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol }; applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
} }
ExternalProxy customUpStreamProxy = null; ExternalProxy? customUpStreamProxy = null;
bool isHttps = session.IsHttps; bool isHttps = session.IsHttps;
if (server.GetCustomUpStreamProxyFunc != null) if (server.GetCustomUpStreamProxyFunc != null)
...@@ -121,7 +121,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -121,7 +121,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect, internal Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect,
SslApplicationProtocol applicationProtocol, bool noCache, CancellationToken cancellationToken) SslApplicationProtocol applicationProtocol, bool noCache, CancellationToken cancellationToken)
{ {
List<SslApplicationProtocol> applicationProtocols = null; List<SslApplicationProtocol>? applicationProtocols = null;
if (applicationProtocol != default) if (applicationProtocol != default)
{ {
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol }; applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
...@@ -141,9 +141,9 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -141,9 +141,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <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>
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect, internal async Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect,
List<SslApplicationProtocol> applicationProtocols, bool noCache, CancellationToken cancellationToken) List<SslApplicationProtocol>? applicationProtocols, bool noCache, CancellationToken cancellationToken)
{ {
ExternalProxy customUpStreamProxy = null; ExternalProxy? customUpStreamProxy = null;
bool isHttps = session.IsHttps; bool isHttps = session.IsHttps;
if (server.GetCustomUpStreamProxyFunc != null) if (server.GetCustomUpStreamProxyFunc != null)
...@@ -179,11 +179,11 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -179,11 +179,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <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>
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; var sslProtocol = session?.ProxyClient.Connection.SslProtocol ?? SslProtocols.None;
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort, var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
isHttps, applicationProtocols, upStreamEndPoint, externalProxy); isHttps, applicationProtocols, upStreamEndPoint, externalProxy);
...@@ -234,8 +234,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -234,8 +234,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <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,
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.
...@@ -269,8 +269,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -269,8 +269,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
} }
} }
TcpClient tcpClient = null; TcpClient? tcpClient = null;
CustomBufferedStream stream = null; CustomBufferedStream? stream = null;
SslApplicationProtocol negotiatedApplicationProtocol = default; SslApplicationProtocol negotiatedApplicationProtocol = default;
...@@ -280,8 +280,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -280,8 +280,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
retry: retry:
try try
{ {
var hostname = useUpstreamProxy ? externalProxy.HostName : remoteHostName; var hostname = useUpstreamProxy ? externalProxy!.HostName : remoteHostName;
var port = useUpstreamProxy ? externalProxy.Port : remotePort; var 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)
...@@ -341,9 +341,9 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -341,9 +341,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
session.TimeLine["Connection Established"] = DateTime.Now; session.TimeLine["Connection Established"] = DateTime.Now;
} }
await proxyServer.InvokeConnectionCreateEvent(tcpClient, false); await proxyServer.InvokeConnectionCreateEvent(tcpClient!, false);
stream = new CustomBufferedStream(tcpClient.GetStream(), proxyServer.BufferPool); stream = new CustomBufferedStream(tcpClient!.GetStream(), proxyServer.BufferPool);
if (useUpstreamProxy && (isConnect || isHttps)) if (useUpstreamProxy && (isConnect || isHttps))
{ {
...@@ -356,7 +356,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -356,7 +356,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
connectRequest.Headers.AddHeader(KnownHeaders.Connection, KnownHeaders.ConnectionKeepAlive); connectRequest.Headers.AddHeader(KnownHeaders.Connection, KnownHeaders.ConnectionKeepAlive);
if (!string.IsNullOrEmpty(externalProxy.UserName) && externalProxy.Password != null) if (!string.IsNullOrEmpty(externalProxy!.UserName) && externalProxy.Password != null)
{ {
connectRequest.Headers.AddHeader(HttpHeader.ProxyConnectionKeepAlive); connectRequest.Headers.AddHeader(HttpHeader.ProxyConnectionKeepAlive);
connectRequest.Headers.AddHeader( connectRequest.Headers.AddHeader(
...@@ -388,7 +388,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -388,7 +388,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{ {
ApplicationProtocols = applicationProtocols, ApplicationProtocols = applicationProtocols,
TargetHost = remoteHostName, TargetHost = remoteHostName,
ClientCertificates = null, ClientCertificates = null!,
EnabledSslProtocols = enabledSslProtocols, EnabledSslProtocols = enabledSslProtocols,
CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation
}; };
...@@ -440,11 +440,6 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -440,11 +440,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="close">Should we just close the connection instead of reusing?</param> /// <param name="close">Should we just close the connection instead of reusing?</param>
internal async Task Release(TcpServerConnection connection, bool close = false) internal async Task Release(TcpServerConnection connection, bool close = false)
{ {
if (connection == null)
{
return;
}
if (close || connection.IsWinAuthenticated || !Server.EnableConnectionPool || connection.IsClosed) if (close || connection.IsWinAuthenticated || !Server.EnableConnectionPool || connection.IsClosed)
{ {
disposalBag.Add(connection); disposalBag.Add(connection);
...@@ -487,11 +482,11 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -487,11 +482,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
} }
} }
internal async Task Release(Task<TcpServerConnection> connectionCreateTask, bool closeServerConnection) internal async Task Release(Task<TcpServerConnection>? connectionCreateTask, bool closeServerConnection)
{ {
if (connectionCreateTask != null) if (connectionCreateTask != null)
{ {
TcpServerConnection connection = null; TcpServerConnection? connection = null;
try try
{ {
connection = await connectionCreateTask; connection = await connectionCreateTask;
...@@ -499,7 +494,10 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -499,7 +494,10 @@ namespace Titanium.Web.Proxy.Network.Tcp
catch { } catch { }
finally finally
{ {
await Release(connection, closeServerConnection); if (connection != null)
{
await Release(connection, closeServerConnection);
}
} }
} }
} }
......
using System; using System;
using System.Net; using System.Net;
#if NETSTANDARD2_1
using System.Net.Security; using System.Net.Security;
#endif
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -29,7 +27,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -29,7 +27,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal bool IsClosed => Stream.IsClosed; internal bool IsClosed => Stream.IsClosed;
internal ExternalProxy UpStreamProxy { get; set; } internal ExternalProxy? UpStreamProxy { get; set; }
internal string HostName { get; set; } internal string HostName { get; set; }
...@@ -44,12 +42,12 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -44,12 +42,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary> /// <summary>
/// Local NIC via connection is made /// Local NIC via connection is made
/// </summary> /// </summary>
internal IPEndPoint UpStreamEndPoint { get; set; } internal IPEndPoint? UpStreamEndPoint { get; set; }
/// <summary> /// <summary>
/// Http version /// Http version
/// </summary> /// </summary>
internal Version Version { get; set; } internal Version Version { get; set; } = HttpHeader.VersionUnknown;
private readonly TcpClient tcpClient; private readonly TcpClient tcpClient;
...@@ -61,12 +59,12 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -61,12 +59,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary> /// <summary>
/// Used to write lines to server /// Used to write lines to server
/// </summary> /// </summary>
internal HttpRequestWriter StreamWriter { get; set; } internal HttpRequestWriter? StreamWriter { get; set; }
/// <summary> /// <summary>
/// Server stream /// Server stream
/// </summary> /// </summary>
internal CustomBufferedStream Stream { get; set; } internal CustomBufferedStream? Stream { get; set; }
/// <summary> /// <summary>
/// Last time this connection was used /// Last time this connection was used
......
...@@ -224,9 +224,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -224,9 +224,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
} }
} }
internal byte[] GetBytes() internal byte[]? GetBytes()
{ {
byte[] buffer = null; byte[]? buffer = null;
if (pBuffers == IntPtr.Zero) if (pBuffers == IntPtr.Zero)
{ {
......
// //
// Nancy.Authentication.Ntlm.Protocol.Type3Message - Authentication // Nancy.Authentication.Ntlm.Protocol.Type3Message - Authentication
// //
// Author: // Author:
...@@ -58,7 +58,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -58,7 +58,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// <summary> /// <summary>
/// Username /// Username
/// </summary> /// </summary>
internal string Username { get; private set; } internal string? Username { get; private set; }
internal Common.NtlmFlags Flags { get; set; } internal Common.NtlmFlags Flags { get; set; }
......
// http://pinvoke.net/default.aspx/secur32/InitializeSecurityContext.html // http://pinvoke.net/default.aspx/secur32/InitializeSecurityContext.html
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Principal; using System.Security.Principal;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Network.WinAuth.Security namespace Titanium.Web.Proxy.Network.WinAuth.Security
...@@ -24,9 +22,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -24,9 +22,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// <param name="authScheme"></param> /// <param name="authScheme"></param>
/// <param name="data"></param> /// <param name="data"></param>
/// <returns></returns> /// <returns></returns>
internal static byte[] AcquireInitialSecurityToken(string hostname, string authScheme, InternalDataStore data) internal static byte[]? AcquireInitialSecurityToken(string hostname, string authScheme, InternalDataStore data)
{ {
byte[] token; byte[]? token;
// null for initial call // null for initial call
var serverToken = new SecurityBufferDesciption(); var serverToken = new SecurityBufferDesciption();
...@@ -91,9 +89,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -91,9 +89,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// <param name="serverChallenge"></param> /// <param name="serverChallenge"></param>
/// <param name="data"></param> /// <param name="data"></param>
/// <returns></returns> /// <returns></returns>
internal static byte[] AcquireFinalSecurityToken(string hostname, byte[] serverChallenge, InternalDataStore data) internal static byte[]? AcquireFinalSecurityToken(string hostname, byte[] serverChallenge, InternalDataStore data)
{ {
byte[] token; byte[]? token;
// user server challenge // user server challenge
var serverToken = new SecurityBufferDesciption(serverChallenge); var serverToken = new SecurityBufferDesciption(serverChallenge);
...@@ -145,19 +143,19 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -145,19 +143,19 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// <returns></returns> /// <returns></returns>
internal static bool ValidateWinAuthState(InternalDataStore data, State.WinAuthState expectedAuthState) internal static bool ValidateWinAuthState(InternalDataStore data, State.WinAuthState expectedAuthState)
{ {
bool stateExists = data.TryGetValueAs(authStateKey, out State state); bool stateExists = data.TryGetValueAs(authStateKey, out State? state);
if (expectedAuthState == State.WinAuthState.UNAUTHORIZED) if (expectedAuthState == State.WinAuthState.UNAUTHORIZED)
{ {
return !stateExists || return !stateExists ||
state.AuthState == State.WinAuthState.UNAUTHORIZED || state!.AuthState == State.WinAuthState.UNAUTHORIZED ||
state.AuthState == State.WinAuthState.AUTHORIZED; // Server may require re-authentication on an open connection state.AuthState == State.WinAuthState.AUTHORIZED; // Server may require re-authentication on an open connection
} }
if (expectedAuthState == State.WinAuthState.INITIAL_TOKEN) if (expectedAuthState == State.WinAuthState.INITIAL_TOKEN)
{ {
return stateExists && return stateExists &&
(state.AuthState == State.WinAuthState.INITIAL_TOKEN || (state!.AuthState == State.WinAuthState.INITIAL_TOKEN ||
state.AuthState == State.WinAuthState.AUTHORIZED); // Server may require re-authentication on an open connection state.AuthState == State.WinAuthState.AUTHORIZED); // Server may require re-authentication on an open connection
} }
...@@ -170,9 +168,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -170,9 +168,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// <param name="data"></param> /// <param name="data"></param>
internal static void AuthenticatedResponse(InternalDataStore data) internal static void AuthenticatedResponse(InternalDataStore data)
{ {
if (data.TryGetValueAs(authStateKey, out State state)) if (data.TryGetValueAs(authStateKey, out State? state))
{ {
state.AuthState = State.WinAuthState.AUTHORIZED; state!.AuthState = State.WinAuthState.AUTHORIZED;
state.UpdatePresence(); state.UpdatePresence();
} }
} }
......
...@@ -7,7 +7,6 @@ using Titanium.Web.Proxy.Exceptions; ...@@ -7,7 +7,6 @@ using Titanium.Web.Proxy.Exceptions;
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.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -30,30 +29,35 @@ namespace Titanium.Web.Proxy ...@@ -30,30 +29,35 @@ namespace Titanium.Web.Proxy
try try
{ {
var header = httpHeaders.GetFirstHeader(KnownHeaders.ProxyAuthorization); var headerObj = httpHeaders.GetFirstHeader(KnownHeaders.ProxyAuthorization);
if (header == null) if (headerObj == null)
{ {
session.HttpClient.Response = createAuthentication407Response("Proxy Authentication Required"); session.HttpClient.Response = createAuthentication407Response("Proxy Authentication Required");
return false; return false;
} }
var headerValueParts = header.Value.Split(ProxyConstants.SpaceSplit); string header = headerObj.Value;
int firstSpace = header.IndexOf(' ');
if (headerValueParts.Length != 2) // header value should contain exactly 1 space
if (firstSpace == -1 || header.IndexOf(' ', firstSpace + 1) != -1)
{ {
// Return not authorized // Return not authorized
session.HttpClient.Response = createAuthentication407Response("Proxy Authentication Invalid"); session.HttpClient.Response = createAuthentication407Response("Proxy Authentication Invalid");
return false; return false;
} }
var authenticationType = header.AsMemory(0, firstSpace);
var credentials = header.AsMemory(firstSpace + 1);
if (ProxyBasicAuthenticateFunc != null) if (ProxyBasicAuthenticateFunc != null)
{ {
return await authenticateUserBasic(session, headerValueParts); return await authenticateUserBasic(session, authenticationType, credentials);
} }
if (ProxySchemeAuthenticateFunc != null) if (ProxySchemeAuthenticateFunc != null)
{ {
var result = await ProxySchemeAuthenticateFunc(session, headerValueParts[0], headerValueParts[1]); var result = await ProxySchemeAuthenticateFunc(session, authenticationType.ToString(), credentials.ToString());
if (result.Result == ProxyAuthenticationResult.ContinuationNeeded) if (result.Result == ProxyAuthenticationResult.ContinuationNeeded)
{ {
...@@ -78,16 +82,16 @@ namespace Titanium.Web.Proxy ...@@ -78,16 +82,16 @@ namespace Titanium.Web.Proxy
} }
} }
private async Task<bool> authenticateUserBasic(SessionEventArgsBase session, string[] headerValueParts) private async Task<bool> authenticateUserBasic(SessionEventArgsBase session, ReadOnlyMemory<char> authenticationType, ReadOnlyMemory<char> credentials)
{ {
if (!headerValueParts[0].EqualsIgnoreCase(KnownHeaders.ProxyAuthorizationBasic)) if (!authenticationType.Span.EqualsIgnoreCase(KnownHeaders.ProxyAuthorizationBasic.AsSpan()))
{ {
// Return not authorized // Return not authorized
session.HttpClient.Response = createAuthentication407Response("Proxy Authentication Invalid"); session.HttpClient.Response = createAuthentication407Response("Proxy Authentication Invalid");
return false; return false;
} }
string decoded = Encoding.UTF8.GetString(Convert.FromBase64String(headerValueParts[1])); string decoded = Encoding.UTF8.GetString(Convert.FromBase64String(credentials.ToString()));
int colonIndex = decoded.IndexOf(':'); int colonIndex = decoded.IndexOf(':');
if (colonIndex == -1) if (colonIndex == -1)
{ {
...@@ -113,7 +117,7 @@ namespace Titanium.Web.Proxy ...@@ -113,7 +117,7 @@ namespace Titanium.Web.Proxy
/// <param name="description">Response description.</param> /// <param name="description">Response description.</param>
/// <param name="continuation">The continuation.</param> /// <param name="continuation">The continuation.</param>
/// <returns></returns> /// <returns></returns>
private Response createAuthentication407Response(string description, string continuation = null) private Response createAuthentication407Response(string description, string? continuation = null)
{ {
var response = new Response var response = new Response
{ {
...@@ -124,7 +128,7 @@ namespace Titanium.Web.Proxy ...@@ -124,7 +128,7 @@ namespace Titanium.Web.Proxy
if (!string.IsNullOrWhiteSpace(continuation)) if (!string.IsNullOrWhiteSpace(continuation))
{ {
return createContinuationResponse(response, continuation); return createContinuationResponse(response, continuation!);
} }
if (ProxyBasicAuthenticateFunc != null) if (ProxyBasicAuthenticateFunc != null)
......
...@@ -91,7 +91,7 @@ namespace Titanium.Web.Proxy ...@@ -91,7 +91,7 @@ namespace Titanium.Web.Proxy
/// Should we attempt to trust certificates with elevated permissions by /// Should we attempt to trust certificates with elevated permissions by
/// prompting for UAC if required? /// prompting for UAC if required?
/// </param> /// </param>
public ProxyServer(string rootCertificateName, string rootCertificateIssuerName, public ProxyServer(string? rootCertificateName, string? rootCertificateIssuerName,
bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
bool trustRootCertificateAsAdmin = false) bool trustRootCertificateAsAdmin = false)
{ {
...@@ -115,7 +115,7 @@ namespace Titanium.Web.Proxy ...@@ -115,7 +115,7 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Manage system proxy settings. /// Manage system proxy settings.
/// </summary> /// </summary>
private SystemProxyManager systemProxySettingsManager { get; } private SystemProxyManager? systemProxySettingsManager { get; }
/// <summary> /// <summary>
/// Number of exception retries when connection pool is enabled. /// Number of exception retries when connection pool is enabled.
...@@ -229,7 +229,9 @@ namespace Titanium.Web.Proxy ...@@ -229,7 +229,9 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// List of supported Ssl versions. /// List of supported Ssl versions.
/// </summary> /// </summary>
#pragma warning disable 618
public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
#pragma warning restore 618
/// <summary> /// <summary>
/// The buffer pool used throughout this proxy instance. /// The buffer pool used throughout this proxy instance.
...@@ -247,18 +249,18 @@ namespace Titanium.Web.Proxy ...@@ -247,18 +249,18 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// External proxy used for Http requests. /// External proxy used for Http requests.
/// </summary> /// </summary>
public ExternalProxy UpStreamHttpProxy { get; set; } public ExternalProxy? UpStreamHttpProxy { get; set; }
/// <summary> /// <summary>
/// External proxy used for Https requests. /// External proxy used for Https requests.
/// </summary> /// </summary>
public ExternalProxy UpStreamHttpsProxy { get; set; } public ExternalProxy? UpStreamHttpsProxy { get; set; }
/// <summary> /// <summary>
/// Local adapter/NIC endpoint where proxy makes request via. /// Local adapter/NIC endpoint where proxy makes request via.
/// Defaults via any IP addresses of this machine. /// Defaults via any IP addresses of this machine.
/// </summary> /// </summary>
public IPEndPoint UpStreamEndPoint { get; set; } public IPEndPoint? UpStreamEndPoint { get; set; }
/// <summary> /// <summary>
/// A list of IpAddress and port this proxy is listening to. /// A list of IpAddress and port this proxy is listening to.
...@@ -269,7 +271,7 @@ namespace Titanium.Web.Proxy ...@@ -269,7 +271,7 @@ namespace Titanium.Web.Proxy
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests. /// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests.
/// User should return the ExternalProxy object with valid credentials. /// User should return the ExternalProxy object with valid credentials.
/// </summary> /// </summary>
public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; } public Func<SessionEventArgsBase, Task<ExternalProxy?>>? GetCustomUpStreamProxyFunc { get; set; }
/// <summary> /// <summary>
/// Callback for error events in this proxy instance. /// Callback for error events in this proxy instance.
...@@ -307,47 +309,47 @@ namespace Titanium.Web.Proxy ...@@ -307,47 +309,47 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Event occurs when client connection count changed. /// Event occurs when client connection count changed.
/// </summary> /// </summary>
public event EventHandler ClientConnectionCountChanged; public event EventHandler? ClientConnectionCountChanged;
/// <summary> /// <summary>
/// Event occurs when server connection count changed. /// Event occurs when server connection count changed.
/// </summary> /// </summary>
public event EventHandler ServerConnectionCountChanged; public event EventHandler? ServerConnectionCountChanged;
/// <summary> /// <summary>
/// Event to override the default verification logic of remote SSL certificate received during authentication. /// Event to override the default verification logic of remote SSL certificate received during authentication.
/// </summary> /// </summary>
public event AsyncEventHandler<CertificateValidationEventArgs> ServerCertificateValidationCallback; public event AsyncEventHandler<CertificateValidationEventArgs>? ServerCertificateValidationCallback;
/// <summary> /// <summary>
/// Event to override client certificate selection during mutual SSL authentication. /// Event to override client certificate selection during mutual SSL authentication.
/// </summary> /// </summary>
public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback; public event AsyncEventHandler<CertificateSelectionEventArgs>? ClientCertificateSelectionCallback;
/// <summary> /// <summary>
/// Intercept request event to server. /// Intercept request event to server.
/// </summary> /// </summary>
public event AsyncEventHandler<SessionEventArgs> BeforeRequest; public event AsyncEventHandler<SessionEventArgs>? BeforeRequest;
/// <summary> /// <summary>
/// Intercept response event from server. /// Intercept response event from server.
/// </summary> /// </summary>
public event AsyncEventHandler<SessionEventArgs> BeforeResponse; public event AsyncEventHandler<SessionEventArgs>? BeforeResponse;
/// <summary> /// <summary>
/// Intercept after response event from server. /// Intercept after response event from server.
/// </summary> /// </summary>
public event AsyncEventHandler<SessionEventArgs> AfterResponse; public event AsyncEventHandler<SessionEventArgs>? AfterResponse;
/// <summary> /// <summary>
/// Customize TcpClient used for client connection upon create. /// Customize TcpClient used for client connection upon create.
/// </summary> /// </summary>
public event AsyncEventHandler<TcpClient> OnClientConnectionCreate; public event AsyncEventHandler<TcpClient>? OnClientConnectionCreate;
/// <summary> /// <summary>
/// Customize TcpClient used for server connection upon create. /// Customize TcpClient used for server connection upon create.
/// </summary> /// </summary>
public event AsyncEventHandler<TcpClient> OnServerConnectionCreate; public event AsyncEventHandler<TcpClient>? OnServerConnectionCreate;
/// <summary> /// <summary>
/// Customize the minimum ThreadPool size (increase it on a server) /// Customize the minimum ThreadPool size (increase it on a server)
...@@ -471,7 +473,7 @@ namespace Titanium.Web.Proxy ...@@ -471,7 +473,7 @@ namespace Titanium.Web.Proxy
endPoint.IsSystemHttpsProxy = true; endPoint.IsSystemHttpsProxy = true;
} }
string proxyType = null; string? proxyType = null;
switch (protocolType) switch (protocolType)
{ {
case ProxyProtocolType.Http: case ProxyProtocolType.Http:
...@@ -572,7 +574,7 @@ namespace Titanium.Web.Proxy ...@@ -572,7 +574,7 @@ namespace Titanium.Web.Proxy
if (systemProxySettingsManager != null && RunTime.IsWindows && !RunTime.IsUwpOnWindows) if (systemProxySettingsManager != null && RunTime.IsWindows && !RunTime.IsUwpOnWindows)
{ {
var proxyInfo = systemProxySettingsManager.GetProxyInfoFromRegistry(); var proxyInfo = systemProxySettingsManager.GetProxyInfoFromRegistry();
if (proxyInfo.Proxies != null) if (proxyInfo?.Proxies != null)
{ {
var protocolToRemove = ProxyProtocolType.None; var protocolToRemove = ProxyProtocolType.None;
foreach (var proxy in proxyInfo.Proxies.Values) foreach (var proxy in proxyInfo.Proxies.Values)
...@@ -704,7 +706,7 @@ namespace Titanium.Web.Proxy ...@@ -704,7 +706,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="sessionEventArgs">The session.</param> /// <param name="sessionEventArgs">The session.</param>
/// <returns>The external proxy as task result.</returns> /// <returns>The external proxy as task result.</returns>
private Task<ExternalProxy> getSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs) private Task<ExternalProxy?> getSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs)
{ {
var proxy = systemProxyResolver.GetProxy(sessionEventArgs.HttpClient.Request.RequestUri); var proxy = systemProxyResolver.GetProxy(sessionEventArgs.HttpClient.Request.RequestUri);
return Task.FromResult(proxy); return Task.FromResult(proxy);
...@@ -717,7 +719,7 @@ namespace Titanium.Web.Proxy ...@@ -717,7 +719,7 @@ namespace Titanium.Web.Proxy
{ {
var endPoint = (ProxyEndPoint)asyn.AsyncState; var endPoint = (ProxyEndPoint)asyn.AsyncState;
TcpClient tcpClient = null; TcpClient? tcpClient = null;
try try
{ {
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Sockets;
#if NETSTANDARD2_1
using System.Net.Security; using System.Net.Security;
#endif using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
...@@ -45,13 +41,13 @@ namespace Titanium.Web.Proxy ...@@ -45,13 +41,13 @@ namespace Titanium.Web.Proxy
/// <param name="prefetchConnectionTask">Prefetched server connection for current client using Connect/SNI headers.</param> /// <param name="prefetchConnectionTask">Prefetched server connection for current client using Connect/SNI headers.</param>
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection, private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter, CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, TunnelConnectSessionEventArgs connectArgs, CancellationTokenSource cancellationTokenSource, string? httpsConnectHostname, TunnelConnectSessionEventArgs? connectArgs,
Task<TcpServerConnection> prefetchConnectionTask = null) Task<TcpServerConnection>? prefetchConnectionTask = null)
{ {
var connectRequest = connectArgs?.HttpClient.ConnectRequest; var connectRequest = connectArgs?.HttpClient.ConnectRequest;
var prefetchTask = prefetchConnectionTask; var prefetchTask = prefetchConnectionTask;
TcpServerConnection connection = null; TcpServerConnection? connection = null;
bool closeServerConnection = false; bool closeServerConnection = false;
try try
...@@ -86,8 +82,7 @@ namespace Titanium.Web.Proxy ...@@ -86,8 +82,7 @@ namespace Titanium.Web.Proxy
{ {
try try
{ {
Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, out var version);
out var version);
// Read the request headers in to unique and non-unique header collections // Read the request headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(clientStream, args.HttpClient.Request.Headers, await HeaderParser.ReadHeaders(clientStream, args.HttpClient.Request.Headers,
...@@ -107,8 +102,8 @@ namespace Titanium.Web.Proxy ...@@ -107,8 +102,8 @@ namespace Titanium.Web.Proxy
} }
else else
{ {
string host = args.HttpClient.Request.Host ?? httpsConnectHostname; string? host = args.HttpClient.Request.Host ?? httpsConnectHostname;
string hostAndPath = host; string? hostAndPath = host;
if (httpUrl.StartsWith("/")) if (httpUrl.StartsWith("/"))
{ {
hostAndPath += httpUrl; hostAndPath += httpUrl;
...@@ -217,7 +212,7 @@ namespace Titanium.Web.Proxy ...@@ -217,7 +212,7 @@ namespace Titanium.Web.Proxy
connection = null; connection = null;
} }
var result = await handleHttpSessionRequest(httpCmd, args, connection, var result = await handleHttpSessionRequest(httpMethod, httpUrl, version, args, connection,
clientConnection.NegotiatedApplicationProtocol, clientConnection.NegotiatedApplicationProtocol,
cancellationToken, cancellationTokenSource); cancellationToken, cancellationTokenSource);
...@@ -226,7 +221,7 @@ namespace Titanium.Web.Proxy ...@@ -226,7 +221,7 @@ namespace Titanium.Web.Proxy
closeServerConnection = !result.Continue; closeServerConnection = !result.Continue;
// throw if exception happened // throw if exception happened
if (!result.IsSuccess) if (result.Exception != null)
{ {
throw result.Exception; throw result.Exception;
} }
...@@ -288,15 +283,17 @@ namespace Titanium.Web.Proxy ...@@ -288,15 +283,17 @@ namespace Titanium.Web.Proxy
} }
finally finally
{ {
await tcpConnectionFactory.Release(connection, if (connection != null)
closeServerConnection); {
await tcpConnectionFactory.Release(connection, closeServerConnection);
}
await tcpConnectionFactory.Release(prefetchTask, closeServerConnection); await tcpConnectionFactory.Release(prefetchTask, closeServerConnection);
} }
} }
private async Task<RetryResult> handleHttpSessionRequest(string httpCmd, SessionEventArgs args, private async Task<RetryResult> handleHttpSessionRequest(string requestHttpMethod, string requestHttpUrl, Version requestVersion, SessionEventArgs args,
TcpServerConnection serverConnection, SslApplicationProtocol sslApplicationProtocol, TcpServerConnection? serverConnection, SslApplicationProtocol sslApplicationProtocol,
CancellationToken cancellationToken, CancellationTokenSource cancellationTokenSource) CancellationToken cancellationToken, CancellationTokenSource cancellationTokenSource)
{ {
// a connection generator task with captured parameters via closure. // a connection generator task with captured parameters via closure.
...@@ -312,10 +309,10 @@ namespace Titanium.Web.Proxy ...@@ -312,10 +309,10 @@ namespace Titanium.Web.Proxy
if (args.HttpClient.Request.UpgradeToWebSocket) if (args.HttpClient.Request.UpgradeToWebSocket)
{ {
args.HttpClient.ConnectRequest.TunnelType = TunnelType.Websocket; args.HttpClient.ConnectRequest!.TunnelType = TunnelType.Websocket;
// if upgrading to websocket then relay the request without reading the contents // if upgrading to websocket then relay the request without reading the contents
await handleWebSocketUpgrade(httpCmd, args, args.HttpClient.Request, await handleWebSocketUpgrade(requestHttpMethod, requestHttpUrl, requestVersion, args, args.HttpClient.Request,
args.HttpClient.Response, args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamWriter, args.HttpClient.Response, args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamWriter,
connection, cancellationTokenSource, cancellationToken); connection, cancellationTokenSource, cancellationToken);
return false; return false;
...@@ -346,9 +343,12 @@ namespace Titanium.Web.Proxy ...@@ -346,9 +343,12 @@ namespace Titanium.Web.Proxy
{ {
var clientStreamWriter = args.ProxyClient.ClientStreamWriter; var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
var response = args.HttpClient.Response; var response = args.HttpClient.Response;
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode,
response.StatusDescription, cancellationToken); var headerBuilder = new HeaderBuilder();
await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken: cancellationToken); headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
headerBuilder.WriteHeaders(response.Headers);
await clientStreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
await args.ClearResponse(cancellationToken); await args.ClearResponse(cancellationToken);
} }
...@@ -358,12 +358,12 @@ namespace Titanium.Web.Proxy ...@@ -358,12 +358,12 @@ namespace Titanium.Web.Proxy
if (request.IsBodyRead) if (request.IsBodyRead)
{ {
var writer = args.HttpClient.Connection.StreamWriter; var writer = args.HttpClient.Connection.StreamWriter;
await writer.WriteBodyAsync(body, request.IsChunked, cancellationToken); await writer.WriteBodyAsync(body!, request.IsChunked, cancellationToken);
} }
else if (!request.ExpectationFailed) else if (!request.ExpectationFailed)
{ {
// get the request body unless an unsuccessful 100 continue request was made // get the request body unless an unsuccessful 100 continue request was made
HttpWriter writer = args.HttpClient.Connection.StreamWriter; HttpWriter writer = args.HttpClient.Connection.StreamWriter!;
await args.CopyRequestBodyAsync(writer, TransformationMode.None, cancellationToken); await args.CopyRequestBodyAsync(writer, TransformationMode.None, cancellationToken);
} }
} }
...@@ -379,7 +379,7 @@ namespace Titanium.Web.Proxy ...@@ -379,7 +379,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
private void prepareRequestHeaders(HeaderCollection requestHeaders) private void prepareRequestHeaders(HeaderCollection requestHeaders)
{ {
string acceptEncoding = requestHeaders.GetHeaderValueOrNull(KnownHeaders.AcceptEncoding); string? acceptEncoding = requestHeaders.GetHeaderValueOrNull(KnownHeaders.AcceptEncoding);
if (acceptEncoding != null) if (acceptEncoding != null)
{ {
......
...@@ -72,8 +72,7 @@ namespace Titanium.Web.Proxy ...@@ -72,8 +72,7 @@ namespace Titanium.Web.Proxy
// write custom user response with body and return. // write custom user response with body and return.
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken); await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken);
if (args.HttpClient.Connection != null if (args.HttpClient.HasConnection && !args.HttpClient.CloseServerConnection)
&& !args.HttpClient.CloseServerConnection)
{ {
// syphon out the original response body from server connection // syphon out the original response body from server connection
// so that connection will be good to be reused. // so that connection will be good to be reused.
...@@ -87,13 +86,15 @@ namespace Titanium.Web.Proxy ...@@ -87,13 +86,15 @@ namespace Titanium.Web.Proxy
// likely after making modifications from User Response Handler // likely after making modifications from User Response Handler
if (args.ReRequest) if (args.ReRequest)
{ {
await tcpConnectionFactory.Release(args.HttpClient.Connection); if (args.HttpClient.HasConnection)
{
await tcpConnectionFactory.Release(args.HttpClient.Connection);
}
// clear current response // clear current response
await args.ClearResponse(cancellationToken); await args.ClearResponse(cancellationToken);
var httpCmd = Request.CreateRequestLine(args.HttpClient.Request.Method, await handleHttpSessionRequest(args.HttpClient.Request.Method, args.HttpClient.Request.RequestUriString, args.HttpClient.Request.HttpVersion,
args.HttpClient.Request.RequestUriString, args.HttpClient.Request.HttpVersion); args, null, args.ClientConnection.NegotiatedApplicationProtocol,
await handleHttpSessionRequest(httpCmd, args, null, args.ClientConnection.NegotiatedApplicationProtocol,
cancellationToken, args.CancellationTokenSource); cancellationToken, args.CancellationTokenSource);
return; return;
} }
...@@ -112,9 +113,10 @@ namespace Titanium.Web.Proxy ...@@ -112,9 +113,10 @@ namespace Titanium.Web.Proxy
else else
{ {
// Write back response status to client // Write back response status to client
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, var headerBuilder = new HeaderBuilder();
response.StatusDescription, cancellationToken); headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken: cancellationToken); headerBuilder.WriteHeaders(response.Headers);
await clientStreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
// Write body if exists // Write body if exists
if (response.HasBody) if (response.HasBody)
......
...@@ -12,11 +12,6 @@ namespace Titanium.Web.Proxy.Shared ...@@ -12,11 +12,6 @@ namespace Titanium.Web.Proxy.Shared
{ {
internal static readonly char DotSplit = '.'; internal static readonly char DotSplit = '.';
internal static readonly char[] SpaceSplit = { ' ' };
internal static readonly char[] ColonSplit = { ':' };
internal static readonly char[] SemiColonSplit = { ';' };
internal static readonly char[] EqualSplit = { '=' };
internal static readonly string NewLine = "\r\n"; internal static readonly string NewLine = "\r\n";
internal static readonly byte[] NewLineBytes = { (byte)'\r', (byte)'\n' }; internal static readonly byte[] NewLineBytes = { (byte)'\r', (byte)'\n' };
......
using System.Buffers; using System.Buffers;
using System.Collections.Concurrent;
namespace Titanium.Web.Proxy.StreamExtended.BufferPool namespace Titanium.Web.Proxy.StreamExtended.BufferPool
{ {
......
...@@ -40,7 +40,7 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -40,7 +40,7 @@ namespace Titanium.Web.Proxy.StreamExtended
} }
} }
public byte[] SessionId { get; set; } public byte[] SessionId { get; }
public int[] Ciphers { get; set; } public int[] Ciphers { get; set; }
...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.StreamExtended
internal int EntensionsStartPosition { get; set; } internal int EntensionsStartPosition { get; set; }
public Dictionary<string, SslExtension> Extensions { get; set; } public Dictionary<string, SslExtension>? Extensions { get; set; }
public SslProtocols SslProtocol public SslProtocols SslProtocol
{ {
...@@ -79,6 +79,11 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -79,6 +79,11 @@ namespace Titanium.Web.Proxy.StreamExtended
} }
} }
public ClientHelloInfo(byte[] sessionId)
{
SessionId = sessionId;
}
private static string SslVersionToString(int major, int minor) private static string SslVersionToString(int major, int minor)
{ {
string str = "Unknown"; string str = "Unknown";
......
...@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
private int bufferLength; private int bufferLength;
private byte[] buffer; private readonly byte[] buffer;
private bool disposed; private bool disposed;
...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
this.bufferPool = bufferPool; this.bufferPool = bufferPool;
} }
public async Task<bool> FillBufferAsync(CancellationToken cancellationToken = default) public async ValueTask<bool> FillBufferAsync(CancellationToken cancellationToken = default)
{ {
await FlushAsync(cancellationToken); await FlushAsync(cancellationToken);
return await reader.FillBufferAsync(cancellationToken); return await reader.FillBufferAsync(cancellationToken);
...@@ -124,7 +124,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -124,7 +124,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
return result; return result;
} }
public Task<string> ReadLineAsync(CancellationToken cancellationToken = default) public Task<string?> ReadLineAsync(CancellationToken cancellationToken = default)
{ {
return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken); return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken);
} }
...@@ -134,9 +134,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -134,9 +134,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
if (!disposed) if (!disposed)
{ {
disposed = true; disposed = true;
var b = buffer; bufferPool.ReturnBuffer(buffer);
buffer = null;
bufferPool.ReturnBuffer(b);
} }
} }
} }
......
...@@ -69,7 +69,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -69,7 +69,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// Fills the buffer asynchronous. /// Fills the buffer asynchronous.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<bool> ICustomStreamReader.FillBufferAsync(CancellationToken cancellationToken) ValueTask<bool> ICustomStreamReader.FillBufferAsync(CancellationToken cancellationToken)
{ {
return baseStream.FillBufferAsync(cancellationToken); return baseStream.FillBufferAsync(cancellationToken);
} }
...@@ -141,7 +141,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -141,7 +141,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// </summary> /// </summary>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<string> ICustomStreamReader.ReadLineAsync(CancellationToken cancellationToken) Task<string?> ICustomStreamReader.ReadLineAsync(CancellationToken cancellationToken)
{ {
return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken); return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken);
} }
......
...@@ -5,6 +5,7 @@ using System.Net.Sockets; ...@@ -5,6 +5,7 @@ using System.Net.Sockets;
using System.Text; 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.StreamExtended.BufferPool; using Titanium.Web.Proxy.StreamExtended.BufferPool;
namespace Titanium.Web.Proxy.StreamExtended.Network namespace Titanium.Web.Proxy.StreamExtended.Network
...@@ -18,10 +19,10 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -18,10 +19,10 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
internal class CustomBufferedStream : Stream, ICustomStreamReader internal class CustomBufferedStream : Stream, ICustomStreamReader
{ {
private readonly bool leaveOpen; private readonly bool leaveOpen;
private byte[] streamBuffer; private readonly byte[] streamBuffer;
// default to UTF-8 // default to UTF-8
private static readonly Encoding encoding = Encoding.UTF8; private static Encoding encoding => HttpHelper.HeaderEncoding;
private static readonly bool networkStreamHack = true; private static readonly bool networkStreamHack = true;
...@@ -35,9 +36,9 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -35,9 +36,9 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
private readonly IBufferPool bufferPool; private readonly IBufferPool bufferPool;
public event EventHandler<DataEventArgs> DataRead; public event EventHandler<DataEventArgs>? DataRead;
public event EventHandler<DataEventArgs> DataWrite; public event EventHandler<DataEventArgs>? DataWrite;
public Stream BaseStream { get; } public Stream BaseStream { get; }
...@@ -396,9 +397,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -396,9 +397,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
BaseStream.Dispose(); BaseStream.Dispose();
} }
var buffer = streamBuffer; bufferPool.ReturnBuffer(streamBuffer);
streamBuffer = null;
bufferPool.ReturnBuffer(buffer);
} }
} }
...@@ -511,7 +510,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -511,7 +510,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// </summary> /// </summary>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns> /// <returns></returns>
public async Task<bool> FillBufferAsync(CancellationToken cancellationToken = default) public async ValueTask<bool> FillBufferAsync(CancellationToken cancellationToken = default)
{ {
if (closed) if (closed)
{ {
...@@ -559,7 +558,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -559,7 +558,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>
public Task<string> ReadLineAsync(CancellationToken cancellationToken = default) public Task<string?> ReadLineAsync(CancellationToken cancellationToken = default)
{ {
return ReadLineInternalAsync(this, bufferPool, cancellationToken); return ReadLineInternalAsync(this, bufferPool, cancellationToken);
} }
...@@ -568,7 +567,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -568,7 +567,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 Task<string> ReadLineInternalAsync(ICustomStreamReader reader, IBufferPool bufferPool, CancellationToken cancellationToken = default) internal static async Task<string?> ReadLineInternalAsync(ICustomStreamReader reader, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{ {
byte lastChar = default; byte lastChar = default;
......
...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// Fills the buffer asynchronous. /// Fills the buffer asynchronous.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<bool> FillBufferAsync(CancellationToken cancellationToken = default); ValueTask<bool> FillBufferAsync(CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Peeks a byte from buffer. /// Peeks a byte from buffer.
...@@ -74,6 +74,6 @@ namespace Titanium.Web.Proxy.StreamExtended.Network ...@@ -74,6 +74,6 @@ 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>
Task<string> ReadLineAsync(CancellationToken cancellationToken = default); Task<string?> ReadLineAsync(CancellationToken cancellationToken = default);
} }
} }
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.StreamExtended
internal int EntensionsStartPosition { get; set; } internal int EntensionsStartPosition { get; set; }
public Dictionary<string, SslExtension> Extensions { get; set; } public Dictionary<string, SslExtension>? Extensions { get; set; }
private static string SslVersionToString(int major, int minor) private static string SslVersionToString(int major, int minor)
{ {
...@@ -109,4 +109,4 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -109,4 +109,4 @@ namespace Titanium.Web.Proxy.StreamExtended
return sb.ToString(); return sb.ToString();
} }
} }
} }
\ No newline at end of file
...@@ -32,7 +32,7 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -32,7 +32,7 @@ namespace Titanium.Web.Proxy.StreamExtended
/// <param name="bufferPool"></param> /// <param name="bufferPool"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<ClientHelloInfo> PeekClientHello(CustomBufferedStream clientStream, IBufferPool bufferPool, CancellationToken cancellationToken = default) public static async Task<ClientHelloInfo?> PeekClientHello(CustomBufferedStream clientStream, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{ {
// detects the HTTPS ClientHello message as it is described in the following url: // detects the HTTPS ClientHello message as it is described in the following url:
// https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format // https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format
...@@ -88,13 +88,12 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -88,13 +88,12 @@ 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 var clientHelloInfo = new ClientHelloInfo(sessionId)
{ {
HandshakeVersion = 2, HandshakeVersion = 2,
MajorVersion = majorVersion, MajorVersion = majorVersion,
MinorVersion = minorVersion, MinorVersion = minorVersion,
Random = random, Random = random,
SessionId = sessionId,
Ciphers = ciphers, Ciphers = ciphers,
ClientHelloLength = peekStream.Position, ClientHelloLength = peekStream.Position,
}; };
...@@ -171,20 +170,19 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -171,20 +170,19 @@ namespace Titanium.Web.Proxy.StreamExtended
int extenstionsStartPosition = peekStream.Position; int extenstionsStartPosition = peekStream.Position;
Dictionary<string, SslExtension> extensions = null; Dictionary<string, SslExtension>? extensions = null;
if(extenstionsStartPosition < recordLength + 5) if(extenstionsStartPosition < recordLength + 5)
{ {
extensions = await ReadExtensions(majorVersion, minorVersion, peekStream, bufferPool, cancellationToken); extensions = await ReadExtensions(majorVersion, minorVersion, peekStream, bufferPool, cancellationToken);
} }
var clientHelloInfo = new ClientHelloInfo var clientHelloInfo = new ClientHelloInfo(sessionId)
{ {
HandshakeVersion = 3, HandshakeVersion = 3,
MajorVersion = majorVersion, MajorVersion = majorVersion,
MinorVersion = minorVersion, MinorVersion = minorVersion,
Random = random, Random = random,
SessionId = sessionId,
Ciphers = ciphers, Ciphers = ciphers,
CompressionData = compressionData, CompressionData = compressionData,
ClientHelloLength = peekStream.Position, ClientHelloLength = peekStream.Position,
...@@ -218,7 +216,7 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -218,7 +216,7 @@ namespace Titanium.Web.Proxy.StreamExtended
/// <param name="bufferPool"></param> /// <param name="bufferPool"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<ServerHelloInfo> PeekServerHello(CustomBufferedStream serverStream, IBufferPool bufferPool, CancellationToken cancellationToken = default) public static async Task<ServerHelloInfo?> PeekServerHello(CustomBufferedStream serverStream, IBufferPool bufferPool, CancellationToken cancellationToken = default)
{ {
// detects the HTTPS ClientHello message as it is described in the following url: // detects the HTTPS ClientHello message as it is described in the following url:
// https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format // https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format
...@@ -324,7 +322,7 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -324,7 +322,7 @@ namespace Titanium.Web.Proxy.StreamExtended
int extenstionsStartPosition = peekStream.Position; int extenstionsStartPosition = peekStream.Position;
Dictionary<string, SslExtension> extensions = null; Dictionary<string, SslExtension>? extensions = null;
if (extenstionsStartPosition < recordLength + 5) if (extenstionsStartPosition < recordLength + 5)
{ {
...@@ -351,9 +349,9 @@ namespace Titanium.Web.Proxy.StreamExtended ...@@ -351,9 +349,9 @@ 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, CustomBufferedPeekStream peekStream, 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 peekStream.EnsureBufferLength(2, cancellationToken))
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<SignAssembly>True</SignAssembly> <SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<LangVersion>7.1</LangVersion> <LangVersion>8.0</LangVersion>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
</PropertyGroup> </PropertyGroup>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
<DelaySign>False</DelaySign> <DelaySign>False</DelaySign>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<LangVersion>7.1</LangVersion> <LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
......
...@@ -8,13 +8,16 @@ ...@@ -8,13 +8,16 @@
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
<DelaySign>False</DelaySign> <DelaySign>False</DelaySign>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<LangVersion>7.1</LangVersion> <Nullable>enable</Nullable>
<LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BrotliSharpLib" Version="0.3.3" /> <PackageReference Include="BrotliSharpLib" Version="0.3.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="System.Buffers" Version="4.5.0" /> <PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
......
...@@ -35,23 +35,19 @@ namespace Titanium.Web.Proxy ...@@ -35,23 +35,19 @@ namespace Titanium.Web.Proxy
var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool); var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool); var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool);
SslStream sslStream = null; SslStream? sslStream = null;
try try
{ {
var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken); var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);
bool isHttps = clientHelloInfo != null; string? httpsHostName = null;
string httpsHostName = null;
if (isHttps) if (clientHelloInfo != null)
{ {
httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName; httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;
var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource) var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource, httpsHostName);
{
SniHostName = httpsHostName
};
await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc); await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);
...@@ -65,7 +61,7 @@ namespace Titanium.Web.Proxy ...@@ -65,7 +61,7 @@ namespace Titanium.Web.Proxy
clientConnection.SslProtocol = clientHelloInfo.SslProtocol; clientConnection.SslProtocol = clientHelloInfo.SslProtocol;
// do client authentication using certificate // do client authentication using certificate
X509Certificate2 certificate = null; X509Certificate2? certificate = null;
try try
{ {
sslStream = new SslStream(clientStream, false); sslStream = new SslStream(clientStream, false);
...@@ -98,7 +94,7 @@ namespace Titanium.Web.Proxy ...@@ -98,7 +94,7 @@ namespace Titanium.Web.Proxy
else else
{ {
var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port, var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
httpVersion: null, isHttps: false, applicationProtocols: null, httpVersion: HttpHeader.VersionUnknown, isHttps: false, applicationProtocols: null,
isConnect: true, proxyServer: this, session:null, upStreamEndPoint: UpStreamEndPoint, isConnect: true, proxyServer: this, session:null, upStreamEndPoint: UpStreamEndPoint,
externalProxy: UpStreamHttpsProxy, noCache: true, cancellationToken: cancellationToken); externalProxy: UpStreamHttpsProxy, noCache: true, cancellationToken: cancellationToken);
...@@ -136,10 +132,11 @@ namespace Titanium.Web.Proxy ...@@ -136,10 +132,11 @@ namespace Titanium.Web.Proxy
return; return;
} }
} }
// HTTPS server created - we can now decrypt the client's traffic // HTTPS server created - we can now decrypt the client's traffic
// Now create the request // Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter, await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
cancellationTokenSource, isHttps ? httpsHostName : null, null, null); cancellationTokenSource, httpsHostName, null, null);
} }
catch (ProxyException e) catch (ProxyException e)
{ {
......
...@@ -16,16 +16,17 @@ namespace Titanium.Web.Proxy ...@@ -16,16 +16,17 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Handle upgrade to websocket /// Handle upgrade to websocket
/// </summary> /// </summary>
private async Task handleWebSocketUpgrade(string httpCmd, private async Task handleWebSocketUpgrade(string requestHttpMethod, string requestHttpUrl, Version requestVersion,
SessionEventArgs args, Request request, Response response, SessionEventArgs args, Request request, Response response,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter, CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
TcpServerConnection serverConnection, TcpServerConnection serverConnection,
CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken) CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken)
{ {
// prepare the prefix content // prepare the prefix content
await serverConnection.StreamWriter.WriteLineAsync(httpCmd, cancellationToken); var headerBuilder = new HeaderBuilder();
await serverConnection.StreamWriter.WriteHeadersAsync(request.Headers, headerBuilder.WriteRequestLine(requestHttpMethod, requestHttpUrl, requestVersion);
cancellationToken: cancellationToken); headerBuilder.WriteHeaders(request.Headers);
await serverConnection.StreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
string httpStatus; string httpStatus;
try try
......
...@@ -47,8 +47,8 @@ namespace Titanium.Web.Proxy ...@@ -47,8 +47,8 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
private async Task handle401UnAuthorized(SessionEventArgs args) private async Task handle401UnAuthorized(SessionEventArgs args)
{ {
string headerName = null; string? headerName = null;
HttpHeader authHeader = null; HttpHeader? authHeader = null;
var response = args.HttpClient.Response; var response = args.HttpClient.Response;
...@@ -91,7 +91,7 @@ namespace Titanium.Web.Proxy ...@@ -91,7 +91,7 @@ namespace Titanium.Web.Proxy
if (authHeader != null) if (authHeader != null)
{ {
string scheme = authSchemes.Contains(authHeader.Value) ? authHeader.Value : null; string? scheme = authSchemes.Contains(authHeader.Value) ? authHeader.Value : null;
var expectedAuthState = var expectedAuthState =
scheme == null ? State.WinAuthState.INITIAL_TOKEN : State.WinAuthState.UNAUTHORIZED; scheme == null ? State.WinAuthState.INITIAL_TOKEN : State.WinAuthState.UNAUTHORIZED;
...@@ -111,7 +111,7 @@ namespace Titanium.Web.Proxy ...@@ -111,7 +111,7 @@ namespace Titanium.Web.Proxy
// initial value will match exactly any of the schemes // initial value will match exactly any of the schemes
if (scheme != null) if (scheme != null)
{ {
string clientToken = WinAuthHandler.GetInitialAuthToken(request.Host, scheme, args.HttpClient.Data); string clientToken = WinAuthHandler.GetInitialAuthToken(request.Host!, scheme, args.HttpClient.Data);
string auth = string.Concat(scheme, clientToken); string auth = string.Concat(scheme, clientToken);
...@@ -127,7 +127,6 @@ namespace Titanium.Web.Proxy ...@@ -127,7 +127,6 @@ namespace Titanium.Web.Proxy
else else
{ {
// challenge value will start with any of the scheme selected // challenge value will start with any of the scheme selected
scheme = authSchemes.First(x => scheme = authSchemes.First(x =>
authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) && authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) &&
authHeader.Value.Length > x.Length + 1); authHeader.Value.Length > x.Length + 1);
......
...@@ -7,6 +7,8 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -7,6 +7,8 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
{ {
internal static class HttpMessageParsing internal static class HttpMessageParsing
{ {
private static readonly char[] colonSplit = { ':' };
/// <summary> /// <summary>
/// This is a terribly inefficient way of reading & parsing an /// This is a terribly inefficient way of reading & parsing an
/// http request, but it's good enough for testing purposes. /// http request, but it's good enough for testing purposes.
...@@ -30,7 +32,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -30,7 +32,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
}; };
while (!string.IsNullOrEmpty(line = reader.ReadLine())) while (!string.IsNullOrEmpty(line = reader.ReadLine()))
{ {
var header = line.Split(ProxyConstants.ColonSplit, 2); var header = line.Split(colonSplit, 2);
request.Headers.AddHeader(header[0], header[1]); request.Headers.AddHeader(header[0], header[1]);
} }
...@@ -76,7 +78,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -76,7 +78,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
while (!string.IsNullOrEmpty(line = reader.ReadLine())) while (!string.IsNullOrEmpty(line = reader.ReadLine()))
{ {
var header = line.Split(ProxyConstants.ColonSplit, 2); var header = line.Split(colonSplit, 2);
response.Headers.AddHeader(header[0], header[1]); response.Headers.AddHeader(header[0], header[1]);
} }
......
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