summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2023-06-30 11:58:21 +0200
committerxengineering <me@xengineering.eu>2023-06-30 11:58:21 +0200
commit04746f75ea935266ded4e28edb6ab25c537d50e1 (patch)
treed13dcd9d9190b008385912d86918fb70f08f83ad
parentc8544b19df055235b9106ff296f0a5fe7cb1fe91 (diff)
parent91766058d76044fa7e07f75c586bc7f89b868328 (diff)
downloadlimox-04746f75ea935266ded4e28edb6ab25c537d50e1.tar
limox-04746f75ea935266ded4e28edb6ab25c537d50e1.tar.zst
limox-04746f75ea935266ded4e28edb6ab25c537d50e1.zip
Merge branch 'routing'
This adds an XML element router and a corresponding unit test. The element router will be used to register XML element handler with a single line.
-rw-r--r--xmpp/router.go59
-rw-r--r--xmpp/router_test.go69
-rw-r--r--xmpp/stream_pair.go8
3 files changed, 129 insertions, 7 deletions
diff --git a/xmpp/router.go b/xmpp/router.go
new file mode 100644
index 0000000..839f870
--- /dev/null
+++ b/xmpp/router.go
@@ -0,0 +1,59 @@
+package xmpp
+
+import (
+ "encoding/xml"
+ "log"
+)
+
+// routingTable is a data structure which contains routing information for XML
+// elements. The xml.StartElement at the beginning of an XML element has a name
+// containing the XML namespace and a local name. Based on this compisition
+// which forms the xml.Name the appropriate handler function is defined by each
+// entry of the routingTable.
+type routingTable []struct {
+ name xml.Name
+ handler func([]xml.Token)
+}
+
+// getRoutingTable returns the routing table used in
+// xengineering.eu/limox/xmpp. Since Go does not allow such a datatype as a
+// constant such a function is a simple yet inefficient approach to guarantee
+// that an unmodified routing table is delivered to each user. A global
+// variable would have the problem that it could be altered during execution.
+func getRoutingTable() routingTable {
+ return routingTable{
+ // TODO fill with entries
+ }
+}
+
+// route determines the correct handler function for the given XML element by a
+// given routingTable. In addition it executes the determined handler function.
+// If no handler function is found an error message is send via the log module.
+func route(e []xml.Token, t routingTable) {
+ var name xml.Name
+
+ // TODO a stronger definition of an XML element (as here
+ // https://www.w3schools.com/xml/xml_elements.asp) would define that the
+ // first Token of an element is a StartElement token. This would make this
+ // code easier.
+ escape := false
+ for _, token := range e {
+ switch s := token.(type) {
+ case xml.StartElement:
+ name = s.Name
+ escape = true
+ }
+ if escape {
+ break
+ }
+ }
+
+ for _, r := range t {
+ if name == r.name {
+ r.handler(e)
+ return
+ }
+ }
+
+ log.Println("Could not route XML element")
+}
diff --git a/xmpp/router_test.go b/xmpp/router_test.go
new file mode 100644
index 0000000..ea13712
--- /dev/null
+++ b/xmpp/router_test.go
@@ -0,0 +1,69 @@
+package xmpp
+
+import (
+ "encoding/xml"
+ "testing"
+ "strings"
+)
+
+// routerTest contains a single test case for the xmpp.router. The XML element
+// is given as a string and the value int describes the expected value of a
+// test variable inside the corresponding unit test. See TestRouter for
+// details.
+type routerTest struct {
+ xml string
+ value int
+}
+
+// TestRouter tests the xmpp/router.go file. The central functionality is the
+// route() function. To test this a fake routing table is created inside this
+// test with XML element handler function produced by the factory function
+// defined below. each handler simply sets the value of the testpoint variable
+// to the value mentioned by the test point. That way the routing can be
+// validated.
+func TestRouter(t *testing.T) {
+ var testpoint int
+
+ factory := func(tp *int, i int) func([]xml.Token) {
+ return func([]xml.Token) {
+ *tp = i
+ }
+ }
+
+ tests := []routerTest{
+ routerTest{`<a></a>`, 1},
+ routerTest{`<b></b>`, 2},
+ routerTest{`<c></c>`, 3},
+ routerTest{`<b xmlns='https://xengineering.eu'></b>`, 4},
+ routerTest{`<a xmlns='https://xengineering.eu'></a>`, 5},
+ }
+
+ testRouting := routingTable{
+ {xml.Name{``, `a`}, factory(&testpoint, 1)},
+ {xml.Name{``, `b`}, factory(&testpoint, 2)},
+ {xml.Name{``, `c`}, factory(&testpoint, 3)},
+ {xml.Name{`https://xengineering.eu`, `b`}, factory(&testpoint, 4)},
+ {xml.Name{`https://xengineering.eu`, `a`}, factory(&testpoint, 5)},
+ }
+
+ for _, v := range tests {
+ testpoint = 0
+
+ r := strings.NewReader(v.xml)
+ d := xml.NewDecoder(r)
+ tokens := make([]xml.Token, 0)
+ for {
+ token, err := d.Token()
+ if err != nil {
+ break
+ }
+ tokens = append(tokens, xml.CopyToken(token))
+ }
+
+ route(tokens, testRouting)
+
+ if testpoint != v.value {
+ t.Fatalf("XML element was not routed correctly!\n")
+ }
+ }
+}
diff --git a/xmpp/stream_pair.go b/xmpp/stream_pair.go
index b13d8a3..c9108d1 100644
--- a/xmpp/stream_pair.go
+++ b/xmpp/stream_pair.go
@@ -28,13 +28,7 @@ func runStreamPair(s *session) {
}
if buf.isComplete() {
element := buf.reset()
- // TODO handle XML element here - this is just a dummy:
- switch start := element[0].(type) {
- case xml.StartElement:
- log.Printf("Got XML element `%s`\n", start.Name.Local)
- default:
- log.Println("No xml.StartElement at start of element buffer!")
- }
+ route(element, getRoutingTable())
}
}
}