connection.go raw
1 package nostr
2
3 import (
4 "context"
5 "crypto/tls"
6 "errors"
7 "fmt"
8 "io"
9 "net/http"
10 "time"
11
12 ws "github.com/coder/websocket"
13 )
14
15 // Connection represents a websocket connection to a Nostr relay.
16 type Connection struct {
17 conn *ws.Conn
18 }
19
20 // NewConnection creates a new websocket connection to a Nostr relay.
21 func NewConnection(ctx context.Context, url string, requestHeader http.Header, tlsConfig *tls.Config) (*Connection, error) {
22 c, _, err := ws.Dial(ctx, url, getConnectionOptions(requestHeader, tlsConfig))
23 if err != nil {
24 return nil, err
25 }
26
27 c.SetReadLimit(2 << 24) // 33MB
28
29 return &Connection{
30 conn: c,
31 }, nil
32 }
33
34 // WriteMessage writes arbitrary bytes to the websocket connection.
35 func (c *Connection) WriteMessage(ctx context.Context, data []byte) error {
36 if err := c.conn.Write(ctx, ws.MessageText, data); err != nil {
37 return fmt.Errorf("failed to write message: %w", err)
38 }
39
40 return nil
41 }
42
43 // ReadMessage reads arbitrary bytes from the websocket connection into the provided buffer.
44 func (c *Connection) ReadMessage(ctx context.Context, buf io.Writer) error {
45 _, reader, err := c.conn.Reader(ctx)
46 if err != nil {
47 return fmt.Errorf("failed to get reader: %w", err)
48 }
49 if _, err := io.Copy(buf, reader); err != nil {
50 return fmt.Errorf("failed to read message: %w", err)
51 }
52 return nil
53 }
54
55 // Close closes the websocket connection.
56 func (c *Connection) Close() error {
57 return c.conn.Close(ws.StatusNormalClosure, "")
58 }
59
60 // Ping sends a ping message to the websocket connection.
61 func (c *Connection) Ping(ctx context.Context) error {
62 ctx, cancel := context.WithTimeoutCause(ctx, time.Millisecond*800, errors.New("ping took too long"))
63 defer cancel()
64 return c.conn.Ping(ctx)
65 }
66