<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>seanmonstar</title>
    <description>My name is Sean McArthur, and here I blabber on about Rust, networking, open source, and a better web.
</description>
    <link>https://seanmonstar.com/</link>
    <atom:link href="https://seanmonstar.com/rss" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 27 Jan 2026 16:56:14 +0000</pubDate>
    <lastBuildDate>Tue, 27 Jan 2026 16:56:14 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>2025 in review</title>
        <description>&lt;p&gt;Come along with me as I review the past year. Heh, I often start these kinds of posts right at the start of the year, but it takes a few weeks longer than I ever expect to think them through.&lt;sup id=&quot;fnref:past&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:past&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h3 id=&quot;two-years-of-being-independent&quot;&gt;Two years of being independent&lt;/h3&gt;

&lt;p&gt;After a second year of operating as an &lt;a href=&quot;https://seanmonstar.com/blog/independent-open-source-maintainer/&quot;&gt;independent open source maintainer&lt;/a&gt;, it’s starting to feel more normal.&lt;/p&gt;

&lt;p&gt;In terms of personal execution, it felt pretty fantastic, actually. Thanks to high-touch conversations from my &lt;a href=&quot;https://seanmonstar.com/sponsor&quot;&gt;retainers&lt;/a&gt;, I knew what was needed; there was an underlying trend. And I was able to spec out a grant that made a project out of that trend. All while managing to do the necessary maintenance work that the ecosystem requires. Granted, it did occasionally feel like a conflict of priorities, but that’s life.&lt;/p&gt;

&lt;p&gt;Honestly, though, I wasn’t so sure when trying to plan this all out initially.&lt;/p&gt;

&lt;h3 id=&quot;maintaining-hyper&quot;&gt;Maintaining hyper&lt;/h3&gt;

&lt;p&gt;Perhaps the biggest deal for &lt;a href=&quot;https://hyper.rs&quot;&gt;hyper&lt;/a&gt; this year was launching our first user survey. I’ve thought of doing it a few times over the years, but finally remembered in Q4 to launch it. Thanks to all who answered! I’ve looked through the results, and I think this will be extremely useful. Some stats real quick: 96% of respondents have upgraded to hyper v1.x, most commonly combine it with Tokio (99%) and rustls (92%).&lt;sup id=&quot;fnref:survey&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:survey&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; A proper analysis coming soon!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://hyper.rs/blog/2025/04/21/welcome-katelyn-martin/&quot;&gt;katelyn martin&lt;/a&gt; joined us as a collaborator, and has continued to be a multiplier with kind reviews and maintenance glue. And general maintenance doesn’t stop, including growing security reports (more below).&lt;sup id=&quot;fnref:maintenance&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:maintenance&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Besides all that, I took on a larger project for the year. You see, after updating the roadmap at the end of the previous year, I started to focus on one of the four defined areas: improved &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyper-util&lt;/code&gt;. This lined up with what many have been asking for.&lt;/p&gt;

&lt;p&gt;I did that by modularizing parts out of reqwest.&lt;/p&gt;

&lt;h3 id=&quot;modularizing-reqwest&quot;&gt;Modularizing reqwest&lt;/h3&gt;

&lt;p&gt;Most of my year was spent on &lt;a href=&quot;https://seanmonstar.com/blog/modular-reqwest/&quot;&gt;modularizing reqwest&lt;/a&gt;. Or, from another angle, giving back the building blocks that reqwest has accumulated over the years. A lot of functionality that people rely on in reqwest started life as internal glue, and this was the year I finally pulled many of those pieces out into places where the rest of the ecosystem could use them too. Between reqwest and hyper‑util, that work ended up producing quite a few releases: 14 for reqwest itself, and 8 for hyper‑util.&lt;/p&gt;

&lt;p&gt;Ages ago, I added a bunch of features that you expect any client to have directly into reqwest. Later, as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower-http&lt;/code&gt; grew, it copied some of those same features. Meanwhile, reqwest was used in weirder and weirder places, so we hardened those features, and tossed in some tests to check for the weird. But &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower-http&lt;/code&gt; never saw any of that.&lt;/p&gt;

&lt;p&gt;This year, we completely tossed the redirect and decompression code from reqwest, depended on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower-http&lt;/code&gt; pieces, and then allowed the test suite to find the difference. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower-http&lt;/code&gt; layers got those fixes backported, and now everyone benefits.&lt;/p&gt;

&lt;p&gt;We also created &lt;em&gt;new&lt;/em&gt; things, but still modular.&lt;/p&gt;

&lt;p&gt;Easier &lt;a href=&quot;https://seanmonstar.com/blog/reqwest-retries/&quot;&gt;retries&lt;/a&gt; were added to reqwest, making use of the lower-level pieces in tower. I’m still interested in ways to improve the feature, so more people can use retries more safely.&lt;/p&gt;

&lt;p&gt;reqwest has grown extensive support for connection proxies. But an increasingly common pattern was people using reqwest &lt;em&gt;only&lt;/em&gt; for the proxy support; they didn’t need any other feature. So I extracted proxy &lt;a href=&quot;https://docs.rs/hyper-util/latest/hyper_util/client/proxy/matcher/index.html&quot;&gt;matchers&lt;/a&gt; and proxy &lt;a href=&quot;https://docs.rs/hyper-util/latest/hyper_util/client/legacy/connect/proxy/index.html&quot;&gt;connectors&lt;/a&gt; (tunnel, socks) into hyper-util.&lt;/p&gt;

&lt;p&gt;The largest piece was designing and implementing composable &lt;a href=&quot;https://seanmonstar.com/blog/hyper-util-composable-pools/&quot;&gt;pools&lt;/a&gt; for hyper-util. In many ways, this was my &lt;strong&gt;highlight of the year&lt;/strong&gt;. It’s a problem I’ve been thinking about since … 2018? I’d done a lot of research throughout the years, and never found anything quite like it. Now, it’s not quite “done”, but it’s a base that allows a lot of new layers and compositions to be explored.&lt;/p&gt;

&lt;p&gt;To end the year, we released v0.13 with &lt;a href=&quot;https://seanmonstar.com/blog/reqwest-v013-rustls-default/&quot;&gt;rustls as default&lt;/a&gt;. It’s a big improvement for &lt;em&gt;most&lt;/em&gt; people. But. I am not currently happy with how difficult it is to build the defaults on some other targets (Windows, Cranelift, cross-compiling). I want that fixed. Maybe that’s improvements to upstream aws-lc-rs; it looks like it’s already been improved to not need cmake. Or maybe we use a different default crypto provider on some targets.&lt;/p&gt;

&lt;h3 id=&quot;the-value-of-deadlines&quot;&gt;The value of deadlines&lt;/h3&gt;

&lt;p&gt;The work on composable pools was hard. The reason it had taken me years to finally try was that I wasn’t sure about some of the design. After staring hard at it during the summer, I did solve some of the questions. But there was &lt;a href=&quot;https://seanmonstar.com/blog/hyper-util-composable-pools/#real-world-usage-in-reqwest&quot;&gt;one problem&lt;/a&gt; towards the end that consumed another month or so of staring. And this time, I couldn’t stop staring.&lt;/p&gt;

&lt;p&gt;With a hard deadline set, however, there was no possibility of waiting longer. Instead, I had to settle with shipping what I had, and accepting that it can always be better.&lt;/p&gt;

&lt;p&gt;And that’s also the beauty of deadlines: they keep you user-driven. As long as I’m staring hard at a problem, holding back shipping, users have &lt;em&gt;nothing&lt;/em&gt;. But software doesn’t need to be shipped all at once. It’s a lesson I’ve learned before, and yet it pops up to, uh, &lt;em&gt;delight&lt;/em&gt; me over and over.&lt;/p&gt;

&lt;p&gt;I feel like I go through waves: I hate setting a deadline, and many times feel disappointed at not shipping all the glory that was in my head. But I always appreciate that at least they got &lt;em&gt;something&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;security-takes-time&quot;&gt;Security takes time&lt;/h3&gt;

&lt;p&gt;We take security seriously, and the amount of reports we receive is slowly increasing. This past year, we had a 8 in total, including our first AI slop report (yay!).&lt;/p&gt;

&lt;p&gt;The biggest one resulted in a large coordinated disclosure event. But &lt;a href=&quot;https://seanmonstar.com/blog/hyper-http2-didnt-madeyoureset/&quot;&gt;hyper was just fine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That didn’t stop it from being stressful trying to handle reports while simultaneously sticking to feature deadlines.&lt;/p&gt;

&lt;p&gt;It is a reminder, though, that this is often urgent and important work that must be handled, but that traditional pay-for-features doesn’t support. Sponsorships and retainers make this sort of maintenance much more sustainable.&lt;/p&gt;

