Browse Source

Remove unused `eventpool` package, improve logging levels, standardize websocket handling, and add `HandleClose` functionality.

main
mleku 4 months ago
parent
commit
5d04afd748
No known key found for this signature in database
  1. 36
      app/handle-close.go
  2. 2
      app/handle-event.go
  3. 1
      app/handle-message.go
  4. 33
      app/handle-websocket.go
  5. 8
      app/main.go
  6. 4
      app/publisher.go
  7. 7
      app/server.go
  8. 65
      cmd/eventpool/eventpool.go

36
app/handle-close.go

@ -1 +1,37 @@
package app 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
}

2
app/handle-event.go

@ -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) { if _, _, err = l.SaveEvent(c, env.E, false, nil); chk.E(err) {
return return
} }
// Send a success response after storing // Send a success response storing
if err = Ok.Ok(l, env, ""); chk.E(err) { if err = Ok.Ok(l, env, ""); chk.E(err) {
return return
} }

1
app/handle-message.go

@ -35,6 +35,7 @@ func (l *Listener) HandleMessage(msg []byte, remote string) {
err = l.HandleReq(l.ctx, rem) err = l.HandleReq(l.ctx, rem)
case closeenvelope.L: case closeenvelope.L:
log.D.F("closeenvelope: %s", rem) log.D.F("closeenvelope: %s", rem)
err = l.HandleClose(rem)
case authenvelope.L: case authenvelope.L:
log.D.F("authenvelope: %s", rem) log.D.F("authenvelope: %s", rem)
default: default:

33
app/handle-websocket.go

@ -9,10 +9,15 @@ import (
"github.com/coder/websocket" "github.com/coder/websocket"
"lol.mleku.dev/chk" "lol.mleku.dev/chk"
"lol.mleku.dev/log" "lol.mleku.dev/log"
"protocol.orly/publish" "utils.orly/units"
) )
const ( 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 // CloseMessage denotes a close control message. The optional message
// payload contains a numeric code and text. Use the FormatCloseMessage // payload contains a numeric code and text. Use the FormatCloseMessage
// function to format a close message payload. // function to format a close message payload.
@ -42,8 +47,7 @@ func (s *Server) HandleWebsocket(w http.ResponseWriter, r *http.Request) {
return return
} }
whitelist: whitelist:
var cancel context.CancelFunc ctx, cancel := context.WithCancel(s.Ctx)
s.Ctx, cancel = context.WithCancel(s.Ctx)
defer cancel() defer cancel()
var err error var err error
var conn *websocket.Conn var conn *websocket.Conn
@ -52,24 +56,32 @@ whitelist:
); chk.E(err) { ); chk.E(err) {
return return
} }
conn.SetReadLimit(DefaultMaxMessageSize)
defer conn.CloseNow() defer conn.CloseNow()
listener := &Listener{ listener := &Listener{
ctx: s.Ctx, ctx: ctx,
Server: s, Server: s,
conn: conn, conn: conn,
remote: remote, remote: remote,
} }
listener.publishers = publish.New(NewPublisher()) ticker := time.NewTicker(DefaultPingWait)
go s.Pinger(s.Ctx, conn, time.NewTicker(time.Second*10), cancel) 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 { for {
select { select {
case <-s.Ctx.Done(): case <-ctx.Done():
return return
default: default:
} }
var typ websocket.MessageType var typ websocket.MessageType
var msg []byte 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( if strings.Contains(
err.Error(), "use of closed network connection", err.Error(), "use of closed network connection",
) { ) {
@ -88,7 +100,7 @@ whitelist:
return return
} }
if typ == PingMessage { 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 return
} }
continue continue
@ -109,8 +121,7 @@ func (s *Server) Pinger(
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
if err = conn.Write(ctx, PingMessage, nil); err != nil { if err = conn.Ping(ctx); chk.E(err) {
log.E.F("error writing ping: %v; closing websocket", err)
return return
} }
case <-ctx.Done(): case <-ctx.Done():

8
app/main.go

@ -9,6 +9,7 @@ import (
"lol.mleku.dev/chk" "lol.mleku.dev/chk"
"lol.mleku.dev/log" "lol.mleku.dev/log"
"next.orly.dev/app/config" "next.orly.dev/app/config"
"protocol.orly/publish"
) )
func Run( func Run(
@ -24,9 +25,10 @@ func Run(
}() }()
// start listener // start listener
l := &Server{ l := &Server{
Ctx: ctx, Ctx: ctx,
Config: cfg, Config: cfg,
D: db, D: db,
publishers: publish.New(NewPublisher()),
} }
addr := fmt.Sprintf("%s:%d", cfg.Listen, cfg.Port) addr := fmt.Sprintf("%s:%d", cfg.Listen, cfg.Port)
log.I.F("starting listener on http://%s", addr) log.I.F("starting listener on http://%s", addr)

4
app/publisher.go

@ -93,10 +93,10 @@ func (p *P) Receive(msg typer.T) {
if m.Cancel { if m.Cancel {
if m.Id == "" { if m.Id == "" {
p.removeSubscriber(m.Conn) p.removeSubscriber(m.Conn)
log.T.F("removed listener %s", m.remote) log.D.F("removed listener %s", m.remote)
} else { } else {
p.removeSubscriberId(m.Conn, m.Id) p.removeSubscriberId(m.Conn, m.Id)
log.T.C( log.D.C(
func() string { func() string {
return fmt.Sprintf( return fmt.Sprintf(
"removed subscription %s for %s", m.Id, "removed subscription %s for %s", m.Id,

7
app/server.go

@ -2,6 +2,7 @@ package app
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
"database.orly" "database.orly"
@ -20,7 +21,11 @@ type Server struct {
} }
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 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" { if r.Header.Get("Upgrade") == "websocket" {
s.HandleWebsocket(w, r) s.HandleWebsocket(w, r)
} else if r.Header.Get("Accept") == "application/nostr+json" { } else if r.Header.Get("Accept") == "application/nostr+json" {

65
cmd/eventpool/eventpool.go

@ -1,65 +0,0 @@
package main
import (
"time"
"encoders.orly/event"
"encoders.orly/hex"
"encoders.orly/json"
"encoders.orly/tag"
"github.com/pkg/profile"
lol "lol.mleku.dev"
"lol.mleku.dev/chk"
"lukechampine.com/frand"
"utils.orly"
"utils.orly/bufpool"
)
func main() {
lol.SetLogLevel("info")
prof := profile.Start(profile.CPUProfile)
defer prof.Stop()
for range 1000000 {
ev := event.New()
ev.ID = frand.Bytes(32)
ev.Pubkey = frand.Bytes(32)
ev.CreatedAt = time.Now().Unix()
ev.Kind = 1
ev.Tags = &tag.S{
{T: [][]byte{[]byte("t"), []byte("hashtag")}},
{
T: [][]byte{
[]byte("e"),
hex.EncAppend(nil, frand.Bytes(32)),
},
},
}
ev.Content = frand.Bytes(frand.Intn(1024) + 1)
ev.Sig = frand.Bytes(64)
// log.I.S(ev)
b, err := json.Marshal(ev)
if chk.E(err) {
return
}
var bc []byte
bc = append(bc, b...)
// log.I.F("%s", bc)
ev2 := event.New()
if err = json.Unmarshal(b, ev2); chk.E(err) {
return
}
var b2 []byte
if b2, err = json.Marshal(ev); err != nil {
return
}
if !utils.FastEqual(bc, b2) {
return
}
// free up the resources for the next iteration
ev.Free()
ev2.Free()
bufpool.PutBytes(b)
bufpool.PutBytes(b2)
bufpool.PutBytes(bc)
}
}
Loading…
Cancel
Save