mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-06-14 03:16:38 +10:00
329 lines
11 KiB
C#
329 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace DataUtils
|
|
{
|
|
[ComVisible (true)]
|
|
[InterfaceType (ComInterfaceType.InterfaceIsDual)]
|
|
public interface IHttpResponse: IDisposable
|
|
{
|
|
int Status { get; } // 兼容旧版,等同于 StatusCode
|
|
int StatusCode { get; }
|
|
string StatusText { get; } // 等同于 StatusDescription
|
|
string StatusDescription { get; }
|
|
string ResponseUrl { get; }
|
|
Uri ResponseUri { get; }
|
|
string ContentType { get; }
|
|
long ContentLength { get; } // 注意:JS 中 Number 可表示 2^53 以内整数
|
|
string CharacterSet { get; }
|
|
string ContentEncoding { get; }
|
|
DateTime LastModified { get; }
|
|
string Method { get; } // 原始请求方法 (GET, POST...)
|
|
Version ProtocolVersion { get; } // 如 1.1,JS 中可能转为字符串
|
|
bool IsFromCache { get; }
|
|
bool IsMutuallyAuthenticated { get; }
|
|
string Text ();
|
|
_I_Enumerable Bytes ();
|
|
}
|
|
[ComVisible (true)]
|
|
[ClassInterface (ClassInterfaceType.AutoDual)]
|
|
public class HttpResponse: IHttpResponse, IDisposable
|
|
{
|
|
private readonly byte [] _data;
|
|
private readonly Dictionary<string, string> _headersDict;
|
|
private bool _disposed = false;
|
|
public int Status => StatusCode;
|
|
public int StatusCode { get; private set; }
|
|
public string StatusText => StatusDescription;
|
|
public string StatusDescription { get; private set; }
|
|
public string ResponseUrl => ResponseUri?.ToString ();
|
|
public Uri ResponseUri { get; private set; }
|
|
public string ContentType { get; private set; }
|
|
public long ContentLength { get; private set; }
|
|
public string CharacterSet { get; private set; }
|
|
public string ContentEncoding { get; private set; }
|
|
public DateTime LastModified { get; private set; }
|
|
public string Method { get; private set; }
|
|
public Version ProtocolVersion { get; private set; }
|
|
public bool IsFromCache { get; private set; }
|
|
public bool IsMutuallyAuthenticated { get; private set; }
|
|
public HttpResponse (HttpWebResponse response)
|
|
{
|
|
if (response == null) throw new ArgumentNullException (nameof (response));
|
|
using (var stream = response.GetResponseStream ())
|
|
using (var ms = new MemoryStream ())
|
|
{
|
|
stream.CopyTo (ms);
|
|
_data = ms.ToArray ();
|
|
}
|
|
StatusCode = (int)response.StatusCode;
|
|
StatusDescription = response.StatusDescription;
|
|
ResponseUri = response.ResponseUri;
|
|
Method = response.Method;
|
|
ProtocolVersion = new Version ((ushort)response.ProtocolVersion.Major, (ushort)response.ProtocolVersion.Minor, (ushort)response.ProtocolVersion.Build, (ushort)response.ProtocolVersion.Revision);
|
|
IsFromCache = response.IsFromCache;
|
|
IsMutuallyAuthenticated = response.IsMutuallyAuthenticated;
|
|
ContentType = response.ContentType ?? "";
|
|
ContentLength = response.ContentLength;
|
|
CharacterSet = response.CharacterSet ?? "";
|
|
ContentEncoding = response.ContentEncoding ?? "";
|
|
LastModified = response.LastModified;
|
|
_headersDict = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
|
|
foreach (string key in response.Headers.AllKeys)
|
|
{
|
|
if (!string.IsNullOrEmpty (key))
|
|
_headersDict [key] = response.Headers [key];
|
|
}
|
|
}
|
|
public HttpResponse (int statusCode, string statusDescription, string responseUrl, byte [] data, Dictionary<string, string> headers)
|
|
{
|
|
StatusCode = statusCode;
|
|
StatusDescription = statusDescription ?? "";
|
|
ResponseUri = string.IsNullOrEmpty (responseUrl) ? null : new Uri (responseUrl);
|
|
_data = data ?? new byte [0];
|
|
_headersDict = headers ?? new Dictionary<string, string> ();
|
|
ContentType = GetHeader ("Content-Type") ?? "";
|
|
ContentLength = _data.Length;
|
|
CharacterSet = "";
|
|
ContentEncoding = "";
|
|
LastModified = DateTime.MinValue;
|
|
Method = "";
|
|
ProtocolVersion = new Version (0, 0);
|
|
IsFromCache = false;
|
|
IsMutuallyAuthenticated = false;
|
|
}
|
|
public string GetHeader (string sName)
|
|
{
|
|
if (string.IsNullOrEmpty (sName)) return null;
|
|
try
|
|
{
|
|
return _headersDict [sName];
|
|
}
|
|
catch { return null; }
|
|
}
|
|
public string GetHeadersToJson ()
|
|
{
|
|
return JsonConvert.SerializeObject (_headersDict);
|
|
}
|
|
public string Text ()
|
|
{
|
|
string charset = CharacterSet;
|
|
Encoding enc = Encoding.UTF8;
|
|
if (!string.IsNullOrEmpty (charset))
|
|
{
|
|
try { enc = Encoding.GetEncoding (charset); }
|
|
catch { /* 保持 UTF-8 */ }
|
|
}
|
|
return enc.GetString (_data);
|
|
}
|
|
public _I_Enumerable Bytes ()
|
|
{
|
|
var list = new List<object> ();
|
|
foreach (byte b in _data)
|
|
list.Add (b);
|
|
return new _I_List (list);
|
|
}
|
|
public void Dispose ()
|
|
{
|
|
Dispose (true);
|
|
GC.SuppressFinalize (this);
|
|
}
|
|
protected virtual void Dispose (bool disposing)
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
_disposed = true;
|
|
}
|
|
}
|
|
}
|
|
[ComVisible (true)]
|
|
[ClassInterface (ClassInterfaceType.AutoDual)]
|
|
public class HttpRequest: IDisposable
|
|
{
|
|
private string _method;
|
|
private string _url;
|
|
private Dictionary<string, string> _headers = new Dictionary<string, string> ();
|
|
public int Timeout { get; set; } = 100000; // 毫秒,默认 100 秒
|
|
public int ReadWriteTimeout { get; set; } = 300000; // 读写超时,默认 5 分钟
|
|
public bool AllowAutoRedirect { get; set; } = true;
|
|
public bool AllowWriteStreamBuffering { get; set; } = true;
|
|
public bool KeepAlive { get; set; } = true;
|
|
public int MaximumAutomaticRedirections { get; set; } = 50;
|
|
public string UserAgent
|
|
{
|
|
get { return GetHeader ("User-Agent"); }
|
|
set { SetHeader ("User-Agent", value); }
|
|
}
|
|
public string Referer
|
|
{
|
|
get { return GetHeader ("Referer"); }
|
|
set { SetHeader ("Referer", value); }
|
|
}
|
|
public string ContentType
|
|
{
|
|
get { return GetHeader ("Content-Type"); }
|
|
set { SetHeader ("Content-Type", value); }
|
|
}
|
|
public string Accept
|
|
{
|
|
get { return GetHeader ("Accept"); }
|
|
set { SetHeader ("Accept", value); }
|
|
}
|
|
public IWebProxy Proxy { get; set; } = null;
|
|
public CookieContainer CookieContainer { get; set; } = null;
|
|
private string _httpver = "1.1";
|
|
public string ProtocolVersion
|
|
{
|
|
get { return _httpver; }
|
|
set { _httpver = value; }
|
|
}
|
|
public System.Version ProtVer
|
|
{
|
|
get
|
|
{
|
|
switch (_httpver)
|
|
{
|
|
case "1.0": return HttpVersion.Version10;
|
|
default:
|
|
case "1.1": return HttpVersion.Version11;
|
|
}
|
|
}
|
|
}
|
|
public bool PreAuthenticate { get; set; } = false;
|
|
public ICredentials Credentials { get; set; } = null;
|
|
public bool AutomaticDecompression { get; set; } = false;
|
|
public Action<long, long> UploadProgressCallback { get; set; } = null;
|
|
public void Open (string sMethod, string sUrl)
|
|
{
|
|
_method = sMethod;
|
|
_url = sUrl;
|
|
}
|
|
public void SetHeader (string sName, string sValue) => _headers [sName] = sValue;
|
|
public string GetHeader (string sName) => _headers [sName];
|
|
public string GetHeadersToJson () => JsonConvert.SerializeObject (_headers);
|
|
public void RemoveHeader (string sName) => _headers.Remove (sName);
|
|
public void ClearHeader () => _headers.Clear ();
|
|
public IHttpResponse Send (string sBody, string encoding)
|
|
{
|
|
var req = (HttpWebRequest)WebRequest.Create (_url);
|
|
req.Method = _method;
|
|
req.Timeout = Timeout;
|
|
req.ReadWriteTimeout = ReadWriteTimeout;
|
|
req.AllowAutoRedirect = AllowAutoRedirect;
|
|
req.AllowWriteStreamBuffering = AllowWriteStreamBuffering;
|
|
req.KeepAlive = KeepAlive;
|
|
req.MaximumAutomaticRedirections = MaximumAutomaticRedirections;
|
|
if (Proxy != null) req.Proxy = Proxy;
|
|
if (CookieContainer != null) req.CookieContainer = CookieContainer;
|
|
req.ProtocolVersion = ProtVer;
|
|
req.PreAuthenticate = PreAuthenticate;
|
|
if (Credentials != null) req.Credentials = Credentials;
|
|
if (AutomaticDecompression)
|
|
req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
|
if (_headers.ContainsKey ("User-Agent"))
|
|
req.UserAgent = _headers ["User-Agent"];
|
|
foreach (var h in _headers)
|
|
{
|
|
if (string.Equals (h.Key, "User-Agent", StringComparison.OrdinalIgnoreCase))
|
|
continue;
|
|
if (string.Equals (h.Key, "Content-Type", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string ct = h.Value;
|
|
if (!string.IsNullOrEmpty (sBody) && !ct.Contains ("charset"))
|
|
{
|
|
Encoding enc = Encoding.GetEncoding (encoding);
|
|
ct = ct + "; charset=" + enc.WebName;
|
|
}
|
|
req.ContentType = ct;
|
|
}
|
|
else if (string.Equals (h.Key, "Accept", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
req.Accept = h.Value;
|
|
}
|
|
else if (string.Equals (h.Key, "Referer", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
req.Referer = h.Value;
|
|
}
|
|
else
|
|
{
|
|
req.Headers [h.Key] = h.Value;
|
|
}
|
|
}
|
|
|
|
// 如果没有显式设置 Content-Type 且是 POST/PUT 且有请求体,设置默认值
|
|
bool hasContentType = _headers.Keys.Any (k => string.Equals (k, "Content-Type", StringComparison.OrdinalIgnoreCase));
|
|
if (!hasContentType && (string.Equals (_method, "POST", StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals (_method, "PUT", StringComparison.OrdinalIgnoreCase)) &&
|
|
!string.IsNullOrEmpty (sBody))
|
|
{
|
|
Encoding enc = Encoding.GetEncoding (encoding);
|
|
req.ContentType = "application/x-www-form-urlencoded; charset=" + enc.WebName;
|
|
}
|
|
|
|
// 写入请求体
|
|
if (!string.IsNullOrEmpty (sBody))
|
|
{
|
|
Encoding enc = Encoding.GetEncoding (encoding);
|
|
byte [] bytes = enc.GetBytes (sBody);
|
|
req.ContentLength = bytes.Length;
|
|
using (var stream = req.GetRequestStream ())
|
|
{
|
|
if (UploadProgressCallback != null)
|
|
{
|
|
int totalWritten = 0;
|
|
int bufferSize = 8192;
|
|
for (int offset = 0; offset < bytes.Length; offset += bufferSize)
|
|
{
|
|
int chunkSize = Math.Min (bufferSize, bytes.Length - offset);
|
|
stream.Write (bytes, offset, chunkSize);
|
|
totalWritten += chunkSize;
|
|
UploadProgressCallback (totalWritten, bytes.Length);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stream.Write (bytes, 0, bytes.Length);
|
|
}
|
|
}
|
|
}
|
|
using (var res = (HttpWebResponse)req.GetResponse ())
|
|
{
|
|
return new HttpResponse (res);
|
|
}
|
|
}
|
|
public void SendAsync (string sBody, string encoding, object pfResolve, object pfReject)
|
|
{
|
|
System.Threading.ThreadPool.QueueUserWorkItem (delegate
|
|
{
|
|
try
|
|
{
|
|
JsUtils.Call (pfResolve, Send (sBody, encoding));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
JsUtils.Call (pfReject, new _I_Exception (ex));
|
|
}
|
|
});
|
|
}
|
|
public void Dispose () { }
|
|
}
|
|
[ComVisible (true)]
|
|
[ClassInterface (ClassInterfaceType.AutoDual)]
|
|
public class _I_Http
|
|
{
|
|
public HttpRequest CreateHttpRequest () => new HttpRequest ();
|
|
}
|
|
[ComVisible (true)]
|
|
[ClassInterface (ClassInterfaceType.AutoDual)]
|
|
public class _I_Web
|
|
{
|
|
public _I_Http Http => new _I_Http ();
|
|
}
|
|
}
|