123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716 |
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Net.Sockets;
- using System.IO;
- using System.Threading;
- using CommonLang;
- using CommonNetwork;
- using CommonNetwork.Sockets;
- using CommonLang.IO;
- using CommonLang.Log;
- using System.Net;
- using CommonLang.Net;
- using System.Net.Security;
- using System.Security.Cryptography.X509Certificates;
- using System.Security.Authentication;
- namespace CommonNetwork.Http
- {
- public delegate void HttpConnectHandler(WebClient www);
- public delegate void HttpPostHandler(string result);
- public delegate void HttpGetHandler(byte[] result);
- public class HttpRequest
- {
- public const string METHOD_GET = "GET";
- public const string METHOD_POST = "POST";
- public const string CONTENT_TYPE_OCTET_STREAM = "application/octet-stream";
- public const string CONTENT_TYPE_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
- public const string CONTENT_TYPE_TEXT_XML = "text/xml";
- public string Method = METHOD_GET;
- public string ContentType = CONTENT_TYPE_OCTET_STREAM;
- public string Referer;
- public string Accept = "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
- public string AcceptLanguage = "zh-CN";
- public string AcceptEncoding = "identity";
- public string UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36";
- public string Connection = "keep-alive";
- public string CacheControl = "no-cache";
- public SslProtocols SslProtocol = SslProtocols.Ssl3;
- public X509CertificateCollection Certificates = null;
- public RemoteCertificateValidationCallback OnRemoteCertificateValidation = null;
- public LocalCertificateSelectionCallback OnLocalCertificateValidation = null;
- private Properties _params;
- public Properties Params
- {
- get
- {
- if (_params == null)
- {
- _params = new Properties();
- }
- return _params;
- }
- }
- public byte[] Content;
- }
- public class HttpResponse
- {
- public Properties Params { get; internal set; }
- public string Status { get; internal set; }
- public string ContentType { get; internal set; }
- public int ContentLength { get; internal set; }
- public string Location { get; internal set; }
- public bool IsGzip { get; internal set; }
- public bool IsChunk { get; internal set; }
- private Stream input;
- public Stream InputStream
- {
- get { return input; }
- internal set
- {
- if (this.IsChunk)
- {
- this.input = new ChunkInputStream(this, value);
- }
- else
- {
- this.input = value;
- }
- }
- }
- public override string ToString()
- {
- var sb = new StringBuilder();
- if (Params != null)
- {
- foreach (var e in Params)
- {
- sb.Append(e.Key + " : " + e.Value + WebClient.BR);
- }
- }
- return sb.ToString();
- }
- public byte[] ReadContentToEnd()
- {
- if (IsChunk)
- {
- byte[] data = IOUtil.ReadToEnd(input);
- return data;
- }
- else
- {
- byte[] data = new byte[ContentLength];
- IOUtil.ReadToEnd(input, data, 0, data.Length);
- return data;
- }
- }
- internal class ChunkInputStream : Stream
- {
- private readonly HttpResponse response;
- private readonly Stream baseStream;
- private int current_chunk_pos = 0;
- private int current_chunk_size = -1;
- public override bool CanRead { get { return baseStream.CanRead; } }
- public override bool CanSeek { get { return false; } }
- public override bool CanWrite { get { return false; } }
- public override long Length { get { return 0; } }
- public override long Position { get { return 0; } set { } }
- internal ChunkInputStream(HttpResponse rsp, Stream s)
- {
- this.response = rsp;
- this.baseStream = s;
- }
- public override int Read(byte[] buffer, int offset, int count)
- {
- if (current_chunk_pos >= current_chunk_size)
- {
- try
- {
- var line = WebClient.ReadLine(baseStream);
- current_chunk_size = Convert.ToInt32(line.Trim(), 16);
- current_chunk_pos = 0;
- }
- catch (Exception err)
- {
- throw new Exception("Maybe exists 'chunk-ext' field.", err);
- }
- }
- if (current_chunk_size == 0)
- {
- return 0;
- }
- int total = current_chunk_size - current_chunk_pos;
- count = Math.Min(total, count);
- int readed = baseStream.Read(buffer, offset, count);
- if (readed > 0)
- {
- current_chunk_pos += readed;
- if (current_chunk_pos >= current_chunk_size)
- {
- WebClient.ReadLine(baseStream);
- }
- }
- return readed;
- }
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotImplementedException();
- }
- public override void SetLength(long value)
- {
- throw new NotImplementedException();
- }
- public override void Flush()
- {
- throw new NotImplementedException();
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- throw new NotImplementedException();
- }
- }
- }
- public class WebClient : IDisposable
- {
- private static Logger _log;
- private static Logger log
- {
- get
- {
- if (_log == null) _log = LoggerFactory.GetLogger("WebClient");
- return _log;
- }
- }
- private TcpClient mSocket = null;
- private Uri url;
- public HttpRequest Request = new HttpRequest();
- public HttpResponse Response { get; private set; }
- public Exception Error { get; private set; }
- public int TimeoutMS { get; set; }
- public WebClient(Uri url)
- {
- this.url = url;
- this.TimeoutMS = 30000;
- }
- public void Dispose()
- {
- try
- {
- if (mSocket != null)
- {
- mSocket.Close();
- }
- }
- catch (System.Exception e)
- {
- log.Warn(e.Message, e);
- }
- }
- public Stream Connect()
- {
- _connect(null);
- if (Response != null)
- {
- return Response.InputStream;
- }
- return null;
- }
- public void ConnectAsync(HttpConnectHandler handler)
- {
- var ts = new ThreadStart(() => { this._connect(handler); });
- var tr = new Thread(ts);
- tr.IsBackground = true;
- tr.Start();
- }
- //-----------------------------------------------------------------------------------------------------------------
- #region Internal
- /// <summary>
- ///
- /// </summary>
- /// <param name="forceIPv6">强制IPv6</param>
- /// <param name="location"></param>
- /// <param name="timeoutMS"></param>
- private static TcpClient _connect_remote(bool forceIPv6, Uri location, int timeoutMS)
- {
- if (location.HostNameType == UriHostNameType.Dns)
- {
- Console.WriteLine("dns : " + location + " " + location.HostNameType.ToString());
- var ips = Dns.GetHostEntry(location.Host);
- // 如果只包含IPv6地址,表示当前环境IPv6 only
- var family = IPUtil.IsOnlyIPv6(ips) ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
- var mSocket = new TcpClient(forceIPv6 ? AddressFamily.InterNetworkV6 : family);
- mSocket.SendTimeout = timeoutMS;
- mSocket.ReceiveTimeout = timeoutMS;
- if (family != AddressFamily.InterNetworkV6 && forceIPv6)
- {
- //首次是IPV6地址,优先选择V6地址//
- foreach (var ip in ips.AddressList)
- {
- if (ip.AddressFamily == AddressFamily.InterNetworkV6)
- {
- mSocket.Connect(ip, location.Port);
- return mSocket;
- }
- }
- //强转V4地址到V6地址//
- foreach (var ip in ips.AddressList)
- {
- if (ip.AddressFamily == AddressFamily.InterNetwork)
- {
- var ipv6 = IPUtil.MapToIPv6(ip);
- Console.WriteLine("ipv4 to ipv6 : " + ip + " - " + ipv6);
- mSocket.Connect(ipv6, location.Port);
- return mSocket;
- }
- }
- mSocket.Connect(ips.AddressList, location.Port);
- }
- else
- {
- mSocket.Connect(ips.AddressList, location.Port);
- }
- return mSocket;
- }
- else
- {
- Console.WriteLine("ip : " + location + " " + location.HostNameType.ToString());
- var mSocket = new TcpClient(forceIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork);
- mSocket.SendTimeout = timeoutMS;
- mSocket.ReceiveTimeout = timeoutMS;
- if (location.HostNameType != UriHostNameType.IPv6 && forceIPv6)
- {
- var ipv6 = IPUtil.MapToIPv6(location.Host);
- Console.WriteLine("ipv4 to ipv6 : " + location.Host + " - " + ipv6);
- mSocket.Connect(ipv6, location.Port);
- }
- else
- {
- mSocket.Connect(location.Host, location.Port);
- }
- return mSocket;
- }
- }
- private void _connect(HttpConnectHandler handler)
- {
- try
- {
- bool isIpV6 = false;
- Uri location = url;
- do
- {
- mSocket = _connect_remote(isIpV6, location, TimeoutMS);
- if (!mSocket.Connected)
- {
- return;
- }
- log.Debug("RemoteEndPoint AddressFamily : " + mSocket.Client.RemoteEndPoint.AddressFamily);
- if (mSocket.Client.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
- {
- isIpV6 = true;
- }
- Stream stream = null;
- if (location.Scheme == "http")
- {
- stream = mSocket.GetStream();
- }
- else if (location.Scheme == "https")
- {
- var sslStream = new SslStream(mSocket.GetStream(), true, Request.OnRemoteCertificateValidation, Request.OnLocalCertificateValidation);
- sslStream.ReadTimeout = TimeoutMS;
- sslStream.WriteTimeout = TimeoutMS;
- var certificates = Request.Certificates;
- if (certificates != null)
- {
- var store = new X509Store(StoreName.My);
- certificates = store.Certificates;
- }
- sslStream.AuthenticateAsClient(location.Host, certificates, Request.SslProtocol, false);
- if (!sslStream.IsAuthenticated)
- {
- return;
- }
- stream = sslStream;
- }
- else
- {
- log.Error("url must start with HTTP or HTTPS:" + url);
- break;
- }
- log.Info("send request : " + location);
- this.Response = _send_request(stream, location, Request.Referer, Request);
- log.Info("response : " + Response.Status);
- // redirect 302
- if (!string.IsNullOrEmpty(this.Response.Location))
- {
- if (mSocket != null)
- {
- try { stream.Close(); } catch (System.Exception e) { log.Warn(e.Message, e); }
- try { mSocket.Close(); } catch (System.Exception e) { log.Warn(e.Message, e); }
- }
- log.Info(" redirect to : " + this.Response.Location);
- location = new Uri(this.Response.Location);
- continue;
- }
- else
- {
- this.Response.InputStream = stream;
- break;
- }
- }
- while (true);
- }
- catch (System.Exception e)
- {
- log.Error(e.Message, e);
- this.Response = null;
- this.Error = e;
- }
- finally
- {
- if (handler != null)
- {
- handler(this);
- }
- }
- }
- private static HttpResponse _send_request(Stream stream, Uri url, string referer, HttpRequest request)
- {
- // Send
- if (request.Method.Equals(HttpRequest.METHOD_GET))
- {
- StringBuilder header_text = new StringBuilder();
- header_text.Append(request.Method + " " + url.PathAndQuery + " HTTP/1.1" + BR);
- header_text.Append("Accept: " + request.Accept + BR);
- header_text.Append("Referer: " + referer + BR);
- header_text.Append("Accept-Language: " + request.AcceptLanguage + BR);
- header_text.Append("Accept-Encoding: " + request.AcceptEncoding + BR);
- header_text.Append("User-Agent: " + request.UserAgent + BR);
- header_text.Append("Host: " + url.Host + BR);
- header_text.Append("Connection: " + request.Connection + BR);
- header_text.Append("Cache-Control: " + request.CacheControl + BR);
- foreach (KeyValuePair<string, string> e in request.Params)
- {
- header_text.Append(e.Key + ": " + e.Value + BR);
- }
- header_text.Append(BR);
- // send HTTP GET //
- string req = header_text.ToString();
- byte[] header_bytes = Encoding.UTF8.GetBytes(req);
- IOUtil.WriteToEnd(stream, header_bytes, 0, header_bytes.Length);
- }
- else if (request.Method.Equals(HttpRequest.METHOD_POST))
- {
- byte[] body = _get_post_data(url, request);
- // send HTTP Post Head //
- StringBuilder header_text = new StringBuilder();
- header_text.Append(request.Method + " " + url.AbsolutePath + " HTTP/1.1" + BR);
- header_text.Append("Host: " + url.Host + BR);
- header_text.Append("Referer: " + referer + BR);
- header_text.Append("Content-Length: " + body.Length + BR);
- header_text.Append("Content-Type: " + request.ContentType + BR);
- header_text.Append("User-Agent: " + request.UserAgent + BR);
- header_text.Append("Connection: " + request.Connection + BR);
- header_text.Append("Cache-Control: " + request.CacheControl + BR);
- header_text.Append("Accept: " + request.Accept + BR);
- foreach (KeyValuePair<string, string> e in request.Params)
- {
- header_text.Append(e.Key + ": " + e.Value + BR);
- }
- header_text.Append(BR);
- string req = header_text.ToString();
- byte[] header_bytes = Encoding.UTF8.GetBytes(req);
- IOUtil.WriteToEnd(stream, header_bytes, 0, header_bytes.Length);
- IOUtil.WriteToEnd(stream, body, 0, body.Length);
- }
- HttpResponse response = new HttpResponse();
- response.Params = new Properties();
- String line = null;
- while ((line = ReadLine(stream)) != null)
- {
- if (line.Length == 0)
- {
- break;
- }
- else if (line.ToUpper().StartsWith("HTTP"))
- {
- response.Status = line;
- }
- else if (response.Params.ParseLine(line, ":"))
- {
- }
- string len = null;
- if (TryGetResponseValue(line, "Content-Length", out len))
- {
- response.ContentLength = int.Parse(len);
- }
- else if (TryGetResponseValue(line, "Content-Type", out len))
- {
- response.ContentType = len;
- }
- else if (TryGetResponseValue(line, "Location", out len))
- {
- response.Location = len;
- }
- else if (TryGetResponseValue(line, "Content-Encoding", out len))
- {
- if (len.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) >= 0)
- {
- response.IsGzip = true;
- }
- }
- else if (TryGetResponseValue(line, "Transfer-Encoding", out len))
- {
- if (len.IndexOf("chunked", StringComparison.OrdinalIgnoreCase) >= 0)
- {
- response.IsChunk = true;
- }
- }
- }
- return response;
- }
- private static byte[] _get_post_data(Uri url, HttpRequest request)
- {
- if (request.Content == null)
- {
- string body = url.Query;
- if (body.StartsWith("?"))
- {
- body = body.Substring(1);
- }
- int body_length = Encoding.UTF8.GetByteCount(body);
- byte[] body_bytes = Encoding.UTF8.GetBytes(body);
- return body_bytes;
- }
- else
- {
- return request.Content;
- }
- }
- #endregion
- //-----------------------------------------------------------------------------------------------------------------
- #region STATIC
- public static string DownloadString(Uri url, Encoding enc = null)
- {
- if (enc == null)
- {
- enc = CUtils.UTF8;
- }
- using (WebClient client = new WebClient(url))
- {
- client.Request.Method = HttpRequest.METHOD_GET;
- client.Request.Referer = url.AbsoluteUri;
- client.Connect();
- byte[] data = client.Response.ReadContentToEnd();
- string ret = enc.GetString(data);
- return ret;
- }
- }
- public static byte[] Get(Uri url)
- {
- using (WebClient client = new WebClient(url))
- {
- client.Request.Method = HttpRequest.METHOD_GET;
- client.Request.Referer = url.AbsoluteUri;
- client.Connect();
- byte[] data = client.Response.ReadContentToEnd();
- return data;
- }
- }
- public static void GetAsync(Uri url, HttpGetHandler handler)
- {
- WebClient client = new WebClient(url);
- client.Request.Method = HttpRequest.METHOD_GET;
- client.Request.Referer = url.AbsoluteUri;
- client.ConnectAsync((www) =>
- {
- try
- {
- if (www.Response != null)
- {
- byte[] data = client.Response.ReadContentToEnd();
- handler.Invoke(data);
- }
- else
- {
- handler.Invoke(null);
- }
- }
- catch (Exception err)
- {
- log.Error(err.Message, err);
- handler.Invoke(null);
- }
- finally
- {
- www.Dispose();
- }
- });
- }
- public static string Post(Uri url, string referer = null, Encoding enc = null)
- {
- if (enc == null)
- {
- enc = CUtils.UTF8;
- }
- if (referer == null)
- {
- referer = url.AbsoluteUri;
- }
- using (WebClient client = new WebClient(url))
- {
- client.Request.Method = HttpRequest.METHOD_POST;
- client.Request.ContentType = "application/x-www-form-urlencoded";
- client.Request.Referer = referer;
- client.Connect();
- byte[] data = client.Response.ReadContentToEnd();
- string ret = enc.GetString(data);
- return ret;
- }
- }
- public static void PostAsync(Uri url, string referer, Encoding enc, HttpPostHandler handler)
- {
- if (enc == null)
- {
- enc = CUtils.UTF8;
- }
- if (referer == null)
- {
- referer = url.AbsoluteUri;
- }
- WebClient client = new WebClient(url);
- client.Request.Method = HttpRequest.METHOD_POST;
- client.Request.ContentType = "application/x-www-form-urlencoded";
- client.Request.Referer = referer;
- client.ConnectAsync((www) =>
- {
- try
- {
- if (www.Response != null)
- {
- byte[] data = client.Response.ReadContentToEnd();
- string ret = enc.GetString(data);
- handler.Invoke(ret);
- }
- else
- {
- handler.Invoke(null);
- }
- }
- catch (Exception err)
- {
- log.Error(err.Message, err);
- handler.Invoke(null);
- }
- finally
- {
- www.Dispose();
- }
- });
- }
- public static void PostAsync(Uri url, Encoding enc, HttpPostHandler handler)
- {
- PostAsync(url, null, CUtils.UTF8, handler);
- }
- public static void PostAsync(Uri url, HttpPostHandler handler)
- {
- PostAsync(url, CUtils.UTF8, handler);
- }
- #endregion
- //----------------------------------------------------------------------------------------------
- #region UTILS
- public static readonly string BR = "\r\n";
- public static readonly char[] SPLIT = new char[] { ':' };
- public static string FormatPath(string path)
- {
- path = path.Replace('\\', '/');
- return path;
- }
- private static bool TryGetResponseValue(string line, string key, out string value)
- {
- if (line.ToLower().StartsWith(key.ToLower()))
- {
- string[] kv = line.Split(SPLIT, 2);
- value = kv[1].Trim();
- return true;
- }
- value = null;
- return false;
- }
- public static string ReadLine(Stream input)
- {
- byte _r = (byte)'\r';
- byte _n = (byte)'\n';
- using (var ms = MemoryStreamObjectPool.AllocAutoRelease())
- {
- int a0 = 0;
- while (a0 >= 0)
- {
- int a1 = input.ReadByte();
- if (a1 < 0)
- {
- break;
- }
- ms.WriteByte((byte)a1);
- if (a0 == _r && a1 == _n)
- {
- break;
- }
- a0 = a1;
- }
- ms.Flush();
- return Encoding.ASCII.GetString(ms.GetBuffer(), 0, (int)(ms.Length - 2));
- }
- }
- #endregion
- }
- }
|