| | @@ -195,6 +195,56 @@ handle 404s rather than answering `[]`. `git clone` does not work yet — Milest |
| 195 | 195 | database, so removing the column would have been a free in-place edit rather than a |
| 196 | 196 | second migration. |
| 197 | 197 | |
| 198 | +### Milestone 4a — Clone over HTTP · in progress |
| 199 | + |
| 200 | +`git http-backend`'s contract, probed against git 2.50.1 by driving the CGI from a |
| 201 | +throwaway server and cloning through it. Everything below is measured, not read. |
| 202 | + |
| 203 | +#### The contract |
| 204 | + |
| 205 | +- **`HTTP_CONTENT_ENCODING`, not `CONTENT_ENCODING`.** CGI gives only `Content-Type` and |
| 206 | + `Content-Length` unprefixed names; every other request header is `HTTP_`-prefixed, and |
| 207 | + `http-backend` looks for the prefixed one. **This is the finding that would have cost a |
| 208 | + day.** With the wrong name, `http-backend` hands the still-compressed body to |
| 209 | + `upload-pack`, which dies with `bad line length character` and the client reports |
| 210 | + `fatal: expected 'packfile'` — nothing names gzip, or the environment, anywhere in the |
| 211 | + failure. And it only happens once a repository has enough refs for the client to bother |
| 212 | + compressing: a one-ref test repo passes. |
| 213 | +- **`HTTP_GIT_PROTOCOL`** carries `version=2` through to `upload-pack`. Clones verified |
| 214 | + on v2 and on `protocol.version=0`, both 201 commits, both gzipped. |
| 215 | +- **`GIT_PROJECT_ROOT` plus `PATH_INFO`**, where `PATH_INFO` is the on-disk path relative |
| 216 | + to the root. Steid's URL and its storage layout differ — `/{handle}/repos/{name}.git/…` |
| 217 | + against `{data_dir}/{handle}/{name}.git` — so the adapter rewrites the middle segment |
| 218 | + out. `GIT_PROJECT_ROOT` is the data directory. |
| 219 | +- **`GIT_HTTP_EXPORT_ALL=1` is required.** Without it every repository answers `Status: |
| 220 | + 404 Not Found` and `Repository not exported`, unless a `git-daemon-export-ok` marker |
| 221 | + file sits in the bare repo (confirmed: dropping that file in re-enables it). |
| 222 | +- **Headers are CRLF-terminated and end at `\r\n\r\n`.** No bare-LF variant was |
| 223 | + observed, but the adapter should accept one rather than hang. |
| 224 | +- **`Status:` appears only on failure.** Its absence means 200, and it must be |
| 225 | + translated, not forwarded as a header. |
| 226 | +- **`http-backend` sets its own `Content-Type` and cache headers** — `Expires: Fri, 01 |
| 227 | + Jan 1980`, `Pragma: no-cache`, `Cache-Control: no-cache, max-age=0, must-revalidate`. |
| 228 | + Steid forwards them rather than inventing its own. |
| 229 | + |
| 230 | +#### Decisions worth remembering |
| 231 | + |
| 232 | +- **`GIT_HTTP_EXPORT_ALL`, never `git-daemon-export-ok`.** The marker file is git's own |
| 233 | + visibility mechanism and it looks tempting, but visibility lives in the `repositories` |
| 234 | + table and the use case is what enforces it. A marker file would be a second source of |
| 235 | + truth for the same question, free to drift from the first, and the drift direction is |
| 236 | + "private repository still clonable". Steid decides; git is told to stop asking. |
| 237 | +- **`receive-pack` is refused by default** — `Status: 403 Forbidden`, `Service not |
| 238 | + enabled: 'receive-pack'`, without any configuration. Convenient for 4a, but the routes |
| 239 | + still refuse writes explicitly rather than leaning on it: a default that helpfully |
| 240 | + changes is not an authorization decision. |
| 241 | +- **A non-zero exit can arrive after the headers are already out.** The gzip failure |
| 242 | + exited 1 having emitted a complete, successful-looking header block. So the exit code |
| 243 | + cannot gate the response — by the time it is known, the status is sent. It belongs in |
| 244 | + the log. |
| 245 | +- **A missing repository is `Status: 404` with exit 0.** Failure is reported in the |
| 246 | + CGI stream, not the exit code, and the two disagree in both directions. |
| 247 | + |
| 198 | 248 | --- |
| 199 | 249 | |
| 200 | 250 | ## Reference: what attempt #2 proved |