ningle.ros 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #|-*- mode:lisp -*-|#
  2. #|
  3. exec ros -Q -- $0 "$@"
  4. |#
  5. ;; Woo is a fast non-blocking HTTP server built on top of
  6. ;; libev. Although Woo is written in Common Lisp, it aims
  7. ;; to be the fastest web server written in any programming
  8. ;; language.
  9. ;; https://github.com/fukamachi/woo
  10. ;; Quicklisp is a library manager for Common Lisp. Use
  11. ;; QuickLisp's quickload function to retrieve external
  12. ;; packages. These packages are automatically curl'd when
  13. ;; the program runs.
  14. ;; Woo - https://github.com/fukamachi/woo
  15. ;; Ningle - https://github.com/fukamachi/ningle
  16. ;; Jonathan - https://github.com/fukamachi/jonathan
  17. ;; CL-MARKUP - https://github.com/arielnetworks/cl-markup
  18. ;; Postmodern - https://github.com/marijnh/Postmodern
  19. ;; QURI - https://github.com/fukamachi/quri
  20. (ql:quickload '(:cl-markup :jonathan :ningle :postmodern :quri :uiop :woo) :silent t)
  21. (use-package :ningle)
  22. (declaim (optimize (debug 0) (safety 0) (speed 3)))
  23. (load "./helpers/starts-with.lisp")
  24. (load "./helpers/parse-argv.lisp")
  25. ;; Initialize the global random state by "some means" (e.g. current time)
  26. (setf *random-state* (make-random-state t))
  27. (defvar *app* (make-instance 'ningle:<app>))
  28. (setf (ningle:route *app* "/plaintext")
  29. #'(lambda (params)
  30. (declare (ignore params))
  31. (setf (lack.response:response-headers *response*)
  32. (append (lack.response:response-headers *response*)
  33. (list :content-type "text/plain"
  34. :server "Woo")))
  35. "Hello, World!"))
  36. (setf (ningle:route *app* "/json")
  37. #'(lambda (params)
  38. (declare (ignore params))
  39. (setf (lack.response:response-headers *response*)
  40. (append (lack.response:response-headers *response*)
  41. (list :content-type "application/json; charset=utf-8"
  42. :server "Woo")))
  43. (jonathan:to-json '(:message "Hello, World!"))))
  44. (defun get-a-random-record (id)
  45. (declare (integer id))
  46. `(:|id| ,id :|randomNumber| ,(postmodern:query (:select 'randomnumber :from 'world :where (:= 'id id)) :single!)))
  47. (setf (ningle:route *app* "/db")
  48. #'(lambda (params)
  49. (declare (ignore params))
  50. (setf (lack.response:response-headers *response*)
  51. (append (lack.response:response-headers *response*)
  52. (list :content-type "application/json; charset=utf-8"
  53. :server "Woo")))
  54. (let ((id (+ 1 (random 10000))))
  55. (jonathan:to-json (get-a-random-record id)))))
  56. (defun ensure-integer-is-between-one-and-five-hundreds (n)
  57. (declare (integer n))
  58. (if (< n 1)
  59. (values 1 nil)
  60. (if (> n 500)
  61. (values 500 nil)
  62. (values n t))))
  63. (defun extract-number-of-records-to-fetch (params)
  64. (let ((n (handler-case
  65. (parse-integer (cdr (assoc "queries" params :test #'equal)))
  66. (error (c) (values 1 c)))))
  67. (ensure-integer-is-between-one-and-five-hundreds n)))
  68. (defun get-some-random-integers-between-one-and-ten-thousand (n)
  69. (declare (integer n))
  70. (loop :repeat n
  71. :collect (+ 1 (random 10000))))
  72. (defun get-some-random-records (n)
  73. (declare (integer n))
  74. (let ((ids (get-some-random-integers-between-one-and-ten-thousand n)))
  75. (mapcar #'get-a-random-record ids)))
  76. (setf (ningle:route *app* "/queries")
  77. #'(lambda (params)
  78. (setf (lack.response:response-headers *response*)
  79. (append (lack.response:response-headers *response*)
  80. (list :content-type "application/json; charset=utf-8"
  81. :server "Woo")))
  82. (jonathan:to-json (get-some-random-records (extract-number-of-records-to-fetch params)))))
  83. (defun get-all-fortunes ()
  84. (postmodern:query (:select 'id 'message :from 'fortune) :rows))
  85. (defun get-all-fortunes-plus-one ()
  86. (let* ((records (get-all-fortunes))
  87. (records-p-one (append records '((0 "Additional fortune added at request time.")))))
  88. (sort (copy-list records-p-one) #'string-lessp :key #'second)))
  89. (setf (ningle:route *app* "/fortunes")
  90. #'(lambda (params)
  91. (declare (ignore params))
  92. (setf (lack.response:response-headers *response*)
  93. (append (lack.response:response-headers *response*)
  94. (list :content-type "text/html; charset=utf-8"
  95. :server "Woo")))
  96. (cl-markup:html5
  97. (:head
  98. (:title "Fortunes"))
  99. (:body
  100. (:table
  101. (:tr
  102. (:th "id")
  103. (:th "message"))
  104. (loop for fortune-row in (get-all-fortunes-plus-one)
  105. collect (cl-markup:markup
  106. (:tr
  107. (:td (format nil "~d" (first fortune-row)))
  108. (:td (second fortune-row))))))))))
  109. (defun get-and-update-some-random-records (n)
  110. (declare (integer n))
  111. (let* ((random-records (get-some-random-records n))
  112. (random-numbers (get-some-random-integers-between-one-and-ten-thousand n))
  113. (index -1)
  114. (updated-records (map 'list
  115. (lambda (row)
  116. (incf index)
  117. (list :|id| (getf row :|id| )
  118. :|randomNumber| (nth index random-numbers)))
  119. random-records))
  120. (record-list (map 'list
  121. (lambda (row)
  122. (list (nth 1 row)
  123. (nth 3 row)))
  124. updated-records)))
  125. (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))
  126. (values updated-records)))
  127. (setf (ningle:route *app* "/updates")
  128. #'(lambda (params)
  129. (setf (lack.response:response-headers *response*)
  130. (append (lack.response:response-headers *response*)
  131. (list :content-type "application/json; charset=utf-8"
  132. :server "Woo")))
  133. (jonathan:to-json (get-and-update-some-random-records (extract-number-of-records-to-fetch params)))))
  134. (defun main (&rest argv)
  135. "Create and start the server, applying argv to the env"
  136. (let ((args (parse-argv argv)))
  137. (apply #'woo:run
  138. (lambda (env)
  139. ;; preprocessing
  140. (let ((res (postmodern:with-connection '("hello_world" "benchmarkdbuser" "benchmarkdbpass" "tfb-database" :pooled-p t)
  141. (ningle.app::call *app* env))))
  142. ;; postprocessing
  143. res))
  144. :debug nil
  145. args)))