fix(gopher): reject resume when caller supplies Writer
This commit is contained in:
@@ -165,6 +165,16 @@ func (p *Protocol) downloadText(ctx context.Context, reader *bufio.Reader, req *
|
||||
// fresh downloads (create) and resume (append). When req.Writer is
|
||||
// set (caller manages the file), we use it as-is.
|
||||
if req.Output != "" && req.Output != "-" {
|
||||
// Resume is meaningful only when we own the output file. If the
|
||||
// caller already supplied a Writer we cannot append to Output
|
||||
// behind their back, so disable resume to keep the partial-state
|
||||
// sidecar consistent with what actually lands on disk.
|
||||
if req.Resume && req.Writer != nil && req.Verbose {
|
||||
fmt.Fprintf(os.Stderr, "[gopher] warning: --resume ignored because a Writer is set; output goes to the Writer only\n")
|
||||
}
|
||||
if req.Resume && req.Writer != nil {
|
||||
req.Resume = false
|
||||
}
|
||||
if req.Resume {
|
||||
canResume, resumeMeta, rerr := output.CanResume(req.Output, req.URL.String())
|
||||
if rerr != nil {
|
||||
|
||||
@@ -438,3 +438,53 @@ func TestGopherResumeStaleMetadata(t *testing.T) {
|
||||
t.Errorf("content = %q, want %q", string(data), "Fresh\n")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGopherResumeIgnoredWhenWriterSet verifies that --resume is silently
|
||||
// disabled when the caller also supplies a Writer: the resume metadata
|
||||
// sidecar would otherwise lie about bytes written to Output, since the
|
||||
// real data ends up in the Writer.
|
||||
func TestGopherResumeIgnoredWhenWriterSet(t *testing.T) {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
port := listener.Addr().(*net.TCPAddr).Port
|
||||
|
||||
go func() {
|
||||
conn, _ := listener.Accept()
|
||||
if conn == nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
r := bufio.NewReader(conn)
|
||||
r.ReadString('\n')
|
||||
conn.Write([]byte("0Line one\t/foo\thost\t70\r\n.\r\n"))
|
||||
}()
|
||||
|
||||
proto := NewProtocol()
|
||||
u, _ := url.Parse("gopher://127.0.0.1:" + strconv.Itoa(port) + "/foo")
|
||||
outPath := t.TempDir() + "/ignored-output.txt"
|
||||
|
||||
var buf strings.Builder
|
||||
res, dlErr := proto.Download(context.Background(), &core.DownloadRequest{
|
||||
URL: u,
|
||||
Output: outPath,
|
||||
Writer: &stringWriter{&buf},
|
||||
Resume: true,
|
||||
})
|
||||
if dlErr != nil {
|
||||
t.Fatalf("download: %v", dlErr)
|
||||
}
|
||||
if res.Resumed {
|
||||
t.Error("expected Resumed=false when Writer is set (resume must be ignored)")
|
||||
}
|
||||
if !strings.Contains(buf.String(), "Line one") {
|
||||
t.Errorf("writer should receive the body, got %q", buf.String())
|
||||
}
|
||||
// Output is supplied but a Writer takes precedence; the file must
|
||||
// not be created or touched by resume machinery.
|
||||
if _, statErr := os.Stat(outPath); statErr == nil {
|
||||
t.Error("Output file should not be created when Writer is set")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user