As mentioned in Joseph's excellent answer, your server is vulnerable to a path-traversal attack. If I send it a request with enough
..s in it, I can reach the root directory, and thus any directory from there.
E.g. if your web root is
/var/www/root, and I request the URI
../../usr/share/doc/bc/bc.html, I will get the HTML documentation of the
bc command from your PC. In fact, I can get any HTML document from your PC (including e.g. a
bookmarks.html from a browser).
You are asking (bold emphasis mine)
I'm looking for all kind of feedback, from style, code smells, anti-patterns, anything that can be improved is very welcome.
So, I will answer your question in a direction that you probably didn't intend but might be interesting for you anyway.
"Real" webservers support a lot of additional features, for example:
- Virtual Hosts
- Access Control
- Server-side scripting
to name just a few.
Obviously, your web server is only intended as a simple exercise, and all of these features require complex configuration files and massive machinery … or do they?
It turns out, there are actually some tiny web servers that support some or all of these features in a clever way. For example, Felix von Leitner (fefe)'s fnord (discontinued) and gatling, or ACME Labs' thttpd. Especially fefe's web servers employ Unix filesystem semantics in "interesting" ways to avoid needing any configuration files.
Configuring a Virtual Host in Gatling is easy: it's just a directory. Gatling does not serve files from the root of the web server directory, rather, it looks for a directory name that matches the
Host HTTP header. So, if a browser sends a
GET request for
Host: 192.168.1.1:80, Gatling will serve the file
If you have ever needed to configure Virtual Hosts in Apache, you will appreciate how simple this is:
mkdir -p 192.168.1.1:80/foo
Boom, you're done.
Access Control is a little trickier. But all three of the web servers I mentioned, have a really neat security feature that I wish more web servers had. Most web servers only care whether they themselves are allowed to read the file they are serving. However, thttpd, fnord, and gatling will only serve files that are explicitly world-readable, and they will only serve from directories that are explicitly word-accessible. They will also only generate directory listings for directories that are world-readable and will only show files within that listing that are world-readable.
It is sometimes surprising to people when web servers make files readable to the world that are not world-readable.
Note that this would also at least somewhat alleviate the path-traversal attack, since now I would only be able to access world-readable files in world-accessible directories.
In Gatling, any file that is executable will not be served as-is, but it will instead be executed, and the output of that file will be served. Specifically, it supports a subset of CGI (RFC 3875).
So, all you need to do to set up scripting in gatling, is
In Gatling, symbolic links signify redirects. Remember that the target of a symbolic link is just a path. It doesn't actually have to exist.
So, if you want to set up a redirect from
https://google.com/, the way you would do that in Gatling is simply this:
ln -s https://google.com/ search.html
Again, compare this to redirects in Apache, or in a typical routing engine of a typical web framework.
At least Gatling and thttpd also support compression. I.e. if the client indicates that it supports deflate compression, and it requests the path
/foo/bar/baz.html, they will first look for a file named
/foo/bar/baz.html.gz and serve that if it exists.
These are just a couple of ideas how to improve and extend your tiny web server. Most of these are additional features, and thus not really in scope for a simply Code Review, but I believe that at least the "only serve world-readable files out of world-accessible directories" part would be a worthwhile addition and increase the security and usability. (Of course, you also need to fix the path-traversal attack identified by Joseph.)