ipn: fix localapi and peerapi protocol for taildrop resume (#9860)

Minor fixes:
* The branch for listing or hashing partial files was inverted.
* The host for peerapi call needs to be real (rather than bogus).
* Handle remote peers that don't support resuming.
* Make resume failures non-fatal (since we can still continue).

This was tested locally, end-to-end system test is future work.

Updates tailscale/corp#14772

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
Co-authored-by: Rhea Ghosh <rhea@tailscale.com>
pull/9861/head
Joe Tsai 1 year ago committed by GitHub
parent 77127a2494
commit 7971333603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -655,7 +655,7 @@ func (h *peerAPIHandler) handlePeerPut(w http.ResponseWriter, r *http.Request) {
var err error var err error
id := taildrop.ClientID(h.peerNode.StableID()) id := taildrop.ClientID(h.peerNode.StableID())
if r.URL.Path == "/v0/put/"+baseName { if prefix == "" {
resp, err = h.ps.taildrop.PartialFiles(id) resp, err = h.ps.taildrop.PartialFiles(id)
} else { } else {
ranges, ok := httphdr.ParseRange(r.Header.Get("Range")) ranges, ok := httphdr.ParseRange(r.Header.Get("Range"))

@ -1325,7 +1325,7 @@ func (h *Handler) serveFilePut(w http.ResponseWriter, r *http.Request) {
Transport: h.b.Dialer().PeerAPITransport(), Transport: h.b.Dialer().PeerAPITransport(),
Timeout: 10 * time.Second, Timeout: 10 * time.Second,
} }
req, err := http.NewRequestWithContext(r.Context(), "GET", "http://peer/v0/put/"+filenameEscaped, nil) req, err := http.NewRequestWithContext(r.Context(), "GET", dstURL.String()+"/v0/put/"+filenameEscaped, nil)
if err != nil { if err != nil {
return taildrop.FileChecksums{}, err return taildrop.FileChecksums{}, err
} }
@ -1335,18 +1335,23 @@ func (h *Handler) serveFilePut(w http.ResponseWriter, r *http.Request) {
return taildrop.FileChecksums{}, fmt.Errorf("invalid offset and length") return taildrop.FileChecksums{}, fmt.Errorf("invalid offset and length")
} }
req.Header.Set("Range", rangeHdr) req.Header.Set("Range", rangeHdr)
resp, err := client.Do(req) switch resp, err := client.Do(req); {
if err != nil { case err != nil:
return taildrop.FileChecksums{}, err return taildrop.FileChecksums{}, err
case resp.StatusCode == http.StatusMethodNotAllowed || resp.StatusCode == http.StatusNotFound:
return taildrop.FileChecksums{}, nil // implies remote peer on older version
case resp.StatusCode != http.StatusOK:
return taildrop.FileChecksums{}, fmt.Errorf("unexpected status code %d", resp.StatusCode)
default:
var checksums taildrop.FileChecksums
err = json.NewDecoder(resp.Body).Decode(&checksums)
return checksums, err
} }
var checksums taildrop.FileChecksums
err = json.NewDecoder(resp.Body).Decode(&checksums)
return checksums, err
}) })
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) // ResumeReader ensures that the returned offset and reader are consistent
return // even if an error is encountered. Thus, we can still proceed.
h.logf("reader could not be fully resumed: %v", err)
} }
outReq, err := http.NewRequestWithContext(r.Context(), "PUT", "http://peer/v0/put/"+filenameEscaped, remainingBody) outReq, err := http.NewRequestWithContext(r.Context(), "PUT", "http://peer/v0/put/"+filenameEscaped, remainingBody)

Loading…
Cancel
Save