Commit a88c68c4 authored by titanium007's avatar titanium007

Merge pull request #29 from titanium007/release

Breaking Change on Release
parents 0cc53a6e 75d6ba76
...@@ -23,6 +23,10 @@ if(!$Configuration) { $Configuration = "Release" } ...@@ -23,6 +23,10 @@ if(!$Configuration) { $Configuration = "Release" }
if(!$Version) { $Version = $env:APPVEYOR_BUILD_VERSION } if(!$Version) { $Version = $env:APPVEYOR_BUILD_VERSION }
if(!$Version) { $Version = "1.0.$BuildNumber" } if(!$Version) { $Version = "1.0.$BuildNumber" }
if(!$Branch) { $Branch = $env:APPVEYOR_REPO_BRANCH }
if(!$Branch) { $Branch = "local" }
if($Branch -eq "release" ) { $Version = "$Version-beta" }
Import-Module "$Here\Common" -DisableNameChecking Import-Module "$Here\Common" -DisableNameChecking
$NuGet = Join-Path $SolutionRoot ".nuget\nuget.exe" $NuGet = Join-Path $SolutionRoot ".nuget\nuget.exe"
......
...@@ -58,12 +58,12 @@ Sample request and response event handlers ...@@ -58,12 +58,12 @@ Sample request and response event handlers
//Test On Request, intercept requests //Test On Request, intercept requests
public void OnRequest(object sender, SessionEventArgs e) public void OnRequest(object sender, SessionEventArgs e)
{ {
Console.WriteLine(e.RequestURL); Console.WriteLine(e.ProxySession.Request.RequestUrl);
//read request headers //read request headers
var requestHeaders = e.RequestHeaders; var requestHeaders = e.ProxySession.Request.RequestHeaders;
if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT") && e.RequestContentLength > 0) if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT"))
{ {
//Get/Set request body bytes //Get/Set request body bytes
byte[] bodyBytes = e.GetRequestBody(); byte[] bodyBytes = e.GetRequestBody();
...@@ -78,7 +78,7 @@ Sample request and response event handlers ...@@ -78,7 +78,7 @@ Sample request and response event handlers
//To cancel a request with a custom HTML content //To cancel a request with a custom HTML content
//Filter URL //Filter URL
if (e.RequestURL.Contains("google.com")) if (e.ProxySession.Request.RequestUrl.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>");
} }
...@@ -86,10 +86,11 @@ Sample request and response event handlers ...@@ -86,10 +86,11 @@ Sample request and response event handlers
public void OnResponse(object sender, SessionEventArgs e) public void OnResponse(object sender, SessionEventArgs e)
{ {
//read response headers ////read response headers
var responseHeaders = e.ResponseHeaders; var responseHeaders = e.ProxySession.Response.ResponseHeaders;
if (e.ResponseStatusCode == HttpStatusCode.OK)
if (e.ResponseStatusCode == "200")
{ {
if (e.ResponseContentType.Trim().ToLower().Contains("text/html")) if (e.ResponseContentType.Trim().ToLower().Contains("text/html"))
{ {
......
...@@ -4,4 +4,17 @@ ...@@ -4,4 +4,17 @@
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client" />
</startup> </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" />
</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" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration> </configuration>
\ No newline at end of file
using System; using System;
using System.Text.RegularExpressions;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
namespace Titanium.Web.Proxy.Test namespace Titanium.Web.Proxy.Test
...@@ -43,10 +44,10 @@ namespace Titanium.Web.Proxy.Test ...@@ -43,10 +44,10 @@ namespace Titanium.Web.Proxy.Test
//Read browser URL send back to proxy by the injection script in OnResponse event //Read browser URL send back to proxy by the injection script in OnResponse event
public void OnRequest(object sender, SessionEventArgs e) public void OnRequest(object sender, SessionEventArgs e)
{ {
Console.WriteLine(e.RequestUrl); Console.WriteLine(e.ProxySession.Request.Url);
////read request headers ////read request headers
//var requestHeaders = e.RequestHeaders; //var requestHeaders = e.ProxySession.Request.RequestHeaders;
//if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT")) //if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT"))
//{ //{
...@@ -63,7 +64,7 @@ namespace Titanium.Web.Proxy.Test ...@@ -63,7 +64,7 @@ namespace Titanium.Web.Proxy.Test
////To cancel a request with a custom HTML content ////To cancel a request with a custom HTML content
////Filter URL ////Filter URL
//if (e.RequestURL.Contains("google.com")) //if (e.ProxySession.Request.RequestUrl.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>");
//} //}
...@@ -73,27 +74,19 @@ namespace Titanium.Web.Proxy.Test ...@@ -73,27 +74,19 @@ namespace Titanium.Web.Proxy.Test
//Insert script to read the Browser URL and send it back to proxy //Insert script to read the Browser URL and send it back to proxy
public void OnResponse(object sender, SessionEventArgs e) public void OnResponse(object sender, SessionEventArgs e)
{ {
////read response headers
//var responseHeaders = e.ResponseHeaders;
////read response headers
// var responseHeaders = e.ProxySession.Response.ResponseHeaders;
//if (e.ResponseStatusCode == HttpStatusCode.OK) //if (!e.ProxySession.Request.Hostname.Equals("medeczane.sgk.gov.tr")) return;
//if (e.RequestMethod == "GET" || e.RequestMethod == "POST")
//{ //{
// if (e.ResponseContentType.Trim().ToLower().Contains("text/html")) // if (e.ProxySession.Response.ResponseStatusCode == "200")
// { // {
// //Get/Set response body bytes // if (e.ProxySession.Response.ContentType.Trim().ToLower().Contains("text/html"))
// byte[] responseBodyBytes = e.GetResponseBody(); // {
// e.SetResponseBody(responseBodyBytes); // string body = e.GetResponseBodyAsString();
// }
// //Get response body as string
// string responseBody = e.GetResponseBodyAsString();
// //Modify e.ServerResponse
// Regex rex = new Regex("</body>", RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline);
// string modified = rex.Replace(responseBody, "<script type =\"text/javascript\">alert('Response was modified by this script!');</script></body>", 1);
// //Set modifed response Html Body
// e.SetResponseBodyString(modified);
// } // }
//} //}
} }
......
...@@ -8,10 +8,22 @@ using System.Net.Sockets; ...@@ -8,10 +8,22 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
public class Client
{
internal TcpClient TcpClient { get; set; }
internal Stream ClientStream { get; set; }
internal CustomBinaryReader ClientStreamReader { get; set; }
internal StreamWriter ClientStreamWriter { get; set; }
public int ClientPort { get; internal set; }
public IPAddress ClientIpAddress { get; internal set; }
}
public class SessionEventArgs : EventArgs, IDisposable public class SessionEventArgs : EventArgs, IDisposable
{ {
readonly int _bufferSize; readonly int _bufferSize;
...@@ -19,110 +31,72 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -19,110 +31,72 @@ namespace Titanium.Web.Proxy.EventArguments
internal SessionEventArgs(int bufferSize) internal SessionEventArgs(int bufferSize)
{ {
_bufferSize = bufferSize; _bufferSize = bufferSize;
Client = new Client();
ProxySession = new HttpWebSession();
} }
internal TcpClient Client { get; set; } internal Client Client { get; set; }
internal Stream ClientStream { get; set; }
internal CustomBinaryReader ClientStreamReader { get; set; }
internal StreamWriter ClientStreamWriter { get; set; }
public bool IsHttps { get; internal set; } public bool IsHttps { get; internal set; }
public string RequestUrl { get; internal set; }
public string RequestHostname { get; internal set; }
public int ClientPort { get; internal set; } public HttpWebSession ProxySession { get; set; }
public IPAddress ClientIpAddress { get; internal set; }
internal Encoding RequestEncoding { get; set; }
internal Version RequestHttpVersion { get; set; }
internal bool RequestIsAlive { get; set; }
internal bool CancelRequest { get; set; }
internal byte[] RequestBody { get; set; }
internal string RequestBodyString { get; set; }
internal bool RequestBodyRead { get; set; }
public List<HttpHeader> RequestHeaders { get; internal set; }
internal bool RequestLocked { get; set; }
internal HttpWebRequest ProxyRequest { get; set; }
internal Encoding ResponseEncoding { get; set; }
internal Stream ResponseStream { get; set; }
internal byte[] ResponseBody { get; set; }
internal string ResponseBodyString { get; set; }
internal bool ResponseBodyRead { get; set; }
public List<HttpHeader> ResponseHeaders { get; internal set; }
internal bool ResponseLocked { get; set; }
internal HttpWebResponse ServerResponse { get; set; }
public int RequestContentLength public int RequestContentLength
{ {
get get
{ {
if (RequestHeaders.All(x => x.Name.ToLower() != "content-length")) return -1; return ProxySession.Request.ContentLength;
int contentLen;
int.TryParse(RequestHeaders.First(x => x.Name.ToLower() == "content-length").Value, out contentLen);
if (contentLen != 0)
return contentLen;
return -1;
} }
} }
public string RequestMethod public string RequestMethod
{ {
get { return ProxyRequest.Method; } get { return ProxySession.Request.Method; }
} }
public HttpStatusCode ResponseStatusCode public string ResponseStatusCode
{ {
get { return ServerResponse.StatusCode; } get { return ProxySession.Response.ResponseStatusCode; }
} }
public string ResponseContentType public string ResponseContentType
{ {
get get
{ {
return ResponseHeaders.Any(x => x.Name.ToLower() == "content-type") return ProxySession.Response.ContentType;
? ResponseHeaders.First(x => x.Name.ToLower() == "content-type").Value
: null;
} }
} }
public void Dispose() public void Dispose()
{ {
if (ProxyRequest != null)
ProxyRequest.Abort();
if (ResponseStream != null)
ResponseStream.Dispose();
if (ServerResponse != null)
ServerResponse.Close();
} }
private void ReadRequestBody() private void ReadRequestBody()
{ {
if ((ProxyRequest.Method.ToUpper() != "POST" && ProxyRequest.Method.ToUpper() != "PUT")) if ((ProxySession.Request.Method.ToUpper() != "POST" && ProxySession.Request.Method.ToUpper() != "PUT"))
{ {
throw new BodyNotFoundException("Request don't have a body." + throw new BodyNotFoundException("Request don't have a body." +
"Please verify that this request is a Http POST/PUT and request content length is greater than zero before accessing the body."); "Please verify that this request is a Http POST/PUT and request content length is greater than zero before accessing the body.");
} }
if (RequestBody == null) if (ProxySession.Request.RequestBody == null)
{ {
var isChunked = false; var isChunked = false;
string requestContentEncoding = null; string requestContentEncoding = null;
if (RequestHeaders.Any(x => x.Name.ToLower() == "content-encoding")) if (ProxySession.Request.RequestHeaders.Any(x => x.Name.ToLower() == "content-encoding"))
{ {
requestContentEncoding = RequestHeaders.First(x => x.Name.ToLower() == "content-encoding").Value; requestContentEncoding = ProxySession.Request.RequestHeaders.First(x => x.Name.ToLower() == "content-encoding").Value;
} }
if (RequestHeaders.Any(x => x.Name.ToLower() == "transfer-encoding")) if (ProxySession.Request.RequestHeaders.Any(x => x.Name.ToLower() == "transfer-encoding"))
{ {
var transferEncoding = var transferEncoding =
RequestHeaders.First(x => x.Name.ToLower() == "transfer-encoding").Value.ToLower(); ProxySession.Request.RequestHeaders.First(x => x.Name.ToLower() == "transfer-encoding").Value.ToLower();
if (transferEncoding.Contains("chunked")) if (transferEncoding.Contains("chunked"))
{ {
isChunked = true; isChunked = true;
...@@ -131,7 +105,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -131,7 +105,7 @@ namespace Titanium.Web.Proxy.EventArguments
if (requestContentEncoding == null && !isChunked) if (requestContentEncoding == null && !isChunked)
RequestBody = ClientStreamReader.ReadBytes(RequestContentLength); ProxySession.Request.RequestBody = this.Client.ClientStreamReader.ReadBytes(RequestContentLength);
else else
{ {
using (var requestBodyStream = new MemoryStream()) using (var requestBodyStream = new MemoryStream())
...@@ -140,219 +114,233 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -140,219 +114,233 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
while (true) while (true)
{ {
var chuchkHead = ClientStreamReader.ReadLine(); var chuchkHead = this.Client.ClientStreamReader.ReadLine();
var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber); var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber);
if (chunkSize != 0) if (chunkSize != 0)
{ {
var buffer = ClientStreamReader.ReadBytes(chunkSize); var buffer = this.Client.ClientStreamReader.ReadBytes(chunkSize);
requestBodyStream.Write(buffer, 0, buffer.Length); requestBodyStream.Write(buffer, 0, buffer.Length);
//chunk trail //chunk trail
ClientStreamReader.ReadLine(); this.Client.ClientStreamReader.ReadLine();
} }
else else
{ {
ClientStreamReader.ReadLine(); this.Client.ClientStreamReader.ReadLine();
break; break;
} }
} }
} }
try try
{ {
switch (requestContentEncoding) switch (requestContentEncoding)
{ {
case "gzip": case "gzip":
RequestBody = CompressionHelper.DecompressGzip(requestBodyStream); ProxySession.Request.RequestBody = CompressionHelper.DecompressGzip(requestBodyStream.ToArray());
break; break;
case "deflate": case "deflate":
RequestBody = CompressionHelper.DecompressDeflate(requestBodyStream); ProxySession.Request.RequestBody = CompressionHelper.DecompressDeflate(requestBodyStream);
break; break;
case "zlib": case "zlib":
RequestBody = CompressionHelper.DecompressZlib(requestBodyStream); ProxySession.Request.RequestBody = CompressionHelper.DecompressZlib(requestBodyStream);
break; break;
default: default:
RequestBody = requestBodyStream.ToArray(); ProxySession.Request.RequestBody = requestBodyStream.ToArray();
break; break;
} }
} }
catch catch
{ {
RequestBody = requestBodyStream.ToArray(); ProxySession.Request.RequestBody = requestBodyStream.ToArray();
} }
} }
} }
} }
RequestBodyRead = true; ProxySession.Request.RequestBodyRead = true;
} }
private void ReadResponseBody() private void ReadResponseBody()
{ {
if (ResponseBody == null) if (ProxySession.Response.ResponseBody == null)
{
using (var responseBodyStream = new MemoryStream())
{
if (ProxySession.Response.IsChunked)
{
while (true)
{
var chuchkHead = ProxySession.ProxyClient.ServerStreamReader.ReadLine();
var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber);
if (chunkSize != 0)
{
var buffer = ProxySession.ProxyClient.ServerStreamReader.ReadBytes(chunkSize);
responseBodyStream.Write(buffer, 0, buffer.Length);
//chunk trail
ProxySession.ProxyClient.ServerStreamReader.ReadLine();
}
else
{ {
switch (ServerResponse.ContentEncoding) ProxySession.ProxyClient.ServerStreamReader.ReadLine();
break;
}
}
}
else
{
var buffer = ProxySession.ProxyClient.ServerStreamReader.ReadBytes(ProxySession.Response.ContentLength);
responseBodyStream.Write(buffer, 0, buffer.Length);
}
switch (ProxySession.Response.ContentEncoding)
{ {
case "gzip": case "gzip":
ResponseBody = CompressionHelper.DecompressGzip(ResponseStream); ProxySession.Response.ResponseBody = CompressionHelper.DecompressGzip(responseBodyStream.ToArray());
break; break;
case "deflate": case "deflate":
ResponseBody = CompressionHelper.DecompressDeflate(ResponseStream); ProxySession.Response.ResponseBody = CompressionHelper.DecompressDeflate(responseBodyStream);
break; break;
case "zlib": case "zlib":
ResponseBody = CompressionHelper.DecompressZlib(ResponseStream); ProxySession.Response.ResponseBody = CompressionHelper.DecompressZlib(responseBodyStream);
break; break;
default: default:
ResponseBody = DecodeData(ResponseStream); ProxySession.Response.ResponseBody = responseBodyStream.ToArray();
break; break;
} }
ResponseBodyRead = true;
}
} }
ProxySession.Response.ResponseBodyRead = true;
//stream reader not recomended for images
private byte[] DecodeData(Stream responseStream)
{
var buffer = new byte[_bufferSize];
using (var ms = new MemoryStream())
{
int read;
while ((read = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
} }
} }
public Encoding GetRequestBodyEncoding() public Encoding GetRequestBodyEncoding()
{ {
if (RequestLocked) throw new Exception("You cannot call this function after request is made to server."); if (ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function after request is made to server.");
return RequestEncoding; return ProxySession.Request.Encoding;
} }
public byte[] GetRequestBody() public byte[] GetRequestBody()
{ {
if (RequestLocked) throw new Exception("You cannot call this function after request is made to server."); if (ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function after request is made to server.");
ReadRequestBody(); ReadRequestBody();
return RequestBody; return ProxySession.Request.RequestBody;
} }
public string GetRequestBodyAsString() public string GetRequestBodyAsString()
{ {
if (RequestLocked) throw new Exception("You cannot call this function after request is made to server."); if (ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function after request is made to server.");
ReadRequestBody(); ReadRequestBody();
return RequestBodyString ?? (RequestBodyString = RequestEncoding.GetString(RequestBody)); return ProxySession.Request.RequestBodyString ?? (ProxySession.Request.RequestBodyString = ProxySession.Request.Encoding.GetString(ProxySession.Request.RequestBody));
} }
public void SetRequestBody(byte[] body) public void SetRequestBody(byte[] body)
{ {
if (RequestLocked) throw new Exception("You cannot call this function after request is made to server."); if (ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function after request is made to server.");
if (!RequestBodyRead) if (!ProxySession.Request.RequestBodyRead)
{ {
ReadRequestBody(); ReadRequestBody();
} }
RequestBody = body; ProxySession.Request.RequestBody = body;
RequestBodyRead = true; ProxySession.Request.RequestBodyRead = true;
} }
public void SetRequestBodyString(string body) public void SetRequestBodyString(string body)
{ {
if (RequestLocked) throw new Exception("You cannot call this function after request is made to server."); if (ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function after request is made to server.");
if (!RequestBodyRead) if (!ProxySession.Request.RequestBodyRead)
{ {
ReadRequestBody(); ReadRequestBody();
} }
RequestBody = RequestEncoding.GetBytes(body); ProxySession.Request.RequestBody = ProxySession.Request.Encoding.GetBytes(body);
RequestBodyRead = true; ProxySession.Request.RequestBodyRead = true;
} }
public Encoding GetResponseBodyEncoding() public Encoding GetResponseBodyEncoding()
{ {
if (!RequestLocked) throw new Exception("You cannot call this function before request is made to server."); if (!ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function before request is made to server.");
return ResponseEncoding; return ProxySession.Response.Encoding;
} }
public byte[] GetResponseBody() public byte[] GetResponseBody()
{ {
if (!RequestLocked) throw new Exception("You cannot call this function before request is made to server."); if (!ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function before request is made to server.");
ReadResponseBody(); ReadResponseBody();
return ResponseBody; return ProxySession.Response.ResponseBody;
} }
public string GetResponseBodyAsString() public string GetResponseBodyAsString()
{ {
if (!RequestLocked) throw new Exception("You cannot call this function before request is made to server."); if (!ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function before request is made to server.");
GetResponseBody(); GetResponseBody();
return ResponseBodyString ?? (ResponseBodyString = ResponseEncoding.GetString(ResponseBody)); return ProxySession.Response.ResponseBodyString ?? (ProxySession.Response.ResponseBodyString = ProxySession.Response.Encoding.GetString(ProxySession.Response.ResponseBody));
} }
public void SetResponseBody(byte[] body) public void SetResponseBody(byte[] body)
{ {
if (!RequestLocked) throw new Exception("You cannot call this function before request is made to server."); if (!ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function before request is made to server.");
if (ResponseBody == null) if (ProxySession.Response.ResponseBody == null)
{ {
GetResponseBody(); GetResponseBody();
} }
ResponseBody = body; ProxySession.Response.ResponseBody = body;
} }
public void SetResponseBodyString(string body) public void SetResponseBodyString(string body)
{ {
if (!RequestLocked) throw new Exception("You cannot call this function before request is made to server."); if (!ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function before request is made to server.");
if (ResponseBody == null) if (ProxySession.Response.ResponseBody == null)
{ {
GetResponseBody(); GetResponseBody();
} }
var bodyBytes = ResponseEncoding.GetBytes(body); var bodyBytes = ProxySession.Response.Encoding.GetBytes(body);
SetResponseBody(bodyBytes); SetResponseBody(bodyBytes);
} }
public void Ok(string html) public void Ok(string html)
{ {
if (RequestLocked) throw new Exception("You cannot call this function after request is made to server."); if (ProxySession.Request.RequestLocked) throw new Exception("You cannot call this function after request is made to server.");
if (html == null) if (html == null)
html = string.Empty; html = string.Empty;
var result = Encoding.Default.GetBytes(html); var result = Encoding.Default.GetBytes(html);
var connectStreamWriter = new StreamWriter(ClientStream); var connectStreamWriter = new StreamWriter(this.Client.ClientStream);
var s = string.Format("HTTP/{0}.{1} {2} {3}", RequestHttpVersion.Major, RequestHttpVersion.Minor, 200, "Ok"); connectStreamWriter.WriteLine(string.Format("{0} {2} {3}", ProxySession.Request.HttpVersion, 200, "Ok"));
connectStreamWriter.WriteLine(s);
connectStreamWriter.WriteLine("Timestamp: {0}", DateTime.Now); connectStreamWriter.WriteLine("Timestamp: {0}", DateTime.Now);
connectStreamWriter.WriteLine("content-length: " + result.Length); connectStreamWriter.WriteLine("content-length: " + result.Length);
connectStreamWriter.WriteLine("Cache-Control: no-cache, no-store, must-revalidate"); connectStreamWriter.WriteLine("Cache-Control: no-cache, no-store, must-revalidate");
connectStreamWriter.WriteLine("Pragma: no-cache"); connectStreamWriter.WriteLine("Pragma: no-cache");
connectStreamWriter.WriteLine("Expires: 0"); connectStreamWriter.WriteLine("Expires: 0");
connectStreamWriter.WriteLine(RequestIsAlive ? "Connection: Keep-Alive" : "Connection: close"); connectStreamWriter.WriteLine(ProxySession.Request.IsAlive ? "Connection: Keep-Alive" : "Connection: close");
connectStreamWriter.WriteLine(); connectStreamWriter.WriteLine();
connectStreamWriter.Flush(); connectStreamWriter.Flush();
ClientStream.Write(result, 0, result.Length); this.Client.ClientStream.Write(result, 0, result.Length);
CancelRequest = true; ProxySession.Request.CancelRequest = true;
} }
} }
} }
\ No newline at end of file
using System.Net; using System.Net;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
public static class HttpWebRequestExtensions public static class HttpWebRequestExtensions
{ {
public static Encoding GetEncoding(this HttpWebRequest request) public static Encoding GetEncoding(this HttpWebSession request)
{ {
try try
{ {
if (request.ContentType == null) return Encoding.GetEncoding("ISO-8859-1"); if (request.Request.ContentType == null) return Encoding.GetEncoding("ISO-8859-1");
var contentTypes = request.ContentType.Split(';'); var contentTypes = request.Request.ContentType.Split(';');
foreach (var contentType in contentTypes) foreach (var contentType in contentTypes)
{ {
var encodingSplit = contentType.Split('='); var encodingSplit = contentType.Split('=');
......
using System.Net; using System.Net;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
public static class HttpWebResponseExtensions public static class HttpWebResponseExtensions
{ {
public static Encoding GetEncoding(this HttpWebResponse response) public static Encoding GetResponseEncoding(this HttpWebSession response)
{ {
if (string.IsNullOrEmpty(response.CharacterSet)) return Encoding.GetEncoding("ISO-8859-1"); if (string.IsNullOrEmpty(response.Response.CharacterSet)) return Encoding.GetEncoding("ISO-8859-1");
return Encoding.GetEncoding(response.CharacterSet.Replace(@"""",string.Empty)); return Encoding.GetEncoding(response.Response.CharacterSet.Replace(@"""", string.Empty));
} }
} }
} }
\ No newline at end of file
...@@ -50,11 +50,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -50,11 +50,10 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
public static byte[] DecompressGzip(Stream input) //identify why passing stream instead of bytes returns empty result
public static byte[] DecompressGzip(byte[] gzip)
{ {
using ( using (var decompressor = new System.IO.Compression.GZipStream(new MemoryStream(gzip), System.IO.Compression.CompressionMode.Decompress))
var decompressor = new System.IO.Compression.GZipStream(input,
System.IO.Compression.CompressionMode.Decompress))
{ {
var buffer = new byte[BufferSize]; var buffer = new byte[BufferSize];
......
...@@ -5,7 +5,7 @@ using System.Text; ...@@ -5,7 +5,7 @@ using System.Text;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
internal class CustomBinaryReader : BinaryReader public class CustomBinaryReader : BinaryReader
{ {
internal CustomBinaryReader(Stream stream, Encoding encoding) internal CustomBinaryReader(Stream stream, Encoding encoding)
: base(stream, encoding) : base(stream, encoding)
......
using System;
using System.Net.Configuration;
using System.Reflection;
namespace Titanium.Web.Proxy.Helpers
{
public class NetFrameworkHelper
{
//Fix bug in .Net 4.0 HttpWebRequest (don't use this for 4.5 and above)
//http://stackoverflow.com/questions/856885/httpwebrequest-to-url-with-dot-at-the-end
public static void UrlPeriodFix()
{
var getSyntax = typeof (UriParser).GetMethod("GetSyntax", BindingFlags.Static | BindingFlags.NonPublic);
var flagsField = typeof (UriParser).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
if (getSyntax != null && flagsField != null)
{
foreach (var scheme in new[] {"http", "https"})
{
var parser = (UriParser) getSyntax.Invoke(null, new object[] {scheme});
if (parser != null)
{
var flagsValue = (int) flagsField.GetValue(parser);
if ((flagsValue & 0x1000000) != 0)
flagsField.SetValue(parser, flagsValue & ~0x1000000);
}
}
}
}
// Enable/disable useUnsafeHeaderParsing.
// See http://o2platform.wordpress.com/2010/10/20/dealing-with-the-server-committed-a-protocol-violation-sectionresponsestatusline/
public static bool ToggleAllowUnsafeHeaderParsing(bool enable)
{
//Get the assembly that contains the internal class
var assembly = Assembly.GetAssembly(typeof (SettingsSection));
if (assembly != null)
{
//Use the assembly in order to get the internal type for the internal class
var settingsSectionType = assembly.GetType("System.Net.Configuration.SettingsSectionInternal");
if (settingsSectionType != null)
{
//Use the internal static property to get an instance of the internal settings class.
//If the static instance isn't created already invoking the property will create it for us.
var anInstance = settingsSectionType.InvokeMember("Section",
BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.NonPublic, null, null,
new object[] {});
if (anInstance != null)
{
//Locate the private bool field that tells the framework if unsafe header parsing is allowed
var aUseUnsafeHeaderParsing = settingsSectionType.GetField("useUnsafeHeaderParsing",
BindingFlags.NonPublic | BindingFlags.Instance);
if (aUseUnsafeHeaderParsing != null)
{
aUseUnsafeHeaderParsing.SetValue(anInstance, enable);
return true;
}
}
}
}
return false;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Network
{
public class Request
{
public string Method { get; internal set; }
public Uri RequestUri { get; internal set; }
public string HttpVersion { get; internal set; }
public string Status { get; internal set; }
public int ContentLength { get; internal set; }
public bool SendChunked { get; internal set; }
public string ContentType { get; internal set; }
public bool KeepAlive { get; internal set; }
public string Hostname { get; internal set; }
public string Url { get; internal set; }
internal Encoding Encoding { get; set; }
internal bool IsAlive { get; set; }
internal bool CancelRequest { get; set; }
internal byte[] RequestBody { get; set; }
internal string RequestBodyString { get; set; }
internal bool RequestBodyRead { get; set; }
internal bool UpgradeToWebSocket { get; set; }
public List<HttpHeader> RequestHeaders { get; internal set; }
internal bool RequestLocked { get; set; }
public Request()
{
this.RequestHeaders = new List<HttpHeader>();
}
}
public class Response
{
internal Encoding Encoding { get; set; }
internal Stream ResponseStream { get; set; }
internal byte[] ResponseBody { get; set; }
internal string ResponseBodyString { get; set; }
internal bool ResponseBodyRead { get; set; }
internal bool ResponseLocked { get; set; }
public List<HttpHeader> ResponseHeaders { get; internal set; }
internal string CharacterSet { get; set; }
internal string ContentEncoding { get; set; }
internal string HttpVersion { get; set; }
public string ResponseStatusCode { get; internal set; }
public string ResponseStatusDescription { get; internal set; }
internal bool ResponseKeepAlive { get; set; }
public string ContentType { get; internal set; }
internal int ContentLength { get; set; }
internal bool IsChunked { get; set; }
public Response()
{
this.ResponseHeaders = new List<HttpHeader>();
this.ResponseKeepAlive = true;
}
}
public class HttpWebSession
{
private const string Space = " ";
public bool IsSecure
{
get
{
return this.Request.RequestUri.Scheme == Uri.UriSchemeHttps;
}
}
public Request Request { get; set; }
public Response Response { get; set; }
internal TcpConnection ProxyClient { get; set; }
public void SetConnection(TcpConnection Connection)
{
Connection.LastAccess = DateTime.Now;
ProxyClient = Connection;
}
public HttpWebSession()
{
this.Request = new Request();
this.Response = new Response();
}
public void SendRequest()
{
Stream stream = ProxyClient.Stream;
StringBuilder requestLines = new StringBuilder();
requestLines.AppendLine(string.Join(" ", new string[3]
{
this.Request.Method,
this.Request.RequestUri.PathAndQuery,
this.Request.HttpVersion
}));
foreach (HttpHeader httpHeader in this.Request.RequestHeaders)
{
requestLines.AppendLine(httpHeader.Name + ':' + httpHeader.Value);
}
requestLines.AppendLine();
string request = requestLines.ToString();
byte[] requestBytes = Encoding.ASCII.GetBytes(request);
stream.Write(requestBytes, 0, requestBytes.Length);
stream.Flush();
}
public void ReceiveResponse()
{
var httpResult = ProxyClient.ServerStreamReader.ReadLine().Split(new char[] { ' ' }, 3);
if(string.IsNullOrEmpty(httpResult[0]))
{
var s = ProxyClient.ServerStreamReader.ReadLine();
}
this.Response.HttpVersion = httpResult[0];
this.Response.ResponseStatusCode = httpResult[1];
string status = httpResult[2];
this.Response.ResponseStatusDescription = status;
List<string> responseLines = ProxyClient.ServerStreamReader.ReadAllLines();
for (int index = 0; index < responseLines.Count; ++index)
{
string[] strArray = responseLines[index].Split(new char[] { ':' }, 2);
this.Response.ResponseHeaders.Add(new HttpHeader(strArray[0], strArray[1]));
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.IO;
using System.Net.Security;
using Titanium.Web.Proxy.Helpers;
using System.Threading;
namespace Titanium.Web.Proxy.Network
{
public class TcpConnection
{
public string HostName { get; set; }
public int port { get; set; }
public bool IsSecure { get; set; }
public TcpClient TcpClient { get; set; }
public CustomBinaryReader ServerStreamReader { get; set; }
public Stream Stream { get; set; }
public DateTime LastAccess { get; set; }
public TcpConnection()
{
LastAccess = DateTime.Now;
}
}
internal class TcpConnectionManager
{
static List<TcpConnection> ConnectionCache = new List<TcpConnection>();
public static TcpConnection GetClient(string Hostname, int port, bool IsSecure)
{
TcpConnection cached = null;
while (true)
{
lock (ConnectionCache)
{
cached = ConnectionCache.FirstOrDefault(x => x.HostName == Hostname && x.port == port && x.IsSecure == IsSecure && x.TcpClient.Connected);
if (cached != null)
ConnectionCache.Remove(cached);
}
if (cached != null && !cached.TcpClient.Client.IsConnected())
continue;
if (cached == null)
break;
}
if (cached == null)
cached = CreateClient(Hostname, port, IsSecure);
if (ConnectionCache.Where(x => x.HostName == Hostname && x.port == port && x.IsSecure == IsSecure && x.TcpClient.Connected).Count() < 2)
{
Task.Factory.StartNew(() => ReleaseClient(CreateClient(Hostname, port, IsSecure)));
}
return cached;
}
private static TcpConnection CreateClient(string Hostname, int port, bool IsSecure)
{
var client = new TcpClient(Hostname, port);
var stream = (Stream)client.GetStream();
if (IsSecure)
{
var sslStream = (SslStream)null;
try
{
sslStream = new SslStream(stream);
sslStream.AuthenticateAsClient(Hostname);
stream = (Stream)sslStream;
}
catch
{
if (sslStream != null)
sslStream.Dispose();
throw;
}
}
return new TcpConnection()
{
HostName = Hostname,
port = port,
IsSecure = IsSecure,
TcpClient = client,
ServerStreamReader = new CustomBinaryReader(stream, Encoding.ASCII),
Stream = stream
};
}
public static void ReleaseClient(TcpConnection Connection)
{
Connection.LastAccess = DateTime.Now;
ConnectionCache.Add(Connection);
}
public static void ClearIdleConnections()
{
while (true)
{
lock (ConnectionCache)
{
var cutOff = DateTime.Now.AddSeconds(-60);
ConnectionCache
.Where(x => x.LastAccess < cutOff)
.ToList()
.ForEach(x => x.TcpClient.Close());
ConnectionCache.RemoveAll(x => x.LastAccess < cutOff);
}
Thread.Sleep(1000 * 60 * 3);
}
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Network
{
internal static class TcpExtensions
{
public static bool IsConnected(this Socket client)
{
// This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking;
try
{
byte[] tmp = new byte[1];
client.Blocking = false;
client.Send(tmp, 0, 0);
return true;
}
catch (SocketException e)
{
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035))
return true;
else
{
return false;
}
}
finally
{
client.Blocking = blockingState;
}
}
}
}
...@@ -9,6 +9,7 @@ using System.Text.RegularExpressions; ...@@ -9,6 +9,7 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -25,7 +26,7 @@ namespace Titanium.Web.Proxy ...@@ -25,7 +26,7 @@ namespace Titanium.Web.Proxy
private static readonly Regex CookieSplitRegEx = new Regex(@",(?! )"); private static readonly Regex CookieSplitRegEx = new Regex(@",(?! )");
private static readonly byte[] ChunkTrail = Encoding.ASCII.GetBytes(Environment.NewLine); private static readonly byte[] NewLineBytes = Encoding.ASCII.GetBytes(Environment.NewLine);
private static readonly byte[] ChunkEnd = private static readonly byte[] ChunkEnd =
Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine); Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine);
...@@ -59,26 +60,7 @@ namespace Titanium.Web.Proxy ...@@ -59,26 +60,7 @@ namespace Titanium.Web.Proxy
public static void Initialize() public static void Initialize()
{ {
ServicePointManager.Expect100Continue = false; Task.Factory.StartNew(()=>TcpConnectionManager.ClearIdleConnections());
WebRequest.DefaultWebProxy = null;
ServicePointManager.DefaultConnectionLimit = int.MaxValue;
ServicePointManager.DnsRefreshTimeout = 3 * 60 * 1000; //3 minutes
ServicePointManager.MaxServicePointIdleTime = 3 * 60 * 1000;
//HttpWebRequest certificate validation callback
ServicePointManager.ServerCertificateValidationCallback =
delegate(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None) return true;
return false;
};
#if NET40
//Fix a bug in .NET 4.0
NetFrameworkHelper.UrlPeriodFix();
//useUnsafeHeaderParsing
#endif
NetFrameworkHelper.ToggleAllowUnsafeHeaderParsing(true);
} }
...@@ -100,7 +82,10 @@ namespace Titanium.Web.Proxy ...@@ -100,7 +82,10 @@ namespace Titanium.Web.Proxy
{ {
SystemProxyHelper.EnableProxyHttp( SystemProxyHelper.EnableProxyHttp(
Equals(ListeningIpAddress, IPAddress.Any) ? "127.0.0.1" : ListeningIpAddress.ToString(), ListeningPort); Equals(ListeningIpAddress, IPAddress.Any) ? "127.0.0.1" : ListeningIpAddress.ToString(), ListeningPort);
#if !DEBUG
FireFoxHelper.AddFirefox(); FireFoxHelper.AddFirefox();
#endif
if (EnableSsl) if (EnableSsl)
...@@ -142,7 +127,9 @@ namespace Titanium.Web.Proxy ...@@ -142,7 +127,9 @@ namespace Titanium.Web.Proxy
if (SetAsSystemProxy) if (SetAsSystemProxy)
{ {
SystemProxyHelper.DisableAllProxy(); SystemProxyHelper.DisableAllProxy();
#if !DEBUG
FireFoxHelper.RemoveFirefox(); FireFoxHelper.RemoveFirefox();
#endif
} }
_listener.Stop(); _listener.Stop();
......
...@@ -13,6 +13,7 @@ using System.Threading.Tasks; ...@@ -13,6 +13,7 @@ using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
...@@ -29,6 +30,7 @@ namespace Titanium.Web.Proxy ...@@ -29,6 +30,7 @@ namespace Titanium.Web.Proxy
try try
{ {
//read the first line HTTP command //read the first line HTTP command
var httpCmd = clientStreamReader.ReadLine(); var httpCmd = clientStreamReader.ReadLine();
if (string.IsNullOrEmpty(httpCmd)) if (string.IsNullOrEmpty(httpCmd))
...@@ -113,16 +115,19 @@ namespace Titanium.Web.Proxy ...@@ -113,16 +115,19 @@ namespace Titanium.Web.Proxy
private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string secureTunnelHostName) CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string secureTunnelHostName)
{ {
TcpConnection connection = null;
string lastRequestHostName = null;
while (true) while (true)
{ {
if (string.IsNullOrEmpty(httpCmd)) if (string.IsNullOrEmpty(httpCmd))
{ {
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
return; break;
} }
var args = new SessionEventArgs(BUFFER_SIZE); var args = new SessionEventArgs(BUFFER_SIZE);
args.Client = client; args.Client.TcpClient = client;
try try
{ {
...@@ -149,74 +154,72 @@ namespace Titanium.Web.Proxy ...@@ -149,74 +154,72 @@ namespace Titanium.Web.Proxy
args.IsHttps = true; args.IsHttps = true;
} }
args.RequestHeaders = new List<HttpHeader>();
string tmpLine; args.ProxySession.Request.RequestHeaders = new List<HttpHeader>();
string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine())) while (!string.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
{ {
var header = tmpLine.Split(ColonSpaceSplit, 2, StringSplitOptions.None); var header = tmpLine.Split(new char[] { ':' }, 2);
args.RequestHeaders.Add(new HttpHeader(header[0], header[1])); args.ProxySession.Request.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
} }
for (var i = 0; i < args.RequestHeaders.Count; i++) SetRequestHeaders(args.ProxySession.Request.RequestHeaders, args.ProxySession);
{
var rawHeader = args.RequestHeaders[i];
if (args.ProxySession.Request.UpgradeToWebSocket)
//if request was upgrade to web-socket protocol then relay the request without proxying
if ((rawHeader.Name.ToLower() == "upgrade") && (rawHeader.Value.ToLower() == "websocket"))
{ {
TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.RequestHeaders, TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.ProxySession.Request.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, httpRemoteUri.Scheme == Uri.UriSchemeHttps); httpRemoteUri.Host, httpRemoteUri.Port, httpRemoteUri.Scheme == Uri.UriSchemeHttps);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; return;
} }
}
//construct the web request that we are going to issue on behalf of the client. args.ProxySession.Request.RequestUri = httpRemoteUri;
args.ProxyRequest = (HttpWebRequest)WebRequest.Create(httpRemoteUri);
args.ProxyRequest.Proxy = null; args.ProxySession.Request.Method = httpMethod;
args.ProxyRequest.UseDefaultCredentials = true; args.ProxySession.Request.HttpVersion = httpVersion;
args.ProxyRequest.Method = httpMethod; args.Client.ClientStream = clientStream;
args.ProxyRequest.ProtocolVersion = version; args.Client.ClientStreamReader = clientStreamReader;
args.ClientStream = clientStream; args.Client.ClientStreamWriter = clientStreamWriter;
args.ClientStreamReader = clientStreamReader; args.ProxySession.Request.Hostname = args.ProxySession.Request.RequestUri.Host;
args.ClientStreamWriter = clientStreamWriter; args.ProxySession.Request.Url = args.ProxySession.Request.RequestUri.OriginalString;
args.ProxyRequest.AllowAutoRedirect = false; args.Client.ClientPort = ((IPEndPoint)client.Client.RemoteEndPoint).Port;
args.ProxyRequest.AutomaticDecompression = DecompressionMethods.None; args.Client.ClientIpAddress = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
args.RequestHostname = args.ProxyRequest.RequestUri.Host;
args.RequestUrl = args.ProxyRequest.RequestUri.OriginalString;
args.ClientPort = ((IPEndPoint)client.Client.RemoteEndPoint).Port;
args.ClientIpAddress = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
args.RequestHttpVersion = version;
args.RequestIsAlive = args.ProxyRequest.KeepAlive;
args.ProxyRequest.AllowWriteStreamBuffering = true;
//If requested interception //If requested interception
if (BeforeRequest != null) if (BeforeRequest != null)
{ {
args.RequestEncoding = args.ProxyRequest.GetEncoding(); args.ProxySession.Request.Encoding = args.ProxySession.GetEncoding();
BeforeRequest(null, args); BeforeRequest(null, args);
} }
args.RequestLocked = true; args.ProxySession.Request.RequestLocked = true;
if (args.CancelRequest) if (args.ProxySession.Request.CancelRequest)
{ {
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; break;
} }
SetRequestHeaders(args.RequestHeaders, args.ProxyRequest);
//construct the web request that we are going to issue on behalf of the client.
connection = connection == null ?
TcpConnectionManager.GetClient(args.ProxySession.Request.RequestUri.Host, args.ProxySession.Request.RequestUri.Port, args.IsHttps)
: lastRequestHostName != args.ProxySession.Request.Hostname ? TcpConnectionManager.GetClient(args.ProxySession.Request.RequestUri.Host, args.ProxySession.Request.RequestUri.Port, args.IsHttps)
: connection;
lastRequestHostName = args.ProxySession.Request.Hostname;
args.ProxySession.SetConnection(connection);
args.ProxySession.SendRequest();
//If request was modified by user //If request was modified by user
if (args.RequestBodyRead) if (args.ProxySession.Request.RequestBodyRead)
{ {
args.ProxyRequest.ContentLength = args.RequestBody.Length; args.ProxySession.Request.ContentLength = args.ProxySession.Request.RequestBody.Length;
var newStream = args.ProxyRequest.GetRequestStream(); var newStream = args.ProxySession.ProxyClient.ServerStreamReader.BaseStream;
newStream.Write(args.RequestBody, 0, args.RequestBody.Length); newStream.Write(args.ProxySession.Request.RequestBody, 0, args.ProxySession.Request.RequestBody.Length);
} }
else else
{ {
...@@ -230,8 +233,9 @@ namespace Titanium.Web.Proxy ...@@ -230,8 +233,9 @@ namespace Titanium.Web.Proxy
HandleHttpSessionResponse(args); HandleHttpSessionResponse(args);
//if connection is closing exit //if connection is closing exit
if (args.ResponseHeaders.Any(x => x.Name.ToLower() == "connection" && x.Value.ToLower() == "close")) if (args.ProxySession.Response.ResponseKeepAlive == false)
{ {
connection.TcpClient.Close();
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; return;
} }
...@@ -243,188 +247,162 @@ namespace Titanium.Web.Proxy ...@@ -243,188 +247,162 @@ namespace Titanium.Web.Proxy
catch catch
{ {
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; break;
} }
} }
if (connection != null)
TcpConnectionManager.ReleaseClient(connection);
} }
private static void WriteConnectResponse(StreamWriter clientStreamWriter, string httpVersion) private static void WriteConnectResponse(StreamWriter clientStreamWriter, string httpVersion)
{ {
clientStreamWriter.WriteLine(httpVersion + " 200 Connection established"); clientStreamWriter.WriteLine(httpVersion + " 200 Connection established");
clientStreamWriter.WriteLine("Timestamp: {0}", DateTime.Now); clientStreamWriter.WriteLine("Timestamp: {0}", DateTime.Now);
//clientStreamWriter.WriteLine("connection:close");
clientStreamWriter.WriteLine(); clientStreamWriter.WriteLine();
clientStreamWriter.Flush(); clientStreamWriter.Flush();
} }
private static void SetRequestHeaders(List<HttpHeader> requestHeaders, HttpWebRequest webRequest) private static void SetRequestHeaders(List<HttpHeader> requestHeaders, HttpWebSession webRequest)
{ {
for (var i = 0; i < requestHeaders.Count; i++) for (var i = 0; i < requestHeaders.Count; i++)
{ {
switch (requestHeaders[i].Name.ToLower()) switch (requestHeaders[i].Name.ToLower())
{ {
case "accept":
webRequest.Accept = requestHeaders[i].Value;
break;
case "accept-encoding": case "accept-encoding":
webRequest.Headers.Add("Accept-Encoding", "gzip,deflate,zlib"); requestHeaders[i].Value = "gzip,deflate,zlib";
break;
case "cookie":
webRequest.Headers["Cookie"] = requestHeaders[i].Value;
break; break;
case "connection": case "connection":
if (requestHeaders[i].Value.ToLower() == "keep-alive") if (requestHeaders[i].Value.ToLower() == "keep-alive")
webRequest.KeepAlive = true; webRequest.Request.KeepAlive = true;
break; break;
case "content-length": case "content-length":
int contentLen; int contentLen;
int.TryParse(requestHeaders[i].Value, out contentLen); int.TryParse(requestHeaders[i].Value, out contentLen);
if (contentLen != 0) if (contentLen != 0)
webRequest.ContentLength = contentLen; webRequest.Request.ContentLength = contentLen;
break; break;
case "content-type": case "content-type":
webRequest.ContentType = requestHeaders[i].Value; webRequest.Request.ContentType = requestHeaders[i].Value;
break;
case "expect":
if (requestHeaders[i].Value.ToLower() == "100-continue")
webRequest.ServicePoint.Expect100Continue = true;
else
webRequest.Expect = requestHeaders[i].Value;
break; break;
case "host": case "host":
webRequest.Host = requestHeaders[i].Value; webRequest.Request.Hostname = requestHeaders[i].Value;
break;
case "if-modified-since":
var sb = requestHeaders[i].Value.Trim().Split(SemiSplit);
DateTime d;
if (DateTime.TryParse(sb[0], out d))
webRequest.IfModifiedSince = d;
break; break;
case "proxy-connection": case "proxy-connection":
if (requestHeaders[i].Value.ToLower() == "keep-alive") if (requestHeaders[i].Value.ToLower() == "keep-alive")
webRequest.KeepAlive = true; webRequest.Request.KeepAlive = true;
else if (requestHeaders[i].Value.ToLower() == "close") else if (requestHeaders[i].Value.ToLower() == "close")
webRequest.KeepAlive = false; webRequest.Request.KeepAlive = false;
break;
case "range":
var startEnd = requestHeaders[i].Value.Replace(Environment.NewLine, "").Remove(0, 6).Split('-');
if (startEnd.Length > 1)
{
if (!string.IsNullOrEmpty(startEnd[1]))
webRequest.AddRange(int.Parse(startEnd[0]), int.Parse(startEnd[1]));
else webRequest.AddRange(int.Parse(startEnd[0]));
}
else
webRequest.AddRange(int.Parse(startEnd[0]));
break; break;
case "referer":
webRequest.Referer = requestHeaders[i].Value; case "upgrade":
break; if (requestHeaders[i].Value.ToLower() == "websocket")
case "user-agent": webRequest.Request.UpgradeToWebSocket = true;
webRequest.UserAgent = requestHeaders[i].Value;
break; break;
//revisit this, transfer-encoding is not a request header according to spec //revisit this, transfer-encoding is not a request header according to spec
//But how to identify if client is sending chunked body for PUT/POST? //But how to identify if client is sending chunked body for PUT/POST?
case "transfer-encoding": case "transfer-encoding":
if (requestHeaders[i].Value.ToLower().Contains("chunked")) if (requestHeaders[i].Value.ToLower().Contains("chunked"))
webRequest.SendChunked = true; webRequest.Request.SendChunked = true;
else else
webRequest.SendChunked = false; webRequest.Request.SendChunked = false;
break;
case "upgrade":
if (requestHeaders[i].Value.ToLower() == "http/1.1")
webRequest.Headers.Add("Upgrade", requestHeaders[i].Value);
break; break;
default: default:
webRequest.Headers.Add(requestHeaders[i].Name, requestHeaders[i].Value);
break; break;
} }
} }
FixRequestProxyHeaders(requestHeaders);
webRequest.Request.RequestHeaders = requestHeaders;
}
private static void FixRequestProxyHeaders(List<HttpHeader> headers)
{
//If proxy-connection close was returned inform to close the connection
var proxyHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "proxy-connection");
var connectionheader = headers.FirstOrDefault(x => x.Name.ToLower() == "connection");
if (proxyHeader != null)
if (connectionheader == null)
{
headers.Add(new HttpHeader("connection", proxyHeader.Value));
}
else
{
connectionheader.Value = proxyHeader.Value;
} }
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
}
//This is called when the request is PUT/POST to read the body //This is called when the request is PUT/POST to read the body
private static void SendClientRequestBody(SessionEventArgs args) private static void SendClientRequestBody(SessionEventArgs args)
{ {
// End the operation // End the operation
var postStream = args.ProxyRequest.GetRequestStream(); var postStream = args.ProxySession.ProxyClient.Stream;
if (args.ProxyRequest.ContentLength > 0) if (args.ProxySession.Request.ContentLength > 0)
{ {
args.ProxyRequest.AllowWriteStreamBuffering = true; //args.ProxyRequest.AllowWriteStreamBuffering = true;
try try
{ {
var totalbytesRead = 0; var totalbytesRead = 0;
int bytesToRead; int bytesToRead;
if (args.ProxyRequest.ContentLength < BUFFER_SIZE) if (args.ProxySession.Request.ContentLength < BUFFER_SIZE)
{ {
bytesToRead = (int)args.ProxyRequest.ContentLength; bytesToRead = (int)args.ProxySession.Request.ContentLength;
} }
else else
bytesToRead = BUFFER_SIZE; bytesToRead = BUFFER_SIZE;
while (totalbytesRead < (int)args.ProxyRequest.ContentLength) while (totalbytesRead < (int)args.ProxySession.Request.ContentLength)
{ {
var buffer = args.ClientStreamReader.ReadBytes(bytesToRead); var buffer = args.Client.ClientStreamReader.ReadBytes(bytesToRead);
totalbytesRead += buffer.Length; totalbytesRead += buffer.Length;
var remainingBytes = (int)args.ProxyRequest.ContentLength - totalbytesRead; var remainingBytes = (int)args.ProxySession.Request.ContentLength - totalbytesRead;
if (remainingBytes < bytesToRead) if (remainingBytes < bytesToRead)
{ {
bytesToRead = remainingBytes; bytesToRead = remainingBytes;
} }
postStream.Write(buffer, 0, buffer.Length); postStream.Write(buffer, 0, buffer.Length);
} }
postStream.Close();
} }
catch catch
{ {
postStream.Close();
postStream.Dispose();
throw; throw;
} }
} }
//Need to revist, find any potential bugs //Need to revist, find any potential bugs
else if (args.ProxyRequest.SendChunked) else if (args.ProxySession.Request.SendChunked)
{ {
args.ProxyRequest.AllowWriteStreamBuffering = true;
try try
{ {
while (true) while (true)
{ {
var chuchkHead = args.ClientStreamReader.ReadLine(); var chuchkHead = args.Client.ClientStreamReader.ReadLine();
var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber); var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber);
if (chunkSize != 0) if (chunkSize != 0)
{ {
var buffer = args.ClientStreamReader.ReadBytes(chunkSize); var buffer = args.Client.ClientStreamReader.ReadBytes(chunkSize);
postStream.Write(buffer, 0, buffer.Length); postStream.Write(buffer, 0, buffer.Length);
//chunk trail //chunk trail
args.ClientStreamReader.ReadLine(); args.Client.ClientStreamReader.ReadLine();
} }
else else
{ {
args.ClientStreamReader.ReadLine(); args.Client.ClientStreamReader.ReadLine();
break; break;
} }
} }
postStream.Close();
} }
catch catch
{ {
postStream.Close();
postStream.Dispose();
throw; throw;
} }
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
...@@ -17,73 +20,64 @@ namespace Titanium.Web.Proxy ...@@ -17,73 +20,64 @@ namespace Titanium.Web.Proxy
//Called asynchronously when a request was successfully and we received the response //Called asynchronously when a request was successfully and we received the response
private static void HandleHttpSessionResponse(SessionEventArgs args) private static void HandleHttpSessionResponse(SessionEventArgs args)
{ {
args.ProxySession.ReceiveResponse();
try try
{ {
args.ServerResponse = (HttpWebResponse)args.ProxyRequest.GetResponse();
}
catch (WebException webEx)
{
//Things line 404, 500 etc
args.ServerResponse = webEx.Response as HttpWebResponse;
}
try args.ProxySession.Response.ResponseHeaders = ReadResponseHeaders(args.ProxySession);
{ args.ProxySession.Response.ResponseStream = args.ProxySession.ProxyClient.ServerStreamReader.BaseStream;
if (args.ServerResponse != null)
{
args.ResponseHeaders = ReadResponseHeaders(args.ServerResponse);
args.ResponseStream = args.ServerResponse.GetResponseStream();
if (BeforeResponse != null) if (BeforeResponse != null)
{ {
args.ResponseEncoding = args.ServerResponse.GetEncoding(); args.ProxySession.Response.Encoding = args.ProxySession.GetResponseEncoding();
BeforeResponse(null, args); BeforeResponse(null, args);
} }
args.ResponseLocked = true; args.ProxySession.Response.ResponseLocked = true;
if (args.ResponseBodyRead) if (args.ProxySession.Response.ResponseBodyRead)
{ {
var isChunked = args.ServerResponse.GetResponseHeader("transfer-encoding").ToLower().Contains("chunked"); var isChunked = args.ProxySession.Response.IsChunked;
var contentEncoding = args.ServerResponse.ContentEncoding; var contentEncoding = args.ProxySession.Response.ContentEncoding;
switch (contentEncoding.ToLower()) if(contentEncoding!=null)
switch (contentEncoding)
{ {
case "gzip": case "gzip":
args.ResponseBody = CompressionHelper.CompressGzip(args.ResponseBody); args.ProxySession.Response.ResponseBody = CompressionHelper.CompressGzip(args.ProxySession.Response.ResponseBody);
break; break;
case "deflate": case "deflate":
args.ResponseBody = CompressionHelper.CompressDeflate(args.ResponseBody); args.ProxySession.Response.ResponseBody = CompressionHelper.CompressDeflate(args.ProxySession.Response.ResponseBody);
break; break;
case "zlib": case "zlib":
args.ResponseBody = CompressionHelper.CompressZlib(args.ResponseBody); args.ProxySession.Response.ResponseBody = CompressionHelper.CompressZlib(args.ProxySession.Response.ResponseBody);
break; break;
} }
WriteResponseStatus(args.ServerResponse.ProtocolVersion, args.ServerResponse.StatusCode, WriteResponseStatus(args.ProxySession.Response.HttpVersion, args.ProxySession.Response.ResponseStatusCode,
args.ServerResponse.StatusDescription, args.ClientStreamWriter); args.ProxySession.Response.ResponseStatusDescription, args.Client.ClientStreamWriter);
WriteResponseHeaders(args.ClientStreamWriter, args.ResponseHeaders, args.ResponseBody.Length, WriteResponseHeaders(args.Client.ClientStreamWriter, args.ProxySession.Response.ResponseHeaders, args.ProxySession.Response.ResponseBody.Length,
isChunked); isChunked);
WriteResponseBody(args.ClientStream, args.ResponseBody, isChunked); WriteResponseBody(args.Client.ClientStream, args.ProxySession.Response.ResponseBody, isChunked);
} }
else else
{ {
var isChunked = args.ServerResponse.GetResponseHeader("transfer-encoding").ToLower().Contains("chunked"); WriteResponseStatus(args.ProxySession.Response.HttpVersion, args.ProxySession.Response.ResponseStatusCode,
args.ProxySession.Response.ResponseStatusDescription, args.Client.ClientStreamWriter);
WriteResponseHeaders(args.Client.ClientStreamWriter, args.ProxySession.Response.ResponseHeaders);
WriteResponseStatus(args.ServerResponse.ProtocolVersion, args.ServerResponse.StatusCode, if (args.ProxySession.Response.IsChunked || args.ProxySession.Response.ContentLength > 0)
args.ServerResponse.StatusDescription, args.ClientStreamWriter); WriteResponseBody(args.ProxySession.ProxyClient.ServerStreamReader, args.Client.ClientStream, args.ProxySession.Response.IsChunked, args.ProxySession.Response.ContentLength);
WriteResponseHeaders(args.ClientStreamWriter, args.ResponseHeaders);
WriteResponseBody(args.ResponseStream, args.ClientStream, isChunked);
} }
args.ClientStream.Flush(); args.Client.ClientStream.Flush();
}
} }
catch catch
{ {
Dispose(args.Client, args.ClientStream, args.ClientStreamReader, args.ClientStreamWriter, args); Dispose(args.Client.TcpClient, args.Client.ClientStream, args.Client.ClientStreamReader, args.Client.ClientStreamWriter, args);
} }
finally finally
{ {
...@@ -91,47 +85,60 @@ namespace Titanium.Web.Proxy ...@@ -91,47 +85,60 @@ namespace Titanium.Web.Proxy
} }
} }
private static List<HttpHeader> ReadResponseHeaders(HttpWebResponse response) private static List<HttpHeader> ReadResponseHeaders(HttpWebSession response)
{
for (var i = 0; i < response.Response.ResponseHeaders.Count; i++)
{
switch (response.Response.ResponseHeaders[i].Name.ToLower())
{ {
var returnHeaders = new List<HttpHeader>(); case "content-length":
response.Response.ContentLength = int.Parse(response.Response.ResponseHeaders[i].Value.Trim());
break;
string cookieHeaderName = null; case "content-encoding":
string cookieHeaderValue = null; response.Response.ContentEncoding = response.Response.ResponseHeaders[i].Value.Trim().ToLower();
break;
foreach (string headerKey in response.Headers.Keys) case "content-type":
{ if (response.Response.ResponseHeaders[i].Value.Contains(";"))
if (headerKey.ToLower() == "set-cookie")
{ {
cookieHeaderName = headerKey; response.Response.ContentType = response.Response.ResponseHeaders[i].Value.Split(';')[0].Trim();
cookieHeaderValue = response.Headers[headerKey]; response.Response.CharacterSet = response.Response.ResponseHeaders[i].Value.Split(';')[1].ToLower().Replace("charset=", string.Empty).Trim();
} }
else else
returnHeaders.Add(new HttpHeader(headerKey, response.Headers[headerKey])); response.Response.ContentType = response.Response.ResponseHeaders[i].Value.ToLower().Trim();
} break;
if (!string.IsNullOrWhiteSpace(cookieHeaderValue)) case "transfer-encoding":
{ if (response.Response.ResponseHeaders[i].Value.ToLower().Contains("chunked"))
response.Headers.Remove(cookieHeaderName); response.Response.IsChunked = true;
var cookies = CookieSplitRegEx.Split(cookieHeaderValue); break;
foreach (var cookie in cookies)
returnHeaders.Add(new HttpHeader("Set-Cookie", cookie)); case "connection":
if (response.Response.ResponseHeaders[i].Value.ToLower().Contains("close"))
response.Response.ResponseKeepAlive = false;
break;
default:
break;
}
} }
return returnHeaders; return response.Response.ResponseHeaders;
} }
private static void WriteResponseStatus(Version version, HttpStatusCode code, string description,
private static void WriteResponseStatus(string version, string code, string description,
StreamWriter responseWriter) StreamWriter responseWriter)
{ {
var s = string.Format("HTTP/{0}.{1} {2} {3}", version.Major, version.Minor, (int)code, description); responseWriter.WriteLine(string.Format("{0} {1} {2}", version, code, description));
responseWriter.WriteLine(s);
} }
private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers) private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers)
{ {
if (headers != null) if (headers != null)
{ {
FixProxyHeaders(headers); FixResponseProxyHeaders(headers);
foreach (var header in headers) foreach (var header in headers)
{ {
...@@ -142,23 +149,29 @@ namespace Titanium.Web.Proxy ...@@ -142,23 +149,29 @@ namespace Titanium.Web.Proxy
responseWriter.WriteLine(); responseWriter.WriteLine();
responseWriter.Flush(); responseWriter.Flush();
} }
private static void FixProxyHeaders(List<HttpHeader> headers) private static void FixResponseProxyHeaders(List<HttpHeader> headers)
{ {
//If proxy-connection close was returned inform to close the connection //If proxy-connection close was returned inform to close the connection
if (headers.Any(x => x.Name.ToLower() == "proxy-connection" && x.Value.ToLower() == "close")) var proxyHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "proxy-connection");
if (headers.Any(x => x.Name.ToLower() == "connection") == false) var connectionHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "connection");
if (proxyHeader != null)
if (connectionHeader == null)
{ {
headers.Add(new HttpHeader("connection", "close")); headers.Add(new HttpHeader("connection", proxyHeader.Value));
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
} }
else else
headers.Find(x => x.Name.ToLower() == "connection").Value = "close"; {
connectionHeader.Value = "close";
}
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
} }
private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers, int length, private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers, int length,
bool isChunked) bool isChunked)
{ {
FixProxyHeaders(headers); FixResponseProxyHeaders(headers);
if (!isChunked) if (!isChunked)
{ {
...@@ -193,39 +206,68 @@ namespace Titanium.Web.Proxy ...@@ -193,39 +206,68 @@ namespace Titanium.Web.Proxy
WriteResponseBodyChunked(data, clientStream); WriteResponseBodyChunked(data, clientStream);
} }
private static void WriteResponseBody(Stream inStream, Stream outStream, bool isChunked) private static void WriteResponseBody(CustomBinaryReader inStreamReader, Stream outStream, bool isChunked, int BodyLength)
{ {
if (!isChunked) if (!isChunked)
{ {
int bytesToRead = BUFFER_SIZE;
if (BodyLength < BUFFER_SIZE)
bytesToRead = BodyLength;
var buffer = new byte[BUFFER_SIZE]; var buffer = new byte[BUFFER_SIZE];
int bytesRead; var bytesRead = 0;
while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0) var totalBytesRead = 0;
while ((bytesRead += inStreamReader.BaseStream.Read(buffer, 0, bytesToRead)) > 0)
{ {
outStream.Write(buffer, 0, bytesRead); outStream.Write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
if (totalBytesRead == BodyLength)
break;
bytesRead = 0;
var remainingBytes = (BodyLength - totalBytesRead);
bytesToRead = remainingBytes > BUFFER_SIZE ? BUFFER_SIZE : remainingBytes;
} }
} }
else else
WriteResponseBodyChunked(inStream, outStream); WriteResponseBodyChunked(inStreamReader, outStream);
} }
//Send chunked response //Send chunked response
private static void WriteResponseBodyChunked(Stream inStream, Stream outStream) private static void WriteResponseBodyChunked(CustomBinaryReader inStreamReader, Stream outStream)
{ {
var buffer = new byte[BUFFER_SIZE]; while (true)
{
var chuchkHead = inStreamReader.ReadLine();
var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber);
int bytesRead; if (chunkSize != 0)
while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0)
{ {
var chunkHead = Encoding.ASCII.GetBytes(bytesRead.ToString("x2")); var buffer = inStreamReader.ReadBytes(chunkSize);
var chunkHead = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));
outStream.Write(chunkHead, 0, chunkHead.Length); outStream.Write(chunkHead, 0, chunkHead.Length);
outStream.Write(ChunkTrail, 0, ChunkTrail.Length); outStream.Write(NewLineBytes, 0, NewLineBytes.Length);
outStream.Write(buffer, 0, bytesRead);
outStream.Write(ChunkTrail, 0, ChunkTrail.Length);
}
outStream.Write(buffer, 0, chunkSize);
outStream.Write(NewLineBytes, 0, NewLineBytes.Length);
inStreamReader.ReadLine();
}
else
{
inStreamReader.ReadLine();
outStream.Write(ChunkEnd, 0, ChunkEnd.Length); outStream.Write(ChunkEnd, 0, ChunkEnd.Length);
break;
}
}
} }
private static void WriteResponseBodyChunked(byte[] data, Stream outStream) private static void WriteResponseBodyChunked(byte[] data, Stream outStream)
...@@ -233,9 +275,9 @@ namespace Titanium.Web.Proxy ...@@ -233,9 +275,9 @@ namespace Titanium.Web.Proxy
var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2")); var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2"));
outStream.Write(chunkHead, 0, chunkHead.Length); outStream.Write(chunkHead, 0, chunkHead.Length);
outStream.Write(ChunkTrail, 0, ChunkTrail.Length); outStream.Write(NewLineBytes, 0, NewLineBytes.Length);
outStream.Write(data, 0, data.Length); outStream.Write(data, 0, data.Length);
outStream.Write(ChunkTrail, 0, ChunkTrail.Length); outStream.Write(NewLineBytes, 0, NewLineBytes.Length);
outStream.Write(ChunkEnd, 0, ChunkEnd.Length); outStream.Write(ChunkEnd, 0, ChunkEnd.Length);
} }
......
...@@ -50,9 +50,28 @@ ...@@ -50,9 +50,28 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\DotNetZip.1.9.7\lib\net20\Ionic.Zip.dll</HintPath> <HintPath>..\packages\DotNetZip.1.9.7\lib\net20\Ionic.Zip.dll</HintPath>
</Reference> </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>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.configuration" /> <Reference Include="System.configuration" />
<Reference Include="System.Core" /> <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.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
...@@ -66,12 +85,14 @@ ...@@ -66,12 +85,14 @@
<Compile Include="Helpers\CertificateManager.cs" /> <Compile Include="Helpers\CertificateManager.cs" />
<Compile Include="Helpers\Firefox.cs" /> <Compile Include="Helpers\Firefox.cs" />
<Compile Include="Helpers\SystemProxy.cs" /> <Compile Include="Helpers\SystemProxy.cs" />
<Compile Include="Network\TcpExtensions.cs" />
<Compile Include="Network\TcpConnectionManager.cs" />
<Compile Include="Models\HttpHeader.cs" /> <Compile Include="Models\HttpHeader.cs" />
<Compile Include="Network\HttpWebClient.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RequestHandler.cs" /> <Compile Include="RequestHandler.cs" />
<Compile Include="ResponseHandler.cs" /> <Compile Include="ResponseHandler.cs" />
<Compile Include="Helpers\CustomBinaryReader.cs" /> <Compile Include="Helpers\CustomBinaryReader.cs" />
<Compile Include="Helpers\NetFramework.cs" />
<Compile Include="Helpers\Compression.cs" /> <Compile Include="Helpers\Compression.cs" />
<Compile Include="ProxyServer.cs" /> <Compile Include="ProxyServer.cs" />
<Compile Include="EventArguments\SessionEventArgs.cs" /> <Compile Include="EventArguments\SessionEventArgs.cs" />
...@@ -80,6 +101,7 @@ ...@@ -80,6 +101,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup> <ItemGroup>
<None Include="app.config" />
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="Titanium_Proxy_Test_Root.cer"> <None Include="Titanium_Proxy_Test_Root.cer">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
...@@ -98,6 +120,11 @@ ...@@ -98,6 +120,11 @@
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" /> <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target> </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" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
......
<?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" />
</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" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
\ No newline at end of file
...@@ -2,4 +2,7 @@ ...@@ -2,4 +2,7 @@
<packages> <packages>
<package id="DotNetZip" version="1.9.7" targetFramework="net40" /> <package id="DotNetZip" version="1.9.7" targetFramework="net40" />
<package id="DotNetZip" version="1.9.7" targetFramework="net45" /> <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" />
</packages> </packages>
\ No newline at end of file
...@@ -56,9 +56,9 @@ deploy: ...@@ -56,9 +56,9 @@ deploy:
auth_token: auth_token:
secure: ItVm+50ASpDXx1w+FCe2NTpZxqCbjRWfugLjk/bqBIi00DvPnSGOV1rIQ5hnwBW3 secure: ItVm+50ASpDXx1w+FCe2NTpZxqCbjRWfugLjk/bqBIi00DvPnSGOV1rIQ5hnwBW3
on: on:
branch: master branch: /(master|release)/
- provider: NuGet - provider: NuGet
api_key: api_key:
secure: ZbRmjOcp+TDllRV1wxqLZjdRV7hld388rXlWVJuGGiQleomP9Ku+Nsy3a75E7/9k secure: ZbRmjOcp+TDllRV1wxqLZjdRV7hld388rXlWVJuGGiQleomP9Ku+Nsy3a75E7/9k
on: on:
branch: master branch: /(master|release)/
\ No newline at end of file \ 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