Commit 1a2a9546 authored by Honfika's avatar Honfika

Header improvements (part 1)

parent 5497aa64
using System;
using System;
namespace Titanium.Web.Proxy
{
......
......@@ -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;
......
......@@ -16,6 +16,8 @@ namespace Titanium.Web.Proxy.Helpers
{
private static readonly Encoding defaultEncoding = Encoding.GetEncoding("ISO-8859-1");
public static Encoding HeaderEncoding => defaultEncoding;
/// <summary>
/// Gets the character encoding of request/response from content-type header
/// </summary>
......
......@@ -18,7 +18,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 +50,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 +67,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);
......
......@@ -107,11 +107,12 @@ namespace Titanium.Web.Proxy.Http
url = Request.RequestUri.GetOriginalPathAndQuery();
}
// prepare the request & headers
await writer.WriteLineAsync(Request.CreateRequestLine(Request.Method, url, Request.HttpVersion), cancellationToken);
var headerBuilder = new StringBuilder();
// prepare the request & headers
headerBuilder.Append(Request.CreateRequestLine(Request.Method, url, Request.HttpVersion));
headerBuilder.Append(ProxyConstants.NewLine);
// Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null
&& Connection.IsHttps == false
......
......@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.Http
sb.Append($"{CreateRequestLine(Method, RequestUriString, HttpVersion)}{ProxyConstants.NewLine}");
foreach (var header in Headers)
{
sb.Append($"{header.ToString()}{ProxyConstants.NewLine}");
sb.Append($"{header}{ProxyConstants.NewLine}");
}
sb.Append(ProxyConstants.NewLine);
......@@ -205,30 +205,38 @@ namespace Titanium.Web.Proxy.Http
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;
}
......
......@@ -134,22 +134,39 @@ namespace Titanium.Web.Proxy.Http
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;
}
}
}
}
......@@ -30,30 +30,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 +83,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)
{
......
......@@ -12,7 +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 = { '=' };
......
......@@ -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
......@@ -21,7 +22,7 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
private 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;
......
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