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