-
-
Save holyjak/36c6284c047ffb7573e8a34399de27d8 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bb | |
#_" -*- mode: clojure; -*-" | |
;; Based on https://github.com/babashka/babashka/blob/master/examples/image_viewer.clj | |
(ns http-server | |
(:require [babashka.fs :as fs] | |
[clojure.java.browse :as browse] | |
[clojure.string :as str] | |
[clojure.tools.cli :refer [parse-opts]] | |
[org.httpkit.server :as server] | |
[hiccup2.core :as html]) | |
(:import [java.net URLDecoder URLEncoder])) | |
(def cli-options [["-p" "--port PORT" "Port for HTTP server" :default 8090 :parse-fn #(Integer/parseInt %)] | |
["-d" "--dir DIR" "Directory to serve files from" :default "."] | |
["-h" "--help" "Print usage info"]]) | |
(def parsed-args (parse-opts *command-line-args* cli-options)) | |
(def opts (:options parsed-args)) | |
(cond | |
(:help opts) | |
(do (println "Start a http server for static files in the given dir. Usage:\n" (:summary parsed-args)) | |
(System/exit 0)) | |
(:errors parsed-args) | |
(do (println "Invalid arguments:\n" (str/join "\n" (:errors parsed-args))) | |
(System/exit 1)) | |
:else | |
:continue) | |
(def port (:port opts)) | |
(def dir (fs/path (:dir opts))) | |
(assert (fs/directory? dir) (str "The given dir `" dir "` is not a directory.")) | |
(defn index [f] | |
(let [files (map #(str (.relativize dir %)) | |
(fs/list-dir f))] | |
{:body (-> [:html | |
[:head | |
[:meta {:charset "UTF-8"}] | |
[:title (str "Index of `" f "`")]] | |
[:body | |
[:h1 "Index of " [:code (str f)]] | |
[:ul | |
(for [child files] | |
[:li [:a {:href (URLEncoder/encode (str child))} child (when (fs/directory? (fs/path dir child)) "/")]])] | |
[:hr] | |
[:footer {:style {"text-align" "center"}} "Served by http-server.clj"]]] | |
html/html | |
str)})) | |
(defn body [path] | |
{:body (fs/file path)}) | |
(server/run-server | |
(fn [{:keys [:uri]}] | |
(let [f (fs/path dir (str/replace-first (URLDecoder/decode uri) #"^/" "")) | |
index-file (fs/path f "index.html")] | |
(cond | |
(and (fs/directory? f) (fs/readable? index-file)) | |
(body index-file) | |
(fs/directory? f) | |
(index f) | |
(fs/readable? f) | |
(body f) | |
:else | |
{:status 404 :body (str "Not found `" f "` in " dir)}))) | |
{:port port}) | |
(println "Starting http server at " port "for" (str dir)) | |
(browse/browse-url (format "http://localhost:%s/" port)) | |
@(promise) |
Hello @borkdude. Thanks for your help.
bb -e '(babashka.fs/glob "." "*")'
"freezes" (see bellow).bb -e '(babashka.fs/list-dir ".")
works and is ultra-fast (0,01s user 0,01s system 105% cpu 0,016 total
)
I tried using strace
for 1) and it seems that glob "." "*"
is actually traversing nested directories. So it's not actually frozen, it would just take ages (and a lot of memory) to get the result.
strace bb -e '(babashka.fs/glob "." "*")' 2>&1 | grep "/home/brdloush" | grep -e openat -e lstat
it very quickly shows output such as this..
openat(AT_FDCWD, "/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect", O_RDONLY) = 24
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit.js", {st_mode=S_IFREG|0664, st_size=26016, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
openat(AT_FDCWD, "/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit", O_RDONLY) = 26
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/caching.js", {st_mode=S_IFREG|0664, st_size=4409, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/handlers.js", {st_mode=S_IFREG|0664, st_size=12612, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/impl", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
openat(AT_FDCWD, "/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/impl", O_RDONLY) = 28
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/impl/decoder.js", {st_mode=S_IFREG|0664, st_size=12914, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/impl/reader.js", {st_mode=S_IFREG|0664, st_size=2131, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/impl/writer.js", {st_mode=S_IFREG|0664, st_size=18656, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/delimiters.js", {st_mode=S_IFREG|0664, st_size=1062, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/types.js", {st_mode=S_IFREG|0664, st_size=37079, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/eq.js", {st_mode=S_IFREG|0664, st_size=5804, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/com/cognitect/transit/util.js", {st_mode=S_IFREG|0664, st_size=4881, ...}) = 0
lstat("/home/brdloush/./projects/reframe-demo/resources/public/js/compiled/test/out/oops", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
Thanks for checking. Glob should actually not recurse with just *
but there might be a bug in the impl:
I will check.
Found the issue. Due to an ordering mistake the glob is always recursive. I tracked it here and fixed it. Will be fixed in the next release of babashka. For now you can use glob with :max-depth 1
or fs/list-dir
.
Great job troubleshooting this, @brdloush! Thank you for teaching me about strace
:)
@holyjak No problem. strace
is a handy little beast especially in cases where some application is for example not loading some config file you're trying to feed it. With a help of strace
, you often find out you either misplaced your config file, made a typo in its name or path or something similar :) In general, it's nice to see what files the app is trying to access (and whether it succeeds or fails).
@borkdude Thanks a lot for such a quick fix! 👏
The problem with glob scanning all the files in the directory recursively should now be solved in babashka 0.2.13.
I now added the gist to the babashka examples dir:
https://github.com/babashka/babashka/tree/master/examples#file-server
FYI: If you want to run this script in headless environment, the (browse/browse-url (format "http://localhost:%s/" port))
might crash. It internally relies on /usr/bin/xdg-open
on linux, which might not be available on headless distribution. So perhaps you can wrap the browse-url
call into something like
(when-not (str/blank? (:out (sh/sh "which" "xdg-open")))
(browse/browse-url (format "http://localhost:%s/" port)))
There might be some better/more idiomatic way to check presensce of xdg-open
binary. The problematic browse-url
function and its dependencies can be seen here https://github.com/clojure/clojure/blob/master/src/clj/clojure/java/browse.clj
Babashka itself has a slightly modified version of browse-url
which does not depend on java.awt.Desktop
. Feel free to PR improvements to that function.
Haven't tried it yet... but maybe you mean text-align
on line 51 rather than text-aling
?
Thanks, @cassiel, fixed!
You can try
bb -e '(babashka.fs/glob "." "*")'
vsbb -e '(babashka.fs/list-dir ".")