Commit c27905a9 authored by Marek Vavrusa's avatar Marek Vavrusa

modules/http: added safe stream handler, doc

parent 52e76f36
......@@ -157,6 +157,64 @@ exported restful APIs and subscribe to WebSockets.
http.snippets['/health'] = {'Health service', '<p>UP!</p>'}
How to expose RESTful services
A RESTful service is likely to respond differently to different type of methods and requests,
there are three things that you can do in a service handler to send back results.
First is to just send whatever you want to send back, it has to respect MIME type that the service
declared in the endpoint definition. The response code would then be ``200 OK``, any non-string
responses will be packed to JSON. Alternatively, you can respond with a number corresponding to
the HTTP response code or send headers and body yourself.
.. code-block:: lua
-- Our upvalue
local value = 42
-- Expose the service
http.endpoints['/service'] = {'application/json',
function (h, stream)
-- Get request method and deal with it properly
local m = h:get(':method')
local path = h:get(':path')
log('[service] method %s path %s', m, path)
-- Return table, response code will be '200 OK'
if m == 'GET' then
return {key = path, value = value}
-- Save body, perform check and either respond with 505 or 200 OK
elseif m == 'POST' then
local data = stream:get_body_as_string()
if not tonumber(data) then
return 505
value = tonumber(data)
-- Unsupported method, return 405 Method not allowed
return 405
In some cases you might need to send back your own headers instead of default provided by HTTP handler,
you can do this, but then you have to return ``false`` to notify handler that it shouldn't try to generate
a response.
.. code-block:: lua
local headers = require('http.headers')
function (h, stream)
-- Send back headers
local hsend =
hsend:append(':status', '200')
hsend:append('content-type', 'binary/octet-stream')
assert(stream:write_headers(hsend, false))
-- Send back data
local data = 'binary-data'
assert(stream:write_chunk(data, true))
-- Disable default handler action
return false
How to expose more interfaces
......@@ -86,28 +86,30 @@ end
-- Export HTTP service page snippets
M.snippets = {}
-- Serve GET requests, we only support a fixed
-- number of endpoints that are actually preloaded
-- in memory or constructed on request
local function serve_get(h, stream)
-- Serve known requests, for methods other than GET
-- the endpoint must be a closure and not a preloaded string
local function serve(h, stream)
local hsend =
local path = h:get(':path')
local entry = M.endpoints[path]
-- Unpack MIME and data
local mime, data
local mime, data, err
if entry then
mime, data = unpack(entry)
-- Get string data out of service endpoint
if type(data) == 'function' then
data = data(h, stream)
data, err = data(h, stream)
-- Handler doesn't provide any data
if data == false then return end
if type(data) == 'number' then return tostring(data) end
-- Methods other than GET require handler to be closure
elseif h:get(':method') ~= 'GET' then
return '501'
if type(data) == 'table' then data = tojson(data) end
if not mime or type(data) ~= 'string' then
hsend:append(':status', '404')
assert(stream:write_headers(hsend, true))
return '404'
-- Serve content type appropriately
hsend:append(':status', '200')
......@@ -147,14 +149,15 @@ local function route(endpoints)
-- Handle HTTP method appropriately
elseif m == 'GET' then
serve_get(h, stream)
-- Method is not supported
local hsend =
hsend:append(':status', '500')
assert(stream:write_headers(hsend, true))
local ok, err = pcall(serve, h, stream)
if not ok or err then
log('[http] %s %s: %s', m, path, err or '500')
-- Method is not supported
local hsend =
hsend:append(':status', err or '500')
assert(stream:write_headers(hsend, true))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment