Merge pull request #85 from entireio/fix/v2-fetch-premature-eof · Entire
Merge pull request #85 from entireio/fix/v2-fetch-premature-eof
f68ccdf→main·
Soph·4w ago·2 files·+31 added/-4 removed
Treat a pack-less v2 fetch response as an error, not success
Changes
internal/gitproto
Mfetch.go+8/-4
Mfetch_test.go+23
317 unmodified lines
318
319
320
321
322
321
322
323
324
325
326
327
328
329
9 unmodified lines
339
340
341
338
342
343
344
345
2 unmodified lines
348
349
350
347
351
352
353
354
317 unmodified lines
// consumeV2FetchPack walks a protocol v2 fetch response envelope
// (acknowledgments / shallow-info / packfile sections) and hands the
// demuxed packfile stream to consumer when one is present. Returns nil
// for empty / no-pack responses without invoking consumer.
// demuxed packfile stream to consumer. A response that ends without ever
// delivering a packfile is reported as io.ErrUnexpectedEOF rather than as
// silent success: by the time we reach here wants is non-empty (callers
// short-circuit the up-to-date case earlier), so the server owes us a
// packfile section. This mirrors the sibling openV2PackStream — previously a
// truncated or pack-less response let a fetch "succeed" having stored nothing.
func consumeV2FetchPack(
r io.Reader,
verbose bool,
9 unmodified lines
if expectPackfile {
return errors.New("expected packfile to be sent after 'ready'")
}
return nil
return io.ErrUnexpectedEOF
}
return fmt.Errorf("decode protocol v2 fetch response: %w", err)
}
2 unmodified lines
if expectPackfile {
return errors.New("expected packfile to be sent after 'ready'")
}
return nil
return io.ErrUnexpectedEOF
case PacketDelim, PacketResponseEnd:
continue
case PacketData:
Minternal/gitproto/fetch.go+8/-4
856 unmodified lines
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
856 unmodified lines
}
}
// A response that ends with a bare flush — no acknowledgments, no packfile —
// must be a hard error, not silent success that stores nothing. Matches
// openV2PackStream's io.ErrUnexpectedEOF.
func TestStoreV2FetchPackRejectsResponseWithoutPackfile(t *testing.T) {
var wire bytes.Buffer
if err := pktline.WriteFlush(&wire); err != nil {
t.Fatalf("write flush: %v", err)
}
err := storeV2FetchPack(memory.NewStorage(), &wire, false, nil)
if !errors.Is(err, io.ErrUnexpectedEOF) {
t.Fatalf("error = %v, want io.ErrUnexpectedEOF", err)
}
}
// A truncated response (EOF before any packfile) is likewise a hard error,
// not success.
func TestStoreV2FetchPackRejectsTruncatedResponse(t *testing.T) {
err := storeV2FetchPack(memory.NewStorage(), bytes.NewReader(nil), false, nil)
if !errors.Is(err, io.ErrUnexpectedEOF) {
t.Fatalf("error = %v, want io.ErrUnexpectedEOF", err)
}
}
func TestOpenV2PackStreamRejectsAcknowledgmentsWithoutReady(t *testing.T) {
var wire bytes.Buffer
if _, err := pktline.WriteString(&wire, "acknowledgments\n"); err != nil {