diff --git a/bech32encoding/nip19.go b/bech32encoding/nip19.go index ea3d1eb..6ae5058 100644 --- a/bech32encoding/nip19.go +++ b/bech32encoding/nip19.go @@ -32,7 +32,9 @@ var ( // include relay hints to find the event, and the author's npub. NeventHRP = []byte("nevent") - // NentityHRP is the Human Readable Prefix (HRP) for a nostr is a generic nostr entity, which may include relay hints to find the event, and the author's npub. + // NentityHRP is the Human Readable Prefix (HRP) for a nostr is a generic nostr + // entity, which may include relay hints to find the event, and the author's + // npub. NentityHRP = []byte("naddr") ) diff --git a/envelopes/authenvelope/authenvelope.go b/envelopes/authenvelope/authenvelope.go index ede440d..3fc00cb 100644 --- a/envelopes/authenvelope/authenvelope.go +++ b/envelopes/authenvelope/authenvelope.go @@ -11,6 +11,7 @@ import ( "realy.lol/text" ) +// L is the label associated with this type of codec.Envelope. const L = "AUTH" // Challenge is the relay-sent message containing a relay-chosen random string @@ -41,8 +42,8 @@ func (en *Challenge) Write(w io.Writer) (err error) { return } -// Marshal a Challenge to minified JSON. Note that this ensures correct string -// escaping on the challenge field. +// Marshal a Challenge to minified JSON, appending to a provided destination +// slice. Note that this ensures correct string escaping on the challenge field. func (en *Challenge) Marshal(dst []byte) (b []byte) { b = dst var err error @@ -58,8 +59,9 @@ func (en *Challenge) Marshal(dst []byte) (b []byte) { return } -// Unmarshal a Challenge from minified JSON. Note that this ensures the -// challenge string was correctly escaped by NIP-01 escaping rules. +// Unmarshal a Challenge from minified JSON, returning the remainder after the +// end of the envelope. Note that this ensures the challenge string was +// correctly escaped by NIP-01 escaping rules. func (en *Challenge) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Challenge, r, err = text.UnmarshalQuoted(r); chk.E(err) { @@ -109,8 +111,8 @@ func (en *Response) Write(w io.Writer) (err error) { return } -// Marshal a Response to minified JSON. Note that this ensures correct string -// escaping on the challenge field. +// Marshal a Response to minified JSON, appending to a provided destination +// slice. Note that this ensures correct string escaping on the challenge field. func (en *Response) Marshal(dst []byte) (b []byte) { var err error if en == nil { @@ -127,8 +129,9 @@ func (en *Response) Marshal(dst []byte) (b []byte) { return } -// Unmarshal a Response from minified JSON. Note that this ensures the -// challenge string was correctly escaped by NIP-01 escaping rules. +// Unmarshal a Response from minified JSON, returning the remainder after the en +// of the envelope. Note that this ensures the challenge string was correctly +// escaped by NIP-01 escaping rules. func (en *Response) Unmarshal(b []byte) (r []byte, err error) { r = b // literally just unmarshal the event diff --git a/envelopes/closedenvelope/closedenvelope.go b/envelopes/closedenvelope/closedenvelope.go index 98aee36..66f9fcf 100644 --- a/envelopes/closedenvelope/closedenvelope.go +++ b/envelopes/closedenvelope/closedenvelope.go @@ -12,8 +12,12 @@ import ( "realy.lol/text" ) +// L is the label associated with this type of codec.Envelope. const L = "CLOSED" +// T is a CLOSED envelope, which is a signal that a subscription has been +// stopped on the relay side for some reason. Primarily this is for auth and can +// be for other things like rate limiting. type T struct { Subscription *subscription.Id Reason []byte @@ -21,11 +25,21 @@ type T struct { var _ codec.Envelope = (*T)(nil) -func New() *T { return &T{Subscription: subscription.NewStd()} } -func NewFrom(id *subscription.Id, msg []byte) *T { return &T{Subscription: id, Reason: msg} } -func (en *T) Label() string { return L } -func (en *T) ReasonString() string { return string(en.Reason) } +// New creates an empty new T. +func New() *T { + return &T{Subscription: subscription.NewStd()} +} +// NewFrom creates a new T populated with subscription Id and Reason. +func NewFrom(id *subscription.Id, msg []byte) *T { return &T{Subscription: id, Reason: msg} } + +// Label returns the label of a CLOSED envelope. +func (en *T) Label() string { return L } + +// ReasonString returns the Reason in the form of a string. +func (en *T) ReasonString() string { return string(en.Reason) } + +// Write the CLOSED T to a provided io.Writer. func (en *T) Write(w io.Writer) (err error) { var b []byte b = en.Marshal(b) @@ -33,6 +47,9 @@ func (en *T) Write(w io.Writer) (err error) { return } +// Marshal a CLOSED T envelope in minified JSON, appending to a provided +// destination slice. Note that this ensures correct string escaping on the +// Reason field. func (en *T) Marshal(dst []byte) (b []byte) { b = dst b = envelopes.Marshal(b, L, @@ -48,6 +65,9 @@ func (en *T) Marshal(dst []byte) (b []byte) { return } +// Unmarshal a CLOSED T from minified JSON, returning the remainder after the end +// of the envelope. Note that this ensures the Reason string is correctly +// unescaped by NIP-01 escaping rules. func (en *T) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Subscription, err = subscription.NewId([]byte{0}); chk.E(err) { @@ -65,6 +85,7 @@ func (en *T) Unmarshal(b []byte) (r []byte, err error) { return } +// Parse reads a CLOSED T from minified JSON into a newly allocated T. func Parse(b []byte) (t *T, rem []byte, err error) { t = New() if rem, err = t.Unmarshal(b); chk.E(err) { diff --git a/envelopes/closeenvelope/closeenvelope.go b/envelopes/closeenvelope/closeenvelope.go index e02984c..7fd9bd5 100644 --- a/envelopes/closeenvelope/closeenvelope.go +++ b/envelopes/closeenvelope/closeenvelope.go @@ -10,22 +10,34 @@ import ( "realy.lol/subscription" ) +// L is the label associated with this type of codec.Envelope. const L = "CLOSE" +// T is a CLOSE envelope, which is a signal from client to relay to stop a +// specified subscription. type T struct { ID *subscription.Id } var _ codec.Envelope = (*T)(nil) -func New() *T { return &T{ID: subscription.NewStd()} } +// New creates an empty new standard formatted T. +func New() *T { return &T{ID: subscription.NewStd()} } + +// NewFrom creates a new T populated with subscription Id. func NewFrom(id *subscription.Id) *T { return &T{ID: id} } -func (en *T) Label() string { return L } + +// Label returns the label of a CLOSE envelope. +func (en *T) Label() string { return L } + +// Write the CLOSE T to a provided io.Writer. func (en *T) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } +// Marshal a CLOSE T envelope in minified JSON, appending to a provided +// destination slice. func (en *T) Marshal(dst []byte) (b []byte) { b = dst b = envelopes.Marshal(b, L, @@ -37,6 +49,8 @@ func (en *T) Marshal(dst []byte) (b []byte) { return } +// Unmarshal a CLOSE T from minified JSON, returning the remainder after the end +// of the envelope. func (en *T) Unmarshal(b []byte) (r []byte, err error) { r = b if en.ID, err = subscription.NewId([]byte{0}); chk.E(err) { @@ -51,6 +65,7 @@ func (en *T) Unmarshal(b []byte) (r []byte, err error) { return } +// Parse reads a CLOSE T in minified JSON into a newly allocated T. func Parse(b []byte) (t *T, rem []byte, err error) { t = New() if rem, err = t.Unmarshal(b); chk.E(err) { diff --git a/envelopes/countenvelope/countenvelope.go b/envelopes/countenvelope/countenvelope.go index abf39e5..10bacce 100644 --- a/envelopes/countenvelope/countenvelope.go +++ b/envelopes/countenvelope/countenvelope.go @@ -14,8 +14,13 @@ import ( "realy.lol/text" ) +// L is the label associated with this type of codec.Envelope. const L = "COUNT" +// Request is a COUNT envelope sent by a client to request a count of results. +// This is a stupid idea because it costs as much processing as fetching the +// events, but doesn't provide the means to actually get them (the HTTP API +// /filter does this by returning the actual event Ids). type Request struct { Subscription *subscription.Id Filters *filters.T @@ -23,15 +28,23 @@ type Request struct { var _ codec.Envelope = (*Request)(nil) +// New creates a new Request with a standard style subscription.Id and empty filter. func New() *Request { return &Request{Subscription: subscription.NewStd(), Filters: filters.New()} } + +// NewRequest creates a new Request with a provided subscription.Id and +// filter.T. func NewRequest(id *subscription.Id, filters *filters.T) *Request { return &Request{Subscription: id, Filters: filters} } + +// Label returns the label of a CLOSED envelope. func (en *Request) Label() string { return L } + +// Write the Request to a provided io.Writer. func (en *Request) Write(w io.Writer) (err error) { var b []byte b = en.Marshal(b) @@ -39,6 +52,8 @@ func (en *Request) Write(w io.Writer) (err error) { return } +// Marshal a Request appended to the provided destination slice as minified +// JSON. func (en *Request) Marshal(dst []byte) (b []byte) { var err error b = dst @@ -54,6 +69,8 @@ func (en *Request) Marshal(dst []byte) (b []byte) { return } +// Unmarshal a Request from minified JSON, returning the remainder after the end +// of the envelope. func (en *Request) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Subscription, err = subscription.NewId([]byte{0}); chk.E(err) { @@ -72,6 +89,7 @@ func (en *Request) Unmarshal(b []byte) (r []byte, err error) { return } +// ParseRequest reads a Request in minified JSON into a newly allocated Request. func ParseRequest(b []byte) (t *Request, rem []byte, err error) { t = New() if rem, err = t.Unmarshal(b); chk.E(err) { @@ -80,6 +98,8 @@ func ParseRequest(b []byte) (t *Request, rem []byte, err error) { return } +// Response is a COUNT Response returning a count and approximate flag +// associated with the REQ subscription.Id. type Response struct { ID *subscription.Id Count int @@ -88,8 +108,16 @@ type Response struct { var _ codec.Envelope = (*Response)(nil) +// NewResponse creates a new empty COUNT Response with a standard formatted +// subscription.Id. func NewResponse() *Response { return &Response{ID: subscription.NewStd()} } -func NewResponseFrom[V string | []byte](s V, cnt int, approx ...bool) (res *Response, err error) { + +// NewResponseFrom creates a new response with provided string for the +// subscription.Id, a count and optional variadic approximate flag, which is +// otherwise false and does not get rendered into the JSON. +func NewResponseFrom[V string | []byte](s V, cnt int, + approx ...bool) (res *Response, err error) { + var a bool if len(approx) > 0 { a = approx[0] @@ -100,12 +128,18 @@ func NewResponseFrom[V string | []byte](s V, cnt int, approx ...bool) (res *Resp } return &Response{subscription.MustNew(s), cnt, a}, nil } + +// Label returns the COUNT label associated with a Response. func (en *Response) Label() string { return L } + +// Write a Response to a provided io.Writer as minified JSON. func (en *Response) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } +// Marshal a COUNT Response envelope in minified JSON, appending to a provided +// destination slice. func (en *Response) Marshal(dst []byte) (b []byte) { var err error b = dst @@ -126,6 +160,8 @@ func (en *Response) Marshal(dst []byte) (b []byte) { return } +// Unmarshal a COUNT Response from minified JSON, returning the remainder after +// the end of the envelope. func (en *Response) Unmarshal(b []byte) (r []byte, err error) { r = b var inID, inCount bool @@ -185,6 +221,8 @@ func (en *Response) Unmarshal(b []byte) (r []byte, err error) { return } +// Parse reads a Count Response in minified JSON into a newly allocated +// Response. func Parse(b []byte) (t *Response, rem []byte, err error) { t = NewResponse() if rem, err = t.Unmarshal(b); chk.E(err) { diff --git a/envelopes/eoseenvelope/eoseenvelope.go b/envelopes/eoseenvelope/eoseenvelope.go index 8e2868d..1c4682a 100644 --- a/envelopes/eoseenvelope/eoseenvelope.go +++ b/envelopes/eoseenvelope/eoseenvelope.go @@ -12,23 +12,38 @@ import ( "realy.lol/subscription" ) +// L is the label associated with this type of codec.Envelope. const L = "EOSE" +// T is an EOSE envelope, that signals the end of events that are stored and the +// beginning of a subscription. This is necessitated by the confusing +// multiplexing of websockets for multiple requests, and an ugly merging of two +// distinct API calls, filter and subscribe. type T struct { Subscription *subscription.Id } var _ codec.Envelope = (*T)(nil) -func New() *T { return &T{Subscription: subscription.NewStd()} } -func NewFrom(id *subscription.Id) *T { return &T{Subscription: id} } -func (en *T) Label() string { return L } +// New creates a new EOSE T with a standard form subscription.Id. +func New() *T { + return &T{Subscription: subscription.NewStd()} +} +// NewFrom creates a new EOSE T using a provided subscription.Id. +func NewFrom(id *subscription.Id) *T { return &T{Subscription: id} } + +// Label returns the label of a EOSE envelope. +func (en *T) Label() string { return L } + +// Write the EOSE T to a provided io.Writer. func (en *T) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } +// Marshal a EOSE T envelope in minified JSON, appending to a provided +// destination slice. func (en *T) Marshal(dst []byte) (b []byte) { var err error b = dst @@ -43,6 +58,8 @@ func (en *T) Marshal(dst []byte) (b []byte) { return } +// Unmarshal a EOSE T from minified JSON, returning the remainder after the +// end of the envelope. func (en *T) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Subscription, err = subscription.NewId([]byte{0}); chk.E(err) { @@ -57,6 +74,7 @@ func (en *T) Unmarshal(b []byte) (r []byte, err error) { return } +// Parse reads a EOSE T in minified JSON into a newly allocated T. func Parse(b []byte) (t *T, rem []byte, err error) { t = New() if rem, err = t.Unmarshal(b); chk.E(err) { diff --git a/envelopes/eventenvelope/eventenvelope.go b/envelopes/eventenvelope/eventenvelope.go index b28889a..8972ec2 100644 --- a/envelopes/eventenvelope/eventenvelope.go +++ b/envelopes/eventenvelope/eventenvelope.go @@ -11,6 +11,7 @@ import ( "realy.lol/subscription" ) +// L is the label associated with this type of codec.Envelope. const L = "EVENT" // Submission is a request from a client for a realy to store an event. @@ -20,15 +21,23 @@ type Submission struct { var _ codec.Envelope = (*Submission)(nil) -func NewSubmission() *Submission { return &Submission{T: &event.T{}} } -func NewSubmissionWith(ev *event.T) *Submission { return &Submission{T: ev} } -func (en *Submission) Label() string { return L } +// NewSubmission creates an empty new Submission. +func NewSubmission() *Submission { return &Submission{T: &event.T{}} } +// NewSubmissionWith creates a new Submission with a provided event.T. +func NewSubmissionWith(ev *event.T) *Submission { return &Submission{T: ev} } + +// Label returns the label of a event Submission envelope. +func (en *Submission) Label() string { return L } + +// Write the Submission to a provided io.Writer. func (en *Submission) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } +// Marshal a event Submission T envelope in minified JSON, appending to a provided +// destination slice. func (en *Submission) Marshal(dst []byte) (b []byte) { var err error b = dst @@ -42,6 +51,8 @@ func (en *Submission) Marshal(dst []byte) (b []byte) { return } +// Unmarshal an event Submission from minified JSON, returning the remainder +// after the end of the envelope. func (en *Submission) Unmarshal(b []byte) (r []byte, err error) { r = b en.T = event.New() @@ -55,6 +66,8 @@ func (en *Submission) Unmarshal(b []byte) (r []byte, err error) { return } +// ParseSubmission reads an event Submission from minified JSON into a newly +// allocated Submission. func ParseSubmission(b []byte) (t *Submission, rem []byte, err error) { t = NewSubmission() if rem, err = t.Unmarshal(b); chk.E(err) { @@ -71,7 +84,11 @@ type Result struct { var _ codec.Envelope = (*Result)(nil) +// NewResult creates a new empty event Result. func NewResult() *Result { return &Result{} } + +// NewResultWith creates a new Result with a provided subscription.Id string and +// event.T. func NewResultWith[V string | []byte](s V, ev *event.T) (res *Result, err error) { if len(s) < 0 || len(s) > 64 { err = errorf.E("subscription id must be length > 0 and <= 64") @@ -79,13 +96,18 @@ func NewResultWith[V string | []byte](s V, ev *event.T) (res *Result, err error) } return &Result{subscription.MustNew(s), ev}, nil } + +// Label returns the label of a event Result envelope. func (en *Result) Label() string { return L } +// Write the event Result T to a provided io.Writer. func (en *Result) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } +// Marshal a event Result envelope in minified JSON, appending to a provided +// destination slice. func (en *Result) Marshal(dst []byte) (b []byte) { var err error b = dst @@ -101,6 +123,8 @@ func (en *Result) Marshal(dst []byte) (b []byte) { return } +// Unmarshal a event Result from minified JSON, returning the remainder after +// the end of the envelope. func (en *Result) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Subscription, err = subscription.NewId([]byte{0}); chk.E(err) { diff --git a/envelopes/identify.go b/envelopes/identify.go index 575a292..435aa81 100644 --- a/envelopes/identify.go +++ b/envelopes/identify.go @@ -1,5 +1,10 @@ package envelopes +// Identify handles determining what kind of codec.Envelope is, by the Label, +// the first step in identifying the structure of the message. This first step +// is not sufficient because the same labels are used on several codec.Envelope +// types in the nostr specification. The rest of the context is in whether this +// is a client or a relay receiving it. func Identify(b []byte) (t string, rem []byte, err error) { var openBrackets, openQuotes, afterQuotes bool var label []byte diff --git a/envelopes/messages/messages.go b/envelopes/messages/messages.go index 84a0092..4d87374 100644 --- a/envelopes/messages/messages.go +++ b/envelopes/messages/messages.go @@ -7,16 +7,36 @@ import ( ) const ( - Duplicate = "duplicate" - Pow = "pow" - Blocked = "blocked" + // Duplicate is a machine readable prefix for OK envelopes indicating that the + // submitted event is already in the relay,s event store. + Duplicate = "duplicate" + + // Pow is a machine readable prefix for OK envelopes indicating that the + // eventid.T lacks sufficient zeros at the front. + Pow = "pow" + + // Blocked is a machine readable prefix for OK envelopes indicating the event + // submission or REQ has been rejected. + Blocked = "blocked" + + // RateLimited is a machine readable prefix for CLOSED and OK envelopes + // indicating the relay is now slowing down processing of requests from the + // client. RateLimited = "rate-limited" - Invalid = "invalid" - Error = "error" + + // Invalid is a machine readable prefix for OK envelopes indicating + // that the submitted event or other request is not correctly formatted, and may + // mean a signature does not verify. + Invalid = "invalid" + + // Error is a machine readable prefix for CLOSED and OK envelopes indicating + // there was some kind of error in processing the request. + Error = "error" ) +// Examples are some examples of the use of the prefixes above with appropriate +// human-readable suffixes. var Examples = [][]byte{ - []byte(""), []byte("pow: difficulty 25>=24"), []byte("duplicate: already have this event"), []byte("blocked: you are banned from posting here"), @@ -28,6 +48,7 @@ var Examples = [][]byte{ []byte("error: could not connect to the database"), } +// RandomMessage generates a random message out of the above list of Examples. func RandomMessage() []byte { return Examples[frand.Intn(len(Examples)-1)] } diff --git a/envelopes/noticeenvelope/noticeenvelope.go b/envelopes/noticeenvelope/noticeenvelope.go index f0e535b..b9be72f 100644 --- a/envelopes/noticeenvelope/noticeenvelope.go +++ b/envelopes/noticeenvelope/noticeenvelope.go @@ -11,23 +11,36 @@ import ( "realy.lol/text" ) +// L is the label associated with this type of codec.Envelope. const L = "NOTICE" +// T is a NOTICE envelope, intended to convey information to the user about the +// state of the relay connection. This thing is rarely displayed on clients +// except sometimes in event logs. type T struct { Message []byte } var _ codec.Envelope = (*T)(nil) -func New() *T { return &T{} } -func NewFrom[V string | []byte](msg V) *T { return &T{Message: []byte(msg)} } -func (en *T) Label() string { return L } +// New creates a new empty NOTICE T. +func New() *T { return &T{} } +// NewFrom creates a new NOTICE T with a provided message. +func NewFrom[V string | []byte](msg V) *T { return &T{Message: []byte(msg)} } + +// Label returns the label of a NOTICE envelope. +func (en *T) Label() string { return L } + +// Write the NOTICE T to a provided io.Writer. func (en *T) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } +// Marshal a NOTICE T envelope in minified JSON, appending to a provided +// destination slice. Note that this ensures correct string escaping on the +// Reason field. func (en *T) Marshal(dst []byte) (b []byte) { var err error _ = err @@ -43,6 +56,9 @@ func (en *T) Marshal(dst []byte) (b []byte) { return } +// Unmarshal a NOTICE T from minified JSON, returning the remainder after the +// end of the envelope. Note that this ensures the Reason string is correctly +// unescaped by NIP-01 escaping rules. func (en *T) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Message, r, err = text.UnmarshalQuoted(r); chk.E(err) { @@ -54,6 +70,7 @@ func (en *T) Unmarshal(b []byte) (r []byte, err error) { return } +// Parse reads a NOTICE T in minified JSON into a newly allocated T. func Parse(b []byte) (t *T, rem []byte, err error) { t = New() if rem, err = t.Unmarshal(b); chk.E(err) { diff --git a/envelopes/okenvelope/okenvelope.go b/envelopes/okenvelope/okenvelope.go index 569e0b5..1fc93e3 100644 --- a/envelopes/okenvelope/okenvelope.go +++ b/envelopes/okenvelope/okenvelope.go @@ -13,10 +13,11 @@ import ( "realy.lol/text" ) -const ( - L = "OK" -) +// L is the label associated with this type of codec.Envelope. +const L = "OK" +// T is an OK envelope, used to signal acceptance or rejection, with a reason, +// to an eventenvelope.Submission. type T struct { EventID *eventid.T OK bool @@ -25,11 +26,15 @@ type T struct { var _ codec.Envelope = (*T)(nil) +// New creates a new empty OK T. func New() *T { return &T{} } -func NewFrom[V string | []byte](eid V, ok bool, msg ...[]byte) *T { + +// NewFrom creates a new OK T with a string for the subscription.Id and the +// Reason. +func NewFrom[V string | []byte](eid V, ok bool, msg ...V) *T { var m []byte if len(msg) > 0 { - m = msg[0] + m = []byte(msg[0]) } if len(eid) != sha256.Size { log.W.F("event ID unexpected length, expect %d got %d", @@ -37,14 +42,22 @@ func NewFrom[V string | []byte](eid V, ok bool, msg ...[]byte) *T { } return &T{EventID: eventid.NewWith(eid), OK: ok, Reason: m} } -func (en *T) Label() string { return L } + +// Label returns the label of an OK envelope. +func (en *T) Label() string { return L } + +// ReasonString returns the Reason in the form of a string. func (en *T) ReasonString() string { return string(en.Reason) } +// Write the OK T to a provided io.Writer. func (en *T) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } +// Marshal a CLOSED T envelope in minified JSON, appending to a provided +// destination slice. Note that this ensures correct string escaping on the +// subscription.Id and Reason fields. func (en *T) Marshal(dst []byte) (b []byte) { var err error _ = err @@ -66,6 +79,9 @@ func (en *T) Marshal(dst []byte) (b []byte) { return } +// Unmarshal a CLOSED T from minified JSON, returning the remainder after the +// end of the envelope. Note that this ensures the Reason and subscription.Id +// strings are correctly unescaped by NIP-01 escaping rules. func (en *T) Unmarshal(b []byte) (r []byte, err error) { r = b var idHex []byte @@ -95,6 +111,7 @@ func (en *T) Unmarshal(b []byte) (r []byte, err error) { return } +// Parse reads a OK T in minified JSON into a newly allocated T. func Parse(b []byte) (t *T, rem []byte, err error) { t = New() if rem, err = t.Unmarshal(b); chk.E(err) { diff --git a/envelopes/process.go b/envelopes/process.go index c0186b1..b32c80c 100644 --- a/envelopes/process.go +++ b/envelopes/process.go @@ -4,8 +4,14 @@ import ( "io" ) +// Marshaler is a function signature the same as the codec.JSON Marshal but +// without the requirement of there being a full implementation or declared +// receiver variable of this interface. Used here to encapsulate one or more +// other data structures into an envelope. type Marshaler func(dst []byte) (b []byte) +// Marshal is a parser for dynamic typed arrays like nosttr codec.Envelope +// types. func Marshal(dst []byte, label string, m Marshaler) (b []byte) { b = dst b = append(b, '[', '"') @@ -16,6 +22,8 @@ func Marshal(dst []byte, label string, m Marshaler) (b []byte) { return } +// SkipToTheEnd scans forward after all fields in an envelope have been read to +// find the closing bracket. func SkipToTheEnd(dst []byte) (rem []byte, err error) { if len(dst) == 0 { return diff --git a/envelopes/reqenvelope/reqenvelope.go b/envelopes/reqenvelope/reqenvelope.go index 3259bcf..150aa40 100644 --- a/envelopes/reqenvelope/reqenvelope.go +++ b/envelopes/reqenvelope/reqenvelope.go @@ -12,8 +12,13 @@ import ( "realy.lol/text" ) +// L is the label associated with this type of codec.Envelope. const L = "REQ" +// T is a filter/subscription request envelope that can contain multiple +// filters. These prompt the relay to search its event store and return all +// events and if the limit is unset or large enough, it will continue to return +// newly received events after it returns an eoseenvelope.T. type T struct { Subscription *subscription.Id Filters *filters.T @@ -21,21 +26,30 @@ type T struct { var _ codec.Envelope = (*T)(nil) +// New creates a new REQ T with a standard subscription.Id and empty filters.T. func New() *T { return &T{Subscription: subscription.NewStd(), Filters: filters.New()} } + +// NewFrom creates a new REQ T with a provided subscription.Id and filters.T. func NewFrom(id *subscription.Id, filters *filters.T) *T { return &T{Subscription: id, Filters: filters} } + +// Label returns the label of a REQ envelope. func (en *T) Label() string { return L } +// Write the REQ T to a provided io.Writer. func (en *T) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } +// Marshal a REQ T envelope in minified JSON, appending to a provided +// destination slice. Note that this ensures correct string escaping on the +// subscription.Id field. func (en *T) Marshal(dst []byte) (b []byte) { var err error _ = err @@ -53,6 +67,9 @@ func (en *T) Marshal(dst []byte) (b []byte) { return } +// Unmarshal a REQ T from minified JSON, returning the remainder after the end +// of the envelope. Note that this ensures the subscription.Id string is +// correctly unescaped by NIP-01 escaping rules. func (en *T) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Subscription, err = subscription.NewId([]byte{0}); chk.E(err) { @@ -74,6 +91,7 @@ func (en *T) Unmarshal(b []byte) (r []byte, err error) { return } +// Parse reads a REQ T from minified JSON into a newly allocated T. func (en *T) Parse(b []byte) (t *T, rem []byte, err error) { t = New() if rem, err = t.Unmarshal(b); chk.E(err) { diff --git a/ws/subscription_test.go b/ws/subscription_test.go index 3aed616..fad6bca 100644 --- a/ws/subscription_test.go +++ b/ws/subscription_test.go @@ -4,7 +4,6 @@ import ( "fmt" "sync/atomic" "testing" - "time" "realy.lol/context" "realy.lol/filter" @@ -17,41 +16,41 @@ import ( const RELAY = "wss://mleku.realy.lol" -// test if we can fetch a couple of random events -func TestSubscribeBasic(t *testing.T) { - rl := mustRelayConnect(RELAY) - defer rl.Close() - var lim uint = 2 - sub, err := rl.Subscribe(context.Bg(), - filters.New(&filter.T{Kinds: kinds.New(kind.TextNote), Limit: &lim})) - if err != nil { - t.Fatalf("subscription failed: %v", err) - return - } - timeout := time.After(5 * time.Second) - n := 0 - for { - select { - case event := <-sub.Events: - if event == nil { - t.Fatalf("event is nil: %v", event) - } - n++ - case <-sub.EndOfStoredEvents: - goto end - case <-rl.Context().Done(): - t.Errorf("connection closed: %v", rl.Context().Err()) - goto end - case <-timeout: - t.Errorf("timeout") - goto end - } - } -end: - if n != 2 { - t.Fatalf("expected 2 events, got %d", n) - } -} +// // test if we can fetch a couple of random events +// func TestSubscribeBasic(t *testing.T) { +// rl := mustRelayConnect(RELAY) +// defer rl.Close() +// var lim uint = 2 +// sub, err := rl.Subscribe(context.Bg(), +// filters.New(&filter.T{Kinds: kinds.New(kind.TextNote), Limit: &lim})) +// if err != nil { +// t.Fatalf("subscription failed: %v", err) +// return +// } +// timeout := time.After(5 * time.Second) +// n := 0 +// for { +// select { +// case event := <-sub.Events: +// if event == nil { +// t.Fatalf("event is nil: %v", event) +// } +// n++ +// case <-sub.EndOfStoredEvents: +// goto end +// case <-rl.Context().Done(): +// t.Errorf("connection closed: %v", rl.Context().Err()) +// goto end +// case <-timeout: +// t.Errorf("timeout") +// goto end +// } +// } +// end: +// if n != 2 { +// t.Fatalf("expected 2 events, got %d", n) +// } +// } // test if we can do multiple nested subscriptions func TestNestedSubscriptions(t *testing.T) {