mirror of
https://github.com/fiatjaf/nak.git
synced 2026-10-05 15:18:23 +00:00
Merge pull request #154 from mattn/fix/bunker-concurrency
bunker: fix concurrency bugs
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fiatjaf.com/nostr"
|
"fiatjaf.com/nostr"
|
||||||
@@ -235,12 +236,19 @@ var bunker = &cli.Command{
|
|||||||
// it will be stored
|
// it will be stored
|
||||||
newSecret := randString(12)
|
newSecret := randString(12)
|
||||||
|
|
||||||
|
// guards config.Clients, newSecret and cancelPreviousBunkerInfoPrint, which are
|
||||||
|
// accessed from the socket goroutine, the per-request handler goroutines and here
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
// static information
|
// static information
|
||||||
pubkey := sec.Public()
|
pubkey := sec.Public()
|
||||||
npub := nip19.EncodeNpub(pubkey)
|
npub := nip19.EncodeNpub(pubkey)
|
||||||
|
|
||||||
// this function will be called every now and then
|
// this function will be called every now and then
|
||||||
printBunkerInfo := func() {
|
printBunkerInfo := func() {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
iqs := make(url.Values)
|
iqs := make(url.Values)
|
||||||
maps.Copy(iqs, qs)
|
maps.Copy(iqs, qs)
|
||||||
iqs.Set("secret", newSecret)
|
iqs.Set("secret", newSecret)
|
||||||
@@ -342,70 +350,15 @@ var bunker = &cli.Command{
|
|||||||
signer := nip46.NewStaticKeySigner(sec)
|
signer := nip46.NewStaticKeySigner(sec)
|
||||||
signer.DefaultRelays = config.Relays
|
signer.DefaultRelays = config.Relays
|
||||||
|
|
||||||
// unix socket nostrconnect:// handling
|
|
||||||
go func() {
|
|
||||||
for uri := range onSocketConnect(ctx, c) {
|
|
||||||
clientPublicKey, err := nostr.PubKeyFromHex(uri.Host)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log("- got nostrconnect:// request from '%s': %s\n", color.New(color.Bold, color.FgBlue).Sprint(clientPublicKey.Hex()), uri.String())
|
|
||||||
|
|
||||||
relays := uri.Query()["relay"]
|
|
||||||
|
|
||||||
// pre-authorize this client since the user has explicitly added it
|
|
||||||
if !slices.ContainsFunc(config.Clients, func(c BunkerConfigClient) bool {
|
|
||||||
return c.PubKey == clientPublicKey
|
|
||||||
}) {
|
|
||||||
config.Clients = append(config.Clients, BunkerConfigClient{
|
|
||||||
PubKey: clientPublicKey,
|
|
||||||
Name: uri.Query().Get("name"),
|
|
||||||
URL: uri.Query().Get("url"),
|
|
||||||
Icon: uri.Query().Get("icon"),
|
|
||||||
CustomRelays: relays,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if persist != nil {
|
|
||||||
persist()
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, eventResponse, err := signer.HandleNostrConnectURI(ctx, uri)
|
|
||||||
if err != nil {
|
|
||||||
log("* failed to handle: %s\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for event := range sys.Pool.SubscribeMany(ctx, relays, nostr.Filter{
|
|
||||||
Kinds: []nostr.Kind{nostr.KindNostrConnect},
|
|
||||||
Tags: nostr.TagMap{"p": []string{pubkey.Hex()}},
|
|
||||||
Since: nostr.Now(),
|
|
||||||
LimitZero: true,
|
|
||||||
}, nostr.SubscriptionOptions{Label: "nak-bunker"}) {
|
|
||||||
events <- event
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
jresp, _ := json.MarshalIndent(resp, "", " ")
|
|
||||||
log("~ responding with %s\n", string(jresp))
|
|
||||||
for res := range sys.Pool.PublishMany(ctx, relays, eventResponse) {
|
|
||||||
if res.Error == nil {
|
|
||||||
log("* sent through %s\n", res.Relay.URL)
|
|
||||||
} else {
|
|
||||||
log("* failed to send through %s: %s\n", res.RelayURL, res.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// just a gimmick
|
// just a gimmick
|
||||||
var cancelPreviousBunkerInfoPrint context.CancelFunc
|
var cancelPreviousBunkerInfoPrint context.CancelFunc
|
||||||
_, cancel := context.WithCancel(ctx)
|
_, cancel := context.WithCancel(ctx)
|
||||||
cancelPreviousBunkerInfoPrint = cancel
|
cancelPreviousBunkerInfoPrint = cancel
|
||||||
|
|
||||||
signer.AuthorizeRequest = func(harmless bool, from nostr.PubKey, secret string) bool {
|
signer.AuthorizeRequest = func(harmless bool, from nostr.PubKey, secret string) bool {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
if slices.ContainsFunc(config.Clients, func(b BunkerConfigClient) bool { return b.PubKey == from }) {
|
if slices.ContainsFunc(config.Clients, func(b BunkerConfigClient) bool { return b.PubKey == from }) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -441,10 +394,11 @@ var bunker = &cli.Command{
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for ie := range events {
|
handleBunkerRequest := func(ie nostr.RelayEvent) {
|
||||||
|
mu.Lock()
|
||||||
cancelPreviousBunkerInfoPrint() // this prevents us from printing a million bunker info blocks
|
cancelPreviousBunkerInfoPrint() // this prevents us from printing a million bunker info blocks
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
go func() {
|
|
||||||
// handle the NIP-46 request event
|
// handle the NIP-46 request event
|
||||||
from := ie.Event.PubKey
|
from := ie.Event.PubKey
|
||||||
req, resp, eventResponse, err := signer.HandleRequest(ctx, ie.Event)
|
req, resp, eventResponse, err := signer.HandleRequest(ctx, ie.Event)
|
||||||
@@ -465,12 +419,14 @@ var bunker = &cli.Command{
|
|||||||
// use custom relays if they are defined for this client
|
// use custom relays if they are defined for this client
|
||||||
// (normally if the initial connection came from a nostrconnect:// URL)
|
// (normally if the initial connection came from a nostrconnect:// URL)
|
||||||
relays := config.Relays
|
relays := config.Relays
|
||||||
|
mu.Lock()
|
||||||
for _, c := range config.Clients {
|
for _, c := range config.Clients {
|
||||||
if c.PubKey == from && len(c.CustomRelays) > 0 {
|
if c.PubKey == from && len(c.CustomRelays) > 0 {
|
||||||
relays = c.CustomRelays
|
relays = c.CustomRelays
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
for res := range sys.Pool.PublishMany(ctx, relays, eventResponse) {
|
for res := range sys.Pool.PublishMany(ctx, relays, eventResponse) {
|
||||||
if res.Error == nil {
|
if res.Error == nil {
|
||||||
@@ -484,7 +440,9 @@ var bunker = &cli.Command{
|
|||||||
go func() {
|
go func() {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
mu.Lock()
|
||||||
cancelPreviousBunkerInfoPrint = cancel
|
cancelPreviousBunkerInfoPrint = cancel
|
||||||
|
mu.Unlock()
|
||||||
// the idea is that we will print the bunker URL again so it is easier to copy-paste by users
|
// the idea is that we will print the bunker URL again so it is easier to copy-paste by users
|
||||||
// but we will only do if the bunker is inactive for more than 5 minutes
|
// but we will only do if the bunker is inactive for more than 5 minutes
|
||||||
select {
|
select {
|
||||||
@@ -494,7 +452,72 @@ var bunker = &cli.Command{
|
|||||||
printBunkerInfo()
|
printBunkerInfo()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unix socket nostrconnect:// handling
|
||||||
|
go func() {
|
||||||
|
for uri := range onSocketConnect(ctx, c) {
|
||||||
|
clientPublicKey, err := nostr.PubKeyFromHex(uri.Host)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log("- got nostrconnect:// request from '%s': %s\n", color.New(color.Bold, color.FgBlue).Sprint(clientPublicKey.Hex()), uri.String())
|
||||||
|
|
||||||
|
relays := uri.Query()["relay"]
|
||||||
|
|
||||||
|
// pre-authorize this client since the user has explicitly added it
|
||||||
|
mu.Lock()
|
||||||
|
if !slices.ContainsFunc(config.Clients, func(c BunkerConfigClient) bool {
|
||||||
|
return c.PubKey == clientPublicKey
|
||||||
|
}) {
|
||||||
|
config.Clients = append(config.Clients, BunkerConfigClient{
|
||||||
|
PubKey: clientPublicKey,
|
||||||
|
Name: uri.Query().Get("name"),
|
||||||
|
URL: uri.Query().Get("url"),
|
||||||
|
Icon: uri.Query().Get("icon"),
|
||||||
|
CustomRelays: relays,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if persist != nil {
|
||||||
|
persist()
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
resp, eventResponse, err := signer.HandleNostrConnectURI(ctx, uri)
|
||||||
|
if err != nil {
|
||||||
|
log("* failed to handle: %s\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for event := range sys.Pool.SubscribeMany(ctx, relays, nostr.Filter{
|
||||||
|
Kinds: []nostr.Kind{nostr.KindNostrConnect},
|
||||||
|
Tags: nostr.TagMap{"p": []string{pubkey.Hex()}},
|
||||||
|
Since: nostr.Now(),
|
||||||
|
LimitZero: true,
|
||||||
|
}, nostr.SubscriptionOptions{Label: "nak-bunker"}) {
|
||||||
|
// handle directly instead of forwarding into the main events
|
||||||
|
// channel, which is owned (and eventually closed) by the pool
|
||||||
|
go handleBunkerRequest(event)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 25)
|
||||||
|
jresp, _ := json.MarshalIndent(resp, "", " ")
|
||||||
|
log("~ responding with %s\n", string(jresp))
|
||||||
|
for res := range sys.Pool.PublishMany(ctx, relays, eventResponse) {
|
||||||
|
if res.Error == nil {
|
||||||
|
log("* sent through %s\n", res.Relay.URL)
|
||||||
|
} else {
|
||||||
|
log("* failed to send through %s: %s\n", res.RelayURL, res.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for ie := range events {
|
||||||
|
go handleBunkerRequest(ie)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user