<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chris Burdess</title>
    <description>The latest articles on DEV Community by Chris Burdess (@cpkb).</description>
    <link>https://dev.to/cpkb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4095737%2Faa66e080-39a0-4a55-858f-0622a75025d5.png</url>
      <title>DEV Community: Chris Burdess</title>
      <link>https://dev.to/cpkb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cpkb"/>
    <language>en</language>
    <item>
      <title>Writing QUIC in Pure Java</title>
      <dc:creator>Chris Burdess</dc:creator>
      <pubDate>Wed, 26 Aug 2026 12:26:08 +0000</pubDate>
      <link>https://dev.to/cpkb/writing-quic-in-pure-java-2536</link>
      <guid>https://dev.to/cpkb/writing-quic-in-pure-java-2536</guid>
      <description>&lt;p&gt;I maintain &lt;a href="https://github.com/cpkb-bluezoo/gumdrop" rel="noopener noreferrer"&gt;gumdrop&lt;/a&gt;, an async, non-blocking Java server framework. Last year I wanted to add HTTP/3 support, and ran into a wall: the Java ecosystem essentially doesn't have QUIC. The JDK's own experimental support (JEP 517) is client-only. Netty gets HTTP/3 by shelling out to &lt;code&gt;quiche&lt;/code&gt; + BoringSSL over JNI — which works, but you're back to native builds, platform-specific binaries, and a C library sitting underneath your "pure Java" framework. I used that approach first. It was clumsy enough that I went looking for a pure-Java alternative.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There's exactly one: Kwik. But Kwik is blocking per connection — one thread per QUIC connection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a non-starter for a framework built around single-threaded selector loops handling tens of thousands of concurrent connections. So I wrote a QUIC implementation from scratch: packet protection, loss detection and NewReno congestion control, connection migration, 0-RTT, QPACK, an HTTP/3 client and server — all driven by the same non-blocking event loop as everything else in gumdrop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaboration note:&lt;/strong&gt; TLS 1.3 comes from &lt;a href="https://github.com/ptrd/agent15" rel="noopener noreferrer"&gt;Agent15&lt;/a&gt; — also from Kwik's author, Peter Doornbosch, but just the handshake layer, not the connection model. We're currently working together on making PQC — hybrid key exchange and signatures — the default there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the thread model matters
&lt;/h2&gt;

&lt;p&gt;The reason this mattered beyond HTTP/3: gumdrop isn't a web framework with QUIC bolted on, it's a general async I/O framework, and QUIC is just a transport. One thread per connection is exactly the model gumdrop exists to avoid — it caps concurrency at your thread pool, not your file descriptors, and it's the reason a "just use Kwik" fix was never really on the table.&lt;/p&gt;

&lt;p&gt;The same QUIC stack backs DNS-over-QUIC (DoQ) as a first-class DNS transport alongside DoT, DoH, UDP, and TCP — and the DNS &lt;em&gt;resolver itself&lt;/em&gt; is fully async, with no blocking &lt;code&gt;InetAddress.getByName()&lt;/code&gt; anywhere in the I/O path, which is its own small miracle in Java. HTTP, SMTP, IMAP, POP3, FTP, MQTT, SOCKS — it's the same story throughout: single-threaded selector loops, a worker pool sized independently of connection count, &lt;code&gt;ByteBuffer&lt;/code&gt; end to end, no thread-per-connection anywhere in the framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aside — the servlet container:&lt;/strong&gt; There's also a servlet container that runs transparently on top of all this, including HTTP/3 — which is a strange thing to want, since the Servlet API's I/O model is inherently synchronous even with its async extensions. But it means existing J2EE code gets HTTP/3 for free without a rewrite, which is a decent trick even if the API underneath it is fighting the rest of the framework's philosophy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it stands
&lt;/h2&gt;

&lt;p&gt;The QUIC layer ships today: packet protection, NewReno, QPACK dynamic tables, 0-RTT, Retry packets, passive connection migration, WebSocket-over-HTTP/3 (RFC 9220), and automatic h3/h2/h1.x negotiation. No native build step, no &lt;code&gt;QUICHE_DIR&lt;/code&gt;, no BoringSSL — just Java 17+.&lt;/p&gt;

&lt;p&gt;Code, protocol notes, and the rest of the framework it grew out of: &lt;strong&gt;&lt;a href="https://github.com/cpkb-bluezoo/gumdrop" rel="noopener noreferrer"&gt;github.com/cpkb-bluezoo/gumdrop&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>networking</category>
      <category>opensource</category>
      <category>quic</category>
    </item>
  </channel>
</rss>
