fix(nwsync): close the review findings on the framing fix and verify
ci / ci (pull_request) Successful in 3m38s
ci / ci (pull_request) Successful in 3m38s
- compressBlob now asserts its own output declares a content size, instead of trusting that the only way to lose the field is the one #86 found. An encoder upgrade that finds another way would otherwise republish the same undecodable blob in silence, and a blob is skipped by every later emit once written. - inspectBlob asserts the frame whenever one is present, rather than only when the payload is non-empty. - dirSink stats instead of reading when not verifying. Reading every existing blob back on the default path was a plain regression, and it contradicted the documented promise that verifying is a repair pass, not the default. - The manifest sha1 is now checked as hex before it is interpolated into a URL path, rather than only measured. - One blobKey rule for where a blob lives, replacing three copies of the fanout-path expression. - Renamed frameSizeThreshold to oneByteContentSizeCeiling, which says whose threshold it is. - The read-back in zoneSink reads the storage API on purpose; said so, and corrected a comment that claimed a failed read-back re-uploads when it aborts. - Tests derive their expected blob counts from the run instead of hard-coding fixture arithmetic, and the size-mismatch category has a test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,9 +39,10 @@ const (
|
||||
zstdFrameMagic = "\x28\xb5\x2f\xfd"
|
||||
frameSingleSegment = 1 << 5
|
||||
frameDictionaryMask = 0x03
|
||||
// The size below which klauspost/compress writes no Frame_Content_Size,
|
||||
// matching the field's own encoding threshold.
|
||||
frameSizeThreshold = 256
|
||||
// oneByteContentSizeCeiling is the size above which a Frame_Content_Size no
|
||||
// longer fits in one byte. Below it the field's size flag is 0, which is
|
||||
// what lets klauspost/compress leave the field out entirely.
|
||||
oneByteContentSizeCeiling = 256
|
||||
)
|
||||
|
||||
// compressBlob wraps data in NWCompressedBuffer framing.
|
||||
@@ -51,7 +52,15 @@ func compressBlob(data []byte) []byte {
|
||||
for _, field := range header {
|
||||
_ = binary.Write(&out, binary.LittleEndian, field)
|
||||
}
|
||||
out.Write(declareFrameContentSize(blobEncoder.EncodeAll(data, nil), len(data)))
|
||||
frame := declareFrameContentSize(blobEncoder.EncodeAll(data, nil), len(data))
|
||||
// Fail closed rather than publish a blob no client can decode. An encoder
|
||||
// upgrade that finds a new way to omit the field would otherwise reproduce
|
||||
// #86 in silence, and a blob is skipped by every later emit once written.
|
||||
if !frameDeclaresContentSize(frame) {
|
||||
panic(fmt.Sprintf("nwsync: refusing to emit a %d-byte blob whose zstd frame declares no content size (descriptor %#x)",
|
||||
len(data), frame[4]))
|
||||
}
|
||||
out.Write(frame)
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
@@ -68,7 +77,7 @@ func inspectBlob(blob []byte) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("malformed framing: %w", err)
|
||||
}
|
||||
if len(data) > 0 && !frameDeclaresContentSize(blob[blobHeaderBytes:]) {
|
||||
if len(blob) > blobHeaderBytes && !frameDeclaresContentSize(blob[blobHeaderBytes:]) {
|
||||
return nil, fmt.Errorf("malformed framing: the zstd frame declares no content size, which the game client cannot decode")
|
||||
}
|
||||
return data, nil
|
||||
@@ -115,7 +124,7 @@ func frameDeclaresContentSize(frame []byte) bool {
|
||||
// which is sound because the content is under 256 bytes and every match in it
|
||||
// therefore falls inside that window. Same length in, same length out.
|
||||
func declareFrameContentSize(frame []byte, size int) []byte {
|
||||
if size <= 0 || size >= frameSizeThreshold || len(frame) < 6 || string(frame[:4]) != zstdFrameMagic {
|
||||
if size <= 0 || size >= oneByteContentSizeCeiling || len(frame) < 6 || string(frame[:4]) != zstdFrameMagic {
|
||||
return frame
|
||||
}
|
||||
descriptor := frame[4]
|
||||
|
||||
Reference in New Issue
Block a user