RubyKaigi 2023

Ruby Implementation of QUIC: Progress and Challenges

RubyKaigi 2023

00:00:01.140 Okay, so, Asoka.
00:00:10.800 I'm Yusuke Nakamura, and my friends call me Unaske. My first name, Yusuke, is common in Japan, so it's not unique in the Ruby community. The last name Nakamura is common too, so please call me Unaske.
00:00:28.859 I'm currently working as a freelance web application programmer, where I develop and maintain applications as part of my job.
00:00:41.780 Let me take a moment to advertise myself. I'm the author of a book about Ruby. Additionally, there's a music event called Ruby Music Mixing 2023 happening on the last day of the conference, organized by Pixip. I am one of the DJs at this party, so let’s dance!
00:01:22.799 Have you heard of the Ruby Association Grant? I applied for it last year, and thankfully, it was accepted. The project I undertook ended in March, and this talk serves as the final report on that project.
00:01:41.400 My project focused on implementing QUIC in Ruby, aiming to port a Python implementation of QUIC to Ruby. You may ask, 'Why is QUIC important?' To start, what is QUIC?
00:01:58.200 QUIC is the Next Generation communication protocol built on top of UDP, which is used to achieve HTTP/3. This protocol is notably faster than HTTP/2 using TCP. If you’ve ever watched a YouTube or Twitch video or searched for something on Google with a modern web browser, you have undoubtedly used QUIC.
00:02:27.180 This diagram, created by Lobby Math, compares HTTP/2 and HTTP/3. It visualizes how communication differs over HTTP/2 with TCP compared to HTTP/3 using QUIC built on top of UDP.
00:02:46.560 In my past two talks, I discussed attempts to implement QUIC in Ruby. The first was in 2021, where I discussed QUIC utilizing a reactor pattern. In 2022, I addressed the challenges of handling encrypted binary data in Ruby. Until last year's talk, I had worked on implementing QUIC in Ruby from scratch, but I realized it was too difficult.
00:03:15.900 I would like to ask a question to gauge the audience's experience with QUIC. Raise your hand if you have created a QUIC application.
00:03:30.420 So, most people here have implemented some form of a web application but have never created a QUIC protocol implementation. Though the QUIC protocol has been outlined in some RFCs, the references can be somewhat dated. The RFCs do not act as a user-friendly guide for those learning to create their own implementations.
00:04:01.319 To successfully implement the QUIC protocol, one must navigate various complexities inherent in existing libraries. If you have gained knowledge from implementing libraries or tutorials in recent years, you might have the foundation needed to implement QUIC. I decided to import a Python implementation of QUIC into Ruby, which is the core reason I applied for the Ruby Association Grant.
00:04:29.400 Upon receiving the grant, I sought a mentor, Sasarasan, to guide me through this process. Deadlines are a strong motivator, and I’m sure you can relate to feeling the pressure to meet them. This provides context on the background of my efforts to port a Python library to Ruby.
00:05:06.900 Today, I will discuss three core topics: 1) the challenges of porting from Python to Ruby, 2) insights based on my experience with Ruby's implementation of QUIC, and 3) where I envision my QUIC implementation is headed in the future.
00:05:47.580 Python and Ruby are often described as similar programming languages: both are lightweight and object-oriented. However, the porting process is not easy, particularly when dealing with more complex codebases. If you have a few dozen lines of code, the porting can seem straightforward; for example, printing a statement. But when dealing with large frameworks, such as iPic, which consists of around 18,000 lines of Python code, the challenges multiply.
00:06:01.919 I will now share my experiences porting this substantial amount of Python code to Ruby. Before I began, I set some foundational guidelines, such as maintaining the internal data structure and module structure from the original library to ensure consistency throughout the porting process.
00:06:50.460 QUIC is complex, which means that deviations in structure during the porting could result in significant issues. Moreover, I decided to adopt the async/await pattern for the parts that configure the server, given the many differences in syntax and functionality in asynchronous processing between Python and Ruby.
00:07:42.780 Another major challenge I faced was the disparity in how both languages handle binary types. In Python, QUIC communicates through binary data. I found that handling this in Ruby required converting binary data into strings due to Ruby's string encoding.
00:08:43.800 In Python, the length of byte sequences can be calculated with a simple command, while in Ruby, nuances in string representation, especially for multi-byte characters, can complicate byte counting. This discrepancy can lead to tests failing unexpectedly due to length mismatches.
00:09:16.320 I believe that adaptation of buffers or strings could address some of these issues, particularly when implementing QUIC or TLS in the future. An example of the code I adapted from Python to Ruby for defining an enum shows the differences in structure but similar functionality.
00:10:31.890 When working with the packet reception process, I found that the original Python one-line callback function turned into several lines of case statements in Ruby. This help contextualizes the spirited differences between how Python and Ruby handle event-driven programming.
00:11:27.480 Moving forward, the implementation of QUIC must also take into account the encryption involved. The mechanism used for encryption relies on TLS 1.3 and OpenSSL, with some differences between how these are handled in Python versus Ruby.
00:12:12.600 To implement the original QUIC Python code efficiently, it was necessary to ensure that function order and arguments were maintained. Examples of different API calls can illustrate just how vital such distinctions are for proper implementation.
00:12:59.880 After completing the port, I successfully wrote around 11,000 lines of Ruby code. While I was unable to pass all test cases, notably those linked to congestion control, I managed to establish communication with other implementations like QUIC-Go.
00:14:30.420 The porting project yielded valuable insights: 1) the complexity of QUIC itself, 2) the challenges surrounding TLS 1.3 implementation, and 3) the realization that there are outstanding issues yet to be addressed.
00:16:12.210 Regarding the future of Ruby's implementation of QUIC, my aim is to make it the go-to solution for quick application development within Ruby. Upcoming iterations should integrate lessons learned during this implementation process.
00:17:19.200 While pushing forward to create a robust implementation, I am also focused on research applications. There seems to be a lack of existing libraries catering to QUIC and HTTP/3, which I hope to address in future work.
00:18:31.740 In conclusion, embarking on the journey of implementing QUIC in Ruby is rightly complex, but my goal is to create something functional that benefits the broader community. Acknowledgments go out to the Ruby Association for supporting my project, as well as to those who contributed to the Ruby implementation of TLS.