Comments and attachments close out phase 2. Files are stored content-addressed by hash, so the path never contains anything the uploader chose — a filename is attacker-controlled text, and building a path from it is how "../" becomes a write outside the data directory. Everything downloads as application/octet-stream: an uploaded SVG served inline would run its own script on this origin with the session cookie attached. Web Push is written directly against RFC 8030/8291/8292 rather than pulled in — it is three specs' worth of well-defined crypto and about two hundred lines, and the alternatives carry a JWT and crypto stack each larger than the file. Phase 4 imports Todoist's CSV and exports it back. Todoist numbers priorities the opposite way to how it displays them — its 4 is P1 — so the two are converted rather than copied; getting it backwards silently inverts the urgency of every imported task. Nesting is positional, so the reader keeps an indent stack and the writer never sorts. The round trip is tested as the brief asks: a CSV in, out, and back produces an identical project, and two exports of one project are byte-identical. Co-Authored-By: Claude Opus 5 <[email protected]>
25 lines
945 B
Go
25 lines
945 B
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// contextWithTimeout bounds a single handler's work without outliving the request
|
|
// itself: if the client has already gone away, the derived context is cancelled too.
|
|
func contextWithTimeout(r *http.Request, d time.Duration) (context.Context, context.CancelFunc) {
|
|
return context.WithTimeout(r.Context(), d)
|
|
}
|
|
|
|
// contextLike is the read surface store.InstanceKeys needs, so the same call works
|
|
// from a request and from a detached background goroutine.
|
|
type contextLike = context.Context
|
|
|
|
// contextWithBackgroundTimeout bounds work that has outlived its request. A push
|
|
// round trip is started from a handler but must not be cancelled when that handler
|
|
// returns, or the notification is dropped the moment the response is written.
|
|
func contextWithBackgroundTimeout(d time.Duration) (context.Context, context.CancelFunc) {
|
|
return context.WithTimeout(context.Background(), d)
|
|
}
|