-
Threaded Proxy?
Hi,
Thanks for reading this!
For the people who never read any of my posts I am trying to write a proxy server in c#.
I am trying to "keep it simple" and stay away from asynchronous requests, but is not possible to totally avoid threading and I think I am encountering some multi threading problems.
The idea is to have a listener socket, binded to a certain prefix. Whatever requests I get there, I append them the correct credentials and send them straight to the (atuthenticated) server on the other side; then I grabb the bytes of the response and show them in the browser. For this I am using the HttpListenerContext class.
Code:
public void Start()
{
try
{
m_listener.Start();
while (true)
{
try
{
HttpListenerContext request = m_listener.GetContext();
ThreadPool.QueueUserWorkItem(ProcessRequest, request);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.ToString());
}
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.ToString());
}
System.Diagnostics.Debug.WriteLine("Listening socket on port " + m_port);
}
The function ProcessRequest does the actual job that I wanted. (note that it does it in an asynchronous way!)
Code:
private void ProcessRequest(object listenerContext)
{
try
{
var context = (HttpListenerContext)listenerContext;
try
{
// READ ADDRESS HERE...
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
Stream instream = context.Request.InputStream;
Stream outstream = response.GetResponseStream();
//TRANSFER DATA FROM REQUEST STREAM TO RESPONSE STREAM
// Close streams
instream.Close();
outstream.Close();
context.Response.OutputStream.Close();
}
catch (Exception ex)
{
}
}
catch
{
Restart();
}
}
Normally, everything goes ok; however, whenever I have a client that does a lot of requests very quickly (almost parallely) there is a problem in line writing the output stream (with the bytes that I just grabbed from the server) and an error is thrown:
An operation was attempted on a nonexistent network connection
This is misterious; is the network connection referring to the incoming stream (the server that I am comunicating with)? if its there is no apparent problem, because I checked and the connection is still on... or is this connection referring to the output stream? if it is, there is also no apparent problem cause I checked it with the functon CanWrite and it returns true, so it is looks in good health;
If anybody has any "light" to to bring into this question I would really appreciate it, cause I googled it to exhaustion and did not come to any conclusions :pirate: :v:
Anyway, moving on: I caught the error and figure out that I need to somehow close the streams that are opened; I did not find a clean easy way to do it (cause I cannot access the streams anymore), so the "brute-force" approach was to close the actual listener and start it again (so that I can again listen for connections);
Code:
private void Restart()
{
Stop();
Start();
}
Most of the times, this is ok, but sometimes, when I enter the Start() function, there is a problem with this line:
Code:
HttpListenerContext request = m_listener.GetContext();
and an exception is thrown:
The I/O operation has been aborted because of either a thread exit or an application request
and then I am in a bit of a mess.
Apart from trying to solve this particular errors that cause my application to fail requests once in a while, I am looking at the bigger picture and wondering if the issues I am encountering are from not approaching properly the situation; am I having simultaneous access to the code and no handling properly the multithreading (I actually tried a lock() of the critical section without any improvement...) ? do I need multithreading here? or is it something else?
As I said, any help here would be much appreciated! Thanks in advance :^:
-
Re: Threaded Proxy?
So I think the summary of the situation concerning multithreading in C# is (correct me if I'm wrong):
- use asynchronous requests and let the system handle the multithreading; (1)
- use threadPool and let the system handle the multitreading (distribute tasks among worker threads); (2)
- implement "real" multi threading, handling synchronization, locks, etc; (3)
I wanted to keep it as simple as possible, without having to write a lot of "thread" of code (unless absolutely necessary), so I tried to stick to option (2), which as Luc Pattyn mentioned, has some limitations but I think I can "live" with them (that is, the app fails sometimes, I can catch the errors and carry on without major implications for the user).
One thing that I amended in my code, was removing the "restart" of the listener on the event of an exception; there is no need to stop and start the listener on error, and in fact, when I had a lot of threads running, the "restart" of the listener itself was a cause of problems...
The I/O operation has been aborted because of either a thread exit or an application request
So, after refactoring my "ProcessRequest" callback, it now looks like this: no restart of the listener and separate handling of different types of exceptions; one important thing is to close the Context.Response after an error, so that it sends a response to the client;
Code:
private void ProcessRequest(object listenerContext)
{
try
{
byte[] buffer;
var context = (HttpListenerContext)listenerContext;
try
{
//Make necessary replacements in the host name
Uri aUri = context.Request.Url;
string url = aUri.AbsolutePath + aUri.Query;
System.Uri uri = m_service.GetUri();
string address = uri.Scheme + "://" + uri.Host + ":" + uri.Port + url;
// Build Request
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
//DO STUFF
// Close streams
instream.Close();
outstream.Close();
context.Response.OutputStream.Close();
}
catch (System.ArgumentException ex)
{
context.Response.StatusCode = int.Parse(ex.Message);
context.Response.StatusDescription = GetStatusDescription(context.Response.StatusCode);
byte[] buff = System.Text.Encoding.UTF8.GetBytes("<HTML><BODY><b>Proxy Failure:</b> (" +
context.Response.StatusCode + ") " + context.Response.StatusDescription + "</HTML></BODY>");
context.Response.ContentLength64 = buff.Length;
context.Response.OutputStream.Write(buff, 0, buff.Length);
}
catch (System.Net.HttpListenerException ex) { }
catch (System.Net.WebException ex)
{
int status = (int)((HttpWebResponse)ex.Response).StatusCode;
context.Response.StatusCode = status;
context.Response.StatusDescription = GetStatusDescription(status);
context.Response.Close();
}
catch (Exception ex) { }//Default
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
// It should not come here! Are we having troubles closing the stream?
}
}
It's not the best solution, has it fails to deliver some jobs (not properly handling the multithreading), but its kind of working and is simple.
cheers,
Jo