Step-sharing scheduling Coroutine HTTP Server
Tags: webdev, programming, opensource, backend
Published: 2023-08-31 16:18:33

This article introduces an open source API server named [Medge](https://github.com/Water-Melon/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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxvibwh20iojxrcg2nlz.png)
After we have installed Medge, we can execute the following command to start it.
```
medge
```
We can also use the following parameters to start Medge.
```
medge -p 8080 -d /opt/medge/ -w 1
```
- `-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 `0.0.0.0:8080`.
- `-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
        |- ...
```
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/
    |- test.com/
        |- entry.m
        |- index.m
```
Let’s implement `entry.m` and `index.m`.
```
//entry.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);
}
```
```
//index.m

Json = Import('json');

Index {
    @index() {
        Resp.headers['Content-Type'] = 'application/json';
        return Json.encode(['code': 200, 'msg': 'OK']);
    }
}
```
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: test.com" http://127.0.0.1:8080/index/index
```
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 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /index/index HTTP/1.1
> Host: test.com
> 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 127.0.0.1 left intact
{"code":200,"msg":"OK"}
```

## 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](https://github.com/Water-Melon/Medge) for more detail.

Thanks for reading!