3 [clojure.java.io :as io])
6 (com.omarpolo.gemini Request)))
9 (set! *warn-on-reflection* true)
16 "Human description for every response code."
20 30 "temporary redirect"
21 31 "permanent redirect"
22 40 "temporary failure"
23 41 "server unavailable"
27 50 "permanent failure"
30 53 "proxy request refused"
32 60 "client certificate required"
33 61 "certificate not authorized"
34 62 "certificate not valid"})
36 (defn is-input? [{c :code}] (= 1 (quot c 10)))
37 (defn is-success? [{c :code}] (= 2 (quot c 10)))
38 (defn is-redirect? [{c :code}] (= 3 (quot c 10)))
39 (defn is-temporary-failure? [{c :code}] (= 4 (quot c 10)))
40 (defn is-permanent-failure? [{c :code}] (= 5 (quot c 10)))
41 (defn is-client-cert-required? [{c :code}] (= 6 (quot c 10)))
45 (defn- parse-params [{:keys [proxy request follow-redirects?]}]
46 (when (and (:host proxy)
48 (throw (ex-info "invalid proxy definition" {:got proxy
49 :reason "missing proxy host"})))
53 (throw (ex-info ":request is nil" {})))
54 :follow-redirects? (case follow-redirects?
60 (defn- resolve-uri [request meta]
61 (let [uri (URI. request)
63 (str (.resolve uri rel))))
65 (defn- fetch' [host port uri]
68 host (Request. ^String host ^int port ^String uri)
69 :else (Request. ^String uri))]
79 "Make a gemini request. `params` is a map with the following
80 keys (only `:request` is mandatory):
82 - `:proxy`: a map of `:host` and `:port`, identifies the server to
83 send the requests to. This allows to use a gemini server as a
84 proxy, it doesn't do any other kind of proxying (e.g. SOCK5.)
86 - `:request` the URI (as string) to require.
88 - `:follow-redirects?` if `false` or `nil` don't follow redirects,
89 if `true` follow up to 5 redirects, or the number of redirects to
92 Return a map with `:request`, `:code`, `:meta`, `:body` on success
93 or `:error` on failure. The request needs to be closed when done
96 (let [{:keys [host port request follow-redirects?] :as orig} (parse-params params)]
97 (loop [n follow-redirects?
100 (let [res (fetch' host port request)
101 redirect? (and (not (:error res))
105 (= follow-redirects? 0) res
107 redirect?) (do (.close ^Request (:request res))
108 (throw (ex-info "too many redirects"
110 :redirects follow-redirects?
112 :meta (:meta res)})))
113 redirect? (do (.close ^Request (:request res))
115 (resolve-uri request (:meta res))
119 (defn body-as-string!
120 "Read all the response into a strings and returns it. The request
123 (let [sw (java.io.StringWriter.)]
124 (with-open [r ^Request r]
125 (io/copy (.body r) sw)
133 (defmacro with-request
134 "Make a request, eval `body` when it succeed and automatically close
135 the request, or throw an exception if the request fails."
138 (when-let [e# (:error ~var)]
140 (with-open [req# (:request ~var)]