Skip to content

Commit

Permalink
Add wrap-validation middleware to validate Ring requests
Browse files Browse the repository at this point in the history
Add new aleph.http.schema ns
  • Loading branch information
KingMob committed Feb 27, 2023
1 parent 5f20de8 commit 26cbe6b
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 6 deletions.
8 changes: 8 additions & 0 deletions src/aleph/http/client_middleware.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
(:require
;; leave this dependency to make sure that HeaderMap is already compiled
[aleph.http.core :as http]
[aleph.http.schema :as schema]
[clj-commons.byte-streams :as bs]
[clojure.edn :as edn]
[clojure.string :as str]
[clojure.walk :refer [prewalk]]
[malli.core :as m]
[manifold.deferred :as d]
[manifold.executor :as ex]
[manifold.stream :as s]
Expand Down Expand Up @@ -920,9 +922,15 @@
(opt req :save-request)
(assoc :aleph/request req'))))

(defn ^:no-doc wrap-validation [req]
(when-not (schema/validate-request req)
(throw (IllegalArgumentException. (format "Invalid spec: %s" (schema/explain-request req)))))
req)

(def ^:no-doc default-middleware
[wrap-method
wrap-url
wrap-validation
wrap-nested-params
wrap-query-params
wrap-form-params
Expand Down
47 changes: 47 additions & 0 deletions src/aleph/http/schema.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
(ns aleph.http.schema
(:require [malli.core :as m]
[malli.util :as mu]))

(def server-port [:maybe :int])
(def server-name [:maybe :string])
(def remote-addr [:maybe :string])
(def uri [:maybe :string])
(def query-string [:maybe :string])
(def scheme [:enum :http :https])
(def request-method :keyword)
(def content-type [:maybe [:or :string :keyword]])
(def content-length [:maybe :int])
(def character-encoding [:maybe :string])

(def ring-request [:map
[:request-method request-method]
[:server-port {:optional true} server-port]
[:server-name {:optional true} server-name]
[:remote-addr {:optional true} remote-addr]
[:uri {:optional true} uri]
[:query-string {:optional true} query-string]
[:scheme {:optional true} scheme]
[:content-type {:optional true} content-type]
[:content-length {:optional true} content-length]
[:character-encoding {:optional true} character-encoding]])

(def validate-request (m/validator ring-request))
(def explain-request (m/explainer ring-request))





(comment

(map #(m/validate server-port %) ["bingo" nil 123])

(m/validate ring-request
{:server-port 80
:server-name "www.example.com"
:remote-addr "127.0.0.1"
:uri "/blah/blah"
:query-string "?foo=bar"
:scheme :https
:request-method :get})
)
40 changes: 34 additions & 6 deletions test/aleph/http/client_middleware_test.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
(ns aleph.http.client-middleware-test
(:require
[aleph.http.client-middleware :as middleware]
[clojure.test :as t :refer [deftest is]])
[aleph.http.schema :as schema]
[clojure.test :as t :refer [deftest is]]
[malli.core :as m]
[malli.generator :as mg])
(:import
(java.net URLDecoder)))

Expand Down Expand Up @@ -46,7 +49,8 @@
(middleware/coerce-form-params {:form-params {:foo :bar}}))))

(deftest test-nested-query-params
(let [req {:query-params {:foo {:bar "baz"}}}
(let [req {:request-method :get
:query-params {:foo {:bar "baz"}}}
{:keys [query-string]} (reduce #(%2 %1) req middleware/default-middleware)]
(is (= "foo[bar]=baz" (URLDecoder/decode query-string)))))

Expand Down Expand Up @@ -131,12 +135,16 @@
(URLDecoder/decode (req->body-raw req)))

(deftest test-nested-params
(is (= "foo[bar]=baz" (req->query-string {:query-params {:foo {:bar "baz"}}})))
(is (= "foo[bar]=baz" (req->query-string {:query-params {:foo {:bar "baz"}}
(is (= "foo[bar]=baz" (req->query-string {:request-method :get
:query-params {:foo {:bar "baz"}}})))
(is (= "foo[bar]=baz" (req->query-string {:request-method :get
:query-params {:foo {:bar "baz"}}
:content-type :json})))
(is (= "foo[bar]=baz" (req->query-string {:query-params {:foo {:bar "baz"}}
(is (= "foo[bar]=baz" (req->query-string {:request-method :get
:query-params {:foo {:bar "baz"}}
:ignore-nested-query-string false})))
(is (= "foo={:bar \"baz\"}" (req->query-string {:query-params {:foo {:bar "baz"}}
(is (= "foo={:bar \"baz\"}" (req->query-string {:request-method :get
:query-params {:foo {:bar "baz"}}
:ignore-nested-query-string true})))
(is (= "foo[bar]=baz" (req->body-decoded {:method :post
:form-params {:foo {:bar "baz"}}})))
Expand Down Expand Up @@ -169,3 +177,23 @@
(middleware/generate-query-string {:name ["John" "Mark"]} nil :array)))
(is (= "name[0]=John&name[1]=Mark"
(middleware/generate-query-string {:name ["John" "Mark"]} nil :indexed))))


(deftest test-wrap-validation
(doseq [req (mg/sample schema/ring-request)]
(is (middleware/wrap-validation req)))

(is (thrown-with-msg?
IllegalArgumentException
#"Invalid spec.*:in \[:request-method\].*:type :malli.core/missing-key"
(middleware/wrap-validation {})))
(is (thrown-with-msg?
IllegalArgumentException
#"Invalid spec.*:in \[:content-length\].*:value \"10\""
(middleware/wrap-validation {:request-method :post
:content-length "10"})))
(is (thrown-with-msg?
IllegalArgumentException
#"Invalid spec.*:in \[:content-type\].*:value 10"
(middleware/wrap-validation {:request-method :post
:content-type 10}))))

0 comments on commit 26cbe6b

Please sign in to comment.