REST module
Introduction
The REST Framework is built to be either used on top of the Control layer or the server core itself. It remaps the µC/HTTP-server hooks or the applicative Control Layer hooks on a single hook per HTTP request method. This single method is called an HTTPs_REST_HOOK
.
The REST Framework is designed to ease the production of simple web service applications. HTTP requests are made to perform actions on web resources. Inside your REST framework, those resources are mapped to HTTPs_REST_RESOURCE
objects.
REST Resource
A resource is associated with a specific path. REST Resources are accessible through path pattern matching and specifies the method supported and the headers needed. Here's the structure defining them:
typedef struct https_rest_resource { const CPU_CHAR *PatternPtr; /* Access path to the resource ending with an EOF char. */ const HTTP_HDR_FIELD *HTTP_Hdrs; /* List of HTTP headers to keep. */ const CPU_SIZE_T HTTP_HdrsNbr; /* Number of HTTP headers in the list. */ const HTTPs_REST_METHOD_HOOKS MethodHooks; /* Hooks Configuration for the resource. */ } HTTPs_REST_RESOURCE;
Hooks Configuration
Each REST resource has its own set of hook functions. Like mentioned before, there is one hook per HTTP method, but the hook pointer can be set to DEF_NULL
in the configuration if not needed for the given resource.
typedef struct https_rest_method_hooks { HTTPs_REST_HOOK_FNCT Delete; HTTPs_REST_HOOK_FNCT Get; HTTPs_REST_HOOK_FNCT Head; HTTPs_REST_HOOK_FNCT Post; HTTPs_REST_HOOK_FNCT Put; } HTTPs_REST_METHOD_HOOKS;
Below is the prototype for REST hook functions:
typedef HTTPs_REST_HOOK_STATE (*HTTPs_REST_HOOK_FNCT) (const HTTPs_REST_RESOURCE *p_resource, const HTTPs_REST_MATCHED_URI *p_uri, const HTTPs_REST_STATE state, void **p_data, const HTTPs_INSTANCE *p_instance, HTTPs_CONN *p_conn, void *p_buf, const CPU_SIZE_T buf_size, CPU_SIZE_T *p_buf_size_used);
Each hook is map on multiple hooks of the server core or Control Layer, therefore the same REST hook will be called many times during an HTTP transaction processing. To differentiate at which moment the REST hook is being called, a state variable HTTPs_REST_STATE
is passed to the hook function. The table below presents a mapping of the HTTPS_REST_STATE
with the corresponding HTTP server hooks.
REST STATE | Hook Function in REST Module | HTTP Server Hook | Control Layer Application Hook |
---|---|---|---|
HTTPs_REST_STATE_INIT | HTTPsREST_Authenticate() | (HTTPs_HOOK_CFG *)->OnReqHook() | (HTTPs_CTRL_LAYER_APP_HOOKS *)->OnReq() |
HTTPs_REST_STATE_RX | HTTPsREST_RxBody() HTTPsREST_ReqRdySignal() | (HTTPs_HOOK_CFG *)->OnReqBodyRxHook() ( HTTPs_HOOK_CFG *)->OnReqRdySignalHook() with argument buf_size == 0 | (HTTPs_CTRL_LAYER_APP_HOOKS *)->OnReqBodyRx() ( HTTPs_CTRL_LAYER_APP_HOOKS *)->OnReqSignal() |
HTTPs_REST_STATE_TX | HTTPsREST_GetChunk() | (HTTPs_HOOK_CFG *)->OnRespChunkHook() | (HTTPs_CTRL_LAYER_APP_HOOKS *)->OnRespChunk() |
HTTPs_REST_STATE_CLOSE |
| (HTTPs_HOOK_CFG *)->OnTransCompleteHook() ( HTTPs_HOOK_CFG *)->OnConnCloseHook() | (HTTPs_CTRL_LAYER_APP_HOOKS *)->OnTransComplete() ( HTTPs_CTRL_LAYER_APP_HOOKS *)->OnConnClose() |
URI Pattern Matching
The URI pattern matching of this framework is quite primitive and shines by it's simplicity. The URI is divided in two sections: the separator and the path component. This leads to the following rules:
- The pattern string must start with a separator.
- The pattern string must end with a '\0'
- A wild card can only be used between a separator and another separator or at the end of the pattern string.
- A wild card will not match a separator char unless the wild card is the last component of the pattern string.
URI Path Separator
The separator within the pattern string is exactly the same that must be found according to rfc3986: '/'. This character cannot be escaped and can't be used in any other context.
URI Path Component
An URI path component is the element between two path separator or between a path separator and the end of the pattern string. This element can either be a wild card or text.
Wild Cards
The wild card begins with a { and ends with a }. A wild card cannot contain another wild card. The element between the curly brackets is used as the wild card token key.
URI Pattern and Match Examples:
REST pattern | Valid? | HTTP uri | Matches? | wild card(s) |
---|---|---|---|---|
/{foo} | yes | / | yes | foo="" |
/foo/bar | yes | foo="foo/bar" | ||
/{foo/{bar}} | no | |||
/{foo}/bar | yes | / | no | |
/foo/bar | yes | foo="foo" | ||
/foo/baz/bar | no | |||
/{foo/baz}/bar | yes | / | no | |
/foo/bar | yes | foo/baz="foo" | ||
/foo/baz/bar | no | |||
/{foo}/{bar} | yes | / | yes | foo="" bar="" |
/foo | yes | foo="foo" bar="" | ||
/foo/bar | yes | foo="foo" bar="bar" | ||
/foo/bar/baz | yes | foo="foo" bar="bar/baz" | ||
/{foo}/bar/{baz} | yes | / | no | |
/bar | no | |||
/foo/bar | yes | foo="foo" buz="" | ||
/foo/bar/baz/qux | yes | foo="foo" buz="buz/qux" | ||
/foo/bar | yes | / | no | |
/foo | no | |||
/foo/bar | yes | |||
/foo/bar/buz | no | |||
/buz/bar | no | |||
\foo | no |
Publish REST Resources
Once a resource has been created, in order to make a resource accessible through the REST Framework, two steps must be followed. The first one is to call the HTTPsREST_Publish()
function which will add the resource to the specified list. Then that list of resource is used in the configuration of the HTTP server.
void HTTPsREST_Publish (const HTTPs_REST_RESOURCE *p_resource,
CPU_INT32U list_ID,
HTTPs_REST_ERR *p_err);
This function can only be called after the HTTP server initialization and before the HTTP server start. Also, the HTTPsREST_Init()
function must be bound to the HTTP server or to the Control layer prior the initialization. Otherwise, the REST memory pools won't be initialized and the resource lists cannot be created.
REST Configuration
No matter which layer REST is bound to, the µC/HTTP-server core or the Control Layer, you need to provide a HTTPs_REST_CFG
structure. That structure is used to configure the REST framework behavior for that specific instance.
typedef struct https_rest_cfg { const CPU_INT32U ListID; } HTTPs_REST_CFG;
/* ********************************************************************************************************* * REST HOOKS CONFIGURATION * * Note(s): See template file http-s_rest_hook_cfg.c in uC-HTTP/Server/Add-on/REST directory. ********************************************************************************************************* */ const HTTs_REST_CFG HTTPs_REST_Cfg = { 0 }; /* 0 is the list ID number. The same used in the resources publishing. */ const HTTPs_HOOK_CFG HTTPs_REST_HookCfg = { HTTPsREST_Init, HTTPsREST_RxHeader, HTTPsREST_Authenticate, HTTPsREST_RxBody, HTTPsREST_ReqRdySignal, DEF_NULL, DEF_NULL, DEF_NULL, HTTPsREST_GetChunk, HTTPsREST_OnTransComplete, DEF_NULL, DEF_NULL, HTTPsREST_OnConnClosed }; /* ********************************************************************************************************* * HTTP SERVER INSTANCE CONFIGURATION ********************************************************************************************************* */ const HTTPs_CFG AppEx_HTTPsInstanceCfg = { ... /* *--------------------------------------------------------------------------------- * HOOK CONFIGURATION *--------------------------------------------------------------------------------- */ &HTTPs_REST_HookCfg, /* .HooksPtr */ &HTTPs_REST_Cfg, /* .Hooks_CfgPtr */ ... };
The REST Hook Configuration variable HTTPs_REST_HookCfg
and the variable HTTPs_REST_Cfg
are given in a template file inside the REST module source code (http-s_rest_hook_cfg.c/h). You can use it has-is for your REST application or you can make your own if you don't need all the hooks to be defined. In the above example, the REST module is used directly on top of the HTTP server core, if you want to use it with the Control Layer, a template Control Layer hooks configuration for REST is also given inside the Control Layer add-on module source code (http-s_ctrl_layer_rest_cfg.c/h).
How to Use REST Module
- Create your REST resource(s) in your application.
- Define the necessary hook functions for each HTTP method of each resource and set the hook configuration inside the REST resource declaration.
- Set the REST configuration for the server instance configuration or the Control Layer configuration.
- Use the API function HTTPsREST_Publish between the HTTPs_InstanceInit and HTTPs_InstanceStart to publish all your REST resources inside the global resources list.
Example
See the complete REST application available in the Example directory of the µC/HTTP-server product (uC-HTTP/Server/Examples/REST).