AHKhttp - HTTP Server

Post your working scripts, libraries and tools
brutus_skywalker
Posts: 175
Joined: 24 Dec 2016, 13:16
Location: Antarctica

Re: AHKhttp - HTTP Server

20 May 2017, 23:50

badpost
Last edited by brutus_skywalker on 27 Sep 2018, 09:44, edited 1 time in total.
Outsourcing Clicks & Presses Since 2004.
brutus_skywalker
Posts: 175
Joined: 24 Dec 2016, 13:16
Location: Antarctica

Re: AHKhttp - HTTP Server

27 May 2017, 01:04

badpost
Last edited by brutus_skywalker on 27 Sep 2018, 09:44, edited 1 time in total.
Outsourcing Clicks & Presses Since 2004.
DanielToward13
Posts: 74
Joined: 18 May 2017, 10:56

Re: AHKhttp - HTTP Server

28 Jul 2017, 22:00

Thanks for sharing.
is it possible to access this HTTP Server using its IP address and port on the internet? is it secure?
Last edited by DanielToward13 on 28 Jul 2017, 22:02, edited 1 time in total.
User avatar
metacognition
Posts: 91
Joined: 22 Oct 2014, 05:57
Location: Alaska

Re: AHKhttp - HTTP Server

28 Jul 2017, 22:02

yes.
DanielToward13
Posts: 74
Joined: 18 May 2017, 10:56

Re: AHKhttp - HTTP Server

28 Jul 2017, 22:07

metacognition wrote:yes.
:thumbup: What are the steps to make it live on the internet? Only set the port in AHKsock and run the script?
User avatar
metacognition
Posts: 91
Joined: 22 Oct 2014, 05:57
Location: Alaska

Re: AHKhttp - HTTP Server

28 Jul 2017, 22:11

If you are using a Nat based router you would have to forward a port, on your router, to that specific port on your pc, using the local ip address, you are headed into deep water here my friend, much deeper than the scope of this forum/topic, I suggest googling about setting up a home http server and port forwarding.
User avatar
kczx3
Posts: 1224
Joined: 06 Oct 2015, 21:39

Re: AHKhttp - HTTP Server

25 Aug 2017, 11:33

Started playing around with this a bit and utilizing JS frameworks as I code in JS quite a bit now.

Requires Jxon.ahk as well in a lib folder. Please note, it does write json to the file system in the script's directory. There is likely a better way of handling the js and css.


EDIT: Updated the code some to work with pagination of the Github API and some additional CSS for appeal.

Code: Select all

#Persistent
#SingleInstance, force
SetBatchLines, -1

paths := {}
paths["/"] := Func("HelloWorld")
paths["/ajax"] := Func("handleAjax")
paths["/javascript"] := Func("javascript")
paths["/css"] := Func("css")
paths["404"] := Func("NotFound")
paths["/logo"] := Func("Logo")

server := new HttpServer()
server.LoadMimes(A_ScriptDir . "/mime.types")
server.SetPaths(paths)
server.Serve(8000)
return

Logo(ByRef req, ByRef res, ByRef server) {
    server.ServeFile(res, A_ScriptDir . "/logo.png")
    res.status := 200
}

NotFound(ByRef req, ByRef res) {
    res.SetBodyText("Page not found")
}

javascript(ByRef req, ByRef res) {
    js := getJS()
    res.SetBodyText(js)
    res.status := 200
}

css(ByRef req, ByRef res, server) {
    css := getCSS()
    res.headers["Content-Type"] := "text/css"
    res.SetBodyText(css)
    res.status := 200
}

handleAjax(ByRef req, ByRef res, server) {
    if (func := req.queries.func) {
        if (req.queries.params)
            params := StrSplit(req.queries.params, "|")
        retval := %func%(params ? params : [])
    }
    res.status := 200
    res.SetBodyText(retval)
}

write(params) {
    FileRead, repos, autohotkey_repositories.json
    FileDelete, autohotkey_repositories.json
    if (!repos)
        repos := {repos: []}
    else
        repos := Jxon_Load(repos)
    for index, item in params
        repos.repos.push(item)
    FileAppend, % Jxon_Dump(repos), autohotkey_repositories.json
    return Jxon_Dump(repos)
    
}

HelloWorld(ByRef req, ByRef res) {
    html := getHTML()
    res.SetBodyText(html)
    res.status := 200
}

#include, %A_ScriptDir%\AHKhttp.ahk
#include <AHKsock>
#Include <Jxon>

