diff --git a/src/Titanium.Web.Proxy/Compression/CompressionUtil.cs b/src/Titanium.Web.Proxy/Compression/CompressionUtil.cs
index 4cccb12ad..7cff47098 100644
--- a/src/Titanium.Web.Proxy/Compression/CompressionUtil.cs
+++ b/src/Titanium.Web.Proxy/Compression/CompressionUtil.cs
@@ -7,13 +7,13 @@ internal static class CompressionUtil
{
public static HttpCompression CompressionNameToEnum(string name)
{
- if (KnownHeaders.ContentEncodingGzip.Equals(name.AsSpan()))
+ if (KnownHeaders.ContentEncodingGzip.Equals(name))
return HttpCompression.Gzip;
- if (KnownHeaders.ContentEncodingDeflate.Equals(name.AsSpan()))
+ if (KnownHeaders.ContentEncodingDeflate.Equals(name))
return HttpCompression.Deflate;
- if (KnownHeaders.ContentEncodingBrotli.Equals(name.AsSpan()))
+ if (KnownHeaders.ContentEncodingBrotli.Equals(name))
return HttpCompression.Brotli;
return HttpCompression.Unsupported;
diff --git a/src/Titanium.Web.Proxy/ExplicitClientHandler.cs b/src/Titanium.Web.Proxy/ExplicitClientHandler.cs
index 21e532a43..cf92d860f 100644
--- a/src/Titanium.Web.Proxy/ExplicitClientHandler.cs
+++ b/src/Titanium.Web.Proxy/ExplicitClientHandler.cs
@@ -47,7 +47,6 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
try
{
- string? connectHostname = null;
TunnelConnectSessionEventArgs? connectArgs = null;
// Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
@@ -62,14 +61,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
Request.ParseRequestLine(httpCmd!, out string _, out string httpUrl, out var version);
- connectHostname = httpUrl;
- int idx = connectHostname.IndexOf(":");
- if (idx >= 0)
- {
- connectHostname = connectHostname.Substring(0, idx);
- }
-
- var connectRequest = new ConnectRequest(connectHostname)
+ var connectRequest = new ConnectRequest(httpUrl)
{
OriginalUrlData = HttpHeader.Encoding.GetBytes(httpUrl),
HttpVersion = version
@@ -130,7 +122,7 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
bool isClientHello = clientHelloInfo != null;
if (clientHelloInfo != null)
{
- connectRequest.Scheme = ProxyServer.UriSchemeHttps;
+ connectRequest.IsHttps = true;
connectRequest.TunnelType = TunnelType.Https;
connectRequest.ClientHelloInfo = clientHelloInfo;
}
@@ -186,6 +178,13 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
}
}
+ string connectHostname = httpUrl;
+ int idx = connectHostname.IndexOf(":");
+ if (idx >= 0)
+ {
+ connectHostname = connectHostname.Substring(0, idx);
+ }
+
X509Certificate2? certificate = null;
try
{
diff --git a/src/Titanium.Web.Proxy/Http/ConnectRequest.cs b/src/Titanium.Web.Proxy/Http/ConnectRequest.cs
index 46aca8b24..1f8f90aac 100644
--- a/src/Titanium.Web.Proxy/Http/ConnectRequest.cs
+++ b/src/Titanium.Web.Proxy/Http/ConnectRequest.cs
@@ -9,10 +9,10 @@ namespace Titanium.Web.Proxy.Http
///
public class ConnectRequest : Request
{
- public ConnectRequest(string hostname)
+ public ConnectRequest(string authority)
{
Method = "CONNECT";
- Hostname = hostname;
+ Authority = authority;
}
public TunnelType TunnelType { get; internal set; }
diff --git a/src/Titanium.Web.Proxy/Http/HttpWebClient.cs b/src/Titanium.Web.Proxy/Http/HttpWebClient.cs
index 5556ef448..1957aa1d9 100644
--- a/src/Titanium.Web.Proxy/Http/HttpWebClient.cs
+++ b/src/Titanium.Web.Proxy/Http/HttpWebClient.cs
@@ -121,6 +121,10 @@ internal async Task SendRequest(bool enable100ContinueBehaviour, bool isTranspar
else
{
url = Request.RequestUri.GetOriginalPathAndQuery();
+ if (url == string.Empty)
+ {
+ url = "/";
+ }
}
var headerBuilder = new HeaderBuilder();
diff --git a/src/Titanium.Web.Proxy/Http/Request.cs b/src/Titanium.Web.Proxy/Http/Request.cs
index ff5c8ad72..b6b9816da 100644
--- a/src/Titanium.Web.Proxy/Http/Request.cs
+++ b/src/Titanium.Web.Proxy/Http/Request.cs
@@ -22,10 +22,10 @@ public class Request : RequestResponseBase
///
/// Is Https?
///
- public bool IsHttps => RequestUri.Scheme == ProxyServer.UriSchemeHttps;
+ public bool IsHttps { get; internal set; }
private ByteString originalUrlData;
- private protected ByteString UrlData;
+ private ByteString urlData;
internal ByteString OriginalUrlData
{
@@ -37,9 +37,21 @@ internal ByteString OriginalUrlData
}
}
- internal string Scheme { get; set; } = ProxyServer.UriSchemeHttp;
+ private protected ByteString UrlData
+ {
+ get => urlData;
+ set
+ {
+ urlData = value;
+ var scheme = getUriScheme(UrlData);
+ if (scheme.Length > 0)
+ {
+ IsHttps = scheme.Equals(ProxyServer.UriSchemeHttps8);
+ }
+ }
+ }
- internal string? Hostname { get; set; }
+ internal string? Authority { get; set; }
///
/// The original request Url.
@@ -53,21 +65,21 @@ public Uri RequestUri
{
get
{
- string url;
- if (startsWithUriScheme(UrlData))
- {
- url = UrlData.GetString();
- }
- else
+ string url = UrlData.GetString();
+ if (getUriScheme(UrlData).Length == 0)
{
- string? host = Host ?? Hostname;
- string? hostAndPath = host;
- if (UrlData.Length > 0 && UrlData[0] == '/')
+ string? hostAndPath = Host ?? Authority;
+
+ if (url.StartsWith("/"))
+ {
+ hostAndPath += url;
+ }
+ else
{
- hostAndPath += UrlData.GetString();
+ //throw new Exception($"Invalid URL: '{url}'");
}
- url = string.Concat(Scheme == ProxyServer.UriSchemeHttps ? "https://" : "http://", hostAndPath);
+ url = string.Concat(IsHttps ? "https://" : "http://", hostAndPath);
}
try
@@ -87,7 +99,16 @@ public Uri RequestUri
public string Url
{
get => UrlData.GetString();
- set => UrlData = value.GetByteString();
+ set
+ {
+ UrlData = value.GetByteString();
+
+ if (Host != null)
+ {
+ var uri = new Uri(value);
+ Host = uri.Authority;
+ }
+ }
}
[Obsolete("This property is obsolete. Use Url property instead")]
@@ -147,7 +168,7 @@ public bool ExpectContinue
get
{
string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Expect);
- return headerValue != null && headerValue.Equals(KnownHeaders.Expect100Continue);
+ return KnownHeaders.Expect100Continue.Equals(headerValue);
}
}
@@ -290,11 +311,11 @@ private static bool isAllUpper(string input)
return true;
}
- private bool startsWithUriScheme(ByteString str)
+ private ByteString getUriScheme(ByteString str)
{
if (str.Length < 3)
{
- return false;
+ return ByteString.Empty;
}
// regex: "^[a-z]*://"
@@ -310,26 +331,26 @@ private bool startsWithUriScheme(ByteString str)
if (ch < 'A' || ch > 'z' || (ch > 'Z' && ch < 'a')) // ASCII letter
{
- return false;
+ return ByteString.Empty;
}
}
if (str[i++] != ':')
{
- return false;
+ return ByteString.Empty;
}
if (str[i++] != '/')
{
- return false;
+ return ByteString.Empty;
}
if (str[i] != '/')
{
- return false;
+ return ByteString.Empty;
}
- return true;
+ return new ByteString(str.Data.Slice(0, i - 2));
}
}
}
diff --git a/src/Titanium.Web.Proxy/Http2/Http2Helper.cs b/src/Titanium.Web.Proxy/Http2/Http2Helper.cs
index 0e8835d1e..a0081aed4 100644
--- a/src/Titanium.Web.Proxy/Http2/Http2Helper.cs
+++ b/src/Titanium.Web.Proxy/Http2/Http2Helper.cs
@@ -263,9 +263,9 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output,
request.HttpVersion = HttpVersion.Version20;
request.Method = method.GetString();
+ request.IsHttps = headerListener.Scheme == ProxyServer.UriSchemeHttps;
+ request.Authority = headerListener.Authority.GetString();
request.OriginalUrlData = path;
- request.Scheme = headerListener.Scheme;
- request.Hostname = headerListener.Authority.GetString();
//request.RequestUri = headerListener.GetUri();
}
@@ -468,9 +468,10 @@ private static async Task sendHeader(Http2Settings settings, Http2FrameHeader fr
if (rr is Request request)
{
+ var uri = request.RequestUri;
encoder.EncodeHeader(writer, StaticTable.KnownHeaderMethod, request.Method.GetByteString());
- encoder.EncodeHeader(writer, StaticTable.KnownHeaderAuhtority, request.RequestUri.Host.GetByteString());
- encoder.EncodeHeader(writer, StaticTable.KnownHeaderScheme, request.RequestUri.Scheme.GetByteString());
+ encoder.EncodeHeader(writer, StaticTable.KnownHeaderAuhtority, uri.Authority.GetByteString());
+ encoder.EncodeHeader(writer, StaticTable.KnownHeaderScheme, uri.Scheme.GetByteString());
encoder.EncodeHeader(writer, StaticTable.KnownHeaderPath, request.Url.GetByteString(), false,
HpackUtil.IndexType.None, false);
}
@@ -572,10 +573,6 @@ class Http2Settings
class MyHeaderListener : IHeaderListener
{
- private static ByteString SchemeHttp = (ByteString)ProxyServer.UriSchemeHttp;
-
- private static ByteString SchemeHttps = (ByteString)ProxyServer.UriSchemeHttps;
-
private readonly Action addHeaderFunc;
public ByteString Method { get; private set; }
@@ -592,12 +589,12 @@ public string Scheme
{
get
{
- if (scheme.Equals(SchemeHttp))
+ if (scheme.Equals(ProxyServer.UriSchemeHttp8))
{
return ProxyServer.UriSchemeHttp;
}
- if (scheme.Equals(SchemeHttps))
+ if (scheme.Equals(ProxyServer.UriSchemeHttps8))
{
return ProxyServer.UriSchemeHttps;
}
diff --git a/src/Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs b/src/Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs
index 27cff2642..a364a9b2d 100644
--- a/src/Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs
+++ b/src/Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs
@@ -1,4 +1,5 @@
-using System.Net;
+using System.Diagnostics;
+using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions;
@@ -9,6 +10,7 @@ namespace Titanium.Web.Proxy.Models
/// A proxy endpoint that the client is aware of.
/// So client application know that it is communicating with a proxy server.
///
+ [DebuggerDisplay("Explicit: {IpAddress}:{Port}")]
public class ExplicitProxyEndPoint : ProxyEndPoint
{
///
diff --git a/src/Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs b/src/Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs
index 8319f9c17..fe5018735 100644
--- a/src/Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs
+++ b/src/Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs
@@ -1,4 +1,5 @@
-using System.Net;
+using System.Diagnostics;
+using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions;
@@ -9,6 +10,7 @@ namespace Titanium.Web.Proxy.Models
/// A proxy end point client is not aware of.
/// Useful when requests are redirected to this proxy end point through port forwarding via router.
///
+ [DebuggerDisplay("Explicit: {IpAddress}:{Port}")]
public class TransparentProxyEndPoint : ProxyEndPoint
{
///
diff --git a/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
index 9af0a0248..ac11521b1 100644
--- a/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
+++ b/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
@@ -125,9 +125,10 @@ internal async Task GetConnectionCacheKey(ProxyServer server, SessionEve
session.CustomUpStreamProxyUsed = customUpStreamProxy;
+ var uri = session.HttpClient.Request.RequestUri;
return GetConnectionCacheKey(
- session.HttpClient.Request.RequestUri.Host,
- session.HttpClient.Request.RequestUri.Port,
+ uri.Host,
+ uri.Port,
isHttps, applicationProtocols,
session.HttpClient.UpStreamEndPoint ?? server.UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? server.UpStreamHttpsProxy : server.UpStreamHttpProxy));
@@ -179,9 +180,10 @@ internal async Task GetServerConnection(ProxyServer server,
session.CustomUpStreamProxyUsed = customUpStreamProxy;
+ var uri = session.HttpClient.Request.RequestUri;
return await GetServerConnection(
- session.HttpClient.Request.RequestUri.Host,
- session.HttpClient.Request.RequestUri.Port,
+ uri.Host,
+ uri.Port,
session.HttpClient.Request.HttpVersion,
isHttps, applicationProtocols, isConnect,
server, session, session.HttpClient.UpStreamEndPoint ?? server.UpStreamEndPoint,
@@ -372,9 +374,11 @@ private async Task createServerConnection(string remoteHost
if (useUpstreamProxy && (isConnect || isHttps))
{
var writer = new HttpRequestWriter(stream, proxyServer.BufferPool);
- var connectRequest = new ConnectRequest(remoteHostName)
+ string authority = $"{remoteHostName}:{remotePort}";
+ var connectRequest = new ConnectRequest(authority)
{
- OriginalUrlData = HttpHeader.Encoding.GetBytes($"{remoteHostName}:{remotePort}"),
+ IsHttps = isHttps,
+ OriginalUrlData = HttpHeader.Encoding.GetBytes(authority),
HttpVersion = httpVersion
};
diff --git a/src/Titanium.Web.Proxy/ProxyServer.cs b/src/Titanium.Web.Proxy/ProxyServer.cs
index 9a0d4f123..31e3ff34f 100644
--- a/src/Titanium.Web.Proxy/ProxyServer.cs
+++ b/src/Titanium.Web.Proxy/ProxyServer.cs
@@ -32,6 +32,10 @@ public partial class ProxyServer : IDisposable
internal static readonly string UriSchemeHttp = Uri.UriSchemeHttp;
internal static readonly string UriSchemeHttps = Uri.UriSchemeHttps;
+ internal static ByteString UriSchemeHttp8 = (ByteString)UriSchemeHttp;
+ internal static ByteString UriSchemeHttps8 = (ByteString)UriSchemeHttps;
+
+
///
/// A default exception log func.
///
diff --git a/src/Titanium.Web.Proxy/RequestHandler.cs b/src/Titanium.Web.Proxy/RequestHandler.cs
index f110bb5a9..14ecbc241 100644
--- a/src/Titanium.Web.Proxy/RequestHandler.cs
+++ b/src/Titanium.Web.Proxy/RequestHandler.cs
@@ -84,8 +84,8 @@ await HeaderParser.ReadHeaders(clientStream, args.HttpClient.Request.Headers,
var request = args.HttpClient.Request;
if (connectRequest != null)
{
- request.Scheme = connectRequest.Scheme;
- request.Hostname = connectRequest.Hostname;
+ request.IsHttps = connectRequest.IsHttps;
+ request.Authority = connectRequest.Authority;
}
request.OriginalUrlData = HttpHeader.Encoding.GetBytes(httpUrl);
diff --git a/tests/Titanium.Web.Proxy.IntegrationTests/Helpers/HttpContinueClient.cs b/tests/Titanium.Web.Proxy.IntegrationTests/Helpers/HttpContinueClient.cs
index feca67d23..3031fcf2f 100644
--- a/tests/Titanium.Web.Proxy.IntegrationTests/Helpers/HttpContinueClient.cs
+++ b/tests/Titanium.Web.Proxy.IntegrationTests/Helpers/HttpContinueClient.cs
@@ -34,7 +34,7 @@ public async Task Post(string server, int port, string content)
var buffer = new byte[1024];
var responseMsg = string.Empty;
- Response response = null;
+ Response response;
while ((response = HttpMessageParsing.ParseResponse(responseMsg)) == null)
{