-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
examples: add examples/veb/websocket, to show how to use http connect…
…ion upgrade to a websocket, from a `veb` route (#22128)
- Loading branch information
Showing
5 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
const messageList = document.getElementById('message-list'); | ||
const messageInput = document.getElementById('message-input'); | ||
const messageStatus = document.getElementById('message-status'); | ||
const protocol = location.protocol === 'https:' ? 'wss' : 'ws'; | ||
// | ||
let status = '---'; | ||
function set_status(s) { status = s; } | ||
function show_status() { | ||
messageStatus.innerHTML = `${status}, ${socket.readyState}`; | ||
requestAnimationFrame(show_status); | ||
} | ||
requestAnimationFrame(show_status); | ||
// | ||
var socket = start_reconnecting_socket(1000, `${protocol}://${location.host}/ws`); | ||
function start_reconnecting_socket(timeout, target) { | ||
var nsocket = new WebSocket(target); | ||
nsocket.addEventListener('open', (event) => { console.log('Connected to WS server'); set_status('connected'); }); | ||
nsocket.addEventListener('message', (event) => { | ||
messageList.innerHTML += `<li>received: <b>${event.data}</b></li>`; | ||
set_status('received message'); | ||
}); | ||
nsocket.addEventListener('close', (event) => { | ||
console.log('on close'); | ||
set_status('connection closed'); | ||
setTimeout(()=>{ | ||
console.log('Try reconnecting ...') | ||
socket = start_reconnecting_socket(timeout, target); | ||
}, timeout); | ||
}); | ||
nsocket.addEventListener('error', (event) => { console.log('on error'); set_status('error'); }); | ||
return nsocket; | ||
} | ||
|
||
// | ||
function send(message) { | ||
socket.send(message); | ||
set_status('sending message'); | ||
} | ||
messageInput.addEventListener('keyup', ({key}) => { | ||
if (key === 'Enter') { | ||
send(messageInput.value); | ||
messageInput.value = ''; | ||
} | ||
}); |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
html { | ||
box-sizing: border-box; | ||
} | ||
*, *:before, *:after { | ||
box-sizing: inherit; | ||
} | ||
body { | ||
padding: 0px; | ||
margin: 0px; | ||
margin-left: 30px; | ||
margin-right: 30px; | ||
} | ||
|
||
#message-list { | ||
width: 100%; | ||
height: 250px; | ||
padding: 5px; | ||
box-sizing: border-box; | ||
margin: 0px; | ||
margin-bottom: 5px; | ||
border: 1px solid lightgray; | ||
overflow-y: scroll; | ||
overscroll-behavior-y: contain; | ||
scroll-snap-type: y proximity; | ||
} | ||
#message-list > li { | ||
list-style-type: none; | ||
} | ||
#message-list > li:last-child { | ||
scroll-snap-align: end; | ||
border-top: 1px solid #eeeeff !important; | ||
} | ||
|
||
#message-input { | ||
width: 100%; | ||
margin: 0px; | ||
padding: 0px; | ||
font-size: 24px; | ||
line-height: 1; | ||
font-weight: 600; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<!doctype html> | ||
<html lang=en> | ||
<head> | ||
<meta charset=utf-8> | ||
<title>veb websocket example</title> | ||
<link rel="stylesheet" type="text/css" href="/assets/style.css"> | ||
</head> | ||
<body> | ||
<p>This is an example of using V's `veb` and `net.websocket` modules in | ||
the same app, where the websocket connection happens on the /ws route of | ||
your veb app.</p> | ||
<ul id="message-list"></ul> | ||
<input id="message-input" placeholder="Enter your message here" value=""/> | ||
<div>Client status: <span id="message-status">---</span></div> | ||
<script type="text/javascript" src="/assets/client.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
module main | ||
|
||
import os | ||
import veb | ||
import log | ||
import time | ||
import term | ||
import net | ||
import net.http | ||
import net.websocket | ||
|
||
const app_port = 8990 | ||
|
||
fn main() { | ||
mut app := &App{ | ||
wss: new_websocker_server()! | ||
} | ||
app.mount_static_folder_at(os.resource_abs_path('assets'), '/assets')! | ||
app.serve_static('/favicon.ico', os.resource_abs_path('assets/favicon.ico'))! | ||
veb.run[App, Context](mut app, app_port) | ||
} | ||
|
||
pub struct Context { | ||
veb.Context | ||
} | ||
|
||
pub struct App { | ||
veb.StaticHandler | ||
mut: | ||
wss &websocket.Server | ||
} | ||
|
||
pub fn (mut app App) index(mut ctx Context) veb.Result { | ||
return $veb.html() | ||
} | ||
|
||
pub fn (mut app App) ws(mut ctx Context) veb.Result { | ||
key := ctx.get_header(http.CommonHeader.sec_websocket_key) or { '' } | ||
if key == '' { | ||
ctx.error('Invalid websocket handshake. Key is missing.') | ||
return ctx.redirect('/') | ||
} | ||
dump(ctx.req.cookie('token') or { http.Cookie{} }.value) | ||
wlog('> transferring connection with key: ${key}, to the websocket server ${voidptr(app.wss)} ...') | ||
ctx.takeover_conn() | ||
ctx.conn.set_write_timeout(time.infinite) | ||
ctx.conn.set_read_timeout(time.infinite) | ||
spawn fn (mut wss websocket.Server, mut connection net.TcpConn, key string) { | ||
wss.handle_handshake(mut connection, key) or { wlog('handle_handshake error: ${err}') } | ||
wlog('>> wss.handle_handshake finished, key: ${key}') | ||
}(mut app.wss, mut ctx.conn, key) | ||
wlog('> done transferring connection') | ||
return veb.no_result() | ||
} | ||
|
||
fn slog(message string) { | ||
eprintln(term.colorize(term.bright_yellow, message)) | ||
} | ||
|
||
fn wlog(message string) { | ||
eprintln(term.colorize(term.bright_blue, message)) | ||
} | ||
|
||
fn new_websocker_server() !&websocket.Server { | ||
mut logger := &log.Log{} | ||
logger.set_level(.info) | ||
mut wss := websocket.new_server(.ip, app_port, '', logger: logger) | ||
wss.set_ping_interval(100) | ||
wss.on_connect(fn [mut logger] (mut server_client websocket.ServerClient) !bool { | ||
server_client.client.logger = logger | ||
slog('wss.on_connect client.id: ${server_client.client.id} | server_client.client_key: ${server_client.client_key}') | ||
return true | ||
})! | ||
wss.on_close(fn (mut client websocket.Client, code int, reason string) ! { | ||
slog('wss.on_close client.id: ${client.id} | code: ${code}, reason: ${reason}') | ||
}) | ||
wss.on_message(fn [mut wss] (mut client websocket.Client, msg &websocket.Message) ! { | ||
slog('wss.on_message client.id: ${client.id} | msg.opcode: ${msg.opcode} | msg.payload: `${msg.payload.bytestr()}`') | ||
text := '${client.id} says: "${msg.payload.bytestr()}"' | ||
// client.write_string(text) or { slog('client.write err: ${err}') return err } | ||
for i, _ in rlock wss.server_state { | ||
wss.server_state.clients | ||
} { | ||
mut c := rlock wss.server_state { | ||
wss.server_state.clients[i] or { continue } | ||
} | ||
if c.client.get_state() == .open { | ||
c.client.write_string(text) or { | ||
slog('error while broadcasting to i: ${i}, ${voidptr(c)}, err: ${err}') | ||
continue | ||
} | ||
} | ||
} | ||
}) | ||
|
||
slog('Websocket Server initialized, wss: ${voidptr(wss)}') | ||
return wss | ||
} |