diff --git a/ring-core/src/ring/middleware/multipart_params.clj b/ring-core/src/ring/middleware/multipart_params.clj index 1877566d..92763ae0 100644 --- a/ring-core/src/ring/middleware/multipart_params.clj +++ b/ring-core/src/ring/middleware/multipart_params.clj @@ -34,9 +34,13 @@ (defn- file-item-seq "Create a seq of FileItem instances from a request context." - [context] + [{:keys [max-file-size max-request-size] + :or {max-file-size -1, max-request-size -1}} context] (file-item-iterator-seq - (.getItemIterator (FileUpload.) context))) + (.getItemIterator (doto (FileUpload.) + (.setFileSizeMax max-file-size) + (.setSizeMax max-request-size)) + context))) (defn- parse-file-item "Parse a FileItemStream into a key-value pair. If the request is a file the @@ -51,9 +55,9 @@ (defn- parse-multipart-params "Parse a map of multipart parameters from the request." - [request encoding store] + [opts request encoding store] (->> (request-context request encoding) - (file-item-seq) + (file-item-seq opts) (map #(parse-file-item % store)) (reduce (fn [m [k v]] (assoc-conj m k v)) {}))) @@ -78,7 +82,7 @@ (:character-encoding request) "UTF-8") params (if (multipart-form? request) - (parse-multipart-params request encoding store) + (parse-multipart-params opts request encoding store) {})] (merge-with merge request {:multipart-params params} @@ -100,7 +104,11 @@ expect a map with :filename, content-type and :stream keys, and its return value will be used as the value for the parameter in the multipart parameter map. The default storage - function is the temp-file-store." + function is the temp-file-store. + + :max-request-size - maximum allowed size of the request in bytes. + + :max-file-size - maximum allowed size of a single uploaded file in bytes." [handler & [opts]] (fn [request] (-> request diff --git a/ring-core/test/ring/middleware/test/multipart_params.clj b/ring-core/test/ring/middleware/test/multipart_params.clj index 78a4aa93..41b487a5 100644 --- a/ring-core/test/ring/middleware/test/multipart_params.clj +++ b/ring-core/test/ring/middleware/test/multipart_params.clj @@ -77,5 +77,49 @@ (is (< (count (all-threads)) 100)))) +(defn- form-body-with-size [sz] + {:pre [(> sz 113)]} + (str "--XXXX\r\n" + "Content-Disposition: form-data;" + "name=\"upload\"; filename=\"test.txt\"\r\n" + "Content-Type: text/plain\r\n\r\n" + (apply str (repeat (- sz 113) "1")) "\r\n" + "--XXXX--")) + +(defn- request-of [form-body] + {:content-type "multipart/form-data; boundary=XXXX" + :content-length (count form-body) + :body (string-input-stream form-body)}) + +(defn- root-cause [e] + (let [cause (.getCause e)] + (if (and cause (not (= java.io.IOException (type cause)))) + (recur cause) + e))) + +(deftest test-max-size-settings + (testing "Respects max request size in bytes" + (let [handler (wrap-multipart-params identity {:max-request-size 300, :store string-store})] + + (let [response (handler (request-of (form-body-with-size 300)))] + (is (= (get-in response [:params "upload" :filename]) "test.txt"))) + + (is (thrown? org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException + (handler (request-of (form-body-with-size 500))))))) + + (testing "Respects max file size in bytes" + (let [handler (wrap-multipart-params identity {:max-file-size 300, :store string-store})] + + ; Actual file size is less than 300 + (let [response (handler (request-of (form-body-with-size 400)))] + (is (= (get-in response [:params "upload" :filename]) "test.txt"))) + + (try + (handler (request-of (form-body-with-size 500))) + (is false "Should fail with exception!") + (catch Exception e + (is (instance? org.apache.commons.fileupload.FileUploadBase$FileSizeLimitExceededException + (root-cause e)))))))) + (deftest multipart-params-request-test - (is (fn? multipart-params-request))) \ No newline at end of file + (is (fn? multipart-params-request)))