Commit 2b1ad86f authored by titanium007's avatar titanium007

Use Version instead of string object

parent 9cfe6e7e
...@@ -36,7 +36,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -36,7 +36,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Does this session uses SSL /// Does this session uses SSL
/// </summary> /// </summary>
public bool IsHttps { get; internal set; } public bool IsHttps => WebSession.Request.RequestUri.Scheme == Uri.UriSchemeHttps;
/// <summary> /// <summary>
/// A web session corresponding to a single request/response sequence /// A web session corresponding to a single request/response sequence
...@@ -87,7 +87,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -87,7 +87,7 @@ namespace Titanium.Web.Proxy.EventArguments
await this.Client.ClientStreamReader.CopyBytesToStream(requestBodyStream, WebSession.Request.ContentLength).ConfigureAwait(false); await this.Client.ClientStreamReader.CopyBytesToStream(requestBodyStream, WebSession.Request.ContentLength).ConfigureAwait(false);
} }
else if (WebSession.Request.HttpVersion.ToLower().Trim().Equals("http/1.0")) else if(WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0)
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(requestBodyStream, long.MaxValue).ConfigureAwait(false); await WebSession.ServerConnection.StreamReader.CopyBytesToStream(requestBodyStream, long.MaxValue).ConfigureAwait(false);
} }
WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray()).ConfigureAwait(false); WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray()).ConfigureAwait(false);
...@@ -123,7 +123,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -123,7 +123,7 @@ namespace Titanium.Web.Proxy.EventArguments
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength).ConfigureAwait(false); await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength).ConfigureAwait(false);
} }
else if(WebSession.Response.HttpVersion.ToLower().Trim().Equals("http/1.0")) else if(WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0)
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, long.MaxValue).ConfigureAwait(false); await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, long.MaxValue).ConfigureAwait(false);
} }
......
...@@ -46,7 +46,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -46,7 +46,7 @@ namespace Titanium.Web.Proxy.Http
{ {
this.Request.Method, this.Request.Method,
this.Request.RequestUri.PathAndQuery, this.Request.RequestUri.PathAndQuery,
this.Request.HttpVersion string.Format("HTTP/{0}.{1}",this.Request.HttpVersion.Major, this.Request.HttpVersion.Minor)
})); }));
foreach (HttpHeader httpHeader in this.Request.RequestHeaders) foreach (HttpHeader httpHeader in this.Request.RequestHeaders)
...@@ -95,8 +95,18 @@ namespace Titanium.Web.Proxy.Http ...@@ -95,8 +95,18 @@ namespace Titanium.Web.Proxy.Http
{ {
await ServerConnection.StreamReader.ReadLineAsync().ConfigureAwait(false); await ServerConnection.StreamReader.ReadLineAsync().ConfigureAwait(false);
} }
var httpVersion = httpResult[0].Trim().ToLower();
Version version;
if (httpVersion == "http/1.1")
{
version = new Version(1, 1);
}
else
{
version = new Version(1, 0);
}
this.Response.HttpVersion = httpResult[0].Trim(); this.Response.HttpVersion = version;
this.Response.ResponseStatusCode = httpResult[1].Trim(); this.Response.ResponseStatusCode = httpResult[1].Trim();
this.Response.ResponseStatusDescription = httpResult[2].Trim(); this.Response.ResponseStatusDescription = httpResult[2].Trim();
......
...@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Http
{ {
public string Method { get; set; } public string Method { get; set; }
public Uri RequestUri { get; set; } public Uri RequestUri { get; set; }
public string HttpVersion { get; set; } public Version HttpVersion { get; set; }
internal string Host internal string Host
{ {
......
...@@ -4,6 +4,7 @@ using System.Linq; ...@@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using System;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
...@@ -31,7 +32,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -31,7 +32,7 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal string HttpVersion { get; set; } internal Version HttpVersion { get; set; }
internal bool ResponseKeepAlive internal bool ResponseKeepAlive
{ {
get get
......
...@@ -37,21 +37,31 @@ namespace Titanium.Web.Proxy.Network ...@@ -37,21 +37,31 @@ namespace Titanium.Web.Proxy.Network
internal class TcpConnectionManager internal class TcpConnectionManager
{ {
static List<TcpConnection> ConnectionCache = new List<TcpConnection>(); static List<TcpConnection> connectionCache = new List<TcpConnection>();
static SemaphoreSlim connectionAccessLock = new SemaphoreSlim(1);
internal static async Task<TcpConnection> GetClient(SessionEventArgs sessionArgs, string hostname, int port, bool isSecure, Version version) internal static async Task<TcpConnection> GetClient(SessionEventArgs sessionArgs, string hostname, int port, bool isSecure, Version version)
{ {
TcpConnection cached = null; TcpConnection cached = null;
while (true) while (true)
{ {
lock (ConnectionCache) await connectionAccessLock.WaitAsync();
try
{ {
cached = ConnectionCache.FirstOrDefault(x => x.HostName == hostname && x.port == port && cached = connectionCache.FirstOrDefault(x => x.HostName == hostname && x.port == port &&
x.IsSecure == isSecure && x.TcpClient.Connected && x.Version.Equals(version)); x.IsSecure == isSecure && x.TcpClient.Connected && x.Version.Equals(version));
//just create one more preemptively
if (connectionCache.Where(x => x.HostName == hostname && x.port == port &&
x.IsSecure == isSecure && x.TcpClient.Connected && x.Version.Equals(version)).Count() < 2)
{
var task = CreateClient(sessionArgs, hostname, port, isSecure, version)
.ContinueWith(async (x) => { if (x.Status == TaskStatus.RanToCompletion) await ReleaseClient(x.Result); });
}
if (cached != null) if (cached != null)
ConnectionCache.Remove(cached); connectionCache.Remove(cached);
} }
finally { connectionAccessLock.Release(); }
if (cached != null && !cached.TcpClient.Client.IsConnected()) if (cached != null && !cached.TcpClient.Client.IsConnected())
continue; continue;
...@@ -63,14 +73,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -63,14 +73,6 @@ namespace Titanium.Web.Proxy.Network
if (cached == null) if (cached == null)
cached = await CreateClient(sessionArgs, hostname, port, isSecure, version).ConfigureAwait(false); cached = await CreateClient(sessionArgs, hostname, port, isSecure, version).ConfigureAwait(false);
//just create one more preemptively
if (ConnectionCache.Where(x => x.HostName == hostname && x.port == port &&
x.IsSecure == isSecure && x.TcpClient.Connected && x.Version.Equals(version)).Count() < 2)
{
var task = CreateClient(sessionArgs, hostname, port, isSecure, version)
.ContinueWith(x => { if (x.Status == TaskStatus.RanToCompletion) ReleaseClient(x.Result); });
}
return cached; return cached;
} }
...@@ -155,27 +157,34 @@ namespace Titanium.Web.Proxy.Network ...@@ -155,27 +157,34 @@ namespace Titanium.Web.Proxy.Network
} }
internal static void ReleaseClient(TcpConnection Connection) internal static async Task ReleaseClient(TcpConnection Connection)
{ {
Connection.LastAccess = DateTime.Now; Connection.LastAccess = DateTime.Now;
ConnectionCache.Add(Connection); await connectionAccessLock.WaitAsync();
try
{
connectionCache.Add(Connection);
}
finally { connectionAccessLock.Release(); }
} }
internal async static void ClearIdleConnections() internal async static void ClearIdleConnections()
{ {
while (true) while (true)
{ {
lock (ConnectionCache) await connectionAccessLock.WaitAsync();
try
{ {
var cutOff = DateTime.Now.AddSeconds(-60); var cutOff = DateTime.Now.AddSeconds(-60);
ConnectionCache connectionCache
.Where(x => x.LastAccess < cutOff) .Where(x => x.LastAccess < cutOff)
.ToList() .ToList()
.ForEach(x => x.TcpClient.Close()); .ForEach(x => x.TcpClient.Close());
ConnectionCache.RemoveAll(x => x.LastAccess < cutOff); connectionCache.RemoveAll(x => x.LastAccess < cutOff);
} }
finally { connectionAccessLock.Release(); }
await Task.Delay(1000 * 60 * 3).ConfigureAwait(false); await Task.Delay(1000 * 60 * 3).ConfigureAwait(false);
} }
......
...@@ -52,10 +52,21 @@ namespace Titanium.Web.Proxy ...@@ -52,10 +52,21 @@ namespace Titanium.Web.Proxy
else else
httpRemoteUri = new Uri(httpCmdSplit[1]); httpRemoteUri = new Uri(httpCmdSplit[1]);
string httpVersion = "HTTP/1.1"; Version version = new Version(1, 1);
if (httpCmdSplit.Length == 3) if (httpCmdSplit.Length == 3)
httpVersion = httpCmdSplit[2]; {
string httpVersion = httpCmdSplit[1].Trim();
if (httpVersion == "http/1.1")
{
version = new Version(1, 1);
}
else
{
version = new Version(1, 0);
}
}
var excluded = endPoint.ExcludedHttpsHostNameRegex != null ? endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false; var excluded = endPoint.ExcludedHttpsHostNameRegex != null ? endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false;
...@@ -65,7 +76,7 @@ namespace Titanium.Web.Proxy ...@@ -65,7 +76,7 @@ namespace Titanium.Web.Proxy
httpRemoteUri = new Uri("https://" + httpCmdSplit[1]); httpRemoteUri = new Uri("https://" + httpCmdSplit[1]);
await clientStreamReader.ReadAllLinesAsync().ConfigureAwait(false); await clientStreamReader.ReadAllLinesAsync().ConfigureAwait(false);
await WriteConnectResponse(clientStreamWriter, httpVersion).ConfigureAwait(false); await WriteConnectResponse(clientStreamWriter, version).ConfigureAwait(false);
var certificate = await CertManager.CreateCertificate(httpRemoteUri.Host); var certificate = await CertManager.CreateCertificate(httpRemoteUri.Host);
...@@ -101,7 +112,7 @@ namespace Titanium.Web.Proxy ...@@ -101,7 +112,7 @@ namespace Titanium.Web.Proxy
else if (httpVerb.ToUpper() == "CONNECT") else if (httpVerb.ToUpper() == "CONNECT")
{ {
await clientStreamReader.ReadAllLinesAsync().ConfigureAwait(false); await clientStreamReader.ReadAllLinesAsync().ConfigureAwait(false);
await WriteConnectResponse(clientStreamWriter, httpVersion).ConfigureAwait(false); await WriteConnectResponse(clientStreamWriter, version).ConfigureAwait(false);
await TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port, await TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
false).ConfigureAwait(false); false).ConfigureAwait(false);
...@@ -178,7 +189,7 @@ namespace Titanium.Web.Proxy ...@@ -178,7 +189,7 @@ namespace Titanium.Web.Proxy
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool IsHttps) CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool IsHttps)
{ {
TcpConnection connection = null; TcpConnection connection = null;
string lastRequestHostName = null; string lastRequest = null;
while (true) while (true)
{ {
...@@ -197,10 +208,10 @@ namespace Titanium.Web.Proxy ...@@ -197,10 +208,10 @@ namespace Titanium.Web.Proxy
var httpCmdSplit = httpCmd.Split(Constants.SpaceSplit, 3); var httpCmdSplit = httpCmd.Split(Constants.SpaceSplit, 3);
var httpMethod = httpCmdSplit[0]; var httpMethod = httpCmdSplit[0];
var httpVersion = httpCmdSplit[2]; var httpVersion = httpCmdSplit[2].ToLower().Trim();
Version version; Version version;
if (httpVersion == "HTTP/1.1") if (httpVersion == "http/1.1")
{ {
version = new Version(1, 1); version = new Version(1, 1);
} }
...@@ -219,26 +230,18 @@ namespace Titanium.Web.Proxy ...@@ -219,26 +230,18 @@ namespace Titanium.Web.Proxy
} }
var httpRemoteUri = new Uri(!IsHttps ? httpCmdSplit[1] : (string.Concat("https://", args.WebSession.Request.Host, httpCmdSplit[1]))); var httpRemoteUri = new Uri(!IsHttps ? httpCmdSplit[1] : (string.Concat("https://", args.WebSession.Request.Host, httpCmdSplit[1])));
args.IsHttps = IsHttps;
args.WebSession.Request.RequestUri = httpRemoteUri; args.WebSession.Request.RequestUri = httpRemoteUri;
args.WebSession.Request.Method = httpMethod; args.WebSession.Request.Method = httpMethod;
args.WebSession.Request.HttpVersion = httpVersion; args.WebSession.Request.HttpVersion = version;
args.Client.ClientStream = clientStream; args.Client.ClientStream = clientStream;
args.Client.ClientStreamReader = clientStreamReader; args.Client.ClientStreamReader = clientStreamReader;
args.Client.ClientStreamWriter = clientStreamWriter; args.Client.ClientStreamWriter = clientStreamWriter;
if (args.WebSession.Request.UpgradeToWebSocket)
{
await TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps).ConfigureAwait(false);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return;
}
PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession); PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession);
args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Host; args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority;
//If requested interception //If requested interception
if (BeforeRequest != null) if (BeforeRequest != null)
...@@ -254,13 +257,23 @@ namespace Titanium.Web.Proxy ...@@ -254,13 +257,23 @@ namespace Titanium.Web.Proxy
await Task.WhenAll(handlerTasks).ConfigureAwait(false); await Task.WhenAll(handlerTasks).ConfigureAwait(false);
} }
if (args.WebSession.Request.UpgradeToWebSocket)
{
await TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps).ConfigureAwait(false);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return;
}
//construct the web request that we are going to issue on behalf of the client. //construct the web request that we are going to issue on behalf of the client.
connection = connection == null ? connection = connection == null ?
await TcpConnectionManager.GetClient(args, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version).ConfigureAwait(false) await TcpConnectionManager.GetClient(args, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version).ConfigureAwait(false)
: lastRequestHostName != args.WebSession.Request.RequestUri.Host ? await TcpConnectionManager.GetClient(args, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version).ConfigureAwait(false) : lastRequest != args.WebSession.Request.RequestUri.Host ? await TcpConnectionManager.GetClient(args, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version).ConfigureAwait(false)
: connection; : connection;
lastRequestHostName = args.WebSession.Request.RequestUri.Host; lastRequest = string.Concat(args.WebSession.Request.RequestUri.Host,":",
args.WebSession.Request.RequestUri.Port,":",
args.IsHttps,":", version.ToString());
args.WebSession.Request.RequestLocked = true; args.WebSession.Request.RequestLocked = true;
...@@ -347,12 +360,12 @@ namespace Titanium.Web.Proxy ...@@ -347,12 +360,12 @@ namespace Titanium.Web.Proxy
} }
if (connection != null) if (connection != null)
TcpConnectionManager.ReleaseClient(connection); await TcpConnectionManager.ReleaseClient(connection);
} }
private static async Task WriteConnectResponse(StreamWriter clientStreamWriter, string httpVersion) private static async Task WriteConnectResponse(StreamWriter clientStreamWriter, Version httpVersion)
{ {
await clientStreamWriter.WriteLineAsync(httpVersion + " 200 Connection established").ConfigureAwait(false); await clientStreamWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2}", httpVersion.Major, httpVersion.Minor,"200 Connection established")).ConfigureAwait(false);
await clientStreamWriter.WriteLineAsync(string.Format("Timestamp: {0}", DateTime.Now)).ConfigureAwait(false); await clientStreamWriter.WriteLineAsync(string.Format("Timestamp: {0}", DateTime.Now)).ConfigureAwait(false);
await clientStreamWriter.WriteLineAsync().ConfigureAwait(false); await clientStreamWriter.WriteLineAsync().ConfigureAwait(false);
await clientStreamWriter.FlushAsync().ConfigureAwait(false); await clientStreamWriter.FlushAsync().ConfigureAwait(false);
......
...@@ -80,7 +80,8 @@ namespace Titanium.Web.Proxy ...@@ -80,7 +80,8 @@ namespace Titanium.Web.Proxy
{ {
await WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders); await WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders);
if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || args.WebSession.Response.HttpVersion.ToLower().Trim() == "http/1.0") if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 ||
(args.WebSession.Response.HttpVersion.Major == 1 && args.WebSession.Response.HttpVersion.Minor == 0))
await WriteResponseBody(args.WebSession.ServerConnection.StreamReader, args.Client.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength).ConfigureAwait(false); await WriteResponseBody(args.WebSession.ServerConnection.StreamReader, args.Client.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength).ConfigureAwait(false);
} }
...@@ -105,10 +106,10 @@ namespace Titanium.Web.Proxy ...@@ -105,10 +106,10 @@ namespace Titanium.Web.Proxy
} }
private static void WriteResponseStatus(string version, string code, string description, private static void WriteResponseStatus(Version version, string code, string description,
StreamWriter responseWriter) StreamWriter responseWriter)
{ {
responseWriter.WriteLineAsync(string.Format("{0} {1} {2}", version, code, description)); responseWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2} {3}", version.Major, version.Minor, code, description));
} }
private static async Task WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers) private static async Task WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers)
......
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