diff --git a/examples/vweb/vweb_websocket/assets/websocket_client.js b/examples/vweb/vweb_websocket/assets/websocket_client.js
new file mode 100644
index 0000000000..629d036ac9
--- /dev/null
+++ b/examples/vweb/vweb_websocket/assets/websocket_client.js
@@ -0,0 +1,22 @@
+const messageList = document.getElementById('message-list');
+const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
+const socket = new WebSocket(`${protocol}://${location.host}/ws`);
+let i = 0;
+
+function send(message) {
+ messageList.innerHTML += `
> ${message}`;
+ socket.send(message);
+}
+
+socket.addEventListener("open", (event) => {
+ console.log('Connected to WS server');
+ send('Hey everyone !');
+});
+
+socket.addEventListener("message", (event) => {
+ const { data } = event;
+ messageList.innerHTML += `< ${data}`;
+ setTimeout(() => {
+ send(`Roger ${i++}`);
+ }, 3000);
+});
\ No newline at end of file
diff --git a/examples/vweb/vweb_websocket/index.html b/examples/vweb/vweb_websocket/index.html
new file mode 100644
index 0000000000..62b5b6e286
--- /dev/null
+++ b/examples/vweb/vweb_websocket/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+vweb websocket example page
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vweb/vweb_websocket/vweb_websocket.v b/examples/vweb/vweb_websocket/vweb_websocket.v
new file mode 100644
index 0000000000..b80ad003bb
--- /dev/null
+++ b/examples/vweb/vweb_websocket/vweb_websocket.v
@@ -0,0 +1,77 @@
+module main
+
+import log
+import net.http
+import net.websocket
+import term
+import vweb
+
+const http_port = 8080
+
+struct App {
+ vweb.Context
+mut:
+ wss &websocket.Server @[vweb_global]
+}
+
+fn slog(message string) {
+ eprintln(term.colorize(term.bright_yellow, message))
+}
+
+fn clog(message string) {
+ eprintln(term.colorize(term.cyan, message))
+}
+
+fn wlog(message string) {
+ eprintln(term.colorize(term.bright_blue, message))
+}
+
+fn main() {
+ mut app := new_app() or { panic(err) }
+ vweb.run(app, http_port)
+}
+
+fn new_app() !&App {
+ mut app := &App{
+ wss: new_websocker_server()!
+ }
+ app.handle_static('assets', true)
+ return app
+}
+
+fn new_websocker_server() !&websocket.Server {
+ mut wss := &websocket.Server{
+ logger: &log.Log{
+ level: .debug
+ }
+ }
+ wss.on_connect(fn (mut server_client websocket.ServerClient) !bool {
+ slog('ws.on_connect, server_client.client_key: ${server_client.client_key}')
+ return true
+ })!
+ wss.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ! {
+ slog('s.on_message msg.opcode: ${msg.opcode} | msg.payload: ${msg.payload}')
+ ws.write(msg.payload, msg.opcode) or {
+ eprintln('ws.write err: ${err}')
+ return err
+ }
+ })
+ wss.on_close(fn (mut ws websocket.Client, code int, reason string) ! {
+ slog('s.on_close code: ${code}, reason: ${reason}')
+ })
+ slog('Websocket Server initialized')
+ return wss
+}
+
+pub fn (mut app App) index() vweb.Result {
+ return $vweb.html()
+}
+
+pub fn (mut app App) ws() !vweb.Result {
+ key := app.req.header.get(http.CommonHeader.sec_websocket_key)!
+ app.wss.handle_handshake(mut app.conn, key) or {
+ wlog('handle_handshake error: ${err.msg()}')
+ return err
+ }
+ return app.text('')
+}
diff --git a/vlib/net/http/header.v b/vlib/net/http/header.v
index 39c2e85c1b..a36b929262 100644
--- a/vlib/net/http/header.v
+++ b/vlib/net/http/header.v
@@ -103,6 +103,7 @@ pub enum CommonHeader {
sec_fetch_site
sec_fetch_user
sec_websocket_accept
+ sec_websocket_key
server
server_timing
set_cookie
@@ -209,6 +210,7 @@ pub fn (h CommonHeader) str() string {
.sec_fetch_site { 'Sec-Fetch-Site' }
.sec_fetch_user { 'Sec-Fetch-User' }
.sec_websocket_accept { 'Sec-WebSocket-Accept' }
+ .sec_websocket_key { 'Sec-WebSocket-Key' }
.server { 'Server' }
.server_timing { 'Server-Timing' }
.set_cookie { 'Set-Cookie' }
@@ -314,6 +316,7 @@ const common_header_map = {
'sec-fetch-site': .sec_fetch_site
'sec-fetch-user': .sec_fetch_user
'sec-websocket-accept': .sec_websocket_accept
+ 'sec_websocket_key': .sec_websocket_key
'server': .server
'server-timing': .server_timing
'set-cookie': .set_cookie
diff --git a/vlib/net/websocket/websocket_server.v b/vlib/net/websocket/websocket_server.v
index d78efd8482..3995f9fee0 100644
--- a/vlib/net/websocket/websocket_server.v
+++ b/vlib/net/websocket/websocket_server.v
@@ -134,24 +134,60 @@ fn (mut s Server) serve_client(mut c Client) ! {
c.logger.debug('server-> End serve client (${c.id})')
}
mut handshake_response, mut server_client := s.handle_server_handshake(mut c)!
- accept := s.send_connect_event(mut server_client)!
- if !accept {
- s.logger.debug('server-> client not accepted')
- c.shutdown_socket()!
- return
- }
- // the client is accepted
- c.socket_write(handshake_response.bytes())!
- lock s.server_state {
- s.server_state.clients[server_client.client.id] = server_client
- }
- s.setup_callbacks(mut server_client)
+ s.attach_client(mut server_client, handshake_response)!
c.listen() or {
s.logger.error(err.msg())
return err
}
}
+// handle_handshake use an existing connection to respond to the handshake for a given key
+pub fn (mut s Server) handle_handshake(mut conn net.TcpConn, key string) !&ServerClient {
+ mut c := &Client{
+ is_server: true
+ conn: conn
+ is_ssl: false
+ logger: &log.Log{
+ level: .debug
+ }
+ client_state: ClientState{
+ state: .open
+ }
+ last_pong_ut: time.now().unix
+ id: rand.uuid_v4()
+ }
+ mut server_client := &ServerClient{
+ resource_name: 'GET'
+ client_key: key
+ client: unsafe { c }
+ server: unsafe { &s }
+ }
+ digest := create_key_challenge_response(key)!
+ handshake_response := 'HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ${digest}\r\n\r\n'
+ s.attach_client(mut server_client, handshake_response)!
+ spawn s.handle_ping()
+ c.listen() or {
+ s.logger.error(err.msg())
+ return err
+ }
+ return server_client
+}
+
+fn (mut s Server) attach_client(mut server_client ServerClient, handshake_response string) ! {
+ accept := s.send_connect_event(mut server_client)!
+ if !accept {
+ s.logger.debug('server-> client not accepted')
+ server_client.client.shutdown_socket()!
+ return
+ }
+ // the client is accepted
+ server_client.client.socket_write(handshake_response.bytes())!
+ lock s.server_state {
+ s.server_state.clients[server_client.client.id] = unsafe { server_client }
+ }
+ s.setup_callbacks(mut server_client)
+}
+
// setup_callbacks initialize all callback functions
fn (mut s Server) setup_callbacks(mut sc ServerClient) {
if s.message_callbacks.len > 0 {