AHKhttp - HTTP Server

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
bichlepa
Posts: 183
Joined: 15 Aug 2014, 06:44
Location: Germany
Contact:

Re: AHKhttp - HTTP Server

03 Sep 2015, 05:18

This is an amazing piece of code! :thumbup:
I'd like to use the HTTP Server in AutoHotFlow to implement a trigger "HTTP Request".
In the examples, when a reqest has been received, the called function can set some data as a reply, and as soon the function returns, the response will be immediately sent. But this won't work in AutoHotFlow. I have some requiremenets:
  • I want to specify a function which will be called as soon as a request has been received (It would trigger a flow and start a new instance). When the function returns, the response schould not be sent yet. Instead I want to set the response data later (as soon as action "Set HTTP Response" is executed), and then call a function which sends the reply (As soon as the current instance ends).
  • I want to be able to ignore a request and not to send a reply (If no action "Set HTTP Resopnse" was executed and the instance ends).
Is this possible? Does the HTTP Server code need to be modified?
Scripting is too complicated? Try AutoHotFlow, the graphical automation tool! Written in AutoHotkey.
phaleth
Posts: 38
Joined: 13 Apr 2015, 03:49

Re: AHKhttp - HTTP Server

11 Oct 2015, 12:12

Thanks for such a great lib!

Can anyone please post an example with HTTP POST method using this lib?

EDIT: Got it to work. The following is an example of a login page including the other example i gathered from this thread.

Code: Select all

#Persistent
#SingleInstance, Force
SetBatchLines, -1

global SiteContents
global UserLogin
global UserPass
global StoredLogin
global StoredPass
global StoredReqBody
global val1
global val2

SiteContents =
(LTrim
	<!DOCTYPE html>
	<html>
		<head>
			<meta charset="UTF-8">
			<meta name="viewport" content="width=device-width">
			<title>http-Server</title>
			<link href='http://fonts.googleapis.com/css?family=Ubuntu' rel='stylesheet' type='text/css'>
			<style>
				html {
					font-family: Ubuntu;
				}
				body {
					margin:25px;
					margin-top:5px;
				}
				h4 {
					font-size: 20px;
					color:green;
				}
				h5 {
					font-size: 16px;
					color:teal;
				}
			</style>
			<script type="text/javascript">
				[ScriptTagContents]
			</script>
		</head>
		<body>
			[BodyTagContents]
		</body>
	</html>
)
StoredLogin = admin
StoredPass = 1234

paths := {}
paths["404"] := Func("NotFound")
paths["/"] := Func("LoginPage")
paths["/page"] := Func("MainPage")
paths["/reply"] := Func("ResponsePage")

Server := new HttpServer()
Server.LoadMimes(A_ScriptDir . "\mime.types")
Server.SetPaths(paths)
Server.Serve(8000)
StartUpURL := "http://localhost:8000/"
Run, %StartUpURL%
Return

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

LoginPage(ByRef req, ByRef res)
{
	ContentsOfScript = <!--...-->
	ContentsToDisplay =
	(LTrim
		<div class="container" align="center">
			<section id="content">
				<form action="/page" method="post">
					<h4>Log in</h4>
					<div id="username">
						<input type="text" placeholder="Username" required="" name="username" />
					</div>
					<div id="password">
						<input type="password" placeholder="Password" required="" name="password" />
					</div>
					<br />
					<div id="button1">
						<input type="submit" value="Log in" />
					</div>
				</form>
				<div class="button">
				</div>
			</section>
		</div>
	)
	StringReplace, ServeTemp, SiteContents, [ScriptTagContents], %ContentsOfScript%, All
	StringReplace, Serve, ServeTemp, [BodyTagContents], %ContentsToDisplay%, All
	res.SetBodyText(Serve)
	res.status := 200
}

MainPage(ByRef req, ByRef res)
{
	UserLogin = ;Wipe all contents
	UserPass = ;Wipe all contents
	HttpReqBodyArray := "" ;Release the object
	HttpReqBodyArray := Object()
	For each, Pair in StrSplit(req.body,"&")
	{
		Part := StrSplit(Pair, "=")
		HttpReqBodyArray.Push([Part[1], Part[2]])
	}
	UserLogin := HttpReqBodyArray[1,2]
	UserPass := HttpReqBodyArray[2,2]
	If (UserLogin != StoredLogin) || (UserPass != StoredPass)
	{
		ContentsToDisplay = <p>Wrong username or password</p>
	}
	Else
	{
		StoredReqBody := req.body
		IniRead, val1, %A_ScriptDir%\output.ini, Response, param1, "5.5"
		IniRead, val2, %A_ScriptDir%\output.ini, Response, param2, "3.14"
		ContentsOfScript =
		(LTrim
			function sendIt() {
				var prt1 = document.getElementById("firstid").value;
				var prt2 = document.getElementById("secondid").value;
				if (prt1 == "" || prt2 == "") {
					alert("Fill in some values into both fields.");
					return false;
				}
				else if ((isNaN(prt1)) || (isNaN(prt2))) {
					alert("Values in form fields have to be numeric.");
					return false;
				}
				else if (prt1.length > 8 || prt2.length > 8) {
					alert("Too many characters in the entry.");
					return false;
				}
				else {
					return true;
				}
			}
		)
		ContentsToDisplay =
		(LTrim
			<h4>Set any number to each form field</h4>
			<ul>
				<li>Use dot "." as a delimiter</li><br />
				<form action="/reply" method="post">
					<input type=text name=param1 value=%val1% id="firstid" />
					+
					<input type=text name=param2 value=%val2% id="secondid" />
					<input type=submit onClick="return sendIt();" />

				</form>
			</ul>
		)
	}
	StringReplace, ServeTemp, SiteContents, [ScriptTagContents], %ContentsOfScript%, All
	StringReplace, Serve, ServeTemp, [BodyTagContents], %ContentsToDisplay%, All
	res.SetBodyText(Serve)
	res.status := 200
}

ResponsePage(ByRef req, ByRef res)
{
	UserLogin = ;Wipe all contents
	UserPass = ;Wipe all contents
	HttpReqBodyArray := "" ;Release the object
	ReqBodyWithAuth := StoredReqBody . "&" . req.body
	HttpReqBodyArray := Object()
	For each, Pair in StrSplit(ReqBodyWithAuth,"&")
	{
		Part := StrSplit(Pair, "=")
		HttpReqBodyArray.Push([Part[1], Part[2]])
	}
	UserLogin := HttpReqBodyArray[1,2]
	UserPass := HttpReqBodyArray[2,2]
	If (UserLogin != StoredLogin) || (UserPass != StoredPass) || (HttpReqBodyArray[3,2] = "") || (HttpReqBodyArray[4,2] = "")
	{
		ContentsToDisplay = <p>Restricted</p>
	}
	Else
	{
		val1 := HttpReqBodyArray[3,2]
		val2 := HttpReqBodyArray[4,2]
		answer := val1 + val2
		ContentsOfScript = <!--...-->
		ContentsToDisplay =
		(LTrim
			<h5>%val1% + %val2% = <b>%answer%</b></h5>
			<p>Data written to <b>output.ini</b> file</p>
		)
		IniWrite, %val1%, %A_ScriptDir%\output.ini, Response, param1
		IniWrite, %val2%, %A_ScriptDir%\output.ini, Response, param2
		IniWrite, %answer%, %A_ScriptDir%\output.ini, Response, answer
	}
	StringReplace, ServeTemp, SiteContents, [ScriptTagContents], %ContentsOfScript%, All
	StringReplace, Serve, ServeTemp, [BodyTagContents], %ContentsToDisplay%, All
	res.SetBodyText(Serve)
	res.status := 200
}

#Include, %A_ScriptDir%\AHKhttp.ahk ; https://github.com/Skiouros/AHKhttp/blob/master/AHKhttp.ahk
#Include, %A_ScriptDir%\AHKsock.ahk ; https://github.com/jleb/AHKsock/blob/master/AHKsock.ahk
User avatar
jmone
Posts: 36
Joined: 30 Nov 2015, 20:43

Re: AHKhttp - HTTP Server

03 Sep 2016, 02:23

Great Script! I've got the basics working for a WebPage to control TuneBlade (an airplay controller for Windows) that can:
- Read a Bunch of Values from a Web Services call from the TuneBlade API
- Display these values as Buttons in the Web Page

Q: How do I evaluate what Button on the Web Page is then pressed in function "TB(ByRef req, ByRef res)" ?

Thanks
Nathan

Code: Select all

#Persistent
#SingleInstance, force
SetBatchLines, -1
global TB_WS
global TB_Call
global TB_Body
global TB_Open
global Result
TB_WS = 127.0.0.1:51497
global SiteContents
global StoredReqBody
global val1
global val2

SiteContents =
(LTrim
	<!DOCTYPE html>
	<html>
		<head>
			<meta charset="UTF-8">
			<meta name="viewport" content="width=device-width">
			<title>http-Server</title>
			<link href='http://fonts.googleapis.com/css?family=Ubuntu' rel='stylesheet' type='text/css'>
			<style>
				html {
					font-family: Ubuntu;
				}
				body {
					margin:25px;
					margin-top:5px;
				}
				h4 {
					font-size: 20px;
					color:green;
				}
				h5 {
					font-size: 16px;
					color:teal;
				}
			</style>
			<script type="text/javascript">
				[ScriptTagContents]
			</script>
		</head>
		<body>
			[BodyTagContents]
		</body>
	</html>
)

------------------

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

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

----------------- Functions ---------------------
Logo(ByRef req, ByRef res, ByRef server) {
    server.ServeFile(res, A_ScriptDir . "/mcaxiom.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
}

TB(ByRef req, ByRef res) {
    TB_Call = http://%TB_WS%/devices
	TB_Body := ""
	TB_Open := "GET"
	GoSub, TB_API
	
	Loop, parse, Result, `}
	{
		  RegExMatch(A_LoopField,"(?<=ID"":"")(.*)(?="",""Name)", TB_ID)
		  RegExMatch(A_LoopField,"(?<=Name"":"")(.*)(?="",""Volume)", TB_Name)
		  RegExMatch(A_LoopField,"(?<=Status"":"")(.*)(?="",""SubState)", TB_Status)
		  TB_Device_Button = %TB_Device_Button%<div id="button%A_Index%">`n<input type="submit" value="%TB_ID% is %TB_Status%" />`n</div>`n
	}

	ContentsOfScript = <!--...-->
	ContentsToDisplay =
	(LTrim
		<div class="container" align="center">
			<section id="content">
				<form action="/reply" method="post">
					<h4>Device Status - Press Button to Toggle</h4>
					<br />
					%TB_Device_Button%					
				</form>
				<div class="button">
				</div>
			</section>
		</div>
	)
	StringReplace, ServeTemp, SiteContents, [ScriptTagContents], %ContentsOfScript%, All
	StringReplace, Serve, ServeTemp, [BodyTagContents], %ContentsToDisplay%, All
	res.SetBodyText(Serve)
	res.status := 200
}

;--------------ChapterDB API Call-------------------------
TB_API:

  WinHTTP := ComObjCreate("WinHTTP.WinHttpRequest.5.1")
  ComObjError(false)
  WinHTTP.Open(TB_Open, TB_Call)
  WinHTTP.SetRequestHeader("Content-type", "application/json")
  Body = %TB_Body%
  WinHTTP.Send(Body)
  Result := WinHTTP.ResponseText
  Status := WinHTTP.Status
Return

