diff --git a/backend/src/app/media.clj b/backend/src/app/media.clj index a1d22c0832..0aa0cedbc2 100644 --- a/backend/src/app/media.clj +++ b/backend/src/app/media.clj @@ -30,7 +30,12 @@ [datoteka.io :as io]) (:import clojure.lang.XMLHandler + java.io.IOException java.io.InputStream + java.net.ConnectException + java.net.UnknownHostException + java.net.http.HttpConnectTimeoutException + java.net.http.HttpTimeoutException javax.xml.XMLConstants javax.xml.parsers.SAXParserFactory org.apache.commons.io.IOUtils @@ -318,9 +323,37 @@ {:size size :mtype mtype :format format}))] - (let [{:keys [body] :as response} (http/req! client - {:method :get :uri uri} - {:response-type :input-stream}) + (let [{:keys [body] :as response} + (try + (http/req! client + {:method :get :uri uri} + {:response-type :input-stream}) + (catch ConnectException cause + (ex/raise :type :validation + :code :unable-to-access-to-url + :hint "the url is unreachable or the connection was refused" + :cause cause)) + (catch UnknownHostException cause + (ex/raise :type :validation + :code :unable-to-access-to-url + :hint "the url host cannot be resolved" + :cause cause)) + (catch HttpConnectTimeoutException cause + (ex/raise :type :validation + :code :unable-to-access-to-url + :hint "the url connection timed out" + :cause cause)) + (catch HttpTimeoutException cause + (ex/raise :type :validation + :code :unable-to-access-to-url + :hint "the url request timed out" + :cause cause)) + (catch IOException cause + (ex/raise :type :validation + :code :unable-to-access-to-url + :hint "an I/O error occurred while downloading the url" + :cause cause))) + {:keys [size mtype]} (parse-and-validate response) path (tmp/tempfile :prefix "penpot.media.download.") written (io/write* path body :size size)] diff --git a/backend/test/backend_tests/rpc_media_test.clj b/backend/test/backend_tests/rpc_media_test.clj index d583565f39..488e75833a 100644 --- a/backend/test/backend_tests/rpc_media_test.clj +++ b/backend/test/backend_tests/rpc_media_test.clj @@ -248,6 +248,28 @@ (t/is (uuid? (:thumbnail-id result)))))) +(t/deftest media-object-from-url-command-when-url-is-unreachable + (let [prof (th/create-profile* 1) + proj (th/create-project* 1 {:profile-id (:id prof) + :team-id (:default-team-id prof)}) + file (th/create-file* 1 {:profile-id (:id prof) + :project-id (:default-project-id prof) + :is-shared false}) + ;; Use a URL that is guaranteed to be unreachable from the backend + url "http://192.0.2.1/image.png" + params {::th/type :create-file-media-object-from-url + ::rpc/profile-id (:id prof) + :file-id (:id file) + :is-local true + :url url} + out (th/command! params)] + + (let [error (:error out) + error-data (ex-data error)] + (t/is (th/ex-info? error)) + (t/is (= :validation (:type error-data))) + (t/is (= :unable-to-access-to-url (:code error-data)))))) + (t/deftest media-object-upload-command-when-file-is-deleted (let [prof (th/create-profile* 1) proj (th/create-project* 1 {:profile-id (:id prof)