| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- #|-*- mode:lisp -*-|#
- #|
- exec ros -Q -- $0 "$@"
- |#
- ;; Woo is a fast non-blocking HTTP server built on top of
- ;; libev. Although Woo is written in Common Lisp, it aims
- ;; to be the fastest web server written in any programming
- ;; language.
- ;; https://github.com/fukamachi/woo
- ;; Quicklisp is a library manager for Common Lisp. Use
- ;; QuickLisp's quickload function to retrieve external
- ;; packages. These packages are automatically curl'd when
- ;; the program runs.
- ;; Woo - https://github.com/fukamachi/woo
- ;; Ningle - https://github.com/fukamachi/ningle
- ;; Jonathan - https://github.com/fukamachi/jonathan
- ;; CL-MARKUP - https://github.com/arielnetworks/cl-markup
- ;; Postmodern - https://github.com/marijnh/Postmodern
- ;; QURI - https://github.com/fukamachi/quri
- (ql:quickload '(:cl-markup :jonathan :ningle :postmodern :quri :uiop :woo) :silent t)
- (use-package :ningle)
- (declaim (optimize (debug 0) (safety 0) (speed 3)))
- (load "./helpers/starts-with.lisp")
- (load "./helpers/parse-argv.lisp")
- ;; Initialize the global random state by "some means" (e.g. current time)
- (setf *random-state* (make-random-state t))
- (defvar *app* (make-instance 'ningle:<app>))
- (setf (ningle:route *app* "/plaintext")
- #'(lambda (params)
- (declare (ignore params))
- (setf (lack.response:response-headers *response*)
- (append (lack.response:response-headers *response*)
- (list :content-type "text/plain"
- :server "Woo")))
- "Hello, World!"))
- (setf (ningle:route *app* "/json")
- #'(lambda (params)
- (declare (ignore params))
- (setf (lack.response:response-headers *response*)
- (append (lack.response:response-headers *response*)
- (list :content-type "application/json; charset=utf-8"
- :server "Woo")))
- (jonathan:to-json '(:message "Hello, World!"))))
- (defun get-a-random-record (id)
- (declare (integer id))
- `(:|id| ,id :|randomNumber| ,(postmodern:query (:select 'randomnumber :from 'world :where (:= 'id id)) :single!)))
- (setf (ningle:route *app* "/db")
- #'(lambda (params)
- (declare (ignore params))
- (setf (lack.response:response-headers *response*)
- (append (lack.response:response-headers *response*)
- (list :content-type "application/json; charset=utf-8"
- :server "Woo")))
- (let ((id (+ 1 (random 10000))))
- (jonathan:to-json (get-a-random-record id)))))
- (defun ensure-integer-is-between-one-and-five-hundreds (n)
- (declare (integer n))
- (if (< n 1)
- (values 1 nil)
- (if (> n 500)
- (values 500 nil)
- (values n t))))
- (defun extract-number-of-records-to-fetch (params)
- (let ((n (handler-case
- (parse-integer (cdr (assoc "queries" params :test #'equal)))
- (error (c) (values 1 c)))))
- (ensure-integer-is-between-one-and-five-hundreds n)))
- (defun get-some-random-integers-between-one-and-ten-thousand (n)
- (declare (integer n))
- (loop :repeat n
- :collect (+ 1 (random 10000))))
- (defun get-some-random-records (n)
- (declare (integer n))
- (let ((ids (get-some-random-integers-between-one-and-ten-thousand n)))
- (mapcar #'get-a-random-record ids)))
- (setf (ningle:route *app* "/queries")
- #'(lambda (params)
- (setf (lack.response:response-headers *response*)
- (append (lack.response:response-headers *response*)
- (list :content-type "application/json; charset=utf-8"
- :server "Woo")))
- (jonathan:to-json (get-some-random-records (extract-number-of-records-to-fetch params)))))
- (defun get-all-fortunes ()
- (postmodern:query (:select 'id 'message :from 'fortune) :rows))
- (defun get-all-fortunes-plus-one ()
- (let* ((records (get-all-fortunes))
- (records-p-one (append records '((0 "Additional fortune added at request time.")))))
- (sort (copy-list records-p-one) #'string-lessp :key #'second)))
- (setf (ningle:route *app* "/fortunes")
- #'(lambda (params)
- (declare (ignore params))
- (setf (lack.response:response-headers *response*)
- (append (lack.response:response-headers *response*)
- (list :content-type "text/html; charset=utf-8"
- :server "Woo")))
- (cl-markup:html5
- (:head
- (:title "Fortunes"))
- (:body
- (:table
- (:tr
- (:th "id")
- (:th "message"))
- (loop for fortune-row in (get-all-fortunes-plus-one)
- collect (cl-markup:markup
- (:tr
- (:td (format nil "~d" (first fortune-row)))
- (:td (second fortune-row))))))))))
- (defun get-and-update-some-random-records (n)
- (declare (integer n))
- (let* ((random-records (get-some-random-records n))
- (random-numbers (get-some-random-integers-between-one-and-ten-thousand n))
- (index -1)
- (updated-records (map 'list
- (lambda (row)
- (incf index)
- (list :|id| (getf row :|id| )
- :|randomNumber| (nth index random-numbers)))
- random-records))
- (record-list (map 'list
- (lambda (row)
- (list (nth 1 row)
- (nth 3 row)))
- updated-records)))
- (postmodern:query (format nil "UPDATE world AS ori SET randomnumber = new.randomnumber FROM (VALUES ~{(~{~a~^, ~})~^, ~}) AS new (id, randomnumber) WHERE ori.id = new.id" record-list))
- (values updated-records)))
- (setf (ningle:route *app* "/updates")
- #'(lambda (params)
- (setf (lack.response:response-headers *response*)
- (append (lack.response:response-headers *response*)
- (list :content-type "application/json; charset=utf-8"
- :server "Woo")))
- (jonathan:to-json (get-and-update-some-random-records (extract-number-of-records-to-fetch params)))))
- (defun main (&rest argv)
- "Create and start the server, applying argv to the env"
- (let ((args (parse-argv argv)))
- (apply #'woo:run
- (lambda (env)
- ;; preprocessing
- (let ((res (postmodern:with-connection '("hello_world" "benchmarkdbuser" "benchmarkdbpass" "tfb-database" :pooled-p t)
- (ningle.app::call *app* env))))
- ;; postprocessing
- res))
- :debug nil
- args)))
|