;--------------Includes --------------------------------------
#include, %A_ScriptDir%\AHKhttp.ahk
#include  %A_ScriptDir%\AHKsock.ahk
User avatar
metacognition
Posts: 117
Joined: 22 Oct 2014, 05:57
Location: Alaska
Contact:

Re: AHKhttp - HTTP Server

06 Dec 2016, 14:17

Is there an easy way to limit the number of connections? A max connection setting?

Any plans to handle uploads?
Skittlez
Posts: 11
Joined: 13 Mar 2014, 12:16

Re: AHKhttp - HTTP Server

06 Dec 2016, 21:08

I haven't had any time to touch this at all, so no plans of now for file uploading. As for limiting connections it shouldn't be hard for you to implement.
User avatar
metacognition
Posts: 117
Joined: 22 Oct 2014, 05:57
Location: Alaska
Contact:

Re: AHKhttp - HTTP Server

06 Dec 2016, 21:12

Thanks,it's a pretty brilliant piece. I have never used AHKSock, I was learning Python for the very type of function this script, AHKHttp, could fulfill. So Python will be put on hold... :)

Thanks
User avatar
metacognition
Posts: 117
Joined: 22 Oct 2014, 05:57
Location: Alaska
Contact:

Re: AHKhttp - HTTP Server

10 Dec 2016, 10:05

Rewrote ServeFile function to allow for resuming partial downloads. Use the below to replace ServeFile Function in AHKHttp.ahk

Code: Select all

ServeFile(ByRef request, ByRef response, file) { ; metacognition: added ByRef request to check for Range header.
    response.headers["Accept-Ranges"] := "bytes" ; metacognigion: let the client know you accept ranges.
    f := FileOpen(file, "r")
    fileseek := request.headers["Range"] ; get the seek, start byte
    IfInString, fileseek, bytes=
    {
        StringReplace, fileseek, fileseek, bytes=,
        StringReplace, fileseek, fileseek, -,
        
        therange := request.headers["Range"]
        
        f.Seek(fileseek)
        newlength := f.Length - fileseek
                
        length := f.RawRead(data, newlength) 
        f.Close()
        
        response.SetBody(data, length)
        
        response.headers["Content-Type"] := this.GetMimeType(file)  ; metacognition: fixed from "res.headers" to "response.headers", now works properly.
        response.headers["Content-Range"] := "bytes " . fileseek . "-" . (fileseek + newlength - 1) . "/" . (fileseek + newlength) ; content-range response, https://tools.ietf.org/html/rfc2616#section-14.16
        response.status := 206 ; metacognition: 206=partial content, ServeFile must set the status because the caller can't know if this is going to be partial or full. so don't set status after calling servfile.
    }
    else
    {
        length := f.RawRead(data, f.Length)
        f.Close()
        
        response.SetBody(data, length)
        response.headers["Content-Type"] := this.GetMimeType(file) ; metacognition: fixed from "res.headers" to "response.headers", now works properly.
        response.status := 200 ; metacognition: 200=OK, ServeFile must set the status because the caller can't know if this is going to be partial or full. so don't set status after calling servfile.
    }
    
}
You will then setup a path like this:

Code: Select all

paths["/file"] := Func("FileDownload")
and then a function for the path like this:

Code: Select all

FileDownload(ByRef request, ByRef response, ByRef server) {

    server.ServeFile(request, response, A_ScriptDir . "\filename.ext")
    
}
Use a big file, 50mb+, then try downloading over a slow connection, pause and resume away....
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: AHKhttp - HTTP Server

10 Dec 2016, 12:20

nice

User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: AHKhttp - HTTP Server

10 Dec 2016, 15:52

Nice one :+1:
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
User avatar
Klark92
Posts: 161
Joined: 18 Jan 2015, 19:33

Re: AHKhttp - HTTP Server

28 Dec 2016, 15:49

Dont you have ANSI version ? :/
Smart Kombo 1.0 | One of the best Knight Online's key combo program...
User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: AHKhttp - HTTP Server

