Skip to content

Instantly share code, notes, and snippets.

@saga
Forked from ToJans/proxy specs.cs
Created November 5, 2013 15:14
Show Gist options
  • Save saga/7320528 to your computer and use it in GitHub Desktop.
Save saga/7320528 to your computer and use it in GitHub Desktop.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
using System;
using System.Net;
using System.Net.Security;
namespace NetVCR.Specs
{
[TestClass]
public class Verify_basic_proxy_functionality
{
WebClient WC;
Proxy SUT;
Uri uri;
const string faked_url = "http://proxy/ping";
const string faked_response_body = "pong";
const string missing_url = "http://proxy/pang";
const string replay_http_url = "http://httpbin.org/ip";
const string replay_https_url = "https://httpbin.org/ip";
const string word_occuring_in_replay_response = "origin";
[TestInitialize]
public void Init()
{
SUT = new Proxy();
SUT.FakeResponse(Url: faked_url, ResponseBody: faked_response_body);
uri = SUT.Startup(9100, IsRecording: false);
WC = new WebClient(){ Proxy = new WebProxy(uri) };
}
[TestMethod]
public void Faked_url_should_return_faked_response()
{
WC.DownloadString(faked_url).ShouldBe(faked_response_body);
}
[TestMethod]
[ExpectedException(typeof(WebException))]
public void Missing_url_should_return_error()
{
WC.DownloadString(missing_url);
}
[TestMethod]
public void Http_reverse_proxy_should_work()
{
SUT.IsRecording = true;
var initial_result = WC.DownloadString(replay_http_url);
initial_result.ShouldContain(word_occuring_in_replay_response);
SUT.IsRecording = false;
var result = WC.DownloadString(replay_http_url);
result.ShouldBe(initial_result);
}
[TestMethod]
public void Https_recording_should_use_the_Fiddler_cert_as_root()
{
string expected_certificate_Issuer = "CN=DO_NOT_TRUST_FiddlerRoot, O=DO_NOT_TRUST, OU=Created by http://www.fiddler2.com";
string expected_certificate_Subject = "CN=httpbin.org, O=DO_NOT_TRUST, OU=Created by http://www.fiddler2.com";
string certificate_issuer = null;
string certificate_subject = null;
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslerrors) => {
certificate_issuer = certificate.Issuer;
certificate_subject = certificate.Subject;
return sslerrors == SslPolicyErrors.None;
};
SUT.IsRecording = true;
var initial_result = WC.DownloadString(replay_https_url);
certificate_issuer.ShouldBe(expected_certificate_Issuer);
certificate_subject.ShouldBe(expected_certificate_Subject);
}
[TestMethod]
public void Https_reverse_proxy_should_work()
{
SUT.IsRecording = true;
var initial_result = WC.DownloadString(replay_https_url);
initial_result.ShouldContain(word_occuring_in_replay_response);
SUT.IsRecording = false;
var result = WC.DownloadString(replay_https_url);
result.ShouldBe(initial_result);
}
[TestCleanup]
public void Done()
{
SUT.Shutdown();
}
}
}
using Fiddler;
using NetVCR.FiddlerSession;
using System;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
namespace NetVCR
{
public class Proxy : IDisposable
{
ISessionRepository _Repo;
SessionFilter _RequestFilter;
SessionField _MatchingRequestFields;
public bool IsRecording { get; set; }
private Uri _proxyUri;
string fnamecertificate = "FiddlerRoot.cer";
private bool StartupFailed = false;
object Lock = new object();
public Proxy(SessionFilter Filter = null, SessionField MatchingRequestFields = SessionField.FullUrl, ISessionRepository Repo = null)
{
this._RequestFilter = Filter ?? SessionFilter.AllowAll;
this._MatchingRequestFields = MatchingRequestFields;
this._Repo = Repo ?? new SessionRepository();
}
public void FakeResponse(Action<Fiddler.Session> oSessionAction = null,
string Url = "http://bogus.com", string Method = "GET", string RequestBody = "",
int ResponseCode = 200, string ResponseBody = null, string ResponseHeaders = null)
{
var oSession = BuildSession(Method, Url, RequestBody);
oSession.utilCreateResponseAndBypassServer();
oSession.responseCode = ResponseCode;
if (RequestBody != null) oSession.utilSetRequestBody(RequestBody);
if (ResponseHeaders != null) oSession.oResponse.headers.AssignFromString(ResponseHeaders);
if (ResponseBody != null) oSession.utilSetResponseBody(ResponseBody);
if (Url != null) oSession.fullUrl = Url;
if (oSessionAction != null) oSessionAction(oSession);
InsertSessionInternal(oSession);
}
void InsertSessionInternal(Fiddler.Session oSession)
{
var hashcode = oSession.GetHashCodeForRequest(_MatchingRequestFields);
if (!_Repo.ContainsKey(hashcode))
_Repo.Add(hashcode, oSession.ResponseToByteArray());
}
public Uri Startup(int port, bool? IsRecording = null)
{
StartupFailed = false;
if (Fiddler.FiddlerApplication.IsStarted())
{
Fiddler.FiddlerApplication.Shutdown();
}
while (Fiddler.FiddlerApplication.isClosing)
System.Threading.Thread.Sleep(100);
if (!IsRecording.HasValue)
this.IsRecording = _Repo.HasItems == false;
this.IsRecording = IsRecording.Value;
if (this.IsRecording)
_Repo.Truncate();
Fiddler.FiddlerApplication.OnNotification += delegate(object sender, NotificationEventArgs oNEA)
{
Console.WriteLine("** NotifyUser: " + oNEA.NotifyString);
if (oNEA.NotifyString.Contains("System.Net.Sockets.SocketException (0x80004005)"))
{
lock (this)
{
this.StartupFailed = true;
}
}
};
Fiddler.FiddlerApplication.Log.OnLogString += delegate(object sender, LogEventArgs oLEA)
{
Console.WriteLine("** LogString: " + oLEA.LogString);
};
Fiddler.FiddlerApplication.Startup(port, FiddlerCoreStartupFlags.DecryptSSL | FiddlerCoreStartupFlags.ChainToUpstreamGateway);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
while (Fiddler.FiddlerApplication.IsStarted() == false && this.StartupFailed == false)
System.Threading.Thread.Sleep(100);
if (this.StartupFailed)
{
throw new Exception("Unable to start service; probably the network address " + _proxyUri + " is in use");
}
var b = new UriBuilder("http://localhost");
b.Port = port;
this._proxyUri = b.Uri;
if (!File.Exists(fnamecertificate))
{
b.Path = fnamecertificate;
new WebClient().DownloadFile(b.Uri, fnamecertificate);
}
FiddlerApplication.oDefaultClientCertificate = X509Certificate.CreateFromCertFile(fnamecertificate);
Fiddler.FiddlerApplication.BeforeRequest += new Fiddler.SessionStateHandler(FiddlerApplication_BeforeRequest);
Fiddler.FiddlerApplication.AfterSessionComplete += new Fiddler.SessionStateHandler(FiddlerApplication_AfterSessionComplete);
return _proxyUri;
}
void FiddlerApplication_AfterSessionComplete(Fiddler.Session oSession)
{
if (oSession.oRequest.headers.HTTPMethod == "CONNECT") return;
lock (Lock)
{
if (oSession.url.StartsWith(_proxyUri.ToString())) return;
if (IsRecording)
{
InsertSessionInternal(oSession);
//Console.WriteLine(Encoding.UTF8.GetString(oSession.ResponseBody.Take(1024).ToArray()));
}
}
}
void FiddlerApplication_BeforeRequest(Fiddler.Session oSession)
{
if (oSession.oRequest.headers.HTTPMethod == "CONNECT")
{
return;
}
lock (Lock)
{
oSession.bBufferResponse = true;
oSession.utilDecodeRequest();
if (oSession.url.StartsWith(_proxyUri.ToString())) return;
if (_RequestFilter.Allows(oSession) && !IsRecording)
{
var requestHashcode = oSession.GetHashCodeForRequest(_MatchingRequestFields);
byte[] responseBytes = null;
oSession.utilCreateResponseAndBypassServer();
if (_Repo.TryGetValue(requestHashcode, out responseBytes))
{
oSession.ResponseFromByteArray(responseBytes);
}
else
{
//oSession.SetDefaultResponseHeaders();
oSession.responseCode = 500;
oSession.utilSetResponseBody("The requested URI " + oSession.GetValue(SessionField.FullUrl) + " has not been recorded by the proxy and cannot be replayed");
}
}
}
}
public void Shutdown()
{
Fiddler.FiddlerApplication.Shutdown();
Fiddler.FiddlerApplication.BeforeRequest -= new Fiddler.SessionStateHandler(FiddlerApplication_BeforeRequest);
Fiddler.FiddlerApplication.AfterSessionComplete -= new Fiddler.SessionStateHandler(FiddlerApplication_AfterSessionComplete);
//System.Threading.Thread.Sleep(750);
}
public static Fiddler.Session BuildSession(string RequestVerb, string Url, string Body)
{
var headers = new Fiddler.HTTPRequestHeaders
{
HTTPMethod = RequestVerb,
RequestPath = Url
};
var body = System.Text.Encoding.ASCII.GetBytes(Body);
return new Fiddler.Session(headers, body);
}
public void Dispose()
{
Shutdown();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment