Click here to Skip to main content
14,382,380 members

Implementing a Multithreaded HTTP/HTTPS Debugging Proxy Server in C#

Rate this:
4.86 (33 votes)
Please Sign up or sign in to vote.
4.86 (33 votes)
3 Feb 2011Ms-PL
A complete proxy server except instead of SSL Tunneling, will perform a "man-in-the-middle" decryption on SSL traffic allowing you to inspect the encrypted traffic


This article will show you how to implement a multithreaded HTTP proxy server in C# with a non-standard proxy server feature of terminating and then proxying HTTPS traffic. I've added a simple caching mechanism, and have simplified the code by ignoring http/1.1 requests for keeping connections alive, etc.

Disclaimer: Understand that this code is for debugging and testing purposes only. The author does not intend for this code or the executable to be used in any way that may compromise someone's sensitive information. Do not use this server in any environment which has users that are unaware of its use. By using this code or the executable found in this article, you are taking responsibility for the data which may be collected through its use.


If you are familiar with fiddler, then you already know how this proxy server works. It essentially performs a "man-in-the-middle" on the HTTP client to dump and debug HTTP traffic. The System.Net.Security.SslStream class is utilized to handle all the heavy lifting.

Using the Code

The most important part about this code is that when the client asks for a CONNECT, instead of just passing TCP traffic, we're going to handle an SSL handshake and establish an SSL session and receive a request from the client. In the mean time, we'll send the same request to the destination HTTPS server.

First, let's look at creating a server that can handle multiple concurrent TCP connections. We'll use the System.Threading.Thread object to start listening for connections in a separate thread. This thread's job will be to listen for incoming connections, and then spawn a new thread to handle processing, thus allowing the listening thread to continue listening for new connections without blocking while one client is processed.

public sealed class ProxyServer
   private TcpListener _listener;
   private Thread _listenerThread;

   public void Start()
      _listener = new TcpListener(IPAddress.Loopback, 8888);
      _listenerThread = new Thread(new ParameterizedThreadStart(Listen));
   public void Stop()
      //stop listening for incoming connections
      //wait for server to finish processing current connections...

   private static void Listen(Object obj)
      TcpListener listener = (TcpListener)obj;
         while (true)
            TcpClient client = listener.AcceptTcpClient();
            while (!ThreadPool.QueueUserWorkItem
		(new WaitCallback(ProxyServer.ProcessClient), client)) ;
      catch (ThreadAbortException) { }
      catch (SocketException) { }

   private static void ProcessClient(Object obj)
      TcpClient client = (TcpClient)obj;
         //do your processing here
      catch(Exception ex)
         //handle exception

And that's the beginning of the code to handle concurrent TCP clients in a multithreaded manner. Nothing really special there. The interesting bit is when we use SslStream to act as an HTTPS server and "trick" the client into believing it's talking to the destination server. Note that the browser should not actually be tricked because of the SSL certificate chain, but depending on their browser, it may or may not be apparent that the server's identity is in question.

Now let's take a look at the actual processing of the SSL request. Assume that we are somewhere inside the try block of the ProcessClient method shown above.

//read the first line HTTP command
Stream clientStream = client.GetStream();
StreamReader clientStreamReader = new StreamReader(clientStream);
String httpCmd = clientStreamReader.ReadLine();

//break up the line into three components
String[] splitBuffer = httpCmd.Split(spaceSplit, 3);
String method = splitBuffer[0];
String remoteUri = splitBuffer[1];
Version version = new Version(1, 0); //force everything to HTTP/1.0

//this will be the web request issued on behalf of the client
HttpWebRequest webReq;

if (method == "CONNECT")
   //Browser wants to create a secure tunnel
   //instead = we are going to perform a man in the middle
   //the user's browser should warn them of the certification errors however.
   //you are responsible for the use of this code
   //this is the URI we'll request on behalf of the client
   remoteUri = "https://" + splitBuffer[1];
   //read and ignore headers
   while (!String.IsNullOrEmpty(clientStreamReader.ReadLine())) ;

   //tell the client that a tunnel has been established
   StreamWriter connectStreamWriter = new StreamWriter(clientStream);
   connectStreamWriter.WriteLine("HTTP/1.0 200 Connection established");
	(String.Format("Timestamp: {0}", DateTime.Now.ToString()));
   connectStreamWriter.WriteLine("Proxy-agent: matt-dot-net");

   //now-create an https "server"
   sslStream = new SslStream(clientStream, false);
	false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, true);

   //HTTPS server created - we can now decrypt the client's traffic

Points of Interest

You can see that I have an X509Certificate2 _certificate defined elsewhere in the code. To get a certificate for this, you need to use a tool like makecert.exe to create a self-signed certificate. I found makecert.exe in the Windows SDK and it also comes with Fiddler. I have included a certificate file in the source files to allow the server to run, but because it does not include the private key, to actually handle SSL traffic, You will need to run makecert.exe

Here is the syntax I used for makecert.exe:

makecert.exe cert.cer -a sha1 -n "CN=matt-dot-net" -sr LocalMachine -ss My -sky signature -pe -len 2048

One thing to note is that on your HttpWebRequest object, you will need to set the Proxy property to null if you are using Windows internet options to specify using your proxy server. This is because your HttpWebRequest will default to the Windows internet settings and you'll have a proxy server that is trying to use itself as a proxy server!

