diff --git a/src/clj_rpc/context.clj b/src/clj_rpc/context.clj index cb6c9c5..8b92f23 100644 --- a/src/clj_rpc/context.clj +++ b/src/clj_rpc/context.clj @@ -2,10 +2,6 @@ (:require [ring.middleware.cookies :as cookies] [clj-rpc.user-data :as data])) - -;;the options availbe to context -(def context-options [:require-context :params-check :params-inject]) - (defn wrap-client-ip "let :remote-addr represents the real client ip even though the client connects through a proxy server" @@ -37,7 +33,10 @@ (defn add-context "add context to specific command - options is a map include several keys + options is a collection include several keys + like [ [:requre-context true] [:params-checks ...] [... ...] ] + + the meaning of option: :require-context (true or false default false) whether this command must have context :params-checks @@ -53,8 +52,7 @@ if client invoke (fun param1 param2) then the acutally invoke will be like (fun client-ip server-name param1 param2)" [command options] - {:pre [ (every? #(% (set context-options)) (keys options))]} - (merge command options)) + (assoc command :options options)) (defn add-context-to-command-map "add context options to all the command in the command-map @@ -66,69 +64,66 @@ "return true if method-reqeust error else false" [method-request] - (boolean (or (:code method-request) - (:message method-request)))) + (boolean (:error method-request))) -(defmulti pre-handle-method-request - "pre handle method request - return new method-request if successful - else return error like {:code error-code :message error-message}" - (fn [option-key option request method-request] - (if (not (error-method-request? method-request)) - option-key - :default))) +(defmulti render-method-request + "adjust method request by option + return new method-request or nil (not any change)" + (fn [option-key option-value request method-request] + option-key)) -;;pre handle :require-context option -;;return method-request if the command don't need authrization or -;;the context include the authorization information -;;else return {:code :unauthorized} -(defmethod pre-handle-method-request :require-context - [_ option request method-request] - (let [context (get request :context)] - (if (and option - (not (seq context))) - {:code :unauthorized} - method-request))) +;;render method-request :require-context option +(defmethod render-method-request :require-context + [_ option-value request method-request] + (when (and option-value + (not (seq (get request :context) ))) + (assoc method-request :error {:code :unauthorized}))) -;;pre handle :params-check -;;return method-request if the params of the command satisfy -;;the requirement of the command provided specific context -;;else return {:code invalid-params :message error-message} -(defmethod pre-handle-method-request :params-check - [_ option request method-request] - (let [context (get request :context) - params (:params method-request) +;;render method-request :params-check option +;;return method-request with params error or nil +(defmethod render-method-request :params-check + [_ option-value request method-request] + (let [{context :context} request + {params :params} method-request fn-check (fn [[k v]] (if (not= (nth params k) (get-in context v)) {:code :invalid-params - :message (str "the " k "th param must be " (get-in context v))}))] - (or (->> option - (map fn-check ) - (filter identity) - first) - method-request))) + :message (str "the " k "th param must be " + (get-in context v))}))] + (when-let [error (->> option-value + (map fn-check ) + (filter identity) + first)] + (assoc method-request :error + error)))) -;;pre handle :params-inject +;;render method-request :params-inject option ;;inject the params from request needed by the command -;;into the actualparams -;;return the new method-request -(defmethod pre-handle-method-request :params-inject - [_ option request method-request] - (if option +;;into the actual params +;;return the new method-request or nil +(defmethod render-method-request :params-inject + [_ option-value request method-request] + (when option-value (update-in method-request [:params] - #(concat (map (partial get-in request) option) %)) - method-request)) + #(concat (map (partial get-in request) option-value) %)))) -;;default return origin method-request -(defmethod pre-handle-method-request :default - [_ option request method-request] - method-request) +;;default throw RuntimeException +(defmethod render-method-request :default + [option-key option-value request method-request] + (throw (RuntimeException. (str "invalid command option , method-requet: " + method-request " option: " + option-key + " => " option-value) )) ) -(defn check-context - "handle options of the command by order - and return new method-request or error message" +(defn adjust-method-request + "return new method-request (possible with error message)" [cmd request method-request] - (reduce (fn [m-r k] (pre-handle-method-request k (get cmd k) request m-r)) - method-request - context-options)) + (loop [options (:options cmd) + m-r method-request] + (let [[option-key option-value] (first options)] + (if (or (nil? option-key) (error-method-request? m-r)) + m-r + (recur (rest options) + (or (render-method-request option-key option-value request m-r) + m-r)))))) diff --git a/src/clj_rpc/rpc.clj b/src/clj_rpc/rpc.clj index b44bc27..6e2425c 100644 --- a/src/clj_rpc/rpc.clj +++ b/src/clj_rpc/rpc.clj @@ -43,10 +43,12 @@ "execute a function f with params return rpc response id : used for generate the response" - [f params id] + [f {:keys [params id error]}] (try - (if f - (mk-response (apply f params) id) - (mk-error :method-not-found id)) + (if error + (mk-error (:code error) id (:message error)) + (if f + (mk-response (apply f params) id) + (mk-error :method-not-found id))) (catch ArityException e (mk-error :invalid-params id (.getMessage e)) ) (catch Exception e (mk-error :internal-error id (.getMessage e))))) diff --git a/src/clj_rpc/server.clj b/src/clj_rpc/server.clj index fc7fcad..af187cc 100644 --- a/src/clj_rpc/server.clj +++ b/src/clj_rpc/server.clj @@ -37,7 +37,7 @@ (defn export-commands "export all functions (fn-names is null or empty) or specify functions in the namespace ns - + invoker of this method must notice the order of the options options: is a map include several keys :require-context (true or false default false) whether this command must have context @@ -52,7 +52,7 @@ (export-commands \"clojure.core\" nil) (export-commands 'clojure.core ['+]) (export-commands 'clojure.core [\"+\"] - {:require-context true :params-check {0 [:id]}})" + [ [:require-context true] [:params-check {0 [:id]}] ])" [ns fn-names & [options]] (let [ns (symbol ns)] (require ns) @@ -68,14 +68,11 @@ return the execute result" [command-map request method-request] (logging/debug "execute-command == " method-request) - (let [id (:id method-request) - cmd (command-map (:method method-request)) + (let [cmd (command-map (:method method-request)) f (and cmd (command/.func cmd)) - new-method-request (context/check-context cmd request method-request)] - (if (context/error-method-request? new-method-request) - (rpc/mk-error (:code new-method-request) id - (:message new-method-request)) - (rpc/execute-method f (:params new-method-request) id)))) + new-method-request (context/adjust-method-request + cmd request method-request)] + (rpc/execute-method f new-method-request))) (defn help-commands "return the command list" diff --git a/test/clj_rpc/test/context.clj b/test/clj_rpc/test/context.clj index b2e0b08..dea1f3c 100644 --- a/test/clj_rpc/test/context.clj +++ b/test/clj_rpc/test/context.clj @@ -7,37 +7,39 @@ (deftest check-add-context-to-command-map (let [command-map {"str" (command/mk-command "str" #'str) "concat" (command/mk-command "concat" #'concat)} - options {:require-context true :params-check {0 1}} + options [ [:require-context true] [:params-check {0 1}]] new-map (add-context-to-command-map command-map options) vs (vals new-map)] - (is (= (get-in (first vs) [:params-check]) {0 1})) - (is (= (get-in (second vs) [:params-check]) {0 1})))) + (is (= (get (first vs) :options) options)) + (is (= (get (second vs) :options) options)))) (facts "check whether or not the command and params statifies context requirement" (let [cmd (-> (command/mk-command "concat" #'concat) - (add-context {:require-context true - :params-check {0 [:p1] 1 [:p2]}})) + (add-context [ [:require-context true] + [:params-check {0 [:p1] 1 [:p2]}]])) cmd-not-require (command/mk-command "str" #'str) method-request {:method "method" :params [ [1 2] [3 4] [5 6]]}] ;;stastify authorized and params requirement - (check-context cmd {:context {:p1 [1 2] :p2 [3 4]}} + (adjust-method-request cmd {:context {:p1 [1 2] :p2 [3 4]}} method-request) => method-request ;;do not statify authorization requirement - (check-context cmd nil method-request) => (contains {:code :unauthorized}) - (check-context cmd {} method-request) => (contains {:code :unauthorized}) + (adjust-method-request cmd nil method-request) => (contains {:error {:code :unauthorized}}) + (adjust-method-request cmd {} method-request) => (contains {:error {:code :unauthorized}}) ;;do not statify params check requirement - (check-context cmd {:context {:p1 [1 2] }} method-request) => (contains {:code :invalid-params}) + (get-in (adjust-method-request cmd {:context {:p1 [1 2] }} method-request) + [:error :code]) => :invalid-params ;;do not require context check - (check-context cmd-not-require nil {:method "method" :params ["str1" "str2"]}) + (adjust-method-request cmd-not-require nil {:method "method" :params ["str1" "str2"]}) => {:method "method" :params ["str1" "str2"]})) +;.;. The work itself praises the master. -- CPE Bach (facts "test inject parasm" (let [cmd (-> (command/mk-command "str" #'str) (add-context {:params-inject [ [:remote-addr] [:inject]]})) ] - (check-context cmd {:remote-addr "192.168.1.1" :inject "inject-param2"} - {:method "method" :params ["p1" "p2"]}) + (adjust-method-request cmd {:remote-addr "192.168.1.1" :inject "inject-param2"} + {:method "method" :params ["p1" "p2"]}) => {:method "method" :params ["192.168.1.1" "inject-param2" "p1" "p2"]})) diff --git a/test/clj_rpc/test/integrate.clj b/test/clj_rpc/test/integrate.clj index 0a0bac6..84d9dcb 100644 --- a/test/clj_rpc/test/integrate.clj +++ b/test/clj_rpc/test/integrate.clj @@ -36,9 +36,10 @@ (server/export-commands 'clojure.core nil) (server/export-commands "clj-rpc.command" ['mk-command "get-commands"]) (server/export-commands "clj-rpc.test.integrate" ['fn-with-context-check] - {:require-context true :params-check {0 [:username]} }) + [ [:require-context true] + [:params-check {0 [:username]}]] ) (server/export-commands 'clojure.core ['str] - {:params-inject [ [:remote-addr]]}) + [ [:params-inject [ [:remote-addr]]]]) (server/export-commands 'clj-rpc.test.integrate ['fn-save-data 'fn-get-data 'fn-delete-data]))