&lt;h3 id=&quot;talks&quot;&gt;Talks&lt;/h3&gt;

&lt;p&gt;On the 10th anniversary of Rust 1.0, I gave a talk for the &lt;a href=&quot;https://www.youtube.com/watch?v=1PpdNu0Weas&quot;&gt;Rust for Lunch&lt;/a&gt; meetup. It was sort of ‘lessons using Rust for 10 years’, but also ‘why you should consider Rust’.&lt;/p&gt;

&lt;p&gt;And I did a podcast episode on &lt;a href=&quot;https://seanmonstar.com/blog/podcast-netstackfm/&quot;&gt;Netstack.FM&lt;/a&gt;, discussing the history of Rust’s networking ecosystem.&lt;/p&gt;

&lt;h3 id=&quot;pondering&quot;&gt;Pondering&lt;/h3&gt;

&lt;p&gt;Last year, I liked just sharing some questions I’m thinking about. It wasn’t a promise to work on them actively, but I look at them from time to time to see if there’s something that I can tackle soon.&lt;/p&gt;

&lt;p&gt;Here’s just a few things I’m thinking about at the start of 2026:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;How do I balance keeping up with LLM advances while keeping my mind and skills sharp?&lt;/li&gt;
  &lt;li&gt;How far can one reasonably go with typestate builders, considering ergonomics and correctness?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wonder.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:past&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;See some previous yearly reviews: &lt;a href=&quot;https://seanmonstar.com/blog/2024-in-review&quot;&gt;2024&lt;/a&gt;, &lt;a href=&quot;https://seanmonstar.com/blog/2023-in-review&quot;&gt;2023&lt;/a&gt;, &lt;a href=&quot;https://seanmonstar.com/blog/hyper-ish-2022-in-review&quot;&gt;2022&lt;/a&gt;, &lt;a href=&quot;https://seanmonstar.com/blog/hyper-ish-2021-in-review&quot;&gt;2021&lt;/a&gt;. &lt;a href=&quot;#fnref:past&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:survey&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Though a significant amount also use other runtimes and TLS libraries. As mentioned, more coming soon. &lt;a href=&quot;#fnref:survey&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:maintenance&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;There were three feature releases of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyper&lt;/code&gt; and two of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt;, bringing support for things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;on_informational&lt;/code&gt; and constants for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EARLY_HINTS&lt;/code&gt;. There was also five bug fix releases of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h2&lt;/code&gt;, hardening the HTTP/2 story for Rust. &lt;a href=&quot;#fnref:maintenance&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 27 Jan 2026 11:32:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/2025-in-review/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/2025-in-review/</guid>
        
        <category>yearly</category>
        
        <category>rust</category>
        
        <category>open-source</category>
        
        <category>hyper</category>
        
        <category>hyper-util</category>
        
        <category>reqwest</category>
        
        
      </item>
    
      <item>
        <title>reqwest v0.13 - rustls by default</title>
        <description>&lt;p&gt;To end out the year, here comes a new major release of &lt;a href=&quot;https://crates.io/crates/reqwest&quot;&gt;reqwest&lt;/a&gt;, the opinionated higher-level HTTP client for Rust.&lt;/p&gt;

&lt;p&gt;We don’t really need major breaking versions to keep providing value. Improvements keep coming all the time. But we did need one to make one particular big adjustment, and we’ve taken the opportunity to clean up other things too. At the same time, we strove make it disrupt as little as possible, especially if you stick to the defaults.&lt;/p&gt;

&lt;p&gt;reqwest &lt;a href=&quot;https://github.com/seanmonstar/reqwest/releases/tag/v0.13.0&quot;&gt;v0.13.0&lt;/a&gt; is out now! Read on for why.&lt;/p&gt;

&lt;h3 id=&quot;rustls-is-now-the-default-tls-backend&quot;&gt;rustls is now the default TLS backend&lt;/h3&gt;

&lt;p&gt;The biggest deal is that reqwest now sets its default TLS feature to use &lt;a href=&quot;https://rustls.dev/&quot;&gt;rustls&lt;/a&gt;, instead of native-tls.&lt;/p&gt;

&lt;p&gt;Granted, native-tls has its place. It provides a unified library that uses the “native” TLS implementation on each target. SecureTransport on macOS, schannel on Windows, and OpenSSL on Linux (as the most likely already installed). It was the right choice several years, while rustls was young. But rustls is now &lt;a href=&quot;https://docs.rs/rustls/latest/rustls/manual/_01_impl_vulnerabilities/index.html&quot;&gt;safer&lt;/a&gt; and &lt;a href=&quot;https://rustls.dev/perf/&quot;&gt;faster&lt;/a&gt; than most choices.&lt;sup id=&quot;fnref:windows-i686-gnu&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:windows-i686-gnu&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Seems like an obvious &lt;a href=&quot;https://github.com/seanmonstar/reqwest/issues/2025#issuecomment-2913873836&quot;&gt;improvement&lt;/a&gt;. But would people want the better choice?&lt;/p&gt;

&lt;p&gt;A recent hyper user survey found that 93% of respondants already use rustls. 30% said they also use native-tls at times, so we will continue to provide that option. And while a survey is already biased, it’s also most certainly true that the vast majority of users just allow the default options, and it works for them. That will continue to be true for most everyone.&lt;/p&gt;

&lt;p&gt;So, if you use the default options, things just got better for you.&lt;/p&gt;

&lt;h3 id=&quot;certificate-verification-features-are-consolidated&quot;&gt;Certificate verification features are consolidated&lt;/h3&gt;

&lt;p&gt;Previously, reqwest had many crate features to enable various ways to load sources of root certificates. Too many. It’s a bit of a mess.&lt;/p&gt;

&lt;p&gt;We consolidate the options into just a few simple choices:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Defaults to using the native platform verifier.&lt;/li&gt;
  &lt;li&gt;If the target supports it, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls_certs_merge()&lt;/code&gt; can add additional certificates. If it doesn’t, a builder error is returned.&lt;/li&gt;
  &lt;li&gt;If you really need a specific certificate, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls_certs_only()&lt;/code&gt;, which will always work, and doesn’t include native verification.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These basic options should allow for any use case, while reducing complexity within reqwest. For instance, if an application really needs to use the webpki-roots, they can be configured with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls_certs_only(your_roots)&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;soft-deprecation-to-improve-option-naming&quot;&gt;Soft-deprecation to improve option naming&lt;/h3&gt;

&lt;p&gt;While in there, most &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClientBuilder&lt;/code&gt; methods were given better names. The previous methods have been soft-deprecated. That means that you can keep using the old names, without warnings, but they are documented as deprecated, and they will eventually be removed in a later breaking change.&lt;/p&gt;

&lt;p&gt;But in most cases, the improved name is mostly to help with understanding. It doesn’t help to trigger a bunch of additional warnings when you upgrade. Perhaps we’ll add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deprecations&lt;/code&gt; crate feature for those who want to clean up.&lt;/p&gt;

&lt;p&gt;The upside is that all the methods now start with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls_&lt;/code&gt;. They are grouped together in the docs, but it will also help exploration with autocomplete.&lt;/p&gt;

&lt;h3 id=&quot;other-crate-feature-adjustments&quot;&gt;Other crate feature adjustments&lt;/h3&gt;

&lt;p&gt;As this is a breaking change version, we did include one making the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RequestBuilder::query()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;form()&lt;/code&gt; methods optional features, disabled by default. With these as optional, it is now possible to build reqwest without serde.&lt;/p&gt;

&lt;p&gt;We also made &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;native-tls&lt;/code&gt; imply ALPN automatically. This will mean most people who don’t think about it will now get HTTP/2 upgrades. The reason it was opt-in previously is because older native libraries might not have the symbols required. They are quite old at this point. If you still need the previous behavior, there is now &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;native-tls-no-alpn&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;thanks&quot;&gt;Thanks!&lt;/h3&gt;

