Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Commit af254c3

Browse files
committed
Merge pull request #73 from justcoding121/release
External Proxy, Async & Server Certificate Validation
2 parents aeb9f31 + e1cd5fc commit af254c3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1528
-1217
lines changed

Titanium.Web.Proxy.Test/Program.cs renamed to Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using System.Runtime.InteropServices;
33

4-
namespace Titanium.Web.Proxy.Test
4+
namespace Titanium.Web.Proxy.Examples.Basic
55
{
66
public class Program
77
{

Titanium.Web.Proxy.Test/ProxyTestController.cs renamed to Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs

+49-35
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
using System.Collections.Generic;
33
using System.Net;
44
using System.Text.RegularExpressions;
5+
using System.Threading.Tasks;
56
using Titanium.Web.Proxy.EventArguments;
67
using Titanium.Web.Proxy.Models;
78

8-
namespace Titanium.Web.Proxy.Test
9+
namespace Titanium.Web.Proxy.Examples.Basic
910
{
1011
public class ProxyTestController
1112
{
1213
public void StartProxy()
1314
{
1415
ProxyServer.BeforeRequest += OnRequest;
1516
ProxyServer.BeforeResponse += OnResponse;
17+
ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
1618

1719
//Exclude Https addresses you don't want to proxy
1820
//Usefull for clients that use certificate pinning
@@ -34,13 +36,14 @@ public void StartProxy()
3436
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests
3537
//In this example only google.com will work for HTTPS requests
3638
//Other sites will receive a certificate mismatch warning on browser
37-
//Please read about it before asking questions!
3839
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true)
3940
{
4041
GenericCertificateName = "google.com"
4142
};
4243
ProxyServer.AddEndPoint(transparentEndPoint);
4344

45+
//ProxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
46+
//ProxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
4447

4548
foreach (var endPoint in ProxyServer.ProxyEndPoints)
4649
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
@@ -59,69 +62,80 @@ public void Stop()
5962
ProxyServer.Stop();
6063
}
6164

62-
//Test On Request, intecept requests
63-
//Read browser URL send back to proxy by the injection script in OnResponse event
64-
public void OnRequest(object sender, SessionEventArgs e)
65+
//intecept & cancel, redirect or update requests
66+
public async Task OnRequest(object sender, SessionEventArgs e)
6567
{
66-
Console.WriteLine(e.ProxySession.Request.Url);
68+
Console.WriteLine(e.WebSession.Request.Url);
6769

68-
//read request headers
69-
var requestHeaders = e.ProxySession.Request.RequestHeaders;
70+
////read request headers
71+
var requestHeaders = e.WebSession.Request.RequestHeaders;
7072

71-
if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT"))
73+
if ((e.WebSession.Request.Method.ToUpper() == "POST" || e.WebSession.Request.Method.ToUpper() == "PUT"))
7274
{
7375
//Get/Set request body bytes
74-
byte[] bodyBytes = e.GetRequestBody();
75-
e.SetRequestBody(bodyBytes);
76+
byte[] bodyBytes = await e.GetRequestBody();
77+
await e.SetRequestBody(bodyBytes);
7678

7779
//Get/Set request body as string
78-
string bodyString = e.GetRequestBodyAsString();
79-
e.SetRequestBodyString(bodyString);
80+
string bodyString = await e.GetRequestBodyAsString();
81+
await e.SetRequestBodyString(bodyString);
8082

8183
}
8284

8385
//To cancel a request with a custom HTML content
8486
//Filter URL
85-
if (e.ProxySession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
87+
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
8688
{
87-
e.Ok("<!DOCTYPE html>" +
88-
"<html><body><h1>" +
89-
"Website Blocked" +
90-
"</h1>" +
91-
"<p>Blocked by titanium web proxy.</p>" +
92-
"</body>" +
93-
"</html>");
89+
await e.Ok("<!DOCTYPE html>" +
90+
"<html><body><h1>" +
91+
"Website Blocked" +
92+
"</h1>" +
93+
"<p>Blocked by titanium web proxy.</p>" +
94+
"</body>" +
95+
"</html>");
9496
}
9597
//Redirect example
96-
if (e.ProxySession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
98+
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
9799
{
98-
e.Redirect("https://www.paypal.com");
100+
await e.Redirect("https://www.paypal.com");
99101
}
100102
}
101103

102-
//Test script injection
103-
//Insert script to read the Browser URL and send it back to proxy
104-
public void OnResponse(object sender, SessionEventArgs e)
104+
//Modify response
105+
public async Task OnResponse(object sender, SessionEventArgs e)
105106
{
106-
107107
//read response headers
108-
var responseHeaders = e.ProxySession.Response.ResponseHeaders;
108+
var responseHeaders = e.WebSession.Response.ResponseHeaders;
109109

110110
//if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
111-
if (e.RequestMethod == "GET" || e.RequestMethod == "POST")
111+
if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST")
112112
{
113-
if (e.ProxySession.Response.ResponseStatusCode == "200")
113+
if (e.WebSession.Response.ResponseStatusCode == "200")
114114
{
115-
if (e.ProxySession.Response.ContentType.Trim().ToLower().Contains("text/html"))
115+
if (e.WebSession.Response.ContentType!=null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
116116
{
117-
byte[] bodyBytes = e.GetResponseBody();
118-
e.SetResponseBody(bodyBytes);
117+
byte[] bodyBytes = await e.GetResponseBody();
118+
await e.SetResponseBody(bodyBytes);
119119

120-
string body = e.GetResponseBodyAsString();
121-
e.SetResponseBodyString(body);
120+
string body = await e.GetResponseBodyAsString();
121+
await e.SetResponseBodyString(body);
122122
}
123123
}
124124
}
125125
}
126+
127+
/// <summary>
128+
/// Allows overriding default certificate validation logic
129+
/// </summary>
130+
/// <param name="sender"></param>
131+
/// <param name="e"></param>
132+
public async Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
133+
{
134+
//set IsValid to true/false based on Certificate Errors
135+
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
136+
e.IsValid = true;
137+
else
138+
await e.Session.Ok("Cannot validate server certificate! Not safe to proceed.");
139+
}
126140
}
127141
}

Titanium.Web.Proxy.Test/Titanium.Web.Proxy.Test.csproj renamed to Examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
<ProjectGuid>{F3B7E553-1904-4E80-BDC7-212342B5C952}</ProjectGuid>
99
<OutputType>Exe</OutputType>
1010
<AppDesignerFolder>Properties</AppDesignerFolder>
11-
<RootNamespace>Titanium.Web.Proxy.Test</RootNamespace>
12-
<AssemblyName>Titanium.Web.Proxy.Test</AssemblyName>
11+
<RootNamespace>Titanium.Web.Proxy.Examples.Basic</RootNamespace>
12+
<AssemblyName>Titanium.Web.Proxy.Examples.Basic</AssemblyName>
1313
<FileAlignment>512</FileAlignment>
1414
<TargetFrameworkProfile />
1515
</PropertyGroup>
@@ -65,7 +65,7 @@
6565
<None Include="App.config" />
6666
</ItemGroup>
6767
<ItemGroup>
68-
<ProjectReference Include="..\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj">
68+
<ProjectReference Include="..\..\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj">
6969
<Project>{8d73a1be-868c-42d2-9ece-f32cc1a02906}</Project>
7070
<Name>Titanium.Web.Proxy</Name>
7171
</ProjectReference>

README.md

+93-60
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ A light weight http(s) proxy server written in C#
66

77
Kindly report only issues/bugs here . For programming help or questions use [StackOverflow](http://stackoverflow.com/questions/tagged/titanium-web-proxy) with the tag Titanium-Web-Proxy.
88

9-
![alt tag](https://raw.githubusercontent.com/titanium007/Titanium/master/Titanium.Web.Proxy.Test/Capture.PNG)
9+
![alt tag](https://raw.githubusercontent.com/justcoding121/Titanium-Web-Proxy/release/Examples/Titanium.Web.Proxy.Examples.Basic/Capture.PNG)
1010

1111
Features
1212
========
@@ -32,102 +32,135 @@ After installing nuget package mark following files to be copied to app director
3232
Setup HTTP proxy:
3333

3434
```csharp
35-
// listen to client request & server response events
36-
ProxyServer.BeforeRequest += OnRequest;
37-
ProxyServer.BeforeResponse += OnResponse;
38-
39-
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true){
40-
//Exclude Https addresses you don't want to proxy/cannot be proxied
41-
//for example exclude dropbox client which use certificate pinning
42-
ExcludedHttpsHostNameRegex = new List<string>() { "dropbox.com" }
43-
};
44-
45-
//Add an explicit endpoint where the client is aware of the proxy
46-
//So client would send request in a proxy friendly manner
47-
ProxyServer.AddEndPoint(explicitEndPoint);
48-
ProxyServer.Start();
49-
50-
//Only explicit proxies can be set as a system proxy!
51-
ProxyServer.SetAsSystemHttpProxy(explicitEndPoint);
52-
ProxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
53-
54-
foreach (var endPoint in ProxyServer.ProxyEndPoints)
55-
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
56-
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
35+
ProxyServer.BeforeRequest += OnRequest;
36+
ProxyServer.BeforeResponse += OnResponse;
37+
ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
38+
39+
//Exclude Https addresses you don't want to proxy
40+
//Usefull for clients that use certificate pinning
41+
//for example dropbox.com
42+
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
43+
{
44+
// ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }
45+
};
46+
47+
//An explicit endpoint is where the client knows about the existance of a proxy
48+
//So client sends request in a proxy friendly manner
49+
ProxyServer.AddEndPoint(explicitEndPoint);
50+
ProxyServer.Start();
51+
52+
53+
//Transparent endpoint is usefull for reverse proxying (client is not aware of the existance of proxy)
54+
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint
55+
//Currently do not support Server Name Indication (It is not currently supported by SslStream class)
56+
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests
57+
//In this example only google.com will work for HTTPS requests
58+
//Other sites will receive a certificate mismatch warning on browser
59+
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true)
60+
{
61+
GenericCertificateName = "google.com"
62+
};
63+
ProxyServer.AddEndPoint(transparentEndPoint);
5764

58-
//wait here (You can use something else as a wait function, I am using this as a demo)
59-
Console.Read();
65+
//ProxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
66+
//ProxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
67+
68+
foreach (var endPoint in ProxyServer.ProxyEndPoints)
69+
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
70+
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
71+
72+
//Only explicit proxies can be set as system proxy!
73+
ProxyServer.SetAsSystemHttpProxy(explicitEndPoint);
74+
ProxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
75+
76+
//wait here (You can use something else as a wait function, I am using this as a demo)
77+
Console.Read();
6078

61-
//Unsubscribe & Quit
62-
ProxyServer.BeforeRequest -= OnRequest;
63-
ProxyServer.BeforeResponse -= OnResponse;
64-
ProxyServer.Stop();
79+
//Unsubscribe & Quit
80+
ProxyServer.BeforeRequest -= OnRequest;
81+
ProxyServer.BeforeResponse -= OnResponse;
82+
ProxyServer.Stop();
6583

6684
```
6785
Sample request and response event handlers
6886

6987
```csharp
7088

71-
//Test On Request, intecept requests
72-
//Read browser URL send back to proxy by the injection script in OnResponse event
73-
public void OnRequest(object sender, SessionEventArgs e)
89+
//intecept & cancel, redirect or update requests
90+
public async Task OnRequest(object sender, SessionEventArgs e)
7491
{
75-
Console.WriteLine(e.ProxySession.Request.Url);
92+
Console.WriteLine(e.WebSession.Request.Url);
7693

77-
//read request headers
78-
var requestHeaders = e.ProxySession.Request.RequestHeaders;
94+
////read request headers
95+
var requestHeaders = e.WebSession.Request.RequestHeaders;
7996

80-
if ((e.RequestMethod.ToUpper() == "POST" || e.RequestMethod.ToUpper() == "PUT"))
97+
if ((e.WebSession.Request.Method.ToUpper() == "POST" || e.WebSession.Request.Method.ToUpper() == "PUT"))
8198
{
8299
//Get/Set request body bytes
83-
byte[] bodyBytes = e.GetRequestBody();
84-
e.SetRequestBody(bodyBytes);
100+
byte[] bodyBytes = await e.GetRequestBody();
101+
await e.SetRequestBody(bodyBytes);
85102

86103
//Get/Set request body as string
87-
string bodyString = e.GetRequestBodyAsString();
88-
e.SetRequestBodyString(bodyString);
104+
string bodyString = await e.GetRequestBodyAsString();
105+
await e.SetRequestBodyString(bodyString);
89106

90107
}
91108

92109
//To cancel a request with a custom HTML content
93110
//Filter URL
94-
95-
if (e.ProxySession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
111+
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
96112
{
97-
e.Ok("<!DOCTYPE html>"+
98-
"<html><body><h1>"+
99-
"Website Blocked"+
100-
"</h1>"+
101-
"<p>Blocked by titanium web proxy.</p>"+
102-
"</body>"+
103-
"</html>");
113+
await e.Ok("<!DOCTYPE html>" +
114+
"<html><body><h1>" +
115+
"Website Blocked" +
116+
"</h1>" +
117+
"<p>Blocked by titanium web proxy.</p>" +
118+
"</body>" +
119+
"</html>");
120+
}
121+
//Redirect example
122+
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
123+
{
124+
await e.Redirect("https://www.paypal.com");
104125
}
105126
}
106127

107-
//Test script injection
108-
//Insert script to read the Browser URL and send it back to proxy
109-
public void OnResponse(object sender, SessionEventArgs e)
128+
//Modify response
129+
public async Task OnResponse(object sender, SessionEventArgs e)
110130
{
111-
112131
//read response headers
113-
var responseHeaders = e.ProxySession.Response.ResponseHeaders;
114-
132+
var responseHeaders = e.WebSession.Response.ResponseHeaders;
115133

116-
if (e.RequestMethod == "GET" || e.RequestMethod == "POST")
134+
//if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
135+
if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST")
117136
{
118-
if (e.ProxySession.Response.ResponseStatusCode == "200")
137+
if (e.WebSession.Response.ResponseStatusCode == "200")
119138
{
120-
if (e.ProxySession.Response.ContentType.Trim().ToLower().Contains("text/html"))
139+
if (e.WebSession.Response.ContentType!=null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
121140
{
122-
string body = e.GetResponseBodyAsString();
141+
byte[] bodyBytes = await e.GetResponseBody();
142+
await e.SetResponseBody(bodyBytes);
143+
144+
string body = await e.GetResponseBodyAsString();
145+
await e.SetResponseBodyString(body);
123146
}
124147
}
125148
}
126149
}
150+
151+
152+
/// Allows overriding default certificate validation logic
153+
public async Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
154+
{
155+
//set IsValid to true/false based on Certificate Errors
156+
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
157+
e.IsValid = true;
158+
else
159+
await e.Session.Ok("Cannot validate server certificate! Not safe to proceed.");
160+
}
127161
```
128162
Future roadmap
129163
============
130-
* Add callbacks for client/server certificate validation/selection
131164
* Support mutual authentication
132165
* Support Server Name Indication (SNI) for transparent endpoints
133166
* Support HTTP 2.0

Titanium.Web.Proxy.Test/Titanium.Web.Proxy.Test.csproj.user

-6
This file was deleted.

0 commit comments

Comments
 (0)