A web API toolkit playground for the Lua programming language.
luarocks install --server=http://luarocks.org/dev lucid
... or use docker images.
Using a function:
local http = require 'http'
local app = http.app.new()
app:use(http.middleware.routing)
app:get('', function(w, req)
return w:write('Hello World!\n')
end)
return app()
... or a metaclass:
local class = require 'core.class'
local web = require 'web'
local WelcomeHandler = class {
get = function(self)
self.w:write('Hello World!\n')
end
}
local all_urls = {
{'', WelcomeHandler}
}
local options = {
urls = all_urls
}
return web.app({web.middleware.routing}, options)
see more here.
Install development dependencies:
sudo make debian
make env nginx
make test qa
eval "$(env/bin/luarocks path --bin)"
alternative environments:
make env LUA_VERSION=5.2.4
make env LUA_IMPL=luajit LUA_VERSION=2.0.5
make env LUA_IMPL=luajit LUA_VERSION=2.1.0-beta2
Check from the command line:
lurl -v demos/http/hello.lua /
Serve files with a web server:
export app=demos.http.hello ; make run
curl -v http://localhost:8080
Use docker:
docker run -it --rm -p 8080:8080 -v `pwd`/demos:/app \
-e app=http.hello akorn/lucid:dev-luajit2.1-alpine
Open your browser at http://localhost:8080
The app object by convention corresponds to HTTP application. Create it by
calling http.app.new function exported by the http module:
local http = require 'http'
local app = http.app.new()
It also accepts optional table options that affects how the application
behaves:
local app = http.app.new {
root_path = '/api/v1/'
}
Later options can be accessed as app.options.
The same options are shared as a parameter to middleware initialization and available in HTTP request object as
req.options.
The app object has methods for configuring middleware:
app:use(http.middleware.routing)
and routing HTTP requests:
app:get('', function(w, req)
return w:write('Hello World!\n')
end)
Here app:get function corresponds to HTTP GET verb, app:post to HTTP
POST, etc.
Finally you call app to build url mapping, chain middlewares and run it
through initialization step.
return app()
This call returns the first middleware registered with app:use.
The table of adapted HTTP verbs can be accessed as
http.app.http_verbs,
which is an association between a function name and HTTP verb, e.g.
post = POST, etc. The association happens during application initialization
only, thus does not affect runtime.
HTTP verb OPTIONS is not in
http.app.http_verbs.
Use HTTP verb in upper case if is not in http.app.http_verbs.
app:OPTIONS('', function(w, req)
end)
The app.options table has properties that are specific for the application.
app.options.root_path
-- '/'
These options remain throughout the life of the application. You can access
options during middleware initialization and in HTTP request object as
req.options.
The
appobject is not supposed to be shared with request handlers. Usereq.optionsinstead.
The following table describes the properties of the options object.
| Property | Description | Default |
|---|---|---|
| urls | Keeps url path mapping to request handler | {} |
The mounted event is fired on a sub-app, when it is mounted on a
parent app. The parent app is passed to the function.
Sub-app will:
- Not inherit the value of
optionsof the parent application.- Keep own
optionsunchanged.- Use any values from the
parentapplication as necessary.
The following example shows the use of mounted event.
local http = require 'http'
local greetings = http.app.new()
greetings:on('mounted', function(parent)
print('mounted')
end)
greetings:get('hi', function(w)
return w:write('hi')
end)
local app = http.app.new()
app:use(http.middleware.routing)
app:add('greetings/', greetings)
return app()
Mounts specified sub_app at the pattern: the application handles all
requests that match url path according to pattern.
This allows you to build modular or composable applications.
Example: composable application
Here is child application that we could reuse later.
-- child.lua
local http = require 'http'
local app = http.app.new()
app:get('hi', function(w, req)
return w:write('Hello World!\n')
end)
return app
The child app object is returned without a call for initialization.
The parent app builds url mapping for sub_app app.
The middlewares registered in
sub_appare ignored.
The route that matches any path that follows its path immediately after "/greetings/" will be handed to child application.
-- main.lua
local http = require 'http'
local app = http.app.new()
app:add('greetings/', require 'child')
app:use(http.middleware.routing)
return app()
The child application handles requests to /greetings/hi.
The main application can extend child application routing as necessary.
app:get('greetings/hallo', function(w, req)
return w:write('Hallo Welt!\n')
end)
By default the main application can not override patterns (the routing middleware treats this as an error, unless allow_path_override option).
local app = http.app.new {
allow_path_override = true
}
app:add('greetings/', require 'child')
app:get('greetings/hi', function(w, req)
return w:write('hey!\n')
end)
Routes an HTTP request regardless HTTP verb.
app:all('hi', function(w)
return w:write('hi')
end)
The actual HTTP verb can be obtained from request, e.g.:
app:all('hi', function(w, req)
return w:write(req.method)
end)
The following table describes the arguments.
| Argument | Description |
|---|---|
| pattern | The path for which the middleware function is invoked, it can be a string representing a path or a regular expression pattern. |
| route_name | The name used to address this route in reverse URL lookup. Optional. |
| function (following, options) | Middleware function. See below. |
| function (w, req) | Request handler function. See below. |
Middleware function (interceptor) can be used to impose pre-conditions on a route handler.
local function middleware(following, options)
return function(w, req)
return following(w, req)
end
end
The following object by convention corresponds to the next route handler,
which is a function(w, req); options is a table used to initialize
application and holds properties that are specific to application and shared
across.
The middleware function is called only once during application initialization, while returning function for route handling on each request routed.
The middleware function does not influence routing, that means if a call to
following has not been made, the processing is still considered successful,
an attempt to find a next matching route is not performed.
A return value of middleware function is ignored, however
return following(w, req) enables Lua's tail call, thus generally preferred.
Example: middleware function
Here is an example that shows the use of multiple middleware functions:
local function interceptor1(following, options)
return function(w, req)
print('before1')
following(w, req)
print('after1')
end
end
local function interceptor2(following, options)
return function(w, req)
print('before2')
following(w, req)
print('after2')
end
end
app:all('hi', interceptor1, interceptor2, function(w, req)
print('hi')
return w:write(req.method)
end)
The interceptor1 and interceptor2 middleware functions intercept all
calls (one after another) to corresponding route handler function.
You can use multiple middleware functions, the execution order is from left to right. The above example prints:
before1
before2
hi
after2
after1
HTTP route / request handler function processes application logic and writes response if any.
local function handler(w, req)
return w:write('hi')
end
The w object by convention corresponds to HTTP response
writer, req to HTTP request. If req object is not used it can be
safely omitted.
local function handler(w)
return w:write('hi')
end
A return value of HTTP route handler function is ignored, however
return following(w, req) enables Lua's tail call, thus generally
preferred.
Returns an instance of a route, which can be used to further handle HTTP verbs.
app:route('hi')
:get(function(w, req)
-- respond to HTTP GET request
end)
:post(function(w)
-- respond to HTTP POST request
end)
Use app:route() to specify HTTP verb that does not have a valid map in
http.app.http_verbs.
app:route('hi')['OPTIONS'](function(w)
end)
Use app:route() to avoid duplicate routing patterns and potential typo
errors.
Mounts the specified middleware function. The middleware function is executed for each request served by the application.
local http = require 'http'
local app = http.app.new()
app:use(http.middleware.routing)
The req object represents the HTTP request and has properties for the
request method, path, HTTP headers, and so on. By convention, the object is
always referred to as req.
This property holds a reference to the instance of the application app.options.
Contains a string corresponding to the HTTP method of the request: GET, POST, PATCH, and so on.
Contains the path part of the request URL.
-- http://blog.example.com/api/v1/posts?q=lua
req.path
-- "/api/v1/posts"
This property is a table containing properties mapped to the named route “arguments” set by routing middleware.
app:get('{locale}/user/{user_id:i}', function(w, req)
return w:write(req:route_args.user_id)
end)
-- path: /en/user/123
req.route_args
-- {["locale"] = "en", ["user_id"] = "123"}
The req.route_args can have a reserved property route_name
if there is an associated name with the request handler.
app:get('{locale}/user/{user_id:i}', 'user', function(w, req)
end)
-- path: /en/user/123
req.route_args.route_name
-- "user"
If there is no route arguments, it is the empty table, {}.
This property is a table containing a property for each query string parameter (case-sensitive) in the route.
The value is a
stringfor a single occurrence of query parameter or atablefor multiple values.
-- http://blog.example.com/api/v1/posts?q=lua&page=2
req.query
-- {["q"] = "lua", ["page" = "2"]}
req.query.q
-- "lua"
req.query.page
-- "2"
Use req.query or req:parse_query().
app:get('', function(w, req)
local qs = req.query or req:parse_query()
-- ...
end)
If there is no query string, it is the empty table, {}.
-- http://blog.example.com/api/v1/posts
req.query
-- {}
The
req.querytable isnilunless you callreq:parse_query()first.
This property is a table containing a property for each HTTP header name (lowercase).
The value is a
stringfor a single occurrence of HTTP header or atablefor multiple values.
Use req.headers or req:parse_headers().
app:get('', function(w, req)
local headers = req.headers or req:parse_headers()
-- ...
end)
The
req.headerstable isnilunless you callreq:parse_headers()first.
Use req.headers to determine whenever the request is AJAX.
local headers = self.headers or self:parse_headers()
local is_ajax = headers['x-requested-with'] == 'XMLHttpRequest'
This property is a table that contains HTTP cookies (case-sensitive).
Use req.cookies or req:parse_cookie().
app:get('', function(w, req)
local cookies = req.cookies or req:parse_cookie()
-- ...
end)
The req:parse_cookie() uses an empty name if not specified.
-- Cookie:
req.cookies
-- {['']=''}
-- Cookie: abc
req.cookies
-- {['']='abc'}
Returns a table with mapped cookie name to value.
-- Cookie: a=1
req.cookies
-- {['a']='1'}
-- Cookie: a=1; b=2
req.cookies
-- {a='1', b='2'}
Supports cookie value with spaces.
-- Cookie: c1=a b; c2= ; c3=a ; c4= b
req.cookies
-- {['c1']='a b', ['c2']=' ', ['c3']='a ', ['c4']=' b'}
If there is no cookies, it is the empty table, {}.
The
req.cookiestable isnilunless you callreq:parse_cookies()first.
This property type depends on MIME type of incoming HTTP request.
| MIME Type | Value |
|---|---|
| application/x-www-form-urlencoded | a table that contains all the current request POST query arguments. |
| application/json | a table that corresponds to parsed JSON object, either array or map. |
| multipart/form-data | a string, in-memory request body data. |
Use req.body or req:parse_body().
local values = req.body or req:parse_body()
To force in-memory request bodies, set client_body_buffer_size to the same size value in client_max_body_size.
This function returns nil if the request body has zero size.
Parses HTTP query string.
See req.query.
Parses HTTP headers.
See req.headers.
Parses HTTP cookie header.
See req.cookies.
Parses HTTP body.
See req.body.
Returns multiple values representing various server parts.
-- http://blog.example.com/api/v1/posts?q=lua
local scheme, host, port = req:server_parts()
-- "http", "blog.example.com", "80"
Use req.server_parts() to build request URL authority part.
local authority = scheme .. '://' .. host
.. (port == '80' and '' or ':' .. port)
-- http://blog.example.com
The w object represents the HTTP response and has properties for the
response HTTP headers. By convention, the object is always referred to as w.
This property is a table that contains HTTP headers. The keys of the returned table are the header names (case-insensitive) and the values are the respective header values.
The value is a
stringfor a single occurrence of HTTP header or atablefor multiple values.
-- w.headers['X-Request-Count'] = '100'
w.headers['X-Request-Count']
-- "100"
w.headers['x-request-count']
-- "100"
Use table to set multiple values.
-- w.headers['Set-Cookie'] = {'a=1', 'b=2'}
w.headers['set-cookie']
-- {"a=1", "b=2"}
Use value
nilto remove corresponding HTTP response header.
Returns the status code which was sent to the client.
nilvalue indicates successful HTTP response.
Controls the HTTP status code that will be sent to the client when the headers get flushed.
w:set_status_code(403)
Sends a chunk c of the response body. This method may be called multiple
times to provide successive parts of the body.
The response body is omitted when the request is a HEAD request. The 204 and 304 responses must not include a body.
The behavior of this method depends on adapter in use.
| Adapter | Behavior |
|---|---|
| buffered | The w:write() calls are buffered. The headers and body is sent to the client when application will finish processing request. |
| stream | The first time w:write() is called, it will send the headers and the first chunk of the body to the client. |
This method sends the raw HTTP body and do not perform any body encodings.
Flushes response output to the client asynchronously.
This method returns immediately without waiting for output data to be written into the system send buffer.
This function has no effect in case of buffered adapter in use.
Adds a single string value for HTTP header.
w:addHeader('Set-Cookie', 'c=100')
If this header already exists it will add value so multiple headers withthe same name will be sent.
Sets specified cookie string by adding Set-Cookie HTTP header.
w:set_cookie(http.cookie.dump {
name = 'c', value = '100', http_only = true
})
w.headers['Set-Cookie']
-- c=100; HttpOnly
Use this method to delete cookie.
w:set_cookie(http.cookie.delete {name = 'c'})
w.headers['Set-Cookie']
-- c=; Expires=Thu, 01 Jan 1970 00:00:00 GMT
Redirects to the absolute URL with specified status that corresponds to an HTTP status code. If not specified, status defaults to “302 “Found”.
w:redirect('http://example.com')
Use this method together with routing mixin to redirect to named routes.
local mixin = require 'core.mixin'
local http = require 'http'
mixin(http.Request, http.mixins.routing)
app:get('', function(w, req)
return w:redirect(req:absolute_url_for('welcome'))
end)
app:get('welcome', 'welcome', function(w, req)
return w:write('Hello World!\n')
end)
An HTTP cookie (browser cookie) is a small piece of data that a server sends to the user's web browser. The browser may store it and send it back with the next request to the same server.
The http.cookie module represents the HTTP cookie.
Returns a string that represents the HTTP cookie per options provided.
http.cookie.dump {name='a', value='1'}
-- "a=1"
http.cookie.dump {name='a', value='1', path='/abc/'}
-- "a=1; Path=/abc/"
http.cookie.dump {name='a', value='1', domain='example.com'}
-- "a=1; Domain=example.com"
http.cookie.dump {name='a', value='1', expires=1423473707}
-- "a=1; Expires=Mon, 09 Feb 2015 09:21:47 GMT"
http.cookie.dump {name='a', value='1', max_age=600}
-- "a=1; Max-Age=600"
http.cookie.dump {name='a', value='1', same_site='Strict'}
-- "a=1; SameSite=Strict"
http.cookie.dump {name='a', value='1', http_only=true}
-- "a=1; HttpOnly"
http.cookie.dump {name='a', value='1', secure=true}
-- "a=1; Secure"
The following table describes the options.
| Property | Type | Description |
|---|---|---|
| name | string | Name of the cookie. |
| value | string | Cookie value. |
| path | string | Path for the cookie. |
| domain | string | Domain name for the cookie. |
| expires | number | Expiry date of the cookie in GMT. If not specified creates a session cookie. |
| max_age | number | The expiry time relative to the current time in milliseconds. |
| same_site | string | Can be used to disable third-party usage for a specific cookie. Either Lax or Strict. |
| http_only | boolean | Flags the cookie to be accessible only by the web server. |
| secure | boolean | Marks the cookie to be used with HTTPS only. |
Returns a string that corresponds to an empty expired cookie.
http.cookie.delete {name='a'}
-- a=; Expires=Thu, 01 Jan 1970 00:00:00 GMT
http.cookie.delete {name='a', path='/abc/'}
-- a=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/abc/
See options in http.cookie.dump.
Middleware functions are functions that have
access to the
response writer object (w),
the request object (req), the
following function, and the applicat
Content type
Image
Digest
sha256:fbbdfd194…
Size
36.1 MB
Last updated
about 4 years ago
docker pull akorn/lucid:dev-openresty-alpine