&lt;p&gt;Thanks to all who contribute, use, &lt;a href=&quot;https://seanmonstar.com/sponsor&quot;&gt;sponsor&lt;/a&gt;, fix, complain, and help reqwest be what it is! Here’s &lt;a href=&quot;https://github.com/seanmonstar/reqwest/releases/tag/v0.13.0&quot;&gt;v0.13.0&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:windows-i686-gnu&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;This does drop &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i686-pc-windows-gnu&lt;/code&gt; support for the default features. The reality is that a &lt;em&gt;lot&lt;/em&gt; of things don’t work for that target. Even Rust itself has it as a Tier 2 target. We do still support it with native-tls, though. &lt;a href=&quot;#fnref:windows-i686-gnu&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 30 Dec 2025 10:48:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/reqwest-v013-rustls-default/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/reqwest-v013-rustls-default/</guid>
        
        <category>rust</category>
        
        <category>reqwest</category>
        
        <category>open-source</category>
        
        <category>programming</category>
        
        
      </item>
    
      <item>
        <title>hyper-util Composable Pools</title>
        <description>&lt;p&gt;I’m so excited to announce &lt;a href=&quot;https://hyper.rs&quot;&gt;hyper&lt;/a&gt;’s new composable pool layers!&lt;sup id=&quot;fnref:excited&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:excited&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;As part of &lt;a href=&quot;https://seanmonstar.com/blog/modular-reqwest/&quot;&gt;making reqwest more modular&lt;/a&gt;, we’ve designed a new connection pool, and made the pieces available in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyper_util::client::pool&lt;/code&gt;. But this is more than just a “hey, we have a Pool, it moved over there.” We’ve literally pulled apart the pool, in a way I haven’t found elsewhere.&lt;/p&gt;

&lt;p&gt;Building a purpose‑specific pool is now straightforward. Add the features you want, even custom ones, and skip the bloat, no forks required.&lt;/p&gt;

&lt;p&gt;Read on to see what exactly we solved, how, and what comes next. If you just want to use them, &lt;a href=&quot;https://docs.rs/hyper-util/0.1.x/hyper_util/client/pool/&quot;&gt;here’s the docs&lt;/a&gt;. Everyone else, let’s dive in.&lt;/p&gt;

&lt;h3 id=&quot;we-started-with-the-users&quot;&gt;We started with the users&lt;/h3&gt;

&lt;p&gt;We started with the users, looking back over past issues filed, common questions in chat, and private conversations explaining what they needed to do. Boiled down, that got us to these requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A full-featured pool, like the one in &lt;a href=&quot;https://docs.rs/hyper-util/0.1.x/hyper_util/client/legacy/struct.Client.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;legacy&lt;/code&gt;&lt;/a&gt;, must be possible.&lt;/li&gt;
  &lt;li&gt;Microservices shouldn’t have to handle multiple protocols or hostnames.&lt;/li&gt;
  &lt;li&gt;Some clients need custom keys for the pool.&lt;/li&gt;
  &lt;li&gt;Others need to limit new connections made at a time.&lt;/li&gt;
  &lt;li&gt;Or cap the total number of connections.&lt;/li&gt;
  &lt;li&gt;Customize connection expiration based on idle time, max lifetime, or even &lt;a href=&quot;https://docs.rs/hyper-util/0.1.x/hyper_util/client/legacy/connect/struct.Connected.html#method.poison&quot;&gt;poisoning&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;And importantly, allow custom logic not already thought of.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From past experience combining middleware, I had a strong feeling the pool requirements could be broken up into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower&lt;/code&gt; layers. But what would that even &lt;em&gt;look&lt;/em&gt; like? Would it be horrible to use?&lt;/p&gt;

&lt;p&gt;To answer that, we took the requirements and considered the developer experience of using layers. It had to feel nice. Not just to write, but also to come back to and read.&lt;/p&gt;

&lt;p&gt;I then sketched out several of these layers to make sure they could actually work. Once most of it was working, the &lt;a href=&quot;https://github.com/hyperium/hyper/issues/3948&quot;&gt;proposal&lt;/a&gt; was ready.&lt;/p&gt;

&lt;h3 id=&quot;the-initial-4-working-pools&quot;&gt;The initial 4 working pools&lt;/h3&gt;

&lt;p&gt;No plan survives contact with the enemy. We originally proposed five pool types, but launch with just the following four: singleton, cache, negotiate, map.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;singleton&lt;/code&gt; pool wraps a connector&lt;sup id=&quot;fnref:mksvc&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:mksvc&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; that should only produce a single active connection. It bundles all concurrent calls so only one connection is made. All calls to the singleton will return a clone of the inner service once established. This fits the HTTP/2 case well.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cache&lt;/code&gt; pool maintains a list of cached services produced by a connector. Calling the cache returns either an existing service, or makes a new one. When dropped, the cached service is returned to the cache if possible. Importantly for performance, the cache supports connection racing, just like the legacy pool.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;negotiate&lt;/code&gt; pool allows for a service that can decide between two service types based on an intermediate return value. Unlike typical routing, it makes decisions based on the response (the connection) rather than the request. The main use case is supporting ALPN upgrades to HTTP/2, with a fallback to HTTP/1. And its design allows combining two different pooling strategies.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; pool isn’t a typical service like the other pools, but rather is a stand-alone type that maps requests to keys and connectors. As a kind of router, it cannot determine which inner service to check for backpressure until the request is made. The map implementation allows customization of extracting a key, and how to construct a connector for that key.&lt;/p&gt;

&lt;h3 id=&quot;ineffably-unstable&quot;&gt;Ineffably unstable&lt;/h3&gt;

&lt;p&gt;I knew this work would land in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyper-util&lt;/code&gt; first, because it’s not stable yet. Being so freshly designed, changes are expected after some more real-world usage. Still, I wanted to shield earlier adopters from breaking changes. At the same time, valuing performance and flexibility, I wanted to push as much as reasonably possible into the type system.&lt;/p&gt;

&lt;p&gt;When initially tinkering during the summer, I had one of &lt;em&gt;those&lt;/em&gt; thoughts. The kind that clangs like a giant lock snapping open: what about type-state builders and unnameable types? I took a side quest, and tackled the &lt;a href=&quot;https://seanmonstar.com/blog/warp-v04/&quot;&gt;warp v0.4 upgrade&lt;/a&gt;, to test out this API design. That post explains it a bit more.&lt;/p&gt;

&lt;p&gt;The various threads were all coming together.&lt;/p&gt;

&lt;p&gt;With each pool concept a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower&lt;/code&gt; service, once composed, a user shouldn’t care what it is beyond being some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;impl Service&lt;/code&gt;. I tested this out in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqwest&lt;/code&gt;, and yea, I don’t need to name the types. While I did need &lt;em&gt;a&lt;/em&gt; type, I was able to store a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dyn Service&lt;/code&gt;, and inference handled the rest.&lt;/p&gt;

&lt;h3 id=&quot;real-world-usage-in-reqwest&quot;&gt;Real world usage: in reqwest&lt;/h3&gt;

&lt;p&gt;Once those main pieces seemed ready, I needed a real example to test drive them. Tool-makers that don’t use their tools make bad tools, after all.&lt;/p&gt;

&lt;p&gt;I started by replacing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;legacy&lt;/code&gt; pool inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqwest&lt;/code&gt;. Part of the larger diff in reqwest is handling all of reqwest’s different pool configuration options.&lt;/p&gt;

&lt;p&gt;But, putting the default case together is pretty self-explanatory:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Note: some noise has been trimmed&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;http1_request_target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;http1_set_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;MyMetaIdleAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;http1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;http2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pool_layers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;layer_fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;negotiate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.fallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http1&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.upgrade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http2&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.inspect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.is_negotiated_h2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pool_map&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scheme_and_auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_dst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pool_layers&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connector&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And it works! Making the full-featured pool was one of the requirements: check. But, the next part was even more important.&lt;/p&gt;

&lt;p&gt;As I mentioned before, I punted one of the proposed types: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expire&lt;/code&gt;. Expiration is a necessary concept to a pool. But try as I might to fit the various generic shapes, it just wasn’t happening. Thankfully, this work had a hard deadline. And deadlines keep you user-driven: let them have &lt;em&gt;something&lt;/em&gt; now, it can always be better later.&lt;/p&gt;

&lt;p&gt;To prove the general design allowed expiration, I implemented a specific version of it directly in reqwest.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;tokio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;tokio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idle_dur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Instant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pool_map&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.upgrade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.retain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.fallback_mut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.retain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.inner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.inner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.inner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.is_closed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idle_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.idle_at&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idle_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idle_dur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.upgrade_mut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.retain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.is_closed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.fallback_mut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.is_empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.upgrade_mut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.is_empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The ease of adding it helped solidify to me that this was definitely the right design. I was able to slot in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;meta&lt;/code&gt; layer tracking idle time, and then use that to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retain&lt;/code&gt; services. I placed that layer in right next to some of the other HTTP/1-specific layers. Easy!&lt;/p&gt;

&lt;h3 id=&quot;being-modular-opens-up-customization&quot;&gt;Being modular opens up customization&lt;/h3&gt;

