chat: fix ws error

Kim committed Oct 21, 2025 at 16:40 UTC 9fbefa935f02534c2e96423c1bf1a7ad02755f7e
2 files changed +29 -7
cmd/example_chat/view.go
+22 -7
@@ -168,10 +168,11 @@ var indexTmpl = template.Must(template.New("chat").Parse(`<!DOCTYPE html>
168 promptEl.textContent = nick + '@chat:~$';
169 }
170 function randomNick(){
171 - const adjs = ['brisk','calm','clever','daring','eager','gentle','merry','rapid','vivid','bright'];
172 - const animals = ['fox','lion','panda','otter','eagle','whale','koala','lynx','squid','yak'];
173 - const a = adjs[Math.floor(Math.random()*adjs.length)];
174 - const b = animals[Math.floor(Math.random()*animals.length)];
171 + // Programming-meme themed nickname
172 + const techs = ['gopher','rustacean','nixer','unix','kernel','docker','kube','vim','emacs','tmux','nvim','git','linux','bsd','wasm','grpc','lambda','pointer','monad','segfault','null','byte','packet','devops','cli'];
173 + const roles = ['wizard','hacker','guru','daemon','runner','scripter','shell','warrior','artisan','smith'];
174 + const a = techs[Math.floor(Math.random()*techs.length)];
175 + const b = roles[Math.floor(Math.random()*roles.length)];
176 const id = Math.random().toString(36).slice(2,6);
177 return a + '-' + b + '-' + id;
178 }
@@ -193,12 +194,25 @@ var indexTmpl = template.Must(template.New("chat").Parse(`<!DOCTYPE html>
194 user.focus();
195 });
196
197 + // Stable color per nickname
198 + const PALETTE = ['#60a5fa','#22c55e','#f59e0b','#ef4444','#a78bfa','#14b8a6','#eab308','#f472b6','#8b5cf6','#06b6d4'];
199 + function hashNick(s){
200 + let h = 0;
201 + for (let i = 0; i < s.length; i++) { h = ((h << 5) - h) + s.charCodeAt(i); h |= 0; }
202 + return (h >>> 0);
203 + }
204 + function colorFor(nick){
205 + const idx = hashNick(nick || 'anon') % PALETTE.length;
206 + return PALETTE[idx];
207 + }
208 function append(msg){
209 const div = document.createElement('div');
210 div.className = 'line';
211 const ts = new Date(msg.ts).toLocaleTimeString();
200 - div.innerHTML = '<span class="ts">[' + ts + ']</span> <span class="usr">' +
201 - (msg.user || 'anon') + '</span>: ' + escapeHTML(msg.text || '');
212 + const nick = (msg.user || 'anon');
213 + const color = colorFor(nick);
214 + div.innerHTML = '<span class="ts">[' + ts + ']</span> <span class="usr" style="color:' + color + '">' +
215 + nick + '</span>: ' + escapeHTML(msg.text || '');
216 log.appendChild(div);
217 log.scrollTop = log.scrollHeight;
218 }
@@ -207,7 +221,8 @@ var indexTmpl = template.Must(template.New("chat").Parse(`<!DOCTYPE html>
221 }
222
223 const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
210 - const ws = new WebSocket(wsProto + '://' + location.host + location.pathname + 'ws');
224 + const basePath = location.pathname.endsWith('/') ? location.pathname : (location.pathname + '/');
225 + const ws = new WebSocket(wsProto + '://' + location.host + basePath + 'ws');
226 ws.onmessage = (e) => { try{ append(JSON.parse(e.data)); }catch(_){ } };
227 function send(){
228 const payload = { user: (user.value || 'anon'), text: cmd.value.trim() };
relaydns/director.go
+7
@@ -226,6 +226,13 @@ func (d *Director) ProxyHTTP(w http.ResponseWriter, r *http.Request, peerID, pat
226 if err := resp.Write(clientBuf); err == nil {
227 _ = clientBuf.Flush()
228 }
229 + // Flush any bytes already buffered by http.ReadResponse's bufio.Reader
230 + if n := br.Buffered(); n > 0 {
231 + tmp := make([]byte, n)
232 + if _, err := io.ReadFull(br, tmp); err == nil {
233 + _, _ = clientConn.Write(tmp)
234 + }
235 + }
236 // Raw byte tunnel between client and upstream stream
237 go io.Copy(s, clientConn)
238 io.Copy(clientConn, s)