getHTML() {
    html := 
    ( LTrim Join
    "<!doctype html>
    <html lang='en'>
        <head>
            <meta charset='utf-8'>
            <title>Mithril and AHK, Wow!</title>
            <script src='//unpkg.com/mithril/mithril.js'></script>
            <link href='/css' rel='stylesheet' />
        </head>
        <body></body>
        <script src='/javascript'></script>
    </html>"
    )
    return html
}

getJS() {
    JS =
    ( LTrim Join`n
    var root = document.body;
    
    var handleClick = function(text) {
        window.open(text);
    };
    
    var getRepos = function(page) {
        m.request({
            method: "GET",
            url: "https://api.github.com/search/repositories?q=language:AutoHotkey&page=" + page
        }).then(function(data) {
            reposList.list = data.items;
        });
    };
    
    var reposList = {
        list: [],
        oninit: function(vnode) {
            vnode.state.page = vnode.attrs.page;
            m.request({
                method: "GET",
                url: "https://api.github.com/search/repositories?q=language:AutoHotkey&page=" + vnode.state.page
            }).then(function(data) {
                reposList.list = data.items;
            });
        },
        view: function(vnode) {
            return m("ul.list", reposList.list.map(function(repo) {
                    return m("li.list-item", 
                        m("span", {
                            onclick: function() {handleClick(repo.html_url)},
                            title: repo.description
                        }, repo.full_name)
                    `)
                })
            `)
        }
    };
    
    var navButtons = {
        view: function (vnode) {
            return [
                m("button", {onclick: function() {if (nav.currPage === 1) return; nav.currPage--; getRepos(nav.currPage)}}, "Previous"),
                m("span.currPage", vnode.attrs.page),
                m("button", {onclick: function() {nav.currPage++, getRepos(nav.currPage)}}, "Next")
            ];
        }
    };
    
    var nav = {
        currPage: 1,
        view: function(vnode) {
            return m(".container", [
                m(".nav", m(navButtons, {page: vnode.state.currPage})),
                m(reposList, {page: vnode.state.currPage})
            ]);
        }
    };
    
    m.mount(root, nav);
    )
    return js
}

getCSS() {
    CSS =
    ( LTrim Join`n
    .list {list-style:none;margin:0 0 10px;padding:0;display:flex;align-items:center;align-content:flex-end;justify-content:center;flex-direction:row;flex-wrap:wrap;flex-flow:wrap;}
    .list-item {background:#fafafa;border:1px solid #ddd;color:#333;display:block;margin:0 0 1px;padding:8px 15px;text-decoration:none;width: 20`%}
    .list-item > span:hover {text-decoration:underline;cursor: pointer;}
    .nav {padding:5px;margin-bottom:20px;border: 1px solid #ddd;border-radius:5px;display: flex;align-items:center;justify-content:center;}
    .currPage {margin: 1`%;}
    )
    return CSS
}
wywywywy
Posts: 1
Joined: 12 Jan 2019, 21:15

Re: AHKhttp - HTTP Server

13 Jan 2019, 05:55

Hi,

There's a memory leak somewhere, even when using the example scripts.

After receiving some amount of requests (1000? 2000?) it will crash.

I am not sure if the problem is in AhkHttp.ahk or AhkSock.ahk.

I guess maybe an object or array is not cleared somewhere? I have had a look but couldn't find anything.

Has anyone any idea please?

Thanks.
magusneo
Posts: 37
Joined: 30 Sep 2013, 06:34

Re: AHKhttp - HTTP Server

22 May 2019, 06:02

wywywywy wrote:
13 Jan 2019, 05:55
Hi,

There's a memory leak somewhere, even when using the example scripts.

After receiving some amount of requests (1000? 2000?) it will crash.

I am not sure if the problem is in AhkHttp.ahk or AhkSock.ahk.

I guess maybe an object or array is not cleared somewhere? I have had a look but couldn't find anything.

Has anyone any idea please?

Thanks.

There is memory leak.
Try this

Code: Select all

class Uri
{
    Decode(str) {
        Loop
            If RegExMatch(str, "i)(?<=%)[\da-f]{1,2}", hex)
                StringReplace, str, str, `%%hex%, % Chr("0x" . hex), All
            Else Break
        Return, str
    }

    Encode(str) {
        f = %A_FormatInteger%
        SetFormat, Integer, Hex
        If RegExMatch(str, "^\w+:/{0,2}", pr)
            StringTrimLeft, str, str, StrLen(pr)
        StringReplace, str, str, `%, `%25, All
        Loop
            If RegExMatch(str, "i)[^\w\.~%]", char)
                StringReplace, str, str, %char%, % "%" . Asc(char), All
            Else Break
        SetFormat, Integer, %f%
        Return, pr . str
    }
}

class HttpServer
{
    static servers := {}

    LoadMimes(file) {
        if (!FileExist(file))
            return false

        FileRead, data, % file
        types := StrSplit(data, "`n")
        this.mimes := {}
        for i, data in types {
            info := StrSplit(data, " ")
            type := info.Remove(1)
            ; Seperates type of content and file types
            info := StrSplit(LTrim(SubStr(data, StrLen(type) + 1)), " ")

            for i, ext in info {
                this.mimes[ext] := type
            }
        }
        return true
    }

    GetMimeType(file) {
        default := "text/plain"
        if (!this.mimes)
            return default

        SplitPath, file,,, ext
        type := this.mimes[ext]
        if (!type)
            return default
        return type
    }

    ServeFile(ByRef response, file) {
        f := FileOpen(file, "r")
        length := f.RawRead(data, f.Length)
        f.Close()

        response.SetBody(data, length)
        res.headers["Content-Type"] := this.GetMimeType(file)
    }

    SetPaths(paths) {
        this.paths := paths
    }

    Handle(ByRef request) {
        response := new HttpResponse()
        if (!this.paths[request.path]) {
            func := this.paths["404"]
            response.status := 404
            if (func)
                func.(request, response, this)
            return response
        } else {
            this.paths[request.path].(request, response, this)
        }
        return response
    }

    Serve(port) {
        this.port := port
        HttpServer.servers[port] := this

        AHKsock_Listen(port, "HttpHandler")
    }
}

HttpHandler(sEvent, iSocket = 0, sName = 0, sAddr = 0, sPort = 0, ByRef bData = 0, bDataLength = 0) {
    static sockets := {}

    if (!sockets[iSocket]) {
        sockets[iSocket] := new Socket(iSocket)
        AHKsock_SockOpt(iSocket, "SO_KEEPALIVE", true)
    }
    socket := sockets[iSocket]

    if (sEvent == "DISCONNECTED") {
        socket.request := false
        sockets[iSocket] := false
    } else if (sEvent == "SEND") {
        if (socket.TrySend()) {
            socket.Close()
        }

    } else if (sEvent == "RECEIVED") {
        server := HttpServer.servers[sPort]

        text := StrGet(&bData, "UTF-8")

        ; New request or old?
        if (socket.request) {
            ; Get data and append it to the existing request body
            socket.request.bytesLeft -= StrLen(text)
            socket.request.body := socket.request.body . text
            request := socket.request
        } else {
            ; Parse new request
            request := new HttpRequest(text)

            length := request.headers["Content-Length"]
            request.bytesLeft := length + 0

            if (request.body) {
                request.bytesLeft -= StrLen(request.body)
            }
        }

        if (request.bytesLeft <= 0) {
            request.done := true
        } else {
            socket.request := request
        }

        if (request.done || request.IsMultipart()) {
            response := server.Handle(request)
            if (response.status) {
                socket.SetData(response.Generate())
            }
        }
        if (socket.TrySend()) {
            if (!request.IsMultipart() || request.done) {
                socket.Close()
            }
        }    
        
    }
}

class HttpRequest
{
    __New(data = "") {
        if (data)
            this.Parse(data)
    }

    GetPathInfo(top) {
        results := []
        while (pos := InStr(top, " ")) {
            results.Insert(SubStr(top, 1, pos - 1))
            top := SubStr(top, pos + 1)
        }
        this.method := results[1]
        this.path := Uri.Decode(results[2])
        this.protocol := top
    }

    GetQuery() {
        pos := InStr(this.path, "?")
        query := StrSplit(SubStr(this.path, pos + 1), "&")
        if (pos)
            this.path := SubStr(this.path, 1, pos - 1)

        this.queries := {}
        for i, value in query {
            pos := InStr(value, "=")
            key := SubStr(value, 1, pos - 1)
            val := SubStr(value, pos + 1)
            this.queries[key] := val
        }
    }

    Parse(data) {
        this.raw := data
        data := StrSplit(data, "`n`r")
        headers := StrSplit(data[1], "`n")
        this.body := LTrim(data[2], "`n")

        this.GetPathInfo(headers.Remove(1))
        this.GetQuery()
        this.headers := {}

        for i, line in headers {
            pos := InStr(line, ":")
            key := SubStr(line, 1, pos - 1)
            val := Trim(SubStr(line, pos + 1), "`n`r ")

            this.headers[key] := val
        }
    }

    IsMultipart() {
        length := this.headers["Content-Length"]
        expect := this.headers["Expect"]

        if (expect = "100-continue" && length > 0)
            return true
        return false
    }
}

class HttpResponse
{
    __New() {
        this.headers := {}
        this.status := 0
        this.protocol := "HTTP/1.1"
        this.buffer:=""
        this.SetBodyText("")
    }
	__Delete(){
		this.buffer:=""
	}
    
    Generate() {
        FormatTime, date,, ddd, d MMM yyyy HH:mm:ss
        this.headers["Date"] := date

        headers := this.protocol . " " . this.status . "`r`n"
        for key, value in this.headers {
            headers := headers . key . ": " . value . "`r`n"
        }
        headers := headers . "`r`n"
        length := this.headers["Content-Length"]

        this.buffer := new Buffer((StrLen(headers) * 2) + length)
        this.buffer.WriteStr(headers)

        this.buffer.Append(this.body)
        this.buffer.Done()

        return this.buffer
    }

    SetBody(ByRef body, length) {
        this.body := new Buffer(length)
        this.body.Write(&body, length)
        this.headers["Content-Length"] := length
    }

    SetBodyText(text) {
        this.body := Buffer.FromString(text)
        this.headers["Content-Length"] := this.body.length
    }


}

class Socket
{
    __New(socket) {
        this.socket := socket
    }

    Close(timeout = 5000) {
        AHKsock_Close(this.socket, timeout)
    }

    SetData(data) {
        this.data := data
    }

    TrySend() {
        if (!this.data || this.data == "")
            return false

        p := this.data.GetPointer()
        length := this.data.length

        this.dataSent := 0
        loop {
            if ((i := AHKsock_Send(this.socket, p, length - this.dataSent)) < 0) {
                if (i == -2) {
                    return
                } else {
                    ; Failed to send
                    return
                }
            }

            if (i < length - this.dataSent) {
                this.dataSent += i
            } else {
                break
            }
        }
        this.dataSent := 0
        this.data := ""

        return true
    }
}

class Buffer
{
    __New(len) {
        this.SetCapacity("buffer", len)
        this.length := 0
    }
	__Delete(){
		this.buffer:=""
	}

    FromString(str, encoding = "UTF-8") {
        length := Buffer.GetStrSize(str, encoding)
        this.buffer := new Buffer(length)
        this.buffer.WriteStr(str)
        return this.buffer
    }

    GetStrSize(str, encoding = "UTF-8") {
        encodingSize := ((encoding="utf-16" || encoding="cp1200") ? 2 : 1)
        ; length of string, minus null char
        return StrPut(str, encoding) * encodingSize - encodingSize
    }

    WriteStr(str, encoding = "UTF-8") {
        length := this.GetStrSize(str, encoding)
        VarSetCapacity(text, length)
        StrPut(str, &text, encoding)

        this.Write(&text, length)
        return length
    }

    ; data is a pointer to the data
    Write(data, length) {
        p := this.GetPointer()
        DllCall("RtlMoveMemory", "uint", p + this.length, "uint", data, "uint", length)
        this.length += length
    }

    Append(ByRef buffer) {
        destP := this.GetPointer()
        sourceP := buffer.GetPointer()

        DllCall("RtlMoveMemory", "uint", destP + this.length, "uint", sourceP, "uint", buffer.length)
        this.length += buffer.length
    }

    GetPointer() {
        return this.GetAddress("buffer")
    }

    Done() {
        this.SetCapacity("buffer", this.length)
    }
}

User avatar
nnnik
Posts: 4480
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: AHKhttp - HTTP Server

12 Jun 2019, 09:05

Would you mind providing a new example code?
Recommends AHK Studio
magusneo
Posts: 37
Joined: 30 Sep 2013, 06:34

Re: AHKhttp - HTTP Server

28 Jun 2019, 20:30

nnnik wrote:
12 Jun 2019, 09:05
Would you mind providing a new example code?
It's same to the first topic.Just fix memory leak.

Code: Select all

#Persistent
#SingleInstance, force
SetBatchLines, -1

paths := {}
paths["/"] := Func("HelloWorld")
paths["404"] := Func("NotFound")
paths["/logo"] := Func("Logo")

server := new HttpServer()
server.LoadMimes(A_ScriptDir . "/mime.types")
server.SetPaths(paths)
server.Serve(8000)
return

Logo(ByRef req, ByRef res, ByRef server) {
    server.ServeFile(res, A_ScriptDir . "/logo.png")
    res.status := 200
}

NotFound(ByRef req, ByRef res) {
    res.SetBodyText("Page not found")
}

HelloWorld(ByRef req, ByRef res) {
    res.SetBodyText("Hello World")
    res.status := 200
}


#include,AHKhttp.ahk
#include,ahksock-unicode.ahk
BuggyB
Posts: 6
Joined: 25 Mar 2019, 17:59

Re: AHKhttp - HTTP Server

11 Dec 2019, 14:32

#include,ahksock-unicode.ahk
what is it? Is there a special unicode version? I cannot find another reference to this on this forum
User avatar
CrashKoeck
Posts: 2
Joined: 08 Nov 2020, 17:17
GitHub: CrashKoeck

Re: AHKhttp - HTTP Server

08 Nov 2020, 18:28

Amazing work @Skittlez and great patch @magusneo! With the patch I'm able to get up to around 10K calls before running into an issue. But, there is definitely an issue still.

At about 3,000 calls during stress testing I do notice everything tends to slow down quite a bit and at around 10,000 calls I get the error Function recursion limit exceeded (attached pic)

I have tried to figure out exactly what is happening with this function but haven't had any luck. I have also tried to basically "reset" the HTTP server to clear everything at around 2,500 calls to give quite a bit of headroom but that hasn't worked either.

If anyone has any idea on how to extend this past 10K calls (looking like 50K is what I'd need to aim for) OR how to basically reset this without exiting the program, that would be awesome.
Attachments
Screenshot 2020-11-07 223403.png
Function recursion limit exceeded
Screenshot 2020-11-07 223403.png (13.1 KiB) Viewed 365 times
magusneo
Posts: 37
Joined: 30 Sep 2013, 06:34

Re: AHKhttp - HTTP Server

16 Nov 2020, 07:21

CrashKoeck wrote:
08 Nov 2020, 18:28


At about 3,000 calls during stress testing I do notice everything tends to slow down quite a bit and at around 10,000 calls I get the error Function recursion limit exceeded (attached pic)
What's your testing script?
User avatar
CrashKoeck
Posts: 2
Joined: 08 Nov 2020, 17:17
GitHub: CrashKoeck

Re: AHKhttp - HTTP Server

19 Nov 2020, 06:25

magusneo wrote:
16 Nov 2020, 07:21
CrashKoeck wrote:
08 Nov 2020, 18:28


At about 3,000 calls during stress testing I do notice everything tends to slow down quite a bit and at around 10,000 calls I get the error Function recursion limit exceeded (attached pic)
What's your testing script?
Something I threw together quick that allows me to adjust the parameters for each test. I hit the ~10K limit whether I let it just run wild with no delays built in or add a couple second delay between each call.

Code: Select all

#NoEnv
#SingleInstance, Off

global theURL
global targetcalls := 50000
global callCount := 0
global Prog := 0
global CallTimeAvgTally := 0 
global CallTimeAvg := 0 
global ServerSleep := 0
global SleepCount := 0
global SleepRestTrigger := 250
global ServerRestSeconds := 30
global ServerRestms := ServerRestSeconds * 1000

Progress, x100 y100 w750 m cbe6820a cw222222 cte6820a c01, Waiting for user input...`nWaiting for user input...`nWaiting for user input...`nWaiting for user input...`nWaiting for user input...`nWaiting for user input...`nWaiting for user input...`nWaiting for user input...`nWaiting for user input...`nWaiting for user input..., Calls: 0/0, Stress Tester (Press Esc to quit)
Progress, 100

InputBox, targetcalls , Server Stress Tester, Target successful calls,,300,125,,, Locale,, %targetcalls%
if ErrorLevel
    ExitApp

InputBox, theURL , Server Stress Tester, Select URL to stress test,,300,125,,, Locale,, 
if ErrorLevel
    ExitApp

InputBox, ServerSleep , Server Stress Tester, Delay between calls (ms),,300,125,,, Locale,, %ServerSleep%
if ErrorLevel
    ExitApp

SleepRestTrigger += ServerSleep
InputBox, SleepRestTrigger , Server Stress Tester, Sleep threshold (ms),,300,125,,, Locale,, %SleepRestTrigger%
if ErrorLevel
    ExitApp
	
InputBox, ServerRestSeconds , Server Stress Tester, Sleep duration (s),,300,125,,, Locale,, %ServerRestSeconds%
if ErrorLevel
    ExitApp

Progress, 0, Connecting to server...`nConnecting to server...`nConnecting to server...`nConnecting to server...`nConnecting to server...`nConnecting to server...`nConnecting to server...`nConnecting to server...`nConnecting to server...`nConnecting to server..., Calls: 0/%targetcalls%, Stress Tester (Press Esc to quit)

global targetcallsadjusted := 100/targetcalls

Stress()

Return

Stress(){
	TestStart := A_TickCount
	Try {
		tester := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		CallTime := A_TickCount
		Loop
		{
			callCount := A_Index
			tester.Open("GET",theURL)
			tester.Send()
			output := tester.ResponseText
			outputstatus := tester.Status
			Prog += targetcallsadjusted
			
			x := A_TickCount - CallTime - ServerSleep
			
			if(x > SleepRestTrigger and callCount > 1){
				If(SleepCount > 10){
					MsgBox, 4096, Server Rest Exceeded, Server was allowed to rest for %ServerRestSeconds% seconds 10 times and still exceeded %SleepRestTrigger%ms.`nURL: %theURL%`nReturned Data: %output%`nStatus: %outputstatus%`nTarget Calls: %targetcalls%`nCurrent Call Time: %x%ms`nAvg Call Time: %CallTimeAvg%`nCall Delay: %ServerSleep%`nSleep Cycles: %SleepCount%`nSleep Threshold: %SleepRestTrigger%ms`nSleep Duration: %ServerRestSeconds%s, Calls: %callCount%
					ExitApp
				} else {
					Progress, %Prog%, URL: %theURL%`nReturned Data: %output%`nStatus: %outputstatus%`nCurrent Call Time: %x%ms`nAvg Call Time: %CallTimeAvg%`nCall Delay: %ServerSleep%`nSleep Cycles: Sleeping...`nSleep Threshold: %SleepRestTrigger%ms`nSleep Duration: %ServerRestSeconds%s, Calls: %callCount%/%targetcalls%, Stress Tester (Press Esc to quit)
					Sleep, % ServerRestms
					SleepCount++
				}
			}
			
			CallTime := A_TickCount
			CallTimeAvgTally += x
			if(A_Index < 100){
				CallTimeAvg := "Calculating..."
			} else {
				CallTimeAvg := Round(CallTimeAvgTally/callCount) . "ms"
			}

			Progress, %Prog%, URL: %theURL%`nReturned Data: %output%`nStatus: %outputstatus%`nCurrent Call Time: %x%ms`nAvg Call Time: %CallTimeAvg%`nCall Delay: %ServerSleep%`nSleep Cycles: %SleepCount%`nSleep Threshold: %SleepRestTrigger%ms`nSleep Duration: %ServerRestSeconds%s, Calls: %callCount%/%targetcalls%, Stress Tester (Press Esc to quit)
			
			if(Prog >= 100){
				TestDuration := (A_TickCount - TestStart) / 1000
				MsgBox,4096,, Sucessfully made %callCount% calls in %TestDuration% seconds.
				ExitApp
			}
			
			Sleep, % ServerSleep
		}
	} catch e {
		MsgBox,4096, ERROR, % callCount . " calls were made before there was an error`n`nWhat: " . e.what . "`n-----`nFile: " . e.file . "`n-----`nLine: " . e.line . "`n-----`nMessage: " . e.message . "`n-----`nExtra: " . e.extra . "`n-----`nRAW: " . e
		MsgBox,4096, ERROR, URL: %theURL%`nReturned Data: %output%`nStatus: %outputstatus%`nTarget Calls: %targetcalls%`nCalls: %callCount%`nCurrent Call Time: %x%ms`nAvg Call Time: %CallTimeAvg%`nCall Delay: %ServerSleep%`nSleep Cycles: %SleepCount%`nSleep Threshold: %SleepRestTrigger%ms`nSleep Duration: %ServerRestSeconds%s
		ExitApp
	}
}

Esc::
	ExitApp
return

Return to “Scripts and Functions”

Who is online

Users browsing this forum: CyberKlabauter, Spawnova and 21 guests