1 | |
---|
2 | ;; |
---|
3 | ;; Definitions and parsing routines for Uniform Resource Identifiers (RFC 3986). |
---|
4 | ;; |
---|
5 | ;; Based on the Haskell URI library by Graham Klyne <gk@ninebynine.org>. |
---|
6 | ;; |
---|
7 | ;; Copyright 2008 Ivan Raikov. |
---|
8 | ;; |
---|
9 | ;; |
---|
10 | ;; Redistribution and use in source and binary forms, with or without |
---|
11 | ;; modification, are permitted provided that the following conditions |
---|
12 | ;; are met: |
---|
13 | ;; |
---|
14 | ;; - Redistributions of source code must retain the above copyright |
---|
15 | ;; notice, this list of conditions and the following disclaimer. |
---|
16 | ;; |
---|
17 | ;; - Redistributions in binary form must reproduce the above |
---|
18 | ;; copyright notice, this list of conditions and the following |
---|
19 | ;; disclaimer in the documentation and/or other materials provided |
---|
20 | ;; with the distribution. |
---|
21 | ;; |
---|
22 | ;; - Neither name of the copyright holders nor the names of its |
---|
23 | ;; contributors may be used to endorse or promote products derived |
---|
24 | ;; from this software without specific prior written permission. |
---|
25 | ;; |
---|
26 | ;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND THE |
---|
27 | ;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
---|
28 | ;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
---|
29 | ;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
---|
30 | ;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR THE |
---|
31 | ;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
---|
32 | ;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
---|
33 | ;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
---|
34 | ;; USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
---|
35 | ;; AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
36 | ;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
---|
37 | ;; ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
---|
38 | ;; POSSIBILITY OF SUCH DAMAGE. |
---|
39 | ;; |
---|
40 | |
---|
41 | (require-extension syntax-case) |
---|
42 | (require-extension matchable) |
---|
43 | (require-extension srfi-1) |
---|
44 | (require-extension srfi-4) |
---|
45 | (require-extension srfi-13) |
---|
46 | (require-extension srfi-14) |
---|
47 | |
---|
48 | (define-extension uri-generic) |
---|
49 | |
---|
50 | (declare |
---|
51 | (usual-integrations) |
---|
52 | (fixnum) |
---|
53 | (inline) |
---|
54 | (lambda-lift) |
---|
55 | (export uri-reference |
---|
56 | uri? uri-auth uri-authority uri-scheme uri-path uri-query |
---|
57 | uri-fragment uri-host uri-port uri-username uri-password |
---|
58 | absolute-uri uri->string uri->list uri-char-list-escape |
---|
59 | uri-char-list->string uri-string->char-list |
---|
60 | uri-relative-to uri-relative-from |
---|
61 | uri-normalize-case uri-normalize-path-segments)) |
---|
62 | |
---|
63 | |
---|
64 | (define-record URI scheme authority path query fragment) |
---|
65 | (define-record URIAuth username password host port) |
---|
66 | |
---|
67 | (define-record-printer (URI x out) |
---|
68 | (fprintf out "#(URI scheme=~S authority=~A path=~S query=~S fragment=~S)" |
---|
69 | (URI-scheme x) |
---|
70 | (URI-authority x) |
---|
71 | (URI-path x) |
---|
72 | (URI-query x) |
---|
73 | (URI-fragment x))) |
---|
74 | |
---|
75 | (define-record-printer (URIAuth x out) |
---|
76 | (fprintf out "#(URIAuth host=~S port=~A)" |
---|
77 | (URIAuth-host x) |
---|
78 | (URIAuth-port x))) |
---|
79 | |
---|
80 | (define uri? URI?) |
---|
81 | |
---|
82 | (define uri-auth URI-authority ) |
---|
83 | (define uri-authority URI-authority ) |
---|
84 | (define uri-scheme URI-scheme ) |
---|
85 | (define uri-path URI-path ) |
---|
86 | (define uri-query URI-query ) |
---|
87 | (define uri-fragment URI-fragment ) |
---|
88 | |
---|
89 | (define (uri-host x) |
---|
90 | (let ((auth (URI-authority x))) |
---|
91 | (and auth (URIAuth-host auth)))) |
---|
92 | |
---|
93 | (define (uri-port x) |
---|
94 | (let ((auth (URI-authority x))) |
---|
95 | (and auth (URIAuth-port auth)))) |
---|
96 | |
---|
97 | (define (uri-username x) |
---|
98 | (let ((auth (URI-authority x))) |
---|
99 | (and auth (URIAuth-username auth)))) |
---|
100 | |
---|
101 | (define (uri-password x) |
---|
102 | (let ((auth (URI-authority x))) |
---|
103 | (and auth (URIAuth-password auth)))) |
---|
104 | |
---|
105 | ;; Character classes |
---|
106 | |
---|
107 | (define (hexdigit-char? c) (char-set-contains? char-set:hex-digit c)) |
---|
108 | |
---|
109 | (define (reserved-char? c) (char-set-contains? char-set:reserved c)) |
---|
110 | |
---|
111 | (define (unreserved-char? c) (char-set-contains? char-set:unreserved c)) |
---|
112 | |
---|
113 | (define (scheme-char? c) (char-set-contains? char-set:scheme c)) |
---|
114 | |
---|
115 | (define (ipv-future-char? c) (char-set-contains? char-set:ipv-future c)) |
---|
116 | |
---|
117 | (define (pct-escaped? c) (match c ((#\% h1 h2) #t) (else #f))) |
---|
118 | |
---|
119 | |
---|
120 | ;; Helper functions for character parsing |
---|
121 | |
---|
122 | (define (uchar extras) |
---|
123 | (let ((extras-set (or (and (char-set? extras) extras) (string->char-set extras)))) |
---|
124 | (lambda (c) (or (pct-escaped? c) (unreserved-char? c) |
---|
125 | (char-set-contains? char-set:sub-delims c) |
---|
126 | (char-set-contains? extras-set c) )))) |
---|
127 | |
---|
128 | ;; same as uchar, but without sub-delims |
---|
129 | (define (schar extras) |
---|
130 | (let ((extras-set (or (and (char-set? extras) extras) (string->char-set extras)))) |
---|
131 | (lambda (c) (or (pct-escaped? c) (unreserved-char? c) |
---|
132 | (char-set-contains? extras-set c) )))) |
---|
133 | |
---|
134 | (define (many pred?) |
---|
135 | (lambda (s) |
---|
136 | (let loop ((lst (list)) (rst s)) |
---|
137 | (cond ((null? rst) (list (reverse lst) rst)) |
---|
138 | ((pred? (car rst)) (loop (cons (car rst) lst) (cdr rst))) |
---|
139 | (else (list (reverse lst) rst)))))) |
---|
140 | |
---|
141 | (define (many1 pred?) |
---|
142 | (lambda (s) |
---|
143 | (let ((a1 (and (not (null? s)) (pred? (car s)) (car s)))) |
---|
144 | (and a1 (match ((many pred?) (cdr s)) |
---|
145 | ((as rst) (list (cons a1 as) rst)) |
---|
146 | (else #f)))))) |
---|
147 | |
---|
148 | |
---|
149 | (define (count-min-max m n pred?) |
---|
150 | (lambda (s) |
---|
151 | (let loop ((m m) (n n) (lst (list)) (rst s)) |
---|
152 | (cond ((and (pair? rst) (positive? m)) |
---|
153 | (if (pred? (car rst)) |
---|
154 | (loop (- m 1) (- n 1) (cons (car rst) lst) (cdr rst)) #f)) |
---|
155 | ((or (<= n 0) (null? rst)) (list (reverse lst) rst)) |
---|
156 | (else |
---|
157 | (if (pred? (car rst)) |
---|
158 | (loop 0 (- n 1) (cons (car rst) lst) (cdr rst)) |
---|
159 | (list (reverse lst) rst))))))) |
---|
160 | |
---|
161 | ;; Parser combinators |
---|
162 | |
---|
163 | (define (consume f) |
---|
164 | (lambda (s) |
---|
165 | (let loop ((lst (list)) (rst s)) |
---|
166 | (match (f rst) |
---|
167 | ((a rst) (loop (cons a lst) rst)) |
---|
168 | (else (list (reverse lst) rst)))))) |
---|
169 | |
---|
170 | |
---|
171 | (define (consume-count n f) |
---|
172 | (lambda (s) |
---|
173 | (let loop ((n n) (lst (list)) (rst s)) |
---|
174 | (if (positive? n) |
---|
175 | (match (or (f rst) (list #f s)) |
---|
176 | ((x rst) (and x (loop (- n 1) (cons x lst) rst)))) |
---|
177 | (list (reverse lst) rst))))) |
---|
178 | |
---|
179 | |
---|
180 | (define (consume-min-max m n f) |
---|
181 | (lambda (s) |
---|
182 | (let loop ((m m) (n n) (lst (list)) (rst s)) |
---|
183 | (cond ((positive? m) |
---|
184 | (match (f rst) |
---|
185 | ((a1 rst) (loop (- m 1) (- n 1) (cons a1 lst) rst)) |
---|
186 | (else #f))) |
---|
187 | ((<= n 0) (list (reverse lst) rst)) |
---|
188 | (else |
---|
189 | (match (f rst) |
---|
190 | ((a1 rst) (loop 0 (- n 1) (cons a1 lst) rst)) |
---|
191 | (else #f))))))) |
---|
192 | |
---|
193 | ;; Helper function for malformed ip address error messages |
---|
194 | |
---|
195 | (define (try-ip-literal->string s) |
---|
196 | (let loop ((lst (list)) (rst s)) |
---|
197 | (match rst ((#\] . rst) (uri-char-list->string (reverse lst))) |
---|
198 | (() (uri-char-list->string (reverse lst))) |
---|
199 | (else (loop (cons (car rst) lst) (cdr rst)))))) |
---|
200 | |
---|
201 | ;; RFC 3986, section 2.1 |
---|
202 | ;; |
---|
203 | ;; Returns a 'pct-encoded' sequence of octets. |
---|
204 | ;; |
---|
205 | (define (pct-escape lst) |
---|
206 | (define (hex-digit i) (and (>= i 0) (< i 16) (car (string->list (sprintf "~X" i))))) |
---|
207 | (reverse (fold (lambda (x ax) |
---|
208 | (let ((h1 (hex-digit (quotient x 16))) |
---|
209 | (h2 (hex-digit (remainder x 16)))) |
---|
210 | (cons `(#\% ,h1 ,h2) ax))) |
---|
211 | (list) lst))) |
---|
212 | |
---|
213 | ;; RFC3986, section 2.2 |
---|
214 | ;; |
---|
215 | ;; Reserved characters. |
---|
216 | ;; |
---|
217 | |
---|
218 | (define char-set:gen-delims (string->char-set ":/?#[]@")) |
---|
219 | (define char-set:sub-delims (string->char-set "!$&'()*+,;=")) |
---|
220 | |
---|
221 | (define char-set:reserved (char-set-union char-set:gen-delims char-set:sub-delims)) |
---|
222 | |
---|
223 | ;; RFC3986, section 2.3 |
---|
224 | ;; |
---|
225 | ;; "Unreserved" characters. |
---|
226 | ;; |
---|
227 | |
---|
228 | (define char-set:unreserved |
---|
229 | (char-set-union char-set:letter+digit (string->char-set "-_.~"))) |
---|
230 | |
---|
231 | |
---|
232 | ;; RFC3986, section 3 |
---|
233 | ;; |
---|
234 | ;; URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] |
---|
235 | ;; |
---|
236 | ;; hier-part = "//" authority path-abempty |
---|
237 | ;; / path-abs |
---|
238 | ;; / path-rootless |
---|
239 | ;; / path-empty |
---|
240 | |
---|
241 | (define (uri s) |
---|
242 | (let ((s (if (string? s) (uri-string->char-list s) s))) |
---|
243 | (match (scheme s) |
---|
244 | ((us rst) |
---|
245 | (match-let* (((ua up rst) (hier-part rst)) |
---|
246 | ((uq rst) (match rst ((#\? . rst) (query rst)) |
---|
247 | (else (list #f rst)))) |
---|
248 | ((uf rst) (match rst ((#\# . rst) (fragment rst)) |
---|
249 | (else (list #f rst))))) |
---|
250 | (make-URI (string->symbol (list->string us)) ua |
---|
251 | (map uri-char-list->string up) (and uq (filter-map query->string uq)) |
---|
252 | (and uf (uri-char-list->string uf))))) |
---|
253 | (else #f)))) |
---|
254 | |
---|
255 | (define (hier-part s) |
---|
256 | (match s ((#\/ #\/ . rst) |
---|
257 | (match-let* (((ua rst) (authority rst)) |
---|
258 | ((up rst) (path-abempty rst))) |
---|
259 | (list ua up rst))) |
---|
260 | (else (match-let (((up rst) (or (path-abs s) (path-rootless s) (list (list) s)))) |
---|
261 | (list #f up rst))))) |
---|
262 | |
---|
263 | ;; RFC3986, section 3.1 |
---|
264 | |
---|
265 | (define scheme0 (many scheme-char?)) |
---|
266 | (define (scheme s) |
---|
267 | (match (scheme0 s) |
---|
268 | ((ss (#\: . rst)) (list ss rst)) |
---|
269 | (else #f))) |
---|
270 | |
---|
271 | (define char-set:scheme |
---|
272 | (char-set-union char-set:letter+digit (string->char-set "+-."))) |
---|
273 | |
---|
274 | |
---|
275 | ;; RFC3986, section 3.2 |
---|
276 | |
---|
277 | (define (authority s) |
---|
278 | (match-let* (((uu uw rst) (or (userinfo s) (list #f #f s))) |
---|
279 | ((uh rst) (host rst)) |
---|
280 | ((up rst) (or (port rst) (list #f rst)))) |
---|
281 | (list (make-URIAuth (and uu (uri-char-list->string uu)) (and uw (uri-char-list->string uw)) |
---|
282 | (uri-char-list->string uh) |
---|
283 | (and (pair? up) (string->number (list->string up)))) |
---|
284 | rst))) |
---|
285 | |
---|
286 | ;; RFC3986, section 3.2.1 |
---|
287 | |
---|
288 | (define userinfo0 (many (uchar ";&=+$,"))) |
---|
289 | |
---|
290 | (define (userinfo s) |
---|
291 | (match (userinfo0 s) |
---|
292 | ((uu ( #\: . rst)) (match (userinfo0 rst) |
---|
293 | ((up ( #\@ . rst) ) (list uu up rst)) |
---|
294 | (else #f))) |
---|
295 | ((uu ( #\@ . rst)) (list uu (list) rst)) |
---|
296 | (else #f))) |
---|
297 | |
---|
298 | |
---|
299 | |
---|
300 | ;; RFC3986, section 3.2.2 |
---|
301 | |
---|
302 | (define (host s) (or (ip-literal s) (ipv4-address s) (reg-name s))) |
---|
303 | |
---|
304 | (define (ip-literal s) |
---|
305 | (match s ((#\[ . rst) |
---|
306 | (match (or (ipv6-address rst) (ipv-future rst)) |
---|
307 | ((ua (#\] . rst)) (list ua rst)) |
---|
308 | (else (error 'ip-literal "malformed ip literal" (try-ip-literal->string rst))))) |
---|
309 | (else #f))) |
---|
310 | |
---|
311 | (define ipv-future0 (many ipv-future-char?)) |
---|
312 | |
---|
313 | (define (ipv-future s) |
---|
314 | (match s ((#\v (? hexdigit-char?) #\. . rst) (ipv-future0 rst)) |
---|
315 | (else #f))) |
---|
316 | |
---|
317 | (define char-set:ipv-future |
---|
318 | (char-set-union char-set:unreserved char-set:sub-delims (char-set #\;))) |
---|
319 | |
---|
320 | |
---|
321 | |
---|
322 | ;; Pv6address = 6( h16 ":" ) ls32 |
---|
323 | ;; / "::" 5( h16 ":" ) ls32 |
---|
324 | ;; / [ h16 ] "::" 4( h16 ":" ) ls32 |
---|
325 | ;; / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 |
---|
326 | ;; / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 |
---|
327 | ;; / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 |
---|
328 | ;; / [ *4( h16 ":" ) h16 ] "::" ls32 |
---|
329 | ;; / [ *5( h16 ":" ) h16 ] "::" h16 |
---|
330 | ;; / [ *6( h16 ":" ) h16 ] "::" |
---|
331 | |
---|
332 | ;; ls32 = ( h16 ":" h16 ) / IPv4address |
---|
333 | ;; ; least-significant 32 bits of address |
---|
334 | |
---|
335 | ;; h16 = 1*4HEXDIG |
---|
336 | ;; ; 16 bits of address represented in hexadecimal |
---|
337 | |
---|
338 | |
---|
339 | (define (ipv6-address s) |
---|
340 | (or (match (u6-h4c s) ;; 6( h16 ":" ) ls32 |
---|
341 | |
---|
342 | ((a2 rst) (match (ls32 rst) |
---|
343 | ((a3 rst) (list (append (concatenate a2) a3) rst)) |
---|
344 | (else #f))) |
---|
345 | (else #f)) |
---|
346 | (match s ;; "::" 5( h16 ":" ) ls32 |
---|
347 | ((#\: #\: . rst) |
---|
348 | (match (u5-h4c rst) |
---|
349 | ((a2 rst) (match (ls32 rst) |
---|
350 | ((a3 rst) (list (append (list #\: #\:) (concatenate a2) a3) rst)) |
---|
351 | (else #f))))) |
---|
352 | (else #f)) |
---|
353 | (match (u_opt_n_h4c_h4 0 s) |
---|
354 | ((a1 rst) (match rst |
---|
355 | ((#\: #\: . rst) |
---|
356 | (match (u4-h4c rst) |
---|
357 | ((a2 rst) (match (ls32 rst) |
---|
358 | ((a3 rst) |
---|
359 | (list (append (concatenate a1) (list #\: #\:) |
---|
360 | (concatenate a2) a3) rst)) |
---|
361 | (else #f))) |
---|
362 | (else #f) |
---|
363 | )) |
---|
364 | (else #f))) |
---|
365 | (else #f)) |
---|
366 | (match (u_opt_n_h4c_h4 1 s) |
---|
367 | ((a1 rst) |
---|
368 | (match rst |
---|
369 | ((#\: #\: . rst) |
---|
370 | (match (u3-h4c rst) |
---|
371 | ((a2 rst) (match (ls32 rst) |
---|
372 | ((a3 rst) |
---|
373 | (list (append (concatenate a1) (list #\: #\:) |
---|
374 | (concatenate a2) a3) rst)) |
---|
375 | (else #f))) |
---|
376 | (else #f) |
---|
377 | )) |
---|
378 | (else #f))) |
---|
379 | (else #f)) |
---|
380 | (match (u_opt_n_h4c_h4 2 s) |
---|
381 | ((a1 rst) (match rst |
---|
382 | ((#\: #\: . rst) |
---|
383 | (match (u2-h4c rst) |
---|
384 | ((a2 rst) (match (ls32 rst) |
---|
385 | ((a3 rst) (list (append (concatenate a1) (list #\: #\:) |
---|
386 | (concatenate a2) a3) rst)) |
---|
387 | (else #f))) |
---|
388 | (else #f) |
---|
389 | )) |
---|
390 | (else #f))) |
---|
391 | (else #f)) |
---|
392 | (match (u_opt_n_h4c_h4 3 s) |
---|
393 | ((a1 rst) (match rst |
---|
394 | ((#\: #\: . rst) |
---|
395 | (match (h4c rst) |
---|
396 | ((a2 rst) (match (ls32 rst) |
---|
397 | ((a3 rst) (list (append (concatenate a1) (list #\: #\:) |
---|
398 | (concatenate a2) a3) rst)) |
---|
399 | (else #f))) |
---|
400 | (else #f) |
---|
401 | )) |
---|
402 | (else #f))) |
---|
403 | (else #f)) |
---|
404 | (match (u_opt_n_h4c_h4 4 s) |
---|
405 | ((a1 rst) (match rst |
---|
406 | ((#\: #\: . rst) |
---|
407 | (match (ls32 rst) |
---|
408 | ((a3 rst) (list (append (concatenate a1) (list #\: #\:) a3) rst)) |
---|
409 | (else #f))) |
---|
410 | (else #f))) |
---|
411 | (else #f)) |
---|
412 | (match (u_opt_n_h4c_h4 5 s) |
---|
413 | ((a1 rst) (match rst |
---|
414 | ((#\: #\: . rst) |
---|
415 | (match (h4 rst) |
---|
416 | ((a3 rst) (list (append (concatenate a1) (list #\: #\:) a3) rst)) |
---|
417 | (else #f))) |
---|
418 | (else #f))) |
---|
419 | (else #f)) |
---|
420 | (match (u_opt_n_h4c_h4 6 s) |
---|
421 | ((a1 rst) (match rst |
---|
422 | ((#\: #\: . rst) |
---|
423 | (list (append (concatenate a1) (list #\: #\:)) rst)) |
---|
424 | (else #f))) |
---|
425 | (else #f)) |
---|
426 | (error 'ipv6-address "malformed ipv6 address" (try-ip-literal->string s)))) |
---|
427 | |
---|
428 | |
---|
429 | |
---|
430 | (define (u_opt_n_h4c_h4 n s) |
---|
431 | (match ((consume-min-max 0 n h4c) s) |
---|
432 | ((a1 rst) (match (h4 rst) |
---|
433 | ((a2 rst) (list (append a1 (list a2)) rst)) |
---|
434 | (else #f))) |
---|
435 | (else #f))) |
---|
436 | |
---|
437 | (define (ls32 s) |
---|
438 | (match (h4c s) |
---|
439 | ((a1 rst) (match (h4 rst) |
---|
440 | ((a2 rst) (list (append a1 a2) rst)) |
---|
441 | (else (ipv4-address s)))) |
---|
442 | (else (ipv4-address s)))) |
---|
443 | |
---|
444 | (define (h4c s) |
---|
445 | (match (h4 s) |
---|
446 | ((a1 (#\: (and r1 (not #\:)) . rst)) |
---|
447 | (list (append a1 (list #\:)) (cons r1 rst))) |
---|
448 | (else #f))) |
---|
449 | |
---|
450 | (define u6-h4c (consume-count 6 h4c)) |
---|
451 | (define u5-h4c (consume-count 5 h4c)) |
---|
452 | (define u4-h4c (consume-count 4 h4c)) |
---|
453 | (define u3-h4c (consume-count 3 h4c)) |
---|
454 | (define u2-h4c (consume-count 2 h4c)) |
---|
455 | |
---|
456 | (define h4 (count-min-max 1 4 hexdigit-char?)) |
---|
457 | |
---|
458 | (define (ipv4-address s) |
---|
459 | (match (dec-octet s) |
---|
460 | ((a1 (#\. rst)) |
---|
461 | (match (dec-octet rst) |
---|
462 | ((a2 (#\. rst)) |
---|
463 | (match (dec-octet rst) |
---|
464 | ((a3 (#\. rst)) |
---|
465 | (match (dec-octet rst) |
---|
466 | ((a4 rst) (list (append a1 #\. a2 #\. a3 #\. a4) rst)) |
---|
467 | (else #f))) |
---|
468 | (else #f))) |
---|
469 | (else #f))) |
---|
470 | (else #f))) |
---|
471 | |
---|
472 | (define (dec-char->num c) |
---|
473 | (case c ((#\0) 0) ((#\1) 1) ((#\2) 2) ((#\3) 3) ((#\4) 4) ((#\5) 5) |
---|
474 | ((#\6) 6) ((#\7) 7) ((#\8) 8) ((#\9) 9) (else #f))) |
---|
475 | |
---|
476 | (define (ipv4-octet? lst) |
---|
477 | (let loop ((n (reverse (map dec-char->num lst))) (i 1) (ax 0)) |
---|
478 | (if (null? n) (and (>= ax 0) (<= ax 255)) |
---|
479 | (loop (cdr n) (* i 10) (+ ax (* i (car n))))))) |
---|
480 | |
---|
481 | (define (dec-octet s) |
---|
482 | (match ((count-min-max 1 3 char-numeric?) s) |
---|
483 | (((and a1 (? ipv4-octet?)) rst) (list a1 rst)) |
---|
484 | (else #f))) |
---|
485 | |
---|
486 | (define reg-name |
---|
487 | (count-min-max 0 255 (lambda (c) (or (unreserved-char? c) |
---|
488 | (pct-escaped? c) |
---|
489 | (char-set-contains? char-set:sub-delims c) )))) |
---|
490 | |
---|
491 | ;; RFC3986, section 3.2.3 |
---|
492 | |
---|
493 | (define port0 (many char-numeric?)) |
---|
494 | |
---|
495 | (define (port s) |
---|
496 | (match s ((#\: . rst) (port0 rst)) |
---|
497 | (else #f))) |
---|
498 | |
---|
499 | |
---|
500 | ;; |
---|
501 | ;; RFC3986, section 3.3 |
---|
502 | ;; |
---|
503 | ;; path = path-abempty ; begins with "/" or is empty |
---|
504 | ;; / path-abs ; begins with "/" but not "//" |
---|
505 | ;; / path-noscheme ; begins with a non-colon segment |
---|
506 | ;; / path-rootless ; begins with a segment |
---|
507 | ;; / path-empty ; zero characters |
---|
508 | ;; |
---|
509 | ;; path-abempty = *( "/" segment ) |
---|
510 | ;; path-abs = "/" [ segment-nz *( "/" segment ) ] |
---|
511 | ;; path-noscheme = segment-nzc *( "/" segment ) |
---|
512 | ;; path-rootless = segment-nz *( "/" segment ) |
---|
513 | ;; path-empty = 0<pchar> |
---|
514 | ;; |
---|
515 | ;; segment = *pchar |
---|
516 | ;; segment-nz = 1*pchar |
---|
517 | ;; segment-nzc = 1*( unreserved / pct-encoded / sub-delims / "@" ) |
---|
518 | ;; |
---|
519 | ;; pchar = unreserved / pct-encoded / sub-delims / ":" / "@" |
---|
520 | |
---|
521 | (define (path s) |
---|
522 | (or (path-abempty s) (path-abs s) (path-noscheme s) |
---|
523 | (path-rootless s) (list (list) s))) |
---|
524 | |
---|
525 | |
---|
526 | (define (slash-segment s) |
---|
527 | (match s |
---|
528 | ((#\/ . rst) (match (segment rst) |
---|
529 | ((ss rst) (list (cons #\/ ss) rst)) |
---|
530 | (else #f))) |
---|
531 | (else #f))) |
---|
532 | |
---|
533 | (define pchar (uchar ":@")) |
---|
534 | |
---|
535 | (define segment (many pchar)) |
---|
536 | |
---|
537 | (define segment-nz (many1 pchar)) |
---|
538 | |
---|
539 | (define segment-nzc (many1 (uchar "@"))) |
---|
540 | |
---|
541 | (define path-abempty (consume slash-segment)) |
---|
542 | |
---|
543 | (define (path-abs s) |
---|
544 | (match s |
---|
545 | ((#\/ . rst) (match (path-rootless rst) |
---|
546 | ((() rst) (list (list #\/) rst)) |
---|
547 | ((lst rst) (list (cons (cons #\/ (car lst)) (cdr lst)) rst)) |
---|
548 | (else #f))) |
---|
549 | (else #f))) |
---|
550 | |
---|
551 | (define (path-noscheme s) |
---|
552 | (match (segment-nzc s) |
---|
553 | ((s1 rst) (match (path-abempty rst) |
---|
554 | ((ss rst) (list (cons s1 ss) rst)) |
---|
555 | (else (list (list s1) rst)))) |
---|
556 | (else #f))) |
---|
557 | |
---|
558 | (define (path-rootless s) |
---|
559 | (match (segment-nz s) |
---|
560 | ((s1 rst) (match (path-abempty rst) |
---|
561 | ((ss rst) (list (cons s1 ss) rst)) |
---|
562 | (else #f))) |
---|
563 | (else #f))) |
---|
564 | |
---|
565 | ;; RFC3986, section 3.4 |
---|
566 | |
---|
567 | |
---|
568 | (define query0 (many (schar ":@/?!$'()*+,;="))) |
---|
569 | (define (query1 s) |
---|
570 | (match s ((#\& . rst) (query0 rst)) |
---|
571 | (else #f))) |
---|
572 | |
---|
573 | (define (query s) |
---|
574 | (match (query0 s) |
---|
575 | ((q1 rst) |
---|
576 | (match ((consume query1) rst) |
---|
577 | ((qs rst) (list (cons q1 qs) rst)) |
---|
578 | (else (list (list q1) rst)))) |
---|
579 | (else #f))) |
---|
580 | |
---|
581 | (define query-part (many (schar ":@/?!$'()*+,;"))) |
---|
582 | |
---|
583 | (define (query->string s) |
---|
584 | (match (query-part s) |
---|
585 | ((p1 (#\= . rst)) (match (query-part rst) |
---|
586 | ((p2 _) `(,(uri-char-list->string p1) . ,(uri-char-list->string p2))) |
---|
587 | (else #f))) |
---|
588 | ((p1 ()) `(,(uri-char-list->string p1))) |
---|
589 | (else #f))) |
---|
590 | |
---|
591 | |
---|
592 | ;; RFC3986, section 3.5 |
---|
593 | |
---|
594 | |
---|
595 | (define fragment0 (many (uchar ":@/?"))) |
---|
596 | (define (fragment s) |
---|
597 | (match (fragment0 s) |
---|
598 | ((ss rst) (list ss rst)) |
---|
599 | (else #f))) |
---|
600 | |
---|
601 | ;; Reference, Relative and Absolute URI forms |
---|
602 | ;; |
---|
603 | ;; RFC3986, section 4.1 |
---|
604 | |
---|
605 | (define (uri-reference s) |
---|
606 | (let ((s (if (string? s) (uri-string->char-list s) s))) |
---|
607 | (or (uri s) (relative-ref s)))) |
---|
608 | |
---|
609 | ;; RFC3986, section 4.2 |
---|
610 | ;; |
---|
611 | ;; relative-URI = relative-part [ "?" query ] [ "#" fragment ] |
---|
612 | ;; |
---|
613 | ;; relative-part = "//" authority path-abempty |
---|
614 | ;; / path-abs |
---|
615 | ;; / path-noscheme |
---|
616 | ;; / path-empty |
---|
617 | |
---|
618 | (define (relative-ref s) |
---|
619 | (and (not (scheme s)) |
---|
620 | (match-let* (((ua up rst) (relative-part s)) |
---|
621 | ((uq rst) (match rst ((#\? . rst) (query rst)) |
---|
622 | (else (list #f rst)))) |
---|
623 | ((uf rst) (match rst ((#\# . rst) (fragment rst)) |
---|
624 | (else (list #f rst))))) |
---|
625 | (make-URI #f ua (map uri-char-list->string up) |
---|
626 | (and uq (filter-map query->string uq)) |
---|
627 | (and uf (uri-char-list->string uf)))))) |
---|
628 | (define (relative-part s) |
---|
629 | (match s |
---|
630 | ((#\/ #\/ . rst) |
---|
631 | (match-let* (((ua rst) (authority rst)) |
---|
632 | ((up rst) (path-abempty rst))) |
---|
633 | (list ua up rst))) |
---|
634 | (else (match-let* (((up rst) (or (path-abs s) (path-noscheme s) (list (list) s)))) |
---|
635 | (list #f up rst))))) |
---|
636 | |
---|
637 | |
---|
638 | |
---|
639 | ;; RFC3986, section 4.3 |
---|
640 | |
---|
641 | (define (absolute-uri s) |
---|
642 | (match (scheme s) |
---|
643 | ((us rst) |
---|
644 | (match-let (((ua up rst) (hier-part rst)) |
---|
645 | ((uq rst) (match rst ((#\? . rst) (query rst)) |
---|
646 | (else (list (list) rst))))) |
---|
647 | (make-URI us ua up uq #f))))) |
---|
648 | |
---|
649 | |
---|
650 | ;; Turns a URI into a string. |
---|
651 | ;; |
---|
652 | ;; Uses a supplied function to map the userinfo part of the URI. |
---|
653 | ;; |
---|
654 | |
---|
655 | (define (uri->string uri . rest) |
---|
656 | (let-optionals rest ((userinfomap (lambda (u pw) (string-append u ":******" )))) |
---|
657 | (match uri |
---|
658 | (($ URI scheme authority path query fragment) |
---|
659 | (string-append |
---|
660 | ((lambda (x) (or (and x (string-append (->string x) ":")) "")) scheme) |
---|
661 | (if authority (string-append (uri-auth->string authority userinfomap) "/") "") |
---|
662 | (string-concatenate path) |
---|
663 | (if query (string-concatenate (cons "?" (intersperse query "&"))) "") |
---|
664 | (if fragment (string-append "#" fragment) ""))) |
---|
665 | (else #f)))) |
---|
666 | |
---|
667 | (define (uri-auth->string uri-auth userinfomap) |
---|
668 | (match uri-auth |
---|
669 | (($ URIAuth username password host port) |
---|
670 | (string-append "//" (if (and username password) |
---|
671 | ((lambda (x) (or (and x (string-append x "@")) "")) |
---|
672 | (userinfomap username password)) "") |
---|
673 | host ((lambda (x) (or (and x (string-append ":" (->string x))) "")) |
---|
674 | port))) |
---|
675 | (else #f))) |
---|
676 | |
---|
677 | ; specific: ((uri-authority uri) (uri-path uri) (uri-query uri)). |
---|
678 | |
---|
679 | (define (uri->list uri . rest) |
---|
680 | (let-optionals rest ((userinfomap (lambda (u pw) (string-append u ":******" )))) |
---|
681 | (match uri |
---|
682 | (($ URI scheme authority path query fragment) |
---|
683 | `(,scheme (,(uri-auth->list authority userinfomap) ,path ,query) ,fragment)) |
---|
684 | (else #f)))) |
---|
685 | |
---|
686 | (define (uri-auth->list uri-auth userinfomap) |
---|
687 | (match uri-auth |
---|
688 | (($ URIAuth username password regname port) |
---|
689 | `(,(if (and username password) (userinfomap username password) #f) ,regname ,port )) |
---|
690 | (else #f))) |
---|
691 | |
---|
692 | |
---|
693 | ;; Escape sequence handling |
---|
694 | |
---|
695 | (define (uri-char-list-escape p enc str) |
---|
696 | (reverse |
---|
697 | (fold (lambda (c ax) |
---|
698 | (if (not (p c)) (let* ((os (enc c)) (cs (map pct-escape os))) |
---|
699 | (append (reverse cs) ax)) |
---|
700 | (cons c ax))) |
---|
701 | (list) str))) |
---|
702 | |
---|
703 | ;; Convert a URI character list to a string |
---|
704 | |
---|
705 | (define (uri-char-list->string s) |
---|
706 | (list->string |
---|
707 | (reverse |
---|
708 | (fold (lambda (x ax) |
---|
709 | (cond ((char? x) (cons x ax)) |
---|
710 | ((list? x) (append (reverse x) ax)))) (list) s)))) |
---|
711 | |
---|
712 | ;; Convert a string to a URI character list |
---|
713 | |
---|
714 | (define (uri-string->char-list s) |
---|
715 | (let loop ((cs (list)) (lst (string->list s))) |
---|
716 | (if (null? lst) (reverse cs) |
---|
717 | (match lst |
---|
718 | ((#\% h1 h2 . rst) (loop (cons (list #\% h1 h2) cs) rst)) |
---|
719 | (((and c (? char?)) . rst) (loop (cons c cs) rst)))))) |
---|
720 | |
---|
721 | ;; |
---|
722 | ;; Resolving a relative URI relative to a base URI |
---|
723 | ;; |
---|
724 | ;; Returns a new URI which represents the value of the first URI |
---|
725 | ;; interpreted as relative to the second URI. |
---|
726 | ;; |
---|
727 | ;; For example: |
---|
728 | ;; |
---|
729 | ;; (uri->string (relative-to (uri-reference "foo") (uri "http://bar.org/")) ) |
---|
730 | ;; => "http://bar.org/foo" |
---|
731 | ;; |
---|
732 | ;; (uri->string (non-strict-relative-to (uri "http:foo") (uri "http://bar.org/")) ) |
---|
733 | ;; => "http://bar.org/foo" |
---|
734 | ;; |
---|
735 | ;; Algorithm from RFC3986, section 5.2.2 |
---|
736 | ;; |
---|
737 | |
---|
738 | (define (uri-relative-to ref base) |
---|
739 | (and (uri? ref) (uri? base) |
---|
740 | (cond ((uri-scheme ref) (just-segments ref)) |
---|
741 | ((uri-authority ref) (let ((x (just-segments ref))) |
---|
742 | (URI-scheme-set! x (uri-scheme base)) |
---|
743 | x)) |
---|
744 | |
---|
745 | (((lambda (p) (and (not (null? p)) p)) (uri-path ref)) => |
---|
746 | (lambda (ref-path) |
---|
747 | (if (and (pair? ref-path) (string-prefix? "/" (car ref-path))) |
---|
748 | (let ((x (just-segments ref))) |
---|
749 | (URI-scheme-set! x (uri-scheme base)) |
---|
750 | (URI-authority-set! x (uri-auth base)) |
---|
751 | x) |
---|
752 | (let ((x (udup ref))) |
---|
753 | (URI-scheme-set! x (uri-scheme base)) |
---|
754 | (URI-authority-set! x (uri-auth base)) |
---|
755 | (URI-path-set! x (merge-paths base x)) |
---|
756 | (just-segments x))))) |
---|
757 | |
---|
758 | ((uri-query ref) (let ((x (udup ref))) |
---|
759 | (URI-scheme-set! x (uri-scheme base)) |
---|
760 | (URI-authority-set! x (uri-auth base)) |
---|
761 | (URI-path-set! x (list "/")) |
---|
762 | (URI-path-set! x (merge-paths base x)) |
---|
763 | (just-segments x))) |
---|
764 | |
---|
765 | (else (let ((x (just-segments ref))) |
---|
766 | (URI-scheme-set! x (uri-scheme base)) |
---|
767 | (URI-authority-set! x (uri-auth base)) |
---|
768 | (URI-path-set! x (uri-path base)) |
---|
769 | (URI-query-set! x (uri-query base)) |
---|
770 | x))))) |
---|
771 | |
---|
772 | (define (just-segments u) |
---|
773 | (let ((p (remove-dot-segments (uri-path u)))) |
---|
774 | (make-URI (uri-scheme u) (uri-auth u) p |
---|
775 | (uri-query u) (uri-fragment u)))) |
---|
776 | |
---|
777 | (define (merge0 pb pr) |
---|
778 | (let* ((rpb (reverse pb)) |
---|
779 | (pb1 (reverse (if (not (string=? (car rpb) "/")) (cdr rpb) rpb))) |
---|
780 | (pr1 (or (and (pair? pr) (not (string-prefix? ".." (car pr))) (not (string-prefix? "." (car pr))) |
---|
781 | (not (string-prefix? "/" (car pr))) |
---|
782 | (cons (string-append "/" (car pr)) (cdr pr))) |
---|
783 | pr))) |
---|
784 | (append pb1 pr1))) |
---|
785 | |
---|
786 | (define (merge-paths b r) |
---|
787 | (let ((ba (uri-authority b)) |
---|
788 | (pb (uri-path b)) |
---|
789 | (pr (uri-path r))) |
---|
790 | (let ((mp (if (and ba (null? pb)) (cons "/" pr) (merge0 pb pr)))) |
---|
791 | mp))) |
---|
792 | |
---|
793 | (define (uri-non-strict-relative-to ref base) |
---|
794 | (let ((rs (uri-scheme ref)) |
---|
795 | (rb (uri-scheme base))) |
---|
796 | (let ((ref1 (make-URI (if (eq? rs rb) #f (uri-scheme ref)) |
---|
797 | (uri-auth ref) (uri-path ref) |
---|
798 | (uri-query ref) (uri-fragment ref)))) |
---|
799 | (uri-relative-to ref1 base)))) |
---|
800 | |
---|
801 | |
---|
802 | ;; Remove dot segments, but protect leading '/' character |
---|
803 | |
---|
804 | (define (remove-dot-segments ps) |
---|
805 | (match ps (("/" . rst) (cons "/" (elim-dots rst))) |
---|
806 | (else (elim-dots ps)))) |
---|
807 | |
---|
808 | (define (elim-dots ps) |
---|
809 | (let loop ((ps ps) (lst (list))) |
---|
810 | (if (null? ps) (reverse lst) |
---|
811 | (match ps |
---|
812 | (((or "." "/.")) |
---|
813 | (loop (list) (if (and (pair? lst) (string=? "/" (car lst))) (cdr lst) (cons "/" lst)))) |
---|
814 | (((or "." "/.") . rst) |
---|
815 | (loop rst (if (and (pair? lst) (string=? "/" (car lst))) (cdr lst) lst))) |
---|
816 | (((or ".." "/..")) |
---|
817 | (loop (list) (if (pair? lst) (cons "/" (cdr lst)) lst))) |
---|
818 | (((or ".." "/..") . rst) |
---|
819 | (loop rst (if (pair? lst) (cdr lst) lst))) |
---|
820 | ((x . rst) (loop rst (cons x lst))))))) |
---|
821 | |
---|
822 | ;; |
---|
823 | ;; Finding a URI relative to a base URI |
---|
824 | ;; |
---|
825 | ;; Returns a new URI which represents the relative location of the |
---|
826 | ;; first URI with respect to the second URI. Thus, the values |
---|
827 | ;; supplied are expected to be absolute URIs, and the result returned |
---|
828 | ;; may be a relative URI. |
---|
829 | ;; |
---|
830 | ;; Example: |
---|
831 | ;; |
---|
832 | ;; (uri->string |
---|
833 | ;; (uri-relative-from (uri "http://example.com/Root/sub1/name2#frag") |
---|
834 | ;; (uri "http://example.com/Root/sub2/name2#frag"))) |
---|
835 | ;; ==> "../sub1/name2#frag" |
---|
836 | ;; |
---|
837 | |
---|
838 | |
---|
839 | (define (uri-relative-from uabs base) |
---|
840 | (cond ((ucdiff? uri-scheme uabs base) (udup uabs)) |
---|
841 | ((ucdiff? uri-authority uabs base) (let ((x (udup uabs))) |
---|
842 | (URI-scheme-set! x #f) |
---|
843 | x)) |
---|
844 | ((ucdiff? uri-path uabs base) |
---|
845 | (let ((x (udup uabs)) |
---|
846 | (path (rel-path-from (remove-body-dot-segments (uri-path uabs)) |
---|
847 | (remove-body-dot-segments (uri-path base))))) |
---|
848 | (URI-scheme-set! x #f) |
---|
849 | (URI-authority-set! x #f) |
---|
850 | (URI-path-set! x path) |
---|
851 | x)) |
---|
852 | ((ucdiff? uri-query uabs base) |
---|
853 | (let ((x (udup uabs))) |
---|
854 | (URI-scheme-set! x #f) |
---|
855 | (URI-authority-set! x #f) |
---|
856 | (URI-path-set! x (list)) |
---|
857 | x)) |
---|
858 | (else |
---|
859 | (let ((x (udup uabs))) |
---|
860 | (URI-scheme-set! x #f) |
---|
861 | (URI-authority-set! x #f) |
---|
862 | (URI-query-set! x #f) |
---|
863 | (URI-path-set! x (list)) |
---|
864 | x)))) |
---|
865 | |
---|
866 | |
---|
867 | (define (udup u) |
---|
868 | (make-URI (uri-scheme u) (uri-auth u) (uri-path u) |
---|
869 | (uri-query u) (uri-fragment u))) |
---|
870 | |
---|
871 | (define (ucdiff? sel u1 u2) |
---|
872 | (let ((s1 (sel u1)) |
---|
873 | (s2 (sel u2))) |
---|
874 | (not (cond ((and (URIAuth? s1) (URIAuth? s2)) |
---|
875 | (not (or (ucdiff? uri-username u1 u2) (ucdiff? uri-host u1 u2) (ucdiff? uri-port u1 u2)))) |
---|
876 | ((and (list? s1) (list? s2)) (every string=? s1 s2)) |
---|
877 | ((and (string? s1) (string? s2)) (string=? s1 s2)) |
---|
878 | (else (eq? s1 s2)))))) |
---|
879 | |
---|
880 | (define (remove-body-dot-segments p) |
---|
881 | (or (and (pair? p) |
---|
882 | (let ((r (reverse p))) |
---|
883 | (reverse (cons (car r) (remove-dot-segments (cdr r)))))) |
---|
884 | p)) |
---|
885 | |
---|
886 | (define (rel-path-from pabs base) |
---|
887 | (cond ((null? pabs) (list "/")) |
---|
888 | ((null? base) pabs) |
---|
889 | ;; Construct a relative path segment if the paths share a |
---|
890 | ;; leading segment other than a leading '/' |
---|
891 | (else (match (list pabs base) |
---|
892 | (((sa1 . (and ra1 (sa2 . ra2))) (sb1 . (and rb1 (sb2 . rb2)))) |
---|
893 | (if (string=? sa1 sb1) |
---|
894 | (make-rel-path |
---|
895 | (if (string=? "/" sa1) |
---|
896 | (if (string=? sa2 sb2) (rel-path-from1 ra2 rb2) pabs) |
---|
897 | (rel-path-from1 ra1 rb1))) |
---|
898 | pabs)) |
---|
899 | (((sa1) (sb1 . rb1)) |
---|
900 | (if (string=? sa1 sb1) (rel-segs-from (list) rb1) |
---|
901 | pabs)))))) |
---|
902 | |
---|
903 | (define (make-rel-path x) |
---|
904 | (if (pair? x) (if (string-prefix? "/" (car x)) (cons "." x) x) x)) |
---|
905 | |
---|
906 | ;; rel-path-from1 strips off trailing names from the supplied paths, |
---|
907 | |
---|
908 | (define (rel-path-from1 pabs base) |
---|
909 | (match-let* (((na . sa) (reverse pabs)) |
---|
910 | ((nb . sb) (reverse base))) |
---|
911 | (let ((rp (rel-segs-from (reverse sa) (reverse sb)))) |
---|
912 | (if (null? rp) (cond ((string=? na nb) (list)) |
---|
913 | ((protect? na) (list (string-append "./" na))) |
---|
914 | (else (list na))) |
---|
915 | (append rp (list na)))))) |
---|
916 | |
---|
917 | |
---|
918 | (define (protect? sa) (or (string-null? sa) (string-contains sa ":"))) |
---|
919 | |
---|
920 | |
---|
921 | |
---|
922 | ;; rel-segs-from discards any common leading segments from both paths, |
---|
923 | ;; then invokes dif-segs-from to calculate a relative path from the end |
---|
924 | ;; of the base path to the end of the target path. The final name is |
---|
925 | ;; handled separately, so this deals only with "directory" segments. |
---|
926 | |
---|
927 | (define (rel-segs-from sabs base) |
---|
928 | (cond ((and (null? sabs) (null? base)) (list)) |
---|
929 | ((or (null? sabs) (null? base)) (dif-segs-from sabs base)) |
---|
930 | (else (match-let (((sa1 . ra1) sabs) |
---|
931 | ((sb1 . rb1) base)) |
---|
932 | (if (string=? sa1 sb1) |
---|
933 | (rel-segs-from ra1 rb1) |
---|
934 | (dif-segs-from sabs base)))))) |
---|
935 | |
---|
936 | ;; dif-segs-from calculates a path difference from base to target, |
---|
937 | ;; not including the final name at the end of the path (i.e. results |
---|
938 | ;; always ends with '/') |
---|
939 | ;; |
---|
940 | ;; This function operates under the invariant that the supplied value |
---|
941 | ;; of sabs is the desired path relative to the beginning of base. |
---|
942 | ;; Thus, when base is empty, the desired path has been found. |
---|
943 | |
---|
944 | (define (dif-segs-from sabs base) |
---|
945 | (if (null? base) sabs (dif-segs-from (cons ".." sabs) (cdr base)))) |
---|
946 | |
---|
947 | |
---|
948 | |
---|
949 | ;; Other normalization functions |
---|
950 | ;; |
---|
951 | ;; Case normalization; cf. RFC3986 section 6.2.2.1 |
---|
952 | ;; NOTE: authority case normalization is not performed |
---|
953 | |
---|
954 | (define (uri-normalize-case uri) |
---|
955 | (let ((u1 (udup uri)) |
---|
956 | (scheme (string->symbol (string-downcase (->string (uri-scheme uri))))) |
---|
957 | (path (map (lambda (c) (match c (('% h1 h2) `(% ,(char-upcase h1) ,(char-upcase h2))) |
---|
958 | (else c))) (uri-path uri)))) |
---|
959 | (URI-scheme-set! u1 scheme) |
---|
960 | (URI-path-set! u1 path) |
---|
961 | u1)) |
---|
962 | |
---|
963 | |
---|
964 | ;; Path segment normalization; cf. RFC3986 section 6.2.2.4 |
---|
965 | |
---|
966 | (define (uri-normalize-path-segments uri) |
---|
967 | (let ((u1 (udup uri)) |
---|
968 | (path (remove-dot-segments (uri-path uri)))) |
---|
969 | (URI-path-set! u1 path) |
---|
970 | u1)) |
---|