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 } }