#Setup
- install Minimacy, as described in the [Setup section](page://2000.100.100). Build Minimacy or use the Release distribution.
- clone the demo repository:
>git clone https://github.com/ambermind/http-time-server.git
We assume now that:
- [minimacy] is the full path of the Minimacy directory, it contains at least these subfolders: ./bin ./rom ./programs
- [minimacyBinary] is the full path of Minimacy binary (minimacy.exe on Windows, minimacy on Unix, minimacyMac on MacOS).
- [http-time-server] is the full path of this example on your disk
Then, you may start this example with:
>[minimacyBinary] [http-time-server]/boot.mcy
Alternately, you may replace the content of [minimacy]/programs with the content of [http-time-server]. It will overwrite 'boot.mcy'. You should create a dedicated copy of Minimacy in order to do so.
#Usage
Once started, the terminal should display:
>Local http server started on port 1234. Open http://127.0.0.1:1234
Open your favorite browser, and type [http://127.0.0.1:1234](http://127.0.0.1:1234)
You get a basic html web page showing the current time, and providing alternative links:
[
](html)
The Json view looks like this:
[
](html)
The analog view looks like this:
[
](html)
The digital view looks like this:
[
](html)
#Implementation
The Http server is started with the following code:
fun main()=
let 1234 -> port in
let httpSrvStart(
nil, // no tls key => we use http
nil, // no ip local address => the server is bound to all local addresses
port,
lambda(h, verb, uri)=
match uri with // call the rendering function depending on the uri. Each rendering functions returns a string
"/" -> timeText(h),
"/json" -> timeJson(h),
"/digital" -> timeDigital(h),
"/analog" -> timeAnalog(h)
) -> httpsrv in
echoLn if httpsrv==nil then "Cannot start http server"
else strFormat("Local http server started on port *. Open http://127.0.0.1:*", port, port);;
The last argument of [[httpSrvStart]] is a lambda function which will be called on each http request. It is expected to return the html response body as a string. Its parameter are:
- h: a handler on this request, it may be used to retrieve http parameters, configure the response, for example to set a code (200 is the default).
- verb: the http verb of the request
- uri: the uri of the request
We use a [[match]] to route the different uris to their dedicated function. Each of these function will return the full html response body.
The main uri returns the current time and the 3 links:
fun timeText(h) = strBuild([
fullDate(time()),
"
Alternative views: json analog digital"
]);;
[[strBuild]] is a convenient way to concatenate many elements into a single string. As you can see we build a rough html response, without header or anything else.
For the Json we need to specify the mime-type, calling [[httpSrvSetHeader]] on the request handler 'h'.
fun timeJson(h) =
httpSrvSetHeader(h, "Content-Type", "application/json");
let time() -> t in
let date(t) -> [year, month, day, weekDay, hour, minute, second] in
jsonEncodePretty(
jsonObject
["timestamp", jsonInt(t)]::
["year", jsonInt(year)]::
["month", jsonInt(month)]::
["day", jsonInt(day)]::
["weekDay", jsonInt(weekDay)]::
["hour", jsonInt(hour)]::
["minute", jsonInt(minute)]::
["second", jsonInt(second)]::
nil);;
Then we create a jsonObject with a list of [field, json_value]. We call [[jsonEncodePretty]] to convert this Json object into a readable string.
For the Analog page, we perform some graphics. We want to return a 400x400 pixels bitmap as a png file.
In order to draw the hands, we need a conversion function which returns x,y coordinates from angular, radius:
// coordinates transformation, from angular to xy
fun _xyCoordinates(scale, alpha) = [200 + intFromFloat(scale *. sin(alpha)), 200 - intFromFloat(scale *. cos(alpha))];;
Minimacy has precise typing of numbers: integer are not floating numbers. Alpha and scale are float s, but coordinates are integers. '*.', cos, sin work with floats while '+' and '-' operate with integers.
Coordinates are returned as a tuple of two integers.
Then we declare a function to draw a single hand as a triangle, with an orientation and a size (aka scale):
// hands are drawn as green triangles
fun _drawHand(bmp, scale, alpha) =
let _xyCoordinates(scale, alpha) -> [x1, y1] in
let _xyCoordinates(20., alpha+. 3.) -> [x2, y2] in
let _xyCoordinates(20., alpha-. 3.) -> [x3, y3] in (
bitmapLine(bmp, x1, y1, x2, y2, 0x00ff00, nil);
bitmapLine(bmp, x2, y2, x3, y3, 0x00ff00, nil);
bitmapLine(bmp, x3, y3, x1, y1, 0x00ff00, nil)
);;
Colors are provided as 32 bits ARGB integers. Here 0x00ff00 is pure green at full intensity.
Now we can write the function which specifies "image/png" as the mime-type, get the current angulars of the hour and minute hands.
fun timeAnalog(h) =
httpSrvSetHeader(h, "Content-Type", "image/png");
let date(time()) -> [year, month, day, weekDay, hour, minute, second] in
let floatFromInt(minute)*. 2. *. pi /. 60. -> minute in // convert minutes to radiant angle
let floatFromInt(hour)*. 2. *. pi /. 12. +. (minute /. 12.) -> hour in // convert hours to radiant angle
let bitmapCreate(400, 400, 0x404040) -> bmp in (
bitmapCircle(bmp, 20, 20, 360, 360, 0x00ff00, nil);
_drawHand(bmp, 100., hour);
_drawHand(bmp, 150., minute);
pngFromBitmap(bmp, false)
);;
The last function [[pngFromBitmap]] takes a bitmap and a boolean and returns the png encoding as a string. The boolean indicates whether the png contains an alpha layer.
For the digital page, we first need a Font to draw the text.
const Font=fontFromBitmap(bitmapFromPng(load("rsc/fonts/Arial_32_256.png")));;
We load such a font from a bitmap you will found in ./minimacy/rom/rsc/fonts/Arial_32_256.png
Fonction composition could be read like this:
- first we [[load]] the file
- we assume it is a png file and call [[bitmapFromPng]] to create a bitmap from it
- finally we call [[fontFromBitmap]] to make a font from it.
More information about these bitmaps in [core.2d.font](page://ref.core.2d.font) and more specificaly in [[fontFromBitmap]]. You may create your own font bitmaps using [FontMaker](https://minimacy.net/book/#/font_maker).
Once the font is ready, we can use it to create the digital reply:
fun timeDigital(h) =
httpSrvSetHeader(h, "Content-Type", "image/png");
let bitmapCreate(500, 40, 0xe0e0e0) -> bmp in (
bitmapText(bmp, 250, 20, ALIGN_CENTER|ALIGN_MIDDLE, fullDate(time()), Font, 0x404040, nil);
pngFromBitmap(bmp, false)
);;
We specify "image/png" as the mime-type, then create a bitmap 500x40 with a light gray background. We draw the date in dark gray. Finally we encode the bitmap as png, and return the resulting string.