-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathgemini.go
137 lines (113 loc) · 3.69 KB
/
gemini.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
package gemini
import (
"fmt"
"net/url"
"strings"
)
const (
URLMaxLength = 1024
MetaMaxLength = 1024
)
// Gemini status codes as defined in the Gemini spec Appendix 1.
const (
StatusInput = 10
StatusSensitiveInput = 11
StatusSuccess = 20
StatusRedirect = 30
StatusRedirectTemporary = 30
StatusRedirectPermanent = 31
StatusTemporaryFailure = 40
StatusUnavailable = 41
StatusCGIError = 42
StatusProxyError = 43
StatusSlowDown = 44
StatusPermanentFailure = 50
StatusNotFound = 51
StatusGone = 52
StatusProxyRequestRefused = 53
StatusBadRequest = 59
StatusClientCertificateRequired = 60
StatusCertificateNotAuthorised = 61
StatusCertificateNotValid = 62
)
var statusText = map[int]string{
StatusInput: "Input",
StatusSensitiveInput: "Sensitive Input",
StatusSuccess: "Success",
// StatusRedirect: "Redirect - Temporary"
StatusRedirectTemporary: "Redirect - Temporary",
StatusRedirectPermanent: "Redirect - Permanent",
StatusTemporaryFailure: "Temporary Failure",
StatusUnavailable: "Server Unavailable",
StatusCGIError: "CGI Error",
StatusProxyError: "Proxy Error",
StatusSlowDown: "Slow Down",
StatusPermanentFailure: "Permanent Failure",
StatusNotFound: "Not Found",
StatusGone: "Gone",
StatusProxyRequestRefused: "Proxy Request Refused",
StatusBadRequest: "Bad Request",
StatusClientCertificateRequired: "Client Certificate Required",
StatusCertificateNotAuthorised: "Certificate Not Authorised",
StatusCertificateNotValid: "Certificate Not Valid",
}
// StatusText returns a text for the Gemini status code. It returns the empty
// string if the code is unknown.
func StatusText(code int) string {
return statusText[code]
}
// SimplifyStatus simplify the response status by ommiting the detailed second digit of the status code.
func SimplifyStatus(status int) int {
return (status / 10) * 10
}
// IsStatusValid checks whether an int status is covered by the spec.
// Note that:
// A client SHOULD deal with undefined status codes
// between '10' and '69' per the default action of the initial digit.
func IsStatusValid(status int) bool {
_, found := statusText[status]
return found
}
// StatusInRange returns true if the status has a valid first digit.
// This means it can be handled even if it's not defined by the spec,
// because it has a known category
func StatusInRange(status int) bool {
if status < 10 || status > 69 {
return false
}
return true
}
// CleanStatus returns the status code as is, unless it's invalid but still in range
// Then it returns the status code with the second digit zeroed. So 51 returns 51,
// but 22 returns 20.
//
// This corresponds with the spec:
// A client SHOULD deal with undefined status codes
// between '10' and '69' per the default action of the initial digit.
func CleanStatus(status int) int {
// All the functions come together!
if !IsStatusValid(status) && StatusInRange(status) {
return SimplifyStatus(status)
}
return status
}
// QueryEscape provides URL query escaping in a way that follows the Gemini spec.
// It is the same as url.PathEscape, but it also replaces the +, because Gemini
// requires percent-escaping for queries.
func QueryEscape(query string) string {
return strings.ReplaceAll(url.PathEscape(query), "+", "%2B")
}
// QueryUnescape is the same as url.PathUnescape
func QueryUnescape(query string) (string, error) {
return url.PathUnescape(query)
}
type Error struct {
Err error
Status int
}
func (e Error) Error() string {
return fmt.Sprintf("Status %d: %v", e.Status, e.Err)
}
func (e Error) Unwrap() error {
return e.Err
}