Skip to content

Instantly share code, notes, and snippets.

@anonymous1184
Last active January 19, 2024 04:58
Show Gist options
  • Save anonymous1184/e6062286ac7f4c35b612d3a53535cc2a to your computer and use it in GitHub Desktop.
Save anonymous1184/e6062286ac7f4c35b612d3a53535cc2a to your computer and use it in GitHub Desktop.
WinHttpRequest.ahk
#Requires AutoHotkey v1.1
; Version: 2023.07.05.2
; https://gist.github.com/e6062286ac7f4c35b612d3a53535cc2a
; Usage and examples: https://redd.it/mcjj4s
; Testing: http://httpbin.org/ | http://httpbun.org/ | http://ptsv2.com/
WinHttpRequest(oOptions := "") {
return new WinHttpRequest(oOptions)
}
class WinHttpRequest extends WinHttpRequest.Functor {
whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
;#region: Meta
__New(oOptions := "") {
static HTTPREQUEST_PROXYSETTING_DEFAULT := 0, HTTPREQUEST_PROXYSETTING_DIRECT := 1, HTTPREQUEST_PROXYSETTING_PROXY := 2, EnableCertificateRevocationCheck := 18, SslErrorIgnoreFlags := 4, SslErrorFlag_Ignore_All := 13056, SecureProtocols := 9, WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 := 8192, WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 := 2048, UserAgentString := 0
if (!IsObject(oOptions)) {
oOptions := {}
}
if (!oOptions.HasKey("Proxy") || !oOptions.Proxy) {
this.whr.SetProxy(HTTPREQUEST_PROXYSETTING_DEFAULT)
} else if (oOptions.Proxy = "DIRECT") {
this.whr.SetProxy(HTTPREQUEST_PROXYSETTING_DIRECT)
} else {
this.whr.SetProxy(HTTPREQUEST_PROXYSETTING_PROXY, oOptions.Proxy)
}
if (oOptions.HasKey("Revocation")) {
this.whr.Option[EnableCertificateRevocationCheck] := !!oOptions.Revocation
} else {
this.whr.Option[EnableCertificateRevocationCheck] := true
}
if (oOptions.HasKey("SslError")) {
if (oOptions.SslError = false) {
this.whr.Option[SslErrorIgnoreFlags] := SslErrorFlag_Ignore_All
}
}
if (!oOptions.HasKey("TLS")) {
this.whr.Option[SecureProtocols] := WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
} else {
this.whr.Option[SecureProtocols] := oOptions.TLS
}
if (oOptions.HasKey("UA")) {
this.whr.Option[UserAgentString] := oOptions.UA
}
}
;#endregion
;#region: Static
EncodeUri(sUri) {
return this._EncodeDecode(sUri, true, false)
}
EncodeUriComponent(sComponent) {
return this._EncodeDecode(sComponent, true, true)
}
DecodeUri(sUri) {
return this._EncodeDecode(sUri, false, false)
}
DecodeUriComponent(sComponent) {
return this._EncodeDecode(sComponent, false, true)
}
ObjToQuery(oData) {
if (!IsObject(oData)) {
return oData
}
out := ""
for key, val in oData {
out .= this._EncodeDecode(key, true, true) "="
out .= this._EncodeDecode(val, true, true) "&"
}
return RTrim(out, "&")
}
QueryToObj(sData) {
if (IsObject(sData)) {
return sData
}
sData := LTrim(sData, "?")
obj := {}
for _, part in StrSplit(sData, "&") {
pair := StrSplit(part, "=", "", 2)
key := this._EncodeDecode(pair[1], false, true)
val := this._EncodeDecode(pair[2], false, true)
obj[key] := val
}
return obj
}
;#endregion
;#region: Public
Request(sMethod, sUrl, mBody := "", oHeaders := false, oOptions := false) {
if (this.whr = "") {
throw Exception("Not initialized.", -1)
}
sMethod := Format("{:U}", sMethod) ; CONNECT not supported
if !(sMethod ~= "^(DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT|TRACE)$") {
throw Exception("Invalid HTTP verb.", -1, sMethod)
}
if !(sUrl := Trim(sUrl)) {
throw Exception("Empty URL.", -1)
}
if (!IsObject(oHeaders)) {
oHeaders := {}
}
if (!IsObject(oOptions)) {
oOptions := {}
}
if (sMethod = "POST") {
multi := oOptions.HasKey("Multipart") ? !!oOptions.Multipart : false
this._Post(mBody, oHeaders, multi)
} else if (sMethod = "GET" && mBody) {
sUrl := RTrim(sUrl, "&")
sUrl .= InStr(sUrl, "?") ? "&" : "?"
sUrl .= WinHttpRequest.ObjToQuery(mBody)
mBody := ""
}
this.whr.Open(sMethod, sUrl, true)
for key, val in oHeaders {
this.whr.SetRequestHeader(key, val)
}
this.whr.Send(mBody)
this.whr.WaitForResponse()
if (oOptions.HasKey("Save")) {
target := RegExReplace(oOptions.Save, "^\h*\*\h*", "", forceSave)
if (this.whr.Status = 200 || forceSave) {
this._Save(target)
}
return this.whr.Status
}
out := new WinHttpRequest._Response()
out.Headers := this._Headers()
out.Status := this.whr.Status
out.Text := this._Text(oOptions.HasKey("Encoding") ? oOptions.Encoding : "")
return out
}
;#endregion
;#region: Private
static _doc := ""
_EncodeDecode(Text, bEncode, bComponent) {
if (this._doc = "") {
this._doc := ComObjCreate("HTMLFile")
this._doc.write("<meta http-equiv='X-UA-Compatible' content='IE=Edge'>")
}
action := (bEncode ? "en" : "de") "codeURI" (bComponent ? "Component" : "")
return ObjBindMethod(this._doc.parentWindow, action).Call(Text)
}
_Headers() {
headers := this.whr.GetAllResponseHeaders()
headers := RTrim(headers, "`r`n")
out := {}
for _, line in StrSplit(headers, "`n", "`r") {
pair := StrSplit(line, ":", " ", 2)
out[pair[1]] := pair[2]
}
return out
}
_Mime(Extension) {
if (WinHttpRequest.MIME.HasKey(Extension)) {
return WinHttpRequest.MIME[Extension]
}
return "application/octet-stream"
}
_MultiPart(ByRef Body) {
static LMEM_ZEROINIT := 64, EOL := "`r`n"
this._memLen := 0
this._memPtr := DllCall("LocalAlloc", "UInt", LMEM_ZEROINIT, "UInt", 1)
boundary := "----------WinHttpRequest-" A_NowUTC A_MSec
for field, value in Body {
this._MultiPartAdd(boundary, EOL, field, value)
}
this._MultipartStr("--" boundary "--" EOL)
Body := ComObjArray(0x11, this._memLen)
pvData := NumGet(ComObjValue(Body) + 8 + A_PtrSize, "Ptr")
DllCall("RtlMoveMemory", "Ptr", pvData, "Ptr", this._memPtr, "UInt", this._memLen)
DllCall("LocalFree", "Ptr", this._memPtr)
return boundary
}
_MultiPartAdd(Boundary, EOL, Field, Value) {
if (!IsObject(Value)) {
str := "--" Boundary
str .= EOL
str .= "Content-Disposition: form-data; name=""" Field """"
str .= EOL
str .= EOL
str .= Value
str .= EOL
this._MultipartStr(str)
return
}
for _, path in Value {
SplitPath path, filename, , ext
str := "--" Boundary
str .= EOL
str .= "Content-Disposition: form-data; name=""" Field """; filename=""" filename """"
str .= EOL
str .= "Content-Type: " this._Mime(ext)
str .= EOL
str .= EOL
this._MultipartStr(str)
this._MultipartFile(path)
this._MultipartStr(EOL)
}
}
_MultipartFile(Path) {
static LHND := 66
try {
oFile := FileOpen(Path, 0x0)
} catch {
throw Exception("Couldn't open file for reading.", -1, Path)
}
this._memLen += oFile.Length
this._memPtr := DllCall("LocalReAlloc", "Ptr", this._memPtr, "UInt", this._memLen, "UInt", LHND)
oFile.RawRead(this._memPtr + this._memLen - oFile.length, oFile.length)
}
_MultipartStr(Text) {
static LHND := 66
size := StrPut(Text, "UTF-8") - 1
this._memLen += size
this._memPtr := DllCall("LocalReAlloc", "Ptr", this._memPtr, "UInt", this._memLen, "UInt", LHND)
StrPut(Text, this._memPtr + this._memLen - size, size, "UTF-8")
}
_Post(ByRef Body, ByRef Headers, bMultipart) {
isMultipart := 0
for _, value in Body {
isMultipart += !!IsObject(value)
}
if (isMultipart || bMultipart) {
Body := WinHttpRequest.QueryToObj(Body)
boundary := this._MultiPart(Body)
Headers["Content-Type"] := "multipart/form-data; boundary=""" boundary """"
} else {
Body := WinHttpRequest.ObjToQuery(Body)
if (!Headers.HasKey("Content-Type")) {
Headers["Content-Type"] := "application/x-www-form-urlencoded"
}
}
}
_Save(Path) {
arr := this.whr.ResponseBody
pData := NumGet(ComObjValue(arr) + 8 + A_PtrSize, "Ptr")
length := arr.MaxIndex() + 1
FileOpen(Path, 0x1).RawWrite(pData + 0, length)
}
_Text(Encoding) {
response := ""
try response := this.whr.ResponseText
if (response = "" || Encoding != "") {
try {
arr := this.whr.ResponseBody
pData := NumGet(ComObjValue(arr) + 8 + A_PtrSize, "Ptr")
length := arr.MaxIndex() + 1
response := StrGet(pData, length, Encoding)
}
}
return response
}
class Functor {
__Call(Method, Parameters*) {
return this.Request(Method, Parameters*)
}
}
class _Response {
Json {
get {
method := Json.HasKey("parse") ? "parse" : "Load"
oJson := ObjBindMethod(Json, method, this.Text).Call()
ObjRawSet(this, "Json", oJson)
return oJson
}
}
}
;#endregion
class MIME {
static 7z := "application/x-7z-compressed"
static gif := "image/gif"
static jpg := "image/jpeg"
static json := "application/json"
static png := "image/png"
static zip := "application/zip"
}
}
#Requires AutoHotkey v2.0
; Version: 2023.07.05.2
; https://gist.github.com/e6062286ac7f4c35b612d3a53535cc2a
; Usage and examples: https://redd.it/mcjj4s
; Testing: http://httpbin.org/ | http://httpbun.org/ | http://ptsv2.com/
class WinHttpRequest extends WinHttpRequest.Functor {
whr := ComObject("WinHttp.WinHttpRequest.5.1")
;#region: Meta
__New(oOptions := "") {
static HTTPREQUEST_PROXYSETTING_DEFAULT := 0, HTTPREQUEST_PROXYSETTING_DIRECT := 1, HTTPREQUEST_PROXYSETTING_PROXY := 2, EnableCertificateRevocationCheck := 18, SslErrorIgnoreFlags := 4, SslErrorFlag_Ignore_All := 13056, SecureProtocols := 9, WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 := 8192, WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 := 2048, UserAgentString := 0
if (!IsObject(oOptions)) {
oOptions := Map()
}
if (!oOptions.Has("Proxy") || !oOptions["Proxy"]) {
this.whr.SetProxy(HTTPREQUEST_PROXYSETTING_DEFAULT)
} else if (oOptions["Proxy"] = "DIRECT") {
this.whr.SetProxy(HTTPREQUEST_PROXYSETTING_DIRECT)
} else {
this.whr.SetProxy(HTTPREQUEST_PROXYSETTING_PROXY, oOptions["Proxy"])
}
if (oOptions.Has("Revocation")) {
this.whr.Option[EnableCertificateRevocationCheck] := !!oOptions["Revocation"]
} else {
this.whr.Option[EnableCertificateRevocationCheck] := true
}
if (oOptions.Has("SslError")) {
if (oOptions["SslError"] = false) {
this.whr.Option[SslErrorIgnoreFlags] := SslErrorFlag_Ignore_All
}
}
if (!oOptions.Has("TLS")) {
this.whr.Option[SecureProtocols] := WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
} else {
this.whr.Option[SecureProtocols] := oOptions["TLS"]
}
if (oOptions.Has("UA")) {
this.whr.Option[UserAgentString] := oOptions["UA"]
}
}
;#endregion
;#region: Static
static EncodeUri(sUri) {
return this._EncodeDecode(sUri, true, false)
}
static EncodeUriComponent(sComponent) {
return this._EncodeDecode(sComponent, true, true)
}
static DecodeUri(sUri) {
return this._EncodeDecode(sUri, false, false)
}
static DecodeUriComponent(sComponent) {
return this._EncodeDecode(sComponent, false, true)
}
static ObjToQuery(oData) {
if (!IsObject(oData)) {
return oData
}
out := ""
for key, val in oData {
out .= this._EncodeDecode(key, true, true) "="
out .= this._EncodeDecode(val, true, true) "&"
}
return RTrim(out, "&")
}
static QueryToObj(sData) {
if (IsObject(sData)) {
return sData
}
sData := LTrim(sData, "?")
obj := Map()
for _, part in StrSplit(sData, "&") {
pair := StrSplit(part, "=", "", 2)
key := this._EncodeDecode(pair[1], false, true)
val := this._EncodeDecode(pair[2], false, true)
obj[key] := val
}
return obj
}
;#endregion
;#region: Public
Request(sMethod, sUrl, mBody := "", oHeaders := false, oOptions := false) {
if (this.whr = "") {
throw Error("Not initialized.", -1)
}
sMethod := Format("{:U}", sMethod) ; CONNECT not supported
if !(sMethod ~= "^(DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT|TRACE)$") {
throw Error("Invalid HTTP verb.", -1, sMethod)
}
if !(sUrl := Trim(sUrl)) {
throw Error("Empty URL.", -1)
}
if (!IsObject(oHeaders)) {
oHeaders := Map()
}
if (!IsObject(oOptions)) {
oOptions := Map()
}
if (sMethod = "POST") {
multi := oOptions.Has("Multipart") ? !!oOptions["Multipart"] : false
this._Post(&mBody, &oHeaders, multi)
} else if (sMethod = "GET" && mBody) {
sUrl := RTrim(sUrl, "&")
sUrl .= InStr(sUrl, "?") ? "&" : "?"
sUrl .= WinHttpRequest.ObjToQuery(mBody)
mBody := ""
}
this.whr.Open(sMethod, sUrl, true)
for key, val in oHeaders {
this.whr.SetRequestHeader(key, val)
}
this.whr.Send(mBody)
this.whr.WaitForResponse()
if (oOptions.Has("Save")) {
target := RegExReplace(oOptions["Save"], "^\h*\*\h*", "", &forceSave)
if (this.whr.Status = 200 || forceSave) {
this._Save(target)
}
return this.whr.Status
}
out := WinHttpRequest._Response()
out.Headers := this._Headers()
out.Status := this.whr.Status
out.Text := this._Text(oOptions.Has("Encoding") ? oOptions["Encoding"] : "")
return out
}
;#endregion
;#region: Private
static _doc := ""
static _EncodeDecode(Text, bEncode, bComponent) {
if (this._doc = "") {
this._doc := ComObject("HTMLFile")
this._doc.write("<meta http-equiv='X-UA-Compatible' content='IE=Edge'>")
}
action := (bEncode ? "en" : "de") "codeURI" (bComponent ? "Component" : "")
return ObjBindMethod(this._doc.parentWindow, action).Call(Text)
}
_Headers() {
headers := this.whr.GetAllResponseHeaders()
headers := RTrim(headers, "`r`n")
out := Map()
for _, line in StrSplit(headers, "`n", "`r") {
pair := StrSplit(line, ":", " ", 2)
out.Set(pair*)
}
return out
}
_Mime(Extension) {
if (WinHttpRequest.MIME.HasProp(Extension)) {
return WinHttpRequest.MIME.%Extension%
}
return "application/octet-stream"
}
_MultiPart(&Body) {
static LMEM_ZEROINIT := 64, EOL := "`r`n"
this._memLen := 0
this._memPtr := DllCall("LocalAlloc", "UInt", LMEM_ZEROINIT, "UInt", 1)
boundary := "----------WinHttpRequest-" A_NowUTC A_MSec
for field, value in Body {
this._MultiPartAdd(boundary, EOL, field, value)
}
this._MultipartStr("--" boundary "--" EOL)
Body := ComObjArray(0x11, this._memLen)
pvData := NumGet(ComObjValue(Body) + 8 + A_PtrSize, "Ptr")
DllCall("RtlMoveMemory", "Ptr", pvData, "Ptr", this._memPtr, "UInt", this._memLen)
DllCall("LocalFree", "Ptr", this._memPtr)
return boundary
}
_MultiPartAdd(Boundary, EOL, Field, Value) {
if (!IsObject(Value)) {
str := "--" Boundary
str .= EOL
str .= "Content-Disposition: form-data; name=`"" Field "`""
str .= EOL
str .= EOL
str .= Value
str .= EOL
this._MultipartStr(str)
return
}
for _, path in Value {
SplitPath(path, &filename, , &ext)
str := "--" Boundary
str .= EOL
str .= "Content-Disposition: form-data; name=`"" Field "`"; filename=`"" filename "`""
str .= EOL
str .= "Content-Type: " this._Mime(ext)
str .= EOL
str .= EOL
this._MultipartStr(str)
this._MultipartFile(path)
this._MultipartStr(EOL)
}
}
_MultipartFile(Path) {
static LHND := 66
try {
oFile := FileOpen(Path, 0x0)
} catch {
throw Error("Couldn't open file for reading.", -1, Path)
}
this._memLen += oFile.Length
this._memPtr := DllCall("LocalReAlloc", "Ptr", this._memPtr, "UInt", this._memLen, "UInt", LHND)
oFile.RawRead(this._memPtr + this._memLen - oFile.length, oFile.length)
}
_MultipartStr(Text) {
static LHND := 66
size := StrPut(Text, "UTF-8") - 1
this._memLen += size
this._memPtr := DllCall("LocalReAlloc", "Ptr", this._memPtr, "UInt", this._memLen, "UInt", LHND)
StrPut(Text, this._memPtr + this._memLen - size, size, "UTF-8")
}
_Post(&Body, &Headers, bMultipart) {
isMultipart := 0
for _, value in Body {
isMultipart += !!IsObject(value)
}
if (isMultipart || bMultipart) {
Body := WinHttpRequest.QueryToObj(Body)
boundary := this._MultiPart(&Body)
Headers["Content-Type"] := "multipart/form-data; boundary=`"" boundary "`""
} else {
Body := WinHttpRequest.ObjToQuery(Body)
if (!Headers.Has("Content-Type")) {
Headers["Content-Type"] := "application/x-www-form-urlencoded"
}
}
}
_Save(Path) {
arr := this.whr.ResponseBody
pData := NumGet(ComObjValue(arr) + 8 + A_PtrSize, "Ptr")
length := arr.MaxIndex() + 1
FileOpen(Path, 0x1).RawWrite(pData + 0, length)
}
_Text(Encoding) {
response := ""
try response := this.whr.ResponseText
if (response = "" || Encoding != "") {
try {
arr := this.whr.ResponseBody
pData := NumGet(ComObjValue(arr) + 8 + A_PtrSize, "Ptr")
length := arr.MaxIndex() + 1
response := StrGet(pData, length, Encoding)
}
}
return response
}
class Functor {
/* _H v2.0.2 adds Object.Prototype.Get() breaking
GET verb's dynamic call, this is a workaround. */
GET(Parameters*) {
return this.Request("GET", Parameters*)
}
__Call(Method, Parameters) {
return this.Request(Method, Parameters*)
}
}
class _Response {
Json {
get {
method := HasMethod(JSON, "parse") ? "parse" : "Load"
oJson := ObjBindMethod(JSON, method, this.Text).Call()
this.DefineProp("Json", { Value: oJson })
return oJson
}
}
}
;#endregion
class MIME {
static 7z := "application/x-7z-compressed"
static gif := "image/gif"
static jpg := "image/jpeg"
static json := "application/json"
static png := "image/png"
static zip := "application/zip"
}
}
--- WinHttpRequest.ahk
+++ WinHttpRequest-deprecated.ahk
@@ -1 +1 @@
-#Requires AutoHotkey v2.0
+#Requires AutoHotkey v1.1
@@ -7,0 +8,4 @@
+WinHttpRequest(oOptions := "") {
+ return new WinHttpRequest(oOptions)
+}
+
@@ -10 +14 @@
- whr := ComObject("WinHttp.WinHttpRequest.5.1")
+ whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
@@ -17 +21 @@
- oOptions := Map()
+ oOptions := {}
@@ -19 +23 @@
- if (!oOptions.Has("Proxy") || !oOptions["Proxy"]) {
+ if (!oOptions.HasKey("Proxy") || !oOptions.Proxy) {
@@ -21 +25 @@
- } else if (oOptions["Proxy"] = "DIRECT") {
+ } else if (oOptions.Proxy = "DIRECT") {
@@ -24 +28 @@
- this.whr.SetProxy(HTTPREQUEST_PROXYSETTING_PROXY, oOptions["Proxy"])
+ this.whr.SetProxy(HTTPREQUEST_PROXYSETTING_PROXY, oOptions.Proxy)
@@ -26,2 +30,2 @@
- if (oOptions.Has("Revocation")) {
- this.whr.Option[EnableCertificateRevocationCheck] := !!oOptions["Revocation"]
+ if (oOptions.HasKey("Revocation")) {
+ this.whr.Option[EnableCertificateRevocationCheck] := !!oOptions.Revocation
@@ -31,2 +35,2 @@
- if (oOptions.Has("SslError")) {
- if (oOptions["SslError"] = false) {
+ if (oOptions.HasKey("SslError")) {
+ if (oOptions.SslError = false) {
@@ -36 +40 @@
- if (!oOptions.Has("TLS")) {
+ if (!oOptions.HasKey("TLS")) {
@@ -39 +43 @@
- this.whr.Option[SecureProtocols] := oOptions["TLS"]
+ this.whr.Option[SecureProtocols] := oOptions.TLS
@@ -41,2 +45,2 @@
- if (oOptions.Has("UA")) {
- this.whr.Option[UserAgentString] := oOptions["UA"]
+ if (oOptions.HasKey("UA")) {
+ this.whr.Option[UserAgentString] := oOptions.UA
@@ -49 +53 @@
- static EncodeUri(sUri) {
+ EncodeUri(sUri) {
@@ -53 +57 @@
- static EncodeUriComponent(sComponent) {
+ EncodeUriComponent(sComponent) {
@@ -57 +61 @@
- static DecodeUri(sUri) {
+ DecodeUri(sUri) {
@@ -61 +65 @@
- static DecodeUriComponent(sComponent) {
+ DecodeUriComponent(sComponent) {
@@ -65 +69 @@
- static ObjToQuery(oData) {
+ ObjToQuery(oData) {
@@ -77 +81 @@
- static QueryToObj(sData) {
+ QueryToObj(sData) {
@@ -82 +86 @@
- obj := Map()
+ obj := {}
@@ -97 +101 @@
- throw Error("Not initialized.", -1)
+ throw Exception("Not initialized.", -1)
@@ -101 +105 @@
- throw Error("Invalid HTTP verb.", -1, sMethod)
+ throw Exception("Invalid HTTP verb.", -1, sMethod)
@@ -104 +108 @@
- throw Error("Empty URL.", -1)
+ throw Exception("Empty URL.", -1)
@@ -107 +111 @@
- oHeaders := Map()
+ oHeaders := {}
@@ -110 +114 @@
- oOptions := Map()
+ oOptions := {}
@@ -113,2 +117,2 @@
- multi := oOptions.Has("Multipart") ? !!oOptions["Multipart"] : false
- this._Post(&mBody, &oHeaders, multi)
+ multi := oOptions.HasKey("Multipart") ? !!oOptions.Multipart : false
+ this._Post(mBody, oHeaders, multi)
@@ -127,2 +131,2 @@
- if (oOptions.Has("Save")) {
- target := RegExReplace(oOptions["Save"], "^\h*\*\h*", "", &forceSave)
+ if (oOptions.HasKey("Save")) {
+ target := RegExReplace(oOptions.Save, "^\h*\*\h*", "", forceSave)
@@ -134 +138 @@
- out := WinHttpRequest._Response()
+ out := new WinHttpRequest._Response()
@@ -137 +141 @@
- out.Text := this._Text(oOptions.Has("Encoding") ? oOptions["Encoding"] : "")
+ out.Text := this._Text(oOptions.HasKey("Encoding") ? oOptions.Encoding : "")
@@ -146 +150 @@
- static _EncodeDecode(Text, bEncode, bComponent) {
+ _EncodeDecode(Text, bEncode, bComponent) {
@@ -148 +152 @@
- this._doc := ComObject("HTMLFile")
+ this._doc := ComObjCreate("HTMLFile")
@@ -158 +162 @@
- out := Map()
+ out := {}
@@ -161 +165 @@
- out.Set(pair*)
+ out[pair[1]] := pair[2]
@@ -167,2 +171,2 @@
- if (WinHttpRequest.MIME.HasProp(Extension)) {
- return WinHttpRequest.MIME.%Extension%
+ if (WinHttpRequest.MIME.HasKey(Extension)) {
+ return WinHttpRequest.MIME[Extension]
@@ -173 +177 @@
- _MultiPart(&Body) {
+ _MultiPart(ByRef Body) {
@@ -193 +197 @@
- str .= "Content-Disposition: form-data; name=`"" Field "`""
+ str .= "Content-Disposition: form-data; name=""" Field """"
@@ -202 +206 @@
- SplitPath(path, &filename, , &ext)
+ SplitPath path, filename, , ext
@@ -205 +209 @@
- str .= "Content-Disposition: form-data; name=`"" Field "`"; filename=`"" filename "`""
+ str .= "Content-Disposition: form-data; name=""" Field """; filename=""" filename """"
@@ -221 +225 @@
- throw Error("Couldn't open file for reading.", -1, Path)
+ throw Exception("Couldn't open file for reading.", -1, Path)
@@ -236 +240 @@
- _Post(&Body, &Headers, bMultipart) {
+ _Post(ByRef Body, ByRef Headers, bMultipart) {
@@ -243,2 +247,2 @@
- boundary := this._MultiPart(&Body)
- Headers["Content-Type"] := "multipart/form-data; boundary=`"" boundary "`""
+ boundary := this._MultiPart(Body)
+ Headers["Content-Type"] := "multipart/form-data; boundary=""" boundary """"
@@ -247 +251 @@
- if (!Headers.Has("Content-Type")) {
+ if (!Headers.HasKey("Content-Type")) {
@@ -276,7 +280 @@
- /* _H v2.0.2 adds Object.Prototype.Get() breaking
- GET verb's dynamic call, this is a workaround. */
- GET(Parameters*) {
- return this.Request("GET", Parameters*)
- }
-
- __Call(Method, Parameters) {
+ __Call(Method, Parameters*) {
@@ -292,3 +290,3 @@
- method := HasMethod(JSON, "parse") ? "parse" : "Load"
- oJson := ObjBindMethod(JSON, method, this.Text).Call()
- this.DefineProp("Json", { Value: oJson })
+ method := Json.HasKey("parse") ? "parse" : "Load"
+ oJson := ObjBindMethod(Json, method, this.Text).Call()
+ ObjRawSet(this, "Json", oJson)
@witt
Copy link

witt commented Feb 4, 2022

Amazing! Thank you!

@Metal-666
Copy link

Thank you for making this!

@htadashi
Copy link

htadashi commented Feb 5, 2023

Thanks for your awesome work! :)

I had some trouble decoding UTF8 responses in my script, so I had to change the _Text() function to parse from the response body instead of the response text (cf. this solution posted in r/AutoHotKey).

@anonymous1184
Copy link
Author

@htadashi Great it worked for you. I've been trying to get my hands in something that will return an error for the ._Text() method, so it throws an exception. The answer you linked and the code after the try do the same.

Can you share where you get issues, so I can fine-tune the class?

@htadashi
Copy link

Hi @anonymous1184, sorry for my late reply.

When I used WinHttpRequest without modifying the .Text() method (the diff can be seen in htadashi/GPT3-AHK@896feb8), the script didn't throw an exception. However, some accented characters (such as "É") in the response rendered as garbled text, though the original response had the correct accented characters.

@htadashi
Copy link

Here is an error report detailing the issue of the accented characters: https://www.reddit.com/r/AutoHotkey/comments/zrzjc2/comment/j28hxi0/

@anonymous1184
Copy link
Author

I'm aware of the issue, and I tried to prevent it, but seems like it needs another approach. However, I haven't come across a URL that returns the issue.

What I require is a URL that returns an error with my current implementation, so I can fix it. Even tho, in fact is quite simple: I just need to remove the try block and use the byte array from the response.

arr := this.whr.ResponseBody
pData := NumGet(ComObjValue(arr) + 8 + A_PtrSize)
response := StrGet(pData, arr.MaxIndex() + 1, "UTF-8")

But that's the point, I rather test than blindly do it. What's the URL that returns the data that required you to change the ._Text() method?

@htadashi
Copy link

Ah, I understood your point now. To return the faulty data, I made a POST to https://api.openai.com/v1/completions with the headers

Content-Type: application/json
Authorization: Bearer API_TOKEN

and the body

{
"prompt":"Write the polish alphabet: ",
"model":"text-davinci-003",
"max_tokens":200
}

API_TOKEN should be your OpenAI API token, but in case you do not want to create one for this, I made a server with Google Apps Script that should return the same response to this query. You just need to do a POST to https://script.google.com/macros/s/AKfycbyKYNdD2h1F5T9vdq5qEA3Po3tawn77fprCnLJUhwzT-lGQKPYewcMtxS6DX1aR8OM/exec .

@anonymous1184
Copy link
Author

@htadashi Thanks for setting up the Google Apps Script, that lead me to figure out that the issue is OpenAI and not the script. Google sends the response properly encoded, while OpenAI sends it as ASCII.

So there is no need for me to update the script, the problem lies within the server. If you wanted to address the issue, you only needed to pass the encoding (UTF-8 is all that's needed for basically everything).

Image

20230219050125

@htadashi
Copy link

htadashi commented Mar 3, 2023

Got it now! Thanks for the detailed explanation @anonymous1184 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment