jvanegmond

Powerful HTTP Server in Pure AutoIt

170 posts in this topic

#1 ·  Posted (edited)

This is a full HTTP server written in pure AutoIt. No external libraries are used. The server code is readable and very simple to modify.

Abilities:

  • A full web server in a single executable.
  • Able to integrate the web pages into your source code and modify its contents with code. ( This is how the first web pages were build )
  • The ability to send a page with over 1000 images in under 5 seconds.
  • Fully compatible with both Firefox, Internet Explorer 6 through 9, Chrome and Safari.
  • Nearly every line is commented. It helps you understand the code easily, so that you may reuse it and add on new code with ease.
  • GET, POST are both supported and even cookies and sessions are supported

This is intended to be a base for any future projects that provide any service over HTTP. Three different servers give you the base code that you need, and nothing you won't need. In the download there is included a short guide on how to choose the right one.

BrettF has done some awesome work and added to capability to retain sessions in this post: >http://www.autoitscript.com/forum/topic/...e-autoit/page__view__findpost_
If you need sessions, you can use his version. This version is added in the above v1.1 download.

Edit: Please find the webserver attached to this post. You guys keep eating my bandwidth and I had to move it again.

webserver_011.zip

Edited by Manadar
3 people like this

Share this post


Link to post
Share on other sites



@Manadar

I am sorry to say that you are not the first to come up wiht a webserver.

There are at least 3 or 4 others before you.

One of the was picasso and here is an other one from Dhilip's Web Server v0.1a.

Anyway a good start is to make a webserver that can handle the AuCGI.

regards,

ptrex

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

I know I'm not the first, ptrex, and it is not my intention to be "the first" in making something. I'm trying to make something for the benefit of others and this is just a great base for creating your own web server.

The other web servers available, at least the 2 decent ones, are made by picasso and Dhilip89.

Dhilip89 has made a web server built in pure AutoIt, but it has a GUI. That means you can't easily use it a base to work from. Mine is GUI-less, which is important when you are building a base for others to build off.

picasso only uses AutoIt as a pre-processor for HTML before Abyss sends it, so it doesn't even count as a web server.

That means that this script is the first of it's kind and I am, despite what you said, the first to make this.

Edited by Manadar

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

hi

works fine for me

somehow traytip wont work for me?

i added

TrayTip("AutoIt Web Server", "Server created on http://" & $IP & "/",10)

MsgBox(0x20, "AutoIt Web Server", "Server created on http://" & $IP & "/")

...

Case ".css"

_SendFile($sRootDir & "\" & $Request, "text/html", $Socket[$x])

Edited by nobbe

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

i think its awesome, but it would be greater if you could incorporate AuCGI

btw, anyone care to test-> http://82.116.81.90 <--- Sweet :D Works :D

:D

Added _Get_Post, it allows you to use $_POST as in php :)

and a little test function as view on my "Webpage"

was mostly just for testing :D

TrayTip("AutoIt Web Server", "Starting to create server..",10)

Dim $sRootDir = @ScriptDir & "\www\" ; The absolute path to the root directory of the server.

Dim $IP = @IPAddress2
Dim $Port = 80 ; the listening port

Dim $Max_Users = 15

Dim $Socket[$Max_Users]
Dim $Buffer[$Max_Users]
$Socket[0] = -1

TCPStartup()

$MainSocket = TCPListen($IP,$Port) ;create main listening socket
If @error Then
    MsgBox(0x20, "AutoIt Webserver", "Unable to create a socket on port " & $Port & ".")
    Exit
EndIf

TrayTip("AutoIt Web Server", "Server created on http://" & $IP & "/",10)

While 1
    Sleep(10)
    
    $NewSocket = TCPAccept($MainSocket)
    If $NewSocket >= 0 Then
        For $x = 0 to UBound($Socket)-1
            If $Socket[$x] = -1 Then
                $Socket[$x] = $NewSocket ;store the new socket
                ExitLoop
            EndIf
        Next
    EndIf

    
    For $x = 0 to UBound($Socket)-1 ; loop to receive data from all sockets
        If $Socket[$x] = -1 Then ContinueLoop
        $NewData = TCPRecv($Socket[$x],1024)
        If @error Then
            $Socket[$x] = -1
            ContinueLoop
        ElseIf $NewData Then ; data received
            $Buffer[$x] &= $NewData ;store it in the buffer
            If StringInStr(StringStripCR($Buffer[$x]),@LF&@LF) Then
                $FirstLine = StringLeft($Buffer[$x],StringInStr($Buffer[$x],@LF))
                $RequestType = StringLeft($FirstLine,StringInStr($FirstLine," ")-1)
                If $RequestType = "GET" Then
                    $Request = StringTrimRight(StringTrimLeft($FirstLine,4),11)
                    If $Request = "/" Then
                        $Request = "/index.html"
                    EndIf
                    $Request = StringReplace($Request,"/","\")
                    If FileExists($sRootDir & "\" & $Request) Then
                        $sFileType = StringRight($Request,4)
                        Switch $sFileType
                            Case "html"
                                _SendFile($sRootDir & "\" & $Request, "text/html", $Socket[$x])
                            Case ".htm"
                                _SendFile($sRootDir & "\" & $Request, "text/html", $Socket[$x])
                            Case ".jpg"
                                _SendFile($sRootDir & "\" & $Request, "image/jpeg", $Socket[$x])
                            Case "jpeg"
                                _SendFile($sRootDir & "\" & $Request, "image/jpeg", $Socket[$x])
                            Case ".png"
                                _SendFile($sRootDir & "\" & $Request, "image/png", $Socket[$x])
                            Case Else
                                _SendError($Socket[$x])
                        EndSwitch
                    Else
                        _SendError($Socket[$x])
                    EndIf
                ElseIf $RequestType = "POST" Then
                    
                    ;Get the lenght of the data in the $_POST
                    $_POST = _Get_Post($Buffer[$x])
                    
                    ;This is my little test function ;)
                    ;Add special chars from the name
                    $Name = StringReplace(DllStructGetData($_POST,'Name'), '%', '')
                    For $t = 0 To @extended
                        $Find_Char = StringLeft(StringTrimLeft($Name, StringInStr($Name, '%')) ,2)
                        $Name = StringReplace($Name, '%' & $Find_Char, Chr(Dec($Find_Char)))
                    Next
                    
                    ;Add special chars from the comment
                    $Comment = StringReplace(DllStructGetData($_POST,'Comment'), '+', ' ')
                    StringReplace($Comment, '%', '')
                    For $t = 0 To @extended
                        $Find_Char = StringLeft( StringTrimLeft($Comment, StringInStr($Comment, '%')) ,2)
                        $Comment = StringReplace($Comment, '%' & $Find_Char, Chr(Dec($Find_Char)))
                    Next
                        
                    If $Name <> '' And $Comment <> '' Then
                        
                        ;Very bad way of doing it :s
                        $File_Data = StringReplace(FileRead($sRootDir & '\index.html'), '</TABLE>','')
                        FileDelete($sRootDir & '\index.html')
                        FileWrite($sRootDir & '\index.html',$File_Data &  '<td>' & $Name & ': <td>' & $Comment & '<tr>' & @CRLF & '</TABLE>')
                    EndIf
                    
                    ;"Refresh" the users webpage
                    _SendFile($sRootDir & "\index.html", "text/html", $Socket[$x])
                EndIf
                $Buffer[$x] = ""
                TCPCloseSocket($Socket[$x])
                $Socket[$x] = -1
            EndIf
        EndIf
    Next
WEnd

Func _SendHTML($sHTML,$sSocket)
    $iLen = StringLen($sHTML)
    $sPacket = Binary("HTTP/1.1 200 OK" & @CRLF & _ 
    "Server: ManadarX/1.0 (" & @OSVersion & ") AutoIt " & @AutoItVersion & @CRLF & _
    "Connection: close" & @CRLF & _
    "Content-Lenght: " & $iLen & @CRLF & _
    "Content-Type: text/html" & @CRLF & _
    @CRLF & _
    $sHTML)
    $sSplit = StringSplit($sPacket,"")
    $sPacket = ""
    For $i = 1 to $sSplit[0]
        If Asc($sSplit[$i]) <> 0 Then ; Just make sure we don't send any null bytes, because they show up as ???? in your browser.
            $sPacket = $sPacket & $sSplit[$i]
        EndIf
    Next
    TCPSend($sSocket,$sPacket)
EndFunc

Func _SendFile($sAddress, $sType, $sSocket)
    $File = FileOpen($sAddress,16)
    $sImgBuffer = FileRead($File)
    FileClose($File)
    
    $Packet = Binary("HTTP/1.1 200 OK" & @CRLF & _ 
    "Server: ManadarX/1.3.26 (" & @OSVersion & ") AutoIt " & @AutoItVersion & @CRLF & _
    "Connection: close" & @CRLF & _
    "Content-Type: " & $sType & @CRLF & _
    @CRLF)
    TCPSend($sSocket,$Packet)
    
    While BinaryLen($sImgbuffer) ;LarryDaLooza's idea to send in chunks to reduce stress on the application
        $a = TCPSend($sSocket,$sImgbuffer)
        $sImgbuffer = BinaryMid($sImgbuffer,$a+1,BinaryLen($sImgbuffer)-$a)
    WEnd
    
    $Packet = Binary(@CRLF & _
    @CRLF)
    TCPSend($sSocket,$Packet)
    TCPCloseSocket($sSocket)
EndFunc

Func _SendError($sSocket)
    _SendHTML("404 Error: " & @CRLF & @CRLF & "The file you requested could not be found.", $sSocket)
EndFunc

Func _Get_Post($s_Buffer)
    Local $s_Temp_Post,$s_Post_Data
    Local $Temp, $s_Struct, $s_Len
    
    ;Get the lenght of the data in the POST
    $s_Temp_Post = StringTrimLeft($s_Buffer,StringInStr($s_Buffer,'Content-Length:'))
    $s_Len = StringTrimLeft($s_Temp_Post,StringInStr($s_Temp_Post,': '))
    
    ;Create the base struck
    $s_Post_Data = StringSplit(StringRight($s_Buffer,$s_Len),'&')
    For $t = 1 To $s_Post_Data[0]
        $Temp = StringSplit($s_Post_Data[$t],'=')
        $s_Struct &= 'char ' & $Temp[1] & '[' & StringLen($Temp[2])+1 & '];'
    Next
    $s_Temp_Post = DllStructCreate($s_Struct)
    
    ;add the data to the struck
    For $t = 1 To $s_Post_Data[0]
        $Temp = StringSplit($s_Post_Data[$t],'=')
        DllStructSetData($s_Temp_Post,$Temp[1],$Temp[2])
    Next
    
    Return $s_Temp_Post
EndFunc
Edited by Alek

[font="Impact"]Never fear, I is here.[/font]

Share this post


Link to post
Share on other sites

Very nice. I will be testing this when I get home from school.


[left][sub]We're trapped in the belly of this horrible machine.[/sub][sup]And the machine is bleeding to death...[/sup][sup][/sup][/left]

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

This is great!

I only think you held back the most important MIME and subtype there is :D YES! i mean application/octet-stream :D

For the folks that dont know: Thats the combination for getting raw binary data to your webserver-user. (AKA "downloading")

For example: If you want your webserver to upload .zip and .exe on request, add the following cases ABOVE the case else:

Case ".zip"
    _SendFile($sRootDir & "\" & $Request, "application/octet-stream", $Socket[$x])
Case ".exe"
    _SendFile($sRootDir & "\" & $Request, "application/octet-stream", $Socket[$x])
Edited by Creator

Share this post


Link to post
Share on other sites

@Manadar

Don't feel offended. I felt that I had to correct what you said in the first post

there are no pure AutoIt Web Servers around

This is not quite true there are multiple au3 webservers around.

Anyhow good job with yours !!

Like I and Alek suggested, thare are no Webservers around that support the AuCGI.

This might be an next challange.

regards,

ptrex

Share this post


Link to post
Share on other sites

UPDATED 15 APRIL 2008

Changes:

Added _POST thanks to Alek

Minor fixes thanks to Alek

Added application/octet-stream (zip, gzip, exe etc.) thanks to Creator ( I didn't miss it, I just didn't know the proper MIME type :D )

@Alek, I have rewritten your post example, to be more coherent with the rest of code. It also doesn't use structs anymore, instead it uses a 2D array and a function to mimic the associative arrays.

After some consideration, AuCGI looks like something that is easy to implement in this web-server. Although, I'd rather leave that implementation up to whoever made AuCGI in the first place.

Share this post


Link to post
Share on other sites

i am loving this!!!! i am actually using it to host a website for my band =D

real question: how can you run multiple instances of this for multiple sites, i tried copy/pasting the script and changing the port to 70, and used no-ip.com to redirect it from port 80 to port 79, but it didnt seem to work. replys would be much apreciated!

Share this post


Link to post
Share on other sites

i am loving this!!!! i am actually using it to host a website for my band =D

real question: how can you run multiple instances of this for multiple sites, i tried copy/pasting the script and changing the port to 70, and used no-ip.com to redirect it from port 80 to port 79, but it didnt seem to work. replys would be much apreciated!

I'm glad you like it so much, but I can seriously recommend you to set up a proper HTTP server like Apache.

http://httpd.apache.org/

Share this post


Link to post
Share on other sites

Just brainstorming here but some nice features would be:

  • HTTP Control Panel
  • In /Program Files you should make a folder along with settings and the WWW folder
  • MySQL support
  • Password protect folders
I am very sure MySQL support would be very difficult. The HTTP control panel would be a couple of linked .html files which will just change settings such as the directory of WWW, the port number etc.

James

Share this post


Link to post
Share on other sites

Just brainstorming here but some nice features would be:

  • HTTP Control Panel
  • In /Program Files you should make a folder along with settings and the WWW folder
  • MySQL support
  • Password protect folders
I am very sure MySQL support would be very difficult. The HTTP control panel would be a couple of linked .html files which will just change settings such as the directory of WWW, the port number etc.

James

For me, i don't see any reason to turn this into a production type server with support for every other thing....there's plenty of those out there already that would do better.

Ideally i'd really like to see this type of autoit server used as a component of autoit applications.

For example, one could,

have a gui with an embedded instance of IE....rather an embedded web server to host your autoit application.

The server could serve either pages from a "www" directory OR return code directly to the server from the application itself using the full scripting power of Autoit.....

$html = "<h1>Welcome " & @UserName & "</h1>"

return $html

This would be extremely useful. Many applications like modem and router user managment interfaces use exactly this technique.

Hope someone takes up the challenge.

Good work Manadar!

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

Thanks, Will66. Your post proves you have a good insight on what to do with this, and I completely agree with you.

Many applications like modem and router user managment interfaces use exactly this technique.

You're completely right! This web server should be all about providing a base for people to build applications off, and that is what I am using it for too. Making a browser-based remote control for your PC is extremely easy with this.

A future feature I have in mind is to release a version that uses "AuCGI", it basically means you can use <?au3 ?> tags inside your HTML. That would mean that this code:

$html = "<h1>Welcome " & @UserName & "</h1>"
<h1>Welcome  <?au3 ConsoleWrite( @UserName) ?> </h1>
is essentially the same.

However, I will keep this base version of a web server up for anyone to download who wishes not to use this feature.

Edited by Manadar

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

Thanks, Will66. Your post proves you have a good insight on what to do with this, and I completely agree with you.

You're completely right! This web server should be all about providing a base for people to build applications off, and that is what I am using it for too. Making a browser-based remote control for your PC is extremely easy with this.

A future feature I have in mind is to release a version that uses "AuCGI", it basically means you can use <?au3 ?> tags inside your HTML. That would mean that this code:

$html = "<h1>Welcome " & @UserName & "</h1>"
<h1>Welcome  <?au3 ConsoleWrite( @UserName) ?> </h1>
is essentially the same.

However, I will keep this base version of a web server up for anyone to download who wishes not to use this feature.

Hi Manadar, using the web server in this way eliminates the need for AuCGI at all, in my view all files would reside inside the executable in the form of a function,

for example, if the server requests index.html?q=will66, rather than look to the "www" folder, call a function based on the requested file name instead:

func index($query="")

$html = "<h1>Welcome " & $query & "</h1>"

Return $html

EndFunc

Here's a rather crude unfinished example based on DtTvB's/picasso's server http://www.autoitscript.com/forum/index.php?showtopic=36845 using async.dll for multi threading and sqllite:

Server_Gui.zip

Edited by Will66

Share this post


Link to post
Share on other sites

I see what you are saying, reading what you said I understand there is no need for all of that support. But a settings panel, would make this more useful in my eyes.

I shall leave you to continue you with your work Manadar :D

Share this post


Link to post
Share on other sites

Hi Manadar, using the web server in this way eliminates the need for AuCGI at all, in my view all files would reside inside the executable in the form of a function,

for example, if the server requests index.html?q=will66, rather than look to the "www" folder, call a function based on the requested file name instead:

Here's a rather crude unfinished example based on DtTvB's/picasso's server http://www.autoitscript.com/forum/index.php?showtopic=36845 using async.dll for multi threading and sqllite:

Server_Gui.zip

Awesome, thanks for your insights. No more AuCGI, as I didn't want to do it in the first place.

I'll look at the multi-threading, since it is definitely something worth having.

Share this post


Link to post
Share on other sites

#19 ·  Posted (edited)

Awesome, thanks for your insights. No more AuCGI, as I didn't want to do it in the first place.

I'll look at the multi-threading, since it is definitely something worth having.

the zip in my above post is a different/better one than the one i posted in the DtTvB's/picasso thread.

Edited by Will66

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now