ws

package
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 1, 2025 License: Unlicense Imports: 45 Imported by: 0

Documentation

Overview

Package ws provides both relay and client websocket implementations, including a pool for fanning out to multiple relays and managing subscriptions.

Package ws implements nostr websockets with their authentication state.

Index

Constants

View Source
const MaxLocks = 50

MaxLocks is the maximum number of sync.Mutex locks used in a pool todo: is this too few?

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	URL string

	RequestHeader http.Header // e.g. for origin header

	Connection *Connection

	Subscriptions *xsync.MapOf[string, *Subscription]

	ConnectionError error

	AssumeValid bool // this will skip verifying signatures for events received from this relay
	// contains filtered or unexported fields
}

func NewRelay

func NewRelay(c context.T, url string, opts ...RelayOption) *Client

NewRelay returns a new relay. The relay connection will be closed when the context is cancelled.

func RelayConnect

func RelayConnect(ctx context.T, url string, opts ...RelayOption) (
	*Client, error,
)

RelayConnect returns a relay object connected to url. Once successfully connected, cancelling ctx has no effect. To close the connection, call r.Close().

func (*Client) Auth

func (r *Client) Auth(c context.T, sign signer.I) error

Auth sends an "AUTH" command client->relay as in NIP-42 and waits for an OK response.

func (*Client) Close

func (r *Client) Close() error

Close shuts down a websocket client connection.

func (*Client) Connect

func (r *Client) Connect(c context.T) error

Connect tries to establish a websocket connection to r.URL. If the context expires before the connection is complete, an error is returned. Once successfully connected, context expiration has no effect: call r.Close to close the connection.

The underlying relay connection will use a background context. If you want to pass a custom context to the underlying relay connection, use NewRelay() and then Client.Connect().

func (*Client) ConnectWithTLS

func (r *Client) ConnectWithTLS(ctx context.T, tlsConfig *tls.Config) error

ConnectWithTLS tries to establish a secured websocket connection to r.URL using customized tls.Config (CA's, etc.).

func (*Client) Context

func (r *Client) Context() context.T

Context retrieves the context that is associated with this relay connection.

func (*Client) IsConnected

func (r *Client) IsConnected() bool

IsConnected returns true if the connection to this relay seems to be active.

func (*Client) PrepareSubscription

func (r *Client) PrepareSubscription(
	c context.T, ff *filters.T,
	opts ...SubscriptionOption,
) *Subscription

PrepareSubscription creates a subscription, but doesn't fire it.

Remember to cancel subscriptions, either by calling `.Unsub()` on them or ensuring their `context.Context` will be canceled at some point. Failure to do that will result in a huge number of halted goroutines being created.

func (*Client) Publish

func (r *Client) Publish(c context.T, ev *event.E) error

Publish sends an "EVENT" command to the relay r as in NIP-01 and waits for an OK response.

func (*Client) QuerySync

func (r *Client) QuerySync(
	ctx context.T, f *filter.F,
	opts ...SubscriptionOption,
) ([]*event.E, error)

QuerySync is only used in tests. The relay query method is synchronous now anyway (it ensures sort order is respected).

func (*Client) String

func (r *Client) String() string

String just returns the relay URL.

func (*Client) Subscribe

func (r *Client) Subscribe(
	c context.T, ff *filters.T,
	opts ...SubscriptionOption,
) (*Subscription, error)

Subscribe sends a "REQ" command to the relay r as in NIP-01. Events are returned through the channel sub.Events. The subscription is closed when context ctx is cancelled ("CLOSE" in NIP-01).

Remember to cancel subscriptions, either by calling `.Unsub()` on them or ensuring their `context.Context` will be canceled at some point. Failure to do that will result in a huge number of halted goroutines being created.

func (*Client) Write

func (r *Client) Write(msg []byte) <-chan error

Write queues a message to be sent to the relay.

type Connection

type Connection struct {
	// contains filtered or unexported fields
}

Connection is an outbound client -> relay connection.

func NewConnection

func NewConnection(
	c context.T, url string, requestHeader http.Header,
	tlsConfig *tls.Config,
) (connection *Connection, errResult error)

NewConnection creates a new Connection.

func (*Connection) Close

func (cn *Connection) Close() (err error)

Close the Connection.

func (*Connection) ReadMessage

func (cn *Connection) ReadMessage(c context.T, buf io.Writer) (err error)

ReadMessage picks up the next incoming message on a Connection.

func (*Connection) WriteMessage

func (cn *Connection) WriteMessage(c context.T, data []byte) (err error)

WriteMessage dispatches a message through the Connection.

type DirectedFilters

type DirectedFilters struct {
	Filters *filters.T
	Client  string
}

type EventMessage

type EventMessage struct {
	Event event.E
	Relay string
}

EventMessage is an event, with the associated relay URL attached.

type IncomingEvent

type IncomingEvent struct {
	Event  *event.E
	Client *Client
}

func (IncomingEvent) String

func (ie IncomingEvent) String() string

type Listener

type Listener struct {
	Conn    *websocket.Conn
	Request *http.Request
	// contains filtered or unexported fields
}

Listener is a websocket implementation for a relay listener.

func NewListener

func NewListener(
	conn *websocket.Conn, req *http.Request, authRequired bool,
) (ws *Listener)

NewListener creates a new Listener for listening for inbound connections for a relay.

func (*Listener) AuthRequested

func (ws *Listener) AuthRequested() (read bool)

AuthRequested returns whether the Listener has asked for auth from the client.

func (*Listener) AuthedPubkey

func (ws *Listener) AuthedPubkey() []byte

func (*Listener) Challenge

func (ws *Listener) Challenge() []byte

func (*Listener) Close

func (ws *Listener) Close() (err error)

Close the Listener connection from the Listener side.

func (*Listener) GetPendingEvent added in v0.2.18

func (ws *Listener) GetPendingEvent() (ev *event.E)

func (*Listener) IsAuthed

func (ws *Listener) IsAuthed() bool

func (*Listener) RealRemote

func (ws *Listener) RealRemote() string

RealRemote returns the stored remote address of the client.

func (*Listener) Req

func (ws *Listener) Req() *http.Request

Req returns the http.Request associated with the client connection to the Listener.

func (*Listener) RequestAuth

func (ws *Listener) RequestAuth()

RequestAuth stores when auth has been required from a client.

func (*Listener) SetAuthedPubkey

func (ws *Listener) SetAuthedPubkey(b []byte)

func (*Listener) SetChallenge

func (ws *Listener) SetChallenge(b []byte)

func (*Listener) SetPendingEvent added in v0.2.18

func (ws *Listener) SetPendingEvent(ev *event.E)

func (*Listener) Write

func (ws *Listener) Write(p []byte) (n int, err error)

Write a message to send to a client.

func (*Listener) WriteJSON

func (ws *Listener) WriteJSON(any interface{}) error

WriteJSON encodes whatever into JSON and sends it to the client.

func (*Listener) WriteMessage

func (ws *Listener) WriteMessage(t int, b []byte) error

WriteMessage is a wrapper around the websocket WriteMessage, which includes a websocket message type identifier.

type Pool

type Pool struct {
	Relays  *xsync.MapOf[string, *Client]
	Context context.T

	// custom things not often used
	SignatureChecker func(*event.E) bool
	// contains filtered or unexported fields
}

func NewPool

func NewPool(c context.T, opts ...PoolOption) *Pool

func (*Pool) BatchedSubMany

func (pool *Pool) BatchedSubMany(
	c context.T, dfs []DirectedFilters,
) chan IncomingEvent

BatchedSubMany fires subscriptions only to specific relays but batches them when they are the same.

func (*Pool) BatchedSubManyEose

func (pool *Pool) BatchedSubManyEose(
	c context.T, dfs []DirectedFilters,
) chan IncomingEvent

BatchedSubManyEose is like BatchedSubMany, but ends upon receiving EOSE from all relays.

func (*Pool) EnsureRelay

func (pool *Pool) EnsureRelay(url string) (*Client, error)

EnsureRelay connects a pool to a relay or fails.

func (*Pool) QuerySingle

func (pool *Pool) QuerySingle(
	c context.T, urls []string, f *filter.F,
) *IncomingEvent

QuerySingle returns the first event returned by the first relay, cancels everything else.

func (*Pool) SubMany

func (pool *Pool) SubMany(
	c context.T, urls []string, ff *filters.T,
) chan IncomingEvent

SubMany opens a subscription with the given filters to multiple relays the subscriptions only end when the context is canceled

func (*Pool) SubManyEose

func (pool *Pool) SubManyEose(
	c context.T, urls []string, ff *filters.T,
) chan IncomingEvent

SubManyEose is like SubMany, but it stops subscriptions and closes the channel when gets a EOSE

func (*Pool) SubManyEoseNonUnique

func (pool *Pool) SubManyEoseNonUnique(
	c context.T, urls []string,
	ff *filters.T,
) chan IncomingEvent

SubManyEoseNonUnique is like SubManyEose, but returns duplicate events if they come from different relays

func (*Pool) SubManyNonUnique

func (pool *Pool) SubManyNonUnique(
	c context.T, urls []string,
	ff *filters.T,
) chan IncomingEvent

SubManyNonUnique is like SubMany, but returns duplicate events if they come from different relays

type PoolOption

type PoolOption interface {
	ApplyPoolOption(*Pool)
}

type RelayOption

type RelayOption interface {
	ApplyRelayOption(*Client)
}

RelayOption is the type of the argument passed for that.

type Subscription

type Subscription struct {
	Relay   *Client
	Filters *filters.T

	// The Events channel emits all EVENTs that come in a Subscription will be
	// closed when the subscription ends
	Events event.C

	// The EndOfStoredEvents channel is closed when an EOSE comes for that
	// subscription
	EndOfStoredEvents chan struct{}

	// The ClosedReason channel emits the reason when a CLOSED message is
	// received
	ClosedReason chan string

	// Context will be .Done() when the subscription ends
	Context context.T
	// contains filtered or unexported fields
}

Subscription is a client interface for a subscription (what REQ turns into after EOSE).

func (*Subscription) Close

func (sub *Subscription) Close()

Close just sends a CLOSE message. You probably want Unsub() instead.

func (*Subscription) Fire

func (sub *Subscription) Fire() (err error)

Fire sends the "REQ" command to the relay.

func (*Subscription) GetID

func (sub *Subscription) GetID() (id *subscription.Id)

GetID return the Nostr subscription ID as given to the Client it is a concatenation of the label and a serial number.

func (*Subscription) Sub

func (sub *Subscription) Sub(_ context.T, ff *filters.T)

Sub sets sub.Filters and then calls sub.Fire(ctx). The subscription will be closed if the context expires.

func (*Subscription) Unsub

func (sub *Subscription) Unsub()

Unsub closes the subscription, sending "CLOSE" to relay as in NIP-01. Unsub() also closes the channel sub.Events and makes a new one.

type SubscriptionOption

type SubscriptionOption interface {
	IsSubscriptionOption()
}

SubscriptionOption is the type of the argument passed for that. Some examples are WithLabel.

type WithAuthHandler

type WithAuthHandler func() signer.I

WithAuthHandler must be a function that signs the auth event when called. it will be called whenever any relay in the pool returns a `CLOSED` message with the "auth-required:" prefix, only once for each relay

func (WithAuthHandler) ApplyPoolOption

func (h WithAuthHandler) ApplyPoolOption(pool *Pool)

type WithEventMiddleware

type WithEventMiddleware func(IncomingEvent)

WithEventMiddleware is a function that will be called with all events received. more than one can be passed at a time.

func (WithEventMiddleware) ApplyPoolOption

func (h WithEventMiddleware) ApplyPoolOption(pool *Pool)

type WithLabel

type WithLabel string

WithLabel puts a label on the subscription (it is prepended to the automatic id) that is sent to relays.

func (WithLabel) IsSubscriptionOption

func (_ WithLabel) IsSubscriptionOption()

type WithNoticeHandler

type WithNoticeHandler func(notice []byte)

WithNoticeHandler just takes notices and is expected to do something with them. when not given, defaults to logging the notices.

func (WithNoticeHandler) ApplyRelayOption

func (nh WithNoticeHandler) ApplyRelayOption(r *Client)

type WithSignatureChecker

type WithSignatureChecker func(*event.E) bool

WithSignatureChecker must be a function that checks the signature of an event and returns true or false.

func (WithSignatureChecker) ApplyRelayOption

func (sc WithSignatureChecker) ApplyRelayOption(r *Client)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL