During the past year (2021), I managed to land two small fixes into Yaws, the Erlang webserver (well, technically not the only single major webserver written in Erlang, but probably the best known one in large-scale real-world production use).

I become aware of Yaws at around 2007, when I was looking into the benefits of concurrency-oriented programming in Erlang, partly as a platform to build a webserver (such as Yaws) on top.

At that time, Yaws got onto my radar primarily due to the claims made in the famous Apache vs. Yaws article.1 Since those days, I reached for Yaws on multiple occasions, in various projects, at times together with the Nitrogen web application development framework.

Yaws is still going strong, nowadays mature and extremely well tested. However, developing my little web application on top of it (this time on OpenBSD), and trying to get it properly integrated into the system, I stumbled upon a couple things to improve. Huge thanks to Steve Vinoski for reviewing and merging my patches, and of course for maintaining Yaws in general!

I saw the need for this cookie option (to be set for the sake of increased security against CSRF attacks), and soon discovered there was a feature request for it opened back in 2018, addressed by my trivial pull request #430.

The commit message:

Add support for setting the SameSite cookie option

Supported values are 'lax', 'none' and 'strict'.

Example:
yaws_api:set_cookie("s", Cookie, [http_only, {same_site, strict}, ...]).

Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite

Closes #321

Steve was diligent enough to extend my patch with some test coverage (which I sadly missed due to my lack of familiarity with the codebase) before merging it. Thanks!

Detect externally rotated logfiles

A bigger issue I had with Yaws (which, due to the lack of a ready-made port, I built from source first on OpenBSD 6.8) was that I wanted to treat its log files with the usual system-wide log rotation tool newsyslog(8). Even though Yaws supported log rotation, I found that it did not play nice together with external log rotators. In practice, this meant that if an external program moved or truncated the existing logfile currently in use by Yaws, bad things could happen (depending on the exact scenario), such as continuing to write to the wrong file (and those log entries eventually, possibly, silently getting lost).2

Using the built-in log rotation instead of newsyslog would have been okay, had Yaws included support for time-based log rotation (which is what I wanted to set up) instead of log rotation based only on reaching a certain file size. At any rate, I thought it would be neat to allow external logic to govern exactly when the access logs would be rotated.

It turned out that the main problem was a lack of support in Yaws to detect that its logfile had been rotated and replaced—it did detect external log rotation if the logfile disappeared, but it got confused if the log rotator created a fresh new empty logfile to replace the old one. Which newsyslog did as a matter of course, and logrotate too, depending on configuration. In this case, Yaws needs to be a bit more clever and say “Hey, this file is smaller than the amount of data I have written so far, therefore it must be a freshly rotated log. Let’s reopen it to get a valid file descriptor to it, so I won’t keep writing to the old log!”3

Implementing support for this turned out to be rather easy, once I understood how the logging code worked. I addressed the missing feature with my patch that became pull request #431.

The commit message:

Detect externally rotated logfiles

Currently, Yaws does not play well with an external log rotation
mechanism (Linux logrotate or BSD newsyslog). When such an external
program rotates a Yaws log, it will customarily move the existing log
to a new name, open a new logfile with the old name, and (optionally)
send a (configurable) signal to the program that writes to the log, to
notify it to re-open the logfile.

Yaws supports wrapping its logs at a given size (configuration
variable log_wrap_size), but it does not support wrapping it in a
time-based fashion (e.g., wrap once a day at midnight), which is why
an external log rotator is useful.

The problem is that in such a case, Yaws will continue writing to the
old logfile. There is logic to detect that the logfile has been
wrapped, but it does not work if the log rotator creates a new file
in place of the old. In such case, Yaws should detect that the logfile
it sees at the expected path is smaller than what it knows to have
written to the log, and take that as a signal that the log has been
wrapped.

This patch adds support for just this. To make use of it, one should
arrange for the log rotator to issue a 'yaws --hup' post rotating the
files. This will trigger Yaws to immediately re-evaluate the
conditions and lead to migrating to the newly opened logfile.
Otherwise, it will take up to 10 minutes for this to happen, and Yaws
will have been writing to the old file (via the file descriptor it is
holding to) prior to that.

My web application logs have never been happier!


1 Notably, this preceded the meteoric rise of Nginx.

2 The same issues existed using logrotate under Linux.

3 In case you are surprised: Welcome to the fascinating world of UNIX systems programming! The ancient phrase “Here Be Dragons” sums it up much better than I could ever explain in many, many more words.