Files
Cyrene_v2/plugins/channels/qq_obv11/ws_server.go
T

74 lines
2.2 KiB
Go

package qq_obv11
// This file implements the server role of the OneBot v11 WebSocket channel:
// the adapter listens on a TCP port and accepts inbound OneBot (反向) client
// connections, validates any configured access token, and upgrades each HTTP
// request to a WebSocket session.
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"strings"
)
// connectServer starts the HTTP listener in server mode. It returns once the
// listener is bound; the session becomes live when a OneBot client connects.
func (a *Adapter) connectServer(ctx context.Context) error {
if a.cfg.ListenAddr == "" {
return errors.New("qq-obv11: server mode requires listenAddr")
}
mux := http.NewServeMux()
mux.HandleFunc("/", a.wsHandler)
ln, err := net.Listen("tcp", a.cfg.ListenAddr)
if err != nil {
return fmt.Errorf("qq-obv11: listen %s: %w", a.cfg.ListenAddr, err)
}
a.mu.Lock()
a.ln = ln
a.srv = &http.Server{Handler: mux}
a.mu.Unlock()
go func() {
if serveErr := a.srv.Serve(ln); serveErr != nil && serveErr != http.ErrServerClosed {
a.log.Warn("websocket server stopped", "err", serveErr)
}
}()
a.log.Info("websocket server listening (server mode)", "addr", ln.Addr().String())
return nil
}
// wsHandler is the HTTP handler that upgrades one inbound OneBot client
// connection to a WebSocket session.
func (a *Adapter) wsHandler(w http.ResponseWriter, r *http.Request) {
if a.cfg.AccessToken != "" && !a.checkAuth(r) {
http.Error(w, "Forbidden: invalid access_token", http.StatusForbidden)
return
}
conn, err := upgradeWS(w, r)
if err != nil {
a.log.Warn("websocket upgrade failed", "err", err)
return
}
a.setConn(conn)
a.log.Info("onebot client connected (server mode)", "remote", r.RemoteAddr)
}
// checkAuth validates the access token carried by an upgrade request. OneBot
// implementations pass it either as an access_token query parameter or as an
// Authorization: Bearer header.
func (a *Adapter) checkAuth(r *http.Request) bool {
if got := r.URL.Query().Get("access_token"); got != "" {
return got == a.cfg.AccessToken
}
auth := r.Header.Get("Authorization")
if strings.HasPrefix(auth, "Bearer ") {
return strings.TrimPrefix(auth, "Bearer ") == a.cfg.AccessToken
}
return false
}