|
| 1 | += ADR 001: HTTP/2 API |
| 2 | + |
| 3 | +Date: 2023-04-10 |
| 4 | + |
| 5 | + |
| 6 | +== Status |
| 7 | + |
| 8 | +#Proposed# |
| 9 | + |
| 10 | +A decision may be "proposed" if the project stakeholders haven't agreed with it |
| 11 | +yet, or "accepted" once it is agreed. If a later ADR changes or reverses a |
| 12 | +decision, it may be marked as "deprecated" or "superseded" with a reference to |
| 13 | +its replacement. |
| 14 | + |
| 15 | +== Context |
| 16 | + |
| 17 | +- https://datatracker.ietf.org/doc/html/rfc9113 |
| 18 | +- https://web.dev/performance-http2/ |
| 19 | +- https://www.rfc-editor.org/rfc/rfc8441 |
| 20 | + |
| 21 | +=== HTTP/2 |
| 22 | + |
| 23 | +HTTP/2 is eight years old now, and quite popular. It adds numerous advantages, |
| 24 | +some invisible, some requiring user coding. Adding support in both the client and |
| 25 | +server is obvious, but the installed base of Aleph is quite large (1.8m downloads), |
| 26 | +so we have a duty to maintain backwards compatibility while also exposing HTTP/2 |
| 27 | +capabilities to those who want/need it. |
| 28 | + |
| 29 | +The default API Aleph presents is a handler fn that receives a single Ring map |
| 30 | +with an `InputStream` body (potentially transformed by middleware), and returns |
| 31 | +a Ring response map, with a static or streamable body. |
| 32 | + |
| 33 | +HTTP/2 offers new features that users may want to take advantage of: flow |
| 34 | +control, in particular. HTTP/2 introduced other features, in particular server |
| 35 | +push and prioritization, but they did not work out. Server push (the ability to |
| 36 | +send "responses" before receiving a request) has been criticized as difficult |
| 37 | +to use correctly, and effectively https://chromestatus.com/feature/6302414934114304[disabled by Chrome]. |
| 38 | +Prioritization hasn't been disabled exactly, but it has various problems, and |
| 39 | +was replaced with a simpler scheme for HTTP/3. |
| 40 | + |
| 41 | +Since the Aleph team is small, we have to carefully consider what to support. |
| 42 | +Full HTTP/2 support may not be advisable. |
| 43 | + |
| 44 | +=== Prioritization |
| 45 | + |
| 46 | +- https://datatracker.ietf.org/doc/html/rfc9218 |
| 47 | +- https://calendar.perfplanet.com/2022/http-3-prioritization-demystified/ |
| 48 | +- https://blog.cloudflare.com/better-http-2-prioritization-for-a-faster-web/ |
| 49 | +- https://calendar.perfplanet.com/2018/http2-prioritization/ |
| 50 | +- https://github.com/andydavies/http2-prioritization-issues |
| 51 | + |
| 52 | +Prioritization in HTTP/2 is considered overly-complicated and was partially |
| 53 | +deprecated in RFC 9113. The browsers have different interpretations of how to |
| 54 | +arrange the priority tree, and Safari/Edge do not utilize it much at all. Many |
| 55 | +HTTP servers have broken or nonresponsive implementations. Many Linux servers |
| 56 | +have TCP buffers too large too effectively handle preempting lower-priority |
| 57 | +resources or change existing priorities. |
| 58 | + |
| 59 | +Because of this RFC 9218 (Extensible Prioritization) was introduced. For HTTP/2, |
| 60 | +RFC 9218 is suggested as a backwards-compatible alternative, since it depends |
| 61 | +only on standard HTTP headers; it was adopted for HTTP/3 as the primary |
| 62 | +prioritization scheme. |
| 63 | + |
| 64 | +=== Server Push |
| 65 | + |
| 66 | +- https://docs.google.com/document/d/1K0NykTXBbbbTlv60t5MyJvXjqKGsCVNYHyLEXIxYMv0/edit |
| 67 | +- https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/ |
| 68 | +- https://developer.chrome.com/blog/removing-push/ |
| 69 | +- https://chromestatus.com/feature/6302414934114304 |
| 70 | + |
| 71 | +HTTP/2 added the ability for servers to push unrequested data to clients, via |
| 72 | +the PUSH_PROMISE frame type. In theory, it can make better use of bandwidth by |
| 73 | +not waiting for browsers to discover and request necessary resources. In |
| 74 | +practice, it was found difficult to do so, accounting for buffer bloat, RTT, and |
| 75 | +browser caches, and not make things _worse_. Chrome effectively disabled it in |
| 76 | +Sept 2022. |
| 77 | + |
| 78 | +=== Websockets |
| 79 | + |
| 80 | +Websockets were built on top of HTTP/1.1-only fields, like the Upgrade and |
| 81 | +Connection headers and the 101 status code. RFC 8441 describes an HTTP/2 extension |
| 82 | +that allows websockets to be tunneled over an HTTP/2 stream. This allows efficient |
| 83 | +sharing of a single TCP connection for both HTTP/2 and websocket traffic, at the |
| 84 | +cost of a more complicated implementation. Netty does not have built-in support |
| 85 | +for it, though https://github.com/jauntsdn/netty-websocket-http2[user implementations] |
| 86 | +exist. |
| 87 | + |
| 88 | +=== Proxies |
| 89 | + |
| 90 | +HTTP/2 supports CONNECT proxying, but over a single HTTP/2 stream, not a whole TCP connection. Aleph has only ever supported using proxies from the client-side. |
| 91 | + |
| 92 | +== Decision |
| 93 | + |
| 94 | +=== Backwards-compatibility mode |
| 95 | + |
| 96 | +We will maintain a backwards-compatible API for the majority of users, where |
| 97 | +their existing handlers will work as is. All streams will have equal priority, |
| 98 | +and flow control will ... |
| 99 | + |
| 100 | +NOTE: What should the default flow control strategy be? An infinitely large |
| 101 | +window? The default 64kb window, that auto-replenishes as soon as bytes are read |
| 102 | +off? Netty offers both of the previous as default strategies available for use. |
| 103 | + |
| 104 | +NOTE: What should a raw stream look like? A sequence of ByteBufs? Or a sequence |
| 105 | +of Frames, which is Netty's "raw" level for HTTP/2? |
| 106 | + |
| 107 | +=== HTTP/2 connection mode |
| 108 | +We will NOT support server push, as our resources are too limited relative to |
| 109 | +the minimal potential benefit. Outside PRs may be considered, depending on the |
| 110 | +expected maintenance burden. |
| 111 | + |
| 112 | +We will expose a connection-level handler API, where the handler can access the |
| 113 | +entire connection, all streams in the connection, and all frames in the streams. |
| 114 | +We will expose APIs to change connection and stream settings. |
| 115 | + |
| 116 | +We will add APIs to get/set stream priorities and dependencies. We will NOT do |
| 117 | +anything with that knowledge. It will be up to the users to decide what to do |
| 118 | +with the priority/dependencies. |
| 119 | + |
| 120 | +NOTE: Is there an obvious priority/dependency win we can offer? Maybe from Netty? |
| 121 | + |
| 122 | +NOTE: Is there a way to assist in things like pausing or throttling other streams? |
| 123 | +Manifold's `throttle` doesn't support a function to change throttling behavior. |
| 124 | +And nothing supports pausing a stream at the moment. |
| 125 | + |
| 126 | +We will add APIs to expose flow control for getting/setting both stream-level |
| 127 | +and connection-level window sizes. |
| 128 | + |
| 129 | +NOTE: Do we need to do anything special to incorporate Manifold backpressure |
| 130 | +into this somehow? |
| 131 | + |
| 132 | +=== Websockets |
| 133 | + |
| 134 | +We will NOT add support for HTTP/2 websocket tunneling at this time. The |
| 135 | +cost/benefit ratio is too low. PRs may be considered, though. |
| 136 | + |
| 137 | +=== Proxies |
| 138 | + |
| 139 | +NOTE: TBD |
| 140 | + |
| 141 | +== Consequences |
| 142 | + |
| 143 | +This will improve web support for Clojure. I believe http-kit doesn't support |
| 144 | +HTTP/2, and Pedestal, if it supports it, only does so through Jetty, which is |
| 145 | +not very performant. |
| 146 | + |
| 147 | +This will give us a chance to improve the clarity of the code. |
| 148 | + |
| 149 | +As with any major change, this will add to the maintenance burden, and run the |
| 150 | +risk of breaking things, not just in HTTP/2 code, but also in pre-existing, |
| 151 | +overlapping HTTP/1.1 code. |
| 152 | + |
| 153 | +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' |
| 154 | + |
| 155 | +See https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions[Documenting architecture decisions - Michael Nygard] |
| 156 | +for the format and rationale of this document. |
0 commit comments