using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Threading;
namespace TINetworkTest
{
/* These instructions tell me how to set up will as a proxy. This is required to help me
* test this software. This should not be required or helpful for the end user. The end
* user will have his own proxy.
*
* Testing the proxy from the command line:
* curl --proxy will.trade-ideas.com:8080 --head http://www.trade-ideas.com/NetworkTest.php
* I did this from the data center and from my laptop. At this time I was using IP numbers to
* allow the proxy. This gave me the current date, and listed "will" in a "via" header when
* I did this from my laptop. This gave me a "forbidden" error when I tried it from
* bob-saget. So it worked perfectly.
*
* Opening the fire wall on will:
* I don't have a firewall script on will. I can only guess that I filled out a quick form
* when I was installing Fedora on will. I used the following line to add port 8080 to the
* white list:
* iptables --insert INPUT 6 --protocol tcp --destination-port webcache -m state --state NEW -j ACCEPT
* I probably should have used
* iptables --insert INPUT 6 -m state --state NEW --protocol tcp --destination-port webcache -j ACCEPT
* to look more like the existing rules. So the second example is probably better but the
* first one has the advantage of being testing!
* Note that the rule numbers start at 1, not 0. Inserting at position 1 will put the new
* rule at the top of the list.
*
* I tested the firewall with
* telnet will 8080
* I tested the proxy settings (which were incomplete the first time) by typing this on will:
* telnet localhost 8080
* This will tell you if the message is getting through, before you try anything fancier.
*
* To turn on a simple proxy on will I created the file /etc/httpd/conf.d/trade_ideas_proxy.conf
* with the following contents:
* # Use this to test the proxy settings in TI Pro.
* Listen 8080
*
* ProxyRequests On
* ProxyVia On
* DocumentRoot /var/www/alldocs/8080
*
* Order Deny,Allow
* Deny from all
* Allow from 98.176.36.240
*
*
* Then I called
* apachectl graceful
*
* Log files on will:
* less /var/log/httpd/error_log
* This showed some useful stuff. Espeically after receiving a 5xx error.
*
* WARNING: The security on this next sections sucks! It worked well enough for the test,
* but do not leave it up and running! The ip based security worked better, but I'm far
* from an expert here, so it's probably best to disable that, too, when you are done.
*
* Use this config file and restart apache to test a proxy with credentials. This will
* require a username and password when you connect to port 8080. If you enter the wrong
* password, or just skip the username and password, you will get an appropriate error.
* WARNING: For some reason, this turns on proxying for all virtual hosts, but only
* requires a username and password for port 8080. So using will.trade-ideas.com:80 with
* no username and password will work. That doesn't hurt our test, but it is bad.
*/
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
UpdateStartButton();
UpdatePortColor();
}
private Tester TestInProgress;
private void UpdateStartButton()
{
startTestButton.Enabled = (null == TestInProgress) &&
(tryDefaultProxyCheckBox.Checked || tryNoProxyCheckBox.Checked || tryCustomProxyCheckBox.Checked);
abortTestButton.Enabled = null != TestInProgress;
}
private void hostNameTextBox_TextChanged(object sender, EventArgs e)
{
tryCustomProxyCheckBox.Checked = true;
}
private void portTextBox_TextChanged(object sender, EventArgs e)
{
tryCustomProxyCheckBox.Checked = true;
UpdatePortColor();
}
private void UpdatePortColor()
{
bool error = false;
if (tryCustomProxyCheckBox.Checked)
{
int unused;
error = !Int32.TryParse(portTextBox.Text, out unused);
}
portTextBox.BackColor = error ? Color.LightPink : SystemColors.Window;
}
private void userNameTextBox_TextChanged(object sender, EventArgs e)
{
tryCustomProxyCheckBox.Checked = true;
}
private void passwordTextBox_TextChanged(object sender, EventArgs e)
{
tryCustomProxyCheckBox.Checked = true;
}
private void tryDefaultProxyCheckBox_CheckedChanged(object sender, EventArgs e)
{
UpdateStartButton();
}
private void tryNoProxyCheckBox_CheckedChanged(object sender, EventArgs e)
{
UpdateStartButton();
}
private void tryCustomProxyCheckBox_CheckedChanged(object sender, EventArgs e)
{
UpdateStartButton();
UpdatePortColor();
}
private void AddOutput(string line)
{
outputTextBox.AppendText(line + "\r\n");
}
private void AddOutputWithDate(string line)
{
AddOutput(DateTime.Now.ToString() + ": " + line);
}
private void startTestButton_Click(object sender, EventArgs e)
{
TestInProgress = new Tester(this);
UpdateStartButton();
TestInProgress.Start();
}
private class Tester
{
private volatile bool _aborted;
private volatile HttpWebRequest _request;
public void Abort()
{
_aborted = true;
if (null != _request)
_request.Abort();
// Rules:
// _request is initially null.
// _request can change, but it can never change back to null.
// After you set _request, you have to manually check for the _aborted flag.
}
private List Proxies = new List();
private List Urls = new List();
// Use this because it's not likely to fail. Remember that we are converting a block of bytes
// to characters as soon as we receive the data. We might night have the entire message. We
// might have a partial unicode character.
private static readonly Encoding LATIN1 = Encoding.GetEncoding(28591);
private class AbortException : Exception
{
}
private void ThreadFunction()
{
try
{
foreach (Uri url in Urls)
{
AddOutput("Testing url: " + url);
foreach (ProxyInfo proxy in Proxies)
{
AddOutputWithDate("Starting " + proxy.Describe(url));
DateTime start = DateTime.Now;
_request = (HttpWebRequest)WebRequest.Create(url);
_request.Proxy = proxy.Proxy;
_request.Timeout = 5000;
_request.ReadWriteTimeout = 15000;
if (_aborted)
throw new AbortException();
HttpWebResponse response = (HttpWebResponse)_request.GetResponse();
Stream responseStream = response.GetResponseStream();
byte[] buffer = new byte[2048];
while (true)
{
int bytesRead = responseStream.Read(buffer, 0, buffer.Length);
if (_aborted)
throw new AbortException();
if (bytesRead > 0)
{
string body = new String(LATIN1.GetChars(buffer, 0, bytesRead));
AddOutputWithDate(response.StatusCode + ": “" + body + '”');
}
else
break;
}
response.Close();
AddOutputWithDate("Status code: " + response.StatusCode);
}
}
}
catch (AbortException ex)
{
AddOutputWithDate("Aborted at user's request.");
}
catch (Exception ex)
{
AddOutputWithDate("Unexpected exception in test: " + ex);
}
_form.Invoke((MethodInvoker)Done);
}
private void AddOutputWithDate(string line)
{
_form.Invoke((MethodInvoker)delegate { _form.AddOutputWithDate(line); });
}
private void AddOutput(string line)
{
_form.Invoke((MethodInvoker)delegate { _form.AddOutput(line); });
}
private readonly Form1 _form;
public Tester(Form1 form)
{
_form = form;
}
public void Start()
{
_form.tabControl1.SelectedTab = _form.outputTabPage;
_form.AddOutputWithDate("Starting.");
try
{
if (_form.tryDefaultProxyCheckBox.Checked)
Proxies.Add(ProxyInfo.DEFAULT);
if (_form.tryNoProxyCheckBox.Checked)
Proxies.Add(ProxyInfo.NONE);
if (_form.tryCustomProxyCheckBox.Checked)
{
try
{
Proxies.Add(ProxyInfo.GetCustom(_form.hostNameTextBox.Text,
Int32.Parse(_form.portTextBox.Text),
_form.userNameTextBox.Text, _form.passwordTextBox.Text));
}
catch (Exception ex)
{
_form.AddOutputWithDate("Unexpected exception with custom proxy: " + ex.Message);
}
}
Urls.Add(new Uri("http://www.trade-ideas.com/NetworkTest.php"));
Urls.Add(new Uri("http://69.43.145.244/NetworkTest.php"));
Urls.Add(new Uri("http://69.43.145.244/NetworkTest1.php"));
if (_form.customUrlTextBox.Text != "")
try
{
Urls.Add(new Uri(_form.customUrlTextBox.Text));
}
catch (Exception ex)
{
_form.AddOutputWithDate("Unexpected exception with custom URL: " + ex.Message);
}
new Thread(ThreadFunction).Start();
}
catch (Exception ex)
{
_form.AddOutputWithDate("Problem setting up test: " + ex);
Done();
}
}
private void Done()
{
_form.AddOutputWithDate("Done.");
// For simplicity we say that the new test can't start until this test ends.
// In a production system we'd be more likely to start the operation immediately,
// and ignore results producted by any old operations.
System.Diagnostics.Debug.Assert(_form.TestInProgress == this);
_form.TestInProgress = null;
_form.UpdateStartButton();
}
};
private class ProxyInfo
{
public IWebProxy Proxy { get; private set; }
public string Name { get; private set; }
private ProxyInfo(IWebProxy Proxy, string Name)
{
this.Proxy = Proxy;
this.Name = Name;
}
public static ProxyInfo NONE
{
get
{ // GlobalProxySelection.GetEmptyWebProxy()
return new ProxyInfo(null, "No Proxy");
}
}
public static ProxyInfo DEFAULT
{
get
{
return new ProxyInfo(WebRequest.DefaultWebProxy, "Default Proxy");
}
}
public static ProxyInfo GetCustom(string Host, int Port, string UserName, string Password)
{
IWebProxy proxy = new WebProxy(Host, Port);
if ((UserName != "") || (Password != ""))
{
ICredentials Credentials = new NetworkCredential(UserName, Password);
proxy.Credentials = Credentials;
}
return new ProxyInfo(proxy, "Custom Proxy");
}
public string Describe(Uri url)
{
StringBuilder sb = new StringBuilder();
sb.Append(Name);
if (null != Proxy)
{
Uri proxyUrl = Proxy.GetProxy(url);
if (url != proxyUrl)
{
sb.Append(" (");
sb.Append(proxyUrl);
sb.Append(')');
}
}
return sb.ToString();
}
}
private void abortTestButton_Click(object sender, EventArgs e)
{
if (null != TestInProgress)
TestInProgress.Abort();
}
}
}