-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
uri.lisp
153 lines (131 loc) · 5.65 KB
/
uri.lisp
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
(in-package #:org.shirakumo.radiance.core)
(declaim (ftype (function (T) (or (integer 0 65535) null)) port))
(declaim (ftype (function ((or (integer 0 65535) null) T) T) (setf port)))
(defclass uri ()
((domains :initarg :domains :accessor domains)
(port :initarg :port :accessor port)
(path :initarg :path :accessor path)
(matcher :initarg :matcher :accessor matcher))
(:default-initargs
:domains ()
:port NIL
:path NIL
:matcher NIL))
(defmethod shared-initialize :after ((uri uri) slots &key)
(declare (ignore slots))
(when (eql (matcher uri) T)
(setf (matcher uri) (cl-ppcre:create-scanner (or (path uri) "")))))
(defun uri-string (uri)
(format NIL "~{~a~^.~}~@[:~a~]/~@[~a~]"
(reverse (domains uri)) (port uri) (path uri)))
(defmethod print-object ((uri uri) stream)
(format stream "#@~s" (uri-string uri))
uri)
(defmethod make-load-form ((uri uri) &optional env)
(declare (ignore env))
`(make-uri :domains ',(domains uri) :port ,(port uri) :path ,(path uri)))
(defmethod (setf path) :after (val (uri uri))
(when (matcher uri)
(setf (matcher uri) (cl-ppcre:create-scanner (or (path uri) "")))))
(declaim (inline make-uri))
(defun make-uri (&key domains port path matcher)
(make-instance 'uri :domains domains :port port :path path :matcher matcher))
(declaim (inline ensure-uri))
(defun ensure-uri (uri-ish)
(etypecase uri-ish
(uri uri-ish)
(string (parse-uri uri-ish))))
(defun copy-uri (uri)
(etypecase uri
(uri (make-uri :domains (copy-seq (domains uri))
:port (port uri)
:path (path uri)
:matcher (matcher uri)))
(string (parse-uri uri))))
(defvar *uri-regex* (cl-ppcre:create-scanner "^(([a-z0-9\\-]+\\.)*[a-z0-9\\-]+)?(:(\\d{1,5}))?/(.*)" :case-insensitive-mode T))
(defun parse-uri (uri-string)
(or (cl-ppcre:register-groups-bind (domains NIL NIL port path) (*uri-regex* uri-string)
(make-uri :domains (when domains (nreverse (cl-ppcre:split "\\." (string-downcase domains))))
:port (when port (parse-integer port))
:path path))
(error 'unparsable-uri-string :string uri-string)))
(defun read-uri (stream char arg)
(declare (ignore char arg))
(parse-uri (read stream)))
(set-dispatch-macro-character #\# #\@ #'read-uri)
(defvar *default-uri-defaults* (parse-uri "/"))
(defun uri< (a b)
(let ((a (ensure-uri a))
(b (ensure-uri b)))
(or (and (not (port a)) (port b))
(and (not (port a))
(or (< (length (domains a)) (length (domains b)))
(and (= (length (domains a)) (length (domains b)))
(< (length (path a)) (length (path b)))))))))
(defun uri> (a b)
(uri< b a))
(defun uri= (a b)
(let ((a (ensure-uri a))
(b (ensure-uri b)))
(and (eql (port a) (port b))
(equal (path a) (path b))
(= (length (domains a)) (length (domains b)))
(loop for a in (domains a)
for b in (domains b)
always (string-equal a b)))))
(defun uri-matches (uri pattern-uri)
(declare (optimize (speed 3)))
(let ((uri (ensure-uri uri))
(pattern-uri (ensure-uri pattern-uri)))
(unless (matcher pattern-uri)
(setf (matcher pattern-uri) (cl-ppcre:create-scanner (or (path pattern-uri) ""))))
(and (or (not (port pattern-uri))
(not (port uri))
(= (port uri) (port pattern-uri)))
(<= (length (domains pattern-uri)) (length (domains uri)))
(loop for a in (domains uri)
for b in (domains pattern-uri)
always (string-equal a b))
(not (null (cl-ppcre:scan (matcher pattern-uri) (or (path uri) "")))))))
(defun merge-uris (uri &optional (defaults *default-uri-defaults*))
(let ((uri (ensure-uri uri))
(defaults (ensure-uri defaults)))
(make-uri
:domains (append (domains uri) (domains defaults))
:port (or (port uri) (port defaults))
:path (format NIL "~@[~a/~]~@[~a~]"
(or* (path defaults)) (path uri)))))
(defun represent-uri (uri representation)
(ecase representation
((:as-is NIL) (copy-uri uri))
((:external :reverse) (external-uri uri))
((:internal :map) (internal-uri uri))))
(defun uri-to-url (uri &key (representation :as-is)
schema
query
fragment)
(let* ((uri (represent-uri uri representation))
(schema (or schema
(cond ((eql 443 (port uri)) "https")
((boundp '*request*) (header "X-Forwarded-Proto")))
"http"))
(port (case (port uri)
((443 80) NIL)
(T (port uri)))))
(if (domains uri)
(format NIL "~a://~{~a~^.~}~@[:~a~]/~/radiance-core::format-urlpart/~@[?~/radiance-core::format-query/~]~@[#~/radiance-core::format-urlpart/~]"
schema (reverse (domains uri)) port (or (path uri) "") query fragment)
(format NIL "/~/radiance-core::format-urlpart/~@[?~/radiance-core::format-query/~]~@[#~/radiance-core::format-urlpart/~]"
(or (path uri) "") query fragment))))
(defun make-url (&key domains port path schema query fragment (representation :external))
(uri-to-url (make-uri :domains domains
:port port
:path path)
:representation representation
:schema schema
:query query
:fragment fragment))
(defun format-uri (uri-string &rest format-args)
(parse-uri (apply #'format NIL uri-string format-args)))
(define-compiler-macro format-uri (uri-string &rest args)
`(parse-uri (format NIL ,uri-string ,@args)))