mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-04-11 17:57:19 +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 ();
|
||
}
|
||
}
|