|
Class: WebSocketStream
Object
|
+--Stream
|
+--WebSocketStream
- Package:
- stx:goodies/communication
- Category:
- Net-Communication-Websocket
- Version:
- rev:
1.134
date: 2022/12/05 08:49:17
- user: cg
- file: WebSocketStream.st directory: goodies/communication
- module: stx stc-classLibrary: communication
I handle the WebSocket protocol as defined in:
https://datatracker.ietf.org/doc/html/rfc6455
use #next to wait for new data.
use #nextPut: to send data.
use #nextPutAll: to send an array of data.
use #ping to get the ping time duration or nil if timeout (30s).
use #close to shutdown and cleanup everything.
to get familiar play with the tests in /stx/goodies/regression/WebSocketTest.st
avoids hge allocations by using files see #minBytesForUsingFile
ATTENTION: you may receive a Filename when the data is too big.
ATTENTION: when the other side sends a small file, you may receive the data as ByteArray instead of file.
data to be sent must be a String, ByteArray or Filename
you receive a String when a String was sent (as long as the data is not too big -> file).
you receive a ByteArray when a ByteArray was sent (as long as the data is not too big -> file).
copyrightCOPYRIGHT (c) 2020 by eXept Software AG
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
accessing
-
forceCompressionBitForTestingPurpose
-
this is just used for testing purpose
due to we only support decompression in #receive,
we need to force this bit in #send to be able to test the decompression
self forceCompressionBitForTestingPurpose
self forceCompressionBitForTestingPurpose:true
self forceCompressionBitForTestingPurpose:false
-
forceCompressionBitForTestingPurpose: aValue
-
this is just used for testing purpose
due to we only support decompression in #receive,
we need to force this bit in #send to be able to test the decompression
self forceCompressionBitForTestingPurpose
self forceCompressionBitForTestingPurpose:true
self forceCompressionBitForTestingPurpose:false
-
maxBytesPerFrame
-
keep this number small,
to be able to put control frames between
-
maxBytesPerFrame: aNumber
-
keep this number small,
to be able to put control frames between
constants
-
kOpCodeClose
-
-
kOpCodePing
-
-
kOpCodePong
-
-
kOpCodeText
-
-
maxPingTimeDuration
-
-
minBytesForUsingFile
-
if incoming data exceeds this #min value,
use a file to store the incoming data (instead of using memory)
-
readerWriterProcessPriority
-
debugging
-
debug
-
self debug:true.
self verbose:true.
self verboseSpeed:true.
self verboseProtocol:true.
self debug:false.
self verbose:false.
self verboseSpeed:false.
self verboseProtocol:false.
-
debug: aBoolean
-
self debug:true.
self verbose:true.
self verboseSpeed:true.
self verboseProtocol:true.
self debug:false.
self verbose:false.
self verboseSpeed:false.
self verboseProtocol:false.
-
verbose
-
self debug:true.
self verbose:true.
self verboseSpeed:true.
self verboseProtocol:true.
self debug:false.
self verbose:false.
self verboseSpeed:false.
self verboseProtocol:false.
-
verbose: aBoolean
-
self debug:true.
self verbose:true.
self verboseSpeed:true.
self verboseProtocol:true.
self debug:false.
self verbose:false.
self verboseSpeed:false.
self verboseProtocol:false.
-
verboseProtocol
-
self debug:true.
self verbose:true.
self verboseSpeed:true.
self verboseProtocol:true.
self debug:false.
self verbose:false.
self verboseSpeed:false.
self verboseProtocol:false.
-
verboseProtocol: aBoolean
-
self debug:true.
self verbose:true.
self verboseSpeed:true.
self verboseProtocol:true.
self debug:false.
self verbose:false.
self verboseSpeed:false.
self verboseProtocol:false.
-
verboseSpeed
-
self debug:true.
self verbose:true.
self verboseSpeed:true.
self verboseProtocol:true.
self debug:false.
self verbose:false.
self verboseSpeed:false.
self verboseProtocol:false.
-
verboseSpeed: aBoolean
-
self debug:true.
self verbose:true.
self verboseSpeed:true.
self verboseProtocol:true.
self debug:false.
self verbose:false.
self verboseSpeed:false.
self verboseProtocol:false.
initialization
-
initialize
-
Debug := true.
Usage example(s):
WebSocketConstants initializeConstants.
|
instance creation
-
asStreamForClientOnSocket: aSocket
-
-
asStreamForServerOnSocket: aSocket
-
-
new
-
use #asStreamForClientOnSocket: or #asStreamForServerOnSocket:
-
newInstance
-
accessing
-
closedByErrorCallback: aOneOrZeroArgBlock
-
Modified (format): / 07-10-2020 / 18:17:09 / exept MBP
-
closedByPeerCallback: aOneOrZeroArgBlock
-
Modified (format): / 07-10-2020 / 18:17:05 / exept MBP
-
connectionTargetName
-
-
connectionTargetName: aString
-
Modified (format): / 12-03-2022 / 13:05:05 / cg
-
getPeer
-
accessing data
-
next
-
blocking: return the next complete message
the contents of all data frames (until frame with final flag) concatenated
-
nextOrNil
-
non-blocking: return the next complete message if there is one; otherwise nil.
the contents of all data frames (until frame with final flag) concatenated
-
nextPut: textOrByteArrayOrFilename
-
send a message
i.e. the whole data you want to send
not framed, not encoded, just plain text, byte array or filename
the real send will encode the frame for this data
-
nextPutAll: arrayOfTextOrByteArrayOrFilename
-
send many messages but ensures their order,
so the messages will income with the same order
-
nextWithTimeout: secondsOrTimeDurationOrNil
-
blocking: return the next complete message or nil if a non-nil timeout is happening.
Returns the contents of all data frames (until frame with final flag) concatenated
actions
-
ping
-
-
setUsedOnServerSide
-
is this stream currently used on the client or server side?
the frame encoding and decoding differs for client and server side
-
updateProcessNames
-
change & update
-
notify: event with: payload orElse: aBlock
-
tell listeners
or if there are no listeners,
evaluate aBlock (which usually puts it into the receive queue)
-
on: event do: aBlock
-
register an event listener
converting
-
decodeFrame: byteArrayOrReindexedCollectionWithByteArray
-
returns the #decodeData or nil for error and the caller should close this websocket.
See: https://datatracker.ietf.org/doc/html/rfc6455
(Renamed from #decodeFrameHybi17:
HYBI was the name of an IETF Working Group and the 17th report resulted in RFC6455.
so this name was a historic leftover and has be changed).
-
encodeFrame: nilOrStringOrByteArrayOrReindexedCollectionWithByteArrayArg isFinal: isFinal opCode: opCode
-
Renamed from #encodeFrameHybi17....
HYBI was the name of an IETF Working Group and the 17th report resulted in RFC6455.
so this name was a historic leftover and has be changed.
-
encodeFrameText: aString
-
-
maskOrUnmaskPayload: stringOrByteArrayOrReindexedCollectionWithByteArray withMask: maskArg
-
returns a new masked byte array
-
maskOrUnmaskPayload: stringOrByteArrayOrReindexedCollectionWithByteArray withMask: maskArg into: outBufferOrNil
-
returns either outBufferOrNil filled with the data
or a new masked byte array;
The incoming buffer can be overwritten iff it is known to contain temporary data
(i.e. not provided by the websocket-using-client, but allocated here.
Especially, if the buffer contains data from a file.
This can avoid an extra allocation of possibly big buffers.
Usage example(s):
SPEED UP stx code:
payload doWithIndex:[:eachByte :index |
payload
at:index
put:(eachByte bitXor:(mask at:((index - 1) \\ 4) + 1)).
].
TESTS:
(self basicNew
maskOrUnmaskPayload:#[0 0 0 0]
withMask:#[1 2 3 4])
asByteArray
|
frames
-
closeFrame
-
-
pingFrame
-
-
pongFrame
-
helper
-
criticalSocketNextPutAll: data
-
if the current process is not the #writerProcess
increase the process priority over the #writerProcess priority
to give control frames (ping, close etc.) or explicit write calls priority
-
criticalSocketNextPutAll: data ignoreIsConnectionClosed: ignoreIsConnectionClosed
-
if the current process is not the #writerProcess
increase the process priority over the #writerProcess priority
to give control frames (ping, close etc.) or explicit write calls priority
-
decompressMessageContents: contentsOrContentsAsFile
-
try to handle compression in an inconform way,
because I (the server) did not negotiated any compression extension.
we still try to handle this data, because the python debug interface
sends data with this bit set, even without negotiated before 24.04.2020
this is just a try, we dont know yet if the python debug interface will run now
-
handleErrorForProcessLabeled: aLabel do: aBlock
-
assume connection is corrupted,
don't send close frame to a corrupted connection
initialization & release
-
close
-
when you close it by yourself you dont need a callback
-
closeDueToErrorInPing
-
we already have a write error, the next write will not work either
-
closeWithSendCloseFrame: doSendCloseFrame errorMessage: errorMessage callback: aCallback
-
https://expeccoalm.exept.de/D480706
Usage example(s):
#closeWithSendCloseFrame: already called or
this is a recursive call (triggered by terminating the read/write processes)
|
-
initialize
-
lazy initialization from instance method
-
socket: aSocket
-
-
startProcesses
-
ensure same as writer process... otherwise may one process is blocking the other
-
startProcessing
-
the WebSocket has been accepted,
start operating as web socket now.
before it was just a web socket object, to be passed to for e.g. #acceptsWebSocket:
please do not change the socket with #setNonBlocking or start the processes by #startProcesses
before it was realy clear, that this web socket will be accepted and before the client's accepted response has been sent
logging
-
log: message
-
(comment from inherited method)
same as showCR:.
Added to allow for Transcript.log(...) to be used in a similar way as console.log(...).
(and, by the way, JS-actions in expecco see a binding for console -> Transcript.
Not for non-JavaScript usage
-
log: message data: reindexedCollection
-
-
log: message data: reindexedCollection showData: showData
-
-
logFacility
-
(comment from inherited method)
the 'log facility';
this is used by the Logger both as a prefix to the log message,
and maybe (later) used to filter and/or control per-facility log thresholds.
The default here is to base the facility on my class
printing
-
printOn: aStream
-
(comment from inherited method)
append a user printed representation of the receiver to aStream.
The format is suitable for a human - not meant to be read back.
The default here is to output the receiver's class name.
BUT: this method is heavily redefined for objects which
can print prettier.
processes
-
readerProcessLoop
-
reads all incoming packets and puts them into the receiveQueue or notifies any listener
-
writerProcessLoop
-
shuffles all packets from the sendQueue to the socket
queries
-
hasEmptyReceiveQueue
-
-
isConnectionClosed
-
-
isUsedOnServerSide
-
is this stream currently used on the client or server side?
the frame encoding and decoding differs for client and server side
-
lastDataReceivedOrInstanceCreationTimestamp
-
ConnectionClosedError
Frame
Message
|