Implementing QUIC from Scratch with Rust: A Fresh Start
Why Start This Project?
Self-Learning and Documentation
I’ve always wanted to write technical blogs because whenever I come across interesting technical details, I feel an urge to share them—or at least document them for future reference. But I’ve never followed through, likely because there are just too many great games to play and not enough time.
Now, this spontaneous idea of implementing QUIC from scratch with Rust feels exciting to me, like starting a new AAA game. It motivates me to commit to it, and I hope this project marks the beginning of my personal blogging journey.
When I first started working, someone advised me to read the code of interesting projects, try implementing them myself, and then optimize and refactor the implementation. They said this would help me become a better programmer. After years of working, I’ve come to appreciate this advice more deeply. One immediate benefit is a much better understanding of project details—actually implementing something brings many hidden nuances to the surface. A programmer I admire, agentzh, has also shared the practice of hand-copying code as a learning method, which likely has a similar effect. After all, just reading isn’t enough—practice is essential.
Strengthening these details has obvious benefits. When handling production issues, customer tickets, or daily feature development, a deep understanding helps make better decisions, narrow down problems faster, identify root causes, and write more robust features.
For example, Sherlock Holmes solves cases quickly because his observation skills surpass ordinary people’s, allowing him to notice details others overlook. If we hone our grasp of technical details, we might someday become a "Sherlock Holmes" in the eyes of others. 😂
My Connection with QUIC
I vaguely remember back in 2018, right after I graduated, colleagues in the team next door were experimenting with integrating QUIC into our custom-built NGINX. At that time, I had never heard of QUIC. Occasionally, while working overtime, I overheard their discussions, which sounded impressive and mysterious. Looking back, there were very few QUIC open-source libraries, and they weren’t stable enough. Implementing QUIC in NGINX was undoubtedly a big challenge.
About two years ago, I joined a new company, and my first project was to introduce QUIC to improve product quality. That was when I officially started working with QUIC. I repeatedly read the QUIC RFC and studied codebases like ngtcp2
, nginx-quic
, and msquic
. To understand the implementation details, I debugged these open-source projects multiple times. I often marveled at the ingenuity of QUIC’s design and thought it was a brilliant protocol.
However, over time, I began forgetting many of the details I had worked hard to understand, which bothered me. That’s why I recently decided to implement a QUIC stack and client myself and document my understanding through a blog series. I’m sure there will be misunderstandings or gaps in my knowledge, but writing about them allows me to refine my understanding over time. Perhaps kind readers will point out errors, which is another motivation for this blog.
Becoming More Proficient in Rust
There’s already plenty of material praising Rust’s advantages, so I won’t repeat them here. As someone with a C programming background, I was immediately drawn to Rust when I first tried it. Considering the fragmented build systems and unavoidable memory safety issues in C/C++ projects, I often rely on sanitizers to reassure myself after writing code (though they can’t catch everything). Rust’s compile-time memory safety provides much-needed peace of mind.
On top of that, coding in Rust is simply more enjoyable than in C. Every time I write C, I wish I were using Rust instead. Since I don’t have the opportunity to use Rust at work, I take on personal projects like this to explore it. Writing a QUIC stack this time is partly motivated by my desire to code in Rust.
Since I started learning Rust earlier this year, I’ve been trying to write idiomatic Rust code. However, there’s still a lot of room for improvement. Through continuous refactoring and iteration on this project, I hope to become more familiar with Rust’s style.
My FC 25 Account Got Wrongfully Banned
Lastly—and most importantly—my FC 25 game account was wrongfully banned, leaving me with a lot of free time to start this personal project. Here, I’d like to say a few words to EA:
You are the worst gaming company in the world! Your game is riddled with cheats, and I constantly outsmarted them week after week. Instead of fixing the cheating issue, you banned honest players like me with your lazy detection system. I’ve seen many others in forums suffer the same fate. I will never play your games again.
Also, to TGA: awarding FC 25 as the Best Sports Game of 2024 is downright laughable.
What Kind of QUIC Project Do I Want to Build?
First and foremost, this project is not meant for production use—it’s purely for personal exploration. For example, I find QUIC’s handshake mechanism fascinating, so I don’t plan to use rustls
(let alone OpenSSL) for QUIC implementation. Instead, I’ll base it directly on a cryptographic library like ring
. This inherently means the project will fall short of production-grade standards.
In addition to implementing the QUIC stack, I’ll prioritize building a QUIC client. In my mind, it’s more like a tool, supporting as many QUIC-related customizable options as possible—not just basic transport parameters. For instance, the client might allow configuring specific behaviors for QUIC key updates, such as triggering updates after sending a certain number of packets or after a set duration. These detailed and flexible configurations will help deepen my understanding of QUIC. When I was learning QUIC, I had to modify open-source projects manually to test specific scenarios, which was tedious.
Below are the features I plan to implement, categorized into several groups:
Basic Features
These are essential for the QUIC client to function at the most basic level:
- QUIC Handshake
- QUIC Reliability
- QUIC Connection Termination
- QUIC Key Update
- QUIC Streams
- QUIC Flow Control
Quality Features
These significantly affect performance and reliability in various scenarios:
- QUIC 0-RTT
- QUIC Congestion Control
- QUIC Migration
- QUIC MTU Discovery
- QUIC Unreliable Datagram
Experimental Features
These include intriguing QUIC drafts, such as multi-path, and tools like qlog
. I also plan to integrate the project with tokio
since coroutine-based async networking is mainstream. If time permits, I might even implement HTTP/3, considering most QUIC traffic serves HTTP/3. If everything goes well, I hope the client can interact with this very blog, which supports HTTP/3 via OpenResty. 😄
Preparations
Now that I’ve outlined the goals, let’s look at what I need to prepare before starting this project. More precisely, these are the preparations I’ll make:
Documentation
I highly recommend using the QUIC Working Group website as the main entry point for all QUIC-related RFCs. It’s incredibly helpful.
Of course, RFCs alone aren’t enough. While reading them, I often encounter concepts I struggle to understand. Naturally, this isn’t the RFC’s fault—it’s my lack of familiarity. Spending time to understand the prerequisite knowledge helps make progress.
Debugging Tools
I’ve prepared NGINX and OpenSSL built from source, complete with debug information and detailed logs (e.g., enabling specific DEBUG macros). These will serve as the server-side counterpart to interact with my client. If my client has any issues, I can quickly identify the protocol step where the error occurred by checking the server logs. Based on my experience, having such quick feedback is essential for rapid implementation.
Besides server logs, tools like Wireshark provide insights into QUIC’s behavior. While QUIC is TLS-encrypted, making it hard to see protocol details, this can be addressed by using the sslkeylog
feature to write symmetric keys to a file. Wireshark can then decrypt the traffic. I typically use tshark
(Wireshark’s CLI version) as part of my workflow with ssh + tmux
. It’s extremely useful—give it a try if you haven’t already!
Rust Tooling
Rust offers great tools to improve code quality. When I submitted my first Rust pull request to aya
in May, I discovered Rust’s automated linting tools (thanks to CI/CD). Initially, I only used cargo fmt
to enforce style, but now I know about tools like clippy
and miri
, which I’ll integrate into this project’s CI/CD pipeline.
Testing
Reliable tests are indispensable. Since I expect to refactor the code extensively, I won’t invest in unit tests early on. Instead, I’ll add automated tests once the project has basic functionality. Later, I might use tools like cursor
to help with test coverage.
Finally, I’ve created a GitHub repository for this project: feather-quic. I’m confident this journey will be full of fun!