A bit more logging to debug WebSocket issues
This commit is contained in:
parent
b82ca3d34b
commit
a55b4b2b49
|
@ -31,6 +31,7 @@ On first run it will create the default settings.toml file for you which you may
|
||||||
Title = "BareRTC"
|
Title = "BareRTC"
|
||||||
Branding = "BareRTC"
|
Branding = "BareRTC"
|
||||||
WebsiteURL = "https://www.example.com"
|
WebsiteURL = "https://www.example.com"
|
||||||
|
UseXForwardedFor = true
|
||||||
|
|
||||||
[JWT]
|
[JWT]
|
||||||
Enabled = false
|
Enabled = false
|
||||||
|
@ -57,6 +58,7 @@ A description of the config directives includes:
|
||||||
* **WebsiteURL** is the base URL of your actual website which is used in a couple of places:
|
* **WebsiteURL** is the base URL of your actual website which is used in a couple of places:
|
||||||
* The About page will link to your website.
|
* The About page will link to your website.
|
||||||
* If using [JWT authentication](#authentication), avatar and profile URLs may be relative (beginning with a "/") and will append to your website URL to safe space on the JWT token size!
|
* If using [JWT authentication](#authentication), avatar and profile URLs may be relative (beginning with a "/") and will append to your website URL to safe space on the JWT token size!
|
||||||
|
* **UseXForwardedFor**: set it to true and (for logging) the user's remote IP will use the X-Real-IP header or the first address in X-Forwarded-For. Set this if you run the app behind a proxy like nginx if you want IPs not to be all localhost.
|
||||||
* **JWT**: settings for JWT [Authentication](#authentication).
|
* **JWT**: settings for JWT [Authentication](#authentication).
|
||||||
* Enabled (bool): activate the JWT token authentication feature.
|
* Enabled (bool): activate the JWT token authentication feature.
|
||||||
* Strict (bool): if true, **only** valid signed JWT tokens may log in. If false, users with no/invalid token can enter their own username without authentication.
|
* Strict (bool): if true, **only** valid signed JWT tokens may log in. If false, users with no/invalid token can enter their own username without authentication.
|
||||||
|
|
|
@ -10,8 +10,14 @@ import (
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Version of the config format - when new fields are added, it will attempt
|
||||||
|
// to write the settings.toml to disk so new defaults populate.
|
||||||
|
var currentVersion = 1
|
||||||
|
|
||||||
// Config for your BareRTC app.
|
// Config for your BareRTC app.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
Version int // will re-save your settings.toml on migrations
|
||||||
|
|
||||||
JWT struct {
|
JWT struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
Strict bool
|
Strict bool
|
||||||
|
@ -22,6 +28,8 @@ type Config struct {
|
||||||
Branding string
|
Branding string
|
||||||
WebsiteURL string
|
WebsiteURL string
|
||||||
|
|
||||||
|
UseXForwardedFor bool
|
||||||
|
|
||||||
PublicChannels []Channel
|
PublicChannels []Channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +96,16 @@ func LoadSettings() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = toml.Decode(string(data), &Current)
|
_, err = toml.Decode(string(data), &Current)
|
||||||
|
|
||||||
|
// Have we added new config fields? Save the settings.toml.
|
||||||
|
if Current.Version < currentVersion {
|
||||||
|
log.Warn("New options are available for your settings.toml file. Your settings will be re-saved now.")
|
||||||
|
Current.Version = currentVersion
|
||||||
|
if err := WriteSettings(); err != nil {
|
||||||
|
log.Error("Couldn't write your settings.toml file: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,14 @@ func (s *Server) OnLogin(sub *Subscriber, msg Message) {
|
||||||
sub.JWTClaims = claims
|
sub.JWTClaims = claims
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("JWT claims: %+v", claims)
|
if claims.Subject != "" {
|
||||||
|
log.Debug("JWT claims: %+v", claims)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Somehow no username?
|
||||||
|
if msg.Username == "" {
|
||||||
|
msg.Username = "anonymous"
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the username is unique, or rename it.
|
// Ensure the username is unique, or rename it.
|
||||||
var duplicate bool
|
var duplicate bool
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.kirsle.net/apps/barertc/pkg/config"
|
"git.kirsle.net/apps/barertc/pkg/config"
|
||||||
"git.kirsle.net/apps/barertc/pkg/jwt"
|
"git.kirsle.net/apps/barertc/pkg/jwt"
|
||||||
|
@ -71,7 +72,12 @@ func IndexPage() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
// END load the template
|
// END load the template
|
||||||
|
|
||||||
log.Info("Index route hit")
|
log.Info("GET / [%s] %s", r.RemoteAddr, strings.Join([]string{
|
||||||
|
r.Header.Get("X-Forwarded-For"),
|
||||||
|
r.Header.Get("X-Real-IP"),
|
||||||
|
r.Header.Get("User-Agent"),
|
||||||
|
util.IPAddress(r),
|
||||||
|
}, " "))
|
||||||
tmpl.ExecuteTemplate(w, "index", values)
|
tmpl.ExecuteTemplate(w, "index", values)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
23
pkg/util/ip_address.go
Normal file
23
pkg/util/ip_address.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.kirsle.net/apps/barertc/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
IPAddress returns the best guess at the user's IP address, as a string for logging.
|
||||||
|
*/
|
||||||
|
func IPAddress(r *http.Request) string {
|
||||||
|
if config.Current.UseXForwardedFor {
|
||||||
|
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
|
||||||
|
return realIP
|
||||||
|
}
|
||||||
|
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
||||||
|
return strings.SplitN(xff, " ", 1)[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r.RemoteAddr
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"git.kirsle.net/apps/barertc/pkg/jwt"
|
"git.kirsle.net/apps/barertc/pkg/jwt"
|
||||||
"git.kirsle.net/apps/barertc/pkg/log"
|
"git.kirsle.net/apps/barertc/pkg/log"
|
||||||
|
"git.kirsle.net/apps/barertc/pkg/util"
|
||||||
"nhooyr.io/websocket"
|
"nhooyr.io/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,14 +36,18 @@ func (sub *Subscriber) ReadLoop(s *Server) {
|
||||||
for {
|
for {
|
||||||
msgType, data, err := sub.conn.Read(sub.ctx)
|
msgType, data, err := sub.conn.Read(sub.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("ReadLoop error(%s): %+v", sub.Username, err)
|
log.Error("ReadLoop error(%d=%s): %+v", sub.ID, sub.Username, err)
|
||||||
s.DeleteSubscriber(sub)
|
s.DeleteSubscriber(sub)
|
||||||
|
|
||||||
|
// Notify if this user was auth'd
|
||||||
|
if sub.authenticated {
|
||||||
s.Broadcast(Message{
|
s.Broadcast(Message{
|
||||||
Action: ActionPresence,
|
Action: ActionPresence,
|
||||||
Username: sub.Username,
|
Username: sub.Username,
|
||||||
Message: "has exited the room!",
|
Message: "has exited the room!",
|
||||||
})
|
})
|
||||||
s.SendWhoList()
|
s.SendWhoList()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,9 +58,9 @@ func (sub *Subscriber) ReadLoop(s *Server) {
|
||||||
|
|
||||||
// Read the user's posted message.
|
// Read the user's posted message.
|
||||||
var msg Message
|
var msg Message
|
||||||
log.Debug("Read(%s): %s", sub.Username, data)
|
log.Debug("Read(%d=%s): %s", sub.ID, sub.Username, data)
|
||||||
if err := json.Unmarshal(data, &msg); err != nil {
|
if err := json.Unmarshal(data, &msg); err != nil {
|
||||||
log.Error("Message error: %s", err)
|
log.Error("Read(%d=%s) Message error: %s", sub.ID, sub.Username, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +95,7 @@ func (sub *Subscriber) SendJSON(v interface{}) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Debug("SendJSON(%s): %s", sub.Username, data)
|
log.Debug("SendJSON(%d=%s): %s", sub.ID, sub.Username, data)
|
||||||
return sub.conn.Write(sub.ctx, websocket.MessageText, data)
|
return sub.conn.Write(sub.ctx, websocket.MessageText, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,15 +120,18 @@ func (sub *Subscriber) ChatServer(message string, v ...interface{}) {
|
||||||
// WebSocket handles the /ws websocket connection.
|
// WebSocket handles the /ws websocket connection.
|
||||||
func (s *Server) WebSocket() http.HandlerFunc {
|
func (s *Server) WebSocket() http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ip := util.IPAddress(r)
|
||||||
|
log.Info("WebSocket connection from %s - %s", ip, r.Header.Get("User-Agent"))
|
||||||
|
log.Debug("Headers: %+v", r.Header)
|
||||||
c, err := websocket.Accept(w, r, nil)
|
c, err := websocket.Accept(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
fmt.Fprintf(w, "Websocket error: %s", err)
|
fmt.Fprintf(w, "Could not accept websocket connection: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer c.Close(websocket.StatusInternalError, "the sky is falling")
|
defer c.Close(websocket.StatusInternalError, "the sky is falling")
|
||||||
|
|
||||||
log.Debug("WebSocket: %s has connected", r.RemoteAddr)
|
log.Debug("WebSocket: %s has connected", ip)
|
||||||
|
|
||||||
// CloseRead starts a goroutine that will read from the connection
|
// CloseRead starts a goroutine that will read from the connection
|
||||||
// until it is closed.
|
// until it is closed.
|
||||||
|
@ -141,7 +149,7 @@ func (s *Server) WebSocket() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
s.AddSubscriber(sub)
|
s.AddSubscriber(sub)
|
||||||
// defer s.DeleteSubscriber(sub)
|
defer s.DeleteSubscriber(sub)
|
||||||
|
|
||||||
go sub.ReadLoop(s)
|
go sub.ReadLoop(s)
|
||||||
pinger := time.NewTicker(PingInterval)
|
pinger := time.NewTicker(PingInterval)
|
||||||
|
@ -174,7 +182,7 @@ func (s *Server) AddSubscriber(sub *Subscriber) {
|
||||||
// Assign a unique ID.
|
// Assign a unique ID.
|
||||||
SubscriberID++
|
SubscriberID++
|
||||||
sub.ID = SubscriberID
|
sub.ID = SubscriberID
|
||||||
log.Debug("AddSubscriber: %s", sub.ID)
|
log.Debug("AddSubscriber: ID #%d", sub.ID)
|
||||||
|
|
||||||
s.subscribersMu.Lock()
|
s.subscribersMu.Lock()
|
||||||
s.subscribers[sub] = struct{}{}
|
s.subscribers[sub] = struct{}{}
|
||||||
|
@ -210,13 +218,10 @@ func (s *Server) DeleteSubscriber(sub *Subscriber) {
|
||||||
// IterSubscribers loops over the subscriber list with a read lock. If the
|
// IterSubscribers loops over the subscriber list with a read lock. If the
|
||||||
// caller already holds a lock, pass the optional `true` parameter for isLocked.
|
// caller already holds a lock, pass the optional `true` parameter for isLocked.
|
||||||
func (s *Server) IterSubscribers(isLocked ...bool) []*Subscriber {
|
func (s *Server) IterSubscribers(isLocked ...bool) []*Subscriber {
|
||||||
log.Debug("IterSubscribers START..")
|
|
||||||
|
|
||||||
var result = []*Subscriber{}
|
var result = []*Subscriber{}
|
||||||
|
|
||||||
// Has the caller already taken the read lock or do we get it?
|
// Has the caller already taken the read lock or do we get it?
|
||||||
if locked := len(isLocked) > 0 && isLocked[0]; !locked {
|
if locked := len(isLocked) > 0 && isLocked[0]; !locked {
|
||||||
log.Debug("Taking the lock")
|
|
||||||
s.subscribersMu.RLock()
|
s.subscribersMu.RLock()
|
||||||
defer s.subscribersMu.RUnlock()
|
defer s.subscribersMu.RUnlock()
|
||||||
}
|
}
|
||||||
|
@ -225,7 +230,6 @@ func (s *Server) IterSubscribers(isLocked ...bool) []*Subscriber {
|
||||||
result = append(result, sub)
|
result = append(result, sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("IterSubscribers STOP..")
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -347,7 +347,8 @@ const app = Vue.createApp({
|
||||||
|
|
||||||
// Dial the WebSocket connection.
|
// Dial the WebSocket connection.
|
||||||
dial() {
|
dial() {
|
||||||
// console.log("Dialing WebSocket...");
|
this.ChatClient("Establishing connection to server...");
|
||||||
|
|
||||||
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
|
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
|
||||||
const conn = new WebSocket(`${proto}://${location.host}/ws`);
|
const conn = new WebSocket(`${proto}://${location.host}/ws`);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user