From 053832d737dd5150a1b5a183be8f7e4c26a36222 Mon Sep 17 00:00:00 2001 From: James Reeves Date: Sat, 24 Mar 2012 00:19:51 +0000 Subject: [PATCH] Protocol for form-encode to support strings and maps --- ring-core/src/ring/util/codec.clj | 50 ++++++++++++++++++++------------- ring-core/test/ring/util/test/codec.clj | 16 +++++++++++ 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/ring-core/src/ring/util/codec.clj b/ring-core/src/ring/util/codec.clj index bee2006..ace7bb9 100644 --- a/ring-core/src/ring/util/codec.clj +++ b/ring-core/src/ring/util/codec.clj @@ -3,6 +3,7 @@ (:use ring.util.data) (:require [clojure.string :as str]) (:import java.io.File + java.util.Map (java.net URLEncoder URLDecoder) org.apache.commons.codec.binary.Base64)) @@ -59,6 +60,35 @@ [^String encoded] (Base64/decodeBase64 (.getBytes encoded))) +(defprotocol FormEncodeable + (form-encode* [x encoding])) + +(defn form-encode + "Encode the supplied value into www-form-urlencoded format, often used in + URL query strings and POST request bodies, using the specified encoding. + If the encoding is not specified, it defaults to UTF-8" + [x & [encoding]] + (form-encode* x (or encoding "UTF-8"))) + +(extend-protocol FormEncodeable + String + (form-encode* [unencoded encoding] + (URLEncoder/encode unencoded encoding)) + Map + (form-encode* [params encoding] + (letfn [(encode [x] (form-encode x encoding)) + (encode-param [[k v]] (str (encode (name k)) "=" (encode v)))] + (->> params + (mapcat + (fn [[k v]] + (if (or (seq? v) (sequential? v) ) + (map #(encode-param [k %]) v) + [(encode-param [k v])]))) + (str/join "&")))) + Object + (form-encode* [x encoding] + (form-encode* (str x) encoding))) + (defn form-decode "Parse parameters from a string into a map." ([^String param-string] @@ -73,23 +103,3 @@ param-map)) {} (str/split param-string #"&")))) - -(defn form-encode - "Encode parameters from a map into a string." - ([param-map] - (form-encode param-map "UTF-8")) - ([param-map encoding] - (form-encode (keys param-map) - (vals param-map) - encoding)) - ([params values encoding] - (str/join #"&" - (map (fn [param value] - (if (vector? value) - (form-encode (repeat (count value) param) - value - encoding) - (str (url-encode param) - "=" - (url-encode value)))) - params values)))) diff --git a/ring-core/test/ring/util/test/codec.clj b/ring-core/test/ring/util/test/codec.clj index 58cdf1d..c8ef0ac 100644 --- a/ring-core/test/ring/util/test/codec.clj +++ b/ring-core/test/ring/util/test/codec.clj @@ -28,6 +28,22 @@ (let [str-bytes (.getBytes "foo?/+" "UTF-8")] (is (Arrays/equals str-bytes (base64-decode (base64-encode str-bytes)))))) +(deftest test-form-encode + (testing "strings" + (are [x y] (= (form-encode x) y) + "foo bar" "foo+bar" + "foo+bar" "foo%2Bbar" + "foo/bar" "foo%2Fbar") + (is (= (form-encode "foo/bar" "UTF-16") "foo%FE%FF%00%2Fbar"))) + (testing "maps" + (are [x y] (= (form-encode x) y) + {"a" "b"} "a=b" + {:a "b"} "a=b" + {"a" 1} "a=1" + {"a" "b" "c" "d"} "a=b&c=d" + {"a" "b c"} "a=b+c") + (is (= (form-encode {"a" "foo/bar"} "UTF-16") "a=foo%FE%FF%00%2Fbar")))) + (deftest form-encoding (let [encoded-params "p%2F1=v%2F1&p%2F2=v%2F21&p%2F2=v%2F22"] (is (= (form-decode encoded-params)