Tags:
I think that before entering in any specific feature, is interesting to show how the basic http server was made.
Yes and No.
When I started writing, I really thought about start writing my TCP library, HTTP protocol handler, etc. Luckily some lucidity came to me and I decided to look for a basic http server in D and I found a very nice one - vibe.d
Disclaimer: As I'm just learning D, I'm pretty inconsequent writing that.
Suddenly I wasn't only learning D, but also vibe.d and it was easier than I thought. (This code is based on vibe.d tutorial)
shared static this()
{
auto settings = new HTTPServerSettings;
settings.port = 8080;
settings.bindAddresses = ["::1", "0.0.0.0"];
listenHTTP(settings, &handler);
}
HTTP server running, checked! Now let's implement the handler
void handler(HTTPServerRequest req, HTTPServerResponse res)
{
string s = req.path;
string [] items = split(req.path, "/");
//At least 3 items -> first empty, second command, thrird+ parameters
if( items.length < 3 ) {
list_posts("views/", res);
return;
}
if(items[1] == "view") {
view(items[2..$], res);
} else {
res.writeBody("Unknown path: " ~ req.path, "text/html; charset=UTF-8");
}
}
Pretty simple and basic. I'm getting the Request path and parsing to check if I'm receiving a command, otherwise listing posts.
Listing posts is really just a directory lookup. Let's check the implementation
void list_posts(string base_dir, HTTPServerResponse res) {
string buffer;
buffer = "<html><body>";
buffer ~= "<h1>Thiago Cafe welcome you !</h1>";
buffer ~= "<table border>";
buffer ~= "<thead><tr><th>List of posts</th></tr></thead><tbody>";
foreach(DirEntry dir; base_dir.dirEntries(SpanMode.shallow)) {
if( dir.isFile() && dir.name[$-3..$] == ".md" ) {
auto fname = dir.name[base_dir.length..$-3];
buffer ~= "<tr><td><a href='view/" ~ fname ~ "'>" ~ fname ~ "</a></td></tr>";
}
}
buffer ~= "</tbody></table>";
buffer ~= "</html>";
res.writeBody(buffer, "text/html; charset=UTF-8");
}
There are some interesting things here that worth to be seen
This is a classic recursive operation. The way it's implemented in D is quite charming.
base_dir.dirEntries(SpanMode.shallow)
SpanMode could be
view(items[2..$], res);
auto fname = dir.name[base_dir.length..$-3];
Two notable things there:
This is really, really thrilling.
Here we're going to see the command view, that renders the markdown.
void view(string [] params, HTTPServerResponse res) {
string file_name = "views/" ~ params[0] ~ ".md";
if( ! file_name.exists ) {
res.writeBody("Post not found!", "text/html; charset=UTF-8");
}
string buffer = file_name.readText;
string md_buffer = render_md(buffer);
res.writeBody(md_buffer, "text/html; charset=UTF-8");
}
string render_md(string buffer) {
import vibe.textfilter.markdown : filterMarkdown;
return filterMarkdown(buffer);
}
It's pretty disappointing seeing that the markdown was already there, almost no work to do, but...
string buffer = file_name.readText;
This is interesting, file_name is a string! So, string has a method readText that opens a file with the string name and reads the context ?
The answer is: No. readText is not a class function.
One incredibly nice feature of this language is that a function can work as a prototype. Let me explain better.
int toInt(string p);
toInt("123") -> returns int: 123
"123".toInt -> returns int: 123
I can use type.function if the function's first parameter is the prefixed type. Amazing !!!!
Just to show how powerful it is, if I'm using template, instead of a concrete type, my function will become a prototype to, virtually, anything.
TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
copy(source_string, target_string); //Works!
source_string.copy(target_string); //Works!!
source_array.copy(target_array); //Works!!!
Really amazing.
Any comments are very welcome!
Message me on twitter! @thiedri