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:

REST Resource Structure
 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. 

REST Hooks Configuration Structure
 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:

Definition of HTTPs_REST_HOOK_FNCT
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 STATEHook Function in REST ModuleHTTP Server HookControl 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

HTTPsREST_OnTransComplete()
HTTPsREST_OnConnClosed()

(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 patternValid?HTTP uriMatches?wild card(s)
/{foo}yes/yesfoo=""
/foo/baryesfoo="foo/bar"
/{foo/{bar}}no   
/{foo}/bar

yes

/no 
/foo/baryesfoo="foo"
/foo/baz/barno 
/{foo/baz}/bar

yes

/no 
/foo/baryesfoo/baz="foo"
/foo/baz/barno 
/{foo}/{bar}


yes


/yes

foo=""

bar=""

/fooyes

foo="foo"

bar=""

/foo/baryes

foo="foo"

bar="bar"

/foo/bar/bazyes

foo="foo"

bar="bar/baz"

/{foo}/bar/{baz}


yes


 
/no 
/barno 
/foo/baryes

foo="foo"

buz=""

/foo/bar/baz/quxyes

foo="foo"

buz="buz/qux"

/foo/baryes/no 
  /foono 
  /foo/baryes 
  /foo/bar/buzno 
  /buz/barno 
\foono   

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.

REST Configuration Structure
typedef  struct  https_rest_cfg {
    const  CPU_INT32U   ListID;
}  HTTPs_REST_CFG;
Server Instance Configuration Example for REST Application
/*
*********************************************************************************************************
*                                         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

  1. Create your REST resource(s) in your application.
  2. Define the necessary hook functions for each HTTP method of each resource and set the hook configuration inside the REST resource declaration.
  3. Set the REST configuration for the server instance configuration or the Control Layer configuration.
  4. 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).