sort in order: directory or non-directory, filename (case-sensitive).
show filetypes after filename:
- / for directory
- @ for symlink
- | for pipe
- = for socket
- etc
The approach with lastp only works if we only go back one dir at a time.
Of course, this cannot be assumed, so we traverse the path backwards
looking for the previous /.
Compiled against musl, quark will not work as musl needs the presence
of procfs to process paths in realpath().
We could wait for it to be implemented[0] or also notice that we don't
want to overengineer the target-resolving. I don't think it's very
suckless if we deploy such a huge infrastructure to resolve paths.
To counteract this and given there are no good solutions available, I
set out to write the function normabspath(), which normalizes an
absolute path.
It is idempotent and works on the buffer passed to it. We don't need a
target, as the resulting resolved path is guaranteed to be of equal
length or shorter. This requires a memcpy in our case before calling it,
but I see it as a nice demonstration of the possibilities and it might
prove to be useful for other projects.
Not requiring a target buffer (that needs to have its length specified),
the one-string-call also simplifies the calling semantics drasticly.
With this function in place, quark works with musl. Statically linked,
stripped and with -Os, it only weighs 102K.
[0]: http://www.openwall.com/lists/musl/2016/11/03/5
The aim was to write quark without any mallocs. This was successful, but
proved to be a bit ugly looking at how we construct data to be sent.
Before this change, we had static buffers in each function that needed
them and filled them up, possibly risking overflow.
After that, we sent them off using our own function sendbuffer(), which
in itself represented a buffering mechanism.
Using dprintf, which is POSIX 2008, we can send things off directly,
with no need for sendbuffer() or buffers for these things.
This way we can factor out sendbuffer(), dropping a few more LOCs.
Thanks Hiltjo for the suggestion!
I noticed that the data structures didn't allow a flexible handling of
the code while trying to extend it to support if-modified-since-responses.
To tackle this, I refactored the data structures and proceeded to
rewrite the server from the ground up, implementing all present features
plus fixing a lot of bugs and introducing the 304 header handling as
requested by many people.
Please report bugs if you find them.
While at it, I refactored the build system as well and updated all
surrounding files respectively.
Roughly 700 LOC (half of the old quark on the Hiltjo branch) in size,
this rewrite supports partial content and other good stuff that will
make it fun again to use quark for simple static purposes.
The error checking is rigorous and strict and it will report proper
error codes back to the client whenever there was a problem or the
request was invalid in some way.
A cool feature is the support for listening on a UNIX-domain socket,
which will in the long run allow us to solve problems with virtual hosts
and other things in separate programs. But until then, this should be
robust enough for most use-cases.
This resets quark's version to 0, but this was no problem as there
haven't been any quark releases yet.
Feedback is appreciated.
It was sad to see that quark never got the attention it deserved in my
opinion. However, there were good reasons why that was the case.
The project lost focus by trying to add CGI support, which in all
fairness worked only half of the time.
For the rest of the use cases, a static server to make it dead simple to
publish a directory, it was also pretty bad, given it does not support
partial content. Seeking in a mp3 was impossible and it was very
frustrating.
Long ago we discussed in the team how exciting it would be to test out
new concepts of having a web server that listens on a UNIX-domain
socket, potentially allowing new concepts for realizing virtual hosts
and other things.
It took me half a year to make the decision to rewrite quark, so it is
now time to purge the repo and push the initial commit.
Change the behavior of docroot, which is now used as a prefix path for
all file operations related to static files. And add chrootdir, which is
just the old docroot behavior and allows to control the path into which
quark will chroot.
Not having properly distinct configuration variables for chroot,
document root and CGI root was specially annoying since commit 2822488
which allowed users to retrieve the CGI script or binary by just
guessing its path, since quark was chrooting into docroot before
anything else, and thus the CGI script/binary was in the user accessible
path.
This is implemented by moving the reqbuf buffer in the middle of a
bigger buffer, reqpath. That buffer contains the value of docroot at its
beginning and reqbuf simply points to the first byte after this value.
This reverts commit 68f51ac37a.
The idea is good, but we just didn't yet get the right format
for the reading-cycle, which effectively keeps offset at 0
when it's all done in one read.
Let's call it a day and get back to the drawing-boards
tomorrow.
CGI applications can specify a HTTP status to output with the Status:
header. For simplicity the CGI application must use this header on the
first line. With this change cloning git repositories over HTTP with
cgit works.
in config.mk specify _GNU_SOURCE and _POSIX_C_SOURCE instead of
_GNU_SOURCE, this is for getline().
- set some more environment variables (PATH_INFO for example is used by
cgit). Also set REMOTE_ADDR, SERVER_PORT, SERVER_SOFTWARE.
- when a file is requested in cgi mode serve it, if it doesn't exist,
pass it to the CGI script (needed for cgit image/CSS).
If the client decides not to listen, it's not that much of a problem.
Don't flood the logs with "Broken pipe"-messages by silently letting
this "error" pass.
Streaming a file (through mplayer for instance), the socket would
block, because mplayer fills its buffer sequentially.
We would've never gotten to a write(.., n) == n.
Instead, do it like we read from files and accept the fact clients
can accept data chunk-wise, too.
The reason why this error went unnoticed is that I added a faulty
printf-directive (%ls for ssize_t), which silently produced
no output.
Thanks to sin for fixing the %ls -> %zd error, as it made me look
at the code again.
When the client requests a hidden file, we forbid access.
401 is mostly used when a login is required and hasn't been provided.
Thus, given we don't offer a login-prompt to access hidden and bogus
files but categorically reject them, 403 makes more sense here.
Compiling quark against musl slowed down request-times considerably.
After further analysis, I found out that the library does a DNS-
request on each address passed to getnameinfo.
Given we chroot into a folder, the /etc/resolv.conf was missing,
which led to the really long response-times (~3-5s).
After hardlinking the /etc/resolv.conf inside the chroot, the
times dropped to ~200ms, as now the library knew which NS to
contact directly.
This obviously isn't fast enough.
Thanks to Hiltjo's useful tips I rewrote the section using
inet_ntop (POSIX 2001).
Now the response-times are back to 1-2ms and we don't need
to copy /etc/resolv.conf everywhere we go.
FYI: This is not a bug in musl, but rather different behaviour.
Now it should work. It doesn't make much sense to tweak the default
mime-type, given octet-stream is the default and there are
no real alternatives which would make sense.
Thanks Hiltjo and sin!
For a server, a solid logging-facility is very important.
Reading quark's logs isn't very pleasant, given it prints out
more vocabulary than necessary.
This patch fixes this problem by bringing the logs into a
readable and even parsable form:
-timestamp- -keyword- [-message-]
Keywords now take the role as carriers of information.
Instead of writing that we're redirecting, we just
print a line with a status 304 and what the requested
location was. The subsequent 200-response shows the
redirected location.
This also makes clearer what happens in the background.
The tab-separation allows easy parsing.
You can't handle SIGKILL. Given we have control over which
signals are passed to the sighandler, it's enough to catch SIGCHLD
and do standard behaviour than building a big switch for that.
This is the first step for unifying the return-values inside quark.
1: error
0: success
-1: not found (non-fatal, for instance, if you run getresentry
on a non-essential entry)
Instead of providing a function for each entry-type, use a small
static lookup-table and one function to rule them all.
In the future, in case the list grows, we might think about
implementing it with a small hash-lookup, but currently,
it's easy enough synchronizing the enum and the array.
While at it, improve the logic in the code itself
by using logical OR's instead of AND's.
On each request send "last-modified" header with the modification time of
each file. the client will store this field and send it on the next
request(s). On the server check the "if-modified-since" field with the
modified date sent by the client, depending on this send all the data or
the "304 not modified" http status.
CAVEAT: it is assumed the exact field will be send by the client, no date/time
conversion is done for simplicity sake, as far as that's possible with http ;)