Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZnResponse reads response differently depending on the ‘Content-Type’ when ‘Content-Length’ is greater than the body’s size #118

Closed
Rinzwind opened this issue Jul 27, 2023 · 4 comments

Comments

@Rinzwind
Copy link

The behavior of ZnResponse is different for the two ‘Content-Type’ values in the following example. There’s a mismatch between the ‘Content-Length’, which is 3, and the actual response body, which has only 2 bytes. With ‘text/plain’ as the ‘Content-Type’, the contents of the ZnResponse reflects the actual size of the body, while with ‘application/octet-stream’, the contents is the response body padded with a zero byte:

#('text/plain' 'application/octet-stream') collect: [ :contentType |
	| string response |
	string := String crlf join: {
		'HTTP/1.1 200 OK'.
		'Content-Type: ' , contentType.
		'Content-Length: 3'.
		''.
		'ab' }.
	response := ZnResponse readFrom: string asByteArray readStream.
	response contents ]
"=> #('ab' #[97 98 0])"

It would seem better for the contents to not have the padding in the second case. Perhaps a resumable exception should be signaled in both cases.

I noticed this while investigating a bug in which there was such a ‘Content-Length’ mismatch, I don’t think this a very important issue but it seemed worth noting at least. I tested the example in Pharo 12 build 616 with the included version of Zinc. In GemStone/S, an error is signaled in the ‘application/octet-stream’ case within the context of PositionableStream>>#next:into:, as it tries to read the exact number of elements specified while the method in Pharo reads at most the number specified.

@svenvc
Copy link
Owner

svenvc commented Jul 27, 2023

Thanks for the feedback.

An incorrect content-length (too large or too small) will inevitably result in trouble (i.e. things will go horribly wrong).

In general, there is no way to notice this situation. Your example is a bit of an edge case, since your input stream ends. In this case, you could say 'read as much as you can', but it would still be wrong (one byte is missing). In most modern HTTP conversations, there are multiple request/response messages on the same stream. A correct content-length is then absolutely required, as it is used to know when to stop reading.

I do agree that it would probably be better for both cases (ZnStringEntity vs ZnByteArrayEntity) to behave the same. Having looked at the #readFrom: methods of these classes and the code in ZnUtils I am a bit surprised by how messy it is. I think it would be possible to improve the situation a bit and align their behaviour.

I am not able to fix this right now, maybe next week.

@svenvc
Copy link
Owner

svenvc commented Aug 31, 2023

The following test code is more correct, in the case of non-ASCII input the byte and character counts are different:

#('text/plain' 'application/octet-stream') collect: [ :contentType |
	| string response |
	string := String crlf join: {
		'HTTP/1.1 200 OK'.
		'Content-Type: ' , contentType.
		'Content-Length: 4'.
		''.
		'€' }.
	response := ZnResponse readFrom: string utf8Encoded readStream.
	response ]

svenvc pushed a commit that referenced this issue Aug 31, 2023
When the content-length and the actual data available differ (i.e. there are not enough bytes left to read before EOF), which is an error in any case, the response was different between a ZnStringEntity and a ZnByteArrayEntity. Now they behave the same.

Furthermore, in ZnUtils class>>#streamFrom:to:size: there was a read that did not check the amount read.
@svenvc
Copy link
Owner

svenvc commented Aug 31, 2023

Here is what I did: f8c23a6

syrel pushed a commit to feenkcom/gtoolkit that referenced this issue Sep 5, 2023
Metacello new
    baseline: 'GToolkitForPharo9';
    repository: 'github://feenkcom/gtoolkit:v1.0.34/src';
    load

All commits (including upstream repositories) since last build:
feenkcom/gtoolkit-inspector@e0ba68 by Juraj Kubelka
add `FileSystemError` and `Warning` exception debugging views

feenkcom/zinc@dddeaa by Juraj Kubelka
Merge remote-tracking branch 'juraj/master'

# Conflicts:
#	repository/Zinc-Character-Encoding-Core.package/ZnBufferedReadStream.class/instance/back.st
#	repository/Zinc-HTTP-UnixSocket.package/ZnNetworkingUtils.extension/instance/socketStreamToUnixSocketFile..st
#	repository/Zinc-HTTP-UnixSocket.package/ZnNetworkingUtils.extension/instance/unixSocketOnFile..st


feenkcom/zinc@a6f7fa by Sven Van Caekenberghe
Refactoring: Introduce extension NetNameResolver class>>#addressForSocketPath: and use it in ZnNetworkingUtils>>#unixSocketOnFile:

feenkcom/zinc@b772d5 by Sven Van Caekenberghe
Merge pull request #122 from JurajKubelka/master

Initialize network before unix socket creation

feenkcom/zinc@0eda1c by Juraj Kubelka
initialize network before unix socket creation [#3433]

(cherry picked from commit 74fa4f28d3744a8b96249c62df346b79e0153a7f)


feenkcom/zinc@7f7daa by Sven Van Caekenberghe
Merge pull request #121 from JurajKubelka/master

Add support for Unix Domain Sockets: add missing connectTo: method

feenkcom/zinc@980e62 by Juraj Kubelka
Merge branch 'svenvc:master' into master


feenkcom/zinc@275d33 by Juraj Kubelka
add `Socket>>#connectTo:` [#3433]

(cherry picked from commit 517f09e08dfd92eded27383e5ebaab3b6c619283)


feenkcom/zinc@f8c23a by Sven Van Caekenberghe
A fix for svenvc/zinc#118

When the content-length and the actual data available differ (i.e. there are not enough bytes left to read before EOF), which is an error in any case, the response was different between a ZnStringEntity and a ZnByteArrayEntity. Now they behave the same.

Furthermore, in ZnUtils class>>#streamFrom:to:size: there was a read that did not check the amount read.

feenkcom/zinc@a1e4d3 by Sven Van Caekenberghe
Merge pull request #120 from JurajKubelka/master

Add support for Unix Domain Sockets

feenkcom/zinc@02f9a8 by Juraj Kubelka
add `ZnUnixSocketClient` [#3433]

(cherry picked from commit c00a559ffe4da3d26c173bba1cbc69b2ee481c96)


feenkcom/zinc@9cc3c1 by Sven Van Caekenberghe
Merge pull request #117 from akgrant43/Zn116

Modify ZnBufferedReadStream>>back to correctly move back ?

feenkcom/zinc@66b919 by Alistair Grant
Modify ZnBufferedReadStream>>back to correctly move back when the buffer is at the start but the stream is not.

Fixes: svenvc/zinc#116

feenkcom/zinc@17c625 by Sven Van Caekenberghe
Update README.md

feenkcom/zinc@df663b by Sven Van Caekenberghe
Merge pull request #115 from gcotelli/patch-1

Add Pharo 11 to CI

feenkcom/zinc@41c610 by Gabriel Omar Cotelli
Add Pharo 11 to CI

feenkcom/zinc@7e9b68 by Sven Van Caekenberghe
Start using github://pharo-contributions/XML-XMLParser:master/src for XMLParser and XMLWriter

feenkcom/gt4lsp@48ead9 by John Brant
check lsp server capabilities before sending messages
@Rinzwind
Copy link
Author

Rinzwind commented Sep 8, 2023

Looks good! The result of my example is now #('ab' #[97 98]) as expected. This issue can be closed then, unless you still want to add the exception to make the ‘Content-Length’ mismatch clear. In the first comment, I had a resumable exception in mind so that it can be ignored if necessary, alternatively a non-resumable exception with an option in ZnOptions to not signal it could be used.

@svenvc svenvc closed this as completed Sep 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants