From 8710150897183a406829716bda3e90a4c72307ae Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Mon, 8 Jan 2024 20:19:05 +0800 Subject: [PATCH] use reflect to reduce code size --- .../Snap.Hutao.Win32/Interop.Libraries.cs | 54 -- .../Snap.Hutao.Win32/Interop.WinHttp.cs | 624 ------------------ .../IO/Http/DynamicProxy/DynamicHttpProxy.cs | 24 +- .../IO/Http/DynamicProxy/FailedProxyCache.cs | 138 ---- .../Core/IO/Http/DynamicProxy/HttpNoProxy.cs | 15 - .../IO/Http/DynamicProxy/HttpWindowsProxy.cs | 325 --------- .../IO/Http/DynamicProxy/IMultiWebProxy.cs | 16 - .../Core/IO/Http/DynamicProxy/MultiProxy.cs | 268 -------- .../Core/IO/Http/DynamicProxy/SimpleRegex.cs | 73 -- .../Core/IO/Http/DynamicProxy/UriScheme.cs | 24 - .../Http/DynamicProxy/WinInetProxyHelper.cs | 179 ----- ...{RegistryMonitor.cs => RegistryWatcher.cs} | 8 +- 12 files changed, 19 insertions(+), 1729 deletions(-) delete mode 100644 src/Snap.Hutao/Snap.Hutao.Win32/Interop.Libraries.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao.Win32/Interop.WinHttp.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/FailedProxyCache.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/HttpNoProxy.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/HttpWindowsProxy.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/IMultiWebProxy.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/MultiProxy.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/SimpleRegex.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/UriScheme.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/WinInetProxyHelper.cs rename src/Snap.Hutao/Snap.Hutao/Core/Shell/{RegistryMonitor.cs => RegistryWatcher.cs} (94%) diff --git a/src/Snap.Hutao/Snap.Hutao.Win32/Interop.Libraries.cs b/src/Snap.Hutao/Snap.Hutao.Win32/Interop.Libraries.cs deleted file mode 100644 index 32c7a1f4..00000000 --- a/src/Snap.Hutao/Snap.Hutao.Win32/Interop.Libraries.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace Snap.Hutao.Win32; - -internal static partial class Interop -{ - internal static partial class Libraries - { - internal const string Activeds = "activeds.dll"; - internal const string Advapi32 = "advapi32.dll"; - internal const string Authz = "authz.dll"; - internal const string BCrypt = "BCrypt.dll"; - internal const string Credui = "credui.dll"; - internal const string Crypt32 = "crypt32.dll"; - internal const string CryptUI = "cryptui.dll"; - internal const string Dnsapi = "dnsapi.dll"; - internal const string Dsrole = "dsrole.dll"; - internal const string Gdi32 = "gdi32.dll"; - internal const string HttpApi = "httpapi.dll"; - internal const string IpHlpApi = "iphlpapi.dll"; - internal const string Kernel32 = "kernel32.dll"; - internal const string Logoncli = "logoncli.dll"; - internal const string Mswsock = "mswsock.dll"; - internal const string NCrypt = "ncrypt.dll"; - internal const string Netapi32 = "netapi32.dll"; - internal const string Netutils = "netutils.dll"; - internal const string NtDll = "ntdll.dll"; - internal const string Odbc32 = "odbc32.dll"; - internal const string Ole32 = "ole32.dll"; - internal const string OleAut32 = "oleaut32.dll"; - internal const string Pdh = "pdh.dll"; - internal const string Secur32 = "secur32.dll"; - internal const string Shell32 = "shell32.dll"; - internal const string SspiCli = "sspicli.dll"; - internal const string User32 = "user32.dll"; - internal const string Version = "version.dll"; - internal const string WebSocket = "websocket.dll"; - internal const string Wevtapi = "wevtapi.dll"; - internal const string WinHttp = "winhttp.dll"; - internal const string WinMM = "winmm.dll"; - internal const string Wkscli = "wkscli.dll"; - internal const string Wldap32 = "wldap32.dll"; - internal const string Ws2_32 = "ws2_32.dll"; - internal const string Wtsapi32 = "wtsapi32.dll"; - internal const string CompressionNative = "System.IO.Compression.Native"; - internal const string GlobalizationNative = "System.Globalization.Native"; - internal const string MsQuic = "msquic.dll"; - internal const string HostPolicy = "hostpolicy"; - internal const string Ucrtbase = "ucrtbase.dll"; - internal const string Xolehlp = "xolehlp.dll"; - internal const string Comdlg32 = "comdlg32.dll"; - internal const string Gdiplus = "gdiplus.dll"; - internal const string Oleaut32 = "oleaut32.dll"; - internal const string Winspool = "winspool.drv"; - } -} diff --git a/src/Snap.Hutao/Snap.Hutao.Win32/Interop.WinHttp.cs b/src/Snap.Hutao/Snap.Hutao.Win32/Interop.WinHttp.cs deleted file mode 100644 index 84d74504..00000000 --- a/src/Snap.Hutao/Snap.Hutao.Win32/Interop.WinHttp.cs +++ /dev/null @@ -1,624 +0,0 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; -using System.Text; - -namespace Snap.Hutao.Win32; - -internal static partial class Interop -{ - internal static partial class WinHttp - { - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - public static partial SafeWinHttpHandle WinHttpOpen( - IntPtr userAgent, - uint accessType, - string? proxyName, - string? proxyBypass, int flags); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpCloseHandle( - IntPtr handle); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - public static partial SafeWinHttpHandle WinHttpConnect( - SafeWinHttpHandle sessionHandle, - string serverName, - ushort serverPort, - uint reserved); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - public static partial SafeWinHttpHandle WinHttpOpenRequest( - SafeWinHttpHandle connectHandle, - string verb, - string objectName, - string? version, - string referrer, - string acceptTypes, - uint flags); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpAddRequestHeaders( - SafeWinHttpHandle requestHandle, -#if NET7_0_OR_GREATER - [MarshalUsing(typeof(SimpleStringBufferMarshaller))] StringBuilder headers, -#else -#pragma warning disable CA1838 // Uses pooled StringBuilder - [In] StringBuilder headers, -#pragma warning restore CA1838 // Uses pooled StringBuilder -#endif - uint headersLength, - uint modifiers); - -#if NET7_0_OR_GREATER - [CustomMarshaller(typeof(StringBuilder), MarshalMode.ManagedToUnmanagedIn, typeof(SimpleStringBufferMarshaller))] - private static unsafe class SimpleStringBufferMarshaller - { - public static void* ConvertToUnmanaged(StringBuilder builder) - { - int length = builder.Length + 1; - void* value = NativeMemory.Alloc(sizeof(char) * (nuint)length); - Span buffer = new(value, length); - buffer.Clear(); - builder.CopyTo(0, buffer, length - 1); - return value; - } - - public static void Free(void* value) => NativeMemory.Free(value); - } -#endif - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpAddRequestHeaders( - SafeWinHttpHandle requestHandle, - string headers, - uint headersLength, - uint modifiers); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpSendRequest( - SafeWinHttpHandle requestHandle, - IntPtr headers, - uint headersLength, - IntPtr optional, - uint optionalLength, - uint totalLength, - IntPtr context); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpReceiveResponse( - SafeWinHttpHandle requestHandle, - IntPtr reserved); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpQueryDataAvailable( - SafeWinHttpHandle requestHandle, - IntPtr parameterIgnoredAndShouldBeNullForAsync); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpReadData( - SafeWinHttpHandle requestHandle, - IntPtr buffer, - uint bufferSize, - IntPtr parameterIgnoredAndShouldBeNullForAsync); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpQueryHeaders( - SafeWinHttpHandle requestHandle, - uint infoLevel, - string name, - IntPtr buffer, - ref uint bufferLength, - ref uint index); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpQueryHeaders( - SafeWinHttpHandle requestHandle, - uint infoLevel, - string name, - ref uint number, - ref uint bufferLength, - IntPtr index); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpQueryOption( - SafeWinHttpHandle handle, - uint option, - ref IntPtr buffer, - ref uint bufferSize); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpQueryOption( - SafeWinHttpHandle handle, - uint option, - IntPtr buffer, - ref uint bufferSize); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpQueryOption( - SafeWinHttpHandle handle, - uint option, - ref uint buffer, - ref uint bufferSize); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpWriteData( - SafeWinHttpHandle requestHandle, - IntPtr buffer, - uint bufferSize, - IntPtr parameterIgnoredAndShouldBeNullForAsync); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpSetOption( - SafeWinHttpHandle handle, - uint option, - ref uint optionData, - uint optionLength = sizeof(uint)); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpSetOption( - SafeWinHttpHandle handle, - uint option, - IntPtr optionData, - uint optionLength); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpSetCredentials( - SafeWinHttpHandle requestHandle, - uint authTargets, - uint authScheme, - string? userName, - string? password, - IntPtr reserved); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpQueryAuthSchemes( - SafeWinHttpHandle requestHandle, - out uint supportedSchemes, - out uint firstScheme, - out uint authTarget); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpSetTimeouts( - SafeWinHttpHandle handle, - int resolveTimeout, - int connectTimeout, - int sendTimeout, - int receiveTimeout); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpGetIEProxyConfigForCurrentUser( - out WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool WinHttpGetProxyForUrl( - SafeWinHttpHandle? sessionHandle, - string url, - ref WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, - out WINHTTP_PROXY_INFO proxyInfo); - - [LibraryImport(Libraries.WinHttp, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - public static partial IntPtr WinHttpSetStatusCallback( - SafeWinHttpHandle handle, - WINHTTP_STATUS_CALLBACK callback, - uint notificationFlags, - IntPtr reserved); - } - internal static partial class WinHttp - { - internal class SafeWinHttpHandle : SafeHandleZeroOrMinusOneIsInvalid - { - private SafeWinHttpHandle? _parentHandle; - - public SafeWinHttpHandle() : base(true) - { - } - - public static void DisposeAndClearHandle(ref SafeWinHttpHandle? safeHandle) - { - if (safeHandle != null) - { - safeHandle.Dispose(); - safeHandle = null; - } - } - - public void SetParentHandle(SafeWinHttpHandle parentHandle) - { - Debug.Assert(_parentHandle == null); - Debug.Assert(parentHandle != null); - Debug.Assert(!parentHandle.IsInvalid); - - bool ignore = false; - parentHandle.DangerousAddRef(ref ignore); - - _parentHandle = parentHandle; - } - - // Important: WinHttp API calls should not happen while another WinHttp call for the same handle did not - // return. During finalization that was not initiated by the Dispose pattern we don't expect any other WinHttp - // calls in progress. - protected override bool ReleaseHandle() - { - if (_parentHandle != null) - { - _parentHandle.DangerousRelease(); - _parentHandle = null; - } - - return Interop.WinHttp.WinHttpCloseHandle(handle); - } - } - } - internal static partial class WinHttp - { - public const uint ERROR_SUCCESS = 0; - public const uint ERROR_FILE_NOT_FOUND = 2; - public const uint ERROR_INVALID_HANDLE = 6; - public const uint ERROR_INVALID_PARAMETER = 87; - public const uint ERROR_INSUFFICIENT_BUFFER = 122; - public const uint ERROR_NOT_FOUND = 1168; - public const uint ERROR_WINHTTP_INVALID_OPTION = 12009; - public const uint ERROR_WINHTTP_LOGIN_FAILURE = 12015; - public const uint ERROR_WINHTTP_OPERATION_CANCELLED = 12017; - public const uint ERROR_WINHTTP_INCORRECT_HANDLE_STATE = 12019; - public const uint ERROR_WINHTTP_CONNECTION_ERROR = 12030; - public const uint ERROR_WINHTTP_RESEND_REQUEST = 12032; - public const uint ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED = 12044; - public const uint ERROR_WINHTTP_HEADER_NOT_FOUND = 12150; - public const uint ERROR_WINHTTP_SECURE_FAILURE = 12175; - public const uint ERROR_WINHTTP_AUTODETECTION_FAILED = 12180; - - public const uint WINHTTP_OPTION_PROXY = 38; - public const uint WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0; - public const uint WINHTTP_ACCESS_TYPE_NO_PROXY = 1; - public const uint WINHTTP_ACCESS_TYPE_NAMED_PROXY = 3; - public const uint WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY = 4; - - public const uint WINHTTP_AUTOPROXY_AUTO_DETECT = 0x00000001; - public const uint WINHTTP_AUTOPROXY_CONFIG_URL = 0x00000002; - public const uint WINHTTP_AUTOPROXY_HOST_KEEPCASE = 0x00000004; - public const uint WINHTTP_AUTOPROXY_HOST_LOWERCASE = 0x00000008; - public const uint WINHTTP_AUTOPROXY_RUN_INPROCESS = 0x00010000; - public const uint WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY = 0x00020000; - public const uint WINHTTP_AUTOPROXY_NO_DIRECTACCESS = 0x00040000; - public const uint WINHTTP_AUTOPROXY_NO_CACHE_CLIENT = 0x00080000; - public const uint WINHTTP_AUTOPROXY_NO_CACHE_SVC = 0x00100000; - public const uint WINHTTP_AUTOPROXY_SORT_RESULTS = 0x00400000; - - public const uint WINHTTP_AUTO_DETECT_TYPE_DHCP = 0x00000001; - public const uint WINHTTP_AUTO_DETECT_TYPE_DNS_A = 0x00000002; - - public const string WINHTTP_NO_PROXY_NAME = null; - public const string WINHTTP_NO_PROXY_BYPASS = null; - - public const uint WINHTTP_ADDREQ_FLAG_ADD = 0x20000000; - public const uint WINHTTP_ADDREQ_FLAG_REPLACE = 0x80000000; - - public const string WINHTTP_NO_REFERER = null; - public const string WINHTTP_DEFAULT_ACCEPT_TYPES = null; - - public const ushort INTERNET_DEFAULT_PORT = 0; - public const ushort INTERNET_DEFAULT_HTTP_PORT = 80; - public const ushort INTERNET_DEFAULT_HTTPS_PORT = 443; - - public const uint WINHTTP_FLAG_SECURE = 0x00800000; - public const uint WINHTTP_FLAG_ESCAPE_DISABLE = 0x00000040; - public const uint WINHTTP_FLAG_AUTOMATIC_CHUNKING = 0x00000200; - - public const uint WINHTTP_QUERY_FLAG_NUMBER = 0x20000000; - public const uint WINHTTP_QUERY_VERSION = 18; - public const uint WINHTTP_QUERY_STATUS_CODE = 19; - public const uint WINHTTP_QUERY_STATUS_TEXT = 20; - public const uint WINHTTP_QUERY_RAW_HEADERS = 21; - public const uint WINHTTP_QUERY_RAW_HEADERS_CRLF = 22; - public const uint WINHTTP_QUERY_FLAG_TRAILERS = 0x02000000; - public const uint WINHTTP_QUERY_CONTENT_ENCODING = 29; - public const uint WINHTTP_QUERY_SET_COOKIE = 43; - public const uint WINHTTP_QUERY_CUSTOM = 65535; - public const string WINHTTP_HEADER_NAME_BY_INDEX = null; - public const byte[] WINHTTP_NO_OUTPUT_BUFFER = null; - - public const uint WINHTTP_OPTION_DECOMPRESSION = 118; - public const uint WINHTTP_DECOMPRESSION_FLAG_GZIP = 0x00000001; - public const uint WINHTTP_DECOMPRESSION_FLAG_DEFLATE = 0x00000002; - public const uint WINHTTP_DECOMPRESSION_FLAG_ALL = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - - public const uint WINHTTP_OPTION_REDIRECT_POLICY = 88; - public const uint WINHTTP_OPTION_REDIRECT_POLICY_NEVER = 0; - public const uint WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP = 1; - public const uint WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS = 2; - public const uint WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS = 89; - - public const uint WINHTTP_OPTION_MAX_CONNS_PER_SERVER = 73; - public const uint WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER = 74; - - public const uint WINHTTP_OPTION_DISABLE_FEATURE = 63; - public const uint WINHTTP_DISABLE_COOKIES = 0x00000001; - public const uint WINHTTP_DISABLE_REDIRECTS = 0x00000002; - public const uint WINHTTP_DISABLE_AUTHENTICATION = 0x00000004; - public const uint WINHTTP_DISABLE_KEEP_ALIVE = 0x00000008; - - public const uint WINHTTP_OPTION_ENABLE_FEATURE = 79; - public const uint WINHTTP_ENABLE_SSL_REVOCATION = 0x00000001; - - public const uint WINHTTP_OPTION_CLIENT_CERT_CONTEXT = 47; - public const uint WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST = 94; - public const uint WINHTTP_OPTION_SERVER_CERT_CONTEXT = 78; - public const uint WINHTTP_OPTION_SECURITY_FLAGS = 31; - public const uint WINHTTP_OPTION_SECURE_PROTOCOLS = 84; - public const uint WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 = 0x00000008; - public const uint WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 = 0x00000020; - public const uint WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 = 0x00000080; - public const uint WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 = 0x00000200; - public const uint WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 = 0x00000800; - public const uint WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 = 0x00002000; - - public const uint SECURITY_FLAG_IGNORE_UNKNOWN_CA = 0x00000100; - public const uint SECURITY_FLAG_IGNORE_CERT_DATE_INVALID = 0x00002000; - public const uint SECURITY_FLAG_IGNORE_CERT_CN_INVALID = 0x00001000; - public const uint SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE = 0x00000200; - - public const uint WINHTTP_OPTION_AUTOLOGON_POLICY = 77; - public const uint WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM = 0; // default creds only sent to intranet servers (default) - public const uint WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW = 1; // default creds set to all servers - public const uint WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH = 2; // default creds never sent - - public const uint WINHTTP_AUTH_SCHEME_BASIC = 0x00000001; - public const uint WINHTTP_AUTH_SCHEME_NTLM = 0x00000002; - public const uint WINHTTP_AUTH_SCHEME_PASSPORT = 0x00000004; - public const uint WINHTTP_AUTH_SCHEME_DIGEST = 0x00000008; - public const uint WINHTTP_AUTH_SCHEME_NEGOTIATE = 0x00000010; - - public const uint WINHTTP_AUTH_TARGET_SERVER = 0x00000000; - public const uint WINHTTP_AUTH_TARGET_PROXY = 0x00000001; - - public const uint WINHTTP_OPTION_USERNAME = 0x1000; - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. It is property descriptor, not secret value.")] - public const uint WINHTTP_OPTION_PASSWORD = 0x1001; - public const uint WINHTTP_OPTION_PROXY_USERNAME = 0x1002; - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. It is property descriptor, not secret value.")] - public const uint WINHTTP_OPTION_PROXY_PASSWORD = 0x1003; - - public const uint WINHTTP_OPTION_SERVER_SPN_USED = 106; - public const uint WINHTTP_OPTION_SERVER_CBT = 108; - - public const uint WINHTTP_OPTION_CONNECT_TIMEOUT = 3; - public const uint WINHTTP_OPTION_SEND_TIMEOUT = 5; - public const uint WINHTTP_OPTION_RECEIVE_TIMEOUT = 6; - - public const uint WINHTTP_OPTION_URL = 34; - - public const uint WINHTTP_OPTION_MAX_RESPONSE_HEADER_SIZE = 91; - public const uint WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE = 92; - public const uint WINHTTP_OPTION_CONNECTION_INFO = 93; - - public const uint WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS = 111; - - public const uint WINHTTP_OPTION_ENABLE_HTTP2_PLUS_CLIENT_CERT = 161; - public const uint WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL = 133; - public const uint WINHTTP_OPTION_HTTP_PROTOCOL_USED = 134; - public const uint WINHTTP_PROTOCOL_FLAG_HTTP2 = 0x1; - public const uint WINHTTP_HTTP2_PLUS_CLIENT_CERT_FLAG = 0x1; - public const uint WINHTTP_OPTION_DISABLE_STREAM_QUEUE = 139; - - public const uint WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET = 114; - public const uint WINHTTP_OPTION_WEB_SOCKET_CLOSE_TIMEOUT = 115; - public const uint WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL = 116; - - public const uint WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE = 122; - public const uint WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE = 123; - - public const uint WINHTTP_OPTION_TCP_KEEPALIVE = 152; - public const uint WINHTTP_OPTION_STREAM_ERROR_CODE = 159; - public const uint WINHTTP_OPTION_REQUIRE_STREAM_END = 160; - - public enum WINHTTP_WEB_SOCKET_BUFFER_TYPE - { - WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE = 0, - WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE = 1, - WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE = 2, - WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE = 3, - WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE = 4 - } - - public const uint WINHTTP_OPTION_CONTEXT_VALUE = 45; - - public const uint WINHTTP_FLAG_ASYNC = 0x10000000; - - public const uint WINHTTP_CALLBACK_STATUS_RESOLVING_NAME = 0x00000001; - public const uint WINHTTP_CALLBACK_STATUS_NAME_RESOLVED = 0x00000002; - public const uint WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER = 0x00000004; - public const uint WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER = 0x00000008; - public const uint WINHTTP_CALLBACK_STATUS_SENDING_REQUEST = 0x00000010; - public const uint WINHTTP_CALLBACK_STATUS_REQUEST_SENT = 0x00000020; - public const uint WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE = 0x00000040; - public const uint WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED = 0x00000080; - public const uint WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION = 0x00000100; - public const uint WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED = 0x00000200; - public const uint WINHTTP_CALLBACK_STATUS_HANDLE_CREATED = 0x00000400; - public const uint WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING = 0x00000800; - public const uint WINHTTP_CALLBACK_STATUS_DETECTING_PROXY = 0x00001000; - public const uint WINHTTP_CALLBACK_STATUS_REDIRECT = 0x00004000; - public const uint WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE = 0x00008000; - public const uint WINHTTP_CALLBACK_STATUS_SECURE_FAILURE = 0x00010000; - public const uint WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE = 0x00020000; - public const uint WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE = 0x00040000; - public const uint WINHTTP_CALLBACK_STATUS_READ_COMPLETE = 0x00080000; - public const uint WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE = 0x00100000; - public const uint WINHTTP_CALLBACK_STATUS_REQUEST_ERROR = 0x00200000; - public const uint WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE = 0x00400000; - public const uint WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE = 0x01000000; - public const uint WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE = 0x02000000; - public const uint WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE = 0x04000000; - - public const uint WINHTTP_CALLBACK_FLAG_SEND_REQUEST = - WINHTTP_CALLBACK_STATUS_SENDING_REQUEST | - WINHTTP_CALLBACK_STATUS_REQUEST_SENT; - public const uint WINHTTP_CALLBACK_FLAG_HANDLES = - WINHTTP_CALLBACK_STATUS_HANDLE_CREATED | - WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING; - public const uint WINHTTP_CALLBACK_FLAG_REDIRECT = WINHTTP_CALLBACK_STATUS_REDIRECT; - public const uint WINHTTP_CALLBACK_FLAG_SECURE_FAILURE = WINHTTP_CALLBACK_STATUS_SECURE_FAILURE; - public const uint WINHTTP_CALLBACK_FLAG_SENDREQUEST_COMPLETE = WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE; - public const uint WINHTTP_CALLBACK_FLAG_HEADERS_AVAILABLE = WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE; - public const uint WINHTTP_CALLBACK_FLAG_DATA_AVAILABLE = WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE; - public const uint WINHTTP_CALLBACK_FLAG_READ_COMPLETE = WINHTTP_CALLBACK_STATUS_READ_COMPLETE; - public const uint WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; - public const uint WINHTTP_CALLBACK_FLAG_REQUEST_ERROR = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; - public const uint WINHTTP_CALLBACK_FLAG_GETPROXYFORURL_COMPLETE = WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE; - public const uint WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS = - WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE | - WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE | - WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE | - WINHTTP_CALLBACK_STATUS_READ_COMPLETE | - WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE | - WINHTTP_CALLBACK_STATUS_REQUEST_ERROR | - WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE; - public const uint WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS = 0xFFFFFFFF; - - public const uint WININET_E_CONNECTION_RESET = 0x80072EFF; - - public const int WINHTTP_INVALID_STATUS_CALLBACK = -1; - public delegate void WINHTTP_STATUS_CALLBACK( - IntPtr handle, - IntPtr context, - uint internetStatus, - IntPtr statusInformation, - uint statusInformationLength); - -#if NET7_0_OR_GREATER - [NativeMarshalling(typeof(Marshaller))] -#endif - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct WINHTTP_AUTOPROXY_OPTIONS - { - public uint Flags; - public uint AutoDetectFlags; - [MarshalAs(UnmanagedType.LPWStr)] - public string? AutoConfigUrl; - public IntPtr Reserved1; - public uint Reserved2; - [MarshalAs(UnmanagedType.Bool)] - public bool AutoLoginIfChallenged; -#if NET7_0_OR_GREATER - [CustomMarshaller(typeof(WINHTTP_AUTOPROXY_OPTIONS), MarshalMode.Default, typeof(Marshaller))] - public static class Marshaller - { - public static Native ConvertToUnmanaged(WINHTTP_AUTOPROXY_OPTIONS managed) => new(managed); - - public static WINHTTP_AUTOPROXY_OPTIONS ConvertToManaged(Native native) => native.ToManaged(); - - public static void Free(Native native) => native.FreeNative(); - - public readonly struct Native - { - private readonly uint Flags; - private readonly uint AutoDetectFlags; - private readonly IntPtr AutoConfigUrl; - private readonly IntPtr Reserved1; - private readonly uint Reserved2; - private readonly int AutoLoginIfChallenged; - - public Native(WINHTTP_AUTOPROXY_OPTIONS managed) - { - Flags = managed.Flags; - AutoDetectFlags = managed.AutoDetectFlags; - AutoConfigUrl = managed.AutoConfigUrl is not null ? Marshal.StringToCoTaskMemUni(managed.AutoConfigUrl) : IntPtr.Zero; - Reserved1 = managed.Reserved1; - Reserved2 = managed.Reserved2; - AutoLoginIfChallenged = managed.AutoLoginIfChallenged ? 1 : 0; - } - - public WINHTTP_AUTOPROXY_OPTIONS ToManaged() - { - return new WINHTTP_AUTOPROXY_OPTIONS - { - Flags = Flags, - AutoDetectFlags = AutoDetectFlags, - AutoConfigUrl = AutoConfigUrl != IntPtr.Zero ? Marshal.PtrToStringUni(AutoConfigUrl) : null, - Reserved1 = Reserved1, - Reserved2 = Reserved2, - AutoLoginIfChallenged = AutoLoginIfChallenged != 0 - }; - } - - public void FreeNative() - { - Marshal.FreeCoTaskMem(AutoConfigUrl); - } - } - } -#endif - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct WINHTTP_CURRENT_USER_IE_PROXY_CONFIG - { - public int AutoDetect; - public IntPtr AutoConfigUrl; - public IntPtr Proxy; - public IntPtr ProxyBypass; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct WINHTTP_PROXY_INFO - { - public uint AccessType; - public IntPtr Proxy; - public IntPtr ProxyBypass; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct WINHTTP_ASYNC_RESULT - { - public IntPtr dwResult; - public uint dwError; - } - - - [StructLayout(LayoutKind.Sequential)] - public struct tcp_keepalive - { - public uint onoff; - public uint keepalivetime; - public uint keepaliveinterval; - } - - public const uint API_RECEIVE_RESPONSE = 1; - public const uint API_QUERY_DATA_AVAILABLE = 2; - public const uint API_READ_DATA = 3; - public const uint API_WRITE_DATA = 4; - public const uint API_SEND_REQUEST = 5; - - public enum WINHTTP_WEB_SOCKET_OPERATION - { - WINHTTP_WEB_SOCKET_SEND_OPERATION = 0, - WINHTTP_WEB_SOCKET_RECEIVE_OPERATION = 1, - WINHTTP_WEB_SOCKET_CLOSE_OPERATION = 2, - WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION = 3 - } - } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs index 967180c3..808ae2d1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs @@ -3,23 +3,23 @@ using Snap.Hutao.Core.Shell; using System.Net; +using System.Reflection; namespace Snap.Hutao.Core.IO.Http.DynamicProxy; [Injection(InjectAs.Singleton)] internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable { - private readonly RegistryMonitor registryMonitor; + private readonly RegistryWatcher watcher; - private IWebProxy innerProxy; + private IWebProxy innerProxy = default!; public DynamicHttpProxy() { - HttpWindowsProxy.TryCreate(out IWebProxy? proxy); - innerProxy = proxy ?? new HttpNoProxy(); + UpdateProxy(); - registryMonitor = RegistryMonitor.Create(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections", OnRegistryChanged); - registryMonitor.Start(); + watcher = RegistryWatcher.Create(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections", OnRegistryChanged); + watcher.Start(); } /// @@ -50,8 +50,14 @@ internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable public void UpdateProxy() { - HttpWindowsProxy.TryCreate(out IWebProxy? proxy); - InnerProxy = proxy ?? new HttpNoProxy(); + Assembly httpNamespace = Assembly.Load("System.Net.Http"); + Type? systemProxyInfoType = httpNamespace.GetType("System.Net.Http.SystemProxyInfo"); + ArgumentNullException.ThrowIfNull(systemProxyInfoType); + MethodInfo? constructSystemProxyMethod = systemProxyInfoType.GetMethod("ConstructSystemProxy", BindingFlags.Static | BindingFlags.Public); + ArgumentNullException.ThrowIfNull(constructSystemProxyMethod); + IWebProxy? proxy = (IWebProxy?)constructSystemProxyMethod.Invoke(null, null); + ArgumentNullException.ThrowIfNull(proxy); + InnerProxy = proxy; } public Uri? GetProxy(Uri destination) @@ -71,7 +77,7 @@ internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable disposable.Dispose(); } - registryMonitor.Dispose(); + watcher.Dispose(); } private void OnRegistryChanged(object? sender, EventArgs e) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/FailedProxyCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/FailedProxyCache.cs deleted file mode 100644 index f2fdead6..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/FailedProxyCache.cs +++ /dev/null @@ -1,138 +0,0 @@ -#pragma warning disable -using System.Collections.Concurrent; -using System.Runtime.CompilerServices; - -namespace Snap.Hutao.Core.IO.Http.DynamicProxy; - -/// -/// Copied from https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/FailedProxyCache.cs -/// -internal sealed class FailedProxyCache -{ - /// - /// When returned by , indicates a proxy is immediately usable. - /// - public const long Immediate = 0; - - // If a proxy fails, time out 30 minutes. WinHTTP and Firefox both use this. - private const int FailureTimeoutInMilliseconds = 1000 * 60 * 30; - - // Scan through the failures and flush any that have expired every 5 minutes. - private const int FlushFailuresTimerInMilliseconds = 1000 * 60 * 5; - - // _failedProxies will only be flushed (rare but somewhat expensive) if we have more than this number of proxies in our dictionary. See Cleanup() for details. - private const int LargeProxyConfigBoundary = 8; - - // Value is the Environment.TickCount64 to remove the proxy from the failure list. - private readonly ConcurrentDictionary _failedProxies = new ConcurrentDictionary(); - - // When Environment.TickCount64 >= _nextFlushTicks, cause a flush. - private long _nextFlushTicks = Environment.TickCount64 + FlushFailuresTimerInMilliseconds; - - // This lock can be folded into _nextFlushTicks for space optimization, but - // this class should only have a single instance so would rather have clarity. - private SpinLock _flushLock = new SpinLock(enableThreadOwnerTracking: false); // mutable struct; do not make this readonly - - /// - /// Checks when a proxy will become usable. - /// - /// The of the proxy to check. - /// If the proxy can be used, . Otherwise, the next that it should be used. - public long GetProxyRenewTicks(Uri uri) - { - Cleanup(); - - // If not failed, ready immediately. - if (!_failedProxies.TryGetValue(uri, out long renewTicks)) - { - return Immediate; - } - - // If we haven't reached out renew time, the proxy can't be used. - if (Environment.TickCount64 < renewTicks) - { - return renewTicks; - } - - // Renew time reached, we can remove the proxy from the cache. - if (TryRenewProxy(uri, renewTicks)) - { - return Immediate; - } - - // Another thread updated the cache before we could remove it. - // We can't know if this is a removal or an update, so check again. - return _failedProxies.TryGetValue(uri, out renewTicks) ? renewTicks : Immediate; - } - - /// - /// Sets a proxy as failed, to avoid trying it again for some time. - /// - /// The URI of the proxy. - public void SetProxyFailed(Uri uri) - { - _failedProxies[uri] = Environment.TickCount64 + FailureTimeoutInMilliseconds; - Cleanup(); - } - - /// - /// Renews a proxy prior to its period expiring. Used when all proxies are failed to renew the proxy closest to being renewed. - /// - /// The of the proxy to renew. - /// The current renewal time for the proxy. If the value has changed from this, the proxy will not be renewed. - public bool TryRenewProxy(Uri uri, long renewTicks) => - _failedProxies.TryRemove(new KeyValuePair(uri, renewTicks)); - - /// - /// Cleans up any old proxies that should no longer be marked as failing. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Cleanup() - { - if (_failedProxies.Count > LargeProxyConfigBoundary && Environment.TickCount64 >= Interlocked.Read(ref _nextFlushTicks)) - { - CleanupHelper(); - } - } - - /// - /// Cleans up any old proxies that should no longer be marked as failing. - /// - /// - /// I expect this to never be called by in a production system. It is only needed in the case - /// that a system has a very large number of proxies that the PAC script cycles through. It is moderately expensive, - /// so it's only run periodically and is disabled until we exceed failed proxies. - /// - [MethodImpl(MethodImplOptions.NoInlining)] - private void CleanupHelper() - { - bool lockTaken = false; - try - { - _flushLock.TryEnter(ref lockTaken); - if (!lockTaken) - { - return; - } - - long curTicks = Environment.TickCount64; - - foreach (KeyValuePair kvp in _failedProxies) - { - if (curTicks >= kvp.Value) - { - ((ICollection>)_failedProxies).Remove(kvp); - } - } - } - finally - { - if (lockTaken) - { - Interlocked.Exchange(ref _nextFlushTicks, Environment.TickCount64 + FlushFailuresTimerInMilliseconds); - _flushLock.Exit(false); - } - } - } -} -#pragma warning restore \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/HttpNoProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/HttpNoProxy.cs deleted file mode 100644 index 62e624c0..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/HttpNoProxy.cs +++ /dev/null @@ -1,15 +0,0 @@ -#pragma warning disable -using System.Net; - -namespace Snap.Hutao.Core.IO.Http.DynamicProxy; - -/// -/// Copied from https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpNoProxy.cs -/// -internal sealed class HttpNoProxy : IWebProxy -{ - public ICredentials? Credentials { get; set; } - public Uri? GetProxy(Uri destination) => null; - public bool IsBypassed(Uri host) => true; -} -#pragma warning restore \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/HttpWindowsProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/HttpWindowsProxy.cs deleted file mode 100644 index a01b27a4..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/HttpWindowsProxy.cs +++ /dev/null @@ -1,325 +0,0 @@ -#pragma warning disable -using System.Net; -using System.Net.NetworkInformation; -using System.Runtime.InteropServices; -using static Snap.Hutao.Win32.Interop.WinHttp; - -namespace Snap.Hutao.Core.IO.Http.DynamicProxy; - -/// -/// Copied from https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpWindowsProxy.cs -/// -internal sealed class HttpWindowsProxy : IMultiWebProxy, IDisposable -{ - private readonly MultiProxy _insecureProxy; // URI of the http system proxy if set - private readonly MultiProxy _secureProxy; // URI of the https system proxy if set - private readonly FailedProxyCache _failedProxies = new FailedProxyCache(); - private readonly List? _bypass; // list of domains not to proxy - private readonly bool _bypassLocal; // we should bypass domain considered local - private readonly List? _localIp; - private ICredentials? _credentials; - private readonly WinInetProxyHelper _proxyHelper; - private SafeWinHttpHandle? _sessionHandle; - private bool _disposed; - - public static bool TryCreate([NotNullWhen(true)] out IWebProxy? proxy) - { - // This will get basic proxy setting from system using existing - // WinInetProxyHelper functions. If no proxy is enabled, it will return null. - SafeWinHttpHandle? sessionHandle = null; - proxy = null; - - WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); - if (!proxyHelper.ManualSettingsOnly && !proxyHelper.AutoSettingsUsed) - { - return false; - } - - if (proxyHelper.AutoSettingsUsed) - { - sessionHandle = WinHttpOpen( - IntPtr.Zero, - WINHTTP_ACCESS_TYPE_NO_PROXY, - WINHTTP_NO_PROXY_NAME, - WINHTTP_NO_PROXY_BYPASS, - (int)WINHTTP_FLAG_ASYNC); - - if (sessionHandle.IsInvalid) - { - // Proxy failures are currently ignored by managed handler. - sessionHandle.Dispose(); - return false; - } - } - - proxy = new HttpWindowsProxy(proxyHelper, sessionHandle); - return true; - } - - private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle? sessionHandle) - { - _proxyHelper = proxyHelper; - _sessionHandle = sessionHandle; - - if (proxyHelper.ManualSettingsUsed) - { - _secureProxy = MultiProxy.Parse(_failedProxies, proxyHelper.Proxy, true); - _insecureProxy = MultiProxy.Parse(_failedProxies, proxyHelper.Proxy, false); - - if (!string.IsNullOrWhiteSpace(proxyHelper.ProxyBypass)) - { - int idx = 0; - string? tmp; - - // Process bypass list for manual setting. - // Initial list size is best guess based on string length assuming each entry is at least 5 characters on average. - _bypass = new List(proxyHelper.ProxyBypass.Length / 5); - - while (idx < proxyHelper.ProxyBypass.Length) - { - // Strip leading spaces and scheme if any. - while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ' ') { idx += 1; }; - if (string.Compare(proxyHelper.ProxyBypass, idx, "http://", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) - { - idx += 7; - } - else if (string.Compare(proxyHelper.ProxyBypass, idx, "https://", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) - { - idx += 8; - } - - if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == '[') - { - // Strip [] from IPv6 so we can use IdnHost laster for matching. - idx += 1; - } - - int start = idx; - while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ' ' && proxyHelper.ProxyBypass[idx] != ';' && proxyHelper.ProxyBypass[idx] != ']') { idx += 1; }; - - if (idx == start) - { - // Empty string. - tmp = null; - } - else if (string.Compare(proxyHelper.ProxyBypass, start, "", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) - { - _bypassLocal = true; - tmp = null; - } - else - { - tmp = proxyHelper.ProxyBypass.Substring(start, idx - start); - } - - // Skip trailing characters if any. - if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') - { - // Got stopped at space or ']'. Strip until next ';' or end. - while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') { idx += 1; }; - } - if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ';') - { - idx++; - } - if (tmp == null) - { - continue; - } - - _bypass.Add(tmp); - } - if (_bypass.Count == 0) - { - // Bypass string only had garbage we did not parse. - _bypass = null; - } - } - - if (_bypassLocal) - { - _localIp = new List(); - foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) - { - IPInterfaceProperties ipProps = netInterface.GetIPProperties(); - foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) - { - _localIp.Add(addr.Address); - } - } - } - } - } - - public void Dispose() - { - if (!_disposed) - { - _disposed = true; - - if (_sessionHandle != null && !_sessionHandle.IsInvalid) - { - SafeWinHttpHandle.DisposeAndClearHandle(ref _sessionHandle); - } - } - } - - /// - /// Gets the proxy URI. (IWebProxy interface) - /// - public Uri? GetProxy(Uri uri) - { - GetMultiProxy(uri).ReadNext(out Uri? proxyUri, out _); - return proxyUri; - } - - /// - /// Gets the proxy URIs. - /// - public MultiProxy GetMultiProxy(Uri uri) - { - // We need WinHTTP to detect and/or process a PAC (JavaScript) file. This maps to - // "Automatically detect settings" and/or "Use automatic configuration script" from IE - // settings. But, calling into WinHTTP can be slow especially when it has to call into - // the out-of-process service to discover, load, and run the PAC file. So, we skip - // calling into WinHTTP if there was a recent failure to detect a PAC file on the network. - // This is a common error. The default IE settings on a Windows machine consist of the - // single checkbox for "Automatically detect settings" turned on and most networks - // won't actually discover a PAC file on the network since WPAD protocol isn't configured. - if (_proxyHelper.AutoSettingsUsed && !_proxyHelper.RecentAutoDetectionFailure) - { - WINHTTP_PROXY_INFO proxyInfo = default; - try - { - if (_proxyHelper.GetProxyForUrl(_sessionHandle, uri, out proxyInfo)) - { - // If WinHTTP just specified a Proxy with no ProxyBypass list, then - // we can return the Proxy uri directly. - if (proxyInfo.ProxyBypass == IntPtr.Zero) - { - if (proxyInfo.Proxy != IntPtr.Zero) - { - string proxyStr = Marshal.PtrToStringUni(proxyInfo.Proxy)!; - - return MultiProxy.CreateLazy(_failedProxies, proxyStr, IsSecureUri(uri)); - } - else - { - return MultiProxy.Empty; - } - } - - // A bypass list was also specified. This means that WinHTTP has fallen back to - // using the manual IE settings specified and there is a ProxyBypass list also. - // Since we're not really using the full WinHTTP stack, we need to use HttpSystemProxy - // to do the computation of the final proxy uri merging the information from the Proxy - // and ProxyBypass strings. - } - else - { - return MultiProxy.Empty; - } - } - finally - { - Marshal.FreeHGlobal(proxyInfo.Proxy); - Marshal.FreeHGlobal(proxyInfo.ProxyBypass); - } - } - - // Fallback to manual settings if present. - if (_proxyHelper.ManualSettingsUsed) - { - if (_bypassLocal) - { - IPAddress? address; - - if (uri.IsLoopback) - { - // This is optimization for loopback addresses. - // Unfortunately this does not work for all local addresses. - return MultiProxy.Empty; - } - - // Pre-Check if host may be IP address to avoid parsing. - if (uri.HostNameType == UriHostNameType.IPv6 || uri.HostNameType == UriHostNameType.IPv4) - { - // RFC1123 allows labels to start with number. - // Leading number may or may not be IP address. - // IPv6 [::1] notation. '[' is not valid character in names. - if (IPAddress.TryParse(uri.IdnHost, out address)) - { - // Host is valid IP address. - // Check if it belongs to local system. - foreach (IPAddress a in _localIp!) - { - if (a.Equals(address)) - { - return MultiProxy.Empty; - } - } - } - } - if (uri.HostNameType != UriHostNameType.IPv6 && !uri.IdnHost.Contains('.')) - { - // Not address and does not have a dot. - // Hosts without FQDN are considered local. - return MultiProxy.Empty; - } - } - - // Check if we have other rules for bypass. - if (_bypass != null) - { - foreach (string entry in _bypass) - { - // IdnHost does not have []. - if (SimpleRegex.IsMatchWithStarWildcard(uri.IdnHost, entry)) - { - return MultiProxy.Empty; - } - } - } - - // We did not find match on bypass list. - return IsSecureUri(uri) ? _secureProxy : _insecureProxy; - } - - return MultiProxy.Empty; - } - - private static bool IsSecureUri(Uri uri) - { - return uri.Scheme == UriScheme.Https || uri.Scheme == UriScheme.Wss; - } - - /// - /// Checks if URI is subject to proxy or not. - /// - public bool IsBypassed(Uri uri) - { - // This HttpSystemProxy class is only consumed by SocketsHttpHandler and is not exposed outside of - // SocketsHttpHandler. The current pattern for consumption of IWebProxy is to call IsBypassed first. - // If it returns false, then the caller will call GetProxy. For this proxy implementation, computing - // the return value for IsBypassed is as costly as calling GetProxy. We want to avoid doing extra - // work. So, this proxy implementation for the IsBypassed method can always return false. Then the - // GetProxy method will return non-null for a proxy, or null if no proxy should be used. - return false; - } - - public ICredentials? Credentials - { - get - { - return _credentials; - } - set - { - _credentials = value; - } - } - - // Access function for unit tests. - internal List? BypassList => _bypass; -} -#pragma warning restore \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/IMultiWebProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/IMultiWebProxy.cs deleted file mode 100644 index f906d055..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/IMultiWebProxy.cs +++ /dev/null @@ -1,16 +0,0 @@ -#pragma warning disable -using System.Net; - -namespace Snap.Hutao.Core.IO.Http.DynamicProxy; - -/// -/// Copied from https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/IMultiWebProxy.cs -/// -internal interface IMultiWebProxy : IWebProxy -{ - /// - /// Gets the proxy URIs. - /// - public MultiProxy GetMultiProxy(Uri uri); -} -#pragma warning restore \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/MultiProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/MultiProxy.cs deleted file mode 100644 index da75d93b..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/MultiProxy.cs +++ /dev/null @@ -1,268 +0,0 @@ -#pragma warning disable -using System.Diagnostics; - -namespace Snap.Hutao.Core.IO.Http.DynamicProxy; - -/// -/// Copied from https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MultiProxy.cs -/// -internal struct MultiProxy -{ - private readonly FailedProxyCache? _failedProxyCache; - private readonly Uri[]? _uris; - private readonly string? _proxyConfig; - private readonly bool _secure; - private int _currentIndex; - private Uri? _currentUri; - - private MultiProxy(FailedProxyCache? failedProxyCache, Uri[] uris) - { - _failedProxyCache = failedProxyCache; - _uris = uris; - _proxyConfig = null; - _secure = default; - _currentIndex = 0; - _currentUri = null; - } - - private MultiProxy(FailedProxyCache failedProxyCache, string proxyConfig, bool secure) - { - _failedProxyCache = failedProxyCache; - _uris = null; - _proxyConfig = proxyConfig; - _secure = secure; - _currentIndex = 0; - _currentUri = null; - } - - public static MultiProxy Empty => new MultiProxy(null, Array.Empty()); - - /// - /// Parses a WinHTTP proxy config into a MultiProxy instance. - /// - /// The cache of failed proxy requests to employ. - /// The WinHTTP proxy config to parse. - /// If true, return proxies suitable for use with a secure connection. If false, return proxies suitable for an insecure connection. - public static MultiProxy Parse(FailedProxyCache failedProxyCache, string? proxyConfig, bool secure) - { - Debug.Assert(failedProxyCache != null); - - Uri[] uris = Array.Empty(); - - ReadOnlySpan span = proxyConfig; - while (TryParseProxyConfigPart(span, secure, out Uri? uri, out int charactersConsumed)) - { - int idx = uris.Length; - - // Assume that we will typically not have more than 1...3 proxies, so just - // grow by 1. This method is currently only used once per process, so the - // case of an abnormally large config will not be much of a concern anyway. - Array.Resize(ref uris, idx + 1); - uris[idx] = uri; - - span = span.Slice(charactersConsumed); - } - - return new MultiProxy(failedProxyCache, uris); - } - - /// - /// Initializes a MultiProxy instance that lazily parses a given WinHTTP configuration string. - /// - /// The cache of failed proxy requests to employ. - /// The WinHTTP proxy config to parse. - /// If true, return proxies suitable for use with a secure connection. If false, return proxies suitable for an insecure connection. - public static MultiProxy CreateLazy(FailedProxyCache failedProxyCache, string proxyConfig, bool secure) - { - Debug.Assert(failedProxyCache != null); - - return string.IsNullOrEmpty(proxyConfig) == false ? - new MultiProxy(failedProxyCache, proxyConfig, secure) : - MultiProxy.Empty; - } - - /// - /// Reads the next proxy URI from the MultiProxy. - /// - /// The next proxy to use for the request. - /// If true, indicates there are no further proxies to read from the config. - /// If there is a proxy available, true. Otherwise, false. - public bool ReadNext([NotNullWhen(true)] out Uri? uri, out bool isFinalProxy) - { - // Enumerating indicates the previous proxy has failed; mark it as such. - if (_currentUri != null) - { - Debug.Assert(_failedProxyCache != null); - _failedProxyCache.SetProxyFailed(_currentUri); - } - - // If no more proxies to read, return out quickly. - if (!ReadNextHelper(out uri, out isFinalProxy)) - { - _currentUri = null; - return false; - } - - // If this is the first ReadNext() and all proxies are marked as failed, return the proxy that is closest to renewal. - Uri? oldestFailedProxyUri = null; - long oldestFailedProxyTicks = long.MaxValue; - - do - { - Debug.Assert(_failedProxyCache != null); - long renewTicks = _failedProxyCache.GetProxyRenewTicks(uri); - - // Proxy hasn't failed recently, return for use. - if (renewTicks == FailedProxyCache.Immediate) - { - _currentUri = uri; - return true; - } - - if (renewTicks < oldestFailedProxyTicks) - { - oldestFailedProxyUri = uri; - oldestFailedProxyTicks = renewTicks; - } - } - while (ReadNextHelper(out uri, out isFinalProxy)); - - // All the proxies in the config have failed; in this case, return the proxy that is closest to renewal. - if (_currentUri == null) - { - uri = oldestFailedProxyUri; - _currentUri = oldestFailedProxyUri; - - if (oldestFailedProxyUri != null) - { - Debug.Assert(uri != null); - _failedProxyCache.TryRenewProxy(uri, oldestFailedProxyTicks); - return true; - } - } - - return false; - } - - /// - /// Reads the next proxy URI from the MultiProxy, either via parsing a config string or from an array. - /// - private bool ReadNextHelper([NotNullWhen(true)] out Uri? uri, out bool isFinalProxy) - { - Debug.Assert(_uris != null || _proxyConfig != null, $"{nameof(ReadNext)} must not be called on a default-initialized {nameof(MultiProxy)}."); - - if (_uris != null) - { - if (_currentIndex == _uris.Length) - { - uri = default; - isFinalProxy = default; - return false; - } - - uri = _uris[_currentIndex++]; - isFinalProxy = _currentIndex == _uris.Length; - return true; - } - - Debug.Assert(_proxyConfig != null); - if (_currentIndex < _proxyConfig.Length) - { - bool hasProxy = TryParseProxyConfigPart(_proxyConfig.AsSpan(_currentIndex), _secure, out uri!, out int charactersConsumed); - - _currentIndex += charactersConsumed; - Debug.Assert(_currentIndex <= _proxyConfig.Length); - - isFinalProxy = _currentIndex == _proxyConfig.Length; - - return hasProxy; - } - - uri = default; - isFinalProxy = default; - return false; - } - - /// - /// This method is used to parse WinINet Proxy strings, a single proxy at a time. - /// - /// - /// The strings are a semicolon or whitespace separated list, with each entry in the following format: - /// ([<scheme>=][<scheme>"://"]<server>[":"<port>]) - /// - private static bool TryParseProxyConfigPart(ReadOnlySpan proxyString, bool secure, [NotNullWhen(true)] out Uri? uri, out int charactersConsumed) - { - const int SECURE_FLAG = 1; - const int INSECURE_FLAG = 2; - const string ProxyDelimiters = "; \n\r\t"; - - int wantedFlag = secure ? SECURE_FLAG : INSECURE_FLAG; - int originalLength = proxyString.Length; - - while (true) - { - // Skip any delimiters. - int iter = 0; - while (iter < proxyString.Length && ProxyDelimiters.Contains(proxyString[iter])) - { - ++iter; - } - - if (iter == proxyString.Length) - { - break; - } - - proxyString = proxyString.Slice(iter); - - // Determine which scheme this part is for. - // If no schema is defined, use both. - int proxyType = SECURE_FLAG | INSECURE_FLAG; - - if (proxyString.StartsWith("http=")) - { - proxyType = INSECURE_FLAG; - proxyString = proxyString.Slice("http=".Length); - } - else if (proxyString.StartsWith("https=")) - { - proxyType = SECURE_FLAG; - proxyString = proxyString.Slice("https=".Length); - } - - if (proxyString.StartsWith("http://")) - { - proxyType = INSECURE_FLAG; - proxyString = proxyString.Slice("http://".Length); - } - else if (proxyString.StartsWith("https://")) - { - proxyType = SECURE_FLAG; - proxyString = proxyString.Slice("https://".Length); - } - - // Find the next delimiter, or end of string. - iter = proxyString.IndexOfAny(ProxyDelimiters); - if (iter < 0) - { - iter = proxyString.Length; - } - - // Return URI if it's a match to what we want. - if ((proxyType & wantedFlag) != 0 && Uri.TryCreate(string.Concat("http://", proxyString.Slice(0, iter)), UriKind.Absolute, out uri)) - { - charactersConsumed = originalLength - proxyString.Length + iter; - Debug.Assert(charactersConsumed > 0); - - return true; - } - - proxyString = proxyString.Slice(iter); - } - - uri = null; - charactersConsumed = originalLength; - return false; - } -} -#pragma warning restore \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/SimpleRegex.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/SimpleRegex.cs deleted file mode 100644 index 6d992b2d..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/SimpleRegex.cs +++ /dev/null @@ -1,73 +0,0 @@ -#pragma warning disable -using System.Diagnostics; - -namespace Snap.Hutao.Core.IO.Http.DynamicProxy; - -/// -/// Copied from https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/Common/src/System/Text/SimpleRegex.cs -/// -internal static class SimpleRegex -{ - // Based on wildcmp written by Jack Handy - jakkhandy@hotmail.com - // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing - - /// - /// Perform a match between an input string and a pattern in which the only special character - /// is an asterisk, which can map to zero or more of any character in the input. - /// - /// The input to match. - /// The pattern to match against. - /// true if the input matches the pattern; otherwise, false. - public static bool IsMatchWithStarWildcard(ReadOnlySpan input, ReadOnlySpan pattern) - { - int inputPos = 0, inputPosSaved = -1; - int patternPos = 0, patternPosSaved = -1; - - // Loop through each character in the input. - while (inputPos < input.Length) - { - if (patternPos < pattern.Length && pattern[patternPos] == '*') - { - // If we're currently positioned on a wildcard in the pattern, - // move past it and remember where we are to backtrack to. - inputPosSaved = inputPos; - patternPosSaved = ++patternPos; - } - else if (patternPos < pattern.Length && - (pattern[patternPos] == input[inputPos] || - char.ToUpperInvariant(pattern[patternPos]) == char.ToUpperInvariant(input[inputPos]))) - { - // If the characters in the pattern and the input match, advance both. - inputPos++; - patternPos++; - } - else if (patternPosSaved == -1) - { - // If we're not on a wildcard and the current characters don't match and we don't have - // any wildcard to backtrack to, this is not a match. - return false; - } - else - { - // Otherwise, this is not a wildcard, the characters don't match, but we do have a - // wildcard saved, so backtrack to it and use it to consume the next input character. - inputPos = ++inputPosSaved; - patternPos = patternPosSaved; - } - } - - // We've reached the end of the input. Eat all wildcards immediately after where we are - // in the pattern, as if they're at the end, they'll all just map to nothing (and if it - // turns out there's something after them, eating them won't matter). - while (patternPos < pattern.Length && pattern[patternPos] == '*') - { - patternPos++; - } - - // If we are in fact at the end of the pattern, then we successfully matched. - // If there's anything left, it's not a wildcard, so it doesn't match. - Debug.Assert(patternPos <= pattern.Length); - return patternPos == pattern.Length; - } -} -#pragma warning restore \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/UriScheme.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/UriScheme.cs deleted file mode 100644 index 124f898a..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/UriScheme.cs +++ /dev/null @@ -1,24 +0,0 @@ -#pragma warning disable -namespace Snap.Hutao.Core.IO.Http.DynamicProxy; - -/// -/// Copied from https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/Common/src/System/Net/UriScheme.cs -/// -internal static class UriScheme -{ - public const string File = "file"; - public const string Ftp = "ftp"; - public const string Gopher = "gopher"; - public const string Http = "http"; - public const string Https = "https"; - public const string News = "news"; - public const string NetPipe = "net.pipe"; - public const string NetTcp = "net.tcp"; - public const string Nntp = "nntp"; - public const string Mailto = "mailto"; - public const string Ws = "ws"; - public const string Wss = "wss"; - - public const string SchemeDelimiter = "://"; -} -#pragma warning restore \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/WinInetProxyHelper.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/WinInetProxyHelper.cs deleted file mode 100644 index 41be5ec1..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/WinInetProxyHelper.cs +++ /dev/null @@ -1,179 +0,0 @@ -#pragma warning disable -using System.Runtime.InteropServices; -using static Snap.Hutao.Win32.Interop.WinHttp; - -namespace Snap.Hutao.Core.IO.Http.DynamicProxy; - -/// -/// Copied from https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/Common/src/System/Net/Http/WinInetProxyHelper.cs -/// -internal sealed class WinInetProxyHelper -{ - private const int RecentAutoDetectionInterval = 120_000; // 2 minutes in milliseconds. - private readonly string? _autoConfigUrl, _proxy, _proxyBypass; - private readonly bool _autoDetect; - private readonly bool _useProxy; - private bool _autoDetectionFailed; - private int _lastTimeAutoDetectionFailed; // Environment.TickCount units (milliseconds). - - public WinInetProxyHelper() - { - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = default; - - try - { - if (WinHttpGetIEProxyConfigForCurrentUser(out proxyConfig)) - { - _autoConfigUrl = Marshal.PtrToStringUni(proxyConfig.AutoConfigUrl)!; - _autoDetect = proxyConfig.AutoDetect != 0; - _proxy = Marshal.PtrToStringUni(proxyConfig.Proxy)!; - _proxyBypass = Marshal.PtrToStringUni(proxyConfig.ProxyBypass)!; - - _useProxy = true; - } - else - { - // We match behavior of WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY and ignore errors. - int lastError = Marshal.GetLastWin32Error(); - } - } - finally - { - // FreeHGlobal already checks for null pointer before freeing the memory. - Marshal.FreeHGlobal(proxyConfig.AutoConfigUrl); - Marshal.FreeHGlobal(proxyConfig.Proxy); - Marshal.FreeHGlobal(proxyConfig.ProxyBypass); - } - } - - public string? AutoConfigUrl => _autoConfigUrl; - - public bool AutoDetect => _autoDetect; - - public bool AutoSettingsUsed => AutoDetect || !string.IsNullOrEmpty(AutoConfigUrl); - - public bool ManualSettingsUsed => !string.IsNullOrEmpty(Proxy); - - public bool ManualSettingsOnly => !AutoSettingsUsed && ManualSettingsUsed; - - public string? Proxy => _proxy; - - public string? ProxyBypass => _proxyBypass; - - public bool RecentAutoDetectionFailure => - _autoDetectionFailed && - Environment.TickCount - _lastTimeAutoDetectionFailed <= RecentAutoDetectionInterval; - - public bool GetProxyForUrl( - SafeWinHttpHandle? sessionHandle, - Uri uri, - out WINHTTP_PROXY_INFO proxyInfo) - { - proxyInfo.AccessType = WINHTTP_ACCESS_TYPE_NO_PROXY; - proxyInfo.Proxy = IntPtr.Zero; - proxyInfo.ProxyBypass = IntPtr.Zero; - - if (!_useProxy) - { - return false; - } - - bool useProxy = false; - - WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions; - autoProxyOptions.AutoConfigUrl = AutoConfigUrl; - autoProxyOptions.AutoDetectFlags = AutoDetect ? - (WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A) : 0; - autoProxyOptions.AutoLoginIfChallenged = false; - autoProxyOptions.Flags = - (AutoDetect ? WINHTTP_AUTOPROXY_AUTO_DETECT : 0) | - (!string.IsNullOrEmpty(AutoConfigUrl) ? WINHTTP_AUTOPROXY_CONFIG_URL : 0); - autoProxyOptions.Reserved1 = IntPtr.Zero; - autoProxyOptions.Reserved2 = 0; - - // AutoProxy Cache. - // https://docs.microsoft.com/en-us/windows/desktop/WinHttp/autoproxy-cache - // If the out-of-process service is active when WinHttpGetProxyForUrl is called, the cached autoproxy - // URL and script are available to the whole computer. However, if the out-of-process service is used, - // and the fAutoLogonIfChallenged flag in the pAutoProxyOptions structure is true, then the autoproxy - // URL and script are not cached. Therefore, calling WinHttpGetProxyForUrl with the fAutoLogonIfChallenged - // member set to TRUE results in additional overhead operations that may affect performance. - // The following steps can be used to improve performance: - // 1. Call WinHttpGetProxyForUrl with the fAutoLogonIfChallenged parameter set to false. The autoproxy - // URL and script are cached for future calls to WinHttpGetProxyForUrl. - // 2. If Step 1 fails, with ERROR_WINHTTP_LOGIN_FAILURE, then call WinHttpGetProxyForUrl with the - // fAutoLogonIfChallenged member set to TRUE. - // - // We match behavior of WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY and ignore errors. -#pragma warning disable CA1845 // file is shared with a build that lacks string.Concat for spans - // Underlying code does not understand WebSockets so we need to convert it to http or https. - string destination = uri.AbsoluteUri; - if (uri.Scheme == UriScheme.Wss) - { - destination = UriScheme.Https + destination.Substring(UriScheme.Wss.Length); - } - else if (uri.Scheme == UriScheme.Ws) - { - destination = UriScheme.Http + destination.Substring(UriScheme.Ws.Length); - } -#pragma warning restore CA1845 - - var repeat = false; - do - { - _autoDetectionFailed = false; - if (WinHttpGetProxyForUrl( - sessionHandle!, - destination, - ref autoProxyOptions, - out proxyInfo)) - { - useProxy = true; - - break; - } - else - { - var lastError = Marshal.GetLastWin32Error(); - - if (lastError == ERROR_WINHTTP_LOGIN_FAILURE) - { - if (repeat) - { - // We don't retry more than once. - break; - } - else - { - repeat = true; - autoProxyOptions.AutoLoginIfChallenged = true; - } - } - else - { - if (lastError == ERROR_WINHTTP_AUTODETECTION_FAILED) - { - _autoDetectionFailed = true; - _lastTimeAutoDetectionFailed = Environment.TickCount; - } - - break; - } - } - } while (repeat); - - // Fall back to manual settings if available. - if (!useProxy && !string.IsNullOrEmpty(Proxy)) - { - proxyInfo.AccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; - proxyInfo.Proxy = Marshal.StringToHGlobalUni(Proxy); - proxyInfo.ProxyBypass = string.IsNullOrEmpty(ProxyBypass) ? - IntPtr.Zero : Marshal.StringToHGlobalUni(ProxyBypass); - - useProxy = true; - } - - return useProxy; - } -} -#pragma warning restore \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Shell/RegistryMonitor.cs b/src/Snap.Hutao/Snap.Hutao/Core/Shell/RegistryWatcher.cs similarity index 94% rename from src/Snap.Hutao/Snap.Hutao/Core/Shell/RegistryMonitor.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Shell/RegistryWatcher.cs index 3ec03751..45632c6d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Shell/RegistryMonitor.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Shell/RegistryWatcher.cs @@ -7,7 +7,7 @@ using Windows.Win32.System.Registry; namespace Snap.Hutao.Core.Shell; -internal sealed partial class RegistryMonitor : IDisposable +internal sealed partial class RegistryWatcher : IDisposable { private readonly ManualResetEvent eventTerminate = new(false); private readonly CancellationTokenSource cancellationTokenSource = new(); @@ -20,7 +20,7 @@ internal sealed partial class RegistryMonitor : IDisposable private string subKey = default!; private bool disposed; - public RegistryMonitor(string name, EventHandler eventHandler) + public RegistryWatcher(string name, EventHandler eventHandler) { ArgumentException.ThrowIfNullOrEmpty(name); @@ -30,9 +30,9 @@ internal sealed partial class RegistryMonitor : IDisposable public event EventHandler? RegChanged; - public static RegistryMonitor Create(string name, EventHandler eventHandler) + public static RegistryWatcher Create(string name, EventHandler eventHandler) { - return new RegistryMonitor(name, eventHandler); + return new RegistryWatcher(name, eventHandler); } public void Start()