snap/LXD streamed blob waiters get no bytes until the shared fetch finishes

Found while reviewing !17 (merged) for HTTP correctness. This was the case before !17 (merged) too; !17 (merged) doesn't cause it.

In snapproxy.serveDownloadStreaming and lxdproxy.serveBlobStreaming, every client is sent 200 and Content-Length at once. But only the client that started the fetch (the leader) gets the body as it arrives. Clients that join while the fetch runs (waiters) get nothing until the whole blob is committed, then read it from the Store. So:

  • A waiter on a large snap or image from a slow upstream sits silent for the whole download. snapd and LXD give up on a silent connection after roughly 30s, which is the failure that streaming to the leader was added to fix, now hitting waiters.
  • When the fetch fails (upstream error, hash mismatch, idle timeout), a waiter has been promised Content-Length bytes and gets none: a truncated 200.

apt solved this with fill (internal/proxy/stream.go): the download runs on its own goroutine and every client, waiters included, follows the bytes as they are written. The goal is the same for snap and LXD, ideally by sharing that mechanism rather than writing it a third time. With that in place, the leader's special clientwriter path from !17 (merged) becomes unnecessary.