Plugins

It is possible to create custom endpoints by writing an operating system library (.so) and prestd can load it when starting the server (api).

We use the plugin system of the Go language to load the lib, unfortunately it doesn't work well with Microsoft Windows yet - if you are working with prestd on Windows the plugin endpoint will not exist.

The plugin endpoint has the following default: /_PLUGIN/{file}/{func}

When starting the prestd server and there is a plugin in the ./lib folder they are automatically compiled and loaded when accessing their respective endpoint for the first time.

Change where the libraries will be: PREST_PLUGINPATH is the name of the environment variable that has this purpose, by default it comes with the value ./lib.

or via toml:

pluginpath = ./lib

Naming patterns

The plugin endpoint (/_PLUGIN/{file}/{func}) receives two parameters:

  • File name: The name of the file without the extension ({file})
  • Function name: The name of the function ({func})

File name

The file name will be used on the endpoint to identify which library will be loaded when it receives the first access.

After the first access the library will not be loaded again, it will only be executed, i.e., if the file (.so) is changed after the first execution it will have no effect because it has already been loaded.

Function name

When talking about a compiled library we have no way of identifying its functions. Given this characteristic we have defined some name and behavior patterns to develop libraries for prestd.

function name: {HTTP Method}{Function Name}Handler

  • {HTTP Method}: The HTTP method that the function will be called for (in upper case letters)
  • {Function Name}: The name of the function that will be called
  • Handler: The suffix of the function name - it is always Handler

fmt.Sprintf("%s%sHandler", r.Method, funcName)

Process of building

In the first version of the prestd plugin system we are working with Go code.

This doesn't mean that prestd doesn't read plugins (library .so) written in other technologies (e.g. c, cpp, java and ...). The automatic constructor is designed to work with Go code, in the future we will write for other technologies.

Example

  • Source code name: ./lib/src/hello.go
  • Library file name: ./lib/hello.so
  • Function name: GETHelloHandler
  • Endpoint: /_PLUGIN/hello/Hello
  • Verb HTTP: GET
 1// all plugins must have their package name as `main`
 2// each plugin is isolated at compile time
 3package main
 4
 5import (
 6 "encoding/json"
 7)
 8
 9var (
10 // HTTPVars route variables for the current request
11 HTTPVars map[string]string
12 // URLQuery parses RawQuery and returns the corresponding values
13 URLQuery map[string][]string
14)
15
16// Response return structure of the get method
17type Response struct {
18 HTTPVars map[string]string   `json:"http_vars"`
19 URLQuery map[string][]string `json:"url_query"`
20 MSG      string              `json:"msg"`
21}
22
23// GETHelloHandler plugin
24// function is invoked via [go language plugin](https://pkg.go.dev/plugin),
25// it is not possible to pass parameters, that's why there are global
26// variables to receive data from http protocol
27//
28// BUILD:
29// go build -o lib/hello.so -buildmode=plugin lib/src/hello.go
30func GETHelloHandler() (ret string) {
31 resp := Response{
32  HTTPVars: HTTPVars,
33  URLQuery: URLQuery,
34  MSG:      "Hello plugin caller!",
35 }
36 respJSON, err := json.Marshal(resp)
37 if err != nil {
38  return
39 }
40 ret = string(respJSON)
41 return
42}

Request:

1GET /_PLUGIN/hello/Hello?abc=123 HTTP/1.1

Response:

 1{
 2  "http_vars": {
 3    "file": "hello",
 4    "func": "Hello"
 5  },
 6  "url_query": {
 7    "abc": [
 8      "123"
 9    ]
10  },
11  "msg": "Hello plugin caller!"
12}