From 86bbdd6fc8de012f01744158abf62fa4da1b661f Mon Sep 17 00:00:00 2001 From: Norbert Hartl Date: Thu, 24 Sep 2026 11:35:41 +0200 Subject: [PATCH] Read a gzip body without length from a socket stream A gzip-encoded entity with neither Content-Length nor chunked transfer encoding, i.e. one delimited by closing the connection, was inflated directly from the socket stream. InflateStream asks its source for its #position while refilling, which ZdcSocketStream and ZdcSecureSocketStream do not understand, so reading such a response failed with a MessageNotUnderstood. The source is now wrapped in a ZnLimitedReadStream in that case too, as it already was for a gzip body with a Content-Length. ZnUnpositionableReadStream stands in for a socket stream in the test. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../instance/readEntityFromStream.st | 6 +++++- .../testGzippedUpToEndFromUnpositionableStream.st | 15 +++++++++++++++ .../ZnUnpositionableReadStream.class/README.md | 3 +++ .../ZnUnpositionableReadStream.class/class/on..st | 5 +++++ .../instance/atEnd.st | 3 +++ .../instance/isBinary.st | 3 +++ .../instance/isStream.st | 3 +++ .../instance/next..st | 3 +++ .../instance/next.into.startingAt..st | 3 +++ .../instance/next.st | 3 +++ .../instance/readInto.startingAt.count..st | 3 +++ .../instance/setStream..st | 3 +++ .../properties.json | 13 +++++++++++++ 13 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 repository/Zinc-Tests.package/ZnEntityReaderTest.class/instance/testGzippedUpToEndFromUnpositionableStream.st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/README.md create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/class/on..st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/atEnd.st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/isBinary.st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/isStream.st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next..st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next.into.startingAt..st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next.st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/readInto.startingAt.count..st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/setStream..st create mode 100644 repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/properties.json diff --git a/repository/Zinc-HTTP.package/ZnEntityReader.class/instance/readEntityFromStream.st b/repository/Zinc-HTTP.package/ZnEntityReader.class/instance/readEntityFromStream.st index 04117e778..f7f934ffb 100644 --- a/repository/Zinc-HTTP.package/ZnEntityReader.class/instance/readEntityFromStream.st +++ b/repository/Zinc-HTTP.package/ZnEntityReader.class/instance/readEntityFromStream.st @@ -4,7 +4,11 @@ readEntityFromStream chunkedStream := (isChunked := self isChunked) ifTrue: [ stream := ZnChunkedReadStream on: stream ] ifFalse: [ nil ]. self isGzipped ifTrue: [ - self hasContentLength ifTrue: [ stream := ZnLimitedReadStream on: stream limit: self contentLength ]. + self hasContentLength + ifTrue: [ stream := ZnLimitedReadStream on: stream limit: self contentLength ] + ifFalse: [ + "InflateStream asks its source for its position, which a socket stream cannot answer" + isChunked ifFalse: [ stream := ZnLimitedReadStream on: stream limit: SmallInteger maxVal ] ]. decodedEntityLength := nil. stream := GZipReadStream on: stream ] ifFalse: [ decodedEntityLength := self contentLength ]. diff --git a/repository/Zinc-Tests.package/ZnEntityReaderTest.class/instance/testGzippedUpToEndFromUnpositionableStream.st b/repository/Zinc-Tests.package/ZnEntityReaderTest.class/instance/testGzippedUpToEndFromUnpositionableStream.st new file mode 100644 index 000000000..b15a6fd5b --- /dev/null +++ b/repository/Zinc-Tests.package/ZnEntityReaderTest.class/instance/testGzippedUpToEndFromUnpositionableStream.st @@ -0,0 +1,15 @@ +testing +testGzippedUpToEndFromUnpositionableStream + | text compressed headers reader | + text := String streamContents: [ :out | + 1 to: 50000 do: [ :each | out print: each; space ] ]. + compressed := ByteArray streamContents: [ :out | + (GZipWriteStream on: out) nextPutAll: text asByteArray; close ]. + (headers := ZnHeaders new) + at: 'Content-Type' put: 'text/plain'; + at: 'Content-Encoding' put: 'gzip'. + (reader := ZnEntityReader new) + headers: headers; + stream: (ZnUnpositionableReadStream on: compressed readStream); + allowReadingUpToEnd. + self assert: reader readEntity contents equals: text \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/README.md b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/README.md new file mode 100644 index 000000000..b780f330b --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/README.md @@ -0,0 +1,3 @@ +I am ZnUnpositionableReadStream. I wrap a read stream but, like a socket stream, I cannot tell my position. + +I am used in tests to make sure readers do not depend on a positionable source. \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/class/on..st b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/class/on..st new file mode 100644 index 000000000..f0cac5b2d --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/class/on..st @@ -0,0 +1,5 @@ +instance creation +on: readStream + ^ self new + setStream: readStream; + yourself \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/atEnd.st b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/atEnd.st new file mode 100644 index 000000000..3f1258d90 --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/atEnd.st @@ -0,0 +1,3 @@ +testing +atEnd + ^ stream atEnd \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/isBinary.st b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/isBinary.st new file mode 100644 index 000000000..f3ff66bf3 --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/isBinary.st @@ -0,0 +1,3 @@ +testing +isBinary + ^ true \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/isStream.st b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/isStream.st new file mode 100644 index 000000000..4b758e3a6 --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/isStream.st @@ -0,0 +1,3 @@ +testing +isStream + ^ true \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next..st b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next..st new file mode 100644 index 000000000..3cd259515 --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next..st @@ -0,0 +1,3 @@ +accessing +next: count + ^ stream next: count \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next.into.startingAt..st b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next.into.startingAt..st new file mode 100644 index 000000000..6a2b429ee --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next.into.startingAt..st @@ -0,0 +1,3 @@ +accessing +next: count into: collection startingAt: offset + ^ stream next: count into: collection startingAt: offset \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next.st b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next.st new file mode 100644 index 000000000..8a4257a54 --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/next.st @@ -0,0 +1,3 @@ +accessing +next + ^ stream next \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/readInto.startingAt.count..st b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/readInto.startingAt.count..st new file mode 100644 index 000000000..c1f5b1503 --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/readInto.startingAt.count..st @@ -0,0 +1,3 @@ +accessing +readInto: collection startingAt: offset count: count + ^ stream readInto: collection startingAt: offset count: count \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/setStream..st b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/setStream..st new file mode 100644 index 000000000..1ed04e3a2 --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/instance/setStream..st @@ -0,0 +1,3 @@ +initialization +setStream: readStream + stream := readStream \ No newline at end of file diff --git a/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/properties.json b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/properties.json new file mode 100644 index 000000000..8bf3ce73b --- /dev/null +++ b/repository/Zinc-Tests.package/ZnUnpositionableReadStream.class/properties.json @@ -0,0 +1,13 @@ +{ + "commentStamp" : "", + "super" : "Object", + "category" : "Zinc-Tests", + "classinstvars" : [ ], + "pools" : [ ], + "classvars" : [ ], + "instvars" : [ + "stream" + ], + "name" : "ZnUnpositionableReadStream", + "type" : "normal" +} \ No newline at end of file