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

POC implementation of WAComboResponse acting directly on a socket stream #1240

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
instance creation
onStream: aStream bufferSize: anInteger codec: aGRCodec
^ self basicNew
initializeOnStream: aStream bufferSize: anInteger codec: aGRCodec;
yourself
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ streaming
binary
"put the response stream into binary mode"

bufferedStream := (GRCountingStream on: (GRPlatform current readWriteByteStream)).
bufferedStream := GRPlatform current readWriteByteStream.
externalStream binary

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,14 @@ close
"Send a zero-sized chunk to end the data transfer."

closed ifTrue: [ self error: 'Response is closed' ].
externalStream nextPutAll: '0'; crlf; crlf; flush.
externalStream
"zero length chunk"
nextPut: 0;
"crlf"
nextPut: 13;
theseion marked this conversation as resolved.
Show resolved Hide resolved
nextPut: 10;
"crlf"
nextPut: 13;
nextPut: 10;
flush.
closed := true
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
initialization
commit
| encodingStream |
"Begin a partial transmission"

self committedCheck.
Expand All @@ -8,7 +9,20 @@ commit
self headerAt: 'Transfer-Encoding' put: 'chunked'.

committed := true.
self writeStatusOn: externalStream.
self writeHeadersOn: externalStream.
self writeCookiesOn: externalStream.
externalStream crlf; flush