&lt;p&gt;With the ability to build a stack for your pool, consider an example of how we can start to solve other requirements listed earlier.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ServiceBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// cached connections are unaware of the limit&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// in-flight handshakes are limited&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.concurrency_limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;    
    &lt;span class=&quot;nf&quot;&gt;.layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;http1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tcp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It also allows adding in layers we don’t currently have, such as per-host connection semaphores, or a few layers up over all hosts. Adding new functionality isn’t blocked on us, and no one has to “pay” for features they don’t need.&lt;/p&gt;

&lt;p&gt;I can’t wait to see what else is done with the design!&lt;/p&gt;

&lt;h3 id=&quot;pools-ready&quot;&gt;Pools ready&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyper_util::client::pool&lt;/code&gt; module is now available in &lt;a href=&quot;https://github.com/hyperium/hyper-util/releases/tag/v0.1.19&quot;&gt;v0.1.19&lt;/a&gt;. Go check the &lt;a href=&quot;https://docs.rs/hyper-util/0.1.x/hyper_util/client/pool/&quot;&gt;docs&lt;/a&gt;, and try to build cool things. Please file issues if parts are missing, we’ll keep iterating.&lt;/p&gt;

&lt;p&gt;I’ve been working on this feature set for long time. It’s something I started thinking about years ago, and after months of work this year, it feels awesome to finally be able to release it.&lt;/p&gt;

&lt;p&gt;Thanks to my &lt;a href=&quot;https://seanmonstar.com/sponsor&quot;&gt;sponsors&lt;/a&gt;, retainers, and grants for making this all possible!&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:excited&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I mean, who isn’t excited to announce anything? /s &lt;a href=&quot;#fnref:excited&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:mksvc&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;All “connectors” are actually &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MakeService&lt;/code&gt;s, which are jsut a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Service&lt;/code&gt; that produces a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Service&lt;/code&gt;. It doesn’t &lt;em&gt;have&lt;/em&gt; to create a connection, but it reads better when talking about pools. &lt;a href=&quot;#fnref:mksvc&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Wed, 03 Dec 2025 08:48:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/hyper-util-composable-pools/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/hyper-util-composable-pools/</guid>
        
        <category>rust</category>
        
        <category>hyper</category>
        
        <category>hyper-util</category>
        
        <category>tower</category>
        
        <category>open-source</category>
        
        <category>programming</category>
        
        <category>bestof</category>
        
        
      </item>
    
      <item>
        <title>hyper User Survey 2025</title>
        <description>&lt;p&gt;I’m excited to announce the inaugural hyper user survey! (&lt;em&gt;Update: it ended.&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;Part of being user-driven is &lt;a href=&quot;https://hyper.rs/contrib/governance/#making-decisions&quot;&gt;knowing what our users want&lt;/a&gt;, what they’re trying to do, and then we can figure out how best to help.&lt;/p&gt;

&lt;p&gt;This survey should only take about 5 minutes, no questions are required, and responses are anonymous. The survey will be open for a month, until December 17th, and then we’ll analyze the results and share with you all what we find.&lt;/p&gt;

&lt;p&gt;Thanks for taking the time to help make &lt;a href=&quot;https://hyper.rs&quot;&gt;hyper&lt;/a&gt; better!&lt;/p&gt;

&lt;p&gt;&lt;del&gt;[Take the hyper user survey for 2025.&lt;/del&gt;&lt;/p&gt;

</description>
        <pubDate>Tue, 18 Nov 2025 10:20:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/hyper-user-survey-2025/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/hyper-user-survey-2025/</guid>
        
        <category>rust</category>
        
        <category>hyper</category>
        
        <category>survey</category>
        
        <category>open-source</category>
        
        
      </item>
    
      <item>
        <title>Podcast: Netstack.fm, story of Rust&apos;s networking with hyper</title>
        <description>&lt;p&gt;Last week I was a &lt;a href=&quot;https://netstack.fm/#episode-2&quot;&gt;guest on the Netstack podcast&lt;/a&gt;. We talked abit about how I got into Rust, how async Rust developed, and the story behind &lt;a href=&quot;https://hyper.rs&quot;&gt;hyper&lt;/a&gt; and its surrounding ecoystem.&lt;/p&gt;

&lt;p&gt;We started (and ended) with my goal of better software:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;On your about page, you say that “Rust is the least bad option.” Can you elaborate a bit on that?&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;Yeah, I love Rust. I think Rust solves a ton of problems, but I also don’t want to be stuck when something better comes along, which it inevitably will. Then yeah, I’ll move to that.
To me, all these tools are the means to an end, which is to make better software.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The rest of the conversation was really fun to talk about:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;7:54&lt;/em&gt;: beginning of hyper, async Rust&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;13:20&lt;/em&gt;: hyper as something bigger&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;15:36&lt;/em&gt;: splitting off hyper-util&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;17:35&lt;/em&gt;: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;headers&lt;/code&gt; crates&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;22:51&lt;/em&gt;: motivation behind &lt;a href=&quot;/blog/warp-v04&quot;&gt;warp&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;29:00&lt;/em&gt;: &lt;a href=&quot;/blog/modular-reqwest&quot;&gt;reqwest&lt;/a&gt; as the opinionated layer&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;30:17&lt;/em&gt;: open source &lt;a href=&quot;/blog/independent-open-source-maintainer&quot;&gt;independence&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;31:31&lt;/em&gt;: HTTP/3 in hyper and reqwest&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;39:40&lt;/em&gt;: hyper in 5 years, contributors, ownership&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Tue, 02 Sep 2025 09:24:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/podcast-netstackfm/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/podcast-netstackfm/</guid>
        
        <category>rust</category>
        
        <category>hyper</category>
        
        <category>podcast</category>
        
        <category>open-source</category>
        
        
      </item>
    
      <item>
        <title>hyper HTTP/2 (Didn&apos;t) MadeYouReset</title>
        <description>&lt;p&gt;A new HTTP/2 attack vector was disclosed today called MadeYouReset. &lt;a href=&quot;https://hyper.rs&quot;&gt;hyper&lt;/a&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h2&lt;/code&gt; is negligably affected, weathering the attack well. But, we have provided patches just in case. We published patches weeks ago, so if you’ve been keeping up-to-date, you’re fine! If not, you’re most likely fine, but you can upgrade now. No CVE or security advisory is included with this.&lt;/p&gt;

&lt;p&gt;This sort of work is supported &lt;strong&gt;exclusively&lt;/strong&gt; through &lt;a href=&quot;https://seanmonstar.com/sponsor&quot;&gt;retainers&lt;/a&gt;, get in contact to support.&lt;/p&gt;

&lt;p&gt;Follow along for the background information, if you’re curious!&lt;/p&gt;

&lt;h3 id=&quot;what-is-the-attack&quot;&gt;What is the attack&lt;/h3&gt;

&lt;p&gt;The attack is quite similar to the previous &lt;a href=&quot;https://seanmonstar.com/blog/hyper-http2-rapid-reset-unaffected/&quot;&gt;Rapid Reset&lt;/a&gt; attack from a couple years ago. That is, it starts new requests up to the limit imposed by the server. And then instead of &lt;em&gt;sending&lt;/em&gt; explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RST_FRAMES&lt;/code&gt;, it sends malformed frames that force the &lt;em&gt;server&lt;/em&gt; to reset the stream.&lt;/p&gt;

&lt;p&gt;So far, the described behavior is normal HTTP/2 flow. The problem occurs in some implementations that don’t respond well to quick cancelation.&lt;/p&gt;

&lt;p&gt;As the attack describes, in some implementations it allows for more stream concurrency than might have been configured. See more in &lt;a href=&quot;https://www.kb.cert.org/vuls/id/767506&quot;&gt;VU#767506&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;what-hyper-does&quot;&gt;What hyper does&lt;/h3&gt;

&lt;p&gt;A couple years ago, we added a defense mechanism to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h2&lt;/code&gt; that keeps count of how many times a frame from the remote causes a local reset, and once a configurable limit is reached, the connection is abruptly closed. This mechanism caught nearly all variants of the attack.&lt;/p&gt;

&lt;p&gt;We had forgotten one code path dealing with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WINDOW_UPDATE&lt;/code&gt; frames. But what happens when new streams are created and then a bad window frame is received?&lt;/p&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h2&lt;/code&gt;, the reset is sent to user code. Check the request body will notice the error. Additionally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyper&lt;/code&gt;’s server code using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h2&lt;/code&gt; is essentially waiting simultaneously on the user’s service to reply, and for any reset to be received. Once the reset is received, the service future is canceled.&lt;sup id=&quot;fnref:react&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:react&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;So, not much. When a simple hello world server example was tested against the attack, the only thing that happened was increased CPU usage. Which makes sense, it’s receiving and canceling more streams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stream concurrency is enforced&lt;/strong&gt;, since all futures are canceled immediately, and a signal is sent to anything listening for longer.&lt;/p&gt;

&lt;p&gt;All of this before any additional patch.&lt;/p&gt;

&lt;h3 id=&quot;what-we-did&quot;&gt;What we did&lt;/h3&gt;

&lt;p&gt;We received a report&lt;sup id=&quot;fnref:ghsa&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:ghsa&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; about the attack from security researchers&lt;sup id=&quot;fnref:researchers&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:researchers&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; on May 23, 2025. The attack includes many variants, so we set out trying to determine if hyper was vulnerable. We knew that hyper already added a defense mechanism for this exact sort of abuse. Only one of the variants did not get cut off, but saw increased CPU usage, so we dug in. It turns out the original feature forgot to check the limit when WINDOW_UPDATE frames were bad.&lt;/p&gt;

&lt;p&gt;We determined that while the variants was not caught, it also did not break any guarantees. The increase in CPU usage was obvious, since it was necessarily handling more requests in general. Also important to our determination: the attack is &lt;em&gt;observable&lt;/em&gt; by user code, so any user code can react however they wish.&lt;/p&gt;

&lt;p&gt;The researchers did a great job of explaining the concept behind the attack, providing a proof-of-concept script that we could test with, and conversed respectfully and thoughtfully as we went through the investigation.&lt;/p&gt;

&lt;p&gt;Because the code of the aforementioned mechanism was already public, and the damage quite benign, we decided to release a patch weeks before the general embargo. It was just a refactor, converting it from logic-based to utilizing the type system more, so no other cases are “forgotten”. That patch was released on June 30, 2025.&lt;/p&gt;

&lt;p&gt;For similar reasons, we also determined there was no need for a CVE or security advisory.&lt;/p&gt;

&lt;p&gt;The researchers came back a little afterwards showing that in some cases, servers using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h2&lt;/code&gt; under this attack could be forced to crash. They even brough a new reproducible proof-of-concept. After analyzing it, we determined that the problem was not that of hyper/h2, but that of user code which might have unbounded queues and does not react to cancelation.&lt;/p&gt;

&lt;p&gt;We decided to stick with our original determination.&lt;/p&gt;

&lt;h3 id=&quot;what-you-can-do&quot;&gt;What you can do&lt;/h3&gt;

&lt;p&gt;After reading all that, what more can you do? Again, you’re likely fine! But here’s some good ideas, regardless:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If you’ve been staying up-to-date, you’re already done!&lt;/li&gt;
  &lt;li&gt;If you have a server running hyper, it would be wise to make sure your server handlers really can cancel easily.&lt;/li&gt;
  &lt;li&gt;You can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo update -p h2&lt;/code&gt; to upgrade to the latest versions (at least &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.4.11&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.3.27&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;If you’re running hyper v0.14 (so h2 v0.3.x), it’s time to upgrade to v1. After nearly 2 years, patches likely won’t be backported anymore.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s one more thing you can consider: set up a &lt;a href=&quot;https://seanmonstar.com/sponsor&quot;&gt;retainer&lt;/a&gt; to make sure security reports get taken care of. This was a considerable amount of important work. This was several months of higher-stress but important work which doesn’t look like shipping a new feature.&lt;sup id=&quot;fnref:psirt&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:psirt&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Besides funding that important work, I’m also happy to provide pre-disclosure and hands-on guidance to those with a retainer.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:react&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;This difference from Rapid Reset is also important. In Rapid Reset, the resets were not observable by user code, so they could not react to the wasting of resources. With MadeYouReset, however, users can if they need to. &lt;a href=&quot;#fnref:react&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:ghsa&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I just realized today that I cannot publish the GHSA as an informational, since it does not include a severity or CVE. GitHub, please? &lt;a href=&quot;#fnref:ghsa&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:researchers&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The original reporters were &lt;a href=&quot;https://galbarnahum.com/made-you-reset&quot;&gt;Gal Bar Nahum&lt;/a&gt;, Anat Bremler-Barr, and Yaniv Harel of Tel Aviv University. I primarily interacted with Gal, which was a 5/5 experience, would repeat. &lt;a href=&quot;#fnref:researchers&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:psirt&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;On top of what has been outlined here, there’s a lot of coordination work involved, dealing with &lt;a href=&quot;https://www.kb.cert.org/vuls/id/767506&quot;&gt;VINCE&lt;/a&gt; and other vendors. &lt;a href=&quot;#fnref:psirt&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Wed, 13 Aug 2025 08:25:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/hyper-http2-didnt-madeyoureset/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/hyper-http2-didnt-madeyoureset/</guid>
        
        <category>rust</category>
        
        <category>hyper</category>
        
        <category>http2</category>
        
        <category>security</category>
        
        
      </item>
    
      <item>
        <title>warp v0.4</title>
        <description>&lt;p&gt;Yesterday, warp &lt;a href=&quot;https://github.com/seanmonstar/warp/releases/tag/v0.4.0&quot;&gt;v0.4&lt;/a&gt; was released. &lt;a href=&quot;https://crates.io/crates/warp&quot;&gt;warp&lt;/a&gt; is a Rust web server framework, with a focus on functional programming and type system routing.&lt;/p&gt;

&lt;p&gt;Upgrading is likely pretty simple, the main API stayed very similar. The biggest deal is that it should be easier for you to stay up-to-date on dependencies. You could stop there. But if you want to know what else happened, off we go!&lt;/p&gt;

&lt;h3 id=&quot;filters-remain-the-focus&quot;&gt;Filters remain the focus&lt;/h3&gt;

&lt;p&gt;The biggest &lt;em&gt;raison d’être&lt;/em&gt; of warp is its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filter&lt;/code&gt; system. It was why I made a &lt;a href=&quot;https://seanmonstar.com/blog/warp/&quot;&gt;different web framework originally&lt;/a&gt;, and it remains &lt;em&gt;the reason&lt;/em&gt; to consider warp even now.&lt;/p&gt;

&lt;p&gt;Should &lt;em&gt;you&lt;/em&gt; use warp? That depends. If you just want a &lt;em&gt;standard&lt;/em&gt;, super fast, featureful Rust server framework, one that looks like ways you’ve coded servers before, you probably want &lt;a href=&quot;https://crates.io/crates/axum&quot;&gt;Axum&lt;/a&gt;. But, if you like functional programming, and (ab)using the type system, I think warp is pretty cool.&lt;/p&gt;

&lt;p&gt;Consider an example:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;path!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;todos&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.and&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;accept&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.and&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;// ... &lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Every filter extracts something from the request (even if that’s just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;()&lt;/code&gt;). They can all be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.and()&lt;/code&gt;ed together in any order, it will result in natural, typed arguments you can &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can build up layers of filters by just putting another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.and()&lt;/code&gt; on anything. You can make a group of filters with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.or()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once you have combined all into a master filter, you can convert them into a &lt;a href=&quot;https://crates.io/crates/tower&quot;&gt;tower&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Service&lt;/code&gt;, which you can then add any other middleware from other libraries, and serve with an HTTP implementation, such as &lt;a href=&quot;https://hyper.rs&quot;&gt;hyper&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;upgraded-to-hyper-v1&quot;&gt;Upgraded to hyper v1&lt;/h3&gt;

&lt;p&gt;The underlying HTTP dependencies, those which are public, have been upgraded to &lt;a href=&quot;https://seanmonstar.com/blog/hyper-v1&quot;&gt;hyper v1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This should improve interoperability if used with other libraries such as &lt;a href=&quot;https://crates.io/crates/reqwest&quot;&gt;reqwest&lt;/a&gt; or &lt;a href=&quot;https://crates.io/crates/tower-http&quot;&gt;tower-http&lt;/a&gt;. Their latest versions depend on v1 of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyper&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt;. So adding in more middleware, or using a higher-level HTTP client should mean less dependencies compiled, and no conversion required between types.&lt;/p&gt;

&lt;p&gt;It also means warp users will be able to stay on maintained versions of hyper and its dependencies.&lt;sup id=&quot;fnref:v014&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:v014&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h3 id=&quot;crate-features-on-a-diet&quot;&gt;Crate features on a diet&lt;/h3&gt;

&lt;p&gt;In previous versions, a few heavy features were enabled by default. Now, the default features are much slimmer.&lt;/p&gt;

&lt;p&gt;Keeping with filters being the focus, the default features only include the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filter&lt;/code&gt; system, and a way to convert them into an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;impl Service&lt;/code&gt;. You can take that service and use it with your own build of hyper, with HTTP/1 or 2 or whatever enabled, and warp won’t care.&lt;/p&gt;

&lt;p&gt;If you want the simple server runner, just enable the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server&lt;/code&gt; feature, and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;warp::serve(filters)&lt;/code&gt; is back, along with all the hyper implementation to run it.&lt;/p&gt;

&lt;p&gt;Likewise, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;warp::test&lt;/code&gt; helpers have been put behind a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; feature. You can enable it in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev-dependencies&lt;/code&gt; for testing, and while keeping that code out when building for release.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls&lt;/code&gt; feature was dropped completely. It existed previously as a simple way to add TLS to your server, but it’s not strictly required in a server framework, and it adds a bit of maintenance churn to stay up-to-date and safe. Anyone wanting TLS with their warp server can make an accept loop using the TLS implementation of their choice, and use warp’s filters as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Service&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipart&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;websocket&lt;/code&gt; features were removed from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt;, but are still available to enable. This was a planned change once they became features in the first place, since they enable a lot of code that you might not need.&lt;/p&gt;

&lt;h3 id=&quot;iterating-on-the-server-builder&quot;&gt;Iterating on the Server builder&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&lt;/code&gt; API was, uh, iterated on.&lt;/p&gt;

&lt;p&gt;Before, it had a whole bunch of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bind_*&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run_*&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serve_*&lt;/code&gt; methods that slowly grew as people wanted more options to configure the server. Now, most all of them are gone.&lt;/p&gt;

&lt;p&gt;Part of the reason is because it’s not the focus of warp. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filter&lt;/code&gt; system is. I figured the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&lt;/code&gt; is meant to be a simple way to get your filters serving requests. If you wanted more customization, make your own.&lt;/p&gt;

&lt;p&gt;But there’s another reason, too.&lt;/p&gt;

&lt;p&gt;I’ve been noticing &lt;a href=&quot;https://github.com/seanmonstar/reqwest/pull/2763&quot;&gt;more&lt;/a&gt; and &lt;a href=&quot;https://github.com/hyperium/hyper/issues/3849&quot;&gt;more places&lt;/a&gt; where I wish the API of some crate was at the same time simpler, and also more powerful, putting options into the type system. The warp &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&lt;/code&gt; was an excellent place to iterate on this.&lt;/p&gt;

&lt;p&gt;It is now an unnameable builder making use of expandable type state.&lt;/p&gt;

&lt;p&gt;It encodes as generics into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&amp;lt;F, A, R, Etc...&amp;gt;&lt;/code&gt; various parts of how the server works. The filter, the acceptor, the runner loop. Depending on what methods you call, new ones get unlocked. For instance, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bind()&lt;/code&gt; will make the acceptor a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TcpListener&lt;/code&gt;, but once one is set, it doesn’t make sense to be able to call it again.&lt;/p&gt;

&lt;p&gt;The type is rendered in the docs, but otherwise not publicly exported, so you can’t &lt;em&gt;name&lt;/em&gt; it. There’s a benefit to this, as the API designer and maintainer. I can add more options, needing more generics, without it being a breaking change. As long as add a default that keeps it all compiling, there are now instances of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&amp;lt;F, A, R&amp;gt;&lt;/code&gt; that need to be updated to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&amp;lt;F, A, R, E&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And the reality is that the name of the type is not generally interesting to you as a user. You just need a fluid way to add options, and once it’s all combined away, you just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt; a future.&lt;/p&gt;

&lt;p&gt;I’m hopeful this pattern carries over well to other builders as well.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:v014&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I’ve backported important bug fixes to hyper v0.14 (and dependencies like h2), but I think that’s about done. It’s been almost 2 years, time to focus on v1. (If you need help, reach out to me!) &lt;a href=&quot;#fnref:v014&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Wed, 06 Aug 2025 11:05:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/warp-v04/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/warp-v04/</guid>
        
        <category>rust</category>
        
        <category>http</category>
        
        <category>warp</category>
        
        <category>open-source</category>
        
        <category>programming</category>
        
        
      </item>
    
      <item>
        <title>reqwest retries</title>
        <description>&lt;p&gt;One part of working on a more &lt;a href=&quot;https://seanmonstar.com/blog/modular-reqwest/&quot;&gt;modular &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqwest&lt;/code&gt;&lt;/a&gt; was adding support for retries.&lt;/p&gt;

&lt;p&gt;Intermittent failures are a fact of life, and a simple retry could have made things so much better. It’s a somewhat frequent feature request for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqwest&lt;/code&gt;. It’s also a very common pattern to implement manually, and far too easy to do incorrectly. Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqwest&lt;/code&gt;’s internals are shifting more and more to a stack of services, it felt like an ideal time to explore this.&lt;/p&gt;

&lt;p&gt;While skeleton pieces do exist in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower::retry&lt;/code&gt;, they require Expert Mode. Doing things right is tricky. We can do better.&lt;/p&gt;

&lt;h3 id=&quot;policy-composition&quot;&gt;Policy composition&lt;/h3&gt;

&lt;p&gt;A retry policy is several things at once. But each of those things often is only one of a few choices. So what if we made it easier to compose your choices? That is, we can provide the common choices, and a builder to combine them.&lt;/p&gt;

&lt;h4 id=&quot;budgets-by-default&quot;&gt;Budgets by default&lt;/h4&gt;

&lt;p&gt;Retry counts alone are insufficient, but absurdly common. Please don’t do this. We can educate and document and warn and wave our hands, but we can only plead so much.&lt;/p&gt;

&lt;p&gt;Budgets will be default on. Sure, there’s an escape hatch if you absolutely must. But this way we can help you fall into the pit of success.&lt;/p&gt;

&lt;p&gt;And to help users better understand what the budget results in, configuration looks like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.max_extra_load(0.3)&lt;/code&gt;, instead of implementation parameters of the windowed bucket.&lt;/p&gt;

&lt;h4 id=&quot;scoped-retries&quot;&gt;Scoped retries&lt;/h4&gt;

&lt;p&gt;Budgets are basically a must. But budgets also don’t make sense across different targets. Just because requests to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;frobnica.te&lt;/code&gt; start failing doesn’t mean retries should also be disabled to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storali.ze&lt;/code&gt;. This dichotomy perturbed me quite a bit, actually, and so I put off trying to solve the whole thing for a few months. But after letting my mind rest, looking at the exact same problem revealed an obvious solution: scopes.&lt;sup id=&quot;fnref:scopes&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:scopes&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;I believe the most likely use of scopes is based on hostname. So that’s the easiest constructor to start a builder: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retry::for_host(host)&lt;/code&gt;. But it’s actually a sealed trait, to allow continued exploration. Maybe a closure is needed by some people? We’ll find out!&lt;/p&gt;

&lt;h4 id=&quot;cloning-requests&quot;&gt;Cloning requests&lt;/h4&gt;

&lt;p&gt;In many languages, this isn’t even a consideration. You can just share a pointer, and &lt;em&gt;wave hands&lt;/em&gt; mostly reuse it.&lt;/p&gt;

&lt;p&gt;In Rust, with ownership, we need to keep a clone of the request for a potential retry.&lt;/p&gt;

&lt;p&gt;At the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyper&lt;/code&gt; level, we don’t know enough about the request body to automatically clone it. At &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqwest&lt;/code&gt;’s level, we &lt;em&gt;do&lt;/em&gt; know enough: we can absolutely clone when it’s a simple non-streaming body. (Even possibly more, discussed below.)&lt;/p&gt;

&lt;h4 id=&quot;classifiers&quot;&gt;Classifiers&lt;/h4&gt;

&lt;p&gt;You might have a very custom combination of routes and responses. Or an API where every idempotent request has standard status codes. Or you might need a deeper understanding of specific connection or protocol errors.&lt;/p&gt;

&lt;p&gt;So to start, there’s a simpler &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;classify_fn&lt;/code&gt; option, where you provide a closure to determine whether to retry or not.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;.classify_fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req_rep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req_rep&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req_rep&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SERVICE_UNAVAILABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req_rep&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.retryable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// or check req_rep.error()?&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req_rep&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another possible avenue to explore is the composition of classifiers. For example, what if you combined something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;protocol_nacks.or(idempotent.and(service_unavailable))&lt;/code&gt;? Is this a good idea? Would people want this? If you want to &lt;em&gt;use&lt;/em&gt; something like that, file an issue or PR.&lt;/p&gt;

&lt;h4 id=&quot;probable-enhancements&quot;&gt;Probable enhancements&lt;/h4&gt;

&lt;p&gt;Besides the above, there’s a few other things we could add to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqwest::retry&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Backoffs:&lt;/strong&gt; They’re pretty easy to add, though the benefit they bring is only really noticeable in specific situations. This is why they aren’t included yet or would not even be enabled by default.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Replayable Body:&lt;/strong&gt; Retrying needs to clone the request, and a streaming body is not freely able to be cloned and restarted. But it’s desirable by users, so we could make it an easy configurable option, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.max_replay_body_bytes()&lt;/code&gt;. We could likely learn a bit from &lt;a href=&quot;https://linkerd.io/2021/10/26/how-linkerd-retries-http-requests-with-bodies/&quot;&gt;how Linkerd implemented it&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Per-Request Retries:&lt;/strong&gt; More and more, users ask for configuration for a specific request that differs from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Client&lt;/code&gt;. We’ve been working on &lt;a href=&quot;https://github.com/seanmonstar/reqwest/issues/2641&quot;&gt;general per-request config system&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;exploring-and-upstreaming&quot;&gt;Exploring and upstreaming&lt;/h3&gt;

&lt;p&gt;We’re exploring these ideas in reqwest, with the goal of upstreaming the more generic ideas to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower-http&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Maybe that’s a builder:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;tower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;retry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.clone_reqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.matcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RETRY_AFTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.max_extra_load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or maybe something closer to middleware, a trait to compose pieces, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;and()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.or()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;tower_http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;retry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clone_replayable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8192&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.and&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idempotent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.and&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.or&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;protocol_nack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.and&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;budget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Maybe a combination! Who knows?&lt;/p&gt;

&lt;h3 id=&quot;check-it-out&quot;&gt;Check it out&lt;/h3&gt;

&lt;p&gt;There’s a &lt;a href=&quot;https://github.com/seanmonstar/reqwest/pull/2763&quot;&gt;pull request&lt;/a&gt; implementing some of these ideas, and your feedback and usage will help us to keep refining it.&lt;/p&gt;

&lt;p&gt;The two-pronged goal is to allow you to easily get retries in reqwest, and to also be able to get that easier configuration in &lt;em&gt;any&lt;/em&gt; tower stack.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/seanmonstar/reqwest/pull/2763&quot;&gt;Give a whirl!&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:scopes&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Scoping budgets wouldn’t be something to care about if the client were already built for a specific endpoint. That’s a common way to build a stack of tower layers. But reqwest is open-ended, you can send requests to anywhere with an address. So, scopes felt required. &lt;a href=&quot;#fnref:scopes&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 15 Jul 2025 10:39:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/reqwest-retries/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/reqwest-retries/</guid>
        
        <category>rust</category>
        
        <category>http</category>
        
        <category>reqwest</category>
        
        <category>tower</category>
        
        <category>retries</category>
        
        <category>open-source</category>
        
        <category>programming</category>
        
        
      </item>
    
      <item>
        <title>Body::poll_progress</title>
        <description>&lt;p&gt;This describes a proposal for a cancelation problem with hyper’s request and response bodies. &lt;a href=&quot;https://hyper.rs&quot;&gt;hyper&lt;/a&gt; is an HTTP library for the Rust language.&lt;/p&gt;

&lt;h3 id=&quot;background-what-is-the-body-trait&quot;&gt;Background: what is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Body&lt;/code&gt; trait?&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Body&lt;/code&gt; trait used by &lt;a href=&quot;https://hyper.rs&quot;&gt;hyper&lt;/a&gt; is meant to represent a potentially streaming (asynchronous) body of a request or response. It sorta looks like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stream&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsyncIterator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The biggest reason for a different trait was because we needed a stable trait for &lt;a href=&quot;https://seanmonstar.com/blog/hyper-v1/&quot;&gt;hyper v1&lt;/a&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stream&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsyncIterator&lt;/code&gt; were not going to be (and still aren’t) stable. With it being a separate trait, though, we also added some HTTP-body-specific methods to it.&lt;/p&gt;

&lt;p&gt;Its similarity to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stream&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsyncIterator&lt;/code&gt; means we run into a problem with forwarding into a sink. But that we own the trait also lends to a decent solution, which this post outlines.&lt;/p&gt;

&lt;p&gt;First though, piping woes.&lt;/p&gt;

&lt;h3 id=&quot;problem-backpressured-cancelation&quot;&gt;Problem: backpressured cancelation&lt;/h3&gt;

&lt;p&gt;Piping stream-like things into sink-like destinations feels natural, and looks quite simple. Elegant even. They can be piped together, and backpressure occurs naturally. However, they lack a mechanism to completely propagate cancelation. More specifically, cancelation while backpressure is currently applied.&lt;/p&gt;

&lt;p&gt;Consider an example:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// what if body (^) cancels while we wait here?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simple loop is piping a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Body&lt;/code&gt; into some sort of sink. The way most streams and channels work, this mostly propagates backpressure. As long as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dst&lt;/code&gt; is not able to send a frame, we don’t poll for another one, and whatever is the source of the body will back up.&lt;/p&gt;

&lt;p&gt;However, it has a flaw. Those familiar with writing proxies may notice it immediately. The destination might not have space, and so we’ll wait for it to get more. But the body could give up during that time. Since the task is only waiting on when the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dst&lt;/code&gt; is ready, it could wait a significant amount of time and never notice that the body (source) has canceled.&lt;/p&gt;

&lt;h4 id=&quot;why-not-just-timeouts&quot;&gt;Why not just timeouts?&lt;/h4&gt;

&lt;p&gt;One initial question was “why not just timeouts”. Like, why not just add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body_write_timeout()&lt;/code&gt; or something to hyper’s connection builders. Fair. That would work in some cases, for sure.&lt;/p&gt;

&lt;p&gt;The thing is, sometimes timeouts are inappropriate.&lt;/p&gt;

&lt;p&gt;For example, a transparent proxy may not want to force timeouts where they didn’t exist before. They’re willing to wait nearly forever. They just want to cancel the forwarding if the sender gives up.&lt;/p&gt;

&lt;p&gt;Additionally, a chain of potential timeouts means that cancelation propagation can be delayed longer and longer, as each hop in the chain has to wait its own timeout.&lt;/p&gt;

&lt;h4 id=&quot;more-generally-yet-not&quot;&gt;More generally, yet not&lt;/h4&gt;

&lt;p&gt;This issue can also exist for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stream&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsyncIterator&lt;/code&gt;. A &lt;a href=&quot;https://without.boats/blog/poll-progress/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poll_progress&lt;/code&gt; idea was outlined by withoutboats&lt;/a&gt;, though it was solving a different problem.&lt;/p&gt;

&lt;p&gt;I do think there’s some crossover that may affect each other. But it’s also worth considering separately.&lt;/p&gt;

&lt;p&gt;I’m not talking about a concept that some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for await&lt;/code&gt; syntax could magically care for. I don’t want that. Like at all. In the above example, it’d be impossible for a compiler to determine which action I want to take. Maybe the source ended cleanly. I don’t want that to cancel sending the last item.&lt;/p&gt;

&lt;p&gt;Also, and this is a biggie: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Body&lt;/code&gt; will need to solve this problem quicker.&lt;/p&gt;

&lt;h3 id=&quot;solution-bodypoll_progress&quot;&gt;Solution: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Body::poll_progress()&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;We &lt;a href=&quot;https://github.com/hyperium/http-body/pull/90&quot;&gt;propose&lt;/a&gt; to add a method to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Body&lt;/code&gt; trait:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// existing methods ...&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;poll_progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Pin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The purpose of the method is allow for it to make any “progress” that might result in cancelation, while not producing another frame. The details:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poll_*&lt;/code&gt; function, so it allows a body to poll something else async, like a timeout, or perhaps some shutdown channel.&lt;/li&gt;
  &lt;li&gt;Returning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ready(Ok(()))&lt;/code&gt; immediately is fine, it simply means there’s &lt;em&gt;nothing&lt;/em&gt; that the body would do before another frame could be made available.&lt;/li&gt;
  &lt;li&gt;However, returning an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Err&lt;/code&gt; would indicate that the body is canceled, and a piping task can interpret that to shutdown the forwarding operation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We could provide some utilities to aid use of this function, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http_body_util::BodyExt::progress()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Consider an example, something that could be even be used in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyper&lt;/code&gt; internals, but could also occur elsewhere:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// produce a frame&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;select!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.hup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// unexpected EOF&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// send it&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;select!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// continue&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;alternatives-not-poll_closed&quot;&gt;Alternatives: not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poll_closed&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;An alternative we considered was making this method be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poll_closed&lt;/code&gt;. It had the welcome effect of meaning we could await &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body.closed()&lt;/code&gt;, and that is fairly self-documenting. However, it ran into several problems.&lt;/p&gt;

&lt;p&gt;Being an addition to an existing trait, it needs a default implementation. But a default for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poll_closed&lt;/code&gt; would likely end up being confusing. If it returns closed by default, then existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;impl Body&lt;/code&gt;s will suddenly start aborting early. If it returns not-closed, then any naive task that might await &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body.closed()&lt;/code&gt; would hang forever.&lt;/p&gt;

&lt;p&gt;The return value also felt confusing however we put it. Does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;closed&lt;/code&gt; return a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&lt;/code&gt;? Both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ok&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Err&lt;/code&gt; would still mean the body had closed.&lt;/p&gt;

&lt;h3 id=&quot;request-for-comments&quot;&gt;Request for Comments&lt;/h3&gt;

&lt;p&gt;I wrote this up because it felt like a big enough change to a fundamental mechanism in the ecosystem that it’d could benefit from more eyes and comments. It kinda-sorta looks like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsyncIterator::poll_progress&lt;/code&gt;, but it’s also not. Still, are there things we should prepare for? Would wg-async have thoughts? Others who are using hyper deeply?&lt;/p&gt;

&lt;p&gt;Could it be simpler? Is it flexible enough?&lt;sup id=&quot;fnref:redesign&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:redesign&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Comments on the &lt;a href=&quot;https://github.com/hyperium/http-body/pull/90&quot;&gt;pull request are most welcome&lt;/a&gt;!&lt;sup id=&quot;fnref:thanks&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:thanks&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:redesign&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Calls for completely changing hyper’s design to not need such a function aren’t helpful. hyper is stable at v1. &lt;a href=&quot;#fnref:redesign&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:thanks&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Thanks to Steven Fackler and Oliver Gould for helping iterate on this design. &lt;a href=&quot;#fnref:thanks&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 22 Apr 2025 10:01:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/body-poll-progress/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/body-poll-progress/</guid>
        
        <category>rust</category>
        
        <category>http</category>
        
        <category>hyper</category>
        
        <category>open-source</category>
        
        <category>programming</category>
        
        
      </item>
    
      <item>
        <title>A More Modular reqwest</title>
        <description>&lt;p&gt;reqwest came out &lt;a href=&quot;https://seanmonstar.com/blog/introducing-reqwest/&quot;&gt;8 years ago&lt;/a&gt;. It was meant to be a higher-level HTTP client, with batteries and opinions included. And it continues to fulfill that role very well.&lt;/p&gt;

&lt;p&gt;To do so, reqwest combined or implemented a lot of features that weren’t easily accessible elsewhere in Rust. Things like redirect handling, connection proxies, and compression.&lt;/p&gt;

&lt;p&gt;People love it. It’s the most popular HTTP client used in Rust code. As of today, it gets downloaded 10 million times per month from crates.io (which doesn’t include any internal mirrors). It helps engineers all around the world.&lt;/p&gt;

&lt;p&gt;People also wish they could better customize it. They want to add in custom logic somewhere. This makes sense. And the ecosystem has gotten &lt;a href=&quot;https://tokio.rs/blog/2021-05-announcing-tower-http&quot;&gt;even better at reuse&lt;/a&gt;. It’s time for reqwest to help be more reusable.&lt;/p&gt;

&lt;p&gt;First, what is the goal?&lt;/p&gt;

&lt;h3 id=&quot;easy-and-modular&quot;&gt;Easy and modular&lt;/h3&gt;

&lt;p&gt;Top priority is that reqwest remain &lt;strong&gt;easy&lt;/strong&gt; to use. It should be the best default for most people. If you don’t know what customizations you want, you shouldn’t have to care.&lt;/p&gt;

&lt;p&gt;It should also be &lt;em&gt;possible&lt;/em&gt; to customize. To tinker. To grab just a single piece. To re-order the pieces. To add in completely new pieces.&lt;/p&gt;

&lt;h3 id=&quot;some-concrete-work&quot;&gt;Some concrete work&lt;/h3&gt;

&lt;p&gt;So, how do we get there? Well, here’s a start.&lt;/p&gt;

&lt;p&gt;The connection pool can be changed from a single Pool type into a series of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Service&lt;/code&gt;s that allow better composition, such as a racing cache pool, singleton pool for HTTP/2, an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Either&lt;/code&gt; pool to combine them. This allows people to use less complicated pools than one that includes connection racing. It makes way for using tower’s load balancing services directly within reqwest, even.&lt;/p&gt;

&lt;p&gt;The HTTP Proxy internals can be refactored to be another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Service&lt;/code&gt; which fits into the series of “connector” services reqwest uses. These include loading proxy data from environment variables, and providing HTTP/HTTPS/SOCKS proxies and tunneling. Other applications that only use reqwest for proxying can depend on only those utilities.&lt;/p&gt;

&lt;p&gt;reqwest supports some basic redirect policies, and allows for custom ones. Eventually, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower-http&lt;/code&gt; added middleware to do that too. We can now use redirects from tower-http. It’d be better to update reqwest to depend on that middleware internally, allowing reqwest’s test suite to find any pieces lacking in the port.&lt;/p&gt;

&lt;p&gt;We can add &lt;em&gt;easy&lt;/em&gt; request retries. reqwest doesn’t really have retries, other than a very specific case: special HTTP/2 error cases. And conceptually, retries aren’t that different from redirects. Retrying requests is pretty common thing to ask for, and it’s pretty easy to do them wrong. There’s middleware in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower::retry&lt;/code&gt;, but it’s not easy. We could make it as easy as calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.retries(reqwest::retry::idempotent())&lt;/code&gt; on a client builder. Or a few other common policies.&lt;/p&gt;

&lt;p&gt;We can use decompression from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower-http&lt;/code&gt;. reqwest has automatic response body decompression. Eventually, it was added as middleware in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tower-http&lt;/code&gt;. Instead of having the feature essentially duplicated, we can just depend on the modular version, and the test suite will keep the quality up. Doing it this way improves the middleware for everyone. For instance, when one user swapped from reqwest’s decompression to using tower-http, performance got worse. Eventually, a reused allocation was backported, bringing it back in line.&lt;/p&gt;

&lt;h3 id=&quot;better-default-tls&quot;&gt;Better default TLS&lt;/h3&gt;

&lt;p&gt;Initially, reqwest made use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;native-tls&lt;/code&gt; for HTTPS support. Eventually, we added optional support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustls&lt;/code&gt;. But we’ve kept &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;native-tls&lt;/code&gt; as the default. The reason was because it worked for more users, and more sites. However, I think it’s time to change the defaults.&lt;/p&gt;

&lt;p&gt;At this point, reqwest’s defaults actually target the worst common denominator. It not longer feels right to make people opt-in to the better choice. As I said at the beginning, reqwest bundles opinions too.&lt;/p&gt;

&lt;p&gt;Our opinion is that reqwest should have the &lt;em&gt;best&lt;/em&gt; defaults for the most people. Those that need something different can opt-in to that.&lt;sup id=&quot;fnref:defaults&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:defaults&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;So, reqwest will be changing it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default-tls&lt;/code&gt; feature to rely on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustls&lt;/code&gt;. Likely with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws-lc-rs&lt;/code&gt; as the crypto backend. Optional support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;native-tls&lt;/code&gt; will remain, for now.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default-tls&lt;/code&gt; feature theoretically was designed to allow for such a change, since nothing should be exposed with it that isn’t supported by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustls&lt;/code&gt;. But they do have different build requirements, so it won’t be 100% smooth.&lt;/p&gt;

&lt;p&gt;But it will be better for most people.&lt;/p&gt;

&lt;h3 id=&quot;getting-to-work&quot;&gt;Getting to work&lt;/h3&gt;

&lt;p&gt;That’s the work. Keeping reqwest easy. Allowing more customization. Even letting people use fewer parts of it. While having strong opinions. Making it better for most people.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to be part of it?&lt;/strong&gt; Help contribute code or documentation. Here’s &lt;a href=&quot;https://github.com/users/seanmonstar/projects/3&quot;&gt;the issue plan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also &lt;a href=&quot;https://seanmonstar.com/sponsor&quot;&gt;sponsor&lt;/a&gt; the work. Roadmaps and priorities like these come about from conversations with companies that keep retainers.&lt;/p&gt;

&lt;p&gt;All of it helps! To making HTTP requests better!&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:defaults&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;This could also be applied to other features too. I think there’s an argument to be made for switching the default DNS resolver to use hickory-dns instead &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getaddrinfo&lt;/code&gt;. &lt;a href=&quot;#fnref:defaults&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 04 Mar 2025 13:05:00 +0000</pubDate>
        <link>https://seanmonstar.com/blog/modular-reqwest/</link>
        <guid isPermaLink="true">https://seanmonstar.com/blog/modular-reqwest/</guid>
        
        <category>rust</category>
        
        <category>reqwest</category>
        
        <category>tower</category>
        
        <category>http</category>
        
        <category>open-source</category>
        
        <category>programming</category>
        
        
      </item>
    
  </channel>
</rss>
