Remove unused eventpool package, improve logging levels, standardize websocket handling, and add HandleClose functionality.
This commit is contained in:
@@ -1 +1,37 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"encoders.orly/envelopes/closeenvelope"
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/log"
|
||||
)
|
||||
|
||||
// HandleClose processes a CLOSE envelope by unmarshalling the request,
|
||||
// validates the presence of an <id> field, and signals cancellation for
|
||||
// the associated listener through the server's publisher mechanism.
|
||||
func (l *Listener) HandleClose(
|
||||
req []byte,
|
||||
) (err error) {
|
||||
var rem []byte
|
||||
env := closeenvelope.New()
|
||||
if rem, err = env.Unmarshal(req); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if len(rem) > 0 {
|
||||
log.I.F("extra '%s'", rem)
|
||||
}
|
||||
if len(env.ID) == 0 {
|
||||
return errors.New("CLOSE has no <id>")
|
||||
}
|
||||
l.publishers.Receive(
|
||||
&W{
|
||||
Cancel: true,
|
||||
remote: l.remote,
|
||||
Conn: l.conn,
|
||||
Id: string(env.ID),
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func (l *Listener) HandleEvent(c context.Context, msg []byte) (
|
||||
if _, _, err = l.SaveEvent(c, env.E, false, nil); chk.E(err) {
|
||||
return
|
||||
}
|
||||
// Send a success response after storing
|
||||
// Send a success response storing
|
||||
if err = Ok.Ok(l, env, ""); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ func (l *Listener) HandleMessage(msg []byte, remote string) {
|
||||
err = l.HandleReq(l.ctx, rem)
|
||||
case closeenvelope.L:
|
||||
log.D.F("closeenvelope: %s", rem)
|
||||
err = l.HandleClose(rem)
|
||||
case authenvelope.L:
|
||||
log.D.F("authenvelope: %s", rem)
|
||||
default:
|
||||
|
||||
@@ -9,10 +9,15 @@ import (
|
||||
"github.com/coder/websocket"
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/log"
|
||||
"protocol.orly/publish"
|
||||
"utils.orly/units"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultWriteWait = 10 * time.Second
|
||||
DefaultPongWait = 60 * time.Second
|
||||
DefaultPingWait = DefaultPongWait / 2
|
||||
DefaultMaxMessageSize = 1 * units.Mb
|
||||
|
||||
// CloseMessage denotes a close control message. The optional message
|
||||
// payload contains a numeric code and text. Use the FormatCloseMessage
|
||||
// function to format a close message payload.
|
||||
@@ -42,8 +47,7 @@ func (s *Server) HandleWebsocket(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
whitelist:
|
||||
var cancel context.CancelFunc
|
||||
s.Ctx, cancel = context.WithCancel(s.Ctx)
|
||||
ctx, cancel := context.WithCancel(s.Ctx)
|
||||
defer cancel()
|
||||
var err error
|
||||
var conn *websocket.Conn
|
||||
@@ -52,24 +56,32 @@ whitelist:
|
||||
); chk.E(err) {
|
||||
return
|
||||
}
|
||||
conn.SetReadLimit(DefaultMaxMessageSize)
|
||||
defer conn.CloseNow()
|
||||
listener := &Listener{
|
||||
ctx: s.Ctx,
|
||||
ctx: ctx,
|
||||
Server: s,
|
||||
conn: conn,
|
||||
remote: remote,
|
||||
}
|
||||
listener.publishers = publish.New(NewPublisher())
|
||||
go s.Pinger(s.Ctx, conn, time.NewTicker(time.Second*10), cancel)
|
||||
ticker := time.NewTicker(DefaultPingWait)
|
||||
go s.Pinger(ctx, conn, ticker, cancel)
|
||||
defer func() {
|
||||
log.D.F("closing websocket connection from %s", remote)
|
||||
cancel()
|
||||
ticker.Stop()
|
||||
listener.publishers.Receive(&W{Cancel: true})
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-s.Ctx.Done():
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
var typ websocket.MessageType
|
||||
var msg []byte
|
||||
if typ, msg, err = conn.Read(s.Ctx); err != nil {
|
||||
log.I.F("waiting for message from %s", remote)
|
||||
if typ, msg, err = conn.Read(ctx); chk.E(err) {
|
||||
if strings.Contains(
|
||||
err.Error(), "use of closed network connection",
|
||||
) {
|
||||
@@ -88,7 +100,7 @@ whitelist:
|
||||
return
|
||||
}
|
||||
if typ == PingMessage {
|
||||
if err = conn.Write(s.Ctx, PongMessage, msg); chk.E(err) {
|
||||
if err = conn.Write(ctx, PongMessage, msg); chk.E(err) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
@@ -109,8 +121,7 @@ func (s *Server) Pinger(
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err = conn.Write(ctx, PingMessage, nil); err != nil {
|
||||
log.E.F("error writing ping: %v; closing websocket", err)
|
||||
if err = conn.Ping(ctx); chk.E(err) {
|
||||
return
|
||||
}
|
||||
case <-ctx.Done():
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/log"
|
||||
"next.orly.dev/app/config"
|
||||
"protocol.orly/publish"
|
||||
)
|
||||
|
||||
func Run(
|
||||
@@ -24,9 +25,10 @@ func Run(
|
||||
}()
|
||||
// start listener
|
||||
l := &Server{
|
||||
Ctx: ctx,
|
||||
Config: cfg,
|
||||
D: db,
|
||||
Ctx: ctx,
|
||||
Config: cfg,
|
||||
D: db,
|
||||
publishers: publish.New(NewPublisher()),
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%d", cfg.Listen, cfg.Port)
|
||||
log.I.F("starting listener on http://%s", addr)
|
||||
|
||||
@@ -93,10 +93,10 @@ func (p *P) Receive(msg typer.T) {
|
||||
if m.Cancel {
|
||||
if m.Id == "" {
|
||||
p.removeSubscriber(m.Conn)
|
||||
log.T.F("removed listener %s", m.remote)
|
||||
log.D.F("removed listener %s", m.remote)
|
||||
} else {
|
||||
p.removeSubscriberId(m.Conn, m.Id)
|
||||
log.T.C(
|
||||
log.D.C(
|
||||
func() string {
|
||||
return fmt.Sprintf(
|
||||
"removed subscription %s for %s", m.Id,
|
||||
|
||||
@@ -2,6 +2,7 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"database.orly"
|
||||
@@ -20,7 +21,11 @@ type Server struct {
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.T.F("path %v header %v", r.URL, r.Header)
|
||||
log.T.C(
|
||||
func() string {
|
||||
return fmt.Sprintf("path %v header %v", r.URL, r.Header)
|
||||
},
|
||||
)
|
||||
if r.Header.Get("Upgrade") == "websocket" {
|
||||
s.HandleWebsocket(w, r)
|
||||
} else if r.Header.Get("Accept") == "application/nostr+json" {
|
||||
|
||||
Reference in New Issue
Block a user