DEV Community

Cover image for Step-sharing scheduling Coroutine HTTP Server

Posted on

Step-sharing scheduling Coroutine HTTP Server

This article introduces an open source API server named Medge. Medge is an HTTP server, and its authors currently position it as an API server.

In Medge, we can implement API logic through the scripting language Melang. Melang is a step-sharing scheduling coroutine language, in which each script task is regarded as a coroutine running inside the same single thread, and is scheduled according to the execution step. Therefore, during the development of upper-level logic, developers do not need to consider the problem of coroutine scheduling, nor do they need to consider when to call certain functions to give up CPU execution rights.

Now let’s get to the point and see a complete simple usage example.

Image description
After we have installed Medge, we can execute the following command to start it.

Enter fullscreen mode Exit fullscreen mode

We can also use the following parameters to start Medge.

medge -p 8080 -d /opt/medge/ -w 1
Enter fullscreen mode Exit fullscreen mode
  • -p is used to specify the TCP port number that the HTTP server listens to, if not set, the default is 80. In this example HTTP listens on
  • -d is used to specify the unified entry directory path of the API service, in this case it is /opt/medge. We will explain the structure of this directory later. If this parameter is not specified, it defaults to /opt/medge.
  • -w is used to specify the number of worker processes for HTTP. Defaults to 1 if not specified.

Now, the server has been started, but we have not given the specific processing logic of this API service. Let’s write the API service code.

As mentioned above, the -d parameter of Medge specifies the unified entry directory of the API service. Here is the directory tree under this path:

|- /opt/medge/
    |- service_1/
        |- entry.m
        |- ...
    |- service_2/
        |- entry.m
        |- ...
Enter fullscreen mode Exit fullscreen mode

We can see that there are two services in the directory /opt/medge named: service_1 and service_2, and each service has a script file named entry.m. entry.m is the entry file of the service, that is, when Medge receives an HTTP request and finds the corresponding service through the HTTP header Host, it will immediately execute the entry.m script of the service for processing.

In our example, there is only one API service, so the directory tree in this example is:

|- /opt/medge/
        |- entry.m
        |- index.m
Enter fullscreen mode Exit fullscreen mode

Let’s implement entry.m and index.m.

 * Implement a simple controller.
 * There are three variable injected in this script task:
 *   1. Req. Its prototype is:
 *       Req {
 *           method; //string  e.g. GET POST DELETE ...
 *           version; //string e.g. HTTP/1.0 HTTP/1.1
 *           uri; //string e.g. /index/index
 *           args; //an key-value array, e.g. ["key":"val", ...]
 *           headers; //an key-value array, e.g. ["Content-Type":"application/json", ...]
 *           body; //string
 *       }
 *    2. Resp. Its prototype is:
 *        Resp {
 *            version; //same as Req's version
 *            code; //integer  e.g. 200
 *            headers; //same as Req's headers
 *            body; //same as Req's body
 *        }
 *.   3. Basedir. A string of the base directory path. (Not used in this example)

#include "@/index.m"

str = Import('str');
sys = Import('sys');

uri = str.slice(Req.uri, '/');
ctlr = str.capitalize(uri[0]);
o = $ctlr;
if (sys.has(o, uri[1]) != 'method') {
  Resp.code = 404;
} else {
  o.__action__ = uri[1];
  Resp.body = o.__action__();
  Resp.headers['Content-Length'] = str.strlen(Resp.body);
Enter fullscreen mode Exit fullscreen mode

Json = Import('json');

Index {
    @index() {
        Resp.headers['Content-Type'] = 'application/json';
        return Json.encode(['code': 200, 'msg': 'OK']);
Enter fullscreen mode Exit fullscreen mode

There are some comments in front of entry.m, which mentions that each request will inject three variables into the entry.m named Req, Resp and Basedir.

Req contains request-related information, such as: method, version, resource path, parameters, request headers and request body.

Resp contains response-related information, such as: version (default is the same as the request version), response status code (default is 200), response headers and response body.

Basedir is a string whose content is the path specified by the Medge -d parameter.

In this example, entry.m implements a very simple controller in the MVC framework, which finds the corresponding processing class and class method according to the URI, and calls them to process the HTTP request.

In this example, we will send the following HTTP request:

curl -v -H "Host:"
Enter fullscreen mode Exit fullscreen mode

Therefore, the URI /index/index will be split first in the entry.m, and the first index will be capitalized and used as the class name to find out the corresponding class which is named Index in the current scripting environment.

After the class Index is found and instantiated, call its index (that is, the second index in the URI) method to process the request.

After executing the curl command, we will see its output roughly:

*   Trying
* Connected to ( port 8080 (#0)
> GET /index/index HTTP/1.1
> Host:
> User-Agent: curl/7.81.0
> Accept: */*
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Length: 23
< Content-Type: application/json
* Connection #0 to host left intact
Enter fullscreen mode Exit fullscreen mode

The End

At present, Medge is still an experimental project, and there are many functions need to be implemented. For example, it only supports HTTP/1.0 HTTP/1.1 version without keepalive at present, and does not support https yet. In addition, Melang scripts currently only support MySQL databases, and there are no corresponding HTTP library functions or classes for developers to send HTTP request in Melang.

If you are interested in Medge, please visit its Github for more detail.

Thanks for reading!

Top comments (0)