Skip to content

Instantly share code, notes, and snippets.

@Chunjee
Forked from anonymous1184/README.md
Last active April 30, 2021 16:40
Show Gist options
  • Save Chunjee/8cf6aaa7512332ae63e4de7accbccf2c to your computer and use it in GitHub Desktop.
Save Chunjee/8cf6aaa7512332ae63e4de7accbccf2c to your computer and use it in GitHub Desktop.
WinHttpRequest Warpper
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 := ""
}
}
}
{
"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