|
| 1 | +# XrdHTTP |
| 2 | + |
| 3 | +XrdHTTP is the HTTP and HTTPS protocol handler for XRootD. It exposes storage |
| 4 | +operations (GET, PUT, HEAD, DELETE, WebDAV, checksums, third-party copy, and |
| 5 | +more) over standard HTTP semantics and bridges authenticated requests into the |
| 6 | +native XRootD protocol via `XrdXrootdTransit`. |
| 7 | + |
| 8 | +The implementation lives primarily in `XrdHttpProtocol` (connection lifecycle, |
| 9 | +TLS, configuration) and `XrdHttpReq` (per-request state, body I/O, Bridge |
| 10 | +interaction). Since the wire-layer re-platform, HTTP framing is handled by |
| 11 | +components under `wire/` while application logic remains shared across HTTP/1 |
| 12 | +and HTTP/2. |
| 13 | + |
| 14 | +## Architecture |
| 15 | + |
| 16 | +``` |
| 17 | + Client |
| 18 | + | |
| 19 | + v |
| 20 | + XrdHttpProtocol::Process() |
| 21 | + | |
| 22 | + +-- TLS handshake (HTTPS) |
| 23 | + +-- detectWireMode() (ALPN / H2 preface / SETTINGS heuristic) |
| 24 | + | |
| 25 | + +-- HTTP/1.1 +-- HTTP/2 (HTTPS + ALPN h2) |
| 26 | + | getDataOneShot() | XrdHttp2Session::drive() |
| 27 | + | header parse (llhttp or legacy) | nghttp2 stream assembly |
| 28 | + | | |
| 29 | + +------------------+------------------+ |
| 30 | + v |
| 31 | + XrdHttpProtocol::processParsedRequest() |
| 32 | + | |
| 33 | + v |
| 34 | + XrdHttpReq (body, auth, handlers) |
| 35 | + | |
| 36 | + v |
| 37 | + XrdXrootdTransit / Bridge->Run() |
| 38 | + | |
| 39 | + v |
| 40 | + XrdHttp1ResponseWriter or XrdHttp2ResponseWriter |
| 41 | +``` |
| 42 | + |
| 43 | +Both wire protocols funnel parsed requests into the same application entry |
| 44 | +point. Auth, CGI mapping, redirects, range reads, WebDAV, and Bridge semantics |
| 45 | +are unchanged above that boundary. |
| 46 | + |
| 47 | +### Wire layer (`wire/`) |
| 48 | + |
| 49 | +| Component | Role | |
| 50 | +|-----------|------| |
| 51 | +| `XrdHttp1Session` | HTTP/1.1 request-line and header parsing via [llhttp](vendor/llhttp/) | |
| 52 | +| `XrdHttp1ResponseWriter` | HTTP/1.1 response headers, fixed-length bodies, and chunked encoding | |
| 53 | +| `XrdHttp2Session` | nghttp2 session management, stream state, `drive()` I/O loop | |
| 54 | +| `XrdHttp2ResponseWriter` | HTTP/2 response headers and DATA frames | |
| 55 | +| `XrdHttpConnection.hh` | `XrdHttpWireMode` enum (`kHttp1`, `kHttp2`) | |
| 56 | + |
| 57 | +See [`wire/README.md`](wire/README.md) for wire-layer specifics. |
| 58 | + |
| 59 | +### Application layer (largely unchanged) |
| 60 | + |
| 61 | +| File | Role | |
| 62 | +|------|------| |
| 63 | +| `XrdHttpProtocol.cc` | `Process()`, TLS, configuration, `processParsedRequest()` | |
| 64 | +| `XrdHttpReq.cc` | Request state machine, body framing, file ops, Bridge calls | |
| 65 | +| `XrdHttpSecurity.cc` | TLS client auth, token validation | |
| 66 | +| `XrdHttpExtHandler.cc` | Pluggable external handlers (e.g. TPC) | |
| 67 | +| `XrdHttpReadRangeHandler.cc` | Byte-range and multipart responses | |
| 68 | +| `XrdHttpChecksum*.cc` | Checksum discovery and computation | |
| 69 | +| `XrdHttpMon.cc` | Monitoring hooks | |
| 70 | + |
| 71 | +Feature-specific documentation: |
| 72 | + |
| 73 | +- [Kerberos over HTTPS](README-KRB5.md) |
| 74 | +- [Checksums](README-CKSUM.md) |
| 75 | +- [HTTPS third-party copy](../XrdHttpTpc/README.md) |
| 76 | + |
| 77 | +## HTTP/1.1: new wire path vs legacy parser |
| 78 | + |
| 79 | +HTTP/1.1 header parsing has **two** selectable implementations. The default is |
| 80 | +llhttp; the previous line-based parser remains for regression testing. |
| 81 | + |
| 82 | +| Layer | Default (llhttp) | Legacy (`http.parser legacy`) | |
| 83 | +|-------|------------------|-------------------------------| |
| 84 | +| Request headers | `XrdHttp1Session` + llhttp | `BuffgetLine()` + `XrdHttpReq::parseFirstLine()` / `parseLine()` | |
| 85 | +| Request body | Existing `XrdHttpReq` read path | Same | |
| 86 | +| Responses | `XrdHttp1ResponseWriter` | Same | |
| 87 | + |
| 88 | +llhttp parses **headers only**. Content-Length, chunked upload bodies, and |
| 89 | +range-read streaming still use the long-standing read path in `XrdHttpReq`. |
| 90 | + |
| 91 | +The legacy parser is the original naive line/token implementation. It is not |
| 92 | +removed; select it explicitly when comparing behaviour or running the legacy |
| 93 | +regression test suite. |
| 94 | + |
| 95 | +## HTTP/2 |
| 96 | + |
| 97 | +HTTP/2 is available when XRootD is built with nghttp2 (`BUILD_HTTP2`). |
| 98 | + |
| 99 | +- Negotiated on **HTTPS** connections via TLS ALPN (`h2`). |
| 100 | +- Plain HTTP connections remain HTTP/1.1. |
| 101 | +- `detectWireMode()` also recognises the cleartext connection preface and a |
| 102 | + SETTINGS-frame heuristic for edge cases. |
| 103 | +- Multiple requests on one connection are supported (same-connection reuse). |
| 104 | +- `Http2OutboundPending()` defers connection close while response DATA is still |
| 105 | + queued. |
| 106 | + |
| 107 | +## Request lifecycle |
| 108 | + |
| 109 | +1. **`Process(lp)`** — Main scheduler entry point. Reads socket data (HTTP/1), |
| 110 | + drives the nghttp2 session (HTTP/2), or re-enters after Bridge callbacks |
| 111 | + (`lp == nullptr`). |
| 112 | + |
| 113 | +2. **Header parsing** — Populates `CurrentReq` (`XrdHttpReq`). On success, |
| 114 | + `CurrentReq.headerok` is set. |
| 115 | + |
| 116 | +3. **`processParsedRequest(lp)`** — Shared application handler: auth checks, |
| 117 | + self-redirect, login, method dispatch (GET, PUT, PROPFIND, …). |
| 118 | + |
| 119 | +4. **`Bridge->Run()`** — Async file operations via `XrdXrootdTransit`. Bridge |
| 120 | + callbacks re-invoke `Process(nullptr)`. |
| 121 | + |
| 122 | +5. **Response** — `StartSimpleResp()` / `SendSimpleResp()` route to |
| 123 | + `XrdHttp1ResponseWriter` or `XrdHttp2ResponseWriter` based on |
| 124 | + `wireMode_`. |
| 125 | + |
| 126 | +### Scheduler return codes |
| 127 | + |
| 128 | +`Process()` return values follow XRootD scheduler semantics (see |
| 129 | +`src/Xrd/XrdLinkXeq.cc`): |
| 130 | + |
| 131 | +| Return | Meaning | |
| 132 | +|--------|---------| |
| 133 | +| `0` | Continue processing (stick loop or Transit `reInvoke`) | |
| 134 | +| `1` | Idle — wait for poll I/O | |
| 135 | +| `< 0` | Close the connection | |
| 136 | + |
| 137 | +HTTP/2 `drive()` must preserve this contract: return `1` when idle and waiting |
| 138 | +for bytes, but return `0` after `Bridge->Run()` so Transit can re-enter. Forcing |
| 139 | +idle `0 → 1` globally breaks Bridge integration. |
| 140 | + |
| 141 | +## Configuration |
| 142 | + |
| 143 | +Example server stanza: [`xrootd-http.cf`](xrootd-http.cf). |
| 144 | + |
| 145 | +```cfg |
| 146 | +xrd.protocol XrdHttp /usr/lib64/libXrdHttp.so |
| 147 | +
|
| 148 | +http.cert /etc/grid-security/hostcert.pem |
| 149 | +http.key /etc/grid-security/hostkey.pem |
| 150 | +http.cadir /etc/grid-security/certificates |
| 151 | +
|
| 152 | +# HTTP/1.1 header parser (default: llhttp) |
| 153 | +http.parser llhttp |
| 154 | +# http.parser legacy |
| 155 | +
|
| 156 | +# Optional feature modules |
| 157 | +# http.exthandler xrdtpc libXrdHttpTPC.so |
| 158 | +# http.auth krb5 |
| 159 | +``` |
| 160 | + |
| 161 | +| Directive | Values | Notes | |
| 162 | +|-----------|--------|-------| |
| 163 | +| `http.parser` | `llhttp`, `legacy` | HTTP/1.1 header parsing only | |
| 164 | +| `http.tlsclientauth` | `on`, `off` | TLS client certificate authentication | |
| 165 | +| `http.selfhttps2http` | `yes`, `no` | Signed redirect from HTTPS to HTTP | |
| 166 | +| `http.auth` | `krb5`, `tpc`, … | See feature READMEs | |
| 167 | + |
| 168 | +HTTP/2 requires no separate config directive; it is negotiated at the TLS layer |
| 169 | +when the build includes nghttp2 and the client offers ALPN `h2`. |
| 170 | + |
| 171 | +## Build |
| 172 | + |
| 173 | +HTTP support is controlled by `ENABLE_HTTP`. HTTP/2 additionally requires |
| 174 | +**libnghttp2** (the development library, not tools-only packages): |
| 175 | + |
| 176 | +```bash |
| 177 | +# macOS |
| 178 | +brew install libnghttp2 |
| 179 | + |
| 180 | +cmake .. -DENABLE_HTTP=ON |
| 181 | +# HTTP/2 is enabled automatically when libnghttp2 is found (BUILD_HTTP2) |
| 182 | + |
| 183 | +make XrdHttpUtils XrdHttp |
| 184 | +``` |
| 185 | + |
| 186 | +Optional Kerberos HTTP auth: `-DENABLE_KRB5=ON` (see [README-KRB5.md](README-KRB5.md)). |
| 187 | + |
| 188 | +Build outputs: |
| 189 | + |
| 190 | +- `libXrdHttpUtils.so` — Protocol implementation (shared; plugins link against it) |
| 191 | +- `libXrdHttp.so` — Protocol plugin loaded by `xrd.protocol` |
| 192 | + |
| 193 | +## Tests |
| 194 | + |
| 195 | +From the build directory, after configuring with HTTP enabled: |
| 196 | + |
| 197 | +```bash |
| 198 | +# TLS fixture required for httph2 |
| 199 | +bash tests/tls/tls.sh setup |
| 200 | + |
| 201 | +ctest -R 'XRootD::(http|httpparser|httph2)' --output-on-failure |
| 202 | +``` |
| 203 | + |
| 204 | +| CTest name | Script | Port | Coverage | |
| 205 | +|------------|--------|------|----------| |
| 206 | +| `XRootD::http` | `tests/XRootD/http.sh` | 7094 | Full HTTP/1.1 integration | |
| 207 | +| `XRootD::httpparser` | `tests/XRootD/httpparser.sh` | 7095 | llhttp header parsing | |
| 208 | +| `XRootD::httpparserlegacy` | `tests/XRootD/httpparserlegacy.sh` | 7096 | Legacy line parser regression | |
| 209 | +| `XRootD::httph2` | `tests/XRootD/httph2.sh` | 7097 | HTTPS + ALPN h2 (GET/PUT/HEAD/DELETE, same-connection reuse) | |
| 210 | + |
| 211 | +## Source layout |
| 212 | + |
| 213 | +``` |
| 214 | +src/XrdHttp/ |
| 215 | + README.md — this file |
| 216 | + README-KRB5.md — Kerberos / SPNEGO authentication |
| 217 | + README-CKSUM.md — Checksum support |
| 218 | + xrootd-http.cf — Example configuration |
| 219 | + XrdHttpProtocol.{cc,hh} — Protocol handler |
| 220 | + XrdHttpReq.{cc,hh} — Per-request logic |
| 221 | + XrdHttpModule.cc — Plugin entry point |
| 222 | + wire/ — HTTP/1 and HTTP/2 wire substrate |
| 223 | + vendor/llhttp/ — Bundled llhttp (MIT, v9.4.1) |
| 224 | + static/ — Embedded CSS and favicon for directory listings |
| 225 | +``` |
| 226 | + |
| 227 | +## Vendored dependencies |
| 228 | + |
| 229 | +- **llhttp** — HTTP/1.1 request parser ([nodejs/llhttp](https://github.com/nodejs/llhttp)). |
| 230 | + See [`vendor/llhttp/README`](vendor/llhttp/README). |
| 231 | +- **nghttp2** — External dependency for HTTP/2 (not vendored). |
0 commit comments