using Apewer; using Apewer.Internals; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Security; using System.Security; using System.Security.Cryptography.X509Certificates; using System.Text; namespace Apewer.Network { /// public class HttpClient { private string _key = TextUtility.Guid(); internal bool _locked = false; private TextSet _properties = new TextSet(true); private HttpRequest _request = new HttpRequest(); private HttpResponse _response = new HttpResponse(); private Exception _lastexception = null; private HttpWebRequest _lastrequest = null; private HttpWebResponse _lastresponse = null; /// public HttpRequest Request { get { return _request; } } /// public HttpResponse Response { get { return _response; } } /// public string Key { get { return _properties["Key"]; } } /// /// /// /// /// /// /// /// private static HttpWebRequest CreateRequest(HttpRequest request) { var error = (Exception)null; if (request == null) throw new ArgumentNullException("Request", "参数无效。"); var properties = request._properties; var certificates = request._certificates; var headers = request._headers; var cookies = request._cookies; var timeout = request._timeout; var url = properties["Url"]; // 全局证书验证。 var https = url.ToLower().StartsWith("https"); if (https) SslUtility.ApproveValidation(); // 创建对象。 var http = (HttpWebRequest)WebRequest.Create(properties["Url"]); // 证书。 foreach (var certificate in certificates) { if (certificate == null) continue; if (certificate.LongLength < 1L) continue; try { var x509 = new X509Certificate(certificate); http.ClientCertificates.Add(x509); } finally { } } // 请求超时。 if (timeout > 0) http.Timeout = timeout; // 方法。 http.Method = request._method.ToString(); // 允许重定向。 http.AllowAutoRedirect = request._redirect; // 头。 foreach (var header in headers.Origin) { var key = header.Key.ToTrim(); ; var value = header.Value.ToTrim(); if (TextUtility.IsBlank(key)) continue; if (TextUtility.IsBlank(value)) continue; try { switch (key.Lower()) { case "accept": http.Accept = value; break; case "connection": http.Connection = value; break; case "content-length": http.ContentLength = NumberUtility.Int64(value); break; case "content-type": http.ContentType = value; break; case "date": // request.Date = ClockUtility.Parse(value); break; case "expect": http.Expect = value; break; #if !NET20 case "host": http.Host = value; break; #endif case "if-modified-since": // request.IfModifiedSince = ClockUtility.Parse(value); break; case "referer": case "referrer": http.Referer = value; break; case "transfer-encoding": http.TransferEncoding = value; break; case "user-agent": http.UserAgent = value; break; default: http.Headers.Add(header.Key, header.Value); break; } } catch { } } if (!TextUtility.IsBlank(properties["TransferEncoding"])) http.TransferEncoding = properties["TransferEncoding"]; if (!TextUtility.IsBlank(properties["ContentType"])) http.ContentType = properties["ContentType"]; if (!TextUtility.IsBlank(properties["UserAgent"])) http.UserAgent = properties["UserAgent"]; if (!TextUtility.IsBlank(properties["Referer"])) http.Referer = properties["Referer"]; // Cookies。 if (cookies != null) http.CookieContainer = cookies; // 对 POST 请求加入内容数据。 if (request._method == HttpMethod.POST) { if (request._stream != null) { http.ContentLength = request._contentlength; var rs = http.GetRequestStream(); try { BytesUtility.Read(request._stream, rs, request._progress); } catch { } } else if (request._data != null && request._data.LongLength > 0L) { http.ContentLength = request._data.Length; var rs = http.GetRequestStream(); try { rs.Write(request._data, 0, request._data.Length); } catch (Exception exception) { error = exception; } } } if (error == null) return http; throw error; } /// /// /// /// /// /// private static HttpWebResponse CreateResponse(HttpWebRequest request, HttpResponse response) { var error = (Exception)null; // 发起请求。 var http = (HttpWebResponse)request.GetResponse(); // 读取头。 foreach (var key in http.Headers.AllKeys) response._headers[key] = http.Headers[key]; response._cached = http.IsFromCache; response._cookies = http.Cookies; response._contentlength = http.ContentLength; response._statuscode = http.StatusCode; response._properties["CharacterSet"] = http.CharacterSet; response._properties["ContentEncoding"] = http.ContentEncoding; response._properties["ContentType"] = http.ContentType; response._properties["LastModified"] = ClockUtility.Lucid(http.LastModified); response._properties["Method"] = http.Method; response._properties["ProtocolVersion"] = http.ProtocolVersion.ToString(); response._properties["Url"] = http.ResponseUri.OriginalString; response._properties["Server"] = http.Server; response._properties["StatusDescription"] = http.StatusDescription; // 读取内容。 var rs = http.GetResponseStream(); if (response._stream == null) { var memory = new MemoryStream(); try { BytesUtility.Read(rs, memory, response.ProgressCallback); } catch (Exception exception) { error = exception; } response._data = memory.ToArray(); memory.Dispose(); } else { BytesUtility.Read(rs, response._stream, response.ProgressCallback); } http.Close(); if (error == null) return http; throw error; } /// public Exception LatestException { get { return _lastexception; } } /// public WebException WebException { get { return _lastexception as WebException; } } /// public Exception Send() { var error = null as Exception; if (_request._locked || _response._locked) { error = new InvalidOperationException("存在已启动的请求,无法继续此次请求。"); } else { _request._locked = true; _response._locked = true; try { _lastrequest = CreateRequest(_request); _lastresponse = CreateResponse(_lastrequest, _response); } catch (Exception exception) { error = exception; } _request._locked = false; _response._locked = false; } _lastexception = error; return error; } /// GET public static HttpClient SimpleGet(string url, int timeout = 30000) { var http = new HttpClient(); http.Request.Url = url; http.Request.Timeout = timeout; http.Request.Method = HttpMethod.GET; http.Send(); return http; } /// POST public static HttpClient SimplePost(string url, byte[] data, int timeout = 30000, string type = "application/octet-stream") { var http = new HttpClient(); http.Request.Url = url; http.Request.Timeout = timeout; http.Request.Method = HttpMethod.POST; if (type.NotBlank()) http.Request.ContentType = type; http.Request.Data = data ?? BytesUtility.Empty; http.Send(); return http; } /// POST text/plain public static HttpClient SimpleText(string url, string text, int timeout = 30000, string type = "text/plain") { return SimplePost(url, TextUtility.Bytes(text), timeout, type); } /// POST application/x-www-form-urlencoded public static HttpClient SimpleForm(string url, IDictionary form, int timeout = 30000) { if (form == null) return SimplePost(url, BytesUtility.Empty, timeout, "application/x-www-form-urlencoded"); var cache = new List(); foreach (var i in form) { var key = TextUtility.EncodeUrl(i.Key); var value = TextUtility.EncodeUrl(i.Value); cache.Add(key + "=" + value); } var text = string.Join("&", cache.ToArray()); var data = TextUtility.Bytes(text); return SimplePost(url, data, timeout, "application/x-www-form-urlencoded"); } /// POST application/x-www-form-urlencoded public static HttpClient SimpleForm(string url, TextSet form, int timeout = 30000) { return SimpleForm(url, form.Origin, timeout); } /// 合并表单参数,不包含 Query 的 ? 符号。 public static string MergeForm(IDictionary form) { if (form == null) return ""; if (form.Count < 1) return ""; var cache = new List(); foreach (var i in form) { var key = TextUtility.EncodeUrl(i.Key); var value = TextUtility.EncodeUrl(i.Value); cache.Add(key + "=" + value); } var text = string.Join("&", cache.ToArray()); return text; } /// 合并表单参数,不包含 Query 的 ? 符号。 public static string MergeForm(TextSet form) { if (form == null) return ""; return MergeForm(form.Origin()); } } }