khatru/handlers.go

288 lines
6.4 KiB
Go
Raw Normal View History

package relayer
2021-01-13 23:46:06 -03:00
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
2022-07-11 16:21:47 -03:00
"fmt"
2021-01-13 23:46:06 -03:00
"net/http"
"time"
2022-01-02 09:00:14 -03:00
"github.com/fiatjaf/go-nostr"
"github.com/fiatjaf/go-nostr/nip11"
2021-01-13 23:46:06 -03:00
"github.com/gorilla/websocket"
)
const (
// Time allowed to write a message to the peer.
writeWait = 10 * time.Second
// Time allowed to read the next pong message from the peer.
pongWait = 60 * time.Second
// Send pings to peer with this period. Must be less than pongWait.
pingPeriod = pongWait / 2
// Maximum message size allowed from peer.
maxMessageSize = 512000
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
func handleWebsocket(relay Relay) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
store := relay.Storage()
advancedQuerier, _ := store.(AdvancedQuerier)
advancedDeleter, _ := store.(AdvancedDeleter)
advancedSaver, _ := store.(AdvancedSaver)
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Warn().Err(err).Msg("failed to upgrade websocket")
return
}
2021-12-27 11:17:15 -03:00
ticker := time.NewTicker(pingPeriod)
2021-01-13 23:46:06 -03:00
ws := &WebSocket{conn: conn}
// reader
go func() {
defer func() {
2021-12-27 11:17:15 -03:00
ticker.Stop()
conn.Close()
}()
2021-01-13 23:46:06 -03:00
conn.SetReadLimit(maxMessageSize)
2021-01-13 23:46:06 -03:00
conn.SetReadDeadline(time.Now().Add(pongWait))
conn.SetPongHandler(func(string) error {
conn.SetReadDeadline(time.Now().Add(pongWait))
return nil
})
2021-01-13 23:46:06 -03:00
for {
typ, message, err := conn.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(
err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
log.Warn().Err(err).Msg("unexpected close error")
2021-02-14 21:08:02 -03:00
}
break
2021-02-14 21:08:02 -03:00
}
if typ == websocket.PingMessage {
ws.WriteMessage(websocket.PongMessage, nil)
continue
2021-02-17 16:59:56 -03:00
}
2021-01-13 23:46:06 -03:00
go func(message []byte) {
var notice string
defer func() {
if notice != "" {
ws.WriteJSON([]interface{}{"NOTICE", notice})
}
}()
2021-02-14 21:08:02 -03:00
var request []json.RawMessage
if err := json.Unmarshal(message, &request); err != nil {
// stop silently
return
}
if len(request) < 2 {
notice = "request has less than 2 parameters"
2021-02-14 21:08:02 -03:00
return
}
var typ string
json.Unmarshal(request[0], &typ)
switch typ {
case "EVENT":
// it's a new event
2022-01-02 09:00:14 -03:00
var evt nostr.Event
if err := json.Unmarshal(request[1], &evt); err != nil {
notice = "failed to decode event: " + err.Error()
2021-02-14 21:08:02 -03:00
return
}
// check serialization
serialized := evt.Serialize()
2021-02-14 21:08:02 -03:00
// assign ID
hash := sha256.Sum256(serialized)
evt.ID = hex.EncodeToString(hash[:])
2021-02-14 21:08:02 -03:00
// block too many indexable tags
t := 0
for _, tag := range evt.Tags {
if len(tag[0]) == 1 {
t++
}
}
if t > 3 {
notice = "too many indexable tags"
return
}
// check signature (requires the ID to be set)
if ok, err := evt.CheckSignature(); err != nil {
notice = "signature verification error"
return
} else if !ok {
notice = "signature invalid"
return
}
2021-01-13 23:46:06 -03:00
2022-07-11 16:21:47 -03:00
if evt.Kind == 5 {
// event deletion -- nip09
for _, tag := range evt.Tags {
if len(tag) >= 2 && tag[0] == "e" {
if advancedDeleter != nil {
advancedDeleter.BeforeDelete(tag[1], evt.PubKey)
}
if err := store.DeleteEvent(tag[1], evt.PubKey); err != nil {
2022-07-11 16:21:47 -03:00
notice = fmt.Sprintf("failed to delete: %s", err.Error())
return
}
if advancedDeleter != nil {
advancedDeleter.AfterDelete(tag[1], evt.PubKey)
}
2022-07-11 16:21:47 -03:00
}
}
return
}
if !relay.AcceptEvent(&evt) {
return
}
2022-11-03 14:30:53 -03:00
if 20000 <= evt.Kind && evt.Kind < 30000 {
// do not store ephemeral events
} else {
if advancedSaver != nil {
advancedSaver.BeforeSave(&evt)
}
2022-11-03 14:30:53 -03:00
if err := store.SaveEvent(&evt); err != nil {
notice = err.Error()
return
}
2021-01-13 23:46:06 -03:00
2022-11-03 14:30:53 -03:00
if advancedSaver != nil {
advancedSaver.AfterSave(&evt)
}
}
notifyListeners(&evt)
break
case "REQ":
var id string
json.Unmarshal(request[1], &id)
if id == "" {
notice = "REQ has no <id>"
return
}
2021-01-13 23:46:06 -03:00
2022-02-13 08:37:38 -03:00
filters := make(nostr.Filters, len(request)-2)
for i, filterReq := range request[2:] {
if err := json.Unmarshal(
filterReq,
&filters[i],
); err != nil {
notice = "failed to decode filter"
return
}
2021-01-13 23:46:06 -03:00
2022-07-24 19:58:34 -03:00
filter := &filters[i]
if advancedQuerier != nil {
2022-07-24 19:58:34 -03:00
advancedQuerier.BeforeQuery(filter)
}
2022-07-24 19:58:34 -03:00
events, err := store.QueryEvents(filter)
if err == nil {
2022-07-24 19:58:34 -03:00
if advancedQuerier != nil {
advancedQuerier.AfterQuery(events, filter)
}
if filter.Limit > 0 && len(events) > filter.Limit {
events = events[0:filter.Limit]
}
for _, event := range events {
ws.WriteJSON([]interface{}{"EVENT", id, event})
}
2022-07-24 19:58:34 -03:00
ws.WriteJSON([]interface{}{"EOSE", id})
}
}
2021-01-13 23:46:06 -03:00
setListener(id, ws, filters)
break
case "CLOSE":
var id string
json.Unmarshal(request[0], &id)
if id == "" {
notice = "CLOSE has no <id>"
return
}
2021-01-13 23:46:06 -03:00
removeListener(ws, id)
break
default:
notice = "unknown message type " + typ
return
}
}(message)
}
}()
2021-01-13 23:46:06 -03:00
// writer
go func() {
defer func() {
ticker.Stop()
conn.Close()
}()
for {
select {
case <-ticker.C:
err := ws.WriteMessage(websocket.PingMessage, nil)
if err != nil {
log.Warn().Err(err).Msg("error writing ping, closing websocket")
return
}
}
}
}()
2021-01-13 23:46:06 -03:00
}
}
func handleNIP11(relay Relay) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
info := nip11.RelayInformationDocument{
Name: relay.Name(),
Description: "relay powered by the relayer framework",
PubKey: "~",
Contact: "~",
SupportedNIPs: []int{9, 15, 16},
Software: "https://github.com/fiatjaf/relayer",
Version: "~",
}
if ifmer, ok := relay.(Informationer); ok {
info = ifmer.GetNIP11InformationDocument()
}
json.NewEncoder(w).Encode(info)
}
}