Unverified Commit 5e1841ff authored by honfika's avatar honfika Committed by GitHub

Merge pull request #390 from justcoding121/develop

merge to beta (again)
parents b27ba8cf fa0d9f2a
...@@ -5,6 +5,7 @@ using System.Net; ...@@ -5,6 +5,7 @@ using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -13,6 +14,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -13,6 +14,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
public class ProxyTestController public class ProxyTestController
{ {
private readonly object lockObj = new object();
private readonly ProxyServer proxyServer; private readonly ProxyServer proxyServer;
private ExplicitProxyEndPoint explicitEndPoint; private ExplicitProxyEndPoint explicitEndPoint;
...@@ -33,11 +36,28 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -33,11 +36,28 @@ namespace Titanium.Web.Proxy.Examples.Basic
//generate root certificate without storing it in file system //generate root certificate without storing it in file system
//proxyServer.CertificateManager.CreateRootCertificate(false); //proxyServer.CertificateManager.CreateRootCertificate(false);
//proxyServer.CertificateManager.TrustRootCertificate(); //proxyServer.CertificateManager.TrustRootCertificate();
//proxyServer.CertificateManager.TrustRootCertificateAsAdmin(); //proxyServer.CertificateManager.TrustRootCertificateAsAdmin();
proxyServer.ExceptionFunc = exception => Console.WriteLine(exception.Message); proxyServer.ExceptionFunc = exception =>
{
lock (lockObj)
{
var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
if (exception is ProxyHttpException phex)
{
Console.WriteLine(exception.Message + ": " + phex.InnerException?.Message);
}
else
{
Console.WriteLine(exception.Message);
}
Console.ForegroundColor = color;
}
};
proxyServer.ForwardToUpstreamGateway = true; proxyServer.ForwardToUpstreamGateway = true;
proxyServer.CertificateManager.SaveFakeCertificates = true; proxyServer.CertificateManager.SaveFakeCertificates = true;
//optionally set the Certificate Engine //optionally set the Certificate Engine
...@@ -52,7 +72,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -52,7 +72,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
proxyServer.BeforeRequest += OnRequest; proxyServer.BeforeRequest += OnRequest;
proxyServer.BeforeResponse += OnResponse; proxyServer.BeforeResponse += OnResponse;
proxyServer.ServerCertificateValidationCallback += OnCertificateValidation; proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection; proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
...@@ -60,12 +80,12 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -60,12 +80,12 @@ namespace Titanium.Web.Proxy.Examples.Basic
explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true) explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
{ {
//You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown //You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown
//Use self-issued generic certificate on all https requests //Use self-issued generic certificate on all https requests
//Optimizes performance by not creating a certificate for each https-enabled domain //Optimizes performance by not creating a certificate for each https-enabled domain
//Useful when certificate trust is not required by proxy clients //Useful when certificate trust is not required by proxy clients
//GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password") //GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
}; };
//Fired when a CONNECT request is received //Fired when a CONNECT request is received
...@@ -125,7 +145,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -125,7 +145,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e) private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{ {
string hostname = e.WebSession.Request.RequestUri.Host; string hostname = e.WebSession.Request.RequestUri.Host;
Console.WriteLine("Tunnel to: " + hostname); WriteToConsole("Tunnel to: " + hostname);
if (hostname.Contains("dropbox.com")) if (hostname.Contains("dropbox.com"))
{ {
...@@ -143,8 +163,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -143,8 +163,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
//intecept & cancel redirect or update requests //intecept & cancel redirect or update requests
private async Task OnRequest(object sender, SessionEventArgs e) private async Task OnRequest(object sender, SessionEventArgs e)
{ {
Console.WriteLine("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount); WriteToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
Console.WriteLine(e.WebSession.Request.Url); WriteToConsole(e.WebSession.Request.Url);
//read request headers //read request headers
requestHeaderHistory[e.Id] = e.WebSession.Request.Headers; requestHeaderHistory[e.Id] = e.WebSession.Request.Headers;
...@@ -192,16 +212,16 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -192,16 +212,16 @@ namespace Titanium.Web.Proxy.Examples.Basic
private void MultipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e) private void MultipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
{ {
var session = (SessionEventArgs)sender; var session = (SessionEventArgs)sender;
Console.WriteLine("Multipart form data headers:"); WriteToConsole("Multipart form data headers:");
foreach (var header in e.Headers) foreach (var header in e.Headers)
{ {
Console.WriteLine(header); WriteToConsole(header.ToString());
} }
} }
private async Task OnResponse(object sender, SessionEventArgs e) private async Task OnResponse(object sender, SessionEventArgs e)
{ {
Console.WriteLine("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount); WriteToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
//if (requestBodyHistory.ContainsKey(e.Id)) //if (requestBodyHistory.ContainsKey(e.Id))
//{ //{
...@@ -213,7 +233,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -213,7 +233,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
//responseHeaderHistory[e.Id] = e.WebSession.Response.Headers; //responseHeaderHistory[e.Id] = e.WebSession.Response.Headers;
//// print out process id of current session //// print out process id of current session
////Console.WriteLine($"PID: {e.WebSession.ProcessId.Value}"); ////WriteToConsole($"PID: {e.WebSession.ProcessId.Value}");
////if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return; ////if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
//if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST") //if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST")
...@@ -259,5 +279,13 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -259,5 +279,13 @@ namespace Titanium.Web.Proxy.Examples.Basic
return Task.FromResult(0); return Task.FromResult(0);
} }
private void WriteToConsole(string message)
{
lock (lockObj)
{
Console.WriteLine(message);
}
}
} }
} }
...@@ -3,6 +3,7 @@ using System.Collections.Generic; ...@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
...@@ -102,6 +103,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -102,6 +103,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
proxyServer.BeforeRequest += ProxyServer_BeforeRequest; proxyServer.BeforeRequest += ProxyServer_BeforeRequest;
proxyServer.BeforeResponse += ProxyServer_BeforeResponse; proxyServer.BeforeResponse += ProxyServer_BeforeResponse;
proxyServer.AfterResponse += ProxyServer_AfterResponse;
explicitEndPoint.BeforeTunnelConnectRequest += ProxyServer_BeforeTunnelConnectRequest; explicitEndPoint.BeforeTunnelConnectRequest += ProxyServer_BeforeTunnelConnectRequest;
explicitEndPoint.BeforeTunnelConnectResponse += ProxyServer_BeforeTunnelConnectResponse; explicitEndPoint.BeforeTunnelConnectResponse += ProxyServer_BeforeTunnelConnectResponse;
proxyServer.ClientConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ClientConnectionCount = proxyServer.ClientConnectionCount; }); }; proxyServer.ClientConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ClientConnectionCount = proxyServer.ClientConnectionCount; }); };
...@@ -179,6 +181,17 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -179,6 +181,17 @@ namespace Titanium.Web.Proxy.Examples.Wpf
} }
} }
private async Task ProxyServer_AfterResponse(object sender, SessionEventArgs e)
{
await Dispatcher.InvokeAsync(() =>
{
if (sessionDictionary.TryGetValue(e.WebSession, out var item))
{
item.Exception = e.Exception;
}
});
}
private SessionListItem AddSession(SessionEventArgs e) private SessionListItem AddSession(SessionEventArgs e)
{ {
var item = CreateSessionListItem(e); var item = CreateSessionListItem(e);
...@@ -255,9 +268,12 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -255,9 +268,12 @@ namespace Titanium.Web.Proxy.Examples.Wpf
} }
//string hexStr = string.Join(" ", data.Select(x => x.ToString("X2"))); //string hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxRequest.Text = request.HeaderText + request.Encoding.GetString(data) + var sb = new StringBuilder();
(truncated ? Environment.NewLine + $"Data is truncated after {truncateLimit} bytes" : null) + sb.Append(request.HeaderText);
(request as ConnectRequest)?.ClientHelloInfo; sb.Append(request.Encoding.GetString(data));
sb.Append(truncated ? Environment.NewLine + $"Data is truncated after {truncateLimit} bytes" : null);
sb.Append((request as ConnectRequest)?.ClientHelloInfo);
TextBoxRequest.Text = sb.ToString();
var response = session.Response; var response = session.Response;
data = (response.IsBodyRead ? response.Body : null) ?? new byte[0]; data = (response.IsBodyRead ? response.Body : null) ?? new byte[0];
...@@ -268,9 +284,18 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -268,9 +284,18 @@ namespace Titanium.Web.Proxy.Examples.Wpf
} }
//hexStr = string.Join(" ", data.Select(x => x.ToString("X2"))); //hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxResponse.Text = session.Response.HeaderText + session.Response.Encoding.GetString(data) + sb = new StringBuilder();
(truncated ? Environment.NewLine + $"Data is truncated after {truncateLimit} bytes" : null) + sb.Append(response.HeaderText);
(session.Response as ConnectResponse)?.ServerHelloInfo; sb.Append(response.Encoding.GetString(data));
sb.Append(truncated ? Environment.NewLine + $"Data is truncated after {truncateLimit} bytes" : null);
sb.Append((response as ConnectResponse)?.ServerHelloInfo);
if (SelectedSession.Exception != null)
{
sb.Append(Environment.NewLine);
sb.Append(SelectedSession.Exception);
}
TextBoxResponse.Text = sb.ToString();
} }
} }
} }
...@@ -17,6 +17,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -17,6 +17,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private string process; private string process;
private long receivedDataCount; private long receivedDataCount;
private long sentDataCount; private long sentDataCount;
private Exception exception;
public int Number { get; set; } public int Number { get; set; }
...@@ -72,6 +73,12 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -72,6 +73,12 @@ namespace Titanium.Web.Proxy.Examples.Wpf
set => SetField(ref sentDataCount, value); set => SetField(ref sentDataCount, value);
} }
public Exception Exception
{
get => exception;
set => SetField(ref exception, value);
}
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
protected void SetField<T>(ref T field, T value,[CallerMemberName] string propertyName = null) protected void SetField<T>(ref T field, T value,[CallerMemberName] string propertyName = null)
......
...@@ -51,8 +51,8 @@ ...@@ -51,8 +51,8 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="StreamExtended, Version=1.0.138.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL"> <Reference Include="StreamExtended, Version=1.0.141.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.138-beta\lib\net45\StreamExtended.dll</HintPath> <HintPath>..\..\packages\StreamExtended.1.0.141-beta\lib\net45\StreamExtended.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="StreamExtended" version="1.0.138-beta" targetFramework="net45" /> <package id="StreamExtended" version="1.0.141-beta" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Titanium.Web.Proxy.UnitTests</RootNamespace> <RootNamespace>Titanium.Web.Proxy.UnitTests</RootNamespace>
<AssemblyName>Titanium.Web.Proxy.UnitTests</AssemblyName> <AssemblyName>Titanium.Web.Proxy.UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest> <IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType> <TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
......
using System; using System;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression namespace Titanium.Web.Proxy.Compression
{ {
/// <summary> /// <summary>
/// A factory to generate the compression methods based on the type of compression /// A factory to generate the compression methods based on the type of compression
/// </summary> /// </summary>
internal class CompressionFactory internal static class CompressionFactory
{ {
//cache //cache
private static Lazy<ICompression> gzip = new Lazy<ICompression>(() => new GZipCompression()); private static readonly Lazy<ICompression> gzip = new Lazy<ICompression>(() => new GZipCompression());
private static Lazy<ICompression> deflate = new Lazy<ICompression>(() => new DeflateCompression()); private static readonly Lazy<ICompression> deflate = new Lazy<ICompression>(() => new DeflateCompression());
public static ICompression GetCompression(string type) public static ICompression GetCompression(string type)
{ {
switch (type) switch (type)
{ {
case "gzip": case KnownHeaders.ContentEncodingGzip:
return gzip.Value; return gzip.Value;
case "deflate": case KnownHeaders.ContentEncodingDeflate:
return deflate.Value; return deflate.Value;
default: default:
return null; throw new Exception($"Unsupported compression mode: {type}");
} }
} }
} }
......
...@@ -9,17 +9,9 @@ namespace Titanium.Web.Proxy.Compression ...@@ -9,17 +9,9 @@ namespace Titanium.Web.Proxy.Compression
/// </summary> /// </summary>
internal class DeflateCompression : ICompression internal class DeflateCompression : ICompression
{ {
public async Task<byte[]> Compress(byte[] responseBody) public Stream GetStream(Stream stream)
{ {
using (var ms = new MemoryStream()) return new DeflateStream(stream, CompressionMode.Compress, true);
{
using (var zip = new DeflateStream(ms, CompressionMode.Compress, true))
{
await zip.WriteAsync(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
}
} }
} }
} }
...@@ -9,17 +9,9 @@ namespace Titanium.Web.Proxy.Compression ...@@ -9,17 +9,9 @@ namespace Titanium.Web.Proxy.Compression
/// </summary> /// </summary>
internal class GZipCompression : ICompression internal class GZipCompression : ICompression
{ {
public async Task<byte[]> Compress(byte[] responseBody) public Stream GetStream(Stream stream)
{ {
using (var ms = new MemoryStream()) return new GZipStream(stream, CompressionMode.Compress, true);
{
using (var zip = new GZipStream(ms, CompressionMode.Compress, true))
{
await zip.WriteAsync(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
}
} }
} }
} }
using System.Threading.Tasks; using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression namespace Titanium.Web.Proxy.Compression
{ {
...@@ -7,6 +8,6 @@ namespace Titanium.Web.Proxy.Compression ...@@ -7,6 +8,6 @@ namespace Titanium.Web.Proxy.Compression
/// </summary> /// </summary>
interface ICompression interface ICompression
{ {
Task<byte[]> Compress(byte[] responseBody); Stream GetStream(Stream stream);
} }
} }
namespace Titanium.Web.Proxy.Decompression using System;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Decompression
{ {
/// <summary> /// <summary>
/// A factory to generate the de-compression methods based on the type of compression /// A factory to generate the de-compression methods based on the type of compression
/// </summary> /// </summary>
internal class DecompressionFactory internal class DecompressionFactory
{ {
internal IDecompression Create(string type) //cache
private static readonly Lazy<IDecompression> gzip = new Lazy<IDecompression>(() => new GZipDecompression());
private static readonly Lazy<IDecompression> deflate = new Lazy<IDecompression>(() => new DeflateDecompression());
public static IDecompression Create(string type)
{ {
switch (type) switch (type)
{ {
case "gzip": case KnownHeaders.ContentEncodingGzip:
return new GZipDecompression(); return gzip.Value;
case "deflate": case KnownHeaders.ContentEncodingDeflate:
return new DeflateDecompression(); return deflate.Value;
default: default:
return new DefaultDecompression(); throw new Exception($"Unsupported decompression mode: {type}");
} }
} }
} }
......
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// When no compression is specified just return the byte array
/// </summary>
internal class DefaultDecompression : IDecompression
{
public Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
{
return Task.FromResult(compressedArray);
}
}
}
...@@ -10,30 +10,9 @@ namespace Titanium.Web.Proxy.Decompression ...@@ -10,30 +10,9 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary> /// </summary>
internal class DeflateDecompression : IDecompression internal class DeflateDecompression : IDecompression
{ {
public async Task<byte[]> Decompress(byte[] compressedArray, int bufferSize) public Stream GetStream(Stream stream)
{ {
using (var stream = new MemoryStream(compressedArray)) return new DeflateStream(stream, CompressionMode.Decompress, true);
using (var decompressor = new DeflateStream(stream, CompressionMode.Decompress))
{
var buffer = BufferPool.GetBuffer(bufferSize);
try
{
using (var output = new MemoryStream())
{
int read;
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
finally
{
BufferPool.ReturnBuffer(buffer);
}
}
} }
} }
} }
...@@ -10,29 +10,9 @@ namespace Titanium.Web.Proxy.Decompression ...@@ -10,29 +10,9 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary> /// </summary>
internal class GZipDecompression : IDecompression internal class GZipDecompression : IDecompression
{ {
public async Task<byte[]> Decompress(byte[] compressedArray, int bufferSize) public Stream GetStream(Stream stream)
{ {
using (var decompressor = new GZipStream(new MemoryStream(compressedArray), CompressionMode.Decompress)) return new GZipStream(stream, CompressionMode.Decompress, true);
{
var buffer = BufferPool.GetBuffer(bufferSize);
try
{
using (var output = new MemoryStream())
{
int read;
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
finally
{
BufferPool.ReturnBuffer(buffer);
}
}
} }
} }
} }
using System.Threading.Tasks; using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression namespace Titanium.Web.Proxy.Decompression
{ {
...@@ -7,6 +8,6 @@ namespace Titanium.Web.Proxy.Decompression ...@@ -7,6 +8,6 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary> /// </summary>
internal interface IDecompression internal interface IDecompression
{ {
Task<byte[]> Decompress(byte[] compressedArray, int bufferSize); Stream GetStream(Stream stream);
} }
} }
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using StreamExtended.Helpers;
using StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments
{
internal class LimitedStream : Stream
{
private readonly CustomBufferedStream baseStream;
private readonly CustomBinaryReader baseReader;
private readonly bool isChunked;
private bool readChunkTrail;
private long bytesRemaining;
public LimitedStream(CustomBufferedStream baseStream, CustomBinaryReader baseReader, bool isChunked, long contentLength)
{
this.baseStream = baseStream;
this.baseReader = baseReader;
this.isChunked = isChunked;
bytesRemaining = isChunked ? 0 : contentLength;
}
private void GetNextChunk()
{
if (readChunkTrail)
{
// read the chunk trail of the previous chunk
string s = baseReader.ReadLineAsync().Result;
}
readChunkTrail = true;
string chunkHead = baseReader.ReadLineAsync().Result;
int idx = chunkHead.IndexOf(";");
if (idx >= 0)
{
// remove chunk extension
chunkHead = chunkHead.Substring(0, idx);
}
int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber);
bytesRemaining = chunkSize;
if (chunkSize == 0)
{
bytesRemaining = -1;
//chunk trail
string s = baseReader.ReadLineAsync().Result;
}
}
public override void Flush()
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
if (bytesRemaining == -1)
{
return 0;
}
if (bytesRemaining == 0)
{
if (isChunked)
{
GetNextChunk();
}
else
{
bytesRemaining = -1;
}
}
if (bytesRemaining == -1)
{
return 0;
}
int toRead = (int)Math.Min(count, bytesRemaining);
int res = baseStream.Read(buffer, offset, toRead);
bytesRemaining -= res;
return res;
}
public async Task Finish()
{
var buffer = BufferPool.GetBuffer(baseReader.Buffer.Length);
try
{
while (bytesRemaining != -1)
{
await ReadAsync(buffer, 0, buffer.Length);
}
}
finally
{
BufferPool.ReturnBuffer(buffer);
}
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.EventArguments
{
internal enum TransformationMode
{
None,
/// <summary>
/// Removes the chunked encoding
/// </summary>
RemoveChunked,
/// <summary>
/// Uncompress the body (this also removes the chunked encoding if exists)
/// </summary>
Uncompress,
}
}
...@@ -5,6 +5,7 @@ using System.Text; ...@@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended.Helpers;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -152,26 +153,26 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -152,26 +153,26 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="streamReader"></param> /// <param name="streamReader"></param>
/// <param name="isChunked"></param> /// <param name="isChunked"></param>
/// <param name="contentLength"></param> /// <param name="contentLength"></param>
/// <param name="removeChunkedEncoding"></param> /// <param name="onCopy"></param>
/// <returns></returns> /// <returns></returns>
internal Task CopyBodyAsync(CustomBinaryReader streamReader, bool isChunked, long contentLength, bool removeChunkedEncoding) internal Task CopyBodyAsync(CustomBinaryReader streamReader, bool isChunked, long contentLength, Action<byte[], int, int> onCopy)
{ {
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol //For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (isChunked) if (isChunked)
{ {
//Need to revist, find any potential bugs //Need to revist, find any potential bugs
//send the body bytes to server in chunks //send the body bytes to server in chunks
return CopyBodyChunkedAsync(streamReader, removeChunkedEncoding); return CopyBodyChunkedAsync(streamReader, onCopy);
} }
//http 1.0 //http 1.0 or the stream reader limits the stream
if (contentLength == -1) if (contentLength == -1)
{ {
contentLength = long.MaxValue; contentLength = long.MaxValue;
} }
//If not chunked then its easy just read the amount of bytes mentioned in content length header //If not chunked then its easy just read the amount of bytes mentioned in content length header
return CopyBytesFromStream(streamReader, contentLength); return CopyBytesFromStream(streamReader, contentLength, onCopy);
} }
/// <summary> /// <summary>
...@@ -196,29 +197,30 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -196,29 +197,30 @@ namespace Titanium.Web.Proxy.Helpers
/// Copies the streams chunked /// Copies the streams chunked
/// </summary> /// </summary>
/// <param name="reader"></param> /// <param name="reader"></param>
/// <param name="removeChunkedEncoding"></param> /// <param name="onCopy"></param>
/// <returns></returns> /// <returns></returns>
private async Task CopyBodyChunkedAsync(CustomBinaryReader reader, bool removeChunkedEncoding) private async Task CopyBodyChunkedAsync(CustomBinaryReader reader, Action<byte[], int, int> onCopy)
{ {
while (true) while (true)
{ {
string chunkHead = await reader.ReadLineAsync(); string chunkHead = await reader.ReadLineAsync();
int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber); int idx = chunkHead.IndexOf(";");
if (idx >= 0)
if (!removeChunkedEncoding)
{ {
await WriteLineAsync(chunkHead); // remove chunk extension
chunkHead = chunkHead.Substring(0, idx);
} }
int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber);
await WriteLineAsync(chunkHead);
if (chunkSize != 0) if (chunkSize != 0)
{ {
await CopyBytesFromStream(reader, chunkSize); await CopyBytesFromStream(reader, chunkSize, onCopy);
} }
if (!removeChunkedEncoding) await WriteLineAsync();
{
await WriteLineAsync();
}
//chunk trail //chunk trail
await reader.ReadLineAsync(); await reader.ReadLineAsync();
...@@ -235,8 +237,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -235,8 +237,9 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
/// <param name="reader"></param> /// <param name="reader"></param>
/// <param name="count"></param> /// <param name="count"></param>
/// <param name="onCopy"></param>
/// <returns></returns> /// <returns></returns>
private async Task CopyBytesFromStream(CustomBinaryReader reader, long count) private async Task CopyBytesFromStream(CustomBinaryReader reader, long count, Action<byte[], int, int> onCopy)
{ {
var buffer = reader.Buffer; var buffer = reader.Buffer;
long remainingBytes = count; long remainingBytes = count;
...@@ -258,6 +261,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -258,6 +261,8 @@ namespace Titanium.Web.Proxy.Helpers
remainingBytes -= bytesRead; remainingBytes -= bytesRead;
await WriteAsync(buffer, 0, bytesRead); await WriteAsync(buffer, 0, bytesRead);
onCopy?.Invoke(buffer, 0, bytesRead);
} }
} }
} }
......
...@@ -46,7 +46,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -46,7 +46,7 @@ namespace Titanium.Web.Proxy.Helpers
protocol = ProxyServer.UriSchemeHttp; protocol = ProxyServer.UriSchemeHttp;
break; break;
case ProxyProtocolType.Https: case ProxyProtocolType.Https:
protocol = Proxy.ProxyServer.UriSchemeHttps; protocol = ProxyServer.UriSchemeHttps;
break; break;
default: default:
throw new Exception("Unsupported protocol type"); throw new Exception("Unsupported protocol type");
......
...@@ -22,7 +22,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -22,7 +22,6 @@ namespace Titanium.Web.Proxy.Http
StatusDescription = "Connection Established" StatusDescription = "Connection Established"
}; };
response.Headers.AddHeader(KnownHeaders.Timestamp, DateTime.Now.ToString());
return response; return response;
} }
} }
......
...@@ -40,6 +40,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -40,6 +40,8 @@ namespace Titanium.Web.Proxy.Http
// Response headers // Response headers
public const string ContentEncoding = "content-encoding"; public const string ContentEncoding = "content-encoding";
public const string ContentEncodingDeflate = "deflate";
public const string ContentEncodingGzip = "gzip";
public const string Location = "Location"; public const string Location = "Location";
...@@ -47,8 +49,5 @@ namespace Titanium.Web.Proxy.Http ...@@ -47,8 +49,5 @@ namespace Titanium.Web.Proxy.Http
public const string TransferEncoding = "transfer-encoding"; public const string TransferEncoding = "transfer-encoding";
public const string TransferEncodingChunked = "chunked"; public const string TransferEncodingChunked = "chunked";
// ???
public const string Timestamp = "Timestamp";
} }
} }
...@@ -3,28 +3,17 @@ using System.ComponentModel; ...@@ -3,28 +3,17 @@ using System.ComponentModel;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
/// <summary> /// <summary>
/// A HTTP(S) request object /// Http(s) request object
/// </summary> /// </summary>
[TypeConverter(typeof(ExpandableObjectConverter))] [TypeConverter(typeof(ExpandableObjectConverter))]
public class Request public class Request : RequestResponseBase
{ {
/// <summary>
/// Cached request body as byte array
/// </summary>
private byte[] body;
/// <summary>
/// Cached request body as string
/// </summary>
private string bodyString;
/// <summary> /// <summary>
/// Request Method /// Request Method
/// </summary> /// </summary>
...@@ -45,16 +34,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -45,16 +34,6 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public string OriginalUrl { get; set; } public string OriginalUrl { get; set; }
/// <summary>
/// Request Http Version
/// </summary>
public Version HttpVersion { get; set; }
/// <summary>
/// Keeps the request body data after the session is finished
/// </summary>
public bool KeepBody { get; set; }
/// <summary> /// <summary>
/// Has request body? /// Has request body?
/// </summary> /// </summary>
...@@ -97,80 +76,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -97,80 +76,6 @@ namespace Titanium.Web.Proxy.Http
set => Headers.SetOrAddHeaderValue(KnownHeaders.Host, value); set => Headers.SetOrAddHeaderValue(KnownHeaders.Host, value);
} }
/// <summary>
/// Content encoding header value
/// </summary>
public string ContentEncoding => Headers.GetHeaderValueOrNull(KnownHeaders.ContentEncoding);
/// <summary>
/// Request content-length
/// </summary>
public long ContentLength
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.ContentLength);
if (headerValue == null)
{
return -1;
}
long.TryParse(headerValue, out long contentLen);
if (contentLen >= 0)
{
return contentLen;
}
return -1;
}
set
{
if (value >= 0)
{
Headers.SetOrAddHeaderValue(KnownHeaders.ContentLength, value.ToString());
IsChunked = false;
}
else
{
Headers.RemoveHeader(KnownHeaders.ContentLength);
}
}
}
/// <summary>
/// Request content-type
/// </summary>
public string ContentType
{
get => Headers.GetHeaderValueOrNull(KnownHeaders.ContentType);
set => Headers.SetOrAddHeaderValue(KnownHeaders.ContentType, value);
}
/// <summary>
/// Is request body send as chunked bytes
/// </summary>
public bool IsChunked
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.TransferEncoding);
return headerValue != null && headerValue.ContainsIgnoreCase(KnownHeaders.TransferEncodingChunked);
}
set
{
if (value)
{
Headers.SetOrAddHeaderValue(KnownHeaders.TransferEncoding, KnownHeaders.TransferEncodingChunked);
ContentLength = -1;
}
else
{
Headers.RemoveHeader(KnownHeaders.TransferEncoding);
}
}
}
/// <summary> /// <summary>
/// Does this request has a 100-continue header? /// Does this request has a 100-continue header?
/// </summary> /// </summary>
...@@ -190,17 +95,12 @@ namespace Titanium.Web.Proxy.Http ...@@ -190,17 +95,12 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public string Url => RequestUri.OriginalString; public string Url => RequestUri.OriginalString;
/// <summary>
/// Encoding for this request
/// </summary>
public Encoding Encoding => HttpHelper.GetEncodingFromContentType(ContentType);
/// <summary> /// <summary>
/// Terminates the underlying Tcp Connection to client after current request /// Terminates the underlying Tcp Connection to client after current request
/// </summary> /// </summary>
internal bool CancelRequest { get; set; } internal bool CancelRequest { get; set; }
internal void EnsureBodyAvailable(bool throwWhenNotReadYet = true) internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true)
{ {
//GET request don't have a request body to read //GET request don't have a request body to read
if (!HasBody) if (!HasBody)
...@@ -211,7 +111,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -211,7 +111,7 @@ namespace Titanium.Web.Proxy.Http
if (!IsBodyRead) if (!IsBodyRead)
{ {
if (RequestLocked) if (Locked)
{ {
throw new Exception("You cannot get the request body after request is made to server."); throw new Exception("You cannot get the request body after request is made to server.");
} }
...@@ -225,41 +125,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -225,41 +125,6 @@ namespace Titanium.Web.Proxy.Http
} }
} }
/// <summary>
/// Request body as byte array
/// </summary>
[Browsable(false)]
public byte[] Body
{
get
{
EnsureBodyAvailable();
return body;
}
internal set
{
body = value;
bodyString = null;
}
}
/// <summary>
/// Request body as string
/// Use the encoding specified in request to decode the byte[] data to string
/// </summary>
[Browsable(false)]
public string BodyString => bodyString ?? (bodyString = Encoding.GetString(Body));
/// <summary>
/// Request body was read by user?
/// </summary>
public bool IsBodyRead { get; internal set; }
/// <summary>
/// Request is ready to be sent (user callbacks are complete?)
/// </summary>
internal bool RequestLocked { get; set; }
/// <summary> /// <summary>
/// Does this request has an upgrade to websocket header? /// Does this request has an upgrade to websocket header?
/// </summary> /// </summary>
...@@ -278,11 +143,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -278,11 +143,6 @@ namespace Titanium.Web.Proxy.Http
} }
} }
/// <summary>
/// Request header collection
/// </summary>
public HeaderCollection Headers { get; } = new HeaderCollection();
/// <summary> /// <summary>
/// Does server responsed positively for 100 continue request /// Does server responsed positively for 100 continue request
/// </summary> /// </summary>
...@@ -296,7 +156,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -296,7 +156,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Gets the header text. /// Gets the header text.
/// </summary> /// </summary>
public string HeaderText public override string HeaderText
{ {
get get
{ {
...@@ -367,22 +227,5 @@ namespace Titanium.Web.Proxy.Http ...@@ -367,22 +227,5 @@ namespace Titanium.Web.Proxy.Http
return true; return true;
} }
/// <summary>
/// Finish the session
/// </summary>
public void FinishSession()
{
if (!KeepBody)
{
body = null;
bodyString = null;
}
}
public override string ToString()
{
return HeaderText;
}
} }
} }
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http
{
public abstract class RequestResponseBase
{
/// <summary>
/// Cached body content as byte array
/// </summary>
private byte[] body;
/// <summary>
/// Cached body as string
/// </summary>
private string bodyString;
/// <summary>
/// Keeps the body data after the session is finished
/// </summary>
public bool KeepBody { get; set; }
/// <summary>
/// Http Version
/// </summary>
public Version HttpVersion { get; set; } = HttpHeader.VersionUnknown;
/// <summary>
/// Collection of all headers
/// </summary>
public HeaderCollection Headers { get; } = new HeaderCollection();
/// <summary>
/// Length of the body
/// </summary>
public long ContentLength
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.ContentLength);
if (headerValue == null)
{
return -1;
}
if (long.TryParse(headerValue, out long contentLen) && contentLen >= 0)
{
return contentLen;
}
return -1;
}
set
{
if (value >= 0)
{
Headers.SetOrAddHeaderValue(KnownHeaders.ContentLength, value.ToString());
IsChunked = false;
}
else
{
Headers.RemoveHeader(KnownHeaders.ContentLength);
}
}
}
/// <summary>
/// Content encoding for this request/response
/// </summary>
public string ContentEncoding => Headers.GetHeaderValueOrNull(KnownHeaders.ContentEncoding)?.Trim();
/// <summary>
/// Encoding for this request/response
/// </summary>
public Encoding Encoding => HttpHelper.GetEncodingFromContentType(ContentType);
/// <summary>
/// Content-type of the request/response
/// </summary>
public string ContentType
{
get => Headers.GetHeaderValueOrNull(KnownHeaders.ContentType);
set => Headers.SetOrAddHeaderValue(KnownHeaders.ContentType, value);
}
/// <summary>
/// Is body send as chunked bytes
/// </summary>
public bool IsChunked
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.TransferEncoding);
return headerValue != null && headerValue.ContainsIgnoreCase(KnownHeaders.TransferEncodingChunked);
}
set
{
if (value)
{
Headers.SetOrAddHeaderValue(KnownHeaders.TransferEncoding, KnownHeaders.TransferEncodingChunked);
ContentLength = -1;
}
else
{
Headers.RemoveHeader(KnownHeaders.TransferEncoding);
}
}
}
public abstract string HeaderText { get; }
/// <summary>
/// Body as byte array
/// </summary>
[Browsable(false)]
public byte[] Body
{
get
{
EnsureBodyAvailable();
return body;
}
internal set
{
body = value;
bodyString = null;
}
}
internal abstract void EnsureBodyAvailable(bool throwWhenNotReadYet = true);
/// <summary>
/// Body as string
/// Use the encoding specified to decode the byte[] data to string
/// </summary>
[Browsable(false)]
public string BodyString => bodyString ?? (bodyString = Encoding.GetString(Body));
/// <summary>
/// Was the body read by user?
/// </summary>
public bool IsBodyRead { get; internal set; }
/// <summary>
/// Is the request/response no more modifyable by user (user callbacks complete?)
/// </summary>
internal bool Locked { get; set; }
internal void UpdateContentLength()
{
ContentLength = IsChunked ? -1 : body.Length;
}
/// <summary>
/// Finish the session
/// </summary>
internal void FinishSession()
{
if (!KeepBody)
{
body = null;
bodyString = null;
}
}
public override string ToString()
{
return HeaderText;
}
}
}
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
using System.ComponentModel; using System.ComponentModel;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -12,18 +11,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -12,18 +11,8 @@ namespace Titanium.Web.Proxy.Http
/// Http(s) response object /// Http(s) response object
/// </summary> /// </summary>
[TypeConverter(typeof(ExpandableObjectConverter))] [TypeConverter(typeof(ExpandableObjectConverter))]
public class Response public class Response : RequestResponseBase
{ {
/// <summary>
/// Cached response body content as byte array
/// </summary>
private byte[] body;
/// <summary>
/// Cached response body as string
/// </summary>
private string bodyString;
/// <summary> /// <summary>
/// Response Status Code. /// Response Status Code.
/// </summary> /// </summary>
...@@ -34,26 +23,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -34,26 +23,6 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public string StatusDescription { get; set; } public string StatusDescription { get; set; }
/// <summary>
/// Encoding used in response
/// </summary>
public Encoding Encoding => HttpHelper.GetEncodingFromContentType(ContentType);
/// <summary>
/// Content encoding for this response
/// </summary>
public string ContentEncoding => Headers.GetHeaderValueOrNull(KnownHeaders.ContentEncoding)?.Trim();
/// <summary>
/// Http version
/// </summary>
public Version HttpVersion { get; set; } = HttpHeader.VersionUnknown;
/// <summary>
/// Keeps the response body data after the session is finished
/// </summary>
public bool KeepBody { get; set; }
/// <summary> /// <summary>
/// Has response body? /// Has response body?
/// </summary> /// </summary>
...@@ -108,78 +77,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -108,78 +77,9 @@ namespace Titanium.Web.Proxy.Http
} }
} }
/// <summary> internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true)
/// Content type of this response
/// </summary>
public string ContentType => Headers.GetHeaderValueOrNull(KnownHeaders.ContentType);
/// <summary>
/// Length of response body
/// </summary>
public long ContentLength
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.ContentLength);
if (headerValue == null)
{
return -1;
}
if (long.TryParse(headerValue, out long contentLen))
{
return contentLen;
}
return -1;
}
set
{
if (value >= 0)
{
Headers.SetOrAddHeaderValue(KnownHeaders.ContentLength, value.ToString());
IsChunked = false;
}
else
{
Headers.RemoveHeader(KnownHeaders.ContentLength);
}
}
}
/// <summary>
/// Response transfer-encoding is chunked?
/// </summary>
public bool IsChunked
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.TransferEncoding);
return headerValue != null && headerValue.ContainsIgnoreCase(KnownHeaders.TransferEncodingChunked);
}
set
{
if (value)
{
Headers.SetOrAddHeaderValue(KnownHeaders.TransferEncoding, KnownHeaders.TransferEncodingChunked);
ContentLength = -1;
}
else
{
Headers.RemoveHeader(KnownHeaders.TransferEncoding);
}
}
}
/// <summary>
/// Collection of all response headers
/// </summary>
public HeaderCollection Headers { get; } = new HeaderCollection();
internal void EnsureBodyAvailable()
{ {
if (!IsBodyRead) if (!IsBodyRead && throwWhenNotReadYet)
{ {
throw new Exception("Response body is not read yet. " + throw new Exception("Response body is not read yet. " +
"Use SessionEventArgs.GetResponseBody() or SessionEventArgs.GetResponseBodyAsString() " + "Use SessionEventArgs.GetResponseBody() or SessionEventArgs.GetResponseBodyAsString() " +
...@@ -187,41 +87,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -187,41 +87,6 @@ namespace Titanium.Web.Proxy.Http
} }
} }
/// <summary>
/// Response body as byte array
/// </summary>
[Browsable(false)]
public byte[] Body
{
get
{
EnsureBodyAvailable();
return body;
}
internal set
{
body = value;
bodyString = null;
}
}
/// <summary>
/// Response body as string
/// Use the encoding specified in response to decode the byte[] data to string
/// </summary>
[Browsable(false)]
public string BodyString => bodyString ?? (bodyString = Encoding.GetString(Body));
/// <summary>
/// Was response body read by user
/// </summary>
public bool IsBodyRead { get; internal set; }
/// <summary>
/// Is response is no more modifyable by user (user callbacks complete?)
/// </summary>
internal bool ResponseLocked { get; set; }
/// <summary> /// <summary>
/// Is response 100-continue /// Is response 100-continue
/// </summary> /// </summary>
...@@ -240,7 +105,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -240,7 +105,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Gets the header text. /// Gets the header text.
/// </summary> /// </summary>
public string HeaderText public override string HeaderText
{ {
get get
{ {
...@@ -280,22 +145,5 @@ namespace Titanium.Web.Proxy.Http ...@@ -280,22 +145,5 @@ namespace Titanium.Web.Proxy.Http
statusCode = int.Parse(httpResult[1]); statusCode = int.Parse(httpResult[1]);
statusDescription = httpResult[2]; statusDescription = httpResult[2];
} }
/// <summary>
/// Finish the session
/// </summary>
internal void FinishSession()
{
if (!KeepBody)
{
body = null;
bodyString = null;
}
}
public override string ToString()
{
return HeaderText;
}
} }
} }
...@@ -233,7 +233,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -233,7 +233,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
} }
catch (Exception ex) catch (Exception ex)
{ {
exceptionFunc.Invoke(new Exception("Failed to create BC certificate", ex)); exceptionFunc(new Exception("Failed to create BC certificate", ex));
} }
if (!cancellationToken.IsCancellationRequested) if (!cancellationToken.IsCancellationRequested)
......
...@@ -271,7 +271,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -271,7 +271,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
} }
catch (Exception ex) catch (Exception ex)
{ {
exceptionFunc.Invoke(new Exception("Failed to create Win certificate", ex)); exceptionFunc(new Exception("Failed to create Win certificate", ex));
} }
if (!cancellationToken.IsCancellationRequested) if (!cancellationToken.IsCancellationRequested)
......
...@@ -189,6 +189,11 @@ namespace Titanium.Web.Proxy ...@@ -189,6 +189,11 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public event AsyncEventHandler<SessionEventArgs> BeforeResponse; public event AsyncEventHandler<SessionEventArgs> BeforeResponse;
/// <summary>
/// Intercept after response from server
/// </summary>
public event AsyncEventHandler<SessionEventArgs> AfterResponse;
/// <summary> /// <summary>
/// Callback for error events in proxy /// Callback for error events in proxy
/// </summary> /// </summary>
......
This diff is collapsed.
using System; using System;
using System.IO;
using System.IO.Compression;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Compression; using Titanium.Web.Proxy.Compression;
...@@ -36,7 +38,7 @@ namespace Titanium.Web.Proxy ...@@ -36,7 +38,7 @@ namespace Titanium.Web.Proxy
args.ReRequest = false; args.ReRequest = false;
//if user requested call back then do it //if user requested call back then do it
if (!response.ResponseLocked) if (!response.Locked)
{ {
await InvokeBeforeResponse(args); await InvokeBeforeResponse(args);
} }
...@@ -51,7 +53,7 @@ namespace Titanium.Web.Proxy ...@@ -51,7 +53,7 @@ namespace Titanium.Web.Proxy
return; return;
} }
response.ResponseLocked = true; response.Locked = true;
var clientStreamWriter = args.ProxyClient.ClientStreamWriter; var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
...@@ -80,22 +82,23 @@ namespace Titanium.Web.Proxy ...@@ -80,22 +82,23 @@ namespace Titanium.Web.Proxy
bool isChunked = response.IsChunked; bool isChunked = response.IsChunked;
string contentEncoding = response.ContentEncoding; string contentEncoding = response.ContentEncoding;
if (contentEncoding != null) var body = response.Body;
if (contentEncoding != null && body != null)
{ {
response.Body = await GetCompressedResponseBody(contentEncoding, response.Body); body = GetCompressedBody(contentEncoding, body);
if (isChunked == false) if (isChunked == false)
{ {
response.ContentLength = response.Body.Length; response.ContentLength = body.Length;
} }
else else
{ {
response.ContentLength = -1; response.ContentLength = -1;
} }
} }
await clientStreamWriter.WriteHeadersAsync(response.Headers); await clientStreamWriter.WriteHeadersAsync(response.Headers);
await clientStreamWriter.WriteBodyAsync(response.Body, isChunked); await clientStreamWriter.WriteBodyAsync(body, isChunked);
} }
else else
{ {
...@@ -104,7 +107,7 @@ namespace Titanium.Web.Proxy ...@@ -104,7 +107,7 @@ namespace Titanium.Web.Proxy
//Write body if exists //Write body if exists
if (response.HasBody) if (response.HasBody)
{ {
await args.CopyResponseBodyAsync(clientStreamWriter, false); await args.CopyResponseBodyAsync(clientStreamWriter, TransformationMode.None);
} }
} }
} }
...@@ -115,15 +118,23 @@ namespace Titanium.Web.Proxy ...@@ -115,15 +118,23 @@ namespace Titanium.Web.Proxy
} }
/// <summary> /// <summary>
/// get the compressed response body from give response bytes /// get the compressed body from given bytes
/// </summary> /// </summary>
/// <param name="encodingType"></param> /// <param name="encodingType"></param>
/// <param name="responseBodyStream"></param> /// <param name="body"></param>
/// <returns></returns> /// <returns></returns>
private async Task<byte[]> GetCompressedResponseBody(string encodingType, byte[] responseBodyStream) private byte[] GetCompressedBody(string encodingType, byte[] body)
{ {
var compressor = CompressionFactory.GetCompression(encodingType); var compressor = CompressionFactory.GetCompression(encodingType);
return await compressor.Compress(responseBodyStream); using (var ms = new MemoryStream())
{
using (var zip = compressor.GetStream(ms))
{
zip.Write(body, 0, body.Length);
}
return ms.ToArray();
}
} }
...@@ -134,5 +145,13 @@ namespace Titanium.Web.Proxy ...@@ -134,5 +145,13 @@ namespace Titanium.Web.Proxy
await BeforeResponse.InvokeAsync(this, args, ExceptionFunc); await BeforeResponse.InvokeAsync(this, args, ExceptionFunc);
} }
} }
private async Task InvokeAfterResponse(SessionEventArgs args)
{
if (AfterResponse != null)
{
await AfterResponse.InvokeAsync(this, args, ExceptionFunc);
}
}
} }
} }
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.8.1.4" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.1.4" />
<PackageReference Include="StreamExtended" Version="1.0.138-beta" /> <PackageReference Include="StreamExtended" Version="1.0.141-beta" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright> <copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright>
<tags></tags> <tags></tags>
<dependencies> <dependencies>
<dependency id="StreamExtended" version="1.0.138-beta" /> <dependency id="StreamExtended" version="1.0.141-beta" />
<dependency id="Portable.BouncyCastle" version="1.8.1.4" /> <dependency id="Portable.BouncyCastle" version="1.8.1.4" />
</dependencies> </dependencies>
</metadata> </metadata>
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Portable.BouncyCastle" version="1.8.1.4" targetFramework="net45" /> <package id="Portable.BouncyCastle" version="1.8.1.4" targetFramework="net45" />
<package id="StreamExtended" version="1.0.138-beta" targetFramework="net45" /> <package id="StreamExtended" version="1.0.141-beta" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
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