Skip to content

Commit

Permalink
Implements lua scripting improvements
Browse files Browse the repository at this point in the history
* Updates gopher-lua dependency to fix [checkptr: unsafe pointer arithmetic](yuin/gopher-lua#254)
* Adds context path_param projection as table
* Adds request url_query projection as table
* Adds request url_raw_query getter and setter
* Adds request url_path getter and setter
* Adds response status_code getter and setter
* Implements header, path_param and url_query iteration
* Headers and url query return nil on missing key
* Passes indexed script params along with key-values
* Uses proper log module
* Makes 'print' builtin write into log
* Uses lua's RaiseError for error handling
* Returns less results instead of pushing lua.LNil
* Updates documentation and tests

See script_test.go for usage examples.

Signed-off-by: Alexander Yastrebov <[email protected]>
  • Loading branch information
AlexanderYastrebov committed Jul 11, 2020
1 parent da2ab78 commit 749a373
Show file tree
Hide file tree
Showing 12 changed files with 838 additions and 405 deletions.
70 changes: 56 additions & 14 deletions docs/reference/scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ current implementation supports [Lua 5.1](https://www.lua.org/manual/5.1/).

## Route filters

The lua scripts can be added to a route description with the lua() filter,
The lua scripts can be added to a route description with the `lua()` filter,
the first parameter for the filter is the script. This can be either a file
name (ending with `.lua`) or inline code, e.g. as

* file `lua("/path/to/file.lua")` - if a file path is not absolute, the path
is relative to skipper's working directory.
* inline `lua("function request(c, p); print(c.request.url); end")`

Any other additional parameters for the filter must be `key=value` strings.
These will be passed as table to the called functions as second parameter.
**NOTE**: Any parameter starting with "lua-" should not be used to pass
Any other additional parameters for the filter will be passed as
a second table parameter to the called functions.
> Any parameter starting with "lua-" should not be used to pass
values for the script - those will be used for configuring the filter.

## Script requirements
Expand All @@ -26,12 +26,24 @@ in the route as table like
```lua
-- route looks like
--
-- any: * -> lua("./test.lua", "myparam=foo", "other=bar") -> <shunt>
-- any: * -> lua("./test.lua", "myparam=foo", "other=bar", "justkey") -> <shunt>
--
function request(ctx, params)
print(ctx.request.method .. " " .. ctx.request.url .. " -> " .. params.myparam)
print(params[1]) -- myparam=foo
print(params[2]) -- other=bar
print(params[3]) -- justkey
print(params[4]) -- nil
print(params.myparam) -- foo
print(params.other) -- bar
print(params.justkey) -- (empty string)
print(params.x) -- nil
end
```
> Parameter table allows index access as well as key-value access
## print builtin

Lua `print` builtin function writes skipper info log messages.

## Available lua modules

Expand Down Expand Up @@ -60,29 +72,35 @@ in the request and accessing it in the response will most likely fail and lead
to hard debuggable errors. Use the `ctx.state_bag` to propagate values from
`request` to `response` - and any other filter in the chain.

# Request
# Request and response

The `request()` function is run for an incoming request.
The `request()` function is run for an incoming request and `response()` for backend response.

## Headers

Request headers can be accessed by accessing the `ctx.request.header` map like
Request headers can be accessed via `ctx.request.header` table like
```lua
ua = ctx.request.header["user-agent"]
```
and iterated like
```lua
for k, v in ctx.request.header() do
print(k, "=", v);
end
```
> Headers table is a [functable](http://lua-users.org/wiki/FuncTables) that returns [iterator](https://www.lua.org/pil/7.1.html)
Header names are normalized by the `net/http` go module
[like usual](https://godoc.org/net/http#CanonicalHeaderKey). Setting a
header is done by assigning to the headers map. Setting a header to `nil` or
header is done by assigning to the headers table. Setting a header to `nil` or
an empty string deletes the header - setting to `nil` is preferred.

```lua
ctx.request.header["user-agent"] = "skipper.lua/0.0.1"
ctx.request.header["Authorization"] = nil -- delete authorization header
```

Response headers work the same way by accessing / assigning to
`ctx.response.header` - this is of course only valid in the `response()` phase.
Response headers `ctx.response.header` work the same way - this is of course only valid in the `response()` phase.

## Other request fields

Expand All @@ -97,6 +115,13 @@ Response headers work the same way by accessing / assigning to
* `proto` - (read only) something like "HTTP/1.1"
* `method` - (read only) request method, e.g. "GET" or "POST"
* `url` - (read/write) request URL as string
* `url_path` - (read/write) request URL path as string
* `url_query` - (read/write) request URL query parameter table, similar to headers
* `url_raw_query` - (read/write) encoded request URL query values, without '?' as string

## Other response fields

* `status_code` - (read/write) response status code as number, e.g. 200

## Serving requests from lua
Requests can be served with `ctx.serve(table)`, you must return after this
Expand All @@ -109,6 +134,13 @@ call. Possible keys for the table:
See also [redirect](#redirect) and [internal server error](#internal-server-error)
examples below

## Path parameters

Path parameters (if any) can be read via `ctx.path_param` table
```
Path("/api/:id") -> lua("function request(ctx, params); print(ctx.path_param.id); end") -> <shunt>
```

## StateBag

The state bag can be used to pass values from one filter to another in the same
Expand All @@ -126,8 +158,8 @@ end

# Examples

Note: the examples serve as examples. If there is a go based plugin available,
use that instead. The overhead of calling lua is 4-5 times slower than pure go.
>The examples serve as examples. If there is a go based plugin available,
use that instead. For overhead estimate see [benchmark](#Benchmark).

## OAuth2 token as basic auth password
```lua
Expand Down Expand Up @@ -200,6 +232,16 @@ function request(ctx, params)
end
```

## set request header from params
```lua
function request(ctx, params)
ctx.request.header[params[1]] = params[2]
if params[1]:lower() == "host" then
ctx.request.outgoing_host = params[2]
end
end
```

# Benchmark

## redirectTo vs lua redirect
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ require (
github.com/uber/jaeger-client-go v2.16.0+incompatible
github.com/uber/jaeger-lib v2.0.0+incompatible
github.com/yookoala/gofast v0.4.0
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036
github.com/yuin/gopher-lua v0.0.0-20200603152657-dc2b0ca8b37e
go.uber.org/atomic v1.4.0 // indirect
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
golang.org/x/net v0.0.0-20190628185345-da137c7871d7
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ github.com/yookoala/gofast v0.4.0 h1:dLBjghcsbbZNOEHN8N1X/gh9S6srmJed4WQfG7DlKwo
github.com/yookoala/gofast v0.4.0/go.mod h1:rfbkoKaQG1bnuTUZcmV3vAlnfpF4FTq8WbQJf2vcpg8=
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036 h1:1b6PAtenNyhsmo/NKXVe34h7JEZKva1YB/ne7K7mqKM=
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yuin/gopher-lua v0.0.0-20200603152657-dc2b0ca8b37e h1:oIpIX9VKxSCFrfjsKpluGbNPBGq9iNnT9crH781j9wY=
github.com/yuin/gopher-lua v0.0.0-20200603152657-dc2b0ca8b37e/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
Expand Down
234 changes: 0 additions & 234 deletions script/README.md

This file was deleted.

Loading

0 comments on commit 749a373

Please sign in to comment.