From 3d6e72a07405e4557d16db65f1cb923dee7f25ef Mon Sep 17 00:00:00 2001 From: Honfika Date: Sat, 16 Nov 2019 21:42:04 +0100 Subject: [PATCH] fixes --- .../Compression/CompressionUtil.cs | 6 +- .../ExplicitClientHandler.cs | 19 +++-- src/Titanium.Web.Proxy/Http/ConnectRequest.cs | 4 +- src/Titanium.Web.Proxy/Http/HttpWebClient.cs | 4 ++ src/Titanium.Web.Proxy/Http/Request.cs | 69 ++++++++++++------- src/Titanium.Web.Proxy/Http2/Http2Helper.cs | 17 ++--- .../Models/ExplicitProxyEndPoint.cs | 4 +- .../Models/TransparentProxyEndPoint.cs | 4 +- .../Network/Tcp/TcpConnectionFactory.cs | 16 +++-- src/Titanium.Web.Proxy/ProxyServer.cs | 4 ++ src/Titanium.Web.Proxy/RequestHandler.cs | 4 +- .../Helpers/HttpContinueClient.cs | 2 +- 12 files changed, 93 insertions(+), 60 deletions(-) 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) {