Commit aeb9f31e authored by justcoding121's avatar justcoding121

Merge pull request #68 from justcoding121/release

Expect Continue Fixes
parents a4d42abf e4c68b0b
......@@ -31,7 +31,7 @@ Import-Module "$Here\Common" -DisableNameChecking
$NuGet = Join-Path $SolutionRoot ".nuget\nuget.exe"
$MSBuild ="${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\msbuild.exe"
$MSBuild ="${env:ProgramFiles(x86)}\MSBuild\14.0\Bin\msbuild.exe"
FormatTaskName (("-"*25) + "[{0}]" + ("-"*25))
......@@ -39,7 +39,6 @@ Task default -depends Clean, Build, Package
Task Build -depends Restore-Packages {
exec { . $MSBuild $SolutionFile /t:Build /v:normal /p:Configuration=$Configuration }
exec { . $MSBuild $SolutionFile /t:Build /v:normal /p:Configuration=$Configuration-Net45 }
}
Task Package -depends Build {
......
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
\ No newline at end of file
</configuration>
......@@ -9,8 +9,6 @@ namespace Titanium.Web.Proxy.Test
{
public class ProxyTestController
{
public void StartProxy()
{
ProxyServer.BeforeRequest += OnRequest;
......@@ -19,8 +17,9 @@ namespace Titanium.Web.Proxy.Test
//Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning
//for example dropbox.com
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true){
// ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
{
// ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }
};
//An explicit endpoint is where the client knows about the existance of a proxy
......@@ -28,7 +27,7 @@ namespace Titanium.Web.Proxy.Test
ProxyServer.AddEndPoint(explicitEndPoint);
ProxyServer.Start();
//Transparent endpoint is usefull for reverse proxying (client is not aware of the existance of proxy)
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint
//Currently do not support Server Name Indication (It is not currently supported by SslStream class)
......@@ -36,19 +35,17 @@ namespace Titanium.Web.Proxy.Test
//In this example only google.com will work for HTTPS requests
//Other sites will receive a certificate mismatch warning on browser
//Please read about it before asking questions!
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true) {
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true)
{
GenericCertificateName = "google.com"
};
};
ProxyServer.AddEndPoint(transparentEndPoint);
foreach (var endPoint in ProxyServer.ProxyEndPoints)
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
//You can also add/remove end points after proxy has been started
ProxyServer.RemoveEndPoint(transparentEndPoint);
//Only explicit proxies can be set as system proxy!
ProxyServer.SetAsSystemHttpProxy(explicitEndPoint);
ProxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
......@@ -85,10 +82,20 @@ namespace Titanium.Web.Proxy.Test
//To cancel a request with a custom HTML content
//Filter URL
if (e.ProxySession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
{
e.Ok("<!DOCTYPE html><html><body><h1>Website Blocked</h1><p>Blocked by titanium web proxy.</p></body></html>");
e.Ok("<!DOCTYPE html>" +
"<html><body><h1>" +
"Website Blocked" +
"</h1>" +
"<p>Blocked by titanium web proxy.</p>" +
"</body>" +
"</html>");
}
//Redirect example
if (e.ProxySession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
{
e.Redirect("https://www.paypal.com");
}
}
......@@ -107,7 +114,11 @@ namespace Titanium.Web.Proxy.Test
{
if (e.ProxySession.Response.ContentType.Trim().ToLower().Contains("text/html"))
{
byte[] bodyBytes = e.GetResponseBody();
e.SetResponseBody(bodyBytes);
string body = e.GetResponseBodyAsString();
e.SetResponseBodyString(body);
}
}
}
......
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
......@@ -11,49 +11,32 @@
<RootNamespace>Titanium.Web.Proxy.Test</RootNamespace>
<AssemblyName>Titanium.Web.Proxy.Test</AssemblyName>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>TRACE;DEBUG;NET45</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>TRACE;NET45</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Net45|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug-Net45\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Net45|AnyCPU'">
<OutputPath>bin\Release-Net45\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
......

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B6DBABDC-C985-4872-9C38-B4E5079CBC4B}"
EndProject
......@@ -19,27 +19,17 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug-Net45|Any CPU = Debug-Net45|Any CPU
Release|Any CPU = Release|Any CPU
Release-Net45|Any CPU = Release-Net45|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Debug-Net45|Any CPU.ActiveCfg = Debug-Net45|Any CPU
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Debug-Net45|Any CPU.Build.0 = Debug-Net45|Any CPU
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Release|Any CPU.Build.0 = Release|Any CPU
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Release-Net45|Any CPU.ActiveCfg = Release-Net45|Any CPU
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Release-Net45|Any CPU.Build.0 = Release-Net45|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Debug-Net45|Any CPU.ActiveCfg = Debug-Net45|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Debug-Net45|Any CPU.Build.0 = Debug-Net45|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Release|Any CPU.Build.0 = Release|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Release-Net45|Any CPU.ActiveCfg = Release-Net45|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Release-Net45|Any CPU.Build.0 = Release-Net45|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......
namespace Titanium.Web.Proxy.Compression
{
class CompressionFactory
{
public ICompression Create(string type)
{
switch (type)
{
case "gzip":
return new GZipCompression();
case "deflate":
return new DeflateCompression();
case "zlib":
return new ZlibCompression();
default:
return null;
}
}
}
}
using System.IO;
using System.IO.Compression;
namespace Titanium.Web.Proxy.Compression
{
class DeflateCompression : ICompression
{
public byte[] Compress(byte[] responseBody)
{
using (var ms = new MemoryStream())
{
using (var zip = new DeflateStream(ms, CompressionMode.Compress, true))
{
zip.Write(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
}
}
}
}
using Ionic.Zlib;
using System.IO;
namespace Titanium.Web.Proxy.Compression
{
class GZipCompression : ICompression
{
public byte[] Compress(byte[] responseBody)
{
using (var ms = new MemoryStream())
{
using (var zip = new GZipStream(ms, CompressionMode.Compress, true))
{
zip.Write(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
}
}
}
}
namespace Titanium.Web.Proxy.Compression
{
interface ICompression
{
byte[] Compress(byte[] responseBody);
}
}
using Ionic.Zlib;
using System.IO;
namespace Titanium.Web.Proxy.Compression
{
class ZlibCompression : ICompression
{
public byte[] Compress(byte[] responseBody)
{
using (var ms = new MemoryStream())
{
using (var zip = new ZlibStream(ms, CompressionMode.Compress, true))
{
zip.Write(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
}
}
}
}
namespace Titanium.Web.Proxy.Decompression
{
class DecompressionFactory
{
public IDecompression Create(string type)
{
switch(type)
{
case "gzip":
return new GZipDecompression();
case "deflate":
return new DeflateDecompression();
case "zlib":
return new ZlibDecompression();
default:
return new DefaultDecompression();
}
}
}
}
namespace Titanium.Web.Proxy.Decompression
{
class DefaultDecompression : IDecompression
{
public byte[] Decompress(byte[] compressedArray)
{
return compressedArray;
}
}
}
using Ionic.Zlib;
using System.IO;
namespace Titanium.Web.Proxy.Decompression
{
class DeflateDecompression : IDecompression
{
public byte[] Decompress(byte[] compressedArray)
{
var stream = new MemoryStream(compressedArray);
using (var decompressor = new DeflateStream(stream, CompressionMode.Decompress))
{
var buffer = new byte[ProxyServer.BUFFER_SIZE];
using (var output = new MemoryStream())
{
int read;
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
}
}
}
using System.IO;
using System.IO.Compression;
namespace Titanium.Web.Proxy.Decompression
{
class GZipDecompression : IDecompression
{
public byte[] Decompress(byte[] compressedArray)
{
using (var decompressor = new GZipStream(new MemoryStream(compressedArray), CompressionMode.Decompress))
{
var buffer = new byte[ProxyServer.BUFFER_SIZE];
using (var output = new MemoryStream())
{
int read;
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
}
}
}
using System.IO;
namespace Titanium.Web.Proxy.Decompression
{
interface IDecompression
{
byte[] Decompress(byte[] compressedArray);
}
}
using Ionic.Zlib;
using System.IO;
namespace Titanium.Web.Proxy.Decompression
{
class ZlibDecompression : IDecompression
{
public byte[] Decompress(byte[] compressedArray)
{
var memoryStream = new MemoryStream(compressedArray);
using (var decompressor = new ZlibStream(memoryStream, CompressionMode.Decompress))
{
var buffer = new byte[ProxyServer.BUFFER_SIZE];
using (var output = new MemoryStream())
{
int read;
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Responses;
using Titanium.Web.Proxy.Decompression;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// Holds info related to a single proxy session (single request/response sequence)
/// A proxy session is bounded to a single connection from client
......@@ -22,13 +18,11 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public class SessionEventArgs : EventArgs, IDisposable
{
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal SessionEventArgs()
{
Client = new ProxyClient();
ProxySession = new HttpWebSession();
}
......@@ -38,7 +32,6 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
internal ProxyClient Client { get; set; }
/// <summary>
/// Does this session uses SSL
/// </summary>
......@@ -50,7 +43,6 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public HttpWebSession ProxySession { get; set; }
/// <summary>
/// A shortcut to get the request content length
/// </summary>
......@@ -165,22 +157,7 @@ namespace Titanium.Web.Proxy.EventArguments
try
{
//decompress
switch (requestContentEncoding)
{
case "gzip":
ProxySession.Request.RequestBody = CompressionHelper.DecompressGzip(requestBodyStream.ToArray());
break;
case "deflate":
ProxySession.Request.RequestBody = CompressionHelper.DecompressDeflate(requestBodyStream);
break;
case "zlib":
ProxySession.Request.RequestBody = CompressionHelper.DecompressZlib(requestBodyStream);
break;
default:
ProxySession.Request.RequestBody = requestBodyStream.ToArray();
break;
}
ProxySession.Request.RequestBody = GetDecompressedResponseBody(requestContentEncoding, requestBodyStream.ToArray());
}
catch
{
......@@ -235,22 +212,9 @@ namespace Titanium.Web.Proxy.EventArguments
var buffer = ProxySession.ProxyClient.ServerStreamReader.ReadBytes(ProxySession.Response.ContentLength);
responseBodyStream.Write(buffer, 0, buffer.Length);
}
//decompress
switch (ProxySession.Response.ContentEncoding)
{
case "gzip":
ProxySession.Response.ResponseBody = CompressionHelper.DecompressGzip(responseBodyStream.ToArray());
break;
case "deflate":
ProxySession.Response.ResponseBody = CompressionHelper.DecompressDeflate(responseBodyStream);
break;
case "zlib":
ProxySession.Response.ResponseBody = CompressionHelper.DecompressZlib(responseBodyStream);
break;
default:
ProxySession.Response.ResponseBody = responseBodyStream.ToArray();
break;
}
ProxySession.Response.ResponseBody = GetDecompressedResponseBody(ProxySession.Response.ContentEncoding, responseBodyStream.ToArray());
}
//set this to true for caching
ProxySession.Response.ResponseBodyRead = true;
......@@ -379,6 +343,14 @@ namespace Titanium.Web.Proxy.EventArguments
SetResponseBody(bodyBytes);
}
private byte[] GetDecompressedResponseBody(string encodingType, byte[] responseBodyStream)
{
var decompressionFactory = new DecompressionFactory();
var decompressor = decompressionFactory.Create(encodingType);
return decompressor.Decompress(responseBodyStream);
}
/// <summary>
/// Before request is made to server
......@@ -406,20 +378,23 @@ namespace Titanium.Web.Proxy.EventArguments
/// <param name="body"></param>
public void Ok(byte[] result)
{
var response = new Response();
var response = new OkResponse();
response.HttpVersion = ProxySession.Request.HttpVersion;
response.ResponseStatusCode = "200";
response.ResponseStatusDescription = "Ok";
response.ResponseBody = result;
Respond(response);
response.ResponseHeaders.Add(new HttpHeader("Timestamp", DateTime.Now.ToString()));
ProxySession.Request.CancelRequest = true;
}
response.ResponseHeaders.Add(new HttpHeader("content-length", DateTime.Now.ToString()));
response.ResponseHeaders.Add(new HttpHeader("Cache-Control", "no-cache, no-store, must-revalidate"));
response.ResponseHeaders.Add(new HttpHeader("Pragma", "no-cache"));
response.ResponseHeaders.Add(new HttpHeader("Expires", "0"));
public void Redirect(string url)
{
var response = new RedirectResponse();
response.ResponseBody = result;
response.HttpVersion = ProxySession.Request.HttpVersion;
response.ResponseHeaders.Add(new Models.HttpHeader("Location", url));
response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty);
Respond(response);
......@@ -435,7 +410,9 @@ namespace Titanium.Web.Proxy.EventArguments
response.ResponseBodyRead = true;
ProxySession.Response = response;
ProxyServer.HandleHttpSessionResponse(this);
}
}
}
\ No newline at end of file
......@@ -7,7 +7,7 @@ using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Network
namespace Titanium.Web.Proxy.Extensions
{
internal static class TcpExtensions
......
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Ionic.Zlib;
namespace Titanium.Web.Proxy.Helpers
{
/// <summary>
/// A helper to handle compression/decompression (gzip, zlib & deflate)
/// </summary>
public class CompressionHelper
{
/// <summary>
/// compress the given bytes using zlib compression
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
public static byte[] CompressZlib(byte[] bytes)
{
using (var ms = new MemoryStream())
{
using (var zip = new ZlibStream(ms, CompressionMode.Compress, true))
{
zip.Write(bytes, 0, bytes.Length);
}
return ms.ToArray();
}
}
/// <summary>
/// compress the given bytes using deflate compression
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
public static byte[] CompressDeflate(byte[] bytes)
{
using (var ms = new MemoryStream())
{
using (var zip = new DeflateStream(ms, CompressionMode.Compress, true))
{
zip.Write(bytes, 0, bytes.Length);
}
return ms.ToArray();
}
}
/// <summary>
/// compress the given bytes using gzip compression
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
public static byte[] CompressGzip(byte[] bytes)
{
using (var ms = new MemoryStream())
{
using (var zip = new GZipStream(ms, CompressionMode.Compress, true))
{
zip.Write(bytes, 0, bytes.Length);
}
return ms.ToArray();
}
}
/// <summary>
/// decompression the gzip compressed byte array
/// </summary>
/// <param name="gzip"></param>
/// <returns></returns>
//identify why passing stream instead of bytes returns empty result
public static byte[] DecompressGzip(byte[] gzip)
{
using (var decompressor = new System.IO.Compression.GZipStream(new MemoryStream(gzip), System.IO.Compression.CompressionMode.Decompress))
{
var buffer = new byte[ProxyServer.BUFFER_SIZE];
using (var output = new MemoryStream())
{
int read;
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
}
/// <summary>
/// decompress the deflate byte stream
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static byte[] DecompressDeflate(Stream input)
{
using (var decompressor = new DeflateStream(input, CompressionMode.Decompress))
{
var buffer = new byte[ProxyServer.BUFFER_SIZE];
using (var output = new MemoryStream())
{
int read;
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
}
/// <summary>
/// decompress the zlib byte stream
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static byte[] DecompressZlib(Stream input)
{
using (var decompressor = new ZlibStream(input, CompressionMode.Decompress))
{
var buffer = new byte[ProxyServer.BUFFER_SIZE];
using (var output = new MemoryStream())
{
int read;
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
}
}
}
\ No newline at end of file
......@@ -97,10 +97,22 @@ namespace Titanium.Web.Proxy.Network
}
}
public bool ExpectContinue
{
get
{
var header = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "expect");
if (header != null) return header.Value.Equals("100-continue");
return false;
}
}
public string Url { get { return RequestUri.OriginalString; } }
internal Encoding Encoding { get { return this.GetEncoding(); } }
/// <summary>
/// Terminates the underlying Tcp Connection to client after current request
/// </summary>
internal bool CancelRequest { get; set; }
internal byte[] RequestBody { get; set; }
......@@ -125,7 +137,8 @@ namespace Titanium.Web.Proxy.Network
}
public List<HttpHeader> RequestHeaders { get; set; }
public bool Is100Continue { get; internal set; }
public bool ExpectationFailed { get; internal set; }
public Request()
{
......@@ -248,7 +261,8 @@ namespace Titanium.Web.Proxy.Network
internal string ResponseBodyString { get; set; }
internal bool ResponseBodyRead { get; set; }
internal bool ResponseLocked { get; set; }
public bool Is100Continue { get; internal set; }
public bool ExpectationFailed { get; internal set; }
public Response()
{
......@@ -272,6 +286,7 @@ namespace Titanium.Web.Proxy.Network
public Response Response { get; set; }
internal TcpConnection ProxyClient { get; set; }
public void SetConnection(TcpConnection Connection)
{
Connection.LastAccess = DateTime.Now;
......@@ -308,6 +323,28 @@ namespace Titanium.Web.Proxy.Network
byte[] requestBytes = Encoding.ASCII.GetBytes(request);
stream.Write(requestBytes, 0, requestBytes.Length);
stream.Flush();
if (ProxyServer.Enable100ContinueBehaviour)
if (this.Request.ExpectContinue)
{
var httpResult = ProxyClient.ServerStreamReader.ReadLine().Split(new char[] { ' ' }, 3);
var responseStatusCode = httpResult[1].Trim();
var responseStatusDescription = httpResult[2].Trim();
//find if server is willing for expect continue
if (responseStatusCode.Equals("100")
&& responseStatusDescription.ToLower().Equals("continue"))
{
this.Request.Is100Continue = true;
ProxyClient.ServerStreamReader.ReadLine();
}
else if (responseStatusCode.Equals("417")
&& responseStatusDescription.ToLower().Equals("expectation failed"))
{
this.Request.ExpectationFailed = true;
ProxyClient.ServerStreamReader.ReadLine();
}
}
}
public void ReceiveResponse()
......@@ -322,11 +359,29 @@ namespace Titanium.Web.Proxy.Network
var s = ProxyClient.ServerStreamReader.ReadLine();
}
this.Response.HttpVersion = httpResult[0];
this.Response.ResponseStatusCode = httpResult[1];
string status = httpResult[2];
this.Response.HttpVersion = httpResult[0].Trim();
this.Response.ResponseStatusCode = httpResult[1].Trim();
this.Response.ResponseStatusDescription = httpResult[2].Trim();
this.Response.ResponseStatusDescription = status;
//For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
if (this.Response.ResponseStatusCode.Equals("100")
&& this.Response.ResponseStatusDescription.ToLower().Equals("continue"))
{
this.Response.Is100Continue = true;
this.Response.ResponseStatusCode = null;
ProxyClient.ServerStreamReader.ReadLine();
ReceiveResponse();
return;
}
else if (this.Response.ResponseStatusCode.Equals("417")
&& this.Response.ResponseStatusDescription.ToLower().Equals("expectation failed"))
{
this.Response.ExpectationFailed = true;
this.Response.ResponseStatusCode = null;
ProxyClient.ServerStreamReader.ReadLine();
ReceiveResponse();
return;
}
List<string> responseLines = ProxyClient.ServerStreamReader.ReadAllLines();
......
......@@ -10,6 +10,7 @@ using System.Net.Security;
using Titanium.Web.Proxy.Helpers;
using System.Threading;
using System.Security.Authentication;
using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy.Network
{
......
......@@ -40,7 +40,7 @@ namespace Titanium.Web.Proxy
#else
internal static SslProtocols SupportedProtocols = SslProtocols.Tls | SslProtocols.Ssl3;
#endif
static ProxyServer()
{
......@@ -56,6 +56,7 @@ namespace Titanium.Web.Proxy
public static string RootCertificateIssuerName { get; set; }
public static string RootCertificateName { get; set; }
public static bool Enable100ContinueBehaviour { get; set; }
public static event EventHandler<SessionEventArgs> BeforeRequest;
public static event EventHandler<SessionEventArgs> BeforeResponse;
......
......@@ -132,7 +132,7 @@ namespace Titanium.Web.Proxy
//if(endPoint.UseServerNameIndication)
//{
// //implement in future once SNI supported by SSL stream
// certificate = CertManager.CreateCertificate(endPoint.GenericCertificateName);
// certificate = CertManager.CreateCertificate(hostName);
//}
//else
certificate = CertManager.CreateCertificate(endPoint.GenericCertificateName);
......@@ -207,7 +207,6 @@ namespace Titanium.Web.Proxy
version = new Version(1, 0);
}
args.ProxySession.Request.RequestHeaders = new List<HttpHeader>();
string tmpLine;
......@@ -217,13 +216,9 @@ namespace Titanium.Web.Proxy
args.ProxySession.Request.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
}
var httpRemoteUri = new Uri(!IsHttps ? httpCmdSplit[1] : (string.Concat("https://", args.ProxySession.Request.Host, httpCmdSplit[1])));
args.IsHttps = IsHttps;
args.ProxySession.Request.RequestUri = httpRemoteUri;
args.ProxySession.Request.Method = httpMethod;
......@@ -232,20 +227,6 @@ namespace Titanium.Web.Proxy
args.Client.ClientStreamReader = clientStreamReader;
args.Client.ClientStreamWriter = clientStreamWriter;
//If requested interception
if (BeforeRequest != null)
{
BeforeRequest(null, args);
}
args.ProxySession.Request.RequestLocked = true;
if (args.ProxySession.Request.CancelRequest)
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
break;
}
if (args.ProxySession.Request.UpgradeToWebSocket)
{
TcpHelper.SendRaw(clientStream, httpCmd, args.ProxySession.Request.RequestHeaders,
......@@ -263,8 +244,47 @@ namespace Titanium.Web.Proxy
lastRequestHostName = args.ProxySession.Request.RequestUri.Host;
args.ProxySession.Request.Host = args.ProxySession.Request.RequestUri.Host;
args.ProxySession.SetConnection(connection);
args.ProxySession.SendRequest();
//If requested interception
if (BeforeRequest != null)
{
BeforeRequest(null, args);
}
args.ProxySession.Request.RequestLocked = true;
if (args.ProxySession.Request.ExpectContinue)
{
args.ProxySession.SetConnection(connection);
args.ProxySession.SendRequest();
}
if (Enable100ContinueBehaviour)
if (args.ProxySession.Request.Is100Continue)
{
WriteResponseStatus(args.ProxySession.Response.HttpVersion, "100",
"Continue", args.Client.ClientStreamWriter);
args.Client.ClientStreamWriter.WriteLine();
}
else if (args.ProxySession.Request.ExpectationFailed)
{
WriteResponseStatus(args.ProxySession.Response.HttpVersion, "417",
"Expectation Failed", args.Client.ClientStreamWriter);
args.Client.ClientStreamWriter.WriteLine();
}
if (args.ProxySession.Request.CancelRequest)
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
break;
}
if (!args.ProxySession.Request.ExpectContinue)
{
args.ProxySession.SetConnection(connection);
args.ProxySession.SendRequest();
}
//If request was modified by user
if (args.ProxySession.Request.RequestBodyRead)
......@@ -275,14 +295,20 @@ namespace Titanium.Web.Proxy
}
else
{
//If its a post/put request, then read the client html body and send it to server
if (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT")
if (!args.ProxySession.Request.ExpectationFailed)
{
SendClientRequestBody(args);
//If its a post/put request, then read the client html body and send it to server
if (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT")
{
SendClientRequestBody(args);
}
}
}
HandleHttpSessionResponse(args);
if (!args.ProxySession.Request.ExpectationFailed)
{
HandleHttpSessionResponse(args);
}
//if connection is closing exit
if (args.ProxySession.Response.ResponseKeepAlive == false)
......
......@@ -12,6 +12,7 @@ using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Compression;
namespace Titanium.Web.Proxy
{
......@@ -35,35 +36,38 @@ namespace Titanium.Web.Proxy
args.ProxySession.Response.ResponseLocked = true;
if (args.ProxySession.Response.Is100Continue)
{
WriteResponseStatus(args.ProxySession.Response.HttpVersion, "100",
"Continue", args.Client.ClientStreamWriter);
args.Client.ClientStreamWriter.WriteLine();
}
else if (args.ProxySession.Response.ExpectationFailed)
{
WriteResponseStatus(args.ProxySession.Response.HttpVersion, "417",
"Expectation Failed", args.Client.ClientStreamWriter);
args.Client.ClientStreamWriter.WriteLine();
}
WriteResponseStatus(args.ProxySession.Response.HttpVersion, args.ProxySession.Response.ResponseStatusCode,
args.ProxySession.Response.ResponseStatusDescription, args.Client.ClientStreamWriter);
if (args.ProxySession.Response.ResponseBodyRead)
{
var isChunked = args.ProxySession.Response.IsChunked;
var contentEncoding = args.ProxySession.Response.ContentEncoding;
if(contentEncoding!=null)
switch (contentEncoding)
if (contentEncoding != null)
{
case "gzip":
args.ProxySession.Response.ResponseBody = CompressionHelper.CompressGzip(args.ProxySession.Response.ResponseBody);
break;
case "deflate":
args.ProxySession.Response.ResponseBody = CompressionHelper.CompressDeflate(args.ProxySession.Response.ResponseBody);
break;
case "zlib":
args.ProxySession.Response.ResponseBody = CompressionHelper.CompressZlib(args.ProxySession.Response.ResponseBody);
break;
args.ProxySession.Response.ResponseBody = GetCompressedResponseBody(contentEncoding, args.ProxySession.Response.ResponseBody);
}
WriteResponseStatus(args.ProxySession.Response.HttpVersion, args.ProxySession.Response.ResponseStatusCode,
args.ProxySession.Response.ResponseStatusDescription, args.Client.ClientStreamWriter);
WriteResponseHeaders(args.Client.ClientStreamWriter, args.ProxySession.Response.ResponseHeaders, args.ProxySession.Response.ResponseBody.Length,
isChunked);
WriteResponseBody(args.Client.ClientStream, args.ProxySession.Response.ResponseBody, isChunked);
}
else
{
WriteResponseStatus(args.ProxySession.Response.HttpVersion, args.ProxySession.Response.ResponseStatusCode,
args.ProxySession.Response.ResponseStatusDescription, args.Client.ClientStreamWriter);
WriteResponseHeaders(args.Client.ClientStreamWriter, args.ProxySession.Response.ResponseHeaders);
if (args.ProxySession.Response.IsChunked || args.ProxySession.Response.ContentLength > 0)
......@@ -83,6 +87,13 @@ namespace Titanium.Web.Proxy
}
}
private static byte[] GetCompressedResponseBody(string encodingType, byte[] responseBodyStream)
{
var compressionFactory = new CompressionFactory();
var compressor = compressionFactory.Create(encodingType);
return compressor.Compress(responseBodyStream);
}
private static void WriteResponseStatus(string version, string code, string description,
StreamWriter responseWriter)
......
using System;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.Responses
{
public class OkResponse : Response
{
public OkResponse()
{
ResponseStatusCode = "200";
ResponseStatusDescription = "Ok";
}
}
}
using System;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.Responses
{
public class RedirectResponse : Response
{
public RedirectResponse()
{
ResponseStatusCode = "302";
ResponseStatusDescription = "Found";
}
}
}
......@@ -13,6 +13,7 @@
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup>
<StartupObject />
......@@ -20,59 +21,27 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;NET40</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<OutputPath>bin\Release\</OutputPath>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<DefineConstants>NET40</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Net45|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug-Net45\</OutputPath>
<DefineConstants>DEBUG;NET45</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Net45|AnyCPU'">
<OutputPath>bin\Release-Net45\</OutputPath>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<OutputPath>bin\Release\</OutputPath>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<DefineConstants>NET45</DefineConstants>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Ionic.Zip, Version=1.9.7.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\DotNetZip.1.9.7\lib\net20\Ionic.Zip.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
<Reference Include="Ionic.Zip, Version=1.9.8.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
<HintPath>..\packages\DotNetZip.1.9.8\lib\net20\Ionic.Zip.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Net" />
<Reference Include="System.configuration" />
<Reference Include="System.Core" />
<Reference Include="System.IO">
<HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath>
</Reference>
<Reference Include="System.Net" />
<Reference Include="System.Runtime">
<HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
......@@ -80,6 +49,17 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Compression\CompressionFactory.cs" />
<Compile Include="Compression\DeflateCompression.cs" />
<Compile Include="Compression\GZipCompression.cs" />
<Compile Include="Compression\ICompression.cs" />
<Compile Include="Compression\ZlibCompression.cs" />
<Compile Include="Decompression\DecompressionFactory.cs" />
<Compile Include="Decompression\DefaultDecompression.cs" />
<Compile Include="Decompression\DeflateDecompression.cs" />
<Compile Include="Decompression\GZipDecompression.cs" />
<Compile Include="Decompression\IDecompression.cs" />
<Compile Include="Decompression\ZlibDecompression.cs" />
<Compile Include="EventArguments\ProxyClient.cs" />
<Compile Include="Exceptions\BodyNotFoundException.cs" />
<Compile Include="Extensions\HttpWebResponseExtensions.cs" />
......@@ -88,7 +68,7 @@
<Compile Include="Helpers\Firefox.cs" />
<Compile Include="Helpers\SystemProxy.cs" />
<Compile Include="Models\EndPoint.cs" />
<Compile Include="Network\TcpExtensions.cs" />
<Compile Include="Extensions\TcpExtensions.cs" />
<Compile Include="Network\TcpConnectionManager.cs" />
<Compile Include="Models\HttpHeader.cs" />
<Compile Include="Network\HttpWebClient.cs" />
......@@ -96,11 +76,12 @@
<Compile Include="RequestHandler.cs" />
<Compile Include="ResponseHandler.cs" />
<Compile Include="Helpers\CustomBinaryReader.cs" />
<Compile Include="Helpers\Compression.cs" />
<Compile Include="ProxyServer.cs" />
<Compile Include="EventArguments\SessionEventArgs.cs" />
<Compile Include="Helpers\Tcp.cs" />
<Compile Include="Extensions\StreamExtensions.cs" />
<Compile Include="Responses\OkResponse.cs" />
<Compile Include="Responses\RedirectResponse.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
......@@ -120,16 +101,11 @@
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
......@@ -14,12 +14,11 @@
<copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright>
<tags></tags>
<dependencies>
<dependency id="DotNetZip" version="1.9.7" />
<dependency id="DotNetZip" version="1.9.8" />
</dependencies>
</metadata>
<files>
<file src="bin\$configuration$\Titanium.Web.Proxy.dll" target="lib\net40" />
<file src="bin\$configuration$-Net45\Titanium.Web.Proxy.dll" target="lib\net45" />
<file src="bin\$configuration$\Titanium.Web.Proxy.dll" target="lib\net45" />
<file src="bin\$configuration$\makecert.exe" target="content" />
</files>
</package>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
\ No newline at end of file
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DotNetZip" version="1.9.7" targetFramework="net40" />
<package id="DotNetZip" version="1.9.7" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net40" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net40" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net40" />
<package id="DotNetZip" version="1.9.8" targetFramework="net45" />
</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