|
15 | 15 | (defconstant +ascii-printable-end+ 126) |
16 | 16 | ;; C0 control upper bound (US, 31) used to reject control chars except HTAB. |
17 | 17 | (defconstant +ascii-control-end+ 31) |
| 18 | +(defconstant +invalid-media-range-specificity+ -1) |
18 | 19 |
|
19 | | -(defparameter *default-response-accept* '(:|text/plain| ("q" "1.0"))) |
| 20 | +(defparameter *default-response-accept* '(:|text/plain| ("q" . "1.0"))) |
20 | 21 |
|
21 | 22 | (defgeneric respond-with (implementation content request response) |
22 | | - (:documentation "Render CONTENT according to IMPLEMENTATION (a selected media type).") |
| 23 | + (:documentation "Render CONTENT according to IMPLEMENTATION (a selected media type). |
| 24 | +
|
| 25 | +The default method returns RESPONSE unchanged.") |
23 | 26 | (:method ((implementation t) content request response) |
24 | | - content)) |
| 27 | + (declare (ignore implementation content request)) |
| 28 | + response)) |
25 | 29 |
|
26 | 30 | (defun %find-response-accept-for-type (response-accepts media-type) |
27 | 31 | (if (string-equal media-type "*") |
|
37 | 41 | (media-type (first mime-sub)) |
38 | 42 | (media-subtype (second mime-sub))) |
39 | 43 | (cond |
40 | | - ((and (string-equal "*" media-type) |
| 44 | + ((and media-type media-subtype |
| 45 | + (string-equal "*" media-type) |
41 | 46 | (string-equal "*" media-subtype)) |
42 | 47 | (car response-accepts)) |
43 | | - ((string-equal "*" media-subtype) |
| 48 | + ((and media-subtype |
| 49 | + (string-equal "*" media-subtype)) |
44 | 50 | (%find-response-accept-for-type response-accepts media-type)) |
45 | 51 | (t |
46 | 52 | (find (car request-accept) response-accepts :test #'eq))))) |
@@ -129,13 +135,33 @@ REQUEST-ACCEPTS must be ordered by preference (for example, output from |
129 | 135 | (defun %process-request-accepts (request-accepts) |
130 | 136 | (labels ((get-accept-entry-quality-value (entry) |
131 | 137 | (or (find-if (lambda (item) (string-equal (car item) "q")) |
132 | | - entry) |
133 | | - '("q" . "1.0")))) |
| 138 | + entry) |
| 139 | + '("q" . "1.0"))) |
| 140 | + (media-range-specificity (entry) |
| 141 | + (let* ((parts (split "/" (string (car entry)))) |
| 142 | + (media-type (first parts)) |
| 143 | + (media-subtype (second parts))) |
| 144 | + (cond |
| 145 | + ((and media-type media-subtype |
| 146 | + (string-equal media-type "*") |
| 147 | + (string-equal media-subtype "*")) |
| 148 | + 0) |
| 149 | + ((and media-subtype |
| 150 | + (string-equal media-subtype "*")) |
| 151 | + 1) |
| 152 | + ((and media-type media-subtype) |
| 153 | + 2) |
| 154 | + (t |
| 155 | + +invalid-media-range-specificity+))))) |
134 | 156 | (sort request-accepts |
135 | 157 | (lambda (a b) |
136 | 158 | (let* ((qa (get-accept-entry-quality-value (cdr a))) |
137 | | - (qb (get-accept-entry-quality-value (cdr b)))) |
138 | | - (> (serapeum:parse-float (cdr qa)) (serapeum:parse-float (cdr qb)))))))) |
| 159 | + (qb (get-accept-entry-quality-value (cdr b))) |
| 160 | + (qa-value (serapeum:parse-float (cdr qa))) |
| 161 | + (qb-value (serapeum:parse-float (cdr qb)))) |
| 162 | + (if (= qa-value qb-value) |
| 163 | + (> (media-range-specificity a) (media-range-specificity b)) |
| 164 | + (> qa-value qb-value))))))) |
139 | 165 |
|
140 | 166 | (defun parse-request-accept (accept-header) |
141 | 167 | "Parse an HTTP Accept header into media-range entries. |
|
0 commit comments