Commit 6b9c201e authored by justcoding121's avatar justcoding121 Committed by justcoding121

Fix set/get request/response/headers

parent 40a1f0cf
...@@ -57,43 +57,57 @@ Sample request and response event handlers ...@@ -57,43 +57,57 @@ Sample request and response event handlers
public void OnRequest(object sender, SessionEventArgs e) public void OnRequest(object sender, SessionEventArgs e)
{ {
Console.WriteLine(e.RequestURL);
Console.WriteLine(e.RequestURL);
if (e.RequestURL.Contains("somewebsite.com")) //modify request headers
if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT") && e.RequestContentLength > 0) var requestHeaders = e.RequestHeaders;
{
var m = e.GetRequestBody().Replace("a", "b"); if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT") && e.RequestContentLength > 0)
e.SetRequestBody(m); {
//Get/Set request body bytes
byte[] bodyBytes = e.GetRequestBody();
e.SetRequestBody(bodyBytes);
} //Get/Set request body as string
string bodyString = e.GetRequestBodyAsString();
e.SetRequestBodyString(bodyString);
}
//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("somewebsite.com")) if (e.RequestURL.Contains("google.com"))
{ {
e.Ok("<!DOCTYPE html><html><body><h1>Blocked</h1><p>Website blocked.</p></body></html>"); e.Ok("<!DOCTYPE html><html><body><h1>Website Blocked</h1><p>Blocked by titanium web proxy.</p></body></html>");
} }
} }
public void OnResponse(object sender, SessionEventArgs e) public void OnResponse(object sender, SessionEventArgs e)
{ {
if (e.RequestURL.Contains("somewebsite.com"))
//To modify a response headers
var responseHeaders = e.ResponseHeaders;
if (e.ResponseStatusCode == HttpStatusCode.OK) if (e.ResponseStatusCode == HttpStatusCode.OK)
{ {
if (e.ResponseContentType.Trim().ToLower().Contains("text/html")) if (e.ResponseContentType.Trim().ToLower().Contains("text/html"))
{ {
//Get response body //Get/Set response body bytes
string responseBody = e.GetResponseBody(); byte[] responseBodyBytes = e.GetResponseBody();
e.SetResponseBody(responseBodyBytes);
//Get response body as string
string responseBody = e.GetResponseBodyAsString();
//Modify e.ServerResponse //Modify e.ServerResponse
Regex rex = new Regex("</body>", RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline); 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); string modified = rex.Replace(responseBody, "<script type =\"text/javascript\">alert('Response was modified by this script!');</script></body>", 1);
//Set modifed response Html Body //Set modifed response Html Body
e.SetResponseBody(modified); e.SetResponseBodyString(modified);
} }
} }
} }
......
...@@ -6,7 +6,7 @@ using System.Net; ...@@ -6,7 +6,7 @@ using System.Net;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.DirectoryServices.AccountManagement; using System.DirectoryServices.AccountManagement;
using System.DirectoryServices.ActiveDirectory; using System.DirectoryServices.ActiveDirectory;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy; using Titanium.Web.Proxy;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
...@@ -60,21 +60,27 @@ namespace Titanium.Web.Proxy.Test ...@@ -60,21 +60,27 @@ namespace Titanium.Web.Proxy.Test
Console.WriteLine(e.RequestURL); Console.WriteLine(e.RequestURL);
//if (e.RequestURL.Contains("somewebsite.com")) ////modify request headers
// if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT") && e.RequestContentLength > 0) //var requestHeaders = e.RequestHeaders;
// {
//if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT") && e.RequestContentLength > 0)
//{
// //Get/Set request body bytes
// byte[] bodyBytes = e.GetRequestBody();
// e.SetRequestBody(bodyBytes);
// var m = e.GetRequestBody().Replace("a", "b"); // //Get/Set request body as string
// e.SetRequestBody(m); // string bodyString = e.GetRequestBodyAsString();
// e.SetRequestBodyString(bodyString);
// } //}
//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("somewebsite.com")) //if (e.RequestURL.Contains("google.com"))
//{ //{
// e.Ok("<!DOCTYPE html><html><body><h1>Blocked</h1><p>Website blocked.</p></body></html>"); // e.Ok("<!DOCTYPE html><html><body><h1>Website Blocked</h1><p>Blocked by titanium web proxy.</p></body></html>");
//} //}
} }
...@@ -83,22 +89,26 @@ namespace Titanium.Web.Proxy.Test ...@@ -83,22 +89,26 @@ 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)
{ {
//To modify a response //modify response headers
//var responseHeaders = e.ResponseHeaders;
//if (e.RequestURL.Contains("somewebsite.com"))
//if (e.ResponseStatusCode == HttpStatusCode.OK) //if (e.ResponseStatusCode == HttpStatusCode.OK)
//{ //{
// if (e.ResponseContentType.Trim().ToLower().Contains("text/html")) // if (e.ResponseContentType.Trim().ToLower().Contains("text/html"))
// { // {
// //Get response body // //Get/Set response body bytes
// string responseBody = e.GetResponseBody(); // byte[] responseBodyBytes = e.GetResponseBody();
// e.SetResponseBody(responseBodyBytes);
// //Get response body as string
// string responseBody = e.GetResponseBodyAsString();
// //Modify e.ServerResponse // //Modify e.ServerResponse
// Regex rex = new Regex("</body>", RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline); // 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); // string modified = rex.Replace(responseBody, "<script type =\"text/javascript\">alert('Response was modified by this script!');</script></body>", 1);
// //Set modifed response Html Body // //Set modifed response Html Body
// e.SetResponseBody(modified); // e.SetResponseBodyString(modified);
// } // }
//} //}
......
...@@ -5,9 +5,11 @@ using System.Net; ...@@ -5,9 +5,11 @@ using System.Net;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using System.Net.Sockets; using System.Net.Sockets;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using System.Linq;
using System.Collections.Generic;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.EventArguments
namespace Titanium.Web.Proxy.Models
{ {
public class SessionEventArgs : EventArgs, IDisposable public class SessionEventArgs : EventArgs, IDisposable
{ {
...@@ -19,18 +21,18 @@ namespace Titanium.Web.Proxy.Models ...@@ -19,18 +21,18 @@ namespace Titanium.Web.Proxy.Models
internal CustomBinaryReader clientStreamReader { get; set; } internal CustomBinaryReader clientStreamReader { get; set; }
internal StreamWriter clientStreamWriter { get; set; } internal StreamWriter clientStreamWriter { get; set; }
internal int requestContentLength { get; set; }
internal Encoding requestEncoding { get; set; } internal Encoding requestEncoding { get; set; }
internal Version requestHttpVersion { get; set; } internal Version requestHttpVersion { get; set; }
internal bool requestIsAlive { get; set; } internal bool requestIsAlive { get; set; }
internal bool cancelRequest { get; set; } internal bool cancelRequest { get; set; }
internal string requestBody { get; set; } internal byte[] requestBody { get; set; }
internal string requestBodyString { get; set; }
internal bool requestBodyRead { get; set; } internal bool requestBodyRead { get; set; }
internal Encoding responseEncoding { get; set; } internal Encoding responseEncoding { get; set; }
internal Stream responseStream { get; set; } internal Stream responseStream { get; set; }
internal string responseBody { get; set; } internal byte[] responseBody { get; set; }
internal string responseBodyString { get; set; }
internal bool responseBodyRead { get; set; } internal bool responseBodyRead { get; set; }
internal HttpWebRequest proxyRequest { get; set; } internal HttpWebRequest proxyRequest { get; set; }
...@@ -47,104 +49,187 @@ namespace Titanium.Web.Proxy.Models ...@@ -47,104 +49,187 @@ namespace Titanium.Web.Proxy.Models
public IPAddress ClientIpAddress { get { return this.clientIpAddress; } } public IPAddress ClientIpAddress { get { return this.clientIpAddress; } }
public bool IsHttps { get { return this.isHttps; } } public bool IsHttps { get { return this.isHttps; } }
public string RequestURL { get { return this.requestURL; } } public string RequestURL { get { return this.requestURL; } }
public string RequestHostname { get { return this.requestHostname; } } public string RequestHostname { get { return this.requestHostname; } }
public int RequestContentLength
{
get
{
if (this.RequestHeaders.Any(x => x.Name.ToLower() == "content-length"))
{
int contentLen;
int.TryParse(this.RequestHeaders.First(x => x.Name.ToLower() == "content-length").Value, out contentLen);
if (contentLen != 0)
return contentLen;
}
return -1;
}
}
public string RequestMethod { get { return this.proxyRequest.Method; } } public string RequestMethod { get { return this.proxyRequest.Method; } }
public int RequestContentLength { get { return requestContentLength; } } public List<HttpHeader> RequestHeaders { get; set; }
public HttpStatusCode ResponseStatusCode { get { return this.serverResponse.StatusCode; } } public HttpStatusCode ResponseStatusCode { get { return this.serverResponse.StatusCode; } }
public string ResponseContentType { get { return this.serverResponse.ContentType; } } public string ResponseContentType { get { return this.ResponseHeaders.Any(x => x.Name.ToLower() == "content-type") ? this.ResponseHeaders.First(x => x.Name.ToLower() == "content-type").Value : null; } }
public List<HttpHeader> ResponseHeaders { get; set; }
internal SessionEventArgs(int bufferSize) internal SessionEventArgs(int bufferSize)
{ {
BUFFER_SIZE = bufferSize; BUFFER_SIZE = bufferSize;
} }
public void Dispose() private void readRequestBody()
{ {
if (this.proxyRequest != null) if (requestBody == null)
this.proxyRequest.Abort(); {
requestBody = clientStreamReader.ReadBytes(RequestContentLength);
}
requestBodyRead = true;
}
private void readResponseBody()
{
if (responseBody == null)
{
switch (serverResponse.ContentEncoding)
{
case "gzip":
responseBody = CompressionHelper.DecompressGzip(responseStream);
break;
case "deflate":
responseBody = CompressionHelper.DecompressDeflate(responseStream);
break;
case "zlib":
responseBody = CompressionHelper.DecompressZlib(responseStream);
break;
default:
responseBody = DecodeData(responseStream);
break;
}
if (this.responseStream != null) responseBodyRead = true;
this.responseStream.Dispose();
if (this.serverResponse != null) }
this.serverResponse.Close();
} }
public string GetRequestBody() //stream reader not recomended for images
private byte[] DecodeData(Stream responseStream)
{ {
if ((proxyRequest.Method.ToUpper() == "POST" || proxyRequest.Method.ToUpper() == "PUT") && requestContentLength > 0) byte[] buffer = new byte[BUFFER_SIZE];
using (MemoryStream ms = new MemoryStream())
{ {
if (requestBody == null) int read;
while ((read = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{ {
var buffer = clientStreamReader.ReadBytes(requestContentLength); ms.Write(buffer, 0, read);
requestBody = requestEncoding.GetString(buffer);
} }
requestBodyRead = true; return ms.ToArray();
}
}
public Encoding GetRequestBodyEncoding()
{
return requestEncoding;
}
public byte[] GetRequestBody()
{
if ((proxyRequest.Method.ToUpper() == "POST" || proxyRequest.Method.ToUpper() == "PUT") && RequestContentLength > 0)
{
readRequestBody();
return requestBody; return requestBody;
} }
else else
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.");
} }
public void SetRequestBody(string body) public string GetRequestBodyAsString()
{ {
this.requestBody = body; if ((proxyRequest.Method.ToUpper() == "POST" || proxyRequest.Method.ToUpper() == "PUT") && RequestContentLength > 0)
requestBodyRead = true; {
readRequestBody();
if (requestBodyString == null)
{
requestBodyString = requestEncoding.GetString(requestBody);
}
return requestBodyString;
}
else
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.");
} }
public string GetResponseBody()
public void SetRequestBody(byte[] body)
{ {
if (responseBody == null) if (!requestBodyRead)
{ {
readRequestBody();
}
if (responseEncoding == null) responseEncoding = Encoding.GetEncoding(serverResponse.CharacterSet); requestBody = body;
if (responseEncoding == null) responseEncoding = Encoding.Default; requestBodyRead = true;
}
public void SetRequestBodyString(string body)
{
if (!requestBodyRead)
{
readRequestBody();
}
switch (serverResponse.ContentEncoding) this.requestBody = requestEncoding.GetBytes(body);
{ requestBodyRead = true;
case "gzip": }
responseBody = CompressionHelper.DecompressGzip(responseStream, responseEncoding);
break;
case "deflate":
responseBody = CompressionHelper.DecompressDeflate(responseStream, responseEncoding);
break;
case "zlib":
responseBody = CompressionHelper.DecompressZlib(responseStream, responseEncoding);
break;
default:
responseBody = DecodeData(responseStream, responseEncoding);
break;
}
responseBodyRead = true; public Encoding GetResponseBodyEncoding()
{
return responseEncoding;
}
} public byte[] GetResponseBody()
{
readResponseBody();
return responseBody; return responseBody;
} }
public string GetResponseBodyAsString()
public void SetResponseBody(string body)
{ {
if (responseEncoding == null) responseEncoding = Encoding.GetEncoding(serverResponse.CharacterSet); GetResponseBody();
if (responseEncoding == null) responseEncoding = Encoding.Default; if (responseBodyString == null)
{
responseBodyString = responseEncoding.GetString(responseBody);
}
return responseBodyString;
}
public void SetResponseBody(byte[] body)
{
if (responseBody == null)
{
GetResponseBody();
}
responseBody = body;
this.responseBody = body;
responseBodyRead = true;
} }
//stream reader not recomended for images public void SetResponseBodyString(string body)
private string DecodeData(Stream responseStream, Encoding e)
{ {
StreamReader reader = new StreamReader(responseStream, e); if (responseBody == null)
return reader.ReadToEnd(); {
GetResponseBody();
}
var bodyBytes = responseEncoding.GetBytes(body);
SetResponseBody(bodyBytes);
} }
public void Ok(string html) public void Ok(string html)
{ {
...@@ -179,6 +264,19 @@ namespace Titanium.Web.Proxy.Models ...@@ -179,6 +264,19 @@ namespace Titanium.Web.Proxy.Models
} }
public void Dispose()
{
if (this.proxyRequest != null)
this.proxyRequest.Abort();
if (this.responseStream != null)
this.responseStream.Dispose();
if (this.serverResponse != null)
this.serverResponse.Close();
}
} }
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
namespace Titanium.Web.Proxy.Extensions
{
public static class HttpWebResponseExtensions
{
public static Encoding GetEncoding(this HttpWebResponse response)
{
if (string.IsNullOrEmpty(response.CharacterSet)) return Encoding.GetEncoding("ISO-8859-1");
else
return Encoding.GetEncoding(response.CharacterSet);
}
}
}
...@@ -11,31 +11,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -11,31 +11,10 @@ namespace Titanium.Web.Proxy.Helpers
{ {
private static readonly int BUFFER_SIZE = 8192; private static readonly int BUFFER_SIZE = 8192;
public static string DecompressGzip(Stream input, Encoding e)
{
using (System.IO.Compression.GZipStream decompressor = new System.IO.Compression.GZipStream(input, System.IO.Compression.CompressionMode.Decompress))
{
int read = 0;
var buffer = new byte[BUFFER_SIZE];
using (MemoryStream output = new MemoryStream())
{
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return e.GetString(output.ToArray());
}
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
public static byte[] CompressZlib(string responseData, Encoding e) public static byte[] CompressZlib(byte[] bytes)
{ {
Byte[] bytes = e.GetBytes(responseData);
using (MemoryStream ms = new MemoryStream()) using (MemoryStream ms = new MemoryStream())
{ {
using (Ionic.Zlib.ZlibStream zip = new Ionic.Zlib.ZlibStream(ms, Ionic.Zlib.CompressionMode.Compress, true)) using (Ionic.Zlib.ZlibStream zip = new Ionic.Zlib.ZlibStream(ms, Ionic.Zlib.CompressionMode.Compress, true))
...@@ -48,9 +27,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -48,9 +27,8 @@ namespace Titanium.Web.Proxy.Helpers
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
public static byte[] CompressDeflate(string responseData, Encoding e) public static byte[] CompressDeflate(byte[] bytes)
{ {
Byte[] bytes = e.GetBytes(responseData);
using (MemoryStream ms = new MemoryStream()) using (MemoryStream ms = new MemoryStream())
{ {
...@@ -64,9 +42,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -64,9 +42,8 @@ namespace Titanium.Web.Proxy.Helpers
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
public static byte[] CompressGzip(string responseData, Encoding e) public static byte[] CompressGzip(byte[] bytes)
{ {
Byte[] bytes = e.GetBytes(responseData);
using (MemoryStream ms = new MemoryStream()) using (MemoryStream ms = new MemoryStream())
{ {
...@@ -79,7 +56,27 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -79,7 +56,27 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
public static string DecompressDeflate(Stream input, Encoding e)
public static byte[] DecompressGzip(Stream input)
{
using (System.IO.Compression.GZipStream decompressor = new System.IO.Compression.GZipStream(input, System.IO.Compression.CompressionMode.Decompress))
{
int read = 0;
var buffer = new byte[BUFFER_SIZE];
using (MemoryStream output = new MemoryStream())
{
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
}
public static byte[] DecompressDeflate(Stream input)
{ {
using (Ionic.Zlib.DeflateStream decompressor = new Ionic.Zlib.DeflateStream(input, Ionic.Zlib.CompressionMode.Decompress)) using (Ionic.Zlib.DeflateStream decompressor = new Ionic.Zlib.DeflateStream(input, Ionic.Zlib.CompressionMode.Decompress))
{ {
...@@ -92,11 +89,11 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -92,11 +89,11 @@ namespace Titanium.Web.Proxy.Helpers
{ {
output.Write(buffer, 0, read); output.Write(buffer, 0, read);
} }
return e.GetString(output.ToArray()); return output.ToArray();
} }
} }
} }
public static string DecompressZlib(Stream input, Encoding e) public static byte[] DecompressZlib(Stream input)
{ {
using (Ionic.Zlib.ZlibStream decompressor = new Ionic.Zlib.ZlibStream(input, Ionic.Zlib.CompressionMode.Decompress)) using (Ionic.Zlib.ZlibStream decompressor = new Ionic.Zlib.ZlibStream(input, Ionic.Zlib.CompressionMode.Decompress))
{ {
...@@ -109,7 +106,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -109,7 +106,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
output.Write(buffer, 0, read); output.Write(buffer, 0, read);
} }
return e.GetString(output.ToArray()); return output.ToArray();
} }
} }
} }
......
...@@ -8,6 +8,7 @@ using System.Net; ...@@ -8,6 +8,7 @@ using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Sockets; using System.Net.Sockets;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
...@@ -16,10 +17,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -16,10 +17,10 @@ namespace Titanium.Web.Proxy.Helpers
private static readonly int BUFFER_SIZE = 8192; private static readonly int BUFFER_SIZE = 8192;
private static readonly String[] colonSpaceSplit = new string[] { ": " }; private static readonly String[] colonSpaceSplit = new string[] { ": " };
public static void SendRaw(Stream clientStream, string httpCmd, List<string> requestLines, string hostName, int tunnelPort, bool isHttps) public static void SendRaw(Stream clientStream, string httpCmd, List<HttpHeader> requestHeaders, string hostName, int tunnelPort, bool isHttps)
{ {
StringBuilder sb = null; StringBuilder sb = null;
if (httpCmd != null || requestLines != null) if (httpCmd != null || requestHeaders != null)
{ {
sb = new StringBuilder(); sb = new StringBuilder();
if (httpCmd != null) if (httpCmd != null)
...@@ -27,9 +28,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -27,9 +28,9 @@ namespace Titanium.Web.Proxy.Helpers
sb.Append(httpCmd); sb.Append(httpCmd);
sb.Append(Environment.NewLine); sb.Append(Environment.NewLine);
} }
for (int i = 0; i < requestLines.Count; i++) for (int i = 0; i < requestHeaders.Count; i++)
{ {
var header = requestLines[i]; var header = requestHeaders[i].ToString();
sb.Append(header); sb.Append(header);
sb.Append(Environment.NewLine); sb.Append(Environment.NewLine);
} }
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Titanium.Web.Proxy.Models
{
public class HttpHeader
{
public string Name { get; set; }
public string Value { get; set; }
public HttpHeader(string name, string value)
{
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) throw new Exception("Name or value cannot be null");
this.Name = name.Trim();
this.Value = value.Trim();
}
public string ToString()
{
return String.Format("{0}: {1}", this.Name, this.Value);
}
}
}
...@@ -10,8 +10,9 @@ using System.Security.Authentication; ...@@ -10,8 +10,9 @@ using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using System.Text;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
...@@ -29,12 +30,12 @@ namespace Titanium.Web.Proxy ...@@ -29,12 +30,12 @@ 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[] ChunkEnd = Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine);
private static object certificateAccessLock = new object(); private static object certificateAccessLock = new object();
private static TcpListener listener;
private static Thread listenerThread;
private static bool ShouldListen { get; set; } private static TcpListener listener;
private static CertificateManager CertManager { get; set; } private static CertificateManager CertManager { get; set; }
public static List<string> ExcludedHttpsHostNameRegex = new List<string>(); public static List<string> ExcludedHttpsHostNameRegex = new List<string>();
...@@ -82,43 +83,20 @@ namespace Titanium.Web.Proxy ...@@ -82,43 +83,20 @@ namespace Titanium.Web.Proxy
} }
private static void Listen(Object obj)
{
TcpListener listener = (TcpListener)obj;
while (ShouldListen)
{
TcpClient client = null;
try
{
client = listener.AcceptTcpClient();
Task.Factory.StartNew(() => HandleClient(client));
}
catch
{
if (client != null)
client.Close();
}
}
}
public static bool Start() public static bool Start()
{ {
listener = new TcpListener(ListeningIpAddress, ListeningPort); listener = new TcpListener(ListeningIpAddress, ListeningPort);
listener.Start(); listener.Start();
listenerThread = new Thread(new ParameterizedThreadStart(Listen));
listenerThread.IsBackground = true;
ShouldListen = true;
listenerThread.Start(listener);
ListeningPort = ((IPEndPoint)listener.LocalEndpoint).Port; ListeningPort = ((IPEndPoint)listener.LocalEndpoint).Port;
// accept clients asynchronously
listener.BeginAcceptTcpClient(OnAcceptConnection, listener);
if (SetAsSystemProxy) if (SetAsSystemProxy)
{ {
SystemProxyHelper.EnableProxyHTTP("localhost", ListeningPort); SystemProxyHelper.EnableProxyHTTP(ListeningIpAddress == IPAddress.Any ? "127.0.0.1" : ListeningIpAddress.ToString(), ListeningPort);
FireFoxHelper.AddFirefox(); FireFoxHelper.AddFirefox();
...@@ -130,13 +108,28 @@ namespace Titanium.Web.Proxy ...@@ -130,13 +108,28 @@ namespace Titanium.Web.Proxy
//If certificate was trusted by the machine //If certificate was trusted by the machine
if (certTrusted) if (certTrusted)
{ {
SystemProxyHelper.EnableProxyHTTPS("localhost", ListeningPort); SystemProxyHelper.EnableProxyHTTPS(ListeningIpAddress == IPAddress.Any ? "127.0.0.1" : ListeningIpAddress.ToString(), ListeningPort);
} }
} }
} }
return true; return true;
} }
private static void OnAcceptConnection(IAsyncResult asyn)
{
try
{
// Get the listener that handles the client request.
listener.BeginAcceptTcpClient(OnAcceptConnection, listener);
TcpClient client = listener.EndAcceptTcpClient(asyn);
Task.Factory.StartNew(() => HandleClient(client));
}
catch { }
}
public static void Stop() public static void Stop()
{ {
...@@ -146,9 +139,7 @@ namespace Titanium.Web.Proxy ...@@ -146,9 +139,7 @@ namespace Titanium.Web.Proxy
FireFoxHelper.RemoveFirefox(); FireFoxHelper.RemoveFirefox();
} }
ShouldListen = false;
listener.Stop(); listener.Stop();
listenerThread.Interrupt();
CertManager.Dispose(); CertManager.Dispose();
} }
......
...@@ -12,10 +12,11 @@ using System.Diagnostics; ...@@ -12,10 +12,11 @@ using System.Diagnostics;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Reflection; using System.Reflection;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.EventArguments;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -49,30 +50,22 @@ namespace Titanium.Web.Proxy ...@@ -49,30 +50,22 @@ namespace Titanium.Web.Proxy
httpRemoteUri = new Uri(httpCmdSplit[1]); httpRemoteUri = new Uri(httpCmdSplit[1]);
var httpVersion = httpCmdSplit[2]; var httpVersion = httpCmdSplit[2];
//Client wants to create a secure tcp tunnel (its a HTTPS request)
var excluded = ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)); var excluded = ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x));
//Client wants to create a secure tcp tunnel (its a HTTPS request)
if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port == 443) if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port == 443)
{ {
httpRemoteUri = new Uri("https://" + httpCmdSplit[1]); httpRemoteUri = new Uri("https://" + httpCmdSplit[1]);
clientStreamReader.ReadAllLines(); clientStreamReader.ReadAllLines();
WriteConnectedResponse(clientStreamWriter, httpVersion); WriteConnectedResponse(clientStreamWriter, httpVersion);
//Create the fake certificate signed using our fake certificate authority
Monitor.Enter(certificateAccessLock);
var certificate = ProxyServer.CertManager.CreateCertificate(httpRemoteUri.Host); var certificate = ProxyServer.CertManager.CreateCertificate(httpRemoteUri.Host);
Monitor.Exit(certificateAccessLock);
SslStream sslStream = null; SslStream sslStream = null;
//Pinned certificate clients cannot be proxied
//For example dropbox clients use certificate pinning
//So just relay the request
try try
{ {
sslStream = new SslStream(clientStream, true); sslStream = new SslStream(clientStream, true);
...@@ -136,15 +129,9 @@ namespace Titanium.Web.Proxy ...@@ -136,15 +129,9 @@ namespace Titanium.Web.Proxy
return; return;
} }
string tmpLine = null;
List<string> requestLines = new List<string>();
while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
{
requestLines.Add(tmpLine);
}
var args = new SessionEventArgs(BUFFER_SIZE); var args = new SessionEventArgs(BUFFER_SIZE);
args.client = client; args.client = client;
try try
{ {
...@@ -170,33 +157,41 @@ namespace Titanium.Web.Proxy ...@@ -170,33 +157,41 @@ namespace Titanium.Web.Proxy
args.isHttps = true; args.isHttps = true;
} }
//construct the web request that we are going to issue on behalf of the client. args.RequestHeaders = new List<HttpHeader>();
args.proxyRequest = (HttpWebRequest)HttpWebRequest.Create(httpRemoteUri);
args.proxyRequest.Proxy = null; string tmpLine = null;
args.proxyRequest.UseDefaultCredentials = true;
args.proxyRequest.Method = httpMethod; while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
args.proxyRequest.ProtocolVersion = version; {
args.clientStream = clientStream; String[] header = tmpLine.Split(colonSpaceSplit, 2, StringSplitOptions.None);
args.clientStreamReader = clientStreamReader; args.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
args.clientStreamWriter = clientStreamWriter; }
for (int i = 0; i < requestLines.Count; i++) for (int i = 0; i < args.RequestHeaders.Count; i++)
{ {
var rawHeader = requestLines[i]; var rawHeader = args.RequestHeaders[i];
String[] header = rawHeader.ToLower().Trim().Split(colonSpaceSplit, 2, StringSplitOptions.None);
//if request was upgrade to web-socket protocol then relay the request without proxying //if request was upgrade to web-socket protocol then relay the request without proxying
if ((header[0] == "upgrade") && (header[1] == "websocket")) if ((rawHeader.Name.ToLower() == "upgrade") && (rawHeader.Value.ToLower() == "websocket"))
{ {
TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, requestLines, httpRemoteUri.Host, httpRemoteUri.Port, httpRemoteUri.Scheme == Uri.UriSchemeHttps); TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.RequestHeaders, httpRemoteUri.Host, httpRemoteUri.Port, httpRemoteUri.Scheme == Uri.UriSchemeHttps);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; return;
} }
} }
SetClientRequestHeaders(requestLines, args.proxyRequest);
//construct the web request that we are going to issue on behalf of the client.
args.proxyRequest = (HttpWebRequest)HttpWebRequest.Create(httpRemoteUri);
args.proxyRequest.Proxy = null;
args.proxyRequest.UseDefaultCredentials = true;
args.proxyRequest.Method = httpMethod;
args.proxyRequest.ProtocolVersion = version;
args.clientStream = clientStream;
args.clientStreamReader = clientStreamReader;
args.clientStreamWriter = clientStreamWriter;
args.proxyRequest.AllowAutoRedirect = false; args.proxyRequest.AllowAutoRedirect = false;
args.proxyRequest.AutomaticDecompression = DecompressionMethods.None; args.proxyRequest.AutomaticDecompression = DecompressionMethods.None;
args.requestHostname = args.proxyRequest.RequestUri.Host; args.requestHostname = args.proxyRequest.RequestUri.Host;
...@@ -205,33 +200,31 @@ namespace Titanium.Web.Proxy ...@@ -205,33 +200,31 @@ namespace Titanium.Web.Proxy
args.clientIpAddress = ((IPEndPoint)client.Client.RemoteEndPoint).Address; args.clientIpAddress = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
args.requestHttpVersion = version; args.requestHttpVersion = version;
args.requestIsAlive = args.proxyRequest.KeepAlive; args.requestIsAlive = args.proxyRequest.KeepAlive;
args.proxyRequest.ConnectionGroupName = args.requestHostname;
args.proxyRequest.AllowWriteStreamBuffering = true;
//If requested interception //If requested interception
if (BeforeRequest != null) if (BeforeRequest != null)
{ {
args.requestContentLength = (int)args.proxyRequest.ContentLength;
args.requestEncoding = args.proxyRequest.GetEncoding(); args.requestEncoding = args.proxyRequest.GetEncoding();
BeforeRequest(null, args); BeforeRequest(null, args);
} }
if (args.cancelRequest) if (args.cancelRequest)
{ {
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; return;
} }
args.proxyRequest.ConnectionGroupName = args.requestHostname; SetRequestHeaders(args.RequestHeaders, args.proxyRequest);
args.proxyRequest.AllowWriteStreamBuffering = true;
//If request was modified by user //If request was modified by user
if (args.requestBodyRead) if (args.requestBodyRead)
{ {
byte[] requestBytes = args.requestEncoding.GetBytes(args.requestBody); args.proxyRequest.ContentLength = args.requestBody.Length;
args.proxyRequest.ContentLength = requestBytes.Length;
Stream newStream = args.proxyRequest.GetRequestStream(); Stream newStream = args.proxyRequest.GetRequestStream();
newStream.Write(requestBytes, 0, requestBytes.Length); newStream.Write(args.requestBody, 0, args.requestBody.Length);
args.proxyRequest.BeginGetResponse(new AsyncCallback(HandleHttpSessionResponse), args); args.proxyRequest.BeginGetResponse(new AsyncCallback(HandleHttpSessionResponse), args);
...@@ -264,90 +257,82 @@ namespace Titanium.Web.Proxy ...@@ -264,90 +257,82 @@ namespace Titanium.Web.Proxy
} }
private static void SetClientRequestHeaders(List<string> requestLines, HttpWebRequest webRequest) private static void SetRequestHeaders(List<HttpHeader> requestHeaders, HttpWebRequest webRequest)
{ {
for (int i = 0; i < requestHeaders.Count; i++)
for (int i = 1; i < requestLines.Count; i++)
{ {
String httpCmd = requestLines[i]; switch (requestHeaders[i].Name.ToLower())
String[] header = httpCmd.Split(colonSpaceSplit, 2, StringSplitOptions.None);
if (!String.IsNullOrEmpty(header[0].Trim()))
switch (header[0].ToLower())
{ {
case "accept": case "accept":
webRequest.Accept = header[1]; webRequest.Accept = requestHeaders[i].Value;
break; break;
case "accept-encoding": case "accept-encoding":
webRequest.Headers.Add(header[0], "gzip,deflate,zlib"); webRequest.Headers.Add("Accept-Encoding", "gzip,deflate,zlib");
break; break;
case "cookie": case "cookie":
webRequest.Headers["Cookie"] = header[1]; webRequest.Headers["Cookie"] = requestHeaders[i].Value;
break; break;
case "connection": case "connection":
if (header[1].ToLower() == "keep-alive") if (requestHeaders[i].Value.ToLower() == "keep-alive")
webRequest.KeepAlive = true; webRequest.KeepAlive = true;
break; break;
case "content-length": case "content-length":
int contentLen; int contentLen;
int.TryParse(header[1], out contentLen); int.TryParse(requestHeaders[i].Value, out contentLen);
if (contentLen != 0) if (contentLen != 0)
webRequest.ContentLength = contentLen; webRequest.ContentLength = contentLen;
break; break;
case "content-type": case "content-type":
webRequest.ContentType = header[1]; webRequest.ContentType = requestHeaders[i].Value;
break; break;
case "expect": case "expect":
if (header[1].ToLower() == "100-continue") if (requestHeaders[i].Value.ToLower() == "100-continue")
webRequest.ServicePoint.Expect100Continue = true; webRequest.ServicePoint.Expect100Continue = true;
else else
webRequest.Expect = header[1]; webRequest.Expect = requestHeaders[i].Value;
break; break;
case "host": case "host":
webRequest.Host = header[1]; webRequest.Host = requestHeaders[i].Value;
break; break;
case "if-modified-since": case "if-modified-since":
String[] sb = header[1].Trim().Split(semiSplit); String[] sb = requestHeaders[i].Value.Trim().Split(semiSplit);
DateTime d; DateTime d;
if (DateTime.TryParse(sb[0], out d)) if (DateTime.TryParse(sb[0], out d))
webRequest.IfModifiedSince = d; webRequest.IfModifiedSince = d;
break; break;
case "proxy-connection": case "proxy-connection":
if (header[1].ToLower() == "keep-alive") if (requestHeaders[i].Value.ToLower() == "keep-alive")
webRequest.KeepAlive = true; webRequest.KeepAlive = true;
break; break;
case "range": case "range":
var startEnd = header[1].Replace(Environment.NewLine, "").Remove(0, 6).Split('-'); 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])); } 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 else
webRequest.AddRange(int.Parse(startEnd[0])); webRequest.AddRange(int.Parse(startEnd[0]));
break; break;
case "referer": case "referer":
webRequest.Referer = header[1]; webRequest.Referer = requestHeaders[i].Value;
break; break;
case "user-agent": case "user-agent":
webRequest.UserAgent = header[1]; 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 (header[1].ToLower() == "chunked") if (requestHeaders[i].Value.ToLower() == "chunked")
webRequest.SendChunked = true; webRequest.SendChunked = true;
else else
webRequest.SendChunked = false; webRequest.SendChunked = false;
break; break;
case "upgrade": case "upgrade":
if (header[1].ToLower() == "http/1.1") if (requestHeaders[i].Value.ToLower() == "http/1.1")
webRequest.Headers.Add(header[0], header[1]); webRequest.Headers.Add("Upgrade", requestHeaders[i].Value);
break; break;
default: default:
if (header.Length >= 2) webRequest.Headers.Add(requestHeaders[i].Name, requestHeaders[i].Value);
webRequest.Headers.Add(header[0], header[1]);
break; break;
} }
...@@ -485,22 +470,6 @@ namespace Titanium.Web.Proxy ...@@ -485,22 +470,6 @@ namespace Titanium.Web.Proxy
} }
private static void Dispose(TcpClient client, Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, SessionEventArgs args)
{
if (args != null)
args.Dispose();
if (clientStreamReader != null)
clientStreamReader.Dispose();
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Dispose();
if (client != null)
client.Close();
}
} }
} }
\ No newline at end of file
...@@ -9,9 +9,11 @@ using System.Net.Security; ...@@ -9,9 +9,11 @@ using System.Net.Security;
using System.Threading; using System.Threading;
using System.Security.Authentication; using System.Security.Authentication;
using System.Diagnostics; using System.Diagnostics;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -20,7 +22,6 @@ namespace Titanium.Web.Proxy ...@@ -20,7 +22,6 @@ 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(IAsyncResult asynchronousResult) private static void HandleHttpSessionResponse(IAsyncResult asynchronousResult)
{ {
SessionEventArgs args = (SessionEventArgs)asynchronousResult.AsyncState; SessionEventArgs args = (SessionEventArgs)asynchronousResult.AsyncState;
try try
...@@ -37,63 +38,55 @@ namespace Titanium.Web.Proxy ...@@ -37,63 +38,55 @@ namespace Titanium.Web.Proxy
{ {
if (args.serverResponse != null) if (args.serverResponse != null)
{ {
List<Tuple<String, String>> responseHeaders = ProcessResponse(args.serverResponse); args.ResponseHeaders = ReadResponseHeaders(args.serverResponse);
args.responseStream = args.serverResponse.GetResponseStream(); args.responseStream = args.serverResponse.GetResponseStream();
bool isChunked = args.serverResponse.GetResponseHeader("transfer-encoding") == null ? false : args.serverResponse.GetResponseHeader("transfer-encoding").ToLower() == "chunked" ? true : false;
if (BeforeResponse != null) if (BeforeResponse != null)
{
args.responseEncoding = args.serverResponse.GetEncoding();
BeforeResponse(null, args); BeforeResponse(null, args);
}
if (args.responseBodyRead) if (args.responseBodyRead)
{ {
bool isChunked = args.ResponseHeaders.Any(x => x.Name.ToLower() == "transfer-encoding") == false ? false : args.ResponseHeaders.First(x => x.Name.ToLower() == "transfer-encoding").Value.ToLower() == "chunked" ? true : false;
var contentEncoding = args.ResponseHeaders.FirstOrDefault(x => x.Name.ToLower() == "content-encoding");
if (contentEncoding != null)
switch (contentEncoding.Value.ToLower())
{
case "gzip":
args.responseBody = CompressionHelper.CompressGzip(args.responseBody);
break;
case "deflate":
args.responseBody = CompressionHelper.CompressDeflate(args.responseBody);
break;
case "zlib":
args.responseBody = CompressionHelper.CompressZlib(args.responseBody);
break;
default:
throw new Exception("Specified content-encoding header is not supported");
}
byte[] data; WriteResponseStatus(args.serverResponse.ProtocolVersion, args.serverResponse.StatusCode, args.serverResponse.StatusDescription, args.clientStreamWriter);
switch (args.serverResponse.ContentEncoding) WriteResponseHeaders(args.clientStreamWriter, args.ResponseHeaders, args.responseBody.Length, isChunked);
{ WriteResponseBody(args.clientStream, args.responseBody, isChunked);
case "gzip":
data = CompressionHelper.CompressGzip(args.responseBody, args.responseEncoding);
WriteResponseStatus(args.serverResponse.ProtocolVersion, args.serverResponse.StatusCode, args.serverResponse.StatusDescription, args.clientStreamWriter);
WriteResponseHeaders(args.clientStreamWriter, responseHeaders, data.Length);
SendData(args.clientStream, data, isChunked);
break;
case "deflate":
data = CompressionHelper.CompressDeflate(args.responseBody, args.responseEncoding);
WriteResponseStatus(args.serverResponse.ProtocolVersion, args.serverResponse.StatusCode, args.serverResponse.StatusDescription, args.clientStreamWriter);
WriteResponseHeaders(args.clientStreamWriter, responseHeaders, data.Length);
SendData(args.clientStream, data, isChunked);
break;
case "zlib":
data = CompressionHelper.CompressZlib(args.responseBody, args.responseEncoding);
WriteResponseStatus(args.serverResponse.ProtocolVersion, args.serverResponse.StatusCode, args.serverResponse.StatusDescription, args.clientStreamWriter);
WriteResponseHeaders(args.clientStreamWriter, responseHeaders, data.Length);
SendData(args.clientStream, data, isChunked);
break;
default:
data = EncodeData(args.responseBody, args.responseEncoding);
WriteResponseStatus(args.serverResponse.ProtocolVersion, args.serverResponse.StatusCode, args.serverResponse.StatusDescription, args.clientStreamWriter);
WriteResponseHeaders(args.clientStreamWriter, responseHeaders, data.Length);
SendData(args.clientStream, data, isChunked);
break;
}
} }
else else
{ {
WriteResponseStatus(args.serverResponse.ProtocolVersion, args.serverResponse.StatusCode, args.serverResponse.StatusDescription, args.clientStreamWriter); bool isChunked = args.serverResponse.GetResponseHeader("transfer-encoding") == null ? false : args.serverResponse.GetResponseHeader("transfer-encoding").ToLower() == "chunked" ? true : false;
WriteResponseHeaders(args.clientStreamWriter, responseHeaders);
if (isChunked) WriteResponseStatus(args.serverResponse.ProtocolVersion, args.serverResponse.StatusCode, args.serverResponse.StatusDescription, args.clientStreamWriter);
SendChunked(args.responseStream, args.clientStream); WriteResponseHeaders(args.clientStreamWriter, args.ResponseHeaders);
else WriteResponseBody(args.responseStream, args.clientStream, isChunked);
SendNormal(args.responseStream, args.clientStream);
} }
args.clientStream.Flush(); args.clientStream.Flush();
} }
} }
catch catch
{ {
...@@ -106,28 +99,30 @@ namespace Titanium.Web.Proxy ...@@ -106,28 +99,30 @@ namespace Titanium.Web.Proxy
} }
} }
private static List<Tuple<String, String>> ProcessResponse(HttpWebResponse response) private static List<HttpHeader> ReadResponseHeaders(HttpWebResponse response)
{ {
String value = null; var returnHeaders = new List<HttpHeader>();
String header = null;
List<Tuple<String, String>> returnHeaders = new List<Tuple<String, String>>(); String cookieHeaderName = null;
foreach (String s in response.Headers.Keys) String cookieHeaderValue = null;
foreach (String headerKey in response.Headers.Keys)
{ {
if (s.ToLower() == "set-cookie") if (headerKey.ToLower() == "set-cookie")
{ {
header = s; cookieHeaderName = headerKey;
value = response.Headers[s]; cookieHeaderValue = response.Headers[headerKey];
} }
else else
returnHeaders.Add(new Tuple<String, String>(s, response.Headers[s])); returnHeaders.Add(new HttpHeader(headerKey, response.Headers[headerKey]));
} }
if (!String.IsNullOrWhiteSpace(value)) if (!String.IsNullOrWhiteSpace(cookieHeaderValue))
{ {
response.Headers.Remove(header); response.Headers.Remove(cookieHeaderName);
String[] cookies = cookieSplitRegEx.Split(value); String[] cookies = cookieSplitRegEx.Split(cookieHeaderValue);
foreach (String cookie in cookies) foreach (String cookie in cookies)
returnHeaders.Add(new Tuple<String, String>("Set-Cookie", cookie)); returnHeaders.Add(new HttpHeader("Set-Cookie", cookie));
} }
...@@ -140,13 +135,13 @@ namespace Titanium.Web.Proxy ...@@ -140,13 +135,13 @@ namespace Titanium.Web.Proxy
responseWriter.WriteLine(s); responseWriter.WriteLine(s);
} }
private static void WriteResponseHeaders(StreamWriter responseWriter, List<Tuple<String, String>> headers) private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers)
{ {
if (headers != null) if (headers != null)
{ {
foreach (Tuple<String, String> header in headers) foreach (var header in headers)
{ {
responseWriter.WriteLine(String.Format("{0}: {1}", header.Item1, header.Item2)); responseWriter.WriteLine(header.ToString());
} }
} }
...@@ -154,17 +149,24 @@ namespace Titanium.Web.Proxy ...@@ -154,17 +149,24 @@ namespace Titanium.Web.Proxy
responseWriter.Flush(); responseWriter.Flush();
} }
private static void WriteResponseHeaders(StreamWriter responseWriter, List<Tuple<String, String>> headers, int length) private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers, int length, bool isChunked)
{ {
if (headers != null) if (!isChunked)
{ {
if (headers.Any(x => x.Name.ToLower() == "content-length") == false)
{
headers.Add(new HttpHeader("Content-Length", length.ToString()));
}
}
foreach (Tuple<String, String> header in headers) if (headers != null)
{
foreach (var header in headers)
{ {
if (header.Item1.ToLower() != "content-length") if (!isChunked && header.Name.ToLower() == "content-length")
responseWriter.WriteLine(String.Format("{0}: {1}", header.Item1, header.Item2)); header.Value = length.ToString();
else
responseWriter.WriteLine(String.Format("{0}: {1}", "content-length", length.ToString())); responseWriter.WriteLine(header.ToString());
} }
} }
...@@ -172,72 +174,84 @@ namespace Titanium.Web.Proxy ...@@ -172,72 +174,84 @@ namespace Titanium.Web.Proxy
responseWriter.Flush(); responseWriter.Flush();
} }
private static void SendNormal(Stream inStream, Stream outStream)
{
Byte[] buffer = new Byte[BUFFER_SIZE]; private static void WriteResponseBody(Stream clientStream, byte[] data, bool isChunked)
{
int bytesRead; if (!isChunked)
while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0)
{ {
outStream.Write(buffer, 0, bytesRead); clientStream.Write(data, 0, data.Length);
} }
else
WriteResponseBodyChunked(data, clientStream);
}
private static void WriteResponseBody(Stream inStream, Stream outStream, bool isChunked)
{
if (!isChunked)
{
Byte[] buffer = new Byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0)
{
outStream.Write(buffer, 0, bytesRead);
}
}
else
WriteResponseBodyChunked(inStream, outStream);
} }
//Send chunked response //Send chunked response
private static void SendChunked(Stream inStream, Stream outStream) private static void WriteResponseBodyChunked(Stream inStream, Stream outStream)
{ {
Byte[] buffer = new Byte[BUFFER_SIZE]; Byte[] buffer = new Byte[BUFFER_SIZE];
var chunkTrail = Encoding.ASCII.GetBytes(Environment.NewLine);
int bytesRead; int bytesRead;
while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0) while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0)
{ {
var chunkHead = Encoding.ASCII.GetBytes(bytesRead.ToString("x2")); var chunkHead = Encoding.ASCII.GetBytes(bytesRead.ToString("x2"));
outStream.Write(chunkHead, 0, chunkHead.Length); outStream.Write(chunkHead, 0, chunkHead.Length);
outStream.Write(chunkTrail, 0, chunkTrail.Length); outStream.Write(chunkTrail, 0, chunkTrail.Length);
outStream.Write(buffer, 0, bytesRead); outStream.Write(buffer, 0, bytesRead);
outStream.Write(chunkTrail, 0, chunkTrail.Length); outStream.Write(chunkTrail, 0, chunkTrail.Length);
} }
var ChunkEnd = Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine);
outStream.Write(ChunkEnd, 0, ChunkEnd.Length); outStream.Write(ChunkEnd, 0, ChunkEnd.Length);
} }
private static void SendChunked(byte[] data, Stream outStream) private static void WriteResponseBodyChunked(byte[] data, Stream outStream)
{ {
Byte[] buffer = new Byte[BUFFER_SIZE]; Byte[] buffer = new Byte[BUFFER_SIZE];
var chunkTrail = Encoding.ASCII.GetBytes(Environment.NewLine);
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(chunkTrail, 0, chunkTrail.Length);
outStream.Write(data, 0, data.Length); outStream.Write(data, 0, data.Length);
outStream.Write(chunkTrail, 0, chunkTrail.Length); outStream.Write(chunkTrail, 0, chunkTrail.Length);
var ChunkEnd = Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine);
outStream.Write(ChunkEnd, 0, ChunkEnd.Length); outStream.Write(ChunkEnd, 0, ChunkEnd.Length);
} }
private static byte[] EncodeData(string responseData, Encoding e)
{
return e.GetBytes(responseData);
}
private static void SendData(Stream outStream, byte[] data, bool isChunked) private static void Dispose(TcpClient client, Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, SessionEventArgs args)
{ {
if (!isChunked) if (args != null)
{ args.Dispose();
outStream.Write(data, 0, data.Length);
} if (clientStreamReader != null)
else clientStreamReader.Dispose();
SendChunked(data, outStream);
}
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Dispose();
if (client != null)
client.Close();
}
} }
} }
\ No newline at end of file
...@@ -77,10 +77,12 @@ ...@@ -77,10 +77,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Exceptions\BodyNotFoundException.cs" /> <Compile Include="Exceptions\BodyNotFoundException.cs" />
<Compile Include="Extensions\HttpWebResponseExtensions.cs" />
<Compile Include="Extensions\HttpWebRequestExtensions.cs" /> <Compile Include="Extensions\HttpWebRequestExtensions.cs" />
<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="Models\HttpHeader.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" />
...@@ -88,7 +90,7 @@ ...@@ -88,7 +90,7 @@
<Compile Include="Helpers\NetFramework.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="Models\SessionEventArgs.cs" /> <Compile Include="EventArgs\SessionEventArgs.cs" />
<Compile Include="Helpers\Tcp.cs" /> <Compile Include="Helpers\Tcp.cs" />
<Compile Include="Extensions\StreamExtensions.cs" /> <Compile Include="Extensions\StreamExtensions.cs" />
</ItemGroup> </ItemGroup>
......
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