blob: e6642d9f835c739bed17dbb8a01536ac95275829 [file] [log] [blame]
(*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*)
unit Thrift.WinHTTP;
{$I Thrift.Defines.inc}
{$SCOPEDENUMS ON}
// packing according to winhttp.h
{$IFDEF Win64} {$ALIGN 8} {$ELSE} {$ALIGN 4} {$ENDIF}
interface
uses
Windows,
Classes,
SysUtils,
Math,
Generics.Collections;
type
HINTERNET = type Pointer;
INTERNET_PORT = type WORD;
INTERNET_SCHEME = type Integer;
LPLPCWSTR = ^LPCWSTR;
LPURL_COMPONENTS = ^URL_COMPONENTS;
URL_COMPONENTS = record
dwStructSize : DWORD; // set to SizeOf(URL_COMPONENTS)
lpszScheme : LPWSTR; // scheme name
dwSchemeLength : DWORD;
nScheme : INTERNET_SCHEME; // enumerated scheme type
lpszHostName : LPWSTR; // host name
dwHostNameLength : DWORD;
nPort : INTERNET_PORT; // port number
lpszUserName : LPWSTR; // user name
dwUserNameLength : DWORD;
lpszPassword : LPWSTR; // password
dwPasswordLength : DWORD;
lpszUrlPath : LPWSTR; // URL-path
dwUrlPathLength : DWORD;
lpszExtraInfo : LPWSTR; // extra information
dwExtraInfoLength : DWORD;
end;
URL_COMPONENTSW = URL_COMPONENTS;
LPURL_COMPONENTSW = LPURL_COMPONENTS;
// When retrieving proxy data, an application must free the lpszProxy and
// lpszProxyBypass strings contained in this structure (if they are non-NULL)
// using the GlobalFree function.
LPWINHTTP_PROXY_INFO = ^WINHTTP_PROXY_INFO;
WINHTTP_PROXY_INFO = record
dwAccessType : DWORD; // see WINHTTP_ACCESS_* types below
lpszProxy : LPWSTR; // proxy server list
lpszProxyBypass : LPWSTR; // proxy bypass list
end;
LPWINHTTP_PROXY_INFOW = ^WINHTTP_PROXY_INFOW;
WINHTTP_PROXY_INFOW = WINHTTP_PROXY_INFO;
WINHTTP_AUTOPROXY_OPTIONS = record
dwFlags : DWORD;
dwAutoDetectFlags : DWORD;
lpszAutoConfigUrl : LPCWSTR;
lpvReserved : LPVOID;
dwReserved : DWORD;
fAutoLogonIfChallenged : BOOL;
end;
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG = record
fAutoDetect : BOOL;
lpszAutoConfigUrl : LPWSTR;
lpszProxy : LPWSTR;
lpszProxyBypass : LPWSTR;
end;
function WinHttpCloseHandle( aHandle : HINTERNET) : BOOL; stdcall;
function WinHttpOpen( const pszAgentW : LPCWSTR;
const dwAccessType : DWORD;
const pszProxyW : LPCWSTR;
const pszProxyBypassW : LPCWSTR;
const dwFlags : DWORD
) : HINTERNET; stdcall;
function WinHttpConnect( const hSession : HINTERNET;
const pswzServerName : LPCWSTR;
const nServerPort : INTERNET_PORT;
const dwReserved : DWORD
) : HINTERNET; stdcall;
function WinHttpOpenRequest( const hConnect : HINTERNET;
const pwszVerb, pwszObjectName, pwszVersion, pwszReferrer : LPCWSTR;
const ppwszAcceptTypes : LPLPCWSTR;
const dwFlags : DWORD
) : HINTERNET; stdcall;
function WinHttpQueryOption( const hInternet : HINTERNET;
const dwOption : DWORD;
const pBuffer : Pointer;
var dwBufferLength : DWORD) : BOOL; stdcall;
function WinHttpSetOption( const hInternet : HINTERNET;
const dwOption : DWORD;
const pBuffer : Pointer;
const dwBufferLength : DWORD) : BOOL; stdcall;
function WinHttpSetTimeouts( const hRequestOrSession : HINTERNET;
const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32
) : BOOL; stdcall;
function WinHttpAddRequestHeaders( const hRequest : HINTERNET;
const pwszHeaders : LPCWSTR;
const dwHeadersLengthInChars : DWORD;
const dwModifiers : DWORD
) : BOOL; stdcall;
function WinHttpGetProxyForUrl( const hSession : HINTERNET;
const lpcwszUrl : LPCWSTR;
const options : WINHTTP_AUTOPROXY_OPTIONS;
const info : WINHTTP_PROXY_INFO
) : BOOL; stdcall;
function WinHttpGetIEProxyConfigForCurrentUser( var config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
) : BOOL; stdcall;
function WinHttpSendRequest( const hRequest : HINTERNET;
const lpszHeaders : LPCWSTR;
const dwHeadersLength : DWORD;
const lpOptional : Pointer;
const dwOptionalLength : DWORD;
const dwTotalLength : DWORD;
const pContext : Pointer
) : BOOL; stdcall;
function WinHttpWriteData( const hRequest : HINTERNET;
const pBuf : Pointer;
const dwBytesToWrite : DWORD;
out dwBytesWritten : DWORD
) : BOOL; stdcall;
function WinHttpReceiveResponse( const hRequest : HINTERNET; const lpReserved : Pointer) : BOOL; stdcall;
function WinHttpQueryHeaders( const hRequest : HINTERNET;
const dwInfoLevel : DWORD;
const pwszName : LPCWSTR;
const lpBuffer : Pointer;
var dwBufferLength : DWORD;
var dwIndex : DWORD
) : BOOL; stdcall;
function WinHttpQueryDataAvailable( const hRequest : HINTERNET;
var dwNumberOfBytesAvailable : DWORD
) : BOOL; stdcall;
function WinHttpReadData( const hRequest : HINTERNET;
const lpBuffer : Pointer;
const dwBytesToRead : DWORD;
out dwBytesRead : DWORD
) : BOOL; stdcall;
function WinHttpCrackUrl( const pwszUrl : LPCWSTR;
const dwUrlLength : DWORD;
const dwFlags : DWORD;
var urlComponents : URL_COMPONENTS
) : BOOL; stdcall;
function WinHttpCreateUrl( const UrlComponents : URL_COMPONENTS;
const dwFlags : DWORD;
const pwszUrl : LPCWSTR;
var pdwUrlLength : DWORD
) : BOOL; stdcall;
const
// WinHttpOpen dwAccessType values
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0;
WINHTTP_ACCESS_TYPE_NO_PROXY = 1;
WINHTTP_ACCESS_TYPE_NAMED_PROXY = 3;
// flags for WinHttpOpen():
WINHTTP_FLAG_ASYNC = $10000000; // want async session, requires WinHttpSetStatusCallback() usage
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH = 0;
// ports
INTERNET_DEFAULT_PORT = 0; // use the protocol-specific default (80 or 443)
// flags for WinHttpOpenRequest():
WINHTTP_FLAG_SECURE = $00800000; // use SSL if applicable (HTTPS)
WINHTTP_FLAG_ESCAPE_PERCENT = $00000004; // if escaping enabled, escape percent as well
WINHTTP_FLAG_NULL_CODEPAGE = $00000008; // assume all symbols are ASCII, use fast convertion
WINHTTP_FLAG_BYPASS_PROXY_CACHE = $00000100; // add "pragma: no-cache" request header
WINHTTP_FLAG_REFRESH = WINHTTP_FLAG_BYPASS_PROXY_CACHE;
WINHTTP_FLAG_ESCAPE_DISABLE = $00000040; // disable escaping
WINHTTP_FLAG_ESCAPE_DISABLE_QUERY = $00000080; // if escaping enabled escape path part, but do not escape query
// flags for WinHttpSendRequest():
WINHTTP_NO_PROXY_NAME = nil;
WINHTTP_NO_PROXY_BYPASS = nil;
WINHTTP_NO_CLIENT_CERT_CONTEXT = nil;
WINHTTP_NO_REFERER = nil;
WINHTTP_DEFAULT_ACCEPT_TYPES = nil;
WINHTTP_NO_ADDITIONAL_HEADERS = nil;
WINHTTP_NO_REQUEST_DATA = nil;
WINHTTP_HEADER_NAME_BY_INDEX = nil;
WINHTTP_NO_OUTPUT_BUFFER = nil;
WINHTTP_NO_HEADER_INDEX = nil;
// WinHttpAddRequestHeaders() dwModifiers
WINHTTP_ADDREQ_INDEX_MASK = $0000FFFF;
WINHTTP_ADDREQ_FLAGS_MASK = $FFFF0000;
WINHTTP_ADDREQ_FLAG_ADD_IF_NEW = $10000000;
WINHTTP_ADDREQ_FLAG_ADD = $20000000;
WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA = $40000000;
WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON = $01000000;
WINHTTP_ADDREQ_FLAG_COALESCE = WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA;
WINHTTP_ADDREQ_FLAG_REPLACE = $80000000;
// URL functions
ICU_NO_ENCODE = $20000000; // Don't convert unsafe characters to escape sequence
ICU_DECODE = $10000000; // Convert %XX escape sequences to characters
ICU_NO_META = $08000000; // Don't convert .. etc. meta path sequences
ICU_ENCODE_SPACES_ONLY = $04000000; // Encode spaces only
ICU_BROWSER_MODE = $02000000; // Special encode/decode rules for browser
ICU_ENCODE_PERCENT = $00001000; // Encode any percent (ASCII25)
ICU_ESCAPE = $80000000; // (un)escape URL characters
ICU_ESCAPE_AUTHORITY = $00002000; // causes InternetCreateUrlA to escape chars in authority components (user, pwd, host)
ICU_REJECT_USERPWD = $00004000; // rejects usrls whick have username/pwd sections
INTERNET_SCHEME_HTTP = INTERNET_SCHEME(1);
INTERNET_SCHEME_HTTPS = INTERNET_SCHEME(2);
// options manifests for WinHttp{Query|Set}Option
WINHTTP_OPTION_CALLBACK = 1;
WINHTTP_OPTION_RESOLVE_TIMEOUT = 2;
WINHTTP_OPTION_CONNECT_TIMEOUT = 3;
WINHTTP_OPTION_CONNECT_RETRIES = 4;
WINHTTP_OPTION_SEND_TIMEOUT = 5;
WINHTTP_OPTION_RECEIVE_TIMEOUT = 6;
WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT = 7;
WINHTTP_OPTION_HANDLE_TYPE = 9;
WINHTTP_OPTION_READ_BUFFER_SIZE = 12;
WINHTTP_OPTION_WRITE_BUFFER_SIZE = 13;
WINHTTP_OPTION_PARENT_HANDLE = 21;
WINHTTP_OPTION_EXTENDED_ERROR = 24;
WINHTTP_OPTION_SECURITY_FLAGS = 31;
WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT = 32;
WINHTTP_OPTION_URL = 34;
WINHTTP_OPTION_SECURITY_KEY_BITNESS = 36;
WINHTTP_OPTION_PROXY = 38;
WINHTTP_OPTION_USER_AGENT = 41;
WINHTTP_OPTION_CONTEXT_VALUE = 45;
WINHTTP_OPTION_CLIENT_CERT_CONTEXT = 47;
WINHTTP_OPTION_REQUEST_PRIORITY = 58;
WINHTTP_OPTION_HTTP_VERSION = 59;
WINHTTP_OPTION_DISABLE_FEATURE = 63;
WINHTTP_OPTION_CODEPAGE = 68;
WINHTTP_OPTION_MAX_CONNS_PER_SERVER = 73;
WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER = 74;
WINHTTP_OPTION_AUTOLOGON_POLICY = 77;
WINHTTP_OPTION_SERVER_CERT_CONTEXT = 78;
WINHTTP_OPTION_ENABLE_FEATURE = 79;
WINHTTP_OPTION_WORKER_THREAD_COUNT = 80;
WINHTTP_OPTION_PASSPORT_COBRANDING_TEXT = 81;
WINHTTP_OPTION_PASSPORT_COBRANDING_URL = 82;
WINHTTP_OPTION_CONFIGURE_PASSPORT_AUTH = 83;
WINHTTP_OPTION_SECURE_PROTOCOLS = 84;
WINHTTP_OPTION_ENABLETRACING = 85;
WINHTTP_OPTION_PASSPORT_SIGN_OUT = 86;
WINHTTP_OPTION_PASSPORT_RETURN_URL = 87;
WINHTTP_OPTION_REDIRECT_POLICY = 88;
WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS = 89;
WINHTTP_OPTION_MAX_HTTP_STATUS_CONTINUE = 90;
WINHTTP_OPTION_MAX_RESPONSE_HEADER_SIZE = 91;
WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE = 92;
WINHTTP_OPTION_CONNECTION_INFO = 93;
WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST = 94;
WINHTTP_OPTION_SPN = 96;
WINHTTP_OPTION_GLOBAL_PROXY_CREDS = 97;
WINHTTP_OPTION_GLOBAL_SERVER_CREDS = 98;
WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT = 99;
WINHTTP_OPTION_REJECT_USERPWD_IN_URL = 100;
WINHTTP_OPTION_USE_GLOBAL_SERVER_CREDENTIALS = 101;
WINHTTP_OPTION_RECEIVE_PROXY_CONNECT_RESPONSE = 103;
WINHTTP_OPTION_IS_PROXY_CONNECT_RESPONSE = 104;
WINHTTP_OPTION_SERVER_SPN_USED = 106;
WINHTTP_OPTION_PROXY_SPN_USED = 107;
WINHTTP_OPTION_SERVER_CBT = 108;
// options for newer WinHTTP versions
WINHTTP_OPTION_DECOMPRESSION = 118;
//
WINHTTP_FIRST_OPTION = WINHTTP_OPTION_CALLBACK;
//WINHTTP_LAST_OPTION = WINHTTP_OPTION_SERVER_CBT;
WINHTTP_OPTION_USERNAME = $1000;
WINHTTP_OPTION_PASSWORD = $1001;
WINHTTP_OPTION_PROXY_USERNAME = $1002;
WINHTTP_OPTION_PROXY_PASSWORD = $1003;
// manifest value for WINHTTP_OPTION_MAX_CONNS_PER_SERVER and WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER
WINHTTP_CONNS_PER_SERVER_UNLIMITED = $FFFFFFFF;
// values for WINHTTP_OPTION_AUTOLOGON_POLICY
WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM = 0;
WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW = 1;
WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH = 2;
WINHTTP_AUTOLOGON_SECURITY_LEVEL_DEFAULT = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
// values for WINHTTP_OPTION_REDIRECT_POLICY
WINHTTP_OPTION_REDIRECT_POLICY_NEVER = 0;
WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP = 1;
WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS = 2;
WINHTTP_OPTION_REDIRECT_POLICY_LAST = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS;
WINHTTP_OPTION_REDIRECT_POLICY_DEFAULT = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP;
WINHTTP_DISABLE_PASSPORT_AUTH = $00000000;
WINHTTP_ENABLE_PASSPORT_AUTH = $10000000;
WINHTTP_DISABLE_PASSPORT_KEYRING = $20000000;
WINHTTP_ENABLE_PASSPORT_KEYRING = $40000000;
// values for WINHTTP_OPTION_DISABLE_FEATURE
WINHTTP_DISABLE_COOKIES = $00000001;
WINHTTP_DISABLE_REDIRECTS = $00000002;
WINHTTP_DISABLE_AUTHENTICATION = $00000004;
WINHTTP_DISABLE_KEEP_ALIVE = $00000008;
// values for WINHTTP_OPTION_ENABLE_FEATURE
WINHTTP_ENABLE_SSL_REVOCATION = $00000001;
WINHTTP_ENABLE_SSL_REVERT_IMPERSONATION = $00000002;
// values for WINHTTP_OPTION_SPN
WINHTTP_DISABLE_SPN_SERVER_PORT = $00000000;
WINHTTP_ENABLE_SPN_SERVER_PORT = $00000001;
WINHTTP_OPTION_SPN_MASK = WINHTTP_ENABLE_SPN_SERVER_PORT;
// winhttp handle types
WINHTTP_HANDLE_TYPE_SESSION = 1;
WINHTTP_HANDLE_TYPE_CONNECT = 2;
WINHTTP_HANDLE_TYPE_REQUEST = 3;
// values for auth schemes
WINHTTP_AUTH_SCHEME_BASIC = $00000001;
WINHTTP_AUTH_SCHEME_NTLM = $00000002;
WINHTTP_AUTH_SCHEME_PASSPORT = $00000004;
WINHTTP_AUTH_SCHEME_DIGEST = $00000008;
WINHTTP_AUTH_SCHEME_NEGOTIATE = $00000010;
// WinHttp supported Authentication Targets
WINHTTP_AUTH_TARGET_SERVER = $00000000;
WINHTTP_AUTH_TARGET_PROXY = $00000001;
// options for WINHTTP_OPTION_DECOMPRESSION
WINHTTP_DECOMPRESSION_FLAG_GZIP = $00000001;
WINHTTP_DECOMPRESSION_FLAG_DEFLATE = $00000002;
WINHTTP_DECOMPRESSION_FLAG_ALL = WINHTTP_DECOMPRESSION_FLAG_GZIP
or WINHTTP_DECOMPRESSION_FLAG_DEFLATE;
// values for WINHTTP_OPTION_SECURITY_FLAGS
// query only
SECURITY_FLAG_SECURE = $00000001; // can query only
SECURITY_FLAG_STRENGTH_WEAK = $10000000;
SECURITY_FLAG_STRENGTH_MEDIUM = $40000000;
SECURITY_FLAG_STRENGTH_STRONG = $20000000;
// query flags
WINHTTP_QUERY_MIME_VERSION = 0;
WINHTTP_QUERY_CONTENT_TYPE = 1;
WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2;
WINHTTP_QUERY_CONTENT_ID = 3;
WINHTTP_QUERY_CONTENT_DESCRIPTION = 4;
WINHTTP_QUERY_CONTENT_LENGTH = 5;
WINHTTP_QUERY_CONTENT_LANGUAGE = 6;
WINHTTP_QUERY_ALLOW = 7;
WINHTTP_QUERY_PUBLIC = 8;
WINHTTP_QUERY_DATE = 9;
WINHTTP_QUERY_EXPIRES = 10;
WINHTTP_QUERY_LAST_MODIFIED = 11;
WINHTTP_QUERY_MESSAGE_ID = 12;
WINHTTP_QUERY_URI = 13;
WINHTTP_QUERY_DERIVED_FROM = 14;
WINHTTP_QUERY_COST = 15;
WINHTTP_QUERY_LINK = 16;
WINHTTP_QUERY_PRAGMA = 17;
WINHTTP_QUERY_VERSION = 18;
WINHTTP_QUERY_STATUS_CODE = 19;
WINHTTP_QUERY_STATUS_TEXT = 20;
WINHTTP_QUERY_RAW_HEADERS = 21;
WINHTTP_QUERY_RAW_HEADERS_CRLF = 22;
WINHTTP_QUERY_CONNECTION = 23;
WINHTTP_QUERY_ACCEPT = 24;
WINHTTP_QUERY_ACCEPT_CHARSET = 25;
WINHTTP_QUERY_ACCEPT_ENCODING = 26;
WINHTTP_QUERY_ACCEPT_LANGUAGE = 27;
WINHTTP_QUERY_AUTHORIZATION = 28;
WINHTTP_QUERY_CONTENT_ENCODING = 29;
WINHTTP_QUERY_FORWARDED = 30;
WINHTTP_QUERY_FROM = 31;
WINHTTP_QUERY_IF_MODIFIED_SINCE = 32;
WINHTTP_QUERY_LOCATION = 33;
WINHTTP_QUERY_ORIG_URI = 34;
WINHTTP_QUERY_REFERER = 35;
WINHTTP_QUERY_RETRY_AFTER = 36;
WINHTTP_QUERY_SERVER = 37;
WINHTTP_QUERY_TITLE = 38;
WINHTTP_QUERY_USER_AGENT = 39;
WINHTTP_QUERY_WWW_AUTHENTICATE = 40;
WINHTTP_QUERY_PROXY_AUTHENTICATE = 41;
WINHTTP_QUERY_ACCEPT_RANGES = 42;
WINHTTP_QUERY_SET_COOKIE = 43;
WINHTTP_QUERY_COOKIE = 44;
WINHTTP_QUERY_REQUEST_METHOD = 45;
WINHTTP_QUERY_REFRESH = 46;
WINHTTP_QUERY_CONTENT_DISPOSITION = 47;
WINHTTP_QUERY_AGE = 48;
WINHTTP_QUERY_CACHE_CONTROL = 49;
WINHTTP_QUERY_CONTENT_BASE = 50;
WINHTTP_QUERY_CONTENT_LOCATION = 51;
WINHTTP_QUERY_CONTENT_MD5 = 52;
WINHTTP_QUERY_CONTENT_RANGE = 53;
WINHTTP_QUERY_ETAG = 54;
WINHTTP_QUERY_HOST = 55;
WINHTTP_QUERY_IF_MATCH = 56;
WINHTTP_QUERY_IF_NONE_MATCH = 57;
WINHTTP_QUERY_IF_RANGE = 58;
WINHTTP_QUERY_IF_UNMODIFIED_SINCE = 59;
WINHTTP_QUERY_MAX_FORWARDS = 60;
WINHTTP_QUERY_PROXY_AUTHORIZATION = 61;
WINHTTP_QUERY_RANGE = 62;
WINHTTP_QUERY_TRANSFER_ENCODING = 63;
WINHTTP_QUERY_UPGRADE = 64;
WINHTTP_QUERY_VARY = 65;
WINHTTP_QUERY_VIA = 66;
WINHTTP_QUERY_WARNING = 67;
WINHTTP_QUERY_EXPECT = 68;
WINHTTP_QUERY_PROXY_CONNECTION = 69;
WINHTTP_QUERY_UNLESS_MODIFIED_SINCE = 70;
WINHTTP_QUERY_PROXY_SUPPORT = 75;
WINHTTP_QUERY_AUTHENTICATION_INFO = 76;
WINHTTP_QUERY_PASSPORT_URLS = 77;
WINHTTP_QUERY_PASSPORT_CONFIG = 78;
WINHTTP_QUERY_MAX = 78;
WINHTTP_QUERY_CUSTOM = 65535;
WINHTTP_QUERY_FLAG_REQUEST_HEADERS = $80000000;
WINHTTP_QUERY_FLAG_SYSTEMTIME = $40000000;
WINHTTP_QUERY_FLAG_NUMBER = $20000000;
// Secure connection error status flags
WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED = $00000001;
WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT = $00000002;
WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED = $00000004;
WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA = $00000008;
WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID = $00000010;
WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID = $00000020;
WINHTTP_CALLBACK_STATUS_FLAG_CERT_WRONG_USAGE = $00000040;
WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR = $80000000;
WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 = $00000008;
WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 = $00000020;
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 = $00000080;
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 = $00000200;
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 = $00000800;
// Note: SECURE_PROTOCOL_ALL does not include TLS1.1 and higher!
WINHTTP_FLAG_SECURE_PROTOCOL_ALL = WINHTTP_FLAG_SECURE_PROTOCOL_SSL2
or WINHTTP_FLAG_SECURE_PROTOCOL_SSL3
or WINHTTP_FLAG_SECURE_PROTOCOL_TLS1;
// AutoProxy
WINHTTP_AUTOPROXY_AUTO_DETECT = $00000001;
WINHTTP_AUTOPROXY_CONFIG_URL = $00000002;
WINHTTP_AUTOPROXY_HOST_KEEPCASE = $00000004;
WINHTTP_AUTOPROXY_HOST_LOWERCASE = $00000008;
WINHTTP_AUTOPROXY_RUN_INPROCESS = $00010000;
WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY = $00020000;
// Flags for dwAutoDetectFlags
WINHTTP_AUTO_DETECT_TYPE_DHCP = $00000001;
WINHTTP_AUTO_DETECT_TYPE_DNS_A = $00000002;
const
WINHTTP_ERROR_BASE = 12000;
ERROR_WINHTTP_OUT_OF_HANDLES = WINHTTP_ERROR_BASE + 1;
ERROR_WINHTTP_TIMEOUT = WINHTTP_ERROR_BASE + 2;
ERROR_WINHTTP_INTERNAL_ERROR = WINHTTP_ERROR_BASE + 4;
ERROR_WINHTTP_INVALID_URL = WINHTTP_ERROR_BASE + 5;
ERROR_WINHTTP_UNRECOGNIZED_SCHEME = WINHTTP_ERROR_BASE + 6;
ERROR_WINHTTP_NAME_NOT_RESOLVED = WINHTTP_ERROR_BASE + 7;
ERROR_WINHTTP_INVALID_OPTION = WINHTTP_ERROR_BASE + 9;
ERROR_WINHTTP_OPTION_NOT_SETTABLE = WINHTTP_ERROR_BASE + 11;
ERROR_WINHTTP_SHUTDOWN = WINHTTP_ERROR_BASE + 12;
ERROR_WINHTTP_LOGIN_FAILURE = WINHTTP_ERROR_BASE + 15;
ERROR_WINHTTP_OPERATION_CANCELLED = WINHTTP_ERROR_BASE + 17;
ERROR_WINHTTP_INCORRECT_HANDLE_TYPE = WINHTTP_ERROR_BASE + 18;
ERROR_WINHTTP_INCORRECT_HANDLE_STATE = WINHTTP_ERROR_BASE + 19;
ERROR_WINHTTP_CANNOT_CONNECT = WINHTTP_ERROR_BASE + 29;
ERROR_WINHTTP_CONNECTION_ERROR = WINHTTP_ERROR_BASE + 30;
ERROR_WINHTTP_RESEND_REQUEST = WINHTTP_ERROR_BASE + 32;
ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED = WINHTTP_ERROR_BASE + 44;
ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN = WINHTTP_ERROR_BASE + 100;
ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND = WINHTTP_ERROR_BASE + 101;
ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND = WINHTTP_ERROR_BASE + 102;
ERROR_WINHTTP_CANNOT_CALL_AFTER_OPEN = WINHTTP_ERROR_BASE + 103;
ERROR_WINHTTP_HEADER_NOT_FOUND = WINHTTP_ERROR_BASE + 150;
ERROR_WINHTTP_INVALID_SERVER_RESPONSE = WINHTTP_ERROR_BASE + 152;
ERROR_WINHTTP_INVALID_HEADER = WINHTTP_ERROR_BASE + 153;
ERROR_WINHTTP_INVALID_QUERY_REQUEST = WINHTTP_ERROR_BASE + 154;
ERROR_WINHTTP_HEADER_ALREADY_EXISTS = WINHTTP_ERROR_BASE + 155;
ERROR_WINHTTP_REDIRECT_FAILED = WINHTTP_ERROR_BASE + 156;
ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR = WINHTTP_ERROR_BASE + 178;
ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT = WINHTTP_ERROR_BASE + 166;
ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT = WINHTTP_ERROR_BASE + 167;
ERROR_WINHTTP_NOT_INITIALIZED = WINHTTP_ERROR_BASE + 172;
ERROR_WINHTTP_SECURE_FAILURE = WINHTTP_ERROR_BASE + 175;
// Certificate security errors. Additional information is provided
// via the WINHTTP_CALLBACK_STATUS_SECURE_FAILURE callback notification.
ERROR_WINHTTP_SECURE_CERT_DATE_INVALID = WINHTTP_ERROR_BASE + 37;
ERROR_WINHTTP_SECURE_CERT_CN_INVALID = WINHTTP_ERROR_BASE + 38;
ERROR_WINHTTP_SECURE_INVALID_CA = WINHTTP_ERROR_BASE + 45;
ERROR_WINHTTP_SECURE_CERT_REV_FAILED = WINHTTP_ERROR_BASE + 57;
ERROR_WINHTTP_SECURE_CHANNEL_ERROR = WINHTTP_ERROR_BASE + 157;
ERROR_WINHTTP_SECURE_INVALID_CERT = WINHTTP_ERROR_BASE + 169;
ERROR_WINHTTP_SECURE_CERT_REVOKED = WINHTTP_ERROR_BASE + 170;
ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE = WINHTTP_ERROR_BASE + 179;
ERROR_WINHTTP_AUTODETECTION_FAILED = WINHTTP_ERROR_BASE + 180;
ERROR_WINHTTP_HEADER_COUNT_EXCEEDED = WINHTTP_ERROR_BASE + 181;
ERROR_WINHTTP_HEADER_SIZE_OVERFLOW = WINHTTP_ERROR_BASE + 182;
ERROR_WINHTTP_CHUNKED_ENCODING_HEADER_SIZE_OVERFLOW = WINHTTP_ERROR_BASE + 183;
ERROR_WINHTTP_RESPONSE_DRAIN_OVERFLOW = WINHTTP_ERROR_BASE + 184;
ERROR_WINHTTP_CLIENT_CERT_NO_PRIVATE_KEY = WINHTTP_ERROR_BASE + 185;
ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY = WINHTTP_ERROR_BASE + 186;
WINHTTP_ERROR_LAST = WINHTTP_ERROR_BASE + 186;
const
WINHTTP_THRIFT_DEFAULTS = WINHTTP_FLAG_NULL_CODEPAGE
or WINHTTP_FLAG_BYPASS_PROXY_CACHE
or WINHTTP_FLAG_ESCAPE_DISABLE;
type
IWinHTTPSession = interface;
IWinHTTPConnection = interface;
IWinHTTPRequest = interface
['{FADA4B45-D447-4F07-8361-1AD6656E5E8C}']
function Handle : HINTERNET;
function Connection : IWinHTTPConnection;
function AddRequestHeader( const aHeader : string; const addflag : DWORD = WINHTTP_ADDREQ_FLAG_ADD) : Boolean;
function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
procedure TryAutoProxy( const aUrl : string);
procedure EnableAutomaticContentDecompression( const aEnable : Boolean);
function SendRequest( const pBuf : Pointer; const dwBytes : DWORD; const dwExtra : DWORD = 0) : Boolean;
function WriteExtraData( const pBuf : Pointer; const dwBytes : DWORD) : DWORD;
function FlushAndReceiveResponse : Boolean;
function ReadData( const dwRead : DWORD) : TBytes; overload;
function ReadData( const pBuf : Pointer; const dwRead : DWORD) : DWORD; overload;
function QueryDataAvailable : DWORD;
function QueryTotalResponseSize( out dwSize : DWORD) : Boolean;
function QueryHttpStatus( out dwStatusCode : DWORD; out sStatusText : string) : Boolean;
end;
IWinHTTPConnection = interface
['{ED5BCA49-84D6-4CFE-BF18-3238B1FF2AFB}']
function Handle : HINTERNET;
function Session : IWinHTTPSession;
function OpenRequest( const secure : Boolean; const aVerb, aObjName, aAcceptTypes : UnicodeString) : IWinHTTPRequest;
end;
IWinHTTPSession = interface
['{261ADCB7-5465-4407-8840-468C17F009F0}']
function Handle : HINTERNET;
function Connect( const aHostName : UnicodeString; const aPort : INTERNET_PORT = INTERNET_DEFAULT_PORT) : IWinHTTPConnection;
function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
function EnableSecureProtocols( const aFlagSet : DWORD) : Boolean;
end;
IWinHTTPUrl = interface
['{78BE977C-4171-4AF5-A250-FD2890205E63}']
// url parts getter
function GetScheme : UnicodeString;
function GetNumScheme : INTERNET_SCHEME;
function GetHostName : UnicodeString;
function GetPort : INTERNET_PORT;
function GetUserName : UnicodeString;
function GetPassword : UnicodeString;
function GetUrlPath : UnicodeString;
function GetExtraInfo : UnicodeString;
// url parts setter
procedure SetScheme( const value : UnicodeString);
procedure SetHostName ( const value : UnicodeString);
procedure SetPort( const value : INTERNET_PORT);
procedure SetUserName( const value : UnicodeString);
procedure SetPassword( const value : UnicodeString);
procedure SetUrlPath( const value : UnicodeString);
procedure SetExtraInfo( const value : UnicodeString);
// url as a whole
function BuildUrl : UnicodeString;
procedure CrackUrl( const value : UnicodeString);
// url parts
property Scheme : UnicodeString read GetScheme write SetScheme;
property NumScheme : INTERNET_SCHEME read GetNumScheme; // readonly
property HostName : UnicodeString read GetHostName write SetHostName;
property Port : INTERNET_PORT read GetPort write SetPort;
property UserName : UnicodeString read GetUserName write SetUserName;
property Password : UnicodeString read GetPassword write SetPassword;
property UrlPath : UnicodeString read GetUrlPath write SetUrlPath;
property ExtraInfo : UnicodeString read GetExtraInfo write SetExtraInfo;
// url as a whole
property CompleteURL : UnicodeString read BuildUrl write CrackUrl;
end;
type
TWinHTTPHandleObjectImpl = class( TInterfacedObject)
strict protected
FHandle : HINTERNET;
function Handle : HINTERNET;
public
constructor Create( const aHandle : HINTERNET);
destructor Destroy; override;
end;
TWinHTTPSessionImpl = class( TWinHTTPHandleObjectImpl, IWinHTTPSession)
strict protected
// IWinHTTPSession
function Connect( const aHostName : UnicodeString; const aPort : INTERNET_PORT = INTERNET_DEFAULT_PORT) : IWinHTTPConnection;
function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
function EnableSecureProtocols( const aFlagSet : DWORD) : Boolean;
public
constructor Create( const aAgent : UnicodeString;
const aAccessType : DWORD = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
const aProxy : UnicodeString = '';
const aProxyBypass : UnicodeString = '';
const aFlags : DWORD = 0);
destructor Destroy; override;
end;
TWinHTTPConnectionImpl = class( TWinHTTPHandleObjectImpl, IWinHTTPConnection)
strict protected
FSession : IWinHTTPSession;
// IWinHTTPConnection
function OpenRequest( const secure : Boolean; const aVerb, aObjName, aAcceptTypes : UnicodeString) : IWinHTTPRequest;
function Session : IWinHTTPSession;
public
constructor Create( const aSession : IWinHTTPSession; const aHostName : UnicodeString; const aPort : INTERNET_PORT);
destructor Destroy; override;
end;
TAcceptTypesArray = array of string;
TWinHTTPRequestImpl = class( TWinHTTPHandleObjectImpl, IWinHTTPRequest)
strict protected
FConnection : IWinHTTPConnection;
// IWinHTTPRequest
function Connection : IWinHTTPConnection;
function AddRequestHeader( const aHeader : string; const addflag : DWORD = WINHTTP_ADDREQ_FLAG_ADD) : Boolean;
function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
procedure TryAutoProxy( const aUrl : string);
procedure EnableAutomaticContentDecompression( const aEnable : Boolean);
function SendRequest( const pBuf : Pointer; const dwBytes : DWORD; const dwExtra : DWORD = 0) : Boolean;
function WriteExtraData( const pBuf : Pointer; const dwBytes : DWORD) : DWORD;
function FlushAndReceiveResponse : Boolean;
function ReadData( const dwRead : DWORD) : TBytes; overload;
function ReadData( const pBuf : Pointer; const dwRead : DWORD) : DWORD; overload;
function QueryDataAvailable : DWORD;
function QueryTotalResponseSize( out dwSize : DWORD) : Boolean;
function QueryHttpStatus( out dwStatusCode : DWORD; out sStatusText : string) : Boolean;
public
constructor Create( const aConnection : IWinHTTPConnection;
const aVerb, aObjName : UnicodeString;
const aVersion : UnicodeString = '';
const aReferrer : UnicodeString = '';
const aAcceptTypes : UnicodeString = '*/*';
const aFlags : DWORD = WINHTTP_THRIFT_DEFAULTS
);
destructor Destroy; override;
end;
TWinHTTPUrlImpl = class( TInterfacedObject, IWinHTTPUrl)
strict private
FScheme : UnicodeString;
FNumScheme : INTERNET_SCHEME;
FHostName : UnicodeString;
FPort : INTERNET_PORT;
FUserName : UnicodeString;
FPassword : UnicodeString;
FUrlPath : UnicodeString;
FExtraInfo : UnicodeString;
strict protected
// url parts getter
function GetScheme : UnicodeString;
function GetNumScheme : INTERNET_SCHEME;
function GetHostName : UnicodeString;
function GetPort : INTERNET_PORT;
function GetUserName : UnicodeString;
function GetPassword : UnicodeString;
function GetUrlPath : UnicodeString;
function GetExtraInfo : UnicodeString;
// url parts setter
procedure SetScheme( const value : UnicodeString);
procedure SetHostName ( const value : UnicodeString);
procedure SetPort( const value : INTERNET_PORT);
procedure SetUserName( const value : UnicodeString);
procedure SetPassword( const value : UnicodeString);
procedure SetUrlPath( const value : UnicodeString);
procedure SetExtraInfo( const value : UnicodeString);
// url as a whole
function BuildUrl : UnicodeString;
procedure CrackUrl( const value : UnicodeString);
public
constructor Create( const aUri : UnicodeString);
destructor Destroy; override;
end;
WINHTTP_PROXY_INFO_Helper = record helper for WINHTTP_PROXY_INFO
procedure Initialize;
procedure FreeAllocatedResources;
end;
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG_Helper = record helper for WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
procedure Initialize;
procedure FreeAllocatedResources;
end;
EWinHTTPException = class(Exception);
{ helper functions }
function WinHttpSysErrorMessage( const error : Cardinal): string;
procedure RaiseLastWinHttpError;
implementation
const WINHTTP_DLL = 'WinHTTP.dll';
function WinHttpCloseHandle; stdcall; external WINHTTP_DLL;
function WinHttpOpen; stdcall; external WINHTTP_DLL;
function WinHttpConnect; stdcall; external WINHTTP_DLL;
function WinHttpOpenRequest; stdcall; external WINHTTP_DLL;
function WinHttpSendRequest; stdcall; external WINHTTP_DLL;
function WinHttpSetTimeouts; stdcall; external WINHTTP_DLL;
function WinHttpQueryOption; stdcall; external WINHTTP_DLL;
function WinHttpSetOption; stdcall; external WINHTTP_DLL;
function WinHttpAddRequestHeaders; stdcall; external WINHTTP_DLL;
function WinHttpGetProxyForUrl; stdcall; external WINHTTP_DLL;
function WinHttpGetIEProxyConfigForCurrentUser; stdcall; external WINHTTP_DLL;
function WinHttpWriteData; stdcall; external WINHTTP_DLL;
function WinHttpReceiveResponse; stdcall; external WINHTTP_DLL;
function WinHttpQueryHeaders; stdcall; external WINHTTP_DLL;
function WinHttpQueryDataAvailable; stdcall; external WINHTTP_DLL;
function WinHttpReadData; stdcall; external WINHTTP_DLL;
function WinHttpCrackUrl; stdcall; external WINHTTP_DLL;
function WinHttpCreateUrl; stdcall; external WINHTTP_DLL;
{ helper functions }
function WinHttpSysErrorMessage( const error : Cardinal): string;
const FLAGS = FORMAT_MESSAGE_ALLOCATE_BUFFER
or FORMAT_MESSAGE_IGNORE_INSERTS
or FORMAT_MESSAGE_FROM_SYSTEM
or FORMAT_MESSAGE_FROM_HMODULE;
var pBuffer : PChar;
nChars : Cardinal;
begin
if (error < WINHTTP_ERROR_BASE)
or (error > WINHTTP_ERROR_LAST)
then Exit( SysUtils.SysErrorMessage( error));
pBuffer := nil;
try
nChars := FormatMessage( FLAGS,
Pointer( GetModuleHandle( WINHTTP_DLL)),
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
@pBuffer, 0,
nil);
SetString( result, pBuffer, nChars);
finally
LocalFree( NativeUInt( pBuffer));
end;
end;
procedure RaiseLastWinHttpError;
var error : Cardinal;
sMsg : string;
begin
error := Cardinal( GetLastError);
if error <> NOERROR then begin
sMSg := IntToStr(Integer(error))+' '+WinHttpSysErrorMessage(error);
raise EWinHTTPException.Create( sMsg);
end;
end;
{ misc. record helper }
procedure GlobalFreeAndNil( var p : LPWSTR);
begin
if p <> nil then begin
GlobalFree( HGLOBAL( p));
p := nil;
end;
end;
procedure WINHTTP_PROXY_INFO_Helper.Initialize;
begin
FillChar( Self, SizeOf(Self), 0);
end;
procedure WINHTTP_PROXY_INFO_Helper.FreeAllocatedResources;
// The caller must free the lpszProxy and lpszProxyBypass strings
// if they are non-NULL. Use GlobalFree to free the strings.
begin
GlobalFreeAndNil( lpszProxy);
GlobalFreeAndNil( lpszProxyBypass);
Initialize;
end;
procedure WINHTTP_CURRENT_USER_IE_PROXY_CONFIG_Helper.Initialize;
begin
FillChar( Self, SizeOf(Self), 0);
end;
procedure WINHTTP_CURRENT_USER_IE_PROXY_CONFIG_Helper.FreeAllocatedResources;
// The caller must free the lpszProxy, lpszProxyBypass and lpszAutoConfigUrl strings
// if they are non-NULL. Use GlobalFree to free the strings.
begin
GlobalFreeAndNil( lpszProxy);
GlobalFreeAndNil( lpszProxyBypass);
GlobalFreeAndNil( lpszAutoConfigUrl);
Initialize;
end;
{ TWinHTTPHandleObjectImpl }
constructor TWinHTTPHandleObjectImpl.Create( const aHandle : HINTERNET);
begin
inherited Create;
FHandle := aHandle;
if FHandle = nil
then raise EWinHTTPException.Create('Invalid handle');
end;
destructor TWinHTTPHandleObjectImpl.Destroy;
begin
try
if Assigned(FHandle) then begin
WinHttpCloseHandle(FHandle);
FHandle := nil;
end;
finally
inherited Destroy;
end;
end;
function TWinHTTPHandleObjectImpl.Handle : HINTERNET;
begin
result := FHandle;
end;
{ TWinHTTPSessionImpl }
constructor TWinHTTPSessionImpl.Create( const aAgent : UnicodeString; const aAccessType : DWORD;
const aProxy, aProxyBypass : UnicodeString; const aFlags : DWORD);
var handle : HINTERNET;
begin
handle := WinHttpOpen( PWideChar(aAgent), aAccessType,
PWideChar(Pointer(aProxy)), // may be nil
PWideChar(Pointer(aProxyBypass)), // may be nil
aFlags);
if handle = nil then RaiseLastWinHttpError;
inherited Create( handle);
end;
destructor TWinHTTPSessionImpl.Destroy;
begin
inherited Destroy;
// add code here
end;
function TWinHTTPSessionImpl.Connect( const aHostName : UnicodeString; const aPort : INTERNET_PORT) : IWinHTTPConnection;
begin
result := TWinHTTPConnectionImpl.Create( Self, aHostName, aPort);
end;
function TWinHTTPSessionImpl.SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
begin
result := WinHttpSetTimeouts( FHandle, aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout);
end;
function TWinHTTPSessionImpl.EnableSecureProtocols( const aFlagSet : DWORD) : Boolean;
var dwSize : DWORD;
begin
dwSize := SizeOf(aFlagSet);
result := WinHttpSetOption( Handle, WINHTTP_OPTION_SECURE_PROTOCOLS, @aFlagset, dwSize);
end;
{ TWinHTTPConnectionImpl }
constructor TWinHTTPConnectionImpl.Create( const aSession : IWinHTTPSession; const aHostName : UnicodeString; const aPort : INTERNET_PORT);
var handle : HINTERNET;
begin
FSession := aSession;
handle := WinHttpConnect( FSession.Handle, PWideChar(aHostName), aPort, 0);
if handle = nil then RaiseLastWinHttpError;
inherited Create( handle);
end;
destructor TWinHTTPConnectionImpl.Destroy;
begin
inherited Destroy;
FSession := nil;
end;
function TWinHTTPConnectionImpl.Session : IWinHTTPSession;
begin
result := FSession;
end;
function TWinHTTPConnectionImpl.OpenRequest( const secure : Boolean; const aVerb, aObjName, aAcceptTypes : UnicodeString) : IWinHTTPRequest;
var dwFlags : DWORD;
begin
dwFlags := WINHTTP_THRIFT_DEFAULTS;
if secure
then dwFlags := dwFlags or WINHTTP_FLAG_SECURE
else dwFlags := dwFlags and not WINHTTP_FLAG_SECURE;
result := TWinHTTPRequestImpl.Create( Self, aVerb, aObjName, '', '', aAcceptTypes, dwFlags);
end;
{ TWinHTTPRequestImpl }
constructor TWinHTTPRequestImpl.Create( const aConnection : IWinHTTPConnection;
const aVerb, aObjName, aVersion, aReferrer : UnicodeString;
const aAcceptTypes : UnicodeString;
const aFlags : DWORD
);
var handle : HINTERNET;
accept : array[0..1] of PWideChar;
begin
FConnection := aConnection;
accept[0] := PWideChar(aAcceptTypes);
accept[1] := nil;
handle := WinHttpOpenRequest( FConnection.Handle,
PWideChar(UpperCase(aVerb)),
PWideChar(aObjName),
PWideChar(aVersion),
PWideChar(aReferrer),
@accept,
aFlags);
if handle = nil then RaiseLastWinHttpError;
inherited Create( handle);
end;
destructor TWinHTTPRequestImpl.Destroy;
begin
inherited Destroy;
FConnection := nil;
end;
function TWinHTTPRequestImpl.Connection : IWinHTTPConnection;
begin
result := FConnection;
end;
function TWinHTTPRequestImpl.SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
begin
result := WinHttpSetTimeouts( FHandle, aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout);
end;
function TWinHTTPRequestImpl.AddRequestHeader( const aHeader : string; const addflag : DWORD) : Boolean;
begin
result := WinHttpAddRequestHeaders( FHandle, PWideChar(aHeader), DWORD(-1), addflag);
end;
procedure TWinHTTPRequestImpl.TryAutoProxy( const aUrl : string);
// From MSDN:
// AutoProxy support is not fully integrated into the HTTP stack in WinHTTP.
// Before sending a request, the application must call WinHttpGetProxyForUrl
// to obtain the name of a proxy server and then call WinHttpSetOption using
// WINHTTP_OPTION_PROXY to set the proxy configuration on the WinHTTP request
// handle created by WinHttpOpenRequest.
// See https://docs.microsoft.com/en-us/windows/desktop/winhttp/winhttp-autoproxy-api
var
options : WINHTTP_AUTOPROXY_OPTIONS;
proxy : WINHTTP_PROXY_INFO;
ieProxy : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
dwSize : DWORD;
begin
// try AutoProxy via PAC first
proxy.Initialize;
try
FillChar( options, SizeOf(options), 0);
options.dwFlags := WINHTTP_AUTOPROXY_AUTO_DETECT;
options.dwAutoDetectFlags := WINHTTP_AUTO_DETECT_TYPE_DHCP or WINHTTP_AUTO_DETECT_TYPE_DNS_A;
options.fAutoLogonIfChallenged := TRUE;
if WinHttpGetProxyForUrl( FConnection.Session.Handle, PChar(aUrl), options, proxy) then begin
dwSize := SizeOf(proxy);
WinHttpSetOption( Handle, WINHTTP_OPTION_PROXY, @proxy, dwSize);
Exit;
end;
finally
proxy.FreeAllocatedResources;
end;
// Use IE settings as a fallback, useful in client (i.e. non-server) environments
ieProxy.Initialize;
try
if WinHttpGetIEProxyConfigForCurrentUser( ieProxy)
then begin
// lpszAutoConfigUrl = "Use automatic proxy configuration"
if ieProxy.lpszAutoConfigUrl <> nil then begin
options.lpszAutoConfigUrl := ieProxy.lpszAutoConfigUrl;
options.dwFlags := options.dwFlags or WINHTTP_AUTOPROXY_CONFIG_URL;
proxy.Initialize;
try
if WinHttpGetProxyForUrl( FConnection.Session.Handle, PChar(aUrl), options, proxy) then begin
dwSize := SizeOf(proxy);
WinHttpSetOption( Handle, WINHTTP_OPTION_PROXY, @proxy, dwSize);
Exit;
end;
finally
proxy.FreeAllocatedResources;
end;
end;
// lpszProxy = "use a proxy server"
if ieProxy.lpszProxy <> nil then begin
proxy.Initialize;
try
proxy.dwAccessType := WINHTTP_ACCESS_TYPE_NAMED_PROXY;
proxy.lpszProxy := ieProxy.lpszProxy;
proxy.lpszProxyBypass := ieProxy.lpszProxyBypass;
dwSize := SizeOf(proxy);
WinHttpSetOption( Handle, WINHTTP_OPTION_PROXY, @proxy, dwSize);
Exit;
finally
proxy.Initialize; // not FreeAllocatedResources, we only hold pointer copies!
end;
end;
end;
finally
ieProxy.FreeAllocatedResources;
end;
end;
procedure TWinHTTPRequestImpl.EnableAutomaticContentDecompression( const aEnable : Boolean);
// Enable automatic gzip,deflate decompression on systems that support this option
// From the docs: WinHTTP will automatically set an appropriate Accept-Encoding header,
// overriding any value supplied by the caller -> we don't have to do this
// Available on Win 8.1 or higher
var value : DWORD;
begin
if aEnable
then value := WINHTTP_DECOMPRESSION_FLAG_ALL
else value := 0;
// ignore returned value, the option is not supported with older WinHTTP versions
WinHttpSetOption( Handle, WINHTTP_OPTION_DECOMPRESSION, @value, SizeOf(DWORD));
end;
function TWinHTTPRequestImpl.SendRequest( const pBuf : Pointer; const dwBytes, dwExtra : DWORD) : Boolean;
begin
result := WinHttpSendRequest( FHandle,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
pBuf, dwBytes, // number of bytes in pBuf
dwBytes + dwExtra, // becomes the Content-Length
nil); // context for async operations
end;
function TWinHTTPRequestImpl.WriteExtraData( const pBuf : Pointer; const dwBytes : DWORD) : DWORD;
begin
if not WinHttpWriteData( FHandle, pBuf, dwBytes, result)
then result := 0;
end;
function TWinHTTPRequestImpl.FlushAndReceiveResponse : Boolean;
begin
result := WinHttpReceiveResponse( FHandle, nil);
end;
function TWinHTTPRequestImpl.ReadData( const dwRead : DWORD) : TBytes;
var dwAvailable, dwReceived : DWORD;
begin
if WinHttpQueryDataAvailable( FHandle, dwAvailable)
then dwAvailable := Min( dwRead, dwAvailable)
else dwAvailable := 0;
SetLength( result, dwAvailable);
if dwAvailable = 0 then Exit;
if WinHttpReadData( FHandle, @result[0], Length(result), dwReceived)
then SetLength( result, dwReceived)
else SetLength( result, 0);
end;
function TWinHTTPRequestImpl.ReadData( const pBuf : Pointer; const dwRead : DWORD) : DWORD;
var dwAvailable : DWORD;
begin
if WinHttpQueryDataAvailable( FHandle, dwAvailable)
then dwAvailable := Min( dwRead, dwAvailable)
else dwAvailable := 0;
if (dwAvailable = 0)
or not WinHttpReadData( FHandle, pBuf, dwAvailable, result)
then result := 0;
end;
function TWinHTTPRequestImpl.QueryDataAvailable : DWORD;
begin
if not WinHttpQueryDataAvailable( FHandle, result)
then result := 0;
end;
function TWinHTTPRequestImpl.QueryTotalResponseSize( out dwSize : DWORD) : Boolean;
var dwBytes, dwError, dwIndex : DWORD;
bytes : array[0..32-1] of Byte;
begin
dwBytes := SizeOf( result);
dwIndex := DWORD( WINHTTP_NO_HEADER_INDEX);
result := WinHttpQueryHeaders( FHandle,
WINHTTP_QUERY_CONTENT_LENGTH or WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
@dwSize, dwBytes,
dwIndex);
if result then Exit;
dwError := GetLastError;
// Hack: WinHttpQueryHeaders() sometimes returns error 122 even if the buffer is large enough
// According to the docs, WINHTTP_QUERY_FLAG_NUMBER always returns a 32 bit number (which it does!)
// but for some strange reason since win 10 build 18636.815 passing a 4 bytes buffer is not enough anymore
if dwError = ERROR_INSUFFICIENT_BUFFER then begin
dwBytes := sizeof(bytes);
FillChar( bytes[0], dwBytes, 0);
result := WinHttpQueryHeaders( FHandle,
WINHTTP_QUERY_CONTENT_LENGTH or WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
@bytes[0], dwBytes,
dwIndex);
if result then begin
ASSERT( dwBytes = SizeOf(dwSize)); // result is still a DWORD
Move( bytes[0], dwSize, SizeOf(dwSize)); // copy over result data
Exit;
end;
end;
// header may just not be there
dwSize := MAXINT; // we don't know, just return something useful
ASSERT( dwError = ERROR_WINHTTP_HEADER_NOT_FOUND); // anything else would be an real error
end;
function TWinHTTPRequestImpl.QueryHttpStatus( out dwStatusCode : DWORD; out sStatusText : string) : Boolean;
var dwBytes, dwError, dwIndex : DWORD;
begin
// HTTP status code
dwIndex := DWORD( WINHTTP_NO_HEADER_INDEX);
dwBytes := SizeOf(dwStatusCode);
result := WinHttpQueryHeaders( FHandle,
WINHTTP_QUERY_STATUS_CODE or WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
@dwStatusCode, dwBytes,
dwIndex);
if not result then begin
dwStatusCode := 0; // no data
dwError := GetLastError;
if dwError <> ERROR_WINHTTP_HEADER_NOT_FOUND then ASSERT(FALSE); // anything else would be an real error
end;
// HTTP status text
dwIndex := DWORD( WINHTTP_NO_HEADER_INDEX);
dwBytes := 0;
result := WinHttpQueryHeaders( FHandle,
WINHTTP_QUERY_STATUS_TEXT,
WINHTTP_HEADER_NAME_BY_INDEX,
WINHTTP_NO_OUTPUT_BUFFER, // we need to determine the size first
dwBytes, // will receive the required buffer size
dwIndex);
if dwBytes > 0 then begin // allocate buffer and call again to get the value
SetLength( sStatusText, dwBytes div SizeOf(Char));
dwBytes := Length(sStatusText) * SizeOf(Char);
result := WinHttpQueryHeaders( FHandle,
WINHTTP_QUERY_STATUS_TEXT,
WINHTTP_HEADER_NAME_BY_INDEX,
@sStatusText[1], dwBytes,
dwIndex);
end;
if result
then SetLength( sStatusText, StrLen(PChar(sStatusText))) // get rid of any terminating #0 chars
else begin
sStatusText := ''; // no data
dwError := GetLastError;
if dwError <> ERROR_WINHTTP_HEADER_NOT_FOUND then ASSERT(FALSE); // anything else would be an real error
end;
// do we have at least something?
result := (dwStatusCode <> 0) or (sStatusText <> '');
end;
{ TWinHTTPUrlImpl }
constructor TWinHTTPUrlImpl.Create(const aUri: UnicodeString);
begin
inherited Create;
CrackUrl( aUri)
end;
destructor TWinHTTPUrlImpl.Destroy;
begin
inherited Destroy;
end;
procedure TWinHTTPUrlImpl.CrackURL( const value : UnicodeString);
const FLAGS = 0; // no special operations, leave components as-is
var components : URL_COMPONENTS;
begin
FillChar(components, SizeOf(components), 0);
components.dwStructSize := SizeOf(components);
if value <> '' then begin
{ For the WinHttpCrackUrl function, [...] if the pointer member is NULL but the
length member is not zero, both the pointer and length members are returned. }
components.dwSchemeLength := DWORD(-1);
components.dwHostNameLength := DWORD(-1);
components.dwUserNameLength := DWORD(-1);
components.dwPasswordLength := DWORD(-1);
components.dwUrlPathLength := DWORD(-1);
components.dwExtraInfoLength := DWORD(-1);
WinHttpCrackUrl( PWideChar(value), Length(value), FLAGS, components);
end;
FNumScheme := components.nScheme;
FPort := components.nPort;
SetString( FScheme, components.lpszScheme, components.dwSchemeLength);
SetString( FHostName, components.lpszHostName, components.dwHostNameLength);
SetString( FUserName, components.lpszUserName, components.dwUserNameLength);
SetString( FPassword, components.lpszPassword, components.dwPasswordLength);
SetString( FUrlPath, components.lpszUrlPath, components.dwUrlPathLength);
SetString( FExtraInfo, components.lpszExtraInfo, components.dwExtraInfoLength);
end;
function TWinHTTPUrlImpl.BuildUrl : UnicodeString;
const FLAGS = 0; // no special operations, leave components as-is
var components : URL_COMPONENTS;
dwChars : DWORD;
begin
FillChar(components, SizeOf(components), 0);
components.dwStructSize := SizeOf(components);
components.lpszScheme := PWideChar(FScheme);
components.dwSchemeLength := Length(FScheme);
components.lpszHostName := PWideChar(FHostName);
components.dwHostNameLength := Length(FHostName);
components.nPort := FPort;
components.lpszUserName := PWideChar(FUserName);
components.dwUserNameLength := Length(FUserName);
components.lpszPassword := PWideChar(FPassword);
components.dwPasswordLength := Length(FPassword);
components.lpszUrlPath := PWideChar(FUrlPath);
components.dwUrlPathLength := Length(FUrlPath);
components.lpszExtraInfo := PWideChar(FExtraInfo);
components.dwExtraInfoLength := Length(FExtraInfo);
WinHttpCreateUrl( components, FLAGS, nil, dwChars);
if dwChars = 0
then result := ''
else begin
SetLength( result, dwChars + 1);
WinHttpCreateUrl( components, FLAGS, @result[1], dwChars);
SetLength( result, dwChars); // cut off terminating #0
end;
end;
function TWinHTTPUrlImpl.GetExtraInfo: UnicodeString;
begin
result := FExtraInfo;
end;
function TWinHTTPUrlImpl.GetHostName: UnicodeString;
begin
result := FHostName;
end;
function TWinHTTPUrlImpl.GetNumScheme: INTERNET_SCHEME;
begin
result := FNumScheme;
end;
function TWinHTTPUrlImpl.GetPassword: UnicodeString;
begin
result := FPassword;
end;
function TWinHTTPUrlImpl.GetPort: INTERNET_PORT;
begin
result := FPort;
end;
function TWinHTTPUrlImpl.GetScheme: UnicodeString;
begin
result := FScheme;
end;
function TWinHTTPUrlImpl.GetUrlPath: UnicodeString;
begin
result := FUrlPath;
end;
function TWinHTTPUrlImpl.GetUserName: UnicodeString;
begin
result := FUserName;
end;
procedure TWinHTTPUrlImpl.SetExtraInfo(const value: UnicodeString);
begin
FExtraInfo := value;
end;
procedure TWinHTTPUrlImpl.SetHostName(const value: UnicodeString);
begin
FHostName := value;
end;
procedure TWinHTTPUrlImpl.SetPassword(const value: UnicodeString);
begin
FPassword := value;
end;
procedure TWinHTTPUrlImpl.SetPort(const value: INTERNET_PORT);
begin
FPort := value;
end;
procedure TWinHTTPUrlImpl.SetScheme(const value: UnicodeString);
begin
FScheme := value;
end;
procedure TWinHTTPUrlImpl.SetUrlPath(const value: UnicodeString);
begin
FUrlPath := value;
end;
procedure TWinHTTPUrlImpl.SetUserName(const value: UnicodeString);
begin
FUserName := value;
end;
end.