-
Notifications
You must be signed in to change notification settings - Fork 40
/
yadis_discovery.go
119 lines (106 loc) · 3.39 KB
/
yadis_discovery.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
package openid
import (
"errors"
"io"
"io/ioutil"
"strings"
"golang.org/x/net/html"
)
var yadisHeaders = map[string]string{
"Accept": "application/xrds+xml"}
func yadisDiscovery(id string, getter httpGetter) (opEndpoint string, opLocalID string, err error) {
// Section 6.2.4 of Yadis 1.0 specifications.
// The Yadis Protocol is initiated by the Relying Party Agent
// with an initial HTTP request using the Yadis URL.
// This request MUST be either a GET or a HEAD request.
// A GET or HEAD request MAY include an HTTP Accept
// request-header (HTTP 14.1) specifying MIME media type,
// application/xrds+xml.
resp, err := getter.Get(id, yadisHeaders)
if err != nil {
return "", "", err
}
defer resp.Body.Close()
// Section 6.2.5 from Yadis 1.0 spec: Response
contentType := resp.Header.Get("Content-Type")
// The response MUST be one of:
// (see 6.2.6 for precedence)
if l := resp.Header.Get("X-XRDS-Location"); l != "" {
// 2. HTTP response-headers that include an X-XRDS-Location
// response-header, together with a document
return getYadisResourceDescriptor(l, getter)
} else if strings.Contains(contentType, "text/html") {
// 1. An HTML document with a <head> element that includes a
// <meta> element with http-equiv attribute, X-XRDS-Location,
metaContent, err := findMetaXrdsLocation(resp.Body)
if err == nil {
return getYadisResourceDescriptor(metaContent, getter)
}
return "", "", err
} else if strings.Contains(contentType, "application/xrds+xml") {
// 4. A document of MIME media type, application/xrds+xml.
body, err := ioutil.ReadAll(resp.Body)
if err == nil {
return parseXrds(body)
}
return "", "", err
}
// 3. HTTP response-headers only, which MAY include an
// X-XRDS-Location response-header, a content-type
// response-header specifying MIME media type,
// application/xrds+xml, or both.
// (this is handled by one of the 2 previous if statements)
return "", "", errors.New("No expected header, or content type")
}
// Similar as above, but we expect an absolute Yadis document URL.
func getYadisResourceDescriptor(id string, getter httpGetter) (opEndpoint string, opLocalID string, err error) {
resp, err := getter.Get(id, yadisHeaders)
if err != nil {
return "", "", err
}
defer resp.Body.Close()
// 4. A document of MIME media type, application/xrds+xml.
body, err := ioutil.ReadAll(resp.Body)
if err == nil {
return parseXrds(body)
}
return "", "", err
}
// Search for
// <head>
// <meta http-equiv="X-XRDS-Location" content="....">
func findMetaXrdsLocation(input io.Reader) (location string, err error) {
tokenizer := html.NewTokenizer(input)
inHead := false
for {
tt := tokenizer.Next()
switch tt {
case html.ErrorToken:
return "", tokenizer.Err()
case html.StartTagToken, html.EndTagToken, html.SelfClosingTagToken:
tk := tokenizer.Token()
if tk.Data == "head" {
if tt == html.StartTagToken {
inHead = true
} else {
return "", errors.New("Meta X-XRDS-Location not found")
}
} else if inHead && tk.Data == "meta" {
ok := false
content := ""
for _, attr := range tk.Attr {
if attr.Key == "http-equiv" &&
strings.ToLower(attr.Val) == "x-xrds-location" {
ok = true
} else if attr.Key == "content" {
content = attr.Val
}
}
if ok && len(content) > 0 {
return content, nil
}
}
}
}
return "", errors.New("Meta X-XRDS-Location not found")
}