Skip to content

Commit 238525e

Browse files
committed
Import from ring-core project
0 parents  commit 238525e

File tree

5 files changed

+227
-0
lines changed

5 files changed

+227
-0
lines changed

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/target
2+
/lib
3+
/classes
4+
/checkouts
5+
pom.xml
6+
pom.xml.asc
7+
*.jar
8+
*.class
9+
.lein-deps-sum
10+
.lein-failures
11+
.lein-plugins
12+
.lein-repl-history

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# ring-codec
2+
3+
A Clojure library designed to ... well, that part is up to you.
4+
5+
## Usage
6+
7+
FIXME
8+
9+
## License
10+
11+
Copyright © 2013 FIXME
12+
13+
Distributed under the Eclipse Public License, the same as Clojure.

project.clj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(defproject ring-codec "0.1.0-SNAPSHOT"
2+
:description "Library for encoding and decoding data"
3+
:url "http://example.com/FIXME"
4+
:license {:name "The MIT License"
5+
:url "http://opensource.org/licenses/MIT"}
6+
:dependencies [[org.clojure/clojure "1.3.0"]
7+
[commons-codec "1.6"]]
8+
:profiles
9+
{:1.4 {:dependencies [[org.clojure/clojure "1.4.0"]]}
10+
:1.5 {:dependencies [[org.clojure/clojure "1.5.0-RC1"]]}})

src/ring/util/codec.clj

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
(ns ring.util.codec
2+
"Functions for encoding and decoding data."
3+
(:require [clojure.string :as str])
4+
(:import java.io.File
5+
java.util.Map
6+
[java.net URLEncoder URLDecoder]
7+
org.apache.commons.codec.binary.Base64))
8+
9+
(defn assoc-conj
10+
"Associate a key with a value in a map. If the key already exists in the map,
11+
a vector of values is associated with the key."
12+
[map key val]
13+
(assoc map key
14+
(if-let [cur (get map key)]
15+
(if (vector? cur)
16+
(conj cur val)
17+
[cur val])
18+
val)))
19+
20+
(defn- double-escape [^String x]
21+
(.replace (.replace x "\\" "\\\\") "$" "\\$"))
22+
23+
(def ^:private string-replace-bug?
24+
(= "x" (str/replace "x" #"." (fn [x] "$0"))))
25+
26+
(defmacro fix-string-replace-bug [x]
27+
(if string-replace-bug?
28+
`(double-escape ~x)
29+
x))
30+
31+
(defn percent-encode
32+
"Percent-encode every character in the given string using either the specified
33+
encoding, or UTF-8 by default."
34+
[^String unencoded & [^String encoding]]
35+
(->> (.getBytes unencoded (or encoding "UTF-8"))
36+
(map (partial format "%%%02X"))
37+
(str/join)))
38+
39+
(defn- parse-bytes [encoded-bytes]
40+
(->> (re-seq #"%.." encoded-bytes)
41+
(map #(subs % 1))
42+
(map #(.byteValue (Integer/valueOf % 16)))
43+
(byte-array)))
44+
45+
(defn percent-decode
46+
"Decode every percent-encoded character in the given string using the
47+
specified encoding, or UTF-8 by default."
48+
[^String encoded & [^String encoding]]
49+
(str/replace encoded
50+
#"(?:%..)+"
51+
(fn [chars]
52+
(-> ^bytes (parse-bytes chars)
53+
(String. (or encoding "UTF-8"))
54+
(fix-string-replace-bug)))))
55+
56+
(defn url-encode
57+
"Returns the url-encoded version of the given string, using either a specified
58+
encoding or UTF-8 by default."
59+
[unencoded & [encoding]]
60+
(str/replace
61+
unencoded
62+
#"[^A-Za-z0-9_~.+-]+"
63+
#(double-escape (percent-encode % encoding))))
64+
65+
(defn ^String url-decode
66+
"Returns the url-decoded version of the given string, using either a specified
67+
encoding or UTF-8 by default. If the encoding is invalid, nil is returned."
68+
[encoded & [encoding]]
69+
(percent-decode encoded encoding))
70+
71+
(defn base64-encode
72+
"Encode an array of bytes into a base64 encoded string."
73+
[unencoded]
74+
(String. (Base64/encodeBase64 unencoded)))
75+
76+
(defn base64-decode
77+
"Decode a base64 encoded string into an array of bytes."
78+
[^String encoded]
79+
(Base64/decodeBase64 (.getBytes encoded)))
80+
81+
(defprotocol FormEncodeable
82+
(form-encode* [x encoding]))
83+
84+
(extend-protocol FormEncodeable
85+
String
86+
(form-encode* [unencoded encoding]
87+
(URLEncoder/encode unencoded encoding))
88+
Map
89+
(form-encode* [params encoding]
90+
(letfn [(encode [x] (form-encode* x encoding))
91+
(encode-param [[k v]] (str (encode (name k)) "=" (encode v)))]
92+
(->> params
93+
(mapcat
94+
(fn [[k v]]
95+
(if (or (seq? v) (sequential? v) )
96+
(map #(encode-param [k %]) v)
97+
[(encode-param [k v])])))
98+
(str/join "&"))))
99+
Object
100+
(form-encode* [x encoding]
101+
(form-encode* (str x) encoding)))
102+
103+
(defn form-encode
104+
"Encode the supplied value into www-form-urlencoded format, often used in
105+
URL query strings and POST request bodies, using the specified encoding.
106+
If the encoding is not specified, it defaults to UTF-8"
107+
[x & [encoding]]
108+
(form-encode* x (or encoding "UTF-8")))
109+
110+
(defn form-decode-str
111+
"Decode the supplied www-form-urlencoded string using the specified encoding,
112+
or UTF-8 by default."
113+
[^String encoded & [encoding]]
114+
(try
115+
(URLDecoder/decode encoded (or encoding "UTF-8"))
116+
(catch Exception _ nil)))
117+
118+
(defn form-decode
119+
"Decode the supplied www-form-urlencoded string using the specified encoding,
120+
or UTF-8 by default. If the encoded value is a string, a string is returned.
121+
If the encoded value is a map of parameters, a map is returned."
122+
[^String encoded & [encoding]]
123+
(if-not (.contains encoded "=")
124+
(form-decode-str encoded encoding)
125+
(reduce
126+
(fn [m param]
127+
(if-let [[k v] (str/split param #"=" 2)]
128+
(assoc-conj m (form-decode-str k encoding) (form-decode-str v encoding))
129+
m))
130+
{}
131+
(str/split encoded #"&"))))

test/ring/util/test/codec.clj

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
(ns ring.util.test.codec
2+
(:use clojure.test
3+
ring.util.codec)
4+
(:import java.util.Arrays))
5+
6+
(deftest test-percent-encode
7+
(is (= (percent-encode " ") "%20"))
8+
(is (= (percent-encode "+") "%2B"))
9+
(is (= (percent-encode "foo") "%66%6F%6F")))
10+
11+
(deftest test-percent-decode
12+
(is (= (percent-decode "%20") " "))
13+
(is (= (percent-decode "foo%20bar") "foo bar"))
14+
(is (= (percent-decode "foo%FE%FF%00%2Fbar" "UTF-16") "foo/bar"))
15+
(is (= (percent-decode "%24") "$")))
16+
17+
(deftest test-url-encode
18+
(is (= (url-encode "foo/bar") "foo%2Fbar"))
19+
(is (= (url-encode "foo/bar" "UTF-16") "foo%FE%FF%00%2Fbar"))
20+
(is (= (url-encode "foo+bar") "foo+bar"))
21+
(is (= (url-encode "foo bar") "foo%20bar")))
22+
23+
(deftest test-url-decode
24+
(is (= (url-decode "foo%2Fbar") "foo/bar" ))
25+
(is (= (url-decode "foo%FE%FF%00%2Fbar" "UTF-16") "foo/bar"))
26+
(is (= (url-decode "%") "%")))
27+
28+
(deftest test-base64-encoding
29+
(let [str-bytes (.getBytes "foo?/+" "UTF-8")]
30+
(is (Arrays/equals str-bytes (base64-decode (base64-encode str-bytes))))))
31+
32+
(deftest test-form-encode
33+
(testing "strings"
34+
(are [x y] (= (form-encode x) y)
35+
"foo bar" "foo+bar"
36+
"foo+bar" "foo%2Bbar"
37+
"foo/bar" "foo%2Fbar")
38+
(is (= (form-encode "foo/bar" "UTF-16") "foo%FE%FF%00%2Fbar")))
39+
(testing "maps"
40+
(are [x y] (= (form-encode x) y)
41+
{"a" "b"} "a=b"
42+
{:a "b"} "a=b"
43+
{"a" 1} "a=1"
44+
{"a" "b" "c" "d"} "a=b&c=d"
45+
{"a" "b c"} "a=b+c")
46+
(is (= (form-encode {"a" "foo/bar"} "UTF-16") "a=foo%FE%FF%00%2Fbar"))))
47+
48+
(deftest test-form-decode-str
49+
(is (= (form-decode-str "foo=bar+baz") "foo=bar baz"))
50+
(is (nil? (form-decode-str "%D"))))
51+
52+
(deftest test-form-decode
53+
(are [x y] (= (form-decode x) y)
54+
"foo" "foo"
55+
"a=b" {"a" "b"}
56+
"a=b&c=d" {"a" "b" "c" "d"}
57+
"foo+bar" "foo bar"
58+
"a=b+c" {"a" "b c"}
59+
"a=b%2Fc" {"a" "b/c"})
60+
(is (= (form-decode "a=foo%FE%FF%00%2Fbar" "UTF-16")
61+
{"a" "foo/bar"})))

0 commit comments

Comments
 (0)