C# HttpTunnelClient
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Net.Mime;
using System.Reflection;
namespace Rocky.Net
{
public class HttpTunnelClient
{
#region Fields
private HttpRequestEntity _entity;
private SocketProxy _proxy;
private Socket _sock;
#endregion
#region Properties
public WebHeaderCollection Headers
{
get { return _entity.Headers; }
}
public NameValueCollection Form
{
get { return _entity.Form; }
}
public bool KeepAlive { get; set; }
public bool HeadMethod { get; set; }
public int SendReceiveTimeout { get; set; }
#endregion
#region Constructors
public HttpTunnelClient(Uri proxyAddress = null)
{
if (proxyAddress != null)
{
_proxy = new SocketProxy()
{
ProxyType = SocketProxyType.ptHTTP,
Address = SocketHelper.ParseEndPoint(proxyAddress.GetEndPoint())
};
}
_entity = new HttpRequestEntity();
_entity.Headers[HttpRequestHeader.Accept] = "*/*";
_entity.Headers[HttpRequestHeader.Referer] = HttpClient.DefaultReferer;
_entity.Headers[HttpRequestHeader.UserAgent] = HttpClient.DefaultUserAgent;
this.KeepAlive = true;
this.SendReceiveTimeout = 1000 * 30;
}
#endregion
#region Methods
private void Prepare(out string method, out string sForm)
{
method = sForm = null;
_entity.Headers["Proxy-Connection"] = _entity.Headers[HttpRequestHeader.Connection] = this.KeepAlive ? "keep-alive" : "close";
if (this.HeadMethod)
{
method = WebRequestMethods.Http.Head;
return;
}
if (!_entity.HasValue)
{
method = WebRequestMethods.Http.Get;
return;
}
sForm = _entity.GetFormString();
_entity.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
_entity.Headers[HttpRequestHeader.ContentLength] = Encoding.UTF8.GetByteCount(sForm).ToString();
method = WebRequestMethods.Http.Post;
}
private void SetEncoding(StreamReader reader, Encoding encoding, out string bufferedString)
{
Type type = reader.GetType();
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var field = type.GetField("charPos", flags);
int charPos = Convert.ToInt32(field.GetValue(reader));
field = type.GetField("charLen", flags);
int charLen = Convert.ToInt32(field.GetValue(reader));
field = type.GetField("byteBuffer", flags);
byte[] byteBuffer = (byte[])field.GetValue(reader);
bufferedString = encoding.GetString(byteBuffer, charPos, charLen - charPos);
field = type.GetField("encoding", flags);
field.SetValue(reader, encoding);
field = type.GetField("decoder", flags);
field.SetValue(reader, encoding.GetDecoder());
reader.DiscardBufferedData();
}
public HttpResponseEntity GetResponse(Uri requestUri, Action<Stream> onSend = null, Action<Stream> onReceive = null)
{
Contract.Requires(requestUri != null);
_entity.Headers[HttpRequestHeader.Host] = requestUri.Host;
string method, sForm;
this.Prepare(out method, out sForm);
_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_sock.ReceiveTimeout = _sock.SendTimeout = this.SendReceiveTimeout;
try
{
if (_proxy != null)
{
_sock.Connect(_proxy.Address);
byte[] data = ProxyUtility.PrepareRequest(_proxy, SocketHelper.ParseEndPoint(requestUri.GetEndPoint()));
int sent = _sock.Send(data);
Array.Resize(ref data, 256);
int recv = _sock.Receive(data);
ProxyUtility.ValidateResponse(_proxy, data);
}
else
{
_sock.ConnectSync(requestUri.Host, requestUri.Port);
}
var netStream = new NetworkStream(_sock, FileAccess.ReadWrite, false);
Stream src = netStream;
if (requestUri.Scheme == Uri.UriSchemeHttps)
{
var sslStream = new SslStream(src);
sslStream.AuthenticateAsClient(requestUri.Host);
src = sslStream;
}
var writer = new StreamWriter(src, Encoding.ASCII);
writer.WriteLine("{0} {1} HTTP/1.1", method, requestUri.PathAndQuery);
var sHeaders = new StringBuilder(_entity.GetHeadersString());
SocketHelper.Logger.DebugFormat("RequestHeaders:\r\n{0}\r\n", sHeaders);
writer.WriteLine(sHeaders);
writer.WriteLine();
writer.Flush();
if (onSend != null)
{
onSend(src);
}
else
{
if (sForm != null)
{
writer = new StreamWriter(src, Encoding.UTF8);
writer.Write(sForm);
writer.Flush();
}
}
var response = new HttpResponseEntity();
var reader = new StreamReader(src, Encoding.ASCII);
//HTTP/1.1 200 OK
string line = reader.ReadLine();
var status = line.Split(new char[] { ' ' }, 3);
if (status.Length != 3)
{
throw new WebException("InternalServerError");
}
response.StatusCode = (HttpStatusCode)int.Parse(status[1]);
response.StatusDescription = status[2];
sHeaders.Length = 0;
while (!string.IsNullOrEmpty(line = reader.ReadLine()))
{
int i = line.IndexOf(":");
string name = line.Substring(0, i), value = line.Substring(i + 2);
response.Headers.Add(name, value);
sHeaders.AppendLine(line);
}
SocketHelper.Logger.DebugFormat("ResponseHeaders:\r\n{0}\r\n", sHeaders);
string contentType = response.Headers[HttpResponseHeader.ContentType];
int charsetIndex;
if (string.IsNullOrEmpty(contentType) || (charsetIndex = contentType.LastIndexOf("=")) == -1)
{
response.ContentEncoding = Encoding.UTF8;
}
else
{
string charset = contentType.Substring(charsetIndex + 1);
response.ContentEncoding = Encoding.GetEncoding(charset);
}
if (!this.HeadMethod)
{
if (onReceive != null)
{
onReceive(src);
}
else
{
string bufferedString;
this.SetEncoding(reader, response.ContentEncoding, out bufferedString);
response.ResponseText = bufferedString;
response.ResponseText += reader.ReadToEnd();
}
}
return response;
}
finally
{
_entity.Clear(HttpRequestEntity.ItemKind.Form);
if (_sock.Connected)
{
_sock.Disconnect(this.KeepAlive);
}
}
}
#endregion
}
}