Another interesting and frustrating hang-up was the handling of cookies. When I thought that I was processing requests/responses perfectly, I found that I could not maintain state with any websites because cookies were not properly being sent by the client. I determined (from using fiddler and firefox live HTTP headers) that cookies needed to be set individually. The server was returning several cookies in one Set-Cookie header, but I needed to parse them out and return an individual Set-Cookie header for each one. I haven't researched to find out why this is, or what is the proper handling of cookies in HTTP, but all I can assume was that when a browser is set to use a proxy server, it expects the proxy server to process cookies into individual Set-Cookie headers and if not configured to use a proxy, the browser does this itself.


I threw together this code on a Sunday afternoon, so I expect it to have errors and problems. Also, I make no claim that this is properly handling errors, cleaning up objects, or is in any way the best way to do anything.


This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


About the Author

Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

Questiondisk caching Pin
Vip3r01111-Mar-18 9:39
memberVip3r01111-Mar-18 9:39 
Questionhttps sites dont load? Pin
aim199523-Feb-15 12:02
memberaim199523-Feb-15 12:02 
QuestionProxy Server on a network Pin
L4NGERS28-Nov-14 6:53
memberL4NGERS28-Nov-14 6:53 
QuestionBypass HTTPS Pin
anadoluharamisi15-Jul-14 11:22
memberanadoluharamisi15-Jul-14 11:22 
AnswerRe: Bypass HTTPS Pin
GAJERA22-Jul-14 23:29
memberGAJERA22-Jul-14 23:29 
GeneralRe: Bypass HTTPS Pin
anadoluharamisi23-Jul-14 1:28
memberanadoluharamisi23-Jul-14 1:28 
GeneralRe: Bypass HTTPS Pin
GAJERA23-Jul-14 1:38
memberGAJERA23-Jul-14 1:38 
QuestionWhy proxy not working NTML negotiate authentication? Pin
GAJERA8-May-14 2:29
memberGAJERA8-May-14 2:29 
AnswerRe: Why proxy not working NTML negotiate authentication? Pin
GAJERA23-Jul-14 0:20
memberGAJERA23-Jul-14 0:20 
Questionwonderfull!! Pin
blaireau201129-Apr-14 12:19
memberblaireau201129-Apr-14 12:19 
GeneralVery usefull!! Pin
Efams25-Feb-14 8:48
memberEfams25-Feb-14 8:48 
QuestionProper SSL handling (Tutorial) Pin
Vahid_N12-Oct-13 11:00
memberVahid_N12-Oct-13 11:00 
I think this article is base of the a lot of Proxy Server or clients. Thanks for sharing it.
To bypass certificate warnings shown on any browser you need to create the proper certificates:
- run this command to create a root certificate
makecert.exe -r -ss my -n "CN=MyCustomRoot, O=myCert, OU=Created by me" -sky signature -eku -h 1 -cy authority -a sha1 -m 120 -b 09/05/2011 sig.cer

- install it as a trusted certificate (run it as admin)
var fileName = "sig.cer";
var cert = new X509Certificate2(fileName);
var store = new X509Store(StoreName.My, StoreName.Root);
    var contentType = X509Certificate2.GetCertContentType(fileName);
    var pfx = cert.Export(contentType);
    cert = new X509Certificate2(pfx, (string)null, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);

- Now we will need to create a new certificate per each HTTPS site this way:
makecert.exe -pe -ss my -n ", O=myCert, OU=Created by me" -sky exchange -in MyCustomRoot -is my -eku -cy end -a sha1 -m 132 -b 10/08/2011 site.cer

it's important to change the
for every single HTTPS site. otherwise you will get the certificate warnings. This certificate is created for SSL handling (its eku part is properly assigned) and also it's linked/chained to our registered root certificate.
- Now use this new site.cer file in the
part of the codes. so this _certificate should be changed per each HTTPS host.
AnswerRe: Proper SSL handling (Tutorial) Pin
Nigel Shaw6-Nov-13 4:39
memberNigel Shaw6-Nov-13 4:39 
AnswerRe: Proper SSL handling (Tutorial) Pin
GAJERA23-Jul-14 0:14
memberGAJERA23-Jul-14 0:14 
QuestionHow can I bypass the certificate warnings shown on the IE or any browser? Pin
beinblack18-Jun-13 0:42
memberbeinblack18-Jun-13 0:42 
AnswerRe: How can I bypass the certificate warnings shown on the IE or any browser? Pin
GAJERA25-Jun-13 0:05
memberGAJERA25-Jun-13 0:05 
QuestionWhy Image are not upload? Pin
GAJERA22-May-13 5:44
memberGAJERA22-May-13 5:44 
AnswerRe: Why Image are not upload? Pin
GAJERA23-Jul-14 1:39
memberGAJERA23-Jul-14 1:39 
QuestionHow to use HTTP/1.1 ? Pin
GAJERA3-May-13 23:55
memberGAJERA3-May-13 23:55 
QuestionSave encoded certificate to store failed => 0x5 (5) Pin
para para20-Mar-13 13:15
memberpara para20-Mar-13 13:15 
AnswerRe: Save encoded certificate to store failed => 0x5 (5) Pin
para para21-Mar-13 12:33
memberpara para21-Mar-13 12:33 
QuestionCode does not work for HTTPS Pin
aditya_bokade5-Mar-13 1:13
memberaditya_bokade5-Mar-13 1:13 
QuestionMultiple cookies in a response Pin
Nir Asis31-Jan-13 6:14
memberNir Asis31-Jan-13 6:14 
QuestionProblem with Https Pin
Nnamani Uchenna19-Nov-12 23:23
memberNnamani Uchenna19-Nov-12 23:23 
AnswerRe: Problem with Https Pin
matt-dot-net20-Nov-12 9:37
membermatt-dot-net20-Nov-12 9:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Posted 12 Jul 2010

Tagged as


105 bookmarked