"The external stream is usually byte based. Unfortunately, the
streams aren't compatible enough at the moment such that everything
could be written to the external stream directly (in particular, writing of
single characters doesn't work). Luckily, the encoders already translate
characters to bytes, so we only need to wrap the stream with the appropariate
encoder. Header data is always ASCII encoded.
Because older versions of Seaside don't have a dedicated ASCII encoder we
use UTF-8 here (ASCII is a subset of UTF-8)."
encodingStream := (GRCodec forEncoding: 'utf-8') encoderFor: externalStream.
self
writeStatusOn: encodingStream;
writeHeadersOn: encodingStream;
writeCookiesOn: encodingStream.
encodingStream
crlf;
flush
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ destroy

super destroy.
bufferedStream := nil.
externalStream := nil
externalStream := nil.
codec := nil

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
public
flush
"Flush the receiver and send partial content"

committed ifFalse: [ self commit ].

"Write the partial content if any"
self nextChunk: bufferedStream count put: bufferedStream contents.
self nextChunk: bufferedStream size put: bufferedStream contents.
bufferedStream reset

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
initialization
initializeOnStream: aStream bufferSize: anInteger codec: aGRCodec
"Initialize the receiver"

| rawBufferedStream |
self initialize.
codec := aGRCodec.
rawBufferedStream := GRPlatform current writeCharacterStreamOn: (aGRCodec encodedStringClass new: anInteger).
bufferedStream := aGRCodec encoderFor: rawBufferedStream.
externalStream := aStream.
committed := false.
closed := false
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
streaming
nextChunk: anInteger put: data
"Write a chunk of data to the external stream. Does NOT write if no data is provided since this would logically end the chunked transfer. To end data transfer use #close instead. Raise an error if the response has been committed and closed.

Unfortunately the size of the chunk is not measured in bytes but characters so we need the ability to pass in the chunk size
see also
http://code.google.com/p/seaside/issues/detail?id=733"
"Write a chunk of data to the external stream. Does NOT write if no data is provided since this would logically end the chunked transfer. To end data transfer use #close instead. Raise an error if the response has been committed and closed."

data isEmpty ifTrue: [ ^ self ].
closed ifTrue: [ self error: 'Response is closed' ].

externalStream nextPutAll: (anInteger printStringBase: 16); crlf.
externalStream nextPutAll: data; crlf; flush
"Per specification, write the length of the chunk as hexadecimal number.
See https://httpwg.org/specs/rfc7230.html#chunked.encoding"
externalStream
nextPutAll: (anInteger printStringBase: 16) asByteArray;
"crlf"
nextPut: 13;
nextPut: 10;
nextPutAll: data;
"crlf"
nextPut: 13;
nextPut: 10;
flush

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ writing
writeHeadersOn: aStream
self isChunked ifFalse: [
self
headerAt: 'Content-Length'
put: bufferedStream position ].
headerAt: 'Content-Length'
put: bufferedStream position ].
super writeHeadersOn: aStream
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
{
"commentStamp" : "pmm 8/25/2019 11:14",
"commentStamp" : "ar 8/4/2010 20:31",
"super" : "WAResponse",
"category" : "Seaside-Core-HTTP",
"classinstvars" : [ ],
"pools" : [ ],
"classvars" : [ ],
"instvars" : [
"bufferedStream",
"externalStream",
"codec",
"committed",
"closed"
"externalStream",
"closed",
"bufferedStream",
"countingStream"
],
"name" : "WAComboResponse",
"type" : "normal"
Expand Down
32 changes: 16 additions & 16 deletions repository/Seaside-Core.package/monticello.meta/categories.st
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
SystemOrganization addCategory: #'Seaside-Core'!
SystemOrganization addCategory: #'Seaside-Core-Backtracking'!
SystemOrganization addCategory: #'Seaside-Core-Base'!
SystemOrganization addCategory: #'Seaside-Core-Cache'!
SystemOrganization addCategory: #'Seaside-Core-Callbacks'!
SystemOrganization addCategory: #'Seaside-Core-Configuration'!
SystemOrganization addCategory: #'Seaside-Core-Document'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements'!
SystemOrganization addCategory: #'Seaside-Core-Exceptions'!
SystemOrganization addCategory: #'Seaside-Core-Filter'!
SystemOrganization addCategory: #'Seaside-Core-HTTP'!
SystemOrganization addCategory: #'Seaside-Core-Libraries'!
SystemOrganization addCategory: #'Seaside-Core-Manifest'!
SystemOrganization addCategory: #'Seaside-Core-Rendering'!
SystemOrganization addCategory: #'Seaside-Core-RequestHandling'!
SystemOrganization addCategory: #'Seaside-Core-Server'!
SystemOrganization addCategory: #'Seaside-Core-Utilities'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Backtracking'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Base'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Cache'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Callbacks'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Configuration'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Document'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Exceptions'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Filter'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-HTTP'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Libraries'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Manifest'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Rendering'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-RequestHandling'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Server'!
SystemOrganization addCategory: #'Seaside-Core-Document-Elements-Seaside-Core-Utilities'!
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
accessing
response
^ super response ifNil: [ response := WAComboResponse external: (GRPlatform current writeCharacterStreamOn: (String new: 4096)) ]
^ super response ifNil: [
response := WAComboResponse
onStream: (GRPlatform current writeCharacterStreamOn: (ByteArray new: 1024))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look right. #writeCharacterStreamOn: wants a String, should we instead send #writeCharacterStreamOn:?

Copy link
Member Author

@theseion theseion Aug 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be something else, yes. I presume, you meant to write writeBinaryStreamOn:?

We have to be careful though. I remember having to fight a lot with the wrapping streams (i.e., Zinc).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume, you meant to write writeBinaryStreamOn:?

Yes

I remember having to fight a lot with the wrapping streams (i.e., Zinc).

Let's figure out the problem and solve it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#writeCharacterStreamOn: simply delegates to WriteStream on:, which operates on collections, so in that sense it's fine. Also, this is in a test and there is no #writeBinaryStreamOn: so far. I'll just leave it if you don't object.

bufferSize: 1024
codec: (GRCodec forEncoding: 'utf-8') ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
tests
testCommit
self response
contentType: 'text/html';
nextPutAll: 'Visit <a href="http://www.seaside.st">seaside.st</a>.';
commit.

self assert: self response isCommitted
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
tests
testWithBinaryStream
ByteArray streamContents: [ :stream |
response := WAComboResponse
onStream: stream
bufferSize: 1024
codec: (GRCodec forEncoding: 'utf-8').

response
nextPut: Character space;
flush;
close ]


Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
converting
responseFor: aZnRequest
| bufferedStream codecStream |
bufferedStream := GRPlatform current writeCharacterStreamOn: (self codec encodedStringClass new: 4096).
codecStream := self codec encoderFor: bufferedStream.
^ WAComboResponse
onBuffered: (GRCountingStream on: codecStream)
external: aZnRequest stream
onStream: aZnRequest stream
bufferSize: 4096
codec: self codec