Waffle

Fast, asynchronous web framework for Lua/Torch
Alternatives To Waffle
Project NameStarsDownloadsRepos Using ThisPackages Using ThisMost Recent CommitTotal ReleasesLatest ReleaseOpen IssuesLicenseLanguage
Echo25,7581,606a day ago165September 04, 202267mitGo
High performance, minimalist Go web framework
Iris23,993320a day ago212September 21, 202285bsd-3-clauseGo
The fastest HTTP/2 Go Web Framework. New, modern and easy to learn. Fast development with Code you control. Unbeatable cost-performance ratio :rocket:
Perfect13,858
2 days agoFebruary 08, 202164apache-2.0Swift
Server-side Swift. The Perfect core toolset and framework for Swift Developers. (For mobile back-end development, website and API development, and more…)
Cowboy6,9289,060286a month ago23May 12, 2021106iscErlang
Small, fast, modern HTTP server for Erlang/OTP.
Oatpp6,463
5 days ago163apache-2.0C++
🌱Light and powerful C++ web framework for highly scalable and resource-efficient web application. It's zero-dependency and easy-portable.
Kemal3,485
a month ago15mitCrystal
Fast, Effective, Simple Web Framework
Mojo2,49419256416 days ago737September 12, 202270artistic-2.0Perl
:sparkles: Mojolicious - Perl real-time web framework
Dotweb1,373
1a month ago20April 16, 202220mitGo
Simple and easy go web micro framework
Drab8602118 months ago51February 21, 202028mitElixir
Remote controlled frontend framework for Phoenix.
Mojo.js442
a day ago123July 16, 20226mitJavaScript
:unicorn: The Mojolicious real-time web framework for Node.js
Alternatives To Waffle
Select To Compare


Alternative Project Comparisons
Readme

Waffle

Waffle is a fast, asynchronous, express-inspired web framework for Lua/Torch built on top of ASyNC.

Waffle's performance is impressive. On this test, given in examples/fib.lua, Waffle reaches over 20,000 requests/sec (2-4 x Node+express, 1/2 x multithreaded Go). With automatic caching enabled, Waffle can reach over 26,000 requests/sec, equaling single-threaded Go.

This project depends on htmlua for HTML templating.

Installation

> (sudo) luarocks install https://raw.githubusercontent.com/benglard/htmlua/master/htmlua-scm-1.rockspec
> (sudo) luarocks install https://raw.githubusercontent.com/benglard/waffle/master/waffle-scm-1.rockspec

Hello World

local app = require('waffle')

app.get('/', function(req, res)
   res.send('Hello World!')
end)

app.listen()

Requests

app.get('/', function(req, res)
   res.send('Getting...')
end)

app.post('/', function(req, res)
   res.send('Posting...')
end)

app.put('/', function(req, res)
   res.send('Putting...')
end)

app.delete('/', function(req, res)
   res.send('Deleting...')
end)

URL Parameters

app.get('/user/(%d+)', function(req, res)
   local userId = tonumber(req.params[1])
   local users = {
      [1] = 'Lua',
      [2] = 'JavaScript',
      [3] = 'Python'
   }
   res.send(string.format('Hello, %s', users[userId] or 'undefined'))
end)

app.get('/user/(%a+)', function(req, res)
   local name = req.params[1]
   res.send(string.format('Hello, %s', name))
end)

HTML Rendering

There are two options for html rendering. The first involves writing actual html and using the string interp utility provided, ${variable-name}.

<html>
<head></head>
<body>
  <h3>Welcome, ${name}</h3>
  <p>Time: ${time}</p>
</body>
</html>
app.get('/render/(%a+)', function(req, res)
   res.render('./examples/template.html', {
      name = req.params[1],
      time = os.time()
   })
end)

The second, preferable, more powerful way involves writing htmlua scripts, either as separate template files, or inline in view functions.

-- luatemp.html
local base = extends 'examples/baseluatemp.html'
return block(base, 'content'){
   h3 'Welcome, ${name}',
   p 'Time: ${time}',
   ul(each([[${users}]], li)),
   img {
      src = 'https://www.google.com/images/srpr/logo11w.png'
   }
}
-- htmlua.lua
-- Template
app.get('/', function(req, res)
   res.htmlua('luatemp.html', {
      name = 'waffle',
      time = os.time(),
      users = {'lua', 'python', 'javascript'}
   })
end)

-- Inline
app.get('/i', function(req, res)
   res.send(html {
      head { title 'Title' },
      body { p 'Hello World!' }
   })
end)

The htmlua page provides further documentation and examples.

Form Parsing

app.get('/m', function(req, res)
   res.send(html { body { form {
      action = '/m',
      method = 'POST',
      enctype = 'multipart/form-data',
      p { input {
         type = 'text',
         name = 'firstname',
         placeholder = 'First Name'
      }},
      p { input {
         type = 'text',
         name = 'lastname',
         placeholder = 'Last Name'
      }},
      p { input {
         type = 'file',
         name = 'file' 
      }},
      p { input {
         type = 'submit',
         'Upload'
      }}
   }}})
end)

app.post('/m', function(req, res)
   local name = string.format('%s %s', req.form.firstname, req.form.lastname)
   local path = paths.add(os.getenv('HOME'), req.form.file.filename)
   req.form.file:save{path=path}
   res.send('Saved to ' .. path)
end)

You can easily transform an uploaded image into a typical torch tensor like so:

app.post('/', function(req, res)
   local img = req.form.file:toImage()
   local m = img:mean()
   res.send('Image mean: ' .. m)
end)

Websockets

To implement a websocket server, call app.ws with a url path and a function accepting a single table. You can then define checkorigin, onopen, onmessage, onpong, and onclose for that table, to control the server-side websocket connection.

Benchmarking websockets is tricky, but on first attempts, waffle seems competitive with similar node libraries.

local app = require('waffle')
local js = [[
var ws = new WebSocket("ws://127.0.0.1:8080/ws/");
function print() { console.log(ws.readyState); }
ws.onopen = function() {
   console.log("opened");
   print();
   ws.send("Hello");
}

ws.onmessage = function(msg) {
   console.log(msg);
   setTimeout(function() { ws.close(); }, 1000);
}

ws.onclose = function(event) {
   console.log(event);
   console.log("closed");
   print();
}
]]

app.get('/', function(req, res)
   res.send(html { body {
      p 'Hello, World',
      script { type='text/javascript', js }
   }})
end)

app.ws('/ws', function(ws)
   ws.checkorigin = function(origin) return origin == 'http://127.0.0.1:8080' end
   ws.onopen = function(req) print('/ws/opened') end
   ws.onmessage = function(data)
      print(data)
      ws:write('World')
      ws:ping('test')
   end
   ws.onpong = function(data) print(data) end
   ws.onclose = function(req) print('/ws/closed') end
end)

You can broadcast a message to all open websockets on a url like this:

ws:broadcast('message')

or like this:

app.ws.broadcast(req.url.path, 'message')

Query Paramaters

app.get('/search', function(req, res)
   local search = req.url.args.q
   res.redirect('https://www.google.com/search?q=' .. search)
end)

Static Files

local app = require('waffle')
app.set('public', '.')
app.listen()

Error Handling

app.error(404, function(description, req, res)
   local url = string.format('%s%s', req.headers.host, req.url.path)
   res.status(404).send('No page found at ' .. url)
end)

app.error(500, function(description, req, res)
   if app.debug then
      res.status(500).send(description)
   else
      res.status(500).send('500 Error')
   end
end)

Cookies

app.get('/cookie', function(req, res)
   local c = req.cookies.counter or -1
   res.cookie('counter', tonumber(c) + 1)
   res.send('#' .. c)
end)

Sessions

Waffle has both in-memory and redis sessions using redis-async.

local app = require('../waffle').CmdLine()

app.get('/', function(req, res)
   if app.session.type == 'memory' then
      local n = app.session.n or 0
      res.send('#' .. n)
      if n > 19 then app.session.n = nil
      else app.session.n = n + 1 end
   else
      app.session:get('n', function(n)
         res.send('#' .. n)
         if n > 19 then app.session:delete('n')
         else app.session.n = n + 1 end
      end, 0)
   end
end)

app.listen()

JSON

app.get('/', function(req, res)
   res.json{test=true}
end)

urlfor and Modules

-- Add a name parameter, e.g. 'test'
app.get('/test', function(req, res) res.send('Hello World!') end, 'test')

-- Retreive url corresponding to route named 'test'
local url = app.urlfor('test')

Modules let you group routes together by url and name (really by function)

app.module('/', 'home') -- Home Routes
   .get('',     function(req, res) res.send 'Home' end, 'index')
   .get('test', function(req, res) res.send 'Test' end, 'test')

app.module('/auth', 'auth') -- Authentication Routes
   .get('', function(req, res) res.redirect(app.urlfor('auth.login'))
      end, 'index')
   .get('/login',  function(req, res) res.send 'Login'  end, 'login')
   .get('/signup', function(req, res) res.send 'Signup' end, 'signup')

Command Line Options

Allows you to write every currently possible waffle application property as a command line option, and have it handled seamlessly.

local app = require('waffle').CmdLine()
> th examples/cmdline.lua --debug --print --port 3000 --templates examples/

Async Debugging

app = require('waffle')
a = 1
b = 2
c = 3
app.repl()
app.listen()
th> async = require 'async'
                                                                      [0.0133s]  
th> async.repl.connect({host='127.0.0.1', port=8081})
                                                                      [0.0005s]  
th> async.go()
127.0.0.1:8081> a
1  
127.0.0.1:8081> b
2  
127.0.0.1:8081> c
3  
127.0.0.1:8081> app
{ ... }
127.0.0.1:8081> _G
{ ... }

wafflemaker (executable)

The wafflemaker executable can be used:

  • to create project directories in MVC style, like so:
wafflemaker --create name_of_project
  • to serve static files akin to running python -m SimpleHTTPServer, but with much, much, much better performance (almost 20x requests/sec).
cd folder/i/want/to/serve
wafflemaker --serve

Larger example (with autocache)

When autocache is set to true, waffle will automatically store the response body, headers, and status code, and reuse them when a request is sent to the same http method/url. So, for instance, when a request is sent to GET/10 in the example below, it will only have to compute fib(10) once. Note that app.urlCache is set by default to cache the data of the last 20 method/url requests.

local app = require('../waffle') {
   debug = true,
   autocache = true
}

fib = function(n)
   if n == 0 then return 0
   elseif n == 1 then return 1
   else return fib(n-1) + fib(n-2)
   end
end

app.get('/(%d+)', function(req, res)
   local n = req.params[1]
   local result = fib(tonumber(n))
   res.header('Content-Type', 'text/html')
   res.send('ASyNC + Waffle<hr> fib(' .. n .. '): ' .. result)
end)

app.listen()

TODO

  • Named URL route parameters
  • Automatic caching of static files
  • Secure cookies & cookie based sessions
Popular Web Application Framework Projects
Popular Websocket Projects
Popular Frameworks Categories

Get A Weekly Email With Trending Projects For These Categories
No Spam. Unsubscribe easily at any time.
Lua
Websocket
Session
Web Application Framework
Torch