I am working on WebSocket client based on TIPClient just like TIPClientHTTP. Until handshake all is well but when I try to send text from client garbage is received by server ( your code ). Despite my best efforts I am unable to resolve the issue. My understanding reaches that socket created with hbtip lib is not supporting the dual communication. I may be wrong though. Any help is appreciated. There is a small change in TIPClient class where "WS" and "WSS" protocol is resolved.
//----------------------------------------------------------------//
// WS_Client Class
//----------------------------------------------------------------//
CREATE CLASS TIPClientWS INHERIT TIPClient
DATA cMethod
DATA nReplyCode
DATA cReplyDescr
DATA nVersion INIT 13
DATA nSubversion INIT 0
DATA hHeaders INIT { => }
DATA hFields INIT { => }
DATA nLength
DATA cNonce
METHOD New( cServer, nPort )
METHOD Connect( cEndpoint )
METHOD ReadHeaders( lClear )
METHOD StandardFields( cEndpoint )
METHOD SendText( cText )
METHOD ReadFrame()
ENDCLASS
METHOD New( cServer, nPort ) CLASS TIPClientWS
LOCAL oUrl
::nConnTimeout := 3000
hb_HCaseMatch( ::hHeaders, .F. )
oUrl := TUrl():new( cServer + iif( Empty( nPort ), "", ":" + hb_ntos( nPort ) ) )
::super:new( oUrl, "ws", NIL )
::nDefaultPort := iif( ::oUrl:cProto == "wss", 443, 80 )
::open()
RETURN Self
METHOD Connect( cEndpoint ) CLASS TIPClientWS
::cNonce := hb_base64Encode( cld_getRandomJunkString( 16 ) )
IF ::standardFields( cEndpoint )
IF ::readHeaders()
IF ::nReplyCode == 101
IF hb_HHasKey( ::hHeaders, "Upgrade" ) .AND. Upper( ::hHeaders[ "Upgrade" ] ) == "WEBSOCKET"
IF hb_HHasKey( ::hHeaders, "Connection" ) .AND. Upper( ::hHeaders[ "Connection" ] ) == "UPGRADE"
IF hb_HHasKey( ::hHeaders, "Sec-WebSocket-Accept" )
IF ::hHeaders[ "Sec-WebSocket-Accept" ] == hb_base64Encode( hb_SHA1( ::cNonce + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", .T. ) )
RETURN .T.
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN .F.
METHOD StandardFields( cEndpoint ) CLASS TIPClientWS
LOCAL field, s
s := "GET " + cEndpoint + " HTTP/1.1" + ::cCRLF
//
::hFields[ "Upgrade" ] := "websocket"
::hFields[ "Connection" ] := "Upgrade"
::hFields[ "Sec-WebSocket-Key" ] := ::cNonce
::hFields[ "Sec-WebSocket-Protocol" ] := "chat"
::hFields[ "Sec-WebSocket-Version" ] := hb_ntos( ::nVersion )
//
FOR EACH field IN ::hFields
s += field:__enumKey() + ": " + field + ::cCRLF
NEXT
s += ::cCRLF
::inetSendAll( ::SocketCon, s )
RETURN ::inetErrorCode( ::SocketCon ) == 0
METHOD ReadHeaders( lClear ) CLASS TIPClientWS
LOCAL cLine, nPos, aVersion, aHead
IF ( cLine := hb_defaultValue( ::inetRecvLine( ::SocketCon, @nPos, 500 ), "" ) ) == ""
RETURN .F.
ENDIF
// Get Protocol version
aVersion := hb_regex( "^HTTP/(.)\.(.) ([0-9][0-9][0-9]) +(.*)$", cLine )
::cReply := cLine
IF Empty( aVersion )
::nVersion := 0
::nSubversion := 9
::nReplyCode := 0
::cReplyDescr := ""
ELSE
::nVersion := Val( aVersion[ 2 ] )
::nSubversion := Val( aVersion[ 3 ] )
::nReplyCode := Val( aVersion[ 4 ] )
::cReplyDescr := aVersion[ 5 ]
ENDIF
::nLength := -1
IF hb_defaultValue( lClear, .F. ) .AND. ! Empty( ::hHeaders )
::hHeaders := {=>}
ENDIF
cLine := ::inetRecvLine( ::SocketCon, @nPos, 500 )
DO WHILE ::inetErrorCode( ::SocketCon ) == 0 .AND. HB_ISSTRING( cLine ) .AND. ! cLine == ""
IF Len( aHead := hb_regexSplit( ":", cLine,,, 1 ) ) != 2
cLine := ::inetRecvLine( ::SocketCon, @nPos, 500 )
LOOP
ENDIF
::hHeaders[ aHead[ 1 ] ] := LTrim( aHead[ 2 ] )
cLine := ::inetRecvLine( ::SocketCon, @nPos, 500 )
ENDDO
IF ::inetErrorCode( ::SocketCon ) != 0
RETURN .F.
ENDIF
RETURN .T.
METHOD ReadFrame() CLASS TIPClientWS
LOCAL cBuffer
LOCAL cRequest
LOCAL nLen
DO WHILE .T.
cRequest := ""
nLen := 1
DO WHILE nLen > 0
cBuffer := Space( 4096 )
IF ( nLen := ::inetRecv( ::SocketCon, @cBuffer, 3000 ) ) > 0
cRequest += Left( cBuffer, nLen )
ELSE
IF nLen == -1 .AND. ::inetErrorCode( ::SocketCon ) == HB_SOCKET_ERR_TIMEOUT
nLen := 0
ENDIF
ENDIF
ENDDO
IF ! Empty( cRequest )
? cRequest
cRequest := cld_wsUnMask( cRequest )
ENDIF
EXIT
ENDDO
RETURN cRequest
METHOD SendText( cText ) CLASS TIPClientWS
cText := cld_wsMask( cText, OPC_TEXT )
? cText
::inetSendAll( ::SocketCon, cText, Len( cText ) )
RETURN ::inetErrorCode( ::SocketCon ) == 0
//////////////////////////////////////////
This is what I received on server
Harbour websockets server running on port 9000
accept socket request
new client connected
Request... iG♫☻- ♥- ♠☻&DA☻' +s♫♫"E§▲h