HTTPD Library
The HTTPD library can be used to create a mini web-server. You may use this for things such as controlling your script via a web browser. At this time, SSL is not available, so all content served over the HTTPD library will be unsecure; please keep this in mind when designing your project. #Loading the HTTPD Library

The HTTPD library is optional. In order to use this library, you must require it in one of your source files (preferably main.lua) prior to use. It only needs to be required once in a single file. It is recommended you do this at the top of your main.lua file, outside of any functions or other code, and you may do this by using this code:

Example:
require 'httpd/httpd';

#Supported Methods #Constructor httpd Httpd() httpd Httpd(table config)

Creates a new HTTPD server object. If 'config' is not given, defaults are assumed. By passing in a config, you may control the IP to listen to, port to listen on, and which directories to look in for supporting objects (controllers, views, assets, etc.).

As soon as the object is created, it will begin to listen for any web traffic, however, you must also be sure to pass event data back to your HTTPD instance. See Httpd:handleEvent() for more information.

Example with defaults
require 'httpd/httpd'; httpd = Httpd();
Example with custom config
httpd = Httpd({ ip = '0.0.0.0', -- The IP to bind to; use 0.0.0.0 to accept any connection; default 127.0.0.1 port = 80, -- The port to bind to; default 8080 controllerDir = './Controllers/', -- Folder to look for controller objects; default /Controllers/ viewDir = './Views/', -- Folder to look for view files & templates; default /Views/ staticDir = './Static/', -- Folder to look for static files (images, css, javascript, etc.); default /Static/ )};
#Httpd:handleEvent boolean Httpd:handleEvent(string eventType, ...)

Passes an event off to be handled by the Httpd. Returns true if the event was handled by this Httpd, otherwise it returns false. You should place this in your macro.event() function and check its return result.

If you do not use this function to pass off event data to a created Httpd, then it will not be able to function properly as all incoming socket data is handled this way. This will cause your web server to be unresponsive, so remember to call it!.

Example:
function macro.event(e, ...) if( not httpd:handleEvent(e, ...) ) then -- Wasn't handled by the HTTPD, so we might need to handle it some other way end end

#Routes

When initiated, the Httpd expects to be able to load a routes.lua file which is located in the same directory as your calling script(probably main.lua). This routes file should contain calls to the Route object; this object is instantiated for you automatically when you load the HTTPD library. In this file, you will tell the Route object to mount various Controllers to URIs via the Route:controller() function.

#Route:controller Route:controller(string uriSegment, string controllerName)

This function maps the first URI segment of a URL to a specific controller, which must be the exact name of the controller class. For example:

Example:
Route:controller('/', 'HomeController'); -- This is a special URI; this is your base page, as if you accessed http://127.0.0.1:8080 (assuming default config) Route:controller('home', 'HomeController'); -- Just like above, but the home controller is now accessible via / OR /home Route:controller('test', 'TestController'); -- Just some other controller for an example

#Controllers

Controllers are where the processing for your routes happens; they handle the request the user provided and will be responsible for returning the result. Methods for each accepted URI should be prefixed with either "get" or "post" depending on in the request is a GET or POST method. For example, you are likely to have a getIndex() method on your HomeController that will render your site's homepage.

A Request object will be passed to the routed method; this can be used to handle any GET/POST parameters.

A Controller should also return the rendered result. You may directly return the HTML as a Response but it is recommended to render a view with View:make().

Controllers/HomeController.lua
HomeController = class.new(); -- This handles any request to get the page at http://127.0.0.1:8080 function HomeController:getIndex(request) local dashboardData = getDashboardData(); -- Pretend getDashboardData() grabs some data we want to display return View:make('home', dashboardData); -- Render the page and return it to the user end -- If we instead tried to POST data to http://127.0.0.1:8080, it gets routed here instead function HomeController:postIndex(request) local searchString = request:input('search'); local searchResults = getSearchResults(searchString); -- Pretend this does some actual searching return View:make('search', searchResults); end

