forked from miku/metha
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrequest.go
139 lines (132 loc) · 3.62 KB
/
request.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
130
131
132
133
134
135
136
137
138
139
package metha
import (
"bytes"
"errors"
"fmt"
"net/http"
"net/url"
"regexp"
"sort"
)
var (
ErrInvalidVerb = errors.New("invalid OAI verb")
ErrMissingVerb = errors.New("missing verb")
ErrCannotGenerateID = errors.New("cannot generate ID")
ErrMissingURL = errors.New("missing URL")
ErrParameterMissing = errors.New("missing required parameter")
)
// A Request can express any OAI request. Not all combination of values will
// yield valid requests.
type Request struct {
BaseURL string
Verb string
Identifier string
MetadataPrefix string
From string
Until string
Set string
ResumptionToken string
CleanBeforeDecode bool
SuppressFormatParameter bool
ExtraHeaders http.Header
}
// Values enhances the builtin url.Values.
type Values struct {
url.Values
}
// NewValues create a new Values container.
func NewValues() Values {
return Values{url.Values{}}
}
// EncodeVerbatim is like Encode(), but does not escape the keys and values.
func (v Values) EncodeVerbatim() string {
if v.Values == nil {
return ""
}
var buf bytes.Buffer
keys := make([]string, 0, len(v.Values))
for k := range v.Values {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
vs := v.Values[k]
prefix := k + "="
for _, v := range vs {
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(prefix)
buf.WriteString(v)
}
}
return buf.String()
}
// URL returns the URL for a given request. Invalid verbs and missing parameters
// are reported here.
func (r *Request) URL() (*url.URL, error) {
if r.BaseURL == "" {
return nil, ErrMissingURL
}
v := NewValues()
v.Add("verb", r.Verb)
// An exclusive argument with a value that is the flow control token
// returned by a previous a request that issued an incomplete list.
if r.ResumptionToken != "" {
v.Add("resumptionToken", r.ResumptionToken)
var encodedValues string
matched, _ := regexp.MatchString(`(!| |\+)`, r.ResumptionToken)
if matched {
// http://opencontext.org/oai/request has spaces in tokens
// ExLibris Rosetta has + characters in tokens
// Encoding in these cases
encodedValues = v.Encode()
} else {
// Some repos, e.g. http://dash.harvard.edu/oai/request seem to have
// problems with encoded tokens.
encodedValues = v.EncodeVerbatim()
}
return url.Parse(fmt.Sprintf("%s?%s", r.BaseURL, encodedValues))
}
// Only add parameter, if it is not the zero value.
addOptional := func(key, value string) {
if value != "" {
v.Add(key, value)
}
}
// If required parameter is missing, complain.
addRequired := func(key, value string) error {
if value == "" {
return ErrParameterMissing
}
v.Add(key, value)
return nil
}
switch r.Verb {
case "ListMetadataFormats", "ListSets":
case "ListIdentifiers", "ListRecords":
if !r.SuppressFormatParameter {
if err := addRequired("metadataPrefix", r.MetadataPrefix); err != nil {
return nil, err
}
}
addOptional("from", r.From)
addOptional("until", r.Until)
addOptional("set", r.Set)
case "Identify":
addOptional("identifier", r.Identifier)
case "GetRecord":
if err := addRequired("identifier", r.Identifier); err != nil {
return nil, err
}
if !r.SuppressFormatParameter {
if err := addRequired("metadataPrefix", r.MetadataPrefix); err != nil {
return nil, err
}
}
default:
return nil, ErrInvalidVerb
}
// TODO(miku): some endpoints do not like encoded urls, e.g. http://web2.bium.univ-paris5.fr/oai-img/oai2.php
return url.Parse(fmt.Sprintf("%s?%s", r.BaseURL, v.EncodeVerbatim()))
}