From The Challenges - Web Server
Exploring five software engineering lessons we can learn from the solutions I've seen.
Hi this is John with this week’s Coding Challenge.
🙏 Thank you for being one of the 72,816 software developers who have subscribed, I’m honoured to have you as a reader. 🎉
If there is a Coding Challenge you’d like to see, please let me know by replying to this email📧
Welcome To Coding Challenges - From The Challenges!
In this Coding Challenges “from the challenges” newsletter I’m sharing some of the common mistakes I see software engineers make when tackling the Coding Challenges.
I’m sharing both the mistakes people make and some thoughts on how you can you avoid making the same mistakes when taking on the coding challenges or when writing software professionally. Sometimes we have to make mistakes to learn from them, somethings we can learn from other people’s mistakes, then make our own new ones! 😀
Recapping The Web Server Coding Challenge
In the build your own web server coding challenge the goal was to write a web server.
At its core a web server is actually quite simple. It’s a server that listens for connections from clients and responds to them. The clients make those requests using a protocol known as HTTP (and expect responses in the same protocol, obviously).
Early web servers were very basic. The HTTP/0.9 specification provided for only a GET request and returned the document specified. The error messages were human readable HTML with no way to distinguish success from failure.
The build your own web server coding challenge went a little beyond that and supported a small subset of HTTP/1.1.
If You Enjoy Coding Challenges Here Are Three Ways You Can Help Support It
Refer a friend or colleague to the newsletter. 🙏
Sign up for a paid subscription - think of it as buying me a coffee ☕️ twice a month, with the bonus that you also get 20% off any of my courses.
Buy one of my courses that walk you through a Coding Challenge.
🦀 Rustaceans And Gophers* Loved This Coding Challenge
Rust and Go were by far the most used programming languages for all the solutions to this coding challenge that I’ve seen!
* Sorry, there’s no suitable Gopher emoji! 😕
Five Common Mistakes Software Engineers Make Solving the Web Server Coding Challenge
Part of the challenge of implementing a web server is handing the requests, translating the requested path to the correct page on the file system and streaming back the file.
It defeats the objective if you create some fake endpoints like /users
or /books
and return some data. That’s an API not a web server.
Mistake 1 - Hardcoding Routes And File Paths
Part of the challenge of implementing a web server is handing the requests, translating the requested path to the correct page on the file system and streaming back the file.
It defeats the objective if you create some fake endpoints like /users
or /books
and return some data. That’s an API not a web server.
Mistake 2 - Not Checking The File Is Within The Configured WWW Folder
A second mistake that commonly occurs is taking whatever path is given in the URL and simply reading from that path. This creates a security vulnerability, potentially giving a HTTP client access to read any file on the hosts filesystem.
For example if the server is serving files from /var/www
we don’t want to allow a user to request the path ../../etc/pwd
and simply serve it to them!
Mistake 3 - Using The HTTP Server Library From Your Language
Writing a web server is a great way to learn about the Berkley Sockets API, or the networking library provided by your programming language. Using them to implement a TCP server and by reading the RFCs learn about and implement the HTTP protocol. All useful skills as software engineers.
Plus even though you are very unlikely to need to implement the HTTP protocol (use a production level server that already exists) implementing part of it and reading the RFC will give you a better understanding of HTTP and how you can use it when building RESTful (or other) APIs over HTTP.
If on the other hand you just use the HTTP library or the HTTP Server library from your programming language you miss that learning.
Mistake 4 - Not Using Smart Pointers / RAII / Context Managers
I saw various opportunities for memory or other resource leaks in several of the solutions. In many of the programming languages used those leaks are easily avoidable by making sure that resources are released no matter how the enclosing scope is exited.
For example it’s common in C++ to use the Resource Acquisition Is Initialisation idiom (aka RAII). In this idiom a resource is allocated/acquired during object creation and released during object destruction.
For example in C++ you might use a smart pointer to ensure memory is deallocated and a std::lock_guard
to ensure locks are always released:
std::mutex m;
void lock_possibly_not_released()
{
m.lock();
if (might_be_true())
return; // possible early return mutex not released
m.unlock();
}
void using_raii()
{
std::lock_guard<std::mutex> lock(m); // RAII
f();
if (might_be_true())
return;
}
Not all language support the idiom. Most have something similar though, for example Python allows us the possibility of using try
, except
, finally
or context managers:
# try, except, finally (no except shown)
lock.acquire()
try:
_protected_data = updated_value()
finally:
lock.release()
# Using a context manager
lock = Lock()
with lock:
_protected_data = updated_value()
Go allows a similar pattern using defer
, executing the deferred function when the surrounding function returns:
type ProtectedMap struct {
mu sync.RWMutex
data map[string]*Entry
}
func (pm *ProtectedMap) OperationOnMap(k string) error {
pm.mu.Lock()
defer pm.mu.Unlock()
// operations that change the map
}
Rust support RAII, sometimes referring to it as Ownership Based Resource Management (OBRM). The typical example is Box. Java has try-with-resources statement and C# has the using
statement.
Mistake 5 - Assuming A Read From A Socket Always Gets A Full Message
When you’re reading (recv
calls in the Berkeley Sockets API) TCP messages you are not guaranteed to receive a full message. Assuming you will receive a full message is a common mistake that many developers make when programming using TCP.
They assume that message boundaries are preserved. In other words, they assume that sending a single message will result in a single receive call getting the full message.
This is not the case; TCP operates on streams of data. When data is sent by one side, it is added to the stream going to the other side. When the other side reads the stream, it reads a specified number of bytes at a time – usually the call to read bytes from a socket allows us to specify the maximum number of bytes to read and a buffer into which to place those bytes.
When the read function returns, the buffer contains new data and the number of bytes read will be returned.
So, a client might send a 100-byte message and the server might read 10 bytes at a time – meaning that no single read will ‘read’ a full message. Instead, our application will need to know how to combine the data from the reads into a single message. This is known as message framing.
There are two commonly used approaches to handle this: length prefixing, which prepends each message with the length of the message; and delimiting each message with a special byte or byte sequence, i.e. \r\n
.
Request for Feedback
I’m writing these coding challenges and this new from the challenges series to help you develop your skills as a software engineer based on how I’ve approached my own personal learning and development.
What works for me, might not be the best way for you - so if you have suggestions for how I can make these challenges more useful to you and others, please get in touch and let me know. All feedback greatly appreciated.
You can reach me on Bluesky, Twitter, LinkedIn or through SubStack
Thanks and happy coding!
John
P.S. If You Enjoy Coding Challenges Here Are Four Ways You Can Help Support It
Refer a friend or colleague to the newsletter. 🙏
Sign up for a paid subscription - think of it as buying me a coffee ☕️ twice a month, with the bonus that you also get 20% off any of my courses.
Buy one of my courses that walk you through a Coding Challenge.
Subscribe to the Coding Challenges YouTube channel!
I love the focus on learning through implementation rather than relying on built-in libraries.