mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
websocket: enable using an already existing connection (from vweb or another http server) (#20103)
This commit is contained in:
parent
db6ae6ee9b
commit
5be5cd9be1
5 changed files with 161 additions and 12 deletions
22
examples/vweb/vweb_websocket/assets/websocket_client.js
Normal file
22
examples/vweb/vweb_websocket/assets/websocket_client.js
Normal file
|
@ -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 += `<li>> ${message}</li>`;
|
||||||
|
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 += `<li>< ${data}</li>`;
|
||||||
|
setTimeout(() => {
|
||||||
|
send(`Roger ${i++}`);
|
||||||
|
}, 3000);
|
||||||
|
});
|
11
examples/vweb/vweb_websocket/index.html
Normal file
11
examples/vweb/vweb_websocket/index.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang=en>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>vweb websocket example page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ol id="message-list"></ol>
|
||||||
|
<script type="text/javascript" src="websocket_client.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
77
examples/vweb/vweb_websocket/vweb_websocket.v
Normal file
77
examples/vweb/vweb_websocket/vweb_websocket.v
Normal file
|
@ -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('')
|
||||||
|
}
|
|
@ -103,6 +103,7 @@ pub enum CommonHeader {
|
||||||
sec_fetch_site
|
sec_fetch_site
|
||||||
sec_fetch_user
|
sec_fetch_user
|
||||||
sec_websocket_accept
|
sec_websocket_accept
|
||||||
|
sec_websocket_key
|
||||||
server
|
server
|
||||||
server_timing
|
server_timing
|
||||||
set_cookie
|
set_cookie
|
||||||
|
@ -209,6 +210,7 @@ pub fn (h CommonHeader) str() string {
|
||||||
.sec_fetch_site { 'Sec-Fetch-Site' }
|
.sec_fetch_site { 'Sec-Fetch-Site' }
|
||||||
.sec_fetch_user { 'Sec-Fetch-User' }
|
.sec_fetch_user { 'Sec-Fetch-User' }
|
||||||
.sec_websocket_accept { 'Sec-WebSocket-Accept' }
|
.sec_websocket_accept { 'Sec-WebSocket-Accept' }
|
||||||
|
.sec_websocket_key { 'Sec-WebSocket-Key' }
|
||||||
.server { 'Server' }
|
.server { 'Server' }
|
||||||
.server_timing { 'Server-Timing' }
|
.server_timing { 'Server-Timing' }
|
||||||
.set_cookie { 'Set-Cookie' }
|
.set_cookie { 'Set-Cookie' }
|
||||||
|
@ -314,6 +316,7 @@ const common_header_map = {
|
||||||
'sec-fetch-site': .sec_fetch_site
|
'sec-fetch-site': .sec_fetch_site
|
||||||
'sec-fetch-user': .sec_fetch_user
|
'sec-fetch-user': .sec_fetch_user
|
||||||
'sec-websocket-accept': .sec_websocket_accept
|
'sec-websocket-accept': .sec_websocket_accept
|
||||||
|
'sec_websocket_key': .sec_websocket_key
|
||||||
'server': .server
|
'server': .server
|
||||||
'server-timing': .server_timing
|
'server-timing': .server_timing
|
||||||
'set-cookie': .set_cookie
|
'set-cookie': .set_cookie
|
||||||
|
|
|
@ -134,24 +134,60 @@ fn (mut s Server) serve_client(mut c Client) ! {
|
||||||
c.logger.debug('server-> End serve client (${c.id})')
|
c.logger.debug('server-> End serve client (${c.id})')
|
||||||
}
|
}
|
||||||
mut handshake_response, mut server_client := s.handle_server_handshake(mut c)!
|
mut handshake_response, mut server_client := s.handle_server_handshake(mut c)!
|
||||||
accept := s.send_connect_event(mut server_client)!
|
s.attach_client(mut server_client, handshake_response)!
|
||||||
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)
|
|
||||||
c.listen() or {
|
c.listen() or {
|
||||||
s.logger.error(err.msg())
|
s.logger.error(err.msg())
|
||||||
return err
|
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
|
// setup_callbacks initialize all callback functions
|
||||||
fn (mut s Server) setup_callbacks(mut sc ServerClient) {
|
fn (mut s Server) setup_callbacks(mut sc ServerClient) {
|
||||||
if s.message_callbacks.len > 0 {
|
if s.message_callbacks.len > 0 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue