Code: Select all
EnableExplicit
;by celtic88 2018
;à propos de WebSockets
;https://developer.mozilla.org/fr/docs/WebSockets/Writing_WebSocket_servers
UseSHA1Fingerprint()
Structure Byte_Array
Byte.A[0]
EndStructure
Procedure.s WebSocket_DecryptKey(ClientKey.s)
;décrypter le "WebSocket-Key"
Protected Sf.s = StringFingerprint(ClientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", #PB_Cipher_SHA1)
Protected *Buffer = AllocateMemory(1024)
Protected j = 0, i
For i=1 To Len(Sf) Step 2
PokeA(*Buffer+j, Val("$" + Mid(Sf, i ,2)))
j + 1
Next
Protected b64.s = Space(1024)
Base64Encoder(*Buffer,j,@b64, 1024)
ProcedureReturn PeekS(@b64,-1,#PB_Ascii)
EndProcedure
Procedure.s WebSocket_JsonCreateCommand(type.s,content.s)
;utiliser JSON pour communiquer avec le client
Protected jid = CreateJSON(#PB_Any)
Protected Person = SetJSONObject(JSONValue(jid))
SetJSONString(AddJSONMember(Person, "type"), type)
SetJSONString(AddJSONMember(Person, "content"), content)
Protected rjsn.s = ComposeJSON(jid)
FreeJSON(jid)
ProcedureReturn rjsn
EndProcedure
Procedure.s WebSocket_JsonGetCommand(jSs.s,type.s)
;obtenire la command envoiye par le client
Protected jid = ParseJSON(#PB_Any,jSs)
If jid
Protected rjsn.s = GetJSONString(GetJSONMember(JSONValue(jid), type))
FreeJSON(jid)
ProcedureReturn rjsn
EndIf
EndProcedure
Procedure Helper_SwapData(*pData.Byte_Array, Size) ; convert big endian to little endian and v..
Protected *tmem.Byte_Array = AllocateMemory(Size)
Protected i
For i = 0 To Size -1
*tmem\Byte[i] = *pData\Byte[Size - i - 1]
Next
CopyMemory(*tmem, *pData, Size)
FreeMemory(*tmem)
EndProcedure
Procedure.s WebSocket_GetMessage(ClientID,*Buffer.Byte_Array,Len)
;Déchiffrer le message envoyé par le client
Protected opcode.b = *Buffer\Byte[0] & $f
Protected fin.b = *Buffer\Byte[0] >> 4 & $f
; Debug RSet(Bin(PeekA(*Buffer),#PB_Byte), 8, "0")
; Debug opcode
; Debug fin
Protected frameCount = 2
Protected length.q = *Buffer\Byte[1] & 127
If length = 126
length = PeekU(@*Buffer\Byte[2])
Helper_SwapData(@length, 2)
frameCount + 2
ElseIf length = 127
length = PeekQ(@*Buffer\Byte[2])
Helper_SwapData(@length, 8)
frameCount + 8
EndIf
Protected Key = PeekL(@*Buffer\Byte[frameCount])
frameCount + 4
Protected *palldata = AllocateMemory(frameCount+(length*2)+20)
CopyMemory(*Buffer, *palldata, Len)
If length > Len-frameCount
Repeat
Protected rvc = ReceiveNetworkData(ClientID, *palldata + Len, length)
If rvc = -1:Goto __SKIIP:EndIf
Len + rvc
Until length = Len-frameCount
EndIf
Protected *DBuffer = *palldata + length + frameCount
Protected j = 0, i
For i = frameCount To length + frameCount -1
PokeA(*DBuffer + j, PeekA(*palldata + i) ! PeekA(@Key + (j%4)))
j + 1
Next
;ShowMemoryViewer(*DBuffer, j)
Protected pstring.s = PeekS(*DBuffer, j ,#PB_Ascii)
__SKIIP:
FreeMemory(*palldata)
ProcedureReturn pstring
EndProcedure
Procedure WebSocket_SendMessage(ClientID, Message.s)
;envoyer message à client
Protected send.q = 129
SendNetworkData(ClientID, @send, 1)
Protected StringLen.q = StringByteLength(Message, #PB_Ascii)
send = StringLen
Protected datasieze
If send > 65535
send = 127
datasieze = 8
ElseIf send > 125
send = 126
datasieze = 2
EndIf
SendNetworkData(ClientID, @send, 1)
If datasieze
Helper_SwapData(@StringLen, datasieze)
SendNetworkData(ClientID, @StringLen, datasieze)
EndIf
SendNetworkString(ClientID, Message, #PB_Ascii)
EndProcedure
Procedure.s HttpRequest_QueryHeaders(Request.s, FileID.s)
FileID = StringField(Request, 2, FileID)
FileID = StringField(FileID, 1, #CRLF$)
ProcedureReturn FileID
EndProcedure
DisableExplicit
If InitNetwork() = 0
MessageRequester("Error", "Can't initialize the network !", 0)
End
EndIf
Port = 8100
*Buffer = AllocateMemory(1000)
If CreateNetworkServer(0, Port)
Repeat
SEvent = NetworkServerEvent()
If SEvent
ClientID = EventClient()
Select SEvent
Case #PB_NetworkEvent_Connect
Debug "A new client has connected !"
Case #PB_NetworkEvent_Data
FillMemory(*Buffer, 1000)
Len = ReceiveNetworkData(ClientID, *Buffer, 1000)
reQ.s = PeekS(*Buffer, -1, #PB_UTF8)
If HttpRequest_QueryHeaders(reQ, "GET") ; vérifier si la demande est pour la connexion...
; envoyer la demande acceptée...
SreQ.s = "HTTP/1.1 101 Switching Protocols" + #CRLF$ +
"Upgrade: websocket" + #CRLF$ +
"Connection: Upgrade" + #CRLF$ +
"Sec-WebSocket-Accept: " +
WebSocket_DecryptKey(HttpRequest_QueryHeaders(reQ, "Sec-WebSocket-Key: ")) +
#CRLF$ + #CRLF$
SendNetworkString(ClientID, SreQ, #PB_Ascii)
Else
;message a été envoyé par le client
msg.s = WebSocket_GetMessage(ClientID,*Buffer,Len)
;obtenir les informations de la commande
Debug "command Type : " + WebSocket_JsonGetCommand(msg,"type")
Debug "command content : " + WebSocket_JsonGetCommand(msg,"content")
;envoyer "Slt Les pures" :commande type texte
WebSocket_SendMessage(ClientID, WebSocket_JsonCreateCommand("text","Slt les Pures"))
EndIf
Case #PB_NetworkEvent_Disconnect
Debug "Client "+Str(ClientID)+" has closed the connection..."
EndSelect
Else
Delay(10)
EndIf
Until Quit = 1
MessageRequester("PureBasic - Server", "Click to quit the server.", 0)
CloseNetworkServer(0)
Else
MessageRequester("Error", "Can't create the server (port in use ?).", 0)
EndIf
End