28 Dec 2016, 21:16

but the web should be all unicode by now :o
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
User avatar
Klark92
Posts: 161
Joined: 18 Jan 2015, 19:33

Re: AHKhttp - HTTP Server

29 Dec 2016, 12:55

I dont understand what exactly unicode is but that 0-255 charset should enough for all humanity :/
Smart Kombo 1.0 | One of the best Knight Online's key combo program...
User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: AHKhttp - HTTP Server

29 Dec 2016, 13:12

I once thought so too... But then, we have cyrillic, kanji, arabic, greek, etc.
And, let's not forget the emojis! ;)
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
User avatar
Klark92
Posts: 161
Joined: 18 Jan 2015, 19:33

Re: AHKhttp - HTTP Server

29 Dec 2016, 16:02

emojis are unicode but they are always parsing... all the unicode chars are parsing... :) anyways I wanted it ansi because some of funcs are not working with unicode...
Smart Kombo 1.0 | One of the best Knight Online's key combo program...
User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: AHKhttp - HTTP Server

29 Dec 2016, 16:55

Klark92 wrote:[...] I wanted it ansi because some of funcs are not working with unicode...
Hmmm, yes... that is unfortunate... :(
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
Drugoy
Posts: 48
Joined: 11 Jun 2016, 07:37
Contact:

Re: AHKhttp - HTTP Server

15 Jan 2017, 06:04

Is it possible to somehow add php support?
All I found is this, which relies on php being executed/initiated by your webserver (obviously, not an ahk one).
Guest

Re: AHKhttp - HTTP Server

15 Jan 2017, 09:30

@drugoy can't you just Run the php.exe and grab its output?
User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: AHKhttp - HTTP Server

15 Jan 2017, 21:55

yeah, what that dude said :HeHe:
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
brutus_skywalker
Posts: 175
Joined: 24 Dec 2016, 13:16
Location: Antarctica

Re: AHKhttp - HTTP Server

20 May 2017, 11:13

I've can't seem to get the server to load html pages with images(i.e not just loading an image from the server as given in the example), it loads text and other elements like buttons in the page but not the images. so i would appreciate it if someone could either point me towards a fix,or if some could give me an example of the server running an html page with images with in it. Mind you the html page i'm using loads images just fine when used as an html file within an ftp server or just running the file directly,this server however seems unable to load the images in the page,i'm really only familiar with the basics of html so if any html errors are there,feel free to point them out as well. tnx for any help.

Here's my script,

Code: Select all

#Persistent
#SingleInstance, force	
SetBatchLines, -1

Index_Html =
(
<!doctype html>
<html>
<head>
<title> Index </title>
<style>
body {
	background-color : black ;
	color : blue ;
}
</style>
</head>
<body>
<img src="On.png" style="width:100`%" >
<a href="Alert.png"> <button> TEst Button<input type="image" src ="Info.png" /> </button> </a>
</p>

</body>
</html>
)




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

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


Index(ByRef req, ByRef res, ByRef server) {
Global
    res.SetBodyText(Index_Html)
    res.status := 200
}


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, %A_ScriptDir%\AHKhttp.ahk
#include <AHKsock>

The html file by itself.

Code: Select all

<!doctype html>
<html>
<head>
<title> Index </title>
<style>
body {
	background-color : black ;
	color : blue ;
}
</style>
</head>
<body>
<img src="On.png" style="width:100`%" >
<a href="Alert.png"> <button> TEst Button<input type="image" src ="Info.png" /> </button> </a>
</p>

</body>
</html>
Attachments
ImageFiles.7z
The image files i'm using for testing,just png icons.
(5.8 KiB) Downloaded 173 times
Outsourcing Clicks & Presses Since 2004.
Skittlez
Posts: 11
Joined: 13 Mar 2014, 12:16

Re: AHKhttp - HTTP Server

20 May 2017, 14:16

Add a route for your file. If you don't know how to, look at the Logo function for an example.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 106 guests