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
{
internal class HttpClient
{
#region Fields
private ProxyEntity _proxy;
private HttpRequestEntity _entity;
private Socket _sock;
private object[] _args;
#endregion
#region Properties
public ProxyEntity Proxy
{
get { return _proxy; }
}
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 HttpClientSlim(IPEndPoint tunnel)
{
Contract.Requires(tunnel != null);
_proxy = new ProxyEntity()
{
ProxyType = ProxyType.ptHTTP,
Address = tunnel
};
_entity = new HttpRequestEntity();
_entity.Headers[HttpRequestHeader.Accept] = "*/*";
_entity.Headers[HttpRequestHeader.Referer] = HttpClient.DefaultReferer;
_entity.Headers[HttpRequestHeader.UserAgent] = HttpClient.DefaultUserAgent;
_args = new object[3];
this.KeepAlive = true;
this.SendReceiveTimeout = 1000 * 30;
}
#endregion
#region Methods
public HttpResponseEntity GetResponse(Uri requestUri)
{
Contract.Requires(requestUri != null);
_entity.Headers[HttpRequestHeader.Host] = requestUri.Host;
_entity.Headers["Proxy-Connection"] = _entity.Headers[HttpRequestHeader.Connection] = this.KeepAlive ? "keep-alive" : "close";
string sForm = null;
if (_entity.HasValue)
{
sForm = _entity.GetFormString();
_entity.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
_entity.Headers[HttpRequestHeader.ContentLength] = Encoding.UTF8.GetByteCount(sForm).ToString();
}
_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_sock.ReceiveTimeout = _sock.SendTimeout = this.SendReceiveTimeout;
try
{
_sock.Connect(_proxy.Address);
var ipe = SocketHelper.ParseHost(string.Format("{0}:{1}", requestUri.Host, requestUri.Port));
var proxyData = ProxyUtility.GetRequestData(_proxy, ipe);
int sent = _sock.Send(proxyData);
Array.Resize(ref proxyData, 256);
int recv = _sock.Receive(proxyData);
ProxyUtility.ValidateResponseStatus(_proxy, proxyData);
var netStream = new NetworkStream(_sock, FileAccess.ReadWrite, false);
Stream src = netStream;
var writer = new StreamWriter(src, Encoding.ASCII);
string method = this.HeadMethod ? WebRequestMethods.Http.Head :
sForm == null ? WebRequestMethods.Http.Get : WebRequestMethods.Http.Post;
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 (sForm != null)
{
if (requestUri.Scheme == Uri.UriSchemeHttps)
{
var sslStream = new SslStream(src);
sslStream.AuthenticateAsClient(requestUri.Host);
src = sslStream;
}
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 InvalidOperationException("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)
{
string bufferedString;
this.SetEncoding(reader, response.ContentEncoding, out bufferedString);
response.ResponseText = bufferedString;
response.ResponseText += reader.ReadToEnd();
}
return response;
}
finally
{
if (_sock.Connected)
{
_sock.Disconnect(this.KeepAlive);
}
}
}
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();
}
#endregion
}
}