I'm trying to build a class which should be able to use HTTP CONNECT proxy tunnels.
My current code hangs at DoHttpConnect() - "var retvar = await sr.ReadToEndAsync();"
and never reaches Console.WriteLine(retvar);
Connection class:
public class HttpConnect : BaseProxyModule
{
public HttpConnect(HostPortCollection proxyInformation) : base(proxyInformation)
{
}
public override async Task<Stream> OpenStreamAsync(HostPortCollection dstSrv)
{
var socket = new TcpClient();
if (await SwallowExceptionUtils.TryExec(() => socket.ConnectAsync(_proxyInformation.Addr, _proxyInformation.Port)) && socket.Connected)
{
var nStream = socket.GetStream();
var cmd = ProxyCommandBuildUtils.GenerateHttpConnectCommand(dstSrv.Host, dstSrv.Port);
await nStream.WriteAsync(cmd, 0, cmd.Length);
var sr = new StreamReader(nStream);
var cLine = await sr.ReadLineAsync();
var fLineElem = cLine.Split(' ');
if (fLineElem.Length >= 1 && fLineElem[1] == "200")
{
await sr.ReadLineAsync();
return nStream;
}
}
return null;
}
internal class ProxyCommandBuildUtils
{
private const string HTTP_PROXY_CONNECT_CMD = "CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}\r\n\r\n";
public static byte[] GenerateHttpConnectCommand(string host, int port)
{
string connectCmd = String.Format(CultureInfo.InvariantCulture, HTTP_PROXY_CONNECT_CMD, host, port.ToString(CultureInfo.InvariantCulture));
return connectCmd.GetBytes();
}
}
Usage:
public static void Main(string[] args)
{
DoHttpConnect().Wait();
Debugger.Break();
}
public static async Task DoHttpConnect()
{
//var hCon = new HttpConnect(new HostPortCollection("5.135.195.166", 3128)); //Doesn't work..
var hCon = new HttpConnect(new HostPortCollection("109.75.213.146", 53281)); //Doesn't work either :/
var pStream = await hCon.OpenStreamAsync(new HostPortCollection("www.myip.ch", 80));
var request = FormatHttpRequest(new Uri("http://www.myip.ch/"));
using (var sw = new StreamWriter(pStream))
using (var sr = new StreamReader(pStream))
{
await sw.WriteLineAsync(request);
var retvar = await sr.ReadToEndAsync(); //Takes till the end of time itself
Console.WriteLine(retvar);
}
Debugger.Break();
}
private static string FormatHttpRequest(Uri url)
{
return $"GET {url.LocalPath} HTTP/1.0\r\nHost: {url.DnsSafeHost}\r\n\r\n";
}
The WriteLineAsync()
call doesn't actually write out to the network immediately. Instead, the StreamWriter
holds it in a buffer. Because of this, the server never actually gets your request, so won't send you a reply.
You should force the buffer to write out to the network by doing one of the following:
AutoFlush
(reference) for the StreamWriter
, which will make it flush the buffer with every call to WriteLineAsync()
FlushAsync()
(reference)Close the stream (which will flush the buffer) by rewriting the code like this:
using (var sr = new StreamReader(pStream))
{
using (var sw = new StreamWriter(pStream))
{
await sw.WriteLineAsync(request);
}
var retvar = await sr.ReadToEndAsync();
Console.WriteLine(retvar);
}