Skip to content

Instantly share code, notes, and snippets.

@drbobbeaty
Last active March 10, 2022 12:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save drbobbeaty/e580eba3e13a799b91c1 to your computer and use it in GitHub Desktop.
Save drbobbeaty/e580eba3e13a799b91c1 to your computer and use it in GitHub Desktop.
Simple clojure Ring example
(ns performance.server
"The routes for the web server."
(:require [cheshire.core :as json]
[clj-time.core :refer [now]]
[clojure.tools.logging :refer [infof warn warnf error errorf]]
[compojure
[core :refer [defroutes GET POST]]
[handler :as handler]]
[ring.middleware.jsonp :refer [wrap-json-with-padding]]
[ring.middleware.params :refer [wrap-params]]
[ring.util.io :refer [piped-input-stream]])
(:import [java.io BufferedWriter OutputStreamWriter]))
;; extend cheshire to allow for the encoding of Joda DateTime values...
(extend-protocol cheshire.generate/JSONable
org.joda.time.DateTime
(to-json [t jg]
(cheshire.generate/write-string jg (str t))))
(defn safe-parse-json
"Function to attempt to parse a JSON string from the user - and we have to be
VERY careful about this, as we have NO trust in this user - so check for all
possible problems, and if it's invalid JSON, return a nil."
[s]
(cond
(or (empty? s) (not (string? s))) nil
:else (try
(json/parse-string s true)
(catch com.fasterxml.jackson.core.JsonParseException jpe
(warnf "JSON parse exception on: %s => %s" s (.getMessage jpe))
nil))))
(defn return-code
"Creates a ring response for returning the given return code."
[code]
{:status code
:headers {"Content-Type" "application/json; charset=UTF-8"}})
(defn return-json
"Creates a ring response for returning the given object as JSON."
([ob] (return-json ob (now) 200))
([ob lastm] (return-json ob lastm 200))
([ob lastm code]
{:status code
:headers {"Content-Type" "application/json; charset=UTF-8"
"Last-Modified" (str (or lastm (now)))}
;; TODO: the input-stream tactic is foiled by ring.middleware.jsonp
;; which slurps the whole stream before adding the callback. Could
;; fix by patching that lib. We should still be getting the benefit
;; for the non-browser case though.
:body (piped-input-stream
(fn [out]
(->> out
(OutputStreamWriter.)
(BufferedWriter.)
(json/generate-stream ob))))}))
(defroutes app-routes
"Primary routes for the webserver."
(GET "/" []
(return-json {:app "performance",
:hello? "World!",
:code (or (git-commit) "unknown commit")}))
(POST "/v1/deals_performance.json" [:as {body :body}]
(let [deals (json/parse-json (slurp body))]
(cond
(empty? deals) (return-code 400)
(not (coll? deals)) (return-code 400)
:else
(return-json deals)))))
@fuzzyami
Copy link

hola! could you please elaborate on
We should still be getting the benefit for the non-browser case though.
what's the benefit? what's the non-browser case? thx!

@drbobbeaty
Copy link
Author

@fuzzyami The benefit is for Services calling this Service - in a SOA scheme. In browsers, it's possible to have unpredictable users do things like close the tab, and break the streaming of the data before it's complete. A Service making the call wouldn't likely do that, so we get the benefits of Streaming without having to fix the underlying JSONP code.

This was all done primarily for Service-to-Service communication - not Client-to-Service.

@fuzzyami
Copy link

That was super quick. thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment