-
Notifications
You must be signed in to change notification settings - Fork 5
/
mux.go
129 lines (107 loc) · 3.32 KB
/
mux.go
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package epp
import (
"strings"
"aqwari.net/xml/xmltree"
"github.com/bombsimon/epp-go/types"
"github.com/pkg/errors"
)
const nsEPP = "urn:ietf:params:xml:ns:epp-1.0"
// Mux can be used to route different EPP messages to different handlers,
// depending on the message content.
//
// m := Mux{}
//
// m.AddNamespaceAlias("urn:ietf:params:xml:ns:domain-1.0", "domain")
//
// m.AddHandler("hello", handleHello)
// m.AddHandler("command/login", handleLogin)
// m.AddHandler("command/check/urn:ietf:params:xml:ns:contact-1.0", handleCheckContact)
// m.AddHandler("command/check/domain", handleCheckDomain)
type Mux struct {
handlers map[string]HandlerFunc
namespaceAliases map[string]string
}
// NewMux will create and return a new Mux.
func NewMux() *Mux {
m := &Mux{
namespaceAliases: map[string]string{
types.NameSpaceDomain: "domain",
types.NameSpaceHost: "host",
types.NameSpaceContact: "contact",
},
handlers: make(map[string]HandlerFunc),
}
return m
}
// AddNamespaceAlias will add an alias for the specified namespace. After the
// alias is added it can be used in routing. Multiple namespaces can be added
// to the same alias.
// m.AddNamespaceAlias("urn:ietf:params:xml:ns:contact-1.0", "host-and-contact")
// m.AddNamespaceAlias("urn:ietf:params:xml:ns:host-1.0", "host-and-contact")
func (m *Mux) AddNamespaceAlias(ns, alias string) {
m.namespaceAliases[ns] = alias
}
// AddHandler will add a handler for the specified route.
// Routes are defined almost like xpath.
func (m *Mux) AddHandler(path string, handler HandlerFunc) {
m.handlers[path] = handler
}
// Handle will handle an incoming message and route it to the correct handler.
// Pass the function to Server to use the Mux.
func (m *Mux) Handle(s *Session, d []byte) ([]byte, error) {
root, err := xmltree.Parse(d)
if err != nil {
return nil, err
}
path, err := m.buildPath(root)
if err != nil {
return nil, err
}
h, ok := m.handlers[path]
if !ok {
// TODO
return nil, errors.Errorf("no handler for %s", path)
}
return h(s, d)
}
func (m *Mux) buildPath(root *xmltree.Element) (string, error) {
// Ensure the start element is <epp>.
if root.Name.Space != nsEPP || root.Name.Local != "epp" {
return "", errors.New("missing <epp> tag")
}
// We should only have one element inside the <epp> tag.
if len(root.Children) != 1 {
return "", errors.New("<epp> should contain one element")
}
el := root.Children[0]
if el.Name.Local != "command" {
// It's not a command, so we'll just use this tag as the route.
return el.Name.Local, nil
}
pathParts := []string{"command"}
for _, child := range el.Children {
name := child.Name.Local
switch name {
case "extension", "clTRID":
// These tags can exist in a command but are always available
// so we won't do any routing based on them.
continue
}
switch name {
case "login", "logout", "poll":
// Login, Logout and Poll are commands defined in eppcom-1.0.xml
// and does not need any further routing.
pathParts = append(pathParts, name)
default:
// Other commands can be executed on multiple types of objects
// that are defined by their namespace.
ns := child.Children[0].Name.Space
if alias, ok := m.namespaceAliases[ns]; ok {
ns = alias
}
pathParts = append(pathParts, name, ns)
}
break
}
return strings.Join(pathParts, "/"), nil
}