Commit e82ad51e authored by Jehonathan Thomas's avatar Jehonathan Thomas Committed by GitHub

Merge pull request #219 from justcoding121/develop

Merge with develop
parents e928c25a 8e02c62c
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=dda2ffa1_002D435c_002D4111_002D88eb_002D1a7c93c382f0/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Property (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String></wpf:ResourceDictionary>
\ No newline at end of file
using System.Text; using System;
using System.Text;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -29,7 +30,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -29,7 +30,7 @@ namespace Titanium.Web.Proxy.Extensions
foreach (var contentType in contentTypes) foreach (var contentType in contentTypes)
{ {
var encodingSplit = contentType.Split('='); var encodingSplit = contentType.Split('=');
if (encodingSplit.Length == 2 && encodingSplit[0].ToLower().Trim() == "charset") if (encodingSplit.Length == 2 && encodingSplit[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase))
{ {
return Encoding.GetEncoding(encodingSplit[1]); return Encoding.GetEncoding(encodingSplit[1]);
} }
......
using System.Text; using System;
using System.Text;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -26,7 +27,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -26,7 +27,7 @@ namespace Titanium.Web.Proxy.Extensions
foreach (var contentType in contentTypes) foreach (var contentType in contentTypes)
{ {
var encodingSplit = contentType.Split('='); var encodingSplit = contentType.Split('=');
if (encodingSplit.Length == 2 && encodingSplit[0].ToLower().Trim() == "charset") if (encodingSplit.Length == 2 && encodingSplit[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase))
{ {
return Encoding.GetEncoding(encodingSplit[1]); return Encoding.GetEncoding(encodingSplit[1]);
} }
......
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Extensions
{
internal static class StringExtensions
{
internal static bool ContainsIgnoreCase(this string str, string value)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0;
}
}
}
...@@ -3,7 +3,6 @@ using System.Collections.Generic; ...@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
...@@ -15,12 +14,30 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -15,12 +14,30 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
internal class CustomBinaryReader : IDisposable internal class CustomBinaryReader : IDisposable
{ {
private readonly Stream stream; private readonly CustomBufferedStream stream;
private readonly int bufferSize;
private readonly Encoding encoding; private readonly Encoding encoding;
internal CustomBinaryReader(Stream stream) [ThreadStatic]
private static byte[] staticBuffer;
private byte[] buffer
{
get
{
if (staticBuffer == null || staticBuffer.Length != bufferSize)
{
staticBuffer = new byte[bufferSize];
}
return staticBuffer;
}
}
internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize)
{ {
this.stream = stream; this.stream = stream;
this.bufferSize = bufferSize;
//default to UTF-8 //default to UTF-8
encoding = Encoding.UTF8; encoding = Encoding.UTF8;
...@@ -34,33 +51,41 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -34,33 +51,41 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns></returns> /// <returns></returns>
internal async Task<string> ReadLineAsync() internal async Task<string> ReadLineAsync()
{ {
using (var readBuffer = new MemoryStream()) var lastChar = default(byte);
int bufferDataLength = 0;
// try to use the thread static buffer, usually it is enough
var buffer = this.buffer;
while (stream.DataAvailable || await stream.FillBufferAsync())
{ {
var lastChar = default(char); var newChar = stream.ReadByteFromBuffer();
var buffer = new byte[1]; buffer[bufferDataLength] = newChar;
while ((await stream.ReadAsync(buffer, 0, 1)) > 0) //if new line
{ if (lastChar == '\r' && newChar == '\n')
//if new line {
if (lastChar == '\r' && buffer[0] == '\n') return encoding.GetString(buffer, 0, bufferDataLength - 1);
{ }
var result = readBuffer.ToArray(); //end of stream
return encoding.GetString(result.SubArray(0, result.Length - 1)); if (newChar == '\0')
} {
//end of stream return encoding.GetString(buffer, 0, bufferDataLength);
if (buffer[0] == '\0') }
{
return encoding.GetString(readBuffer.ToArray()); bufferDataLength++;
}
//store last char for new line comparison
await readBuffer.WriteAsync(buffer,0,1); lastChar = newChar;
//store last char for new line comparison if (bufferDataLength == buffer.Length)
lastChar = (char)buffer[0]; {
} ResizeBuffer(ref buffer, bufferDataLength * 2);
}
return encoding.GetString(readBuffer.ToArray());
} }
return encoding.GetString(buffer, 0, bufferDataLength);
} }
/// <summary> /// <summary>
...@@ -78,6 +103,17 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -78,6 +103,17 @@ namespace Titanium.Web.Proxy.Helpers
return requestLines; return requestLines;
} }
/// <summary>
/// Read until the last new line, ignores the result
/// </summary>
/// <returns></returns>
internal async Task ReadAndIgnoreAllLinesAsync()
{
while (!string.IsNullOrEmpty(await ReadLineAsync()))
{
}
}
/// <summary> /// <summary>
/// Read the specified number of raw bytes from the base stream /// Read the specified number of raw bytes from the base stream
/// </summary> /// </summary>
...@@ -91,34 +127,51 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -91,34 +127,51 @@ namespace Titanium.Web.Proxy.Helpers
if (totalBytesToRead < bufferSize) if (totalBytesToRead < bufferSize)
bytesToRead = (int)totalBytesToRead; bytesToRead = (int)totalBytesToRead;
var buffer = new byte[bufferSize]; var buffer = this.buffer;
if (bytesToRead > buffer.Length)
{
buffer = new byte[bytesToRead];
}
var bytesRead = 0; int bytesRead;
var totalBytesRead = 0; var totalBytesRead = 0;
using (var outStream = new MemoryStream()) while ((bytesRead = await stream.ReadAsync(buffer, totalBytesRead, bytesToRead)) > 0)
{ {
while ((bytesRead += await stream.ReadAsync(buffer, 0, bytesToRead)) > 0) totalBytesRead += bytesRead;
{
await outStream.WriteAsync(buffer, 0, bytesRead); if (totalBytesRead == totalBytesToRead)
totalBytesRead += bytesRead; break;
if (totalBytesRead == totalBytesToRead) var remainingBytes = totalBytesToRead - totalBytesRead;
break; bytesToRead = Math.Min(bufferSize, (int)remainingBytes);
bytesRead = 0; if (totalBytesRead + bytesToRead > buffer.Length)
var remainingBytes = (totalBytesToRead - totalBytesRead); {
bytesToRead = remainingBytes > (long)bufferSize ? bufferSize : (int)remainingBytes; ResizeBuffer(ref buffer, Math.Min(totalBytesToRead, buffer.Length * 2));
} }
}
return outStream.ToArray(); if (totalBytesRead != buffer.Length)
{
//Normally this should not happen. Resize the buffer anyway
var newBuffer = new byte[totalBytesRead];
Buffer.BlockCopy(buffer, 0, newBuffer, 0, totalBytesRead);
buffer = newBuffer;
} }
return buffer;
} }
public void Dispose() public void Dispose()
{ {
}
private void ResizeBuffer(ref byte[] buffer, long size)
{
var newBuffer = new byte[size];
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
buffer = newBuffer;
} }
} }
} }
\ No newline at end of file
This diff is collapsed.
...@@ -181,7 +181,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -181,7 +181,7 @@ namespace Titanium.Web.Proxy.Helpers
if (tmp.StartsWith("http=") || tmp.StartsWith("https=")) if (tmp.StartsWith("http=") || tmp.StartsWith("https="))
{ {
var endPoint = tmp.Substring(5); var endPoint = tmp.Substring(5);
return new HttpSystemProxyValue() return new HttpSystemProxyValue
{ {
HostName = endPoint.Split(':')[0], HostName = endPoint.Split(':')[0],
Port = int.Parse(endPoint.Split(':')[1]), Port = int.Parse(endPoint.Split(':')[1]),
......
...@@ -78,18 +78,19 @@ namespace Titanium.Web.Proxy.Http ...@@ -78,18 +78,19 @@ namespace Titanium.Web.Proxy.Http
//prepare the request & headers //prepare the request & headers
if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps)) if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps))
{ {
requestLines.AppendLine(string.Join(" ", Request.Method, Request.RequestUri.AbsoluteUri, $"HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}")); requestLines.AppendLine($"{Request.Method} {Request.RequestUri.AbsoluteUri} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}");
} }
else else
{ {
requestLines.AppendLine(string.Join(" ", Request.Method, Request.RequestUri.PathAndQuery, $"HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}")); requestLines.AppendLine($"{Request.Method} {Request.RequestUri.PathAndQuery} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}");
} }
//Send Authentication to Upstream proxy if needed //Send Authentication to Upstream proxy if needed
if (ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false && !string.IsNullOrEmpty(ServerConnection.UpStreamHttpProxy.UserName) && ServerConnection.UpStreamHttpProxy.Password != null) if (ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false && !string.IsNullOrEmpty(ServerConnection.UpStreamHttpProxy.UserName) && ServerConnection.UpStreamHttpProxy.Password != null)
{ {
requestLines.AppendLine("Proxy-Connection: keep-alive"); requestLines.AppendLine("Proxy-Connection: keep-alive");
requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(ServerConnection.UpStreamHttpProxy.UserName + ":" + ServerConnection.UpStreamHttpProxy.Password))); requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(
$"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}")));
} }
//write request headers //write request headers
foreach (var headerItem in Request.RequestHeaders) foreach (var headerItem in Request.RequestHeaders)
...@@ -97,7 +98,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -97,7 +98,7 @@ namespace Titanium.Web.Proxy.Http
var header = headerItem.Value; var header = headerItem.Value;
if (headerItem.Key != "Proxy-Authorization") if (headerItem.Key != "Proxy-Authorization")
{ {
requestLines.AppendLine(header.Name + ": " + header.Value); requestLines.AppendLine($"{header.Name}: {header.Value}");
} }
} }
...@@ -109,7 +110,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -109,7 +110,7 @@ namespace Titanium.Web.Proxy.Http
{ {
if (headerItem.Key != "Proxy-Authorization") if (headerItem.Key != "Proxy-Authorization")
{ {
requestLines.AppendLine(header.Name + ": " + header.Value); requestLines.AppendLine($"{header.Name}: {header.Value}");
} }
} }
} }
...@@ -132,13 +133,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -132,13 +133,13 @@ namespace Titanium.Web.Proxy.Http
//find if server is willing for expect continue //find if server is willing for expect continue
if (responseStatusCode.Equals("100") if (responseStatusCode.Equals("100")
&& responseStatusDescription.ToLower().Equals("continue")) && responseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase))
{ {
Request.Is100Continue = true; Request.Is100Continue = true;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
} }
else if (responseStatusCode.Equals("417") else if (responseStatusCode.Equals("417")
&& responseStatusDescription.ToLower().Equals("expectation failed")) && responseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
{ {
Request.ExpectationFailed = true; Request.ExpectationFailed = true;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
...@@ -178,7 +179,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -178,7 +179,7 @@ namespace Titanium.Web.Proxy.Http
//For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request //For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
if (Response.ResponseStatusCode.Equals("100") if (Response.ResponseStatusCode.Equals("100")
&& Response.ResponseStatusDescription.ToLower().Equals("continue")) && Response.ResponseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase))
{ {
//Read the next line after 100-continue //Read the next line after 100-continue
Response.Is100Continue = true; Response.Is100Continue = true;
...@@ -189,7 +190,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -189,7 +190,7 @@ namespace Titanium.Web.Proxy.Http
return; return;
} }
else if (Response.ResponseStatusCode.Equals("417") else if (Response.ResponseStatusCode.Equals("417")
&& Response.ResponseStatusDescription.ToLower().Equals("expectation failed")) && Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
{ {
//read next line after expectation failed response //read next line after expectation failed response
Response.ExpectationFailed = true; Response.ExpectationFailed = true;
......
...@@ -172,7 +172,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -172,7 +172,7 @@ namespace Titanium.Web.Proxy.Http
{ {
var header = RequestHeaders["transfer-encoding"]; var header = RequestHeaders["transfer-encoding"];
return header.Value.ToLower().Contains("chunked"); return header.Value.ContainsIgnoreCase("chunked");
} }
return false; return false;
...@@ -266,7 +266,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -266,7 +266,7 @@ namespace Titanium.Web.Proxy.Http
var header = RequestHeaders["upgrade"]; var header = RequestHeaders["upgrade"];
return header.Value.ToLower() == "websocket"; return header.Value.Equals("websocket", StringComparison.CurrentCultureIgnoreCase);
} }
} }
......
...@@ -54,7 +54,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -54,7 +54,7 @@ namespace Titanium.Web.Proxy.Http
{ {
var header = ResponseHeaders["connection"]; var header = ResponseHeaders["connection"];
if (header.Value.ToLower().Contains("close")) if (header.Value.ContainsIgnoreCase("close"))
{ {
return false; return false;
} }
...@@ -153,7 +153,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -153,7 +153,7 @@ namespace Titanium.Web.Proxy.Http
{ {
var header = ResponseHeaders["transfer-encoding"]; var header = ResponseHeaders["transfer-encoding"];
if (header.Value.ToLower().Contains("chunked")) if (header.Value.ContainsIgnoreCase("chunked"))
{ {
return true; return true;
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
{ {
...@@ -53,8 +55,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -53,8 +55,8 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class ExplicitProxyEndPoint : ProxyEndPoint public class ExplicitProxyEndPoint : ProxyEndPoint
{ {
private List<string> excludedHttpsHostNameRegex; internal List<Regex> excludedHttpsHostNameRegex;
private List<string> includedHttpsHostNameRegex; internal List<Regex> includedHttpsHostNameRegex;
internal bool IsSystemHttpProxy { get; set; } internal bool IsSystemHttpProxy { get; set; }
...@@ -63,9 +65,9 @@ namespace Titanium.Web.Proxy.Models ...@@ -63,9 +65,9 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// List of host names to exclude using Regular Expressions. /// List of host names to exclude using Regular Expressions.
/// </summary> /// </summary>
public List<string> ExcludedHttpsHostNameRegex public IEnumerable<string> ExcludedHttpsHostNameRegex
{ {
get { return excludedHttpsHostNameRegex; } get { return excludedHttpsHostNameRegex?.Select(x => x.ToString()).ToList(); }
set set
{ {
if (IncludedHttpsHostNameRegex != null) if (IncludedHttpsHostNameRegex != null)
...@@ -73,16 +75,16 @@ namespace Titanium.Web.Proxy.Models ...@@ -73,16 +75,16 @@ namespace Titanium.Web.Proxy.Models
throw new ArgumentException("Cannot set excluded when included is set"); throw new ArgumentException("Cannot set excluded when included is set");
} }
excludedHttpsHostNameRegex = value; excludedHttpsHostNameRegex = value?.Select(x=>new Regex(x, RegexOptions.Compiled)).ToList();
} }
} }
/// <summary> /// <summary>
/// List of host names to exclude using Regular Expressions. /// List of host names to exclude using Regular Expressions.
/// </summary> /// </summary>
public List<string> IncludedHttpsHostNameRegex public IEnumerable<string> IncludedHttpsHostNameRegex
{ {
get { return includedHttpsHostNameRegex; } get { return includedHttpsHostNameRegex?.Select(x => x.ToString()).ToList(); }
set set
{ {
if (ExcludedHttpsHostNameRegex != null) if (ExcludedHttpsHostNameRegex != null)
...@@ -90,7 +92,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -90,7 +92,7 @@ namespace Titanium.Web.Proxy.Models
throw new ArgumentException("Cannot set included when excluded is set"); throw new ArgumentException("Cannot set included when excluded is set");
} }
includedHttpsHostNameRegex = value; includedHttpsHostNameRegex = value?.Select(x => new Regex(x, RegexOptions.Compiled)).ToList();
} }
} }
......
using System; using System;
using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
{ {
...@@ -28,6 +30,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -28,6 +30,7 @@ namespace Titanium.Web.Proxy.Models
/// Header Name. /// Header Name.
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// Header Value. /// Header Value.
/// </summary> /// </summary>
...@@ -41,5 +44,12 @@ namespace Titanium.Web.Proxy.Models ...@@ -41,5 +44,12 @@ namespace Titanium.Web.Proxy.Models
{ {
return $"{Name}: {Value}"; return $"{Name}: {Value}";
} }
internal async Task WriteToStream(StreamWriter writer)
{
await writer.WriteAsync(Name);
await writer.WriteAsync(": ");
await writer.WriteLineAsync(Value);
}
} }
} }
\ No newline at end of file
...@@ -52,14 +52,11 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -52,14 +52,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
Stream.Close(); Stream?.Close();
Stream.Dispose(); Stream?.Dispose();
StreamReader?.Dispose();
TcpClient.LingerState = new LingerOption(true, 0); TcpClient.LingerState = new LingerOption(true, 0);
TcpClient.Client.Shutdown(SocketShutdown.Both);
TcpClient.Client.Close();
TcpClient.Client.Dispose();
TcpClient.Close(); TcpClient.Close();
} }
......
...@@ -8,6 +8,7 @@ using Titanium.Web.Proxy.Helpers; ...@@ -8,6 +8,7 @@ using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using System.Security.Authentication; using System.Security.Authentication;
using System.Linq; using System.Linq;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Network.Tcp namespace Titanium.Web.Proxy.Network.Tcp
...@@ -44,7 +45,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -44,7 +45,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
Stream clientStream, IPEndPoint upStreamEndPoint) Stream clientStream, IPEndPoint upStreamEndPoint)
{ {
TcpClient client; TcpClient client;
Stream stream; CustomBufferedStream stream;
bool isLocalhost = (externalHttpsProxy == null && externalHttpProxy == null) ? false : NetworkHelper.IsLocalIpAddress(remoteHostName); bool isLocalhost = (externalHttpsProxy == null && externalHttpProxy == null) ? false : NetworkHelper.IsLocalIpAddress(remoteHostName);
...@@ -60,7 +61,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -60,7 +61,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{ {
client = new TcpClient(upStreamEndPoint); client = new TcpClient(upStreamEndPoint);
await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port); await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port);
stream = client.GetStream(); stream = new CustomBufferedStream(client.GetStream(), bufferSize);
using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true) { NewLine = ProxyConstants.NewLine }) using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true) { NewLine = ProxyConstants.NewLine })
{ {
...@@ -78,24 +79,23 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -78,24 +79,23 @@ namespace Titanium.Web.Proxy.Network.Tcp
writer.Close(); writer.Close();
} }
using (var reader = new CustomBinaryReader(stream)) using (var reader = new CustomBinaryReader(stream, bufferSize))
{ {
var result = await reader.ReadLineAsync(); var result = await reader.ReadLineAsync();
if (!new[] { "200 OK", "connection established" }.Any(s => result.ContainsIgnoreCase(s)))
if (!new[] { "200 OK", "connection established" }.Any(s => result.ToLower().Contains(s.ToLower())))
{ {
throw new Exception("Upstream proxy failed to create a secure tunnel"); throw new Exception("Upstream proxy failed to create a secure tunnel");
} }
await reader.ReadAllLinesAsync(); await reader.ReadAndIgnoreAllLinesAsync();
} }
} }
else else
{ {
client = new TcpClient(upStreamEndPoint); client = new TcpClient(upStreamEndPoint);
await client.ConnectAsync(remoteHostName, remotePort); await client.ConnectAsync(remoteHostName, remotePort);
stream = client.GetStream(); stream = new CustomBufferedStream(client.GetStream(), bufferSize);
} }
try try
...@@ -105,7 +105,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -105,7 +105,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
await sslStream.AuthenticateAsClientAsync(remoteHostName, null, supportedSslProtocols, false); await sslStream.AuthenticateAsClientAsync(remoteHostName, null, supportedSslProtocols, false);
stream = sslStream; stream = new CustomBufferedStream(sslStream, bufferSize);
} }
catch catch
{ {
...@@ -120,13 +120,13 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -120,13 +120,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
{ {
client = new TcpClient(upStreamEndPoint); client = new TcpClient(upStreamEndPoint);
await client.ConnectAsync(externalHttpProxy.HostName, externalHttpProxy.Port); await client.ConnectAsync(externalHttpProxy.HostName, externalHttpProxy.Port);
stream = client.GetStream(); stream = new CustomBufferedStream(client.GetStream(), bufferSize);
} }
else else
{ {
client = new TcpClient(upStreamEndPoint); client = new TcpClient(upStreamEndPoint);
await client.ConnectAsync(remoteHostName, remotePort); await client.ConnectAsync(remoteHostName, remotePort);
stream = client.GetStream(); stream = new CustomBufferedStream(client.GetStream(), bufferSize);
} }
} }
...@@ -137,7 +137,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -137,7 +137,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
stream.WriteTimeout = connectionTimeOutSeconds * 1000; stream.WriteTimeout = connectionTimeOutSeconds * 1000;
return new TcpConnection() return new TcpConnection
{ {
UpStreamHttpProxy = externalHttpProxy, UpStreamHttpProxy = externalHttpProxy,
UpStreamHttpsProxy = externalHttpsProxy, UpStreamHttpsProxy = externalHttpsProxy,
...@@ -145,7 +145,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -145,7 +145,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
Port = remotePort, Port = remotePort,
IsHttps = isHttps, IsHttps = isHttps,
TcpClient = client, TcpClient = client,
StreamReader = new CustomBinaryReader(stream), StreamReader = new CustomBinaryReader(stream, bufferSize),
Stream = stream, Stream = stream,
Version = httpVersion Version = httpVersion
}; };
......
...@@ -48,7 +48,7 @@ namespace Titanium.Web.Proxy ...@@ -48,7 +48,7 @@ namespace Titanium.Web.Proxy
var header = httpHeaders.FirstOrDefault(t => t.Name == "Proxy-Authorization"); var header = httpHeaders.FirstOrDefault(t => t.Name == "Proxy-Authorization");
if (null == header) throw new NullReferenceException(); if (null == header) throw new NullReferenceException();
var headerValue = header.Value.Trim(); var headerValue = header.Value.Trim();
if (!headerValue.ToLower().StartsWith("basic")) if (!headerValue.StartsWith("basic", StringComparison.CurrentCultureIgnoreCase))
{ {
//Return not authorized //Return not authorized
await WriteResponseStatus(new Version(1, 1), "407", await WriteResponseStatus(new Version(1, 1), "407",
...@@ -95,7 +95,7 @@ namespace Titanium.Web.Proxy ...@@ -95,7 +95,7 @@ namespace Titanium.Web.Proxy
} }
var username = decoded.Substring(0, decoded.IndexOf(':')); var username = decoded.Substring(0, decoded.IndexOf(':'));
var password = decoded.Substring(decoded.IndexOf(':') + 1); var password = decoded.Substring(decoded.IndexOf(':') + 1);
return await AuthenticateUserFunc(username, password).ConfigureAwait(false); return await AuthenticateUserFunc(username, password);
} }
catch (Exception e) catch (Exception e)
{ {
......
...@@ -616,8 +616,6 @@ namespace Titanium.Web.Proxy ...@@ -616,8 +616,6 @@ namespace Titanium.Web.Proxy
} }
else else
{ {
await HandleClient(endPoint as ExplicitProxyEndPoint, tcpClient); await HandleClient(endPoint as ExplicitProxyEndPoint, tcpClient);
} }
...@@ -631,12 +629,7 @@ namespace Titanium.Web.Proxy ...@@ -631,12 +629,7 @@ namespace Titanium.Web.Proxy
//It helps to avoid eventual deterioration of performance due to TCP port exhaustion //It helps to avoid eventual deterioration of performance due to TCP port exhaustion
//due to default TCP CLOSE_WAIT timeout for 4 minutes //due to default TCP CLOSE_WAIT timeout for 4 minutes
tcpClient.LingerState = new LingerOption(true, 0); tcpClient.LingerState = new LingerOption(true, 0);
tcpClient.Client.Shutdown(SocketShutdown.Both);
tcpClient.Client.Close();
tcpClient.Client.Dispose();
tcpClient.Close(); tcpClient.Close();
} }
} }
}); });
......
...@@ -6,7 +6,6 @@ using System.Net; ...@@ -6,7 +6,6 @@ using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication; using System.Security.Authentication;
using System.Text.RegularExpressions;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
...@@ -30,12 +29,12 @@ namespace Titanium.Web.Proxy ...@@ -30,12 +29,12 @@ namespace Titanium.Web.Proxy
private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient) private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
{ {
Stream clientStream = tcpClient.GetStream(); CustomBufferedStream clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);
clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000; clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000;
clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000; clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000;
var clientStreamReader = new CustomBinaryReader(clientStream); var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
var clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; var clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine };
Uri httpRemoteUri; Uri httpRemoteUri;
...@@ -76,12 +75,12 @@ namespace Titanium.Web.Proxy ...@@ -76,12 +75,12 @@ namespace Titanium.Web.Proxy
if (endPoint.ExcludedHttpsHostNameRegex != null) if (endPoint.ExcludedHttpsHostNameRegex != null)
{ {
excluded = endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)); excluded = endPoint.excludedHttpsHostNameRegex.Any(x => x.IsMatch(httpRemoteUri.Host));
} }
if (endPoint.IncludedHttpsHostNameRegex != null) if (endPoint.IncludedHttpsHostNameRegex != null)
{ {
excluded = !endPoint.IncludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)); excluded = !endPoint.includedHttpsHostNameRegex.Any(x => x.IsMatch(httpRemoteUri.Host));
} }
List<HttpHeader> connectRequestHeaders = null; List<HttpHeader> connectRequestHeaders = null;
...@@ -112,7 +111,6 @@ namespace Titanium.Web.Proxy ...@@ -112,7 +111,6 @@ namespace Titanium.Web.Proxy
try try
{ {
sslStream = new SslStream(clientStream, true); sslStream = new SslStream(clientStream, true);
var certificate = endPoint.GenericCertificate ?? certificateManager.CreateCertificate(httpRemoteUri.Host, false); var certificate = endPoint.GenericCertificate ?? certificateManager.CreateCertificate(httpRemoteUri.Host, false);
...@@ -121,11 +119,10 @@ namespace Titanium.Web.Proxy ...@@ -121,11 +119,10 @@ namespace Titanium.Web.Proxy
await sslStream.AuthenticateAsServerAsync(certificate, false, await sslStream.AuthenticateAsServerAsync(certificate, false,
SupportedSslProtocols, false); SupportedSslProtocols, false);
//HTTPS server created - we can now decrypt the client's traffic //HTTPS server created - we can now decrypt the client's traffic
clientStream = sslStream; clientStream = new CustomBufferedStream(sslStream, BufferSize);
clientStreamReader = new CustomBinaryReader(sslStream);
clientStreamWriter = new StreamWriter(sslStream) {NewLine = ProxyConstants.NewLine };
clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine };
} }
catch catch
{ {
...@@ -142,8 +139,8 @@ namespace Titanium.Web.Proxy ...@@ -142,8 +139,8 @@ namespace Titanium.Web.Proxy
//Sorry cannot do a HTTPS request decrypt to port 80 at this time //Sorry cannot do a HTTPS request decrypt to port 80 at this time
else if (httpVerb == "CONNECT") else if (httpVerb == "CONNECT")
{ {
//Cyphen out CONNECT request headers //Siphon out CONNECT request headers
await clientStreamReader.ReadAllLinesAsync(); await clientStreamReader.ReadAndIgnoreAllLinesAsync();
//write back successfull CONNECT response //write back successfull CONNECT response
await WriteConnectResponse(clientStreamWriter, version); await WriteConnectResponse(clientStreamWriter, version);
...@@ -171,8 +168,7 @@ namespace Titanium.Web.Proxy ...@@ -171,8 +168,7 @@ namespace Titanium.Web.Proxy
//So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client //So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
{ {
CustomBufferedStream clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);
Stream clientStream = tcpClient.GetStream();
clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000; clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000;
clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000; clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000;
...@@ -193,8 +189,9 @@ namespace Titanium.Web.Proxy ...@@ -193,8 +189,9 @@ namespace Titanium.Web.Proxy
await sslStream.AuthenticateAsServerAsync(certificate, false, await sslStream.AuthenticateAsServerAsync(certificate, false,
SslProtocols.Tls, false); SslProtocols.Tls, false);
clientStreamReader = new CustomBinaryReader(sslStream); clientStream = new CustomBufferedStream(sslStream, BufferSize);
clientStreamWriter = new StreamWriter(sslStream) { NewLine = ProxyConstants.NewLine }; clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine };
//HTTPS server created - we can now decrypt the client's traffic //HTTPS server created - we can now decrypt the client's traffic
} }
...@@ -205,11 +202,10 @@ namespace Titanium.Web.Proxy ...@@ -205,11 +202,10 @@ namespace Titanium.Web.Proxy
Dispose(sslStream, clientStreamReader, clientStreamWriter, null); Dispose(sslStream, clientStreamReader, clientStreamWriter, null);
return; return;
} }
clientStream = sslStream;
} }
else else
{ {
clientStreamReader = new CustomBinaryReader(clientStream); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine };
} }
...@@ -231,14 +227,14 @@ namespace Titanium.Web.Proxy ...@@ -231,14 +227,14 @@ namespace Titanium.Web.Proxy
{ {
if (GetCustomUpStreamHttpProxyFunc != null) if (GetCustomUpStreamHttpProxyFunc != null)
{ {
customUpStreamHttpProxy = await GetCustomUpStreamHttpProxyFunc(args).ConfigureAwait(false); customUpStreamHttpProxy = await GetCustomUpStreamHttpProxyFunc(args);
} }
} }
else else
{ {
if (GetCustomUpStreamHttpsProxyFunc != null) if (GetCustomUpStreamHttpsProxyFunc != null)
{ {
customUpStreamHttpsProxy = await GetCustomUpStreamHttpsProxyFunc(args).ConfigureAwait(false); customUpStreamHttpsProxy = await GetCustomUpStreamHttpsProxyFunc(args);
} }
} }
...@@ -498,7 +494,7 @@ namespace Titanium.Web.Proxy ...@@ -498,7 +494,7 @@ namespace Titanium.Web.Proxy
} }
//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.
await HandleHttpSessionRequestInternal(null, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false).ConfigureAwait(false); await HandleHttpSessionRequestInternal(null, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false);
if (args.WebSession.Request.CancelRequest) if (args.WebSession.Request.CancelRequest)
......
...@@ -50,9 +50,9 @@ namespace Titanium.Web.Proxy ...@@ -50,9 +50,9 @@ namespace Titanium.Web.Proxy
await Task.WhenAll(handlerTasks); await Task.WhenAll(handlerTasks);
} }
if(args.ReRequest) if (args.ReRequest)
{ {
await HandleHttpSessionRequestInternal(null, args, null, null, true).ConfigureAwait(false); await HandleHttpSessionRequestInternal(null, args, null, null, true);
return; return;
} }
...@@ -124,7 +124,7 @@ namespace Titanium.Web.Proxy ...@@ -124,7 +124,7 @@ namespace Titanium.Web.Proxy
await args.ProxyClient.ClientStream.FlushAsync(); await args.ProxyClient.ClientStream.FlushAsync();
} }
catch(Exception e) catch (Exception e)
{ {
ExceptionFunc(new ProxyHttpException("Error occured wilst handling session response", e, args)); ExceptionFunc(new ProxyHttpException("Error occured wilst handling session response", e, args));
Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader,
...@@ -175,7 +175,7 @@ namespace Titanium.Web.Proxy ...@@ -175,7 +175,7 @@ namespace Titanium.Web.Proxy
foreach (var header in response.ResponseHeaders) foreach (var header in response.ResponseHeaders)
{ {
await responseWriter.WriteLineAsync(header.Value.ToString()); await header.Value.WriteToStream(responseWriter);
} }
//write non unique request headers //write non unique request headers
...@@ -184,11 +184,10 @@ namespace Titanium.Web.Proxy ...@@ -184,11 +184,10 @@ namespace Titanium.Web.Proxy
var headers = headerItem.Value; var headers = headerItem.Value;
foreach (var header in headers) foreach (var header in headers)
{ {
await responseWriter.WriteLineAsync(header.ToString()); await header.WriteToStream(responseWriter);
} }
} }
await responseWriter.WriteLineAsync(); await responseWriter.WriteLineAsync();
await responseWriter.FlushAsync(); await responseWriter.FlushAsync();
} }
...@@ -231,20 +230,15 @@ namespace Titanium.Web.Proxy ...@@ -231,20 +230,15 @@ namespace Titanium.Web.Proxy
private void Dispose(Stream clientStream, CustomBinaryReader clientStreamReader, private void Dispose(Stream clientStream, CustomBinaryReader clientStreamReader,
StreamWriter clientStreamWriter, IDisposable args) StreamWriter clientStreamWriter, IDisposable args)
{ {
clientStream?.Close();
if (clientStream != null) clientStream?.Dispose();
{
clientStream.Close();
clientStream.Dispose();
}
args?.Dispose();
clientStreamReader?.Dispose(); clientStreamReader?.Dispose();
if (clientStreamWriter == null) return; clientStreamWriter?.Close();
clientStreamWriter.Close(); clientStreamWriter?.Dispose();
clientStreamWriter.Dispose();
args?.Dispose();
} }
} }
} }
\ No newline at end of file
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Debug\Titanium.Web.Proxy.XML</DocumentationFile> <DocumentationFile>bin\Debug\Titanium.Web.Proxy.XML</DocumentationFile>
<LangVersion>6</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
...@@ -72,6 +73,8 @@ ...@@ -72,6 +73,8 @@
<Compile Include="EventArguments\CertificateSelectionEventArgs.cs" /> <Compile Include="EventArguments\CertificateSelectionEventArgs.cs" />
<Compile Include="EventArguments\CertificateValidationEventArgs.cs" /> <Compile Include="EventArguments\CertificateValidationEventArgs.cs" />
<Compile Include="Extensions\ByteArrayExtensions.cs" /> <Compile Include="Extensions\ByteArrayExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Helpers\CustomBufferedStream.cs" />
<Compile Include="Helpers\Network.cs" /> <Compile Include="Helpers\Network.cs" />
<Compile Include="Helpers\RunTime.cs" /> <Compile Include="Helpers\RunTime.cs" />
<Compile Include="Http\Responses\GenericResponse.cs" /> <Compile Include="Http\Responses\GenericResponse.cs" />
......
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