diff --git a/project.clj b/project.clj index 8a7f4ccdb..03d0b467b 100644 --- a/project.clj +++ b/project.clj @@ -24,7 +24,6 @@ [cljsjs/moment "2.10.6-4"] [funcool/struct "1.0.0"] [funcool/lentes "1.2.0"] - [funcool/httpurr "0.6.2"] [funcool/promesa "1.5.0"] [funcool/beicon "2.3.0"] [funcool/cuerdas "1.0.2"] diff --git a/src/uxbox/main/repo/impl.cljs b/src/uxbox/main/repo/impl.cljs index d2fe8ead7..959d208a3 100644 --- a/src/uxbox/main/repo/impl.cljs +++ b/src/uxbox/main/repo/impl.cljs @@ -8,9 +8,8 @@ (:require [clojure.walk :as walk] [promesa.core :as p :include-macros true] [beicon.core :as rx] - [httpurr.client.xhr :as http] - [httpurr.status :as status] [uxbox.config :refer (url)] + [uxbox.util.http :as http] [uxbox.util.storage :refer (storage)] [uxbox.util.transit :as t]) (:import [goog.Uri QueryData])) @@ -23,7 +22,7 @@ (defn- handle-http-status [{:keys [body status] :as response}] - (if (status/success? response) + (if (http/success? response) (rx/of {:status status :payload body}) (rx/throw {:status status :payload body}))) @@ -41,8 +40,9 @@ (.extend data (clj->js params)) (.toString data))) -(defn- send! - [{:keys [body headers auth method query url] :or {auth true} :as request}] +(defn send! + [{:keys [body headers auth method query url response-type] + :or {auth true response-type :text}}] (let [headers (merge {} (when (map? body) +headers+) headers @@ -51,9 +51,9 @@ :url url :headers headers :query-string (when query (encode-query query)) - :body (if (map? body) (t/encode body) body)}] - (->> (http/send! request) - (rx/from-promise) + :body (if (map? body) (t/encode body) body)} + options {:response-type response-type}] + (->> (http/send! request options) (rx/map conditional-decode) (rx/mapcat handle-http-status)))) diff --git a/src/uxbox/util/http.cljs b/src/uxbox/util/http.cljs new file mode 100644 index 000000000..f0e9f2208 --- /dev/null +++ b/src/uxbox/util/http.cljs @@ -0,0 +1,100 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) 2016 Andrey Antukh + +(ns uxbox.util.http + "A http client with rx streams interface." + (:refer-clojure :exclude [get]) + (:require [beicon.core :as rx] + [goog.events :as events] + [clojure.string :as str]) + (:import [goog.net ErrorCode EventType] + [goog.net.XhrIo ResponseType] + [goog.net XhrIo] + [goog.Uri QueryData] + [goog Uri])) + +(defn translate-method + [method] + (case method + :head "HEAD" + :options "OPTIONS" + :get "GET" + :post "POST" + :put "PUT" + :patch "PATCH" + :delete "DELETE" + :trace "TRACE")) + +(defn- normalize-headers + [headers] + (reduce-kv (fn [acc k v] + (assoc acc (str/lower-case k) v)) + {} (js->clj headers))) + +(defn- translate-error-code + [code] + (condp = code + ErrorCode.TIMEOUT :timeout + ErrorCode.EXCEPTION :exception + ErrorCode.HTTP_ERROR :http + ErrorCode.ABORT :abort)) + +(defn- translate-response-type + [type] + (println "translate-response-type" type) + (case type + :text ResponseType.TEXT + :blob ResponseType.BLOB + ResponseType.DEFAULT)) + +(defn- create-url + [url qs qp] + (let [uri (Uri. url)] + (when qs (.setQuery uri qs)) + (when qp + (let [dt (.createFromMap QueryData (clj->js qp))] + (.setQueryData uri dt))) + (.toString uri))) + +(defn- fetch + [{:keys [method url query-string query-params headers body] :as request} + {:keys [timeout credentials? response-type] + :or {timeout 0 credentials? false response-type :text}}] + (println "fetch$1" url) + (let [uri (create-url url query-string query-params) + headers (if headers (clj->js headers) #js {}) + method (translate-method method) + xhr (doto (XhrIo.) + (.setResponseType (translate-response-type response-type)) + (.setWithCredentials credentials?) + (.setTimeoutInterval timeout))] + (rx/create + (fn [sink] + (letfn [(on-complete [event] + (if (or (= (.getLastErrorCode xhr) ErrorCode.HTTP_ERROR) + (.isSuccess xhr)) + (sink {:status (.getStatus xhr) + :body (.getResponse xhr) + :headers (normalize-headers + (.getResponseHeaders xhr))}) + (sink (let [type (-> (.getLastErrorCode xhr) + (translate-error-code)) + message (.getLastError xhr)] + (ex-info message {:type type})))))] + + (events/listen xhr EventType.COMPLETE on-complete) + (.send xhr uri method body headers) + #(.abort xhr)))))) + +(defn success? + [{:keys [status]}] + (<= 200 status 299)) + +(defn send! + ([request] + (send! request nil)) + ([request options] + (fetch request options)))