File: eventing.go

package info (click to toggle)
golang-github-anacrolix-dms 1.5.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 436 kB
  • sloc: xml: 19; sh: 19; makefile: 9
file content (92 lines) | stat: -rw-r--r-- 2,367 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package upnp

import (
	"crypto/rand"
	"encoding/xml"
	"fmt"
	"io"
	"net/url"
	"regexp"
	"time"

	"github.com/anacrolix/log"
)

// TODO: Why use namespace prefixes in PropertySet et al? Because the spec
// uses them, and I believe the Golang standard library XML spec implementers
// incorrectly assume that you can get away with just xmlns="".

// propertyset is the root element sent in an event callback.
type PropertySet struct {
	XMLName    struct{} `xml:"e:propertyset"`
	Properties []Property
	// This should be set to `"urn:schemas-upnp-org:event-1-0"`.
	Space string `xml:"xmlns:e,attr"`
}

// propertys provide namespacing to the contained variables.
type Property struct {
	XMLName  struct{} `xml:"e:property"`
	Variable Variable
}

// Represents an evented state variable that has sendEvents="yes" in its
// service spec.
type Variable struct {
	XMLName xml.Name
	Value   string `xml:",chardata"`
}

type subscriber struct {
	sid     string
	nextSeq uint32 // 0 for initial event, wraps from Uint32Max to 1.
	urls    []*url.URL
	expiry  time.Time
}

// Intended to eventually be an embeddable implementation for managing
// eventing for a service. Not complete.
type Eventing struct {
	subscribers map[string]*subscriber
}

func (me *Eventing) Subscribe(callback []*url.URL, timeoutSeconds int) (sid string, actualTimeout int, err error) {
	var uuid [16]byte
	io.ReadFull(rand.Reader, uuid[:])
	sid = FormatUUID(uuid[:])
	if _, ok := me.subscribers[sid]; ok {
		err = fmt.Errorf("already subscribed: %s", sid)
		return
	}
	ssr := &subscriber{
		sid:    sid,
		urls:   callback,
		expiry: time.Now().Add(time.Duration(timeoutSeconds) * time.Second),
	}
	if me.subscribers == nil {
		me.subscribers = make(map[string]*subscriber)
	}
	me.subscribers[sid] = ssr
	actualTimeout = int(ssr.expiry.Sub(time.Now()) / time.Second)
	return
}

func (me *Eventing) Unsubscribe(sid string) error {
	return nil
}

var callbackURLRegexp = regexp.MustCompile("<(.*?)>")

// Parse the CALLBACK HTTP header in an event subscription request. See UPnP
// Device Architecture 4.1.2.
func ParseCallbackURLs(callback string) (ret []*url.URL) {
	for _, match := range callbackURLRegexp.FindAllStringSubmatch(callback, -1) {
		_url, err := url.Parse(match[1])
		if err != nil {
			log.Printf("bad callback url: %q", match[1])
			continue
		}
		ret = append(ret, _url)
	}
	return
}