@@ -85,9 +85,7 @@ func isHTTPTokenChar(c byte) bool {
85
85
// Represents a valid host.
86
86
// https://datatracker.ietf.org/doc/html/rfc952
87
87
// https://datatracker.ietf.org/doc/html/rfc1123#page-13
88
- type host []label
89
-
90
- func parseHost (input string ) (host host , rest string , err error ) {
88
+ func parseHost (input string ) (host []label , rest string , err error ) {
91
89
rest = input
92
90
var label label
93
91
@@ -165,6 +163,129 @@ func isValidLabelChar(c byte) bool {
165
163
}
166
164
}
167
165
166
+ func parsePath (input string ) ([]segment , string , error ) {
167
+ if input == "" {
168
+ return nil , "" , nil
169
+ }
170
+
171
+ var segments []segment
172
+ rest := input
173
+
174
+ // If the path doesn't start with '/', it's not a valid absolute path
175
+ // But we'll be flexible and parse relative paths too
176
+ for {
177
+ // Skip leading slash if present
178
+ if rest != "" && rest [0 ] == '/' {
179
+ rest = rest [1 :]
180
+ }
181
+
182
+ // If we've consumed all input, we're done
183
+ if rest == "" {
184
+ break
185
+ }
186
+
187
+ // Parse the next segment
188
+ seg , remaining , err := parsePathSegment (rest )
189
+ if err != nil {
190
+ return nil , "" , err
191
+ }
192
+
193
+ // If we got an empty segment and there's still input,
194
+ // it means we hit an invalid character
195
+ if seg == "" && remaining != "" {
196
+ break
197
+ }
198
+
199
+ segments = append (segments , seg )
200
+ rest = remaining
201
+
202
+ // If there's no slash after the segment, we're done parsing the path
203
+ if rest == "" || rest [0 ] != '/' {
204
+ break
205
+ }
206
+ }
207
+
208
+ return segments , rest , nil
209
+ }
210
+
211
+ // Represents a valid url path segment.
212
+ type segment string
213
+
214
+ func parsePathSegment (input string ) (segment , string , error ) {
215
+ if input == "" {
216
+ return "" , "" , nil
217
+ }
218
+
219
+ var i int
220
+ for i = 0 ; i < len (input ); i ++ {
221
+ c := input [i ]
222
+
223
+ // Check for percent-encoded characters (%XX)
224
+ if c == '%' {
225
+ if i + 2 >= len (input ) || ! isHexDigit (input [i + 1 ]) || ! isHexDigit (input [i + 2 ]) {
226
+ break
227
+ }
228
+ i += 2
229
+ continue
230
+ }
231
+
232
+ // Check for valid pchar characters
233
+ if ! isPChar (c ) {
234
+ break
235
+ }
236
+ }
237
+
238
+ return segment (input [:i ]), input [i :], nil
239
+ }
240
+
241
+ // isUnreserved returns true if the character is unreserved per RFC 3986
242
+ // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
243
+ func isUnreserved (c byte ) bool {
244
+ return (c >= 'A' && c <= 'Z' ) ||
245
+ (c >= 'a' && c <= 'z' ) ||
246
+ (c >= '0' && c <= '9' ) ||
247
+ c == '-' || c == '.' || c == '_' || c == '~'
248
+ }
249
+
250
+ // isSubDelim returns true if the character is a sub-delimiter per RFC 3986
251
+ // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
252
+ func isSubDelim (c byte ) bool {
253
+ return c == '!' || c == '$' || c == '&' || c == '\'' ||
254
+ c == '(' || c == ')' || c == '*' || c == '+' ||
255
+ c == ',' || c == ';' || c == '='
256
+ }
257
+
258
+ // isPChar returns true if the character is valid in a path segment (excluding percent-encoded)
259
+ // pchar = unreserved / sub-delims / ":" / "@"
260
+ func isPChar (c byte ) bool {
261
+ return isUnreserved (c ) || isSubDelim (c ) || c == ':' || c == '@'
262
+ }
263
+
264
+ // isHexDigit returns true if the character is a hexadecimal digit
265
+ func isHexDigit (c byte ) bool {
266
+ return (c >= '0' && c <= '9' ) ||
267
+ (c >= 'A' && c <= 'F' ) ||
268
+ (c >= 'a' && c <= 'f' )
269
+ }
270
+
271
+ // parseKey parses the predefined keys that the cli can handle. Also strips the `=` following the key.
272
+ func parseKey (rule string ) (string , string , error ) {
273
+ if rule == "" {
274
+ return "" , "" , errors .New ("expected key" )
275
+ }
276
+
277
+ // These are the current keys we support.
278
+ keys := []string {"method" , "domain" , "path" }
279
+
280
+ for _ , key := range keys {
281
+ if rest , found := strings .CutPrefix (rule , key + "=" ); found {
282
+ return key , rest , nil
283
+ }
284
+ }
285
+
286
+ return "" , "" , errors .New ("expected key" )
287
+ }
288
+
168
289
func parseAllowRule (string ) (Rule , error ) {
169
290
return Rule {}, nil
170
291
}
0 commit comments