aboutsummaryrefslogtreecommitdiff
path: root/decoders.lisp
blob: 7f581a54ec49aa5869312547ed68ec6c4a9310b6 (plain)
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
;;;; lazybones.decoders package.

(in-package #:lazybones.decoders)

;;; HELPERS

(defun read-body-to-string (stream content-length)
  "Reads CONTENT-LENGTH characters from STREAM and returns a string."
  (let ((string (make-string content-length)))
    (read-sequence string stream)
    string))


(defun binary-content-p (content-type)
  (or (alexandria:starts-with-subseq "image" content-type)
      (alexandria:starts-with-subseq "audio" content-type)
      (and (alexandria:starts-with-subseq "application" content-type)
           (not (equal content-type "application/json")))
      (alexandria:starts-with-subseq "video" content-type)))


(defun butlast-to-string (res)
  (map 'string 'identity (butlast res)))

(defun make-keyword (str)
  (read-from-string (format nil ":~a" str)))

(defun write-binary-to-tmp-file (body)
  (cl-fad:with-output-to-temporary-file (out-file :element-type '(unsigned-byte 8))
    (loop :for char :across body :do (write-byte (char-int char) out-file))))

;;; PLAIN TEXT DECODER

(defun decode-text/plain (stream content-type content-length)
  (declare (ignore content-type))
  (read-body-to-string stream content-length))

(add-decoder "text/plain" #'decode-text/plain)


;;; JSON DECODER

(defun decode-application/json (stream content-type content-length)
  "Reads LEN characters from stream and decodes them as JSON, returning a PLIST"
  (declare (ignore content-type))
  (jonathan:parse (read-body-to-string stream content-length)))

(add-decoder "application/json" #'decode-application/json)


;;; MULTIPART/FORM-DATA DECODER

;; Temporarily justing the hunchentoot post-parameter feature
(add-decoder "multipart/form-data"
             (lambda (&rest ignore)
               (declare (ignore ignore))
               (loop :for (k . v) :in (hunchentoot:post-parameters*)
                     :when (and  (listp v) (= 3 (length v)))
                       :collect (list :name k
                                      :body (first v)
                                      :filename (second v)
                                      :content-type (third v))
                     :collect (list :name k :body v))))

;;; APPLICATION/X-WWW-FORM-URLENCODED

(defun decode-application/x-www-form-urlencoded (stream content-type content-length)
  (declare (ignore content-type))
  (->> (read-body-to-string stream content-length)
       (split-sequence #\&)
       (mapcar (lambda (s) (split-sequence #\= s)))
       (as->* pairs
              (loop
                 :for (key undecoded) :in pairs
                 :appending (list (make-keyword key)
                                  (urldecode undecoded :queryp t))))))

(add-decoder "application/x-www-form-urlencoded"
             #'decode-application/x-www-form-urlencoded)