-
-
Save Chunjee/8cf6aaa7512332ae63e4de7accbccf2c to your computer and use it in GitHub Desktop.
WinHttpRequest Warpper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
WinHttpRequest(params*) | |
{ | |
static instance := "" | |
return isObject(instance) | |
? instance | |
: instance := new winhttprequest(params*) | |
} | |
class winhttprequest extends WinHttpRequest.Factory | |
{ | |
class MIME | |
{ | |
gif := "image/gif" | |
jpg := "image/jpeg" | |
json := "application/json" | |
mp4 := "video/mp4" | |
png := "image/png" | |
zip := "application/zip" | |
} | |
encoding[] | |
{ | |
get { | |
return this._encoding | |
} | |
set { | |
return this._encoding := value | |
} | |
} | |
; Identical to JS counterpart | |
encodeURI(uri, encoding := "UTF-8") | |
{ | |
encoding := this._encoding ? this._encoding : encoding | |
return this._encode(uri, encoding, "[!#$&-;=?-Z_a-z~]") | |
} | |
; Identical to JS counterpart | |
encodeURIComponent(uri, encoding := "UTF-8") | |
{ | |
encoding := this._encoding ? this._encoding : encoding | |
return this._encode(uri, encoding, "[!'(-*-\.0-9A-Z_a-z~]") | |
} | |
decodeURI(uri, encoding := "UTF-8") | |
{ | |
encoding := this._encoding ? this._encoding : encoding | |
return this._decode(uri, encoding) | |
} | |
decodeURIComponent(uri, encoding := "UTF-8") | |
{ | |
encoding := this._encoding ? this._encoding : encoding | |
return this._decode(uri, encoding) | |
} | |
objToQuery(body) | |
{ | |
if !IsObject(body) | |
return body | |
out := "" | |
for idx,val in body | |
out .= this.encodeURIComponent(idx) "=" this.encodeURIComponent(val) "&" | |
return RTrim(out, "&") | |
} | |
reset() | |
{ | |
this.__New() | |
} | |
/* | |
Input, escaped " and ` for AHK | |
!""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_``abcdefghijklmnopqrstuvwxyz{|}~ | |
Input, escaped " and \ for JavaScript | |
!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ | |
encodeURI() expected output: | |
!%22#$%25&'()*+,-./0123456789:;%3C=%3E?@ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~ | |
encodeURIComponent() expected output: | |
!%22%23%24%25%26'()*%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~ | |
*/ | |
_encode(txt, encoding, regex) | |
{ | |
VarSetCapacity(var, StrLen(txt) * 2, 0) | |
, StrPut(txt, &var, encoding), out := "" | |
while (code := NumGet(var, A_Index - 1, "UChar")) | |
char := Chr(code) | |
, out .= (char ~= regex ? char : Format("%{:02X}", code)) | |
return out | |
} | |
_decode(uri, encoding) | |
{ | |
uri := StrReplace(uri, "%25", "") ; Record Separator | |
, VarSetCapacity(var, 1), p := 1 | |
while p := InStr(uri, "%",, p) | |
code := SubStr(uri, ++p, 2) | |
, NumPut("0x" code, var, 0, "UChar") | |
, uri := StrReplace(uri, "%" code, StrGet(&var, encoding)) | |
return StrReplace(uri, "", "%") ; Record Separator | |
} | |
_loadFile(file) | |
{ | |
objFile := FileOpen(file, 0x0) | |
, this._memLen += objFile.Length | |
, this._memPtr := DllCall("Kernel32\GlobalReAlloc", "Ptr",this._memPtr, "UInt",this._memLen, "UInt",0x2|0x40) | |
, objFile.RawRead(this._memPtr + this._memLen - objFile.length, objFile.length) | |
} | |
_multiPart(ByRef body) | |
{ | |
this._memLen := 0 | |
boundary := "--------WinHttpRequest-" A_NowUTC A_MSec | |
, this._memPtr := DllCall("Kernel32\GlobalAlloc", "UInt",0x40, "UInt",1, "Ptr") | |
; Convention, but not required: | |
; this._strPut("--" boundary "`r`n" | |
; . "Content-Disposition: form-data; name=""_charset_""`r`n`r`n" this._encoding "`r`n") | |
; https://tools.ietf.org/id/draft-ietf-appsawg-multipart-form-data-01.html#rfc.section.4.6 | |
for field,value in body | |
{ | |
if IsObject(value) | |
{ | |
mimes := new this.MIME | |
for i,filePath in value | |
{ | |
attributes := FileExist(filePath) | |
if (!attributes || InStr(attributes, "D")) | |
throw Exception("File doesn't exist.", -1) | |
SplitPath filePath, fileName,, ext | |
mime := mimes[ext] ? mimes[ext] : "application/octet-stream" | |
, this._strPut("--" boundary "`r`n" | |
. "Content-Disposition: form-data; name=""" field """; filename=""" fileName """`r`n" | |
. "Content-Type: " mime "`r`n`r`n") | |
, this._loadFile(filePath) | |
, this._strPut("`r`n") | |
} | |
} else { | |
this._strPut("--" boundary "`r`n" | |
. "Content-Disposition: form-data; name=""" field """`r`n`r`n" value "`r`n") | |
} | |
} | |
this._strPut("--" boundary "--`r`n") | |
, body := ComObjArray(0x11, this._memLen) | |
, pvData := NumGet(ComObjValue(body) + A_PtrSize + 8) | |
, DllCall("Ntdll\RtlMoveMemory", "Ptr",pvData, "Ptr",this._memPtr, "Ptr",this._memLen) | |
, this._memPtr := DllCall("Kernel32\GlobalFree", "Ptr",this._memPtr, "Ptr") | |
return boundary | |
} | |
_strPut(str) | |
{ | |
size := StrPut(str, this._encoding) - 1 | |
, this._memLen += size | |
, this._memPtr := DllCall("Kernel32\GlobalReAlloc", "Ptr",this._memPtr, "UInt",this._memLen + 1, "UInt",0x2|0x40) | |
, StrPut(str, this._memPtr + this._memLen - size, size, this._encoding) | |
} | |
class Factory | |
{ | |
_encoding := "UTF-8" | |
, _options := "" | |
, _reset := false | |
static HTTPREQUEST_PROXYSETTING_DEFAULT := 0x0 | |
, HTTPREQUEST_PROXYSETTING_DIRECT := 0x1 | |
, HTTPREQUEST_PROXYSETTING_PROXY := 0x2 | |
; https://docs.microsoft.com/en-us/windows/win32/winhttp/iwinhttprequest-setproxy | |
, WinHttpRequestOption_UserAgentString := 0x00 | |
, WinHttpRequestOption_SslErrorIgnoreFlags := 0x04 | |
, WinHttpRequestOption_EnableRedirects := 0x06 | |
; https://docs.microsoft.com/en-us/windows/win32/winhttp/winhttprequestoption | |
__New(options := false) | |
{ | |
this._req := "" | |
if !this._options | |
this._options := IsObject(options) ? options : {} | |
this._req := ComObjCreate("WinHttp.WinHttpRequest.5.1") | |
if (this._options.HasKey("cookies") && !this._options.cookies) | |
this._reset := true | |
if !this._options.proxy | |
this._req.SetProxy(this.HTTPREQUEST_PROXYSETTING_DEFAULT) | |
else if (this._options.proxy = "DIRECT") | |
this._req.SetProxy(this.HTTPREQUEST_PROXYSETTING_DIRECT) | |
else this._req.SetProxy(this.HTTPREQUEST_PROXYSETTING_PROXY, this._options.proxy) | |
this._req.Option(this.WinHttpRequestOption_UserAgentString) := this._options.UA | |
? this._options.UA | |
: "AHK/" A_AhkVersion " (Windows " A_OSVersion "; x" (A_Is64bitOS ? "64" : "86") "; " (A_IsUnicode ? "U" (A_PtrSize = 8 ? "64" : "32") : "ANSI") ")" | |
/* SSL Flags | |
0x0100 Unknown certification authority (CA) or unTrusted root | |
0x0200 Wrong usage | |
0x1000 Invalid common name (CN) | |
0x2000 Invalid date or certificate expired | |
*/ | |
if !this._options.verifySSL | |
this._req.Option(this.WinHttpRequestOption_SslErrorIgnoreFlags) := 0x0100|0x0200|0x1000|0x2000 | |
} | |
__Call(method, url, body := false, headers := false, options := false) | |
{ | |
if !this._req | |
throw Exception("Not instantiated.", -1) | |
options := IsObject(options) ? options : {} | |
, headers := IsObject(headers) ? headers : {} | |
method := Format("{:U}", method) | |
if method not in CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE | |
throw Exception("Invalid HTTP verb.", -1) | |
if (method = "GET" && body) | |
url .= "?" this.objToQuery(body) | |
, body := "" | |
else if (method = "POST") | |
{ | |
hasFiles := false | |
for field,value in body | |
if IsObject(value) | |
{ | |
hasFiles := true | |
break | |
} | |
if (hasFiles || (IsObject(body) && options.multipart)) | |
{ | |
boundary := this._multiPart(body) | |
, headers["Content-Type"] := "multipart/form-data; boundary=" boundary | |
} | |
else | |
{ | |
body := this.objToQuery(body) | |
, headers["Content-Type"] := "application/x-www-form-urlencoded" | |
} | |
} | |
this._req.Open(method, url, true) | |
for key,val in headers | |
this._req.SetRequestHeader(key, val) | |
this._req.Send(body) | |
, this._req.WaitForResponse() | |
if options.object | |
{ | |
headersObj := {} | |
, allHeaders := this._req.GetAllResponseHeaders() | |
loop parse, % Trim(allHeaders, "`n`r"), `n, `r | |
header := StrSplit(A_LoopField, ":", " ", 2) | |
, headersObj[ header[1] ] := header[2] | |
ret := { headers: headersObj | |
, text: this._req.ResponseText | |
, status: this._req.Status } | |
} else ret := this._req.ResponseText | |
return ret, (this._reset ? this.__New() : "") | |
} | |
__Delete() | |
{ | |
this._req := "" | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "winhttprequest.ahk", | |
"version": "0.2.0", | |
"description": "WinHttpRequest Wrapper", | |
"main": "export.ahk", | |
"dependencies": {}, | |
"devDependencies": {}, | |
"author": "Shawn Brooker", | |
"license": "MIT" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment