practicalli / clojure-web-services

Develop production grade server-side web services and APIs using Clojure and REPL driven development
https://practical.li/clojure-web-services
Creative Commons Attribution Share Alike 4.0 International
11 stars 14 forks source link

Reitit and websockets #67

Open practicalli-johnny opened 3 years ago

practicalli-johnny commented 3 years ago

Examples of using reitit, ring and http-kit where one or more routes provide a websocket

reitit repository examples: https://github.com/metosin/reitit/tree/master/examples

reitit + ws - mount a ws-endpoint into a route

See the ws route below. I understand that the route is not set up to handle websocket connections. Also when I visit using the url using a regular browser window, I know it will fails. But I expect the httpkit/websocket? to work as described on page https://http-kit.github.io/http-kit/org.httpkit.server.html#var-websocket.3F Would that now print outfalse. But instead I get an error saying: java.lang.IllegalArgumentException: No implementation of method: :websocket? of protocol: #'org.httpkit.server/Channel found for class: clojure.lang.PersistentHashMap.I am very new to clojure. Just trying to figure out what to use for webapps.

(ns sampleapp 
  (:require
   [reitit.ring :as ring]
   [ring.middleware.params :as params]
   [ring.middleware.multipart-params :as multparams]
   [org.httpkit.server :as httpkit]
  ;  [ring.adapter.jetty :as jetty]
  ;  [ring.adapter.jetty9 :as jetty]
    )
  (:gen-class))(defn wrap [handler val]
  (fn [request]
    (let [currval (get request :val [])]
      (handler (assoc request :val (conj currval val))))))(defn jerryhandler [req]
  (println (str "Wrapped value: " (:val req)))
  {:status 200
   :headers {"Content-type" "text/plain"}
   :body (str "Hello Jerry!!\nI got values " (:val req))})(defn busterhandler [req]
  (println (str "Parameters: " (:params req)))
  {:status 200
   :headers {"Content-type" "text/plain"}
   :body "Hello Buster!!"})(defn fileupload [req]
  (println (str "File info: " (get-in req [:params "file"])))
  (let [fileobj (get-in req [:params "file" :tempfile])
        pathtofile (.getAbsolutePath fileobj)
        contents (slurp fileobj)]
    (println contents)
    (println pathtofile)
    {:status 200
     :headers {"Content-type" "text/plain"}
     :body "File uploaded..."})
  )(defn wshandler [req] 
  (println (httpkit/websocket? req))
  {:status 200 :headers {"Content-type" "text/plain" :body "Not valid ws request..."}})(def router
  (ring/router 
   [["/hellojerry" {:get {:middleware [[wrap 1] [wrap 2]] :handler jerryhandler}}]
    ["/hellobuster" {:get (params/wrap-params busterhandler)}]
    ["/fileupload" {:post (multparams/wrap-multipart-params fileupload)}]
    ["/ws" wshandler]]
   ))(def app
  (ring/ring-handler
   router
   (constantly {:status 404 :body "Not found!"}))
  )(defn -main
  [& args]
  (httpkit/run-server app {:port 3000})
  ; (jetty/run-jetty app {:port 3000 :h2? true}))
)  

I believe you need to pull out the channel linked to the request to get web-sockets with http-kit. There seems to be an example here: https://http-kit.github.io/server.html#channel http-kit.github.io HTTP server | http-kit, high performance HTTP Client/Server for Clojure Event-driven I/O HTTP Client & Server for Clojure: small, efficient, Ring-compatible. Asynchronous and websocket supported Test This 6 hours ago

I believe the linked approach is deprecated. However, the example given here: https://http-kit.github.io/http-kit/org.httpkit.server.html#var-as-channel, works well. It appears that my mistake was using (httpkit/websocket?, when, in fact, I was supposed to be using just (:websocket? ).