Commit c210e4a9a302b281d293ab0346344acf8ea477c7

Thomas de Grivel 2018-06-08T00:59:33

file and mime-type

diff --git a/mime.lisp b/mime.lisp
new file mode 100644
index 0000000..d24bf5a
--- /dev/null
+++ b/mime.lisp
@@ -0,0 +1,61 @@
+
+(in-package :thot)
+
+(defpackage :file-extensions
+  #.(let (symbols)
+      (when (find-package :file-extensions)
+	(do-external-symbols (s :file-extensions)
+	  (push s symbols)))
+      `(:export ,@symbols)))
+
+(defgeneric ext (x))
+
+(defmethod ext ((x string))
+  (cond ((string= "" x))
+        ((char= #\. (char x 0))
+         (let ((sym (intern x :file-extensions)))
+           (export sym :file-extensions)
+           sym))
+        (t
+         (ext (str #\. x)))))
+
+(defmethod ext ((x symbol))
+  (ext (symbol-name x)))
+
+(defun mime-type-p (symbol)
+  (find #\/ (symbol-name symbol)))
+
+(defvar *mime-types*
+  (make-hash-table :test 'eq))
+
+(defmacro mime-type (ext)
+  `(gethash ,ext *mime-types*))
+
+(defun safe-read (stream eof)
+  (let ((*read-eval* nil)
+        (*readtable* (copy-readtable nil)))
+    (flet ((read-comment (stream char)
+             (declare (ignore char))
+             (cl:read-line stream)
+             (cl:read stream nil nil t)))
+      (set-macro-character #\# #'read-comment)
+      (cl:read stream nil eof))))
+
+(defun load-mime.types (path)
+  (let ((in (cl:open path)))
+    (unwind-protect
+         (let ((eof (gensym))
+               (mime-type nil))
+           (loop
+              (let ((sym (safe-read in eof)))
+                (cond ((eq eof sym) (return))
+                      ((not (symbolp sym)))
+                      ((mime-type-p sym) (setf mime-type sym))
+                      (mime-type (let ((ext (ext sym)))
+                                   (format t "~S ~S~%" ext mime-type)
+                                   (setf (mime-type ext)
+                                         mime-type)))))))
+      (cl:close in))))
+
+#+openbsd
+(load-mime.types "/usr/share/misc/mime.types")
diff --git a/thot.asd b/thot.asd
index 1043f50..70a829d 100644
--- a/thot.asd
+++ b/thot.asd
@@ -28,7 +28,8 @@
                "unistd-stream")
   :components
   ((:file "package")
-   (:file "thot" :depends-on ("package"))
+   (:file "mime" :depends-on ("package"))
+   (:file "thot" :depends-on ("mime"))
    (:file "thot-simple" :depends-on ("thot"))
    (:file "thot-threaded" :depends-on ("thot-simple"))
    #+linux (:file "thot-epoll" :depends-on ("thot-threaded"))))
diff --git a/thot.lisp b/thot.lisp
index ff0d4fd..2eee9f0 100644
--- a/thot.lisp
+++ b/thot.lisp
@@ -414,11 +414,57 @@ The requested url "
         (when (debug-p :directory)
           (format t "subdir ~S~%" subdir)
           (force-output))
-        (when (probe-dir (str local subdir))
-          `(directory-index ,local ,remote ,subdir))))))
+        (with-stat (stat nil) local-path
+          (when (s-isdir (stat-mode stat))
+            `(directory-index ,local ,remote ,subdir)))))))
+
+(defun fd-file-size (fd)
+  (let ((end (unistd:lseek fd 0 unistd:+seek-end+)))
+    (unistd:lseek fd 0 unistd:+seek-set+)
+    end))
+
+(defun stream-file-size (stream)
+  (fd-file-size (stream-fd stream)))
+
+(defun path-name (path)
+  (let ((start (position #\/ path :from-end t)))
+    (when start
+      (subseq path (1+ start)))))
+
+(defun path-extension (path)
+  (let* ((name (path-name path))
+         (start (position #\. name :from-end t)))
+    (when start
+      (ext (subseq  path start)))))
+
+(defun file (path)
+  (let* ((ext (path-extension path))
+         (type (mime-type ext)))
+    (header "Content-Type: " type)
+    (with-stream (in (unistd-stream-open path :read t))
+      (let ((size (stream-file-size in)))
+        (header "Content-Length: " size))
+      (end-headers)
+      (stream-copy in (reply-stream)))))
+
+(defun file-handler (local remote)
+  (let ((uri (request-uri)))
+    (when (debug-p :file)
+      (format t "uri ~S local ~S remote ~S~%" uri local remote)
+      (force-output))
+    (when (prefix-p remote uri)
+      (let* ((path (subseq uri (length remote)))
+             (local-path (str local path)))
+        (when (debug-p :file)
+          (format t "local-path ~S~%" local-path)
+          (force-output))
+        (with-stat (stat nil) local-path
+          (when (s-isreg (stat-mode stat))
+            `(file ,local-path)))))))
 
 (defparameter *url-handlers*
-  '((directory-handler "/" "/")
+  '((file-handler "/var/www/htdocs/" "/")
+    (directory-handler "/var/www/htdocs/" "/")
     (404-not-found-handler)))
 
 (defun call (list)