web.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. ;; This file is part of scheme-GNUnet.
  2. ;; Copyright © 2021, 2022 GNUnet e.V.
  3. ;;
  4. ;; scheme-GNUnet is free software: you can redistribute it and/or modify it
  5. ;; under the terms of the GNU Affero General Public License as published
  6. ;; by the Free Software Foundation, either version 3 of the License,
  7. ;; or (at your option) any later version.
  8. ;;
  9. ;; scheme-GNUnet is distributed in the hope that it will be useful, but
  10. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;; Affero General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU Affero General Public License
  15. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. ;;
  17. ;; SPDX-License-Identifier: AGPL-3.0-or-later
  18. ;; Go to localhost:8089/reload to reload the module
  19. (define-module (guile-user)
  20. #:declarative? #f)
  21. (use-modules (fibers)
  22. (fibers conditions)
  23. (rnrs bytevectors)
  24. (gnu extractor enum)
  25. (gnu gnunet block)
  26. (gnu gnunet crypto)
  27. (gnu gnunet utils bv-slice)
  28. (gnu gnunet config db)
  29. (gnu gnunet config fs)
  30. (rnrs hashtables)
  31. ((gnu gnunet nse client)
  32. #:prefix #{nse:}#)
  33. ((gnu gnunet dht client)
  34. #:prefix #{dht:}#)
  35. (web response)
  36. (web server)
  37. (web uri)
  38. (web request)
  39. (web form)
  40. (srfi srfi-11)
  41. (ice-9 match)
  42. (sxml simple))
  43. (define config (load-configuration))
  44. (define* (respond/html body #:key (status-code 200))
  45. "@var{status-code}: the HTTP status code to return. By default, the status code
  46. for success is used."
  47. (values (build-response
  48. #:code status-code
  49. #:headers `((content-type application/xhtml+xml) (charset . "utf-8")))
  50. (lambda (port)
  51. (display "<!DOCTYPE html>\n" port)
  52. (sxml->xml `(html (@ (xmlns "http://www.w3.org/1999/xhtml"))
  53. (head (title "Hello"))
  54. (body ,body))
  55. port))))
  56. ;; TODO: make the form work, defaults, ...
  57. (define (data-encoding-input name id)
  58. `(select
  59. (@ (name ,name) (id ,id))
  60. (option (@ (value "utf-8-text")) "free-form text encoded as UTF-8")
  61. (option (@ (value "hexadecimal")) "binary data encoded in hexadecimal")))
  62. (define (common-get/put-form-parts %prefix)
  63. (define (prefix id) ; ids must be unique within a document
  64. (string-append %prefix id))
  65. `((li (label (@ (for "type")) "Type: ")
  66. (input (@ (type "number") (id "type") (name "type"))))
  67. (li (label (@ (for "replication-level")) "Replication level: ")
  68. (input (@ (type "number") (id ,(prefix "replication-level"))
  69. (name "replication-level"))))
  70. (li (label (@ (for "key-encoding")) "Key encoding: ")
  71. ,(data-encoding-input "key-encoding" (prefix "key-encoding")))
  72. (li (label (@ (for "key")) "Key: ")
  73. (input (@ (type "text") (id ,(prefix "key")) (name "key"))))))
  74. (define search-form
  75. `(form
  76. (@ (action "/search-dht") (method "post")) ; TODO should be "get"
  77. (ul ,@(common-get/put-form-parts "get-"))
  78. (input (@ (type "submit") (value "Search the DHT")))))
  79. ;; TODO: make the form work, defaults, ...
  80. (define put-form
  81. `(form
  82. (@ (action "/put-dht") (method "post"))
  83. (ul ,@(common-get/put-form-parts "put-")
  84. (li (label (@ (for "put-data-encoding")) "Encoding of data: ")
  85. ,(data-encoding-input "data-encoding" "put-data-encoding"))
  86. (li (label (@ (for "put-data")) "Data to insert: ")
  87. (input (@ (type "text") (id "put-data") (name "data")))))
  88. (input (@ (type "submit") (value "Put it into the DHT")))))
  89. (define cadet-start-chat-form
  90. `(form
  91. (@ (action "/start-cadet-chat") (method "post"))
  92. (ul (li (label (@ (for "cadet-start-peer"))
  93. "Identity of remote peer to connect to")
  94. (input (@ (type "text") (id "cadet-start-peer") (name "peer"))))
  95. (li (label (@ (for "cadet-port-name"))
  96. "Name of the port to connect to (as a string)")
  97. (input (@ (type "text") (id "cadet-port-name") (name "port")))))
  98. (input (@ (type "submit") (value "Connect!")))))
  99. (define (cadet-chat-forms)
  100. `(p "TODO!"))
  101. (define (estimate->html estimate)
  102. `(dl (dt "Timestamp")
  103. (dd ,(number->string (nse:estimate:timestamp estimate)))
  104. (dt "Number of peers")
  105. (dd ,(number->string (nse:estimate:number-peers estimate)))
  106. (dt "Standard deviation")
  107. (dd ,(number->string (nse:estimate:standard-deviation estimate)))))
  108. (define (decode/key encoding data)
  109. (match encoding
  110. ("utf-8-text"
  111. (hash/sha512 (bv-slice/read-write (string->utf8 data))))
  112. ;; TODO other encodings
  113. ))
  114. (define (decode/data encoding data)
  115. (match encoding
  116. ("utf-8-text"
  117. (bv-slice/read-write (string->utf8 data))
  118. ;; TODO other encodings
  119. )))
  120. (define (process-put-dht dht-server parameters)
  121. ;; TODO replication level, expiration ...
  122. (dht:put! dht-server
  123. (dht:datum->insertion
  124. (dht:make-datum
  125. (string->number (assoc-ref parameters "type"))
  126. (decode/key (assoc-ref parameters "key-encoding")
  127. ;; TODO the key is 00000.... according to gnunet-dht-monitor
  128. (assoc-ref parameters "key"))
  129. (decode/data (assoc-ref parameters "data-encoding")
  130. (assoc-ref parameters "data"))))))
  131. (define (try-utf8->string bv) ; TODO: less duplication
  132. (catch 'decoding-error
  133. (lambda () (utf8->string bv))
  134. (lambda _ #false)))
  135. (define (data->string slice)
  136. (define bv (make-bytevector (slice-length slice)))
  137. (slice-copy! slice (bv-slice/read-write bv))
  138. (define as-string (try-utf8->string bv))
  139. (or as-string (object->string bv)))
  140. (define (parameters->query parameters)
  141. "Perform rudimentary validation on the paramaters @var{parameters}
  142. for a /search-dht form. If correct, return an appropriate query object.
  143. If incorrect, return @code{#false}. TODO more validation."
  144. (let* ((type (and=> (assoc-ref parameters "type") string->number))
  145. (key-encoding (assoc-ref parameters "key-encoding"))
  146. (key (assoc-ref parameters "key"))
  147. (replication-level (assoc-ref parameters "key"))
  148. (desired-replication-level
  149. (and=> (assoc-ref parameters "replication-level") string->number)))
  150. (and type key-encoding key replication-level desired-replication-level
  151. (dht:make-query type
  152. (decode/key key-encoding key)
  153. #:desired-replication-level
  154. desired-replication-level))))
  155. (define (process-search-dht dht-server parameters)
  156. (define search-result)
  157. (define found? (make-condition))
  158. (define (found %search-result)
  159. ;; TODO: document necessity of copies and this procedure
  160. (set! search-result (dht:copy-search-result %search-result))
  161. (unless (signal-condition! found?)
  162. (pk "already signalled, is cancelling working correctly, or was this \
  163. merely a race?")))
  164. (define query (parameters->query parameters))
  165. (if query
  166. (let ((search-handle (dht:start-get! dht-server query found)))
  167. (wait found?)
  168. ;; For this example application, a single response is sufficient.
  169. ;; TODO: cancel from within 'found' (probably buggy)
  170. (dht:stop-get! search-handle)
  171. ;; TODO: properly format the result, streaming, stop searching
  172. ;; after something has been found or if the client closes the connection ...
  173. (respond/html `(div (p "Found! ")
  174. ;; TODO: better output, determine why the data is bogus
  175. (dl (dt "Type: ")
  176. (dd ,(dht:datum-type
  177. (dht:search-result->datum search-result)))
  178. (dt "Key: ")
  179. (dd ,(data->string
  180. (dht:datum-key
  181. (dht:search-result->datum search-result))))
  182. (dt "Value: ")
  183. (dd ,(data->string
  184. (dht:datum-value
  185. (dht:search-result->datum search-result))))
  186. (dt "Expiration: ")
  187. (dd ,(object->string
  188. (dht:datum-expiration
  189. (dht:search-result->datum search-result))))
  190. (dt "Get path: ") ; TODO as list
  191. (dd ,(dht:search-result-get-path search-result))
  192. (dt "Put path: ")
  193. (dd ,(dht:search-result-put-path search-result))))))
  194. (respond/html `(p "Some fields were missing / invalid")
  195. #:status-code 400)))
  196. (define-once started? #f)
  197. (define (slice-copy slice) ; TODO: move to (gnu gnunet utils bv-slice), use elsewhere?
  198. (define s (make-slice/read-write (slice-length slice)))
  199. (slice-copy! slice s)
  200. s)
  201. (define (url-handler dht-server nse-server cadet-server request body)
  202. (match (uri-path (request-uri request))
  203. ("/" (respond/html
  204. `(div (p "A few links")
  205. (ul (li (a (@ (href "/network-size")) "network size"))
  206. (li (a (@ (href "/cadet-chat")) "basic chatting via CADET"))
  207. (li (a (@ (href "/search-dht")) "search the DHT")
  208. (li (a (@ (href "/put-dht")) "add things to the DHT")))))))
  209. ("/reload" ; TODO form with PUT request?
  210. (reload-module (current-module))
  211. (respond/html "reloaded!"))
  212. ("/network-size"
  213. (respond/html
  214. (let ((current-estimate (nse:estimate nse-server)))
  215. (if current-estimate
  216. (estimate->html current-estimate)
  217. '(p "No etimate yet")))))
  218. ("/cadet-chat"
  219. (respond/html `(div (p "You can only connect to a chat here, not start new ones")
  220. (p "Run gnunet-cadet --open-port=PORT to run a new chat!")
  221. (p "Connect to a chat!")
  222. ,cadet-start-chat-form
  223. (p "participate in a chat!")
  224. ,@(cadet-chat-forms))))
  225. ("/search-dht" ; TODO check method and Content-Type, validation ...
  226. (if (pk 'b body)
  227. (process-search-dht dht-server (urlencoded->alist body))
  228. (respond/html search-form)))
  229. ("/put-dht" ; TODO check method and Content-Type, validation ...
  230. (if body
  231. (begin
  232. (process-put-dht dht-server (urlencoded->alist body))
  233. (respond/html '(p "Success!")))
  234. (respond/html put-form)))
  235. (_ (respond/html '(p "not found"))))) ; TODO 404
  236. (define (start config)
  237. (define nse-server (nse:connect config))
  238. (define dht-server (dht:connect config))
  239. (define cadet-server (dht:connect config))
  240. (define impl (lookup-server-impl 'fiberized))
  241. (define server (open-server impl `(#:port 8089)))
  242. (define (url-handler* request body)
  243. (url-handler dht-server nse-server cadet-server request body))
  244. (let loop ()
  245. (let-values (((client request body)
  246. (read-client impl server)))
  247. (spawn-fiber
  248. (lambda ()
  249. (let-values (((response body state)
  250. (handle-request url-handler* request body '())))
  251. (write-client impl server client response body)))))
  252. (loop)))
  253. (when (not started?)
  254. (set! started? #t)
  255. (run-fibers (lambda () (start config))))