#Requests #Request:input number|string Request:input(string inputName)

Returns an input from either POST-ed variables or GET variables with a field that matches the 'inputName'. For example, http://127.0.0.1:8080?searchString=blah is a GET request that will have a Request with 'searchString' = "blah." The type returned will be either a number or a string depending on the input that was received.

If you are expecting to receive a file, use Request:file() instead.

Example:
-- For accepting a request over GET method function SearchController:getSearch(request) local searchString = request:input('searchString'); end -- For accepting a request over POST method function SearchController:postSearch(request) local searchString = request:input('searchString'); end
#Request:file number|string Request:file(string inputName)

Like Request:input(), except it will return a file. The file will be returned as a table with fields for name, filename, size, and file content.

Example:
function UploadController:postFile(request) local file = request:file('uploadFile'); -- Or whatever we named our file upload field local name = file.name; local fileName = file.fileName; local fileSize = file.size; -- Size in bytes local fileContents = file.content; -- Contents of the file uploaded, as a string end
#Request:oldInput number|string Request:oldInput(string inputName, number|string default)

Like Request:input(), except geared towards "old" input after a validation failure. This is useful for re-populating a form in your View after submission.

If the given field has no input, then it will return 'default' (which defaults to '') instead.

Views/search.view.lua
<input type="text" name="searchString" value="{{ request:oldInput('searchString') }}" placeholder="Search for..." />
#Request:all table Request:all()

Returns all inputs (GET and POST) as a table.

#Request:getCookie number|string Request:getCookie(string name)

Returns the value of a set cookie specified by 'name'.

Example:
function UserControlPanelController:getIndex(request) local loginCookie = request:getCookie('login'); -- We might store our login as a cookie and need to validate it here end
#Request:uriSegment string Request:uriSegment(number index, string default)

Returns the URI segment at a given index, or the specified default if it does not exist.

A URI segment refers to strings, separated by a '/', in the URI section of a URL. For example, http://127.0.0.1:8080/home/index/segment3/segment4/segmentn?notasegment=1 represents a full URL where the segment at index 1 (which typically represents the controller) is 'home', index 2 (which typically represents the method/action) is 'index', index 3 is 'segment3', and so on.

Example:
-- http://127.0.0.1:8080/home/index/whatever would be routed here function HomeController:getIndex(request) local something = request:uriSegment(3); -- 'something' should now be assigned 'whatever' end
#Request:getErrors string Request:getErrors()

Returns all validation errors, as a string, separated by "<br />\n"

In any view which may have validation errors
@if( request:getErrors() ) <h1>Errors:</h1> {{ request:getErrors() }} @endif

#Views

The HTTPD library makes use ov Views to separate logic (which should go into controllers) from page displays. Views use a Blade-like templating engine. Your Views will be HTML snippets (with some extra syntax and control structures), that end with the ".view.lua" extension.

#Templates

A template is a special View that other views may choose to extend upon using the @extend directive. A very simplistic template might look something like this:

Example:
<!DOCTYPE html> <html lang="en"> <head> </head> <body> @@section('content') </body> </html>

Once you have a template (which is optional, but highly recommended), any other View can extend it by injecting content into its sections.

Example:
@extends('template') @beginsection('content') Hello World! @endsection
#View:make Response View:make(string viewName) Response View:make(string viewName, table data)

Fully takes care of rendering a view for you and returns the result as a Response.

You should specify the view to render ('viewName') without the "view.lua" extension. You may optionally pass additional variables that will be available to the view as a table of key/value pairs.

Controllers/TestController.lua
function TestController:getIndex(request) local data = { name = Bob, age = 8, }; return View:make('test', data); end
Views/test.view.lua
<html> <body> <h1>Test</h1> Hello, my name is {{ name }} and I am {{ age }} years old. </body> </html>
#@extends() @extends(string viewName)

"Extend" a view by using it as a template. Any sections in this view will be inserted into the proper place in the template.

See Templates for more information.

#@section() @section(string sectionname)

Creates a placeholder (in a template) that can be overriden by another view.

See Templates and @beginsection for more info.

#@beginsection() @beginsection(string sectionname)

Creates a section that can be placed into the template; if it has a @section() with a matching name, this section will override its content.

The section will continue until it reaches a paired @endsection directive.

Example:
@beginsection('mySection') Everything in here will go into the 'mySection' section. @endsection
#@include() @include(string viewName)

The @include directive can be used to include sub-views into the calling view. This can be useful for partial segments that you might want to reuse on multiple pages (such as some sort of user panel, a form, etc.), or just to further separate code and make things easier for you to work with.

Like with any view, you do not need to include the ".view.lua" suffix.

Example:
@extends('template') @beginsection('content') <div class="panel"> @include('panel') </div> @endsection
#@foreach() @foreach(table tableName as string indexName, string viewName) { ... }

The @foreach directive allows you to iterate over a table of items. Essentially, it's a for-loop

You should pass this directive 3 items in a special format: tableName as indexName,valueName. 'tableName' is of course the table to iterate over while 'indexName' and 'valueName' are the name of the variables to use for the key/value of each iteration.

Example:
<table> <tr> <td>Key</td><td>Value</td> </tr> <tr> @foreach(dataTable as index,value) { <td>{{ index }}</td><td>{{ value }}</td> } </tr> </table>
#@for() @for(string variableName = number startCount, number endCount) { ... }

Like @foreach except it iterates from 'startCount' to 'endCount'. For example, for(count = 1, 10) would iterate 10 times, with 'count' incrementing from 1 to 10.

Example:
@for(count = 1, totalCount) { {{ count }}<br /> }
#@if() @if(condition) { ... } @if(condition) { ... } else { ... }

A standard if statement, to conditionally show content. Optionally you may also have an else statement.

Example:
@if(someVariable) { <p>The statement was true</p> } else { <p>The statement was false</p> } @if( 10 > 5 ) { <p>Pretty sure 10 is greater than 5</p> }
#Displaying Data {{ statement }} {{! statement !}} {{# statement #}}

Double curly braces can be used to output some dynamic content into the view. It will also automatically escape HTML entities. Any Lua code or variables can be placed in the braces.

The {{! statement !}} variation can be used to not escape HTML entities. Use this if you are trying to output a string that might contain data you want to actually be rendered as HTML.

The {{# statement #}} variation can be used to run a statement without displaying any of its output.

Example:
<p>Display a string: {{ someString }}</p> <p>{{ if(true) then return 'This statement is true!'; else return 'This statement is false!' end }}</p> <p>Display raw string: {{! "Contains <b>HTML</b>" !}} <br /> Would actually display the text "Contains HTML"; HTML would be bold.</p> <p>Run w/ no display: {{# count = count + 1 #}}</p>

#Responses

Controllers are expected to return a Response object; View:make() renders the view and returns it as a Response for you. In this context, a Response is simply an object that pairs an HTTP status code to an HTML string which should be sent to the requesting client.

You may opt to create a Response rather than rely on View:make() if you want more control over the status code or just to return a simple HTML message. For example, you might want to show a custom error message.

Example:
-- Route for http://127.0.0.1:8080/store/item/xxxx where xxxx is the name of an item to get info on function StoreController:getItem(request) local itemname = request:uriSegment(3, nil); -- If an itemname wasn't specified or if we couldn't find it in the itemtable, show an error if( not itemname or not itemtable[itemname] ) then return Response(404, "<h1>Not Found</h1><p>Sorry, we couldn't find the item for you :(</p>"); end local data = { item = itemtable[itemname], }; return View:make('item_details.view.lua', data); end

Page last updated at 2018-10-08 19:30:49


Copyright 2024