Commit f69bed23 authored by titanium007's avatar titanium007

Fix connection cache

parent 8242536d
...@@ -93,8 +93,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -93,8 +93,8 @@ namespace Titanium.Web.Proxy.EventArguments
//if (ProxyRequest != null) //if (ProxyRequest != null)
// ProxyRequest.Abort(); // ProxyRequest.Abort();
if (ResponseStream != null) //if (ResponseStream != null)
ResponseStream.Dispose(); // ResponseStream.Dispose();
//if (ServerResponse != null) //if (ServerResponse != null)
// ServerResponse.Close(); // ServerResponse.Close();
......
...@@ -81,36 +81,42 @@ namespace Titanium.Web.Proxy.Http ...@@ -81,36 +81,42 @@ namespace Titanium.Web.Proxy.Http
public Request Request { get; set; } public Request Request { get; set; }
public Response Response { get; set; } public Response Response { get; set; }
public TcpClient Client { get; set; } public TcpConnection Client { get; set; }
public void SetConnection(TcpConnection Connection)
{
Client = Connection;
ServerStreamReader = Client.ServerStreamReader;
}
public HttpWebSession() public HttpWebSession()
{ {
this.Request = new Request(); this.Request = new Request();
this.Response = new Response(); this.Response = new Response();
} }
public CustomBinaryReader ServerStreamReader { get; set; } public CustomBinaryReader ServerStreamReader { get; set; }
public async Task SendRequest() public async Task SendRequest()
{ {
Stream stream = Client.GetStream(); Stream stream = Client.Stream;
StringBuilder requestLines = new StringBuilder(); StringBuilder requestLines = new StringBuilder();
requestLines.AppendLine(string.Join(" ", new string[3] requestLines.AppendLine(string.Join(" ", new string[3]
{ {
this.Request.Method, this.Request.Method,
this.Request.RequestUri.AbsolutePath, this.Request.RequestUri.PathAndQuery,
this.Request.Version this.Request.Version
})); }));
foreach (HttpHeader httpHeader in this.Request.RequestHeaders) foreach (HttpHeader httpHeader in this.Request.RequestHeaders)
{ {
requestLines.AppendLine(httpHeader.Name + ':' + httpHeader.Value); requestLines.AppendLine(httpHeader.Name + ':' + httpHeader.Value);
} }
requestLines.AppendLine(); requestLines.AppendLine();
requestLines.AppendLine(); requestLines.AppendLine();
...@@ -121,10 +127,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -121,10 +127,8 @@ namespace Titanium.Web.Proxy.Http
} }
public void ReceiveResponse() public void ReceiveResponse()
{ {
Stream stream = Client.GetStream(); var httpResult = ServerStreamReader.ReadLine().Split(new char[] { ' ' }, 3);
ServerStreamReader = new CustomBinaryReader(stream, Encoding.ASCII);
var httpResult = ServerStreamReader.ReadLine().Split(' ');
var httpVersion = httpResult[0]; var httpVersion = httpResult[0];
...@@ -141,17 +145,14 @@ namespace Titanium.Web.Proxy.Http ...@@ -141,17 +145,14 @@ namespace Titanium.Web.Proxy.Http
this.Response.ResponseProtocolVersion = version; this.Response.ResponseProtocolVersion = version;
this.Response.ResponseStatusCode = httpResult[1]; this.Response.ResponseStatusCode = httpResult[1];
string status = httpResult[2]; string status = httpResult[2];
for (int i = 3; i < httpResult.Length; i++)
{
status = status + Space + httpResult[i];
}
this.Response.ResponseStatusDescription = status; this.Response.ResponseStatusDescription = status;
List<string> responseLines = ServerStreamReader.ReadAllLines(); List<string> responseLines = ServerStreamReader.ReadAllLines();
for (int index = 0; index < responseLines.Count; ++index) for (int index = 0; index < responseLines.Count; ++index)
{ {
string[] strArray = responseLines[index].Split(':'); string[] strArray = responseLines[index].Split(new char[] { ':' }, 2);
this.Response.ResponseHeaders.Add(new HttpHeader(strArray[0], strArray[1])); this.Response.ResponseHeaders.Add(new HttpHeader(strArray[0], strArray[1]));
} }
} }
......
...@@ -7,25 +7,32 @@ using System.Collections.Concurrent; ...@@ -7,25 +7,32 @@ using System.Collections.Concurrent;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.IO; using System.IO;
using System.Net.Security; using System.Net.Security;
using Titanium.Web.Proxy.Helpers;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
public class TcpConnection
{
public TcpClient Client { get; set; }
public CustomBinaryReader ServerStreamReader { get; set; }
public Stream Stream { get; set; }
}
internal class TcpConnectionManager internal class TcpConnectionManager
{ {
static ConcurrentDictionary<string, ConcurrentStack<TcpClient>> ConnectionCache = new ConcurrentDictionary<string, ConcurrentStack<TcpClient>>(); static ConcurrentDictionary<string, ConcurrentStack<TcpConnection>> ConnectionCache = new ConcurrentDictionary<string, ConcurrentStack<TcpConnection>>();
public static async Task<TcpClient> GetClient(string Hostname, int port, bool IsSecure) public static async Task<TcpConnection> GetClient(string Hostname, int port, bool IsSecure)
{ {
var key = string.Concat(Hostname, ":", port, ":", IsSecure); var key = string.Concat(Hostname, ":", port, ":", IsSecure);
ConcurrentStack<TcpClient> connections; ConcurrentStack<TcpConnection> connections;
if (!ConnectionCache.TryGetValue(key, out connections)) if (!ConnectionCache.TryGetValue(key, out connections))
{ {
return await CreateClient(Hostname, port, IsSecure); return await CreateClient(Hostname, port, IsSecure);
} }
TcpClient client; TcpConnection client;
if (!connections.TryPop(out client)) if (!connections.TryPop(out client))
{ {
return await CreateClient(Hostname, port, IsSecure); return await CreateClient(Hostname, port, IsSecure);
...@@ -33,7 +40,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -33,7 +40,7 @@ namespace Titanium.Web.Proxy.Http
return client; return client;
} }
private static async Task<TcpClient> CreateClient(string Hostname, int port, bool IsSecure) private static async Task<TcpConnection> CreateClient(string Hostname, int port, bool IsSecure)
{ {
var client = new TcpClient(Hostname, port); var client = new TcpClient(Hostname, port);
var stream = (Stream)client.GetStream(); var stream = (Stream)client.GetStream();
...@@ -54,17 +61,18 @@ namespace Titanium.Web.Proxy.Http ...@@ -54,17 +61,18 @@ namespace Titanium.Web.Proxy.Http
throw; throw;
} }
} }
return client;
return new TcpConnection() { Client = client, ServerStreamReader = new CustomBinaryReader(stream, Encoding.ASCII), Stream = stream };
} }
public static void AddClient(string Hostname, int port, bool IsSecure, TcpClient Client) public static void AddClient(string Hostname, int port, bool IsSecure, TcpConnection Client)
{ {
var key = string.Concat(Hostname, ":", port, ":", IsSecure); var key = string.Concat(Hostname, ":", port, ":", IsSecure);
ConcurrentStack<TcpClient> connections; ConcurrentStack<TcpConnection> connections;
if (!ConnectionCache.TryGetValue(key, out connections)) if (!ConnectionCache.TryGetValue(key, out connections))
{ {
connections = new ConcurrentStack<TcpClient>(); connections = new ConcurrentStack<TcpConnection>();
connections.Push(Client); connections.Push(Client);
ConnectionCache.TryAdd(key, connections); ConnectionCache.TryAdd(key, connections);
} }
......
...@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy ...@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy
private static readonly Regex CookieSplitRegEx = new Regex(@",(?! )"); private static readonly Regex CookieSplitRegEx = new Regex(@",(?! )");
private static readonly byte[] ChunkTrail = Encoding.ASCII.GetBytes(Environment.NewLine); private static readonly byte[] NewLineBytes = Encoding.ASCII.GetBytes(Environment.NewLine);
private static readonly byte[] ChunkEnd = private static readonly byte[] ChunkEnd =
Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine); Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine);
......
...@@ -30,6 +30,7 @@ namespace Titanium.Web.Proxy ...@@ -30,6 +30,7 @@ namespace Titanium.Web.Proxy
try try
{ {
//read the first line HTTP command //read the first line HTTP command
var httpCmd = clientStreamReader.ReadLine(); var httpCmd = clientStreamReader.ReadLine();
if (string.IsNullOrEmpty(httpCmd)) if (string.IsNullOrEmpty(httpCmd))
...@@ -156,7 +157,7 @@ namespace Titanium.Web.Proxy ...@@ -156,7 +157,7 @@ namespace Titanium.Web.Proxy
while (!string.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine())) while (!string.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
{ {
var header = tmpLine.Split(ColonSpaceSplit, 2, StringSplitOptions.None); var header = tmpLine.Split(new char[] { ':' }, 2);
args.RequestHeaders.Add(new HttpHeader(header[0], header[1])); args.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
} }
...@@ -175,10 +176,11 @@ namespace Titanium.Web.Proxy ...@@ -175,10 +176,11 @@ namespace Titanium.Web.Proxy
} }
} }
//construct the web request that we are going to issue on behalf of the client.
args.ProxySession = new Http.HttpWebSession(); args.ProxySession = new Http.HttpWebSession();
args.ProxySession.Request.RequestUri = httpRemoteUri; args.ProxySession.Request.RequestUri = httpRemoteUri;
//args.ProxyRequest.Proxy = null; //args.ProxyRequest.Proxy = null;
//args.ProxyRequest.UseDefaultCredentials = true; //args.ProxyRequest.UseDefaultCredentials = true;
args.ProxySession.Request.Method = httpMethod; args.ProxySession.Request.Method = httpMethod;
...@@ -197,8 +199,8 @@ namespace Titanium.Web.Proxy ...@@ -197,8 +199,8 @@ namespace Titanium.Web.Proxy
//args.RequestIsAlive = args.ProxyRequest.KeepAlive; //args.RequestIsAlive = args.ProxyRequest.KeepAlive;
//args.ProxyRequest.AllowWriteStreamBuffering = true; //args.ProxyRequest.AllowWriteStreamBuffering = true;
args.Client = await TcpConnectionManager.GetClient(args.ProxySession.Request.RequestUri.Host, args.ProxySession.Request.RequestUri.Port, args.IsHttps);
args.ProxySession.Client = args.Client;
//If requested interception //If requested interception
if (BeforeRequest != null) if (BeforeRequest != null)
{ {
...@@ -215,7 +217,9 @@ namespace Titanium.Web.Proxy ...@@ -215,7 +217,9 @@ namespace Titanium.Web.Proxy
} }
SetRequestHeaders(args.RequestHeaders, args.ProxySession); SetRequestHeaders(args.RequestHeaders, args.ProxySession);
//construct the web request that we are going to issue on behalf of the client.
var connection = await TcpConnectionManager.GetClient(args.ProxySession.Request.RequestUri.Host, args.ProxySession.Request.RequestUri.Port, args.IsHttps);
args.ProxySession.SetConnection(connection);
await args.ProxySession.SendRequest(); await args.ProxySession.SendRequest();
//If request was modified by user //If request was modified by user
...@@ -233,8 +237,8 @@ namespace Titanium.Web.Proxy ...@@ -233,8 +237,8 @@ namespace Titanium.Web.Proxy
SendClientRequestBody(args); SendClientRequestBody(args);
} }
} }
await HandleHttpSessionResponse(args); HandleHttpSessionResponse(args);
//if connection is closing exit //if connection is closing exit
if (args.ResponseHeaders.Any(x => x.Name.ToLower() == "connection" && x.Value.ToLower() == "close")) if (args.ResponseHeaders.Any(x => x.Name.ToLower() == "connection" && x.Value.ToLower() == "close"))
...@@ -243,7 +247,7 @@ namespace Titanium.Web.Proxy ...@@ -243,7 +247,7 @@ namespace Titanium.Web.Proxy
return; return;
} }
//TcpConnectionManager.AddClient(args.ProxySession.Request.RequestUri.Host, args.ProxySession.Request.RequestUri.Port, args.IsHttps, args.ProxySession.Client); TcpConnectionManager.AddClient(args.ProxySession.Request.RequestUri.Host, args.ProxySession.Request.RequestUri.Port, args.IsHttps, args.ProxySession.Client);
// read the next request // read the next request
httpCmd = clientStreamReader.ReadLine(); httpCmd = clientStreamReader.ReadLine();
...@@ -351,10 +355,27 @@ namespace Titanium.Web.Proxy ...@@ -351,10 +355,27 @@ namespace Titanium.Web.Proxy
break; break;
} }
} }
FixRequestProxyHeaders(requestHeaders);
webRequest.Request.RequestHeaders = requestHeaders; webRequest.Request.RequestHeaders = requestHeaders;
} }
private static void FixRequestProxyHeaders(List<HttpHeader> headers)
{
//If proxy-connection close was returned inform to close the connection
var proxyHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "proxy-connection");
var connectionheader = headers.FirstOrDefault(x => x.Name.ToLower() == "connection");
if (proxyHeader != null)
if (connectionheader == null)
{
headers.Add(new HttpHeader("connection", proxyHeader.Value));
}
else
{
connectionheader.Value = proxyHeader.Value;
}
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
}
//This is called when the request is PUT/POST to read the body //This is called when the request is PUT/POST to read the body
private static void SendClientRequestBody(SessionEventArgs args) private static void SendClientRequestBody(SessionEventArgs args)
{ {
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
...@@ -17,10 +18,10 @@ namespace Titanium.Web.Proxy ...@@ -17,10 +18,10 @@ namespace Titanium.Web.Proxy
partial class ProxyServer partial class ProxyServer
{ {
//Called asynchronously when a request was successfully and we received the response //Called asynchronously when a request was successfully and we received the response
private static async Task HandleHttpSessionResponse(SessionEventArgs args) private static void HandleHttpSessionResponse(SessionEventArgs args)
{ {
args.ProxySession.ReceiveResponse(); args.ProxySession.ReceiveResponse();
try try
{ {
...@@ -63,12 +64,14 @@ namespace Titanium.Web.Proxy ...@@ -63,12 +64,14 @@ namespace Titanium.Web.Proxy
} }
else else
{ {
// var isChunked = args.ProxySession.ResponseHeaders.Any(x => x.Name.ToLower() == "transfer-encoding" && x.Value.ToLower().Contains("chunked")); var isChunked = args.ProxySession.Response.ResponseHeaders.Any(x => x.Name.ToLower() == "transfer-encoding" && x.Value.ToLower().Contains("chunked"));
WriteResponseStatus(args.ProxySession.Response.ResponseProtocolVersion, args.ProxySession.Response.ResponseStatusCode, WriteResponseStatus(args.ProxySession.Response.ResponseProtocolVersion, args.ProxySession.Response.ResponseStatusCode,
args.ProxySession.Response.ResponseStatusDescription, args.ClientStreamWriter); args.ProxySession.Response.ResponseStatusDescription, args.ClientStreamWriter);
WriteResponseHeaders(args.ClientStreamWriter, args.ResponseHeaders); WriteResponseHeaders(args.ClientStreamWriter, args.ResponseHeaders);
WriteResponseBody(args.ResponseStream, args.ClientStream, false, args.ProxySession.Response.ContentLength);
if (isChunked || args.ProxySession.Response.ContentLength > 0)
WriteResponseBody(args.ResponseStream, args.ClientStream, isChunked, args.ProxySession.Response.ContentLength);
} }
args.ClientStream.Flush(); args.ClientStream.Flush();
...@@ -90,6 +93,10 @@ namespace Titanium.Web.Proxy ...@@ -90,6 +93,10 @@ namespace Titanium.Web.Proxy
{ {
switch (response.Response.ResponseHeaders[i].Name.ToLower()) switch (response.Response.ResponseHeaders[i].Name.ToLower())
{ {
case "content-length":
response.Response.ContentLength = int.Parse(response.Response.ResponseHeaders[i].Value);
break;
case "content-encoding": case "content-encoding":
response.Response.ResponseContentEncoding = response.Response.ResponseHeaders[i].Value; response.Response.ResponseContentEncoding = response.Response.ResponseHeaders[i].Value;
break; break;
...@@ -105,14 +112,10 @@ namespace Titanium.Web.Proxy ...@@ -105,14 +112,10 @@ namespace Titanium.Web.Proxy
break; break;
case "connection":
break;
default: default:
break; break;
} }
} }
// response.ResponseHeaders.RemoveAll(x => x.Name.ToLower() == "connection");
return response.Response.ResponseHeaders; return response.Response.ResponseHeaders;
} }
...@@ -128,7 +131,7 @@ namespace Titanium.Web.Proxy ...@@ -128,7 +131,7 @@ namespace Titanium.Web.Proxy
{ {
if (headers != null) if (headers != null)
{ {
//FixProxyHeaders(headers); FixResponseProxyHeaders(headers);
foreach (var header in headers) foreach (var header in headers)
{ {
...@@ -139,23 +142,29 @@ namespace Titanium.Web.Proxy ...@@ -139,23 +142,29 @@ namespace Titanium.Web.Proxy
responseWriter.WriteLine(); responseWriter.WriteLine();
responseWriter.Flush(); responseWriter.Flush();
} }
private static void FixProxyHeaders(List<HttpHeader> headers) private static void FixResponseProxyHeaders(List<HttpHeader> headers)
{ {
//If proxy-connection close was returned inform to close the connection //If proxy-connection close was returned inform to close the connection
if (headers.Any(x => x.Name.ToLower() == "proxy-connection" && x.Value.ToLower() == "close")) var proxyHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "proxy-connection");
if (headers.Any(x => x.Name.ToLower() == "connection") == false) var connectionHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "connection");
if (proxyHeader != null)
if (connectionHeader == null)
{ {
headers.Add(new HttpHeader("connection", "close")); headers.Add(new HttpHeader("connection", proxyHeader.Value));
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
} }
else else
headers.Find(x => x.Name.ToLower() == "connection").Value = "close"; {
connectionHeader.Value = "close";
}
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
} }
private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers, int length, private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers, int length,
bool isChunked) bool isChunked)
{ {
FixProxyHeaders(headers); FixResponseProxyHeaders(headers);
if (!isChunked) if (!isChunked)
{ {
...@@ -194,12 +203,27 @@ namespace Titanium.Web.Proxy ...@@ -194,12 +203,27 @@ namespace Titanium.Web.Proxy
{ {
if (!isChunked) if (!isChunked)
{ {
int bytesToRead = BUFFER_SIZE;
if (BodyLength < BUFFER_SIZE)
bytesToRead = BodyLength;
var buffer = new byte[BUFFER_SIZE]; var buffer = new byte[BUFFER_SIZE];
int bytesRead; var bytesRead = 0;
while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0) var totalBytesRead = 0;
while ((bytesRead += inStream.Read(buffer, 0, bytesToRead)) > 0)
{ {
outStream.Write(buffer, 0, bytesRead); outStream.Write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
if (totalBytesRead == BodyLength)
break;
bytesRead = 0;
var remainingBytes = (BodyLength - totalBytesRead);
bytesToRead = remainingBytes > BUFFER_SIZE ? BUFFER_SIZE : remainingBytes;
} }
} }
else else
...@@ -209,20 +233,35 @@ namespace Titanium.Web.Proxy ...@@ -209,20 +233,35 @@ namespace Titanium.Web.Proxy
//Send chunked response //Send chunked response
private static void WriteResponseBodyChunked(Stream inStream, Stream outStream) private static void WriteResponseBodyChunked(Stream inStream, Stream outStream)
{ {
var buffer = new byte[BUFFER_SIZE]; var inStreamReader = new CustomBinaryReader(inStream, Encoding.ASCII);
while (true)
int bytesRead;
while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0)
{ {
var chunkHead = Encoding.ASCII.GetBytes(bytesRead.ToString("x2")); var chuchkHead = inStreamReader.ReadLine();
var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber);
if (chunkSize != 0)
{
var buffer = inStreamReader.ReadBytes(chunkSize);
var chunkHead = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));
outStream.Write(chunkHead, 0, chunkHead.Length);
outStream.Write(NewLineBytes, 0, NewLineBytes.Length);
outStream.Write(buffer, 0, chunkSize);
outStream.Write(NewLineBytes, 0, NewLineBytes.Length);
outStream.Write(chunkHead, 0, chunkHead.Length); inStreamReader.ReadLine();
outStream.Write(ChunkTrail, 0, ChunkTrail.Length); }
outStream.Write(buffer, 0, bytesRead); else
outStream.Write(ChunkTrail, 0, ChunkTrail.Length); {
inStreamReader.ReadLine();
outStream.Write(ChunkEnd, 0, ChunkEnd.Length);
break;
}
} }
outStream.Write(ChunkEnd, 0, ChunkEnd.Length);
} }
private static void WriteResponseBodyChunked(byte[] data, Stream outStream) private static void WriteResponseBodyChunked(byte[] data, Stream outStream)
...@@ -230,9 +269,9 @@ namespace Titanium.Web.Proxy ...@@ -230,9 +269,9 @@ namespace Titanium.Web.Proxy
var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2")); var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2"));
outStream.Write(chunkHead, 0, chunkHead.Length); outStream.Write(chunkHead, 0, chunkHead.Length);
outStream.Write(ChunkTrail, 0, ChunkTrail.Length); outStream.Write(NewLineBytes, 0, NewLineBytes.Length);
outStream.Write(data, 0, data.Length); outStream.Write(data, 0, data.Length);
outStream.Write(ChunkTrail, 0, ChunkTrail.Length); outStream.Write(NewLineBytes, 0, NewLineBytes.Length);
outStream.Write(ChunkEnd, 0, ChunkEnd.Length); outStream.Write(ChunkEnd, 0, ChunkEnd.Length);
} }
......
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