[Thiago Cafe] Programming is fun!

Blog basics

Created by thiago on 2016-06-28 00:25:23

Tags:

I think that before entering in any specific feature, is interesting to show how the basic http server was made.

Is it really from scratch ?

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

Talk is cheap. Show me the code!

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

Directory lookup

This is a classic recursive operation. The way it's implemented in D is quite charming.

base_dir.dirEntries(SpanMode.shallow)

SpanMode could be

Slices

view(items[2..$], res);
auto fname = dir.name[base_dir.length..$-3];

Two notable things there:

This is really, really thrilling.

View command, finally!

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

Tell me your opinion!

Reach me on Twitter - @thiedri