Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Modify the WWW-Authentication header create function. Let it works.

  • Loading branch information...
commit 817ada0bec645527cd8d5ad941d114633ae05fe0 1 parent 0dd0e29
@Ruiyun authored
Showing with 454 additions and 448 deletions.
  1. +269 −271 src/cljain/dum.clj
  2. +155 −148 src/cljain/sip/header.clj
  3. +30 −29 test/cljain/test/register.clj
View
540 src/cljain/dum.clj
@@ -1,271 +1,269 @@
-(ns ^{:doc "The SIP DSL by Clojure.
- Here is a simplest example show how to use it:
-
- (use 'cljain.dum)
- (require '[cljain.sip.core :as sip]
- '[cljain.sip.address :as addr])
-
- (defmethod handle-request :MESSAGE [request & [transaction]]
- (println \"Received: \" (.getContent request))
- (send-response! 200 :in transaction :pack \"I receive the message from myself.\"))
-
- (global-set-account :user \"bob\" :domain \"localhost\" :display-name \"Bob\" :password \"thepwd\")
- (sip/global-bind-sip-provider! (sip/sip-provider! \"my-app\" \"localhost\" 5060 \"udp\"))
- (sip/set-listener! (dum-listener))
- (sip/start!)
-
- (send-request! :MESSAGE :to (addr/address \"sip:bob@localhost\") :pack \"Hello, Bob.\"
- :on-success (fn [& {:keys [response]}] (println \"Fine! response: \" (.getContent response)))
- :on-failure (fn [& {:keys [response]}] (println \"Oops!\" (.getStatusCode response)))
- :on-timeout (fn [_] (println \"Timeout, try it later.\")))
-
- Remember, if you want send REGISTER to sip registry, please use the 'register-to' function, that will
- help you to deal the automatic rigister refresh:
-
- (register-to (addr/address \"sip:the-registry\") 3600
- :on-success #(prn \"Register success.\")
- :on-failure #(prn \"Register failed.\")
- :on-refreshed #(prn \"Refreshed fine.\")
- :on-refresh-failed #(prn \"Refresh failed.\"))
-
- This version cljain.dum has some limitation that if you want auto-refresh work correctly, you must use
- 'global-set-account' to give a root binding with *current-account* like previous."
- :author "ruiyun"
- :added "0.2.0"}
- cljain.dum
- (:use [clojure.string :only [upper-case]])
- (:require [cljain.sip.core :as core]
- [cljain.sip.header :as header]
- [cljain.sip.address :as addr]
- [cljain.sip.message :as msg]
- [cljain.sip.dialog :as dlg]
- [cljain.sip.transaction :as trans]
- [ruiyun.tools.timer :as timer]
- [clojure.tools.logging :as log])
- (:import [javax.sip Transaction SipProvider SipFactory]
- [javax.sip.message Request Response]
- [javax.sip.address Address]
- [javax.sip.header HeaderAddress]
- [gov.nist.javax.sip.clientauthutils AccountManager UserCredentials AuthenticationHelper]
- [gov.nist.javax.sip SipStackExt]))
-
-(def ^{:doc "A map contain these four fields: :user, :domain, :password and :display-name."
- :added "0.4.0"
- :dynamic true}
- *current-account*)
-
-(defn global-set-account
- "Give the *current-account* a root binding.
- Although you can use the clojure dynamic binding form, but use this function in this version
- cljian.dum is more recommended."
- {:added "0.4.0"}
- [& {:keys [user domain password display-name] :as account}]
- (alter-var-root #'*current-account* (fn [_] account)))
-
-(defmulti handle-request (fn [request & [transaction dialog]] (keyword (.getMethod request))))
-
-(defrecord AccountManagerImpl []
- AccountManager
- (getCredentials [this challenged-transaction realm]
- (reify UserCredentials
- (getUserName [_] (*current-account* :user))
- (getPassword [_] (*current-account* :password))
- (getSipDomain [_] (*current-account* :domain)))))
-
-(defn dum-listener
- "Create a dum default event listener.
- You can use it for 'cljain.sip.core/set-listener!' function."
- {:added "0.4.0"}
- []
- {:request (fn [request transaction dialog]
- (let [transaction (or transaction (core/new-server-transaction! request))]
- (handle-request request transaction dialog)))
- :response (fn [response transaction dialog]
- (when (not (nil? transaction))
- (let [{process-success :on-success
- process-failure :on-failure} (.getApplicationData transaction)
- status-code (.getStatusCode response)
- lead-number-of-status-code (quot status-code 100)]
- (cond ; ignore 1xx provisional response
- (= lead-number-of-status-code 2) ; 2xx means final success response
- (and process-success (process-success :transaction transaction :dialog dialog :response response))
-
- (or (= status-code Response/UNAUTHORIZED)
- (= status-code Response/PROXY_AUTHENTICATION_REQUIRED)) ; need authentication
- (let [header-factory (.createHeaderFactory core/sip-factory)
- sip-stack (.getSipStack (core/sip-provider))
- auth-helper (.getAuthenticationHelper sip-stack (AccountManagerImpl.) header-factory)
- client-trans-with-auth (.handleChallenge auth-helper response transaction (core/sip-provider) 5)]
- (.setApplicationData client-trans-with-auth (.getApplicationData transaction))
- (trans/send-request! client-trans-with-auth))
-
- (> lead-number-of-status-code 3) ; 4xx, 5xx, 6xx means error
- (and process-failure (process-failure :transaction transaction :dialog dialog :response response))))))
- :timeout (fn [transaction _]
- (prn "now calling process-timeout.")
- (let [process-timeout (:on-timeout (.getApplicationData transaction))]
- (and process-timeout (process-timeout :transaction transaction))))})
-
-(defn legal-content?
- "Check the content is a string or a map with :type, :sub-type, :length and :content keys."
- {:added "0.2.0"}
- [content]
- (or (string? content)
- (and (map? content)
- (= (sort [:type :sub-type :content]) (sort (keys content))))))
-
-(defn- set-content!
- "Parse the :pack argument from 'send-request!' and 'send-response!',
- then try to set the appropriate Content-Type header and content to the message."
- {:added "0.2.0"}
- [message content]
- (when (not (nil? content))
- (let [content-type (name (or (:type content) "text"))
- content-sub-type (name (or (:sub-type content) "plain"))
- content-type-header (header/content-type content-type content-sub-type)
- content (or (:content content) content)]
- (.setContent message content content-type-header))))
-
-(defn send-request!
- "Fluent style sip message send function.
-
- The simplest example just send a trivial MESSAGE:
-
- (send-request! :MESSAGE :to (sip-address \"192.168.1.128\"))
- (send-request! :INFO :in dialog-with-bob)
-
- More complicate example:
-
- (send-request! \"MESSAGE\" :pack \"Welcome\" :to (sip-address \"192.168.1.128\" :user \"bob\") :use \"UDP\"
- :on-success #(prn %1 %2 %3) :on-failure #(prn %1 %2 %3) :on-timeout #(prn %))
-
- If the pack content is not just a trivial string, provide a well named funciont
- to return a content map like this is recommended:
-
- {:type \"application\"
- :sub-type \"pidf-diff+xml\"
- :content content-object}"
- {:added "0.2.0"}
- [message & {content :pack
- to-address :to
- transport :use
- dialog :in
- more-headers :more-headers
- on-success :on-success
- on-failure :on-failure
- on-timeout :on-timeout}]
- {:pre [(core/provider-can-be-found?)
- (bound? #'*current-account*)
- (or (nil? content) (legal-content? content))
- (or (and (= (upper-case (name message)) "REGISTER") (nil? to-address))
- (addr/address? to-address))
- (or (nil? transport) (#{"UDP" "udp" "TCP" "tcp"} transport))
- (or (nil? dialog) (core/dialog? dialog))
- (or (nil? more-headers) (sequential? more-headers))
- (or (nil? on-success) (fn? on-success))
- (or (nil? on-failure) (fn? on-failure))
- (or (nil? on-timeout) (fn? on-timeout))]}
- (let [{:keys [user domain display-name]} *current-account*
- {:keys [ip port transport]} (if (nil? transport)
- (core/listening-point)
- (core/listening-point transport))
- domain (or domain ip)
- method (upper-case (name message))]
- (if (not (nil? dialog))
- (let [request (dlg/create-request dialog method)
- transaction (core/new-client-transcation! request)]
- (dlg/send-request! dialog transaction)
- request)
- (let [request-uri (if (nil? to-address)
- (throw (IllegalArgumentException. "Require the ':to' option for send an out of dialog request."))
- (.getURI to-address))
- from-address (addr/sip-address domain :user user :display-name display-name)
- from-header (header/from from-address (header/gen-tag))
- to-header (if (= method "REGISTER") ; for REGISTER, To header's uri should equal the From header's.
- (header/to (.getAddress from-header) nil) ; tag will be auto generated in transaction
- (header/to to-address nil))
- contact-header (header/contact (addr/sip-address ip :port port :transport transport :user user))
- call-id-header (core/gen-call-id-header)
- via-header (header/via ip port transport nil) ; via branch will be auto generated before message sent.
- request (msg/request method request-uri from-header call-id-header to-header via-header contact-header more-headers)
- transaction (core/new-client-transcation! request)]
- (.setApplicationData transaction {:on-success on-success, :on-failure on-failure :on-timeout on-timeout})
- (set-content! request content)
- (trans/send-request! transaction)
- request))))
-
-(defn send-response!
- "Send response with a server transactions."
- {:added "0.2.0"}
- [status-code & {transaction :in, content :pack, transport :use, more-headers :more-headers}]
- {:pre [(core/provider-can-be-found?)
- (bound? #'*current-account*)
- (core/transaction? transaction)
- (or (nil? content) (legal-content? content))
- (or (nil? transport) (#{"UDP" "udp" "TCP" "tcp"} transport))
- (or (nil? more-headers) (sequential? more-headers))]}
- (let [{:keys [ip port transport]} (if (nil? transport)
- (core/listening-point)
- (core/listening-point transport))
- user (*current-account* :user)
- contact-header (header/contact (addr/sip-address ip :port port :transport transport :user user))
- request (.getRequest transaction)
- response (msg/response status-code request contact-header more-headers)]
- (set-content! response content)
- (trans/send-response! transaction response)))
-
-(def ^{:doc "Store contexts for the register auto refresh."
- :added "0.3.0"
- :private true}
- register-ctx-map (atom {}))
-
-(defn register-to
- "Send REGISTER sip message to target registry server, and auto refresh register before
- expired.
-
- Notice: please call 'global-set-account' before you call 'register-to'. in this version,
- use dynamic binding form to bind *current-account* can not work for auto-refresh."
- {:added "0.3.0"}
- [registry-address expires-seconds & {:keys [on-success on-failure on-refreshed on-refresh-failed]}]
- {:pre [(addr/address? registry-address)
- (> expires-seconds 38) ; because a transaction timeout is 32 seconds
- (or (nil? on-success) (fn? on-success))
- (or (nil? on-failure) (fn? on-failure))
- (or (nil? on-refreshed) (fn? on-refreshed))
- (or (nil? on-refresh-failed) (fn? on-refresh-failed))]}
- (let [expires-header (header/expires expires-seconds)
- safer-interval-milliseconds (* (- expires-seconds 5) 1000)
- register-ctx (get @register-ctx-map registry-address)]
- (when (nil? register-ctx)
- (send-request! :REGISTER :to registry-address
- :more-headers [expires-header]
- :on-success (fn [& {:keys [transaction]}]
- (swap! register-ctx-map assoc registry-address
- {:timer (timer/run-task! #(let [request (.clone (get-in @register-ctx-map [registry-address :request ]))
- request (msg/inc-sequence-number! request)
- request (doto request (.removeHeader "Via"))
- transaction (core/new-client-transcation! request)]
- (.setApplicationData transaction
- {:on-success (fn [& _] (and on-refreshed (on-refreshed)))
- :on-failure (fn [& _] (and on-refresh-failed (on-refresh-failed)))
- :on-timeout (fn [& _] (and on-refresh-failed (on-refresh-failed)))})
- (trans/send-request! transaction)
- (swap! register-ctx-map assoc-in [registry-address :request ] request))
- :by (timer/timer "Register-Refresh")
- :delay safer-interval-milliseconds
- :period safer-interval-milliseconds
- :on-exception #(log/warn "Register refresh exception: " %))
- :request (trans/request transaction)})
- (prn "current register-ctx-map: " @register-ctx-map)
- (and on-success (on-success)))
- :on-failure (fn [& _] (and on-failure (on-failure)))
- :on-timeout (fn [& _] (and on-failure (on-failure)))))))
-
-(defn unregister-to
- "Send REGISTER sip message with expires 0 for unregister.
- And the auto-refresh timer will be canceled."
- {:added "0.3.0"}
- [registry-address]
- (let [refresh-timer (get-in @register-ctx-map [registry-address :timer])]
- (and refresh-timer (timer/cancel! refresh-timer))
- (swap! register-ctx-map dissoc registry-address)))
+(ns ^{:doc "The SIP DSL by Clojure.
+ Here is a simplest example show how to use it:
+
+ (use 'cljain.dum)
+ (require '[cljain.sip.core :as sip]
+ '[cljain.sip.address :as addr])
+
+ (defmethod handle-request :MESSAGE [request transcation _]
+ (println \"Received: \" (.getContent request))
+ (send-response! 200 :in transaction :pack \"I receive the message from myself.\"))
+
+ (global-set-account :user \"bob\" :domain \"localhost\" :display-name \"Bob\" :password \"thepwd\")
+ (sip/global-bind-sip-provider! (sip/sip-provider! \"my-app\" \"localhost\" 5060 \"udp\"))
+ (sip/set-listener! (dum-listener))
+ (sip/start!)
+
+ (send-request! :MESSAGE :to (addr/address \"sip:bob@localhost\") :pack \"Hello, Bob.\"
+ :on-success (fn [& {:keys [response]}] (println \"Fine! response: \" (.getContent response)))
+ :on-failure (fn [& {:keys [response]}] (println \"Oops!\" (.getStatusCode response)))
+ :on-timeout (fn [_] (println \"Timeout, try it later.\")))
+
+ Remember, if you want send REGISTER to sip registry, please use the 'register-to' function, that will
+ help you to deal the automatic rigister refresh:
+
+ (register-to (addr/address \"sip:the-registry\") 3600
+ :on-success #(prn \"Register success.\")
+ :on-failure #(prn \"Register failed.\")
+ :on-refreshed #(prn \"Refreshed fine.\")
+ :on-refresh-failed #(prn \"Refresh failed.\"))
+
+ This version cljain.dum has some limitation that if you want auto-refresh work correctly, you must use
+ 'global-set-account' to give a root binding with *current-account* like previous."
+ :author "ruiyun"
+ :added "0.2.0"}
+ cljain.dum
+ (:use [clojure.string :only [upper-case]])
+ (:require [cljain.sip.core :as core]
+ [cljain.sip.header :as header]
+ [cljain.sip.address :as addr]
+ [cljain.sip.message :as msg]
+ [cljain.sip.dialog :as dlg]
+ [cljain.sip.transaction :as trans]
+ [ruiyun.tools.timer :as timer]
+ [clojure.tools.logging :as log])
+ (:import [javax.sip Transaction SipProvider SipFactory]
+ [javax.sip.message Request Response]
+ [javax.sip.address Address]
+ [javax.sip.header HeaderAddress]
+ [gov.nist.javax.sip.clientauthutils AccountManager UserCredentials AuthenticationHelper]
+ [gov.nist.javax.sip SipStackExt]))
+
+(def ^{:doc "A map contain these four fields: :user, :domain, :password and :display-name."
+ :added "0.4.0"
+ :dynamic true}
+ *current-account*)
+
+(defn global-set-account
+ "Give the *current-account* a root binding.
+ Although you can use the clojure dynamic binding form, but use this function in this version
+ cljian.dum is more recommended."
+ {:added "0.4.0"}
+ [& {:keys [user domain password display-name] :as account}]
+ (alter-var-root #'*current-account* (fn [_] account)))
+
+(defmulti handle-request (fn [request & [transaction dialog]] (keyword (.getMethod request))))
+
+(defrecord AccountManagerImpl []
+ AccountManager
+ (getCredentials [this challenged-transaction realm]
+ (reify UserCredentials
+ (getUserName [_] (*current-account* :user))
+ (getPassword [_] (*current-account* :password))
+ (getSipDomain [_] (*current-account* :domain)))))
+
+(defn dum-listener
+ "Create a dum default event listener.
+ You can use it for 'cljain.sip.core/set-listener!' function."
+ {:added "0.4.0"}
+ []
+ {:request (fn [request transaction dialog]
+ (let [transaction (or transaction (core/new-server-transaction! request))]
+ (handle-request request transaction dialog)))
+ :response (fn [response transaction dialog]
+ (when (not (nil? transaction))
+ (let [{process-success :on-success
+ process-failure :on-failure} (.getApplicationData transaction)
+ status-code (.getStatusCode response)
+ lead-number-of-status-code (quot status-code 100)]
+ (cond ; ignore 1xx provisional response
+ (= lead-number-of-status-code 2) ; 2xx means final success response
+ (and process-success (process-success :transaction transaction :dialog dialog :response response))
+
+ (or (= status-code Response/UNAUTHORIZED)
+ (= status-code Response/PROXY_AUTHENTICATION_REQUIRED)) ; need authentication
+ (let [header-factory (.createHeaderFactory core/sip-factory)
+ sip-stack (.getSipStack (core/sip-provider))
+ auth-helper (.getAuthenticationHelper sip-stack (AccountManagerImpl.) header-factory)
+ client-trans-with-auth (.handleChallenge auth-helper response transaction (core/sip-provider) 5)]
+ (.setApplicationData client-trans-with-auth (.getApplicationData transaction))
+ (trans/send-request! client-trans-with-auth))
+
+ (> lead-number-of-status-code 3) ; 4xx, 5xx, 6xx means error
+ (and process-failure (process-failure :transaction transaction :dialog dialog :response response))))))
+ :timeout (fn [transaction _]
+ (let [process-timeout (:on-timeout (.getApplicationData transaction))]
+ (and process-timeout (process-timeout :transaction transaction))))})
+
+(defn legal-content?
+ "Check the content is a string or a map with :type, :sub-type, :length and :content keys."
+ {:added "0.2.0"}
+ [content]
+ (or (string? content)
+ (and (map? content)
+ (= (sort [:type :sub-type :content]) (sort (keys content))))))
+
+(defn- set-content!
+ "Parse the :pack argument from 'send-request!' and 'send-response!',
+ then try to set the appropriate Content-Type header and content to the message."
+ {:added "0.2.0"}
+ [message content]
+ (when (not (nil? content))
+ (let [content-type (name (or (:type content) "text"))
+ content-sub-type (name (or (:sub-type content) "plain"))
+ content-type-header (header/content-type content-type content-sub-type)
+ content (or (:content content) content)]
+ (.setContent message content content-type-header))))
+
+(defn send-request!
+ "Fluent style sip message send function.
+
+ The simplest example just send a trivial MESSAGE:
+
+ (send-request! :MESSAGE :to (sip-address \"192.168.1.128\"))
+ (send-request! :INFO :in dialog-with-bob)
+
+ More complicate example:
+
+ (send-request! \"MESSAGE\" :pack \"Welcome\" :to (sip-address \"192.168.1.128\" :user \"bob\") :use \"UDP\"
+ :on-success #(prn %1 %2 %3) :on-failure #(prn %1 %2 %3) :on-timeout #(prn %))
+
+ If the pack content is not just a trivial string, provide a well named funciont
+ to return a content map like this is recommended:
+
+ {:type \"application\"
+ :sub-type \"pidf-diff+xml\"
+ :content content-object}"
+ {:added "0.2.0"}
+ [message & {content :pack
+ to-address :to
+ transport :use
+ dialog :in
+ more-headers :more-headers
+ on-success :on-success
+ on-failure :on-failure
+ on-timeout :on-timeout}]
+ {:pre [(core/provider-can-be-found?)
+ (bound? #'*current-account*)
+ (or (nil? content) (legal-content? content))
+ (or (and (= (upper-case (name message)) "REGISTER") (nil? to-address))
+ (addr/address? to-address))
+ (or (nil? transport) (#{"UDP" "udp" "TCP" "tcp"} transport))
+ (or (nil? dialog) (core/dialog? dialog))
+ (or (nil? more-headers) (sequential? more-headers))
+ (or (nil? on-success) (fn? on-success))
+ (or (nil? on-failure) (fn? on-failure))
+ (or (nil? on-timeout) (fn? on-timeout))]}
+ (let [{:keys [user domain display-name]} *current-account*
+ {:keys [ip port transport]} (if (nil? transport)
+ (core/listening-point)
+ (core/listening-point transport))
+ domain (or domain ip)
+ method (upper-case (name message))]
+ (if (not (nil? dialog))
+ (let [request (dlg/create-request dialog method)
+ transaction (core/new-client-transcation! request)]
+ (dlg/send-request! dialog transaction)
+ request)
+ (let [request-uri (if (nil? to-address)
+ (throw (IllegalArgumentException. "Require the ':to' option for send an out of dialog request."))
+ (.getURI to-address))
+ from-address (addr/sip-address domain :user user :display-name display-name)
+ from-header (header/from from-address (header/gen-tag))
+ to-header (if (= method "REGISTER") ; for REGISTER, To header's uri should equal the From header's.
+ (header/to (.getAddress from-header) nil) ; tag will be auto generated in transaction
+ (header/to to-address nil))
+ contact-header (header/contact (addr/sip-address ip :port port :transport transport :user user))
+ call-id-header (core/gen-call-id-header)
+ via-header (header/via ip port transport nil) ; via branch will be auto generated before message sent.
+ request (msg/request method request-uri from-header call-id-header to-header via-header contact-header more-headers)
+ transaction (core/new-client-transcation! request)]
+ (.setApplicationData transaction {:on-success on-success, :on-failure on-failure :on-timeout on-timeout})
+ (set-content! request content)
+ (trans/send-request! transaction)
+ request))))
+
+(defn send-response!
+ "Send response with a server transactions."
+ {:added "0.2.0"}
+ [status-code & {transaction :in, content :pack, transport :use, more-headers :more-headers}]
+ {:pre [(core/provider-can-be-found?)
+ (bound? #'*current-account*)
+ (core/transaction? transaction)
+ (or (nil? content) (legal-content? content))
+ (or (nil? transport) (#{"UDP" "udp" "TCP" "tcp"} transport))
+ (or (nil? more-headers) (sequential? more-headers))]}
+ (let [{:keys [ip port transport]} (if (nil? transport)
+ (core/listening-point)
+ (core/listening-point transport))
+ user (*current-account* :user)
+ contact-header (header/contact (addr/sip-address ip :port port :transport transport :user user))
+ request (.getRequest transaction)
+ response (msg/response status-code request contact-header more-headers)]
+ (set-content! response content)
+ (trans/send-response! transaction response)))
+
+(def ^{:doc "Store contexts for the register auto refresh."
+ :added "0.3.0"
+ :private true}
+ register-ctx-map (atom {}))
+
+(defn register-to
+ "Send REGISTER sip message to target registry server, and auto refresh register before
+ expired.
+
+ Notice: please call 'global-set-account' before you call 'register-to'. in this version,
+ use dynamic binding form to bind *current-account* can not work for auto-refresh."
+ {:added "0.3.0"}
+ [registry-address expires-seconds & {:keys [on-success on-failure on-refreshed on-refresh-failed]}]
+ {:pre [(addr/address? registry-address)
+ (> expires-seconds 38) ; because a transaction timeout is 32 seconds
+ (or (nil? on-success) (fn? on-success))
+ (or (nil? on-failure) (fn? on-failure))
+ (or (nil? on-refreshed) (fn? on-refreshed))
+ (or (nil? on-refresh-failed) (fn? on-refresh-failed))]}
+ (let [expires-header (header/expires expires-seconds)
+ safer-interval-milliseconds (* (- expires-seconds 5) 1000)
+ register-ctx (get @register-ctx-map registry-address)]
+ (when (nil? register-ctx)
+ (send-request! :REGISTER :to registry-address
+ :more-headers [expires-header]
+ :on-success (fn [& {:keys [transaction]}]
+ (swap! register-ctx-map assoc registry-address
+ {:timer (timer/run-task! #(let [request (.clone (get-in @register-ctx-map [registry-address :request ]))
+ request (msg/inc-sequence-number! request)
+ request (doto request (.removeHeader "Via"))
+ transaction (core/new-client-transcation! request)]
+ (.setApplicationData transaction
+ {:on-success (fn [& _] (and on-refreshed (on-refreshed)))
+ :on-failure (fn [& _] (and on-refresh-failed (on-refresh-failed)))
+ :on-timeout (fn [& _] (and on-refresh-failed (on-refresh-failed)))})
+ (trans/send-request! transaction)
+ (swap! register-ctx-map assoc-in [registry-address :request ] request))
+ :by (timer/timer "Register-Refresh")
+ :delay safer-interval-milliseconds
+ :period safer-interval-milliseconds
+ :on-exception #(log/warn "Register refresh exception: " %))
+ :request (trans/request transaction)})
+ (and on-success (on-success)))
+ :on-failure (fn [& _] (and on-failure (on-failure)))
+ :on-timeout (fn [& _] (and on-failure (on-failure)))))))
+
+(defn unregister-to
+ "Send REGISTER sip message with expires 0 for unregister.
+ And the auto-refresh timer will be canceled."
+ {:added "0.3.0"}
+ [registry-address]
+ (let [refresh-timer (get-in @register-ctx-map [registry-address :timer])]
+ (and refresh-timer (timer/cancel! refresh-timer))
+ (swap! register-ctx-map dissoc registry-address)))
View
303 src/cljain/sip/header.clj
@@ -1,148 +1,155 @@
-(ns ^{:author "ruiyun"
- :added "0.2.0"}
- cljain.sip.header
- (:refer-clojure :exclude [replace reverse require])
- (:use clojure.string
- [cljain.sip.core :only [sip-factory]])
- (:import [javax.sip SipFactory]
- [javax.sip.header HeaderFactory HeaderAddress Header CSeqHeader]
- [javax.sip.address Address URI]
- [gov.nist.javax.sip Utils]))
-
-(def ^{:doc "The factory to create sip headers."
- :added "0.2.0"
- :private true}
- factory (.createHeaderFactory sip-factory))
-
-(defn gen-tag
- "Generate a new tag string."
- {:added "0.2.0"}
- []
- (.. Utils (getInstance) (generateTag)))
-
-(defn gen-branch
- "Generate a new branch id string."
- {:added "0.2.0"}
- []
- (.. Utils (getInstance) (generateBranchId)))
-
-(defmacro defheader
- "Use the macro to define sip headers. More document could be found here
- http://hudson.jboss.org/hudson/job/jain-sip/lastSuccessfulBuild/artifact/javadoc/index.html"
- {:arglists '([name [args*]])
- :added "0.2.0"}
- [name args]
- (let [cls-name (replace (capitalize name) #"-[a-z]" #(upper-case (subs %1 1)))
- cls-name (str cls-name "Header")
- name (symbol name)
- method (symbol (str "create" cls-name))
- m {:doc (str "Create a new " cls-name), :arglists (list 'quote (list args)), :added "0.2.0"}]
- `(def ~(with-meta name m) (partial (memfn ~method ~@args) factory))))
-
-(defheader accept [^String content-type, ^String sub-type])
-(defheader accept-encoding [^String encoding])
-(defheader accept-language [^java.util.Locale language])
-(defheader alert-info [^URI info])
-(defheader allow [^String method])
-(defheader allow-events [^String event-type])
-(defheader authentication-info [^String response])
-(defheader authorization [^String scheme])
-(defheader c-seq [^Long number, ^String method])
-(defheader call-id [^String id])
-(defheader call-info [^URI info])
-(defheader contact [^Address address])
-(defheader content-disposition [^String disposition-type])
-(defheader content-encoding [^String encoding])
-(defheader content-language [^java.util.Locale language])
-(defheader content-length [^Integer length])
-(defheader content-type [^String type, ^String sub-type])
-(defheader date [^java.util.Calendar date])
-(defheader error-info [^URI info])
-(defheader event [^String type])
-(defheader expires [^Integer seconds])
-(defheader from [^Address address, ^String tag])
-(defheader in-reply-to [^String call-id])
-(defheader max-forwards [^Integer number])
-(defheader mime-version [^Integer major, ^Integer minor])
-(defheader min-expires [^Integer seconds])
-(defheader organization [^String value])
-(defheader priority [^String value])
-(defheader proxy-authenticate [^String scheme])
-(defheader proxy-authorization [^String scheme])
-(defheader proxy-require [^String option-tag])
-(defheader r-ack [^Integer r-seq, ^Integer c-seq, ^String method])
-(defheader r-seq [^Integer number])
-(defheader reason [^String protocol ^Integer, cause ^String text])
-(defheader record-route [^Address address])
-(defheader refer-to [^Address address])
-(defheader reply-to [^Address address])
-(defheader require [^String option-tag])
-(defheader retry-after [^Integer seconds])
-(defheader route [^Address address])
-(defheader server [^java.util.List product])
-(defheader subject [^String subject])
-(defheader subscription-state [^String state])
-(defheader supported [^String option-tag])
-(defheader time-stamp [^Float time])
-(defheader to [^Address address, ^String tag])
-(defheader unsupported [^String option-tag])
-(defheader user-agent [^java.util.List product])
-(defheader via [^String host, ^Integer port, ^String transport, ^String branch])
-(defheader warning [^String agent, ^Integer code, ^String comment]) ; 3DIGIT code between 99 and 1000
-
-(defn sip-etag
- "Creates a new SIP-ETag header with the supplied tag value"
- {:added "0.2.0"}
- [^String etag]
- (.createSIPETagHeader factory etag))
-
-(defn sip-if-match
- "Creates a new SIP-If-Match header with the supplied tag value"
- {:added "0.2.0"}
- [^String etag]
- (.createSIPIfMatchHeader factory etag))
-
-(defn wildcard-contact
- "Creates a new wildcard ContactHeader.
- This is used in Register requests to indicate to the server that it should remove all locations the at which
- the user is currently available. This implies that the following conditions are met:
-
- ContactHeader.getAddress.getUserInfo() == *;
- ContactHeader.getAddress.isWildCard() == true;
- ContactHeader.getExpires() == 0;"
- {:added "0.2.0"}
- []
- (.createContactHeader factory))
-
-(defn www-authenticate
- "Creates a new WWWAuthenticateHeader based on the newly supplied scheme value."
- {:added "0.2.0"}
- [^String scheme]
- (.createWWWAuthenticateHeader factory scheme))
-
-(defn extension
- "Creates a new Header based on the newly supplied name and value values."
- {:added "0.2.0"}
- [name value]
- (.createHeader factory name (str value)))
-
-(defn get-address
- "DEPRECATED: Use Java method 'getAddress' directly instead."
- {:added "0.2.0"
- :deprecated "0.4.0"}
- [^HeaderAddress header]
- (.getAddress header))
-
-(defn header?
- "Check whether the object is a Header or not"
- {:added "0.2.0"}
- [object]
- (instance? Header object))
-
-(defn sequence-number
- "DEPRECATED: Use Java method 'getSequenceNumber' directly instead.
- Get the sequence number from a CSeq header."
- {:added "0.3.0"
- :deprecated "0.4.0"}
- [header]
- (.getSequenceNumber header))
+(ns ^{:author "ruiyun"
+ :added "0.2.0"}
+ cljain.sip.header
+ (:refer-clojure :exclude [replace reverse require])
+ (:use clojure.string
+ [cljain.sip.core :only [sip-factory]])
+ (:import [javax.sip SipFactory]
+ [javax.sip.header HeaderFactory HeaderAddress Header CSeqHeader WWWAuthenticateHeader]
+ [javax.sip.address Address URI]
+ [gov.nist.javax.sip Utils]))
+
+(def ^{:doc "The factory to create sip headers."
+ :added "0.2.0"
+ :private true}
+ factory (.createHeaderFactory sip-factory))
+
+(defn gen-tag
+ "Generate a new tag string."
+ {:added "0.2.0"}
+ []
+ (.. Utils (getInstance) (generateTag)))
+
+(defn gen-branch
+ "Generate a new branch id string."
+ {:added "0.2.0"}
+ []
+ (.. Utils (getInstance) (generateBranchId)))
+
+(defmacro defheader
+ "Use the macro to define sip headers. More document could be found here
+ http://hudson.jboss.org/hudson/job/jain-sip/lastSuccessfulBuild/artifact/javadoc/index.html"
+ {:arglists '([name [args*]])
+ :added "0.2.0"}
+ [name args]
+ (let [cls-name (replace (capitalize name) #"-[a-z]" #(upper-case (subs %1 1)))
+ cls-name (str cls-name "Header")
+ name (symbol name)
+ method (symbol (str "create" cls-name))
+ m {:doc (str "Create a new " cls-name), :arglists (list 'quote (list args)), :added "0.2.0"}]
+ `(def ~(with-meta name m) (partial (memfn ~method ~@args) factory))))
+
+(defheader accept [^String content-type, ^String sub-type])
+(defheader accept-encoding [^String encoding])
+(defheader accept-language [^java.util.Locale language])
+(defheader alert-info [^URI info])
+(defheader allow [^String method])
+(defheader allow-events [^String event-type])
+(defheader authentication-info [^String response])
+(defheader authorization [^String scheme])
+(defheader c-seq [^Long number, ^String method])
+(defheader call-id [^String id])
+(defheader call-info [^URI info])
+(defheader contact [^Address address])
+(defheader content-disposition [^String disposition-type])
+(defheader content-encoding [^String encoding])
+(defheader content-language [^java.util.Locale language])
+(defheader content-length [^Integer length])
+(defheader content-type [^String type, ^String sub-type])
+(defheader date [^java.util.Calendar date])
+(defheader error-info [^URI info])
+(defheader event [^String type])
+(defheader expires [^Integer seconds])
+(defheader from [^Address address, ^String tag])
+(defheader in-reply-to [^String call-id])
+(defheader max-forwards [^Integer number])
+(defheader mime-version [^Integer major, ^Integer minor])
+(defheader min-expires [^Integer seconds])
+(defheader organization [^String value])
+(defheader priority [^String value])
+(defheader proxy-authenticate [^String scheme])
+(defheader proxy-authorization [^String scheme])
+(defheader proxy-require [^String option-tag])
+(defheader r-ack [^Integer r-seq, ^Integer c-seq, ^String method])
+(defheader r-seq [^Integer number])
+(defheader reason [^String protocol ^Integer, cause ^String text])
+(defheader record-route [^Address address])
+(defheader refer-to [^Address address])
+(defheader reply-to [^Address address])
+(defheader require [^String option-tag])
+(defheader retry-after [^Integer seconds])
+(defheader route [^Address address])
+(defheader server [^java.util.List product])
+(defheader subject [^String subject])
+(defheader subscription-state [^String state])
+(defheader supported [^String option-tag])
+(defheader time-stamp [^Float time])
+(defheader to [^Address address, ^String tag])
+(defheader unsupported [^String option-tag])
+(defheader user-agent [^java.util.List product])
+(defheader via [^String host, ^Integer port, ^String transport, ^String branch])
+(defheader warning [^String agent, ^Integer code, ^String comment]) ; 3DIGIT code between 99 and 1000
+
+(defn sip-etag
+ "Creates a new SIP-ETag header with the supplied tag value"
+ {:added "0.2.0"}
+ [^String etag]
+ (.createSIPETagHeader factory etag))
+
+(defn sip-if-match
+ "Creates a new SIP-If-Match header with the supplied tag value"
+ {:added "0.2.0"}
+ [^String etag]
+ (.createSIPIfMatchHeader factory etag))
+
+(defn wildcard-contact
+ "Creates a new wildcard ContactHeader.
+ This is used in Register requests to indicate to the server that it should remove all locations the at which
+ the user is currently available. This implies that the following conditions are met:
+
+ ContactHeader.getAddress.getUserInfo() == *;
+ ContactHeader.getAddress.isWildCard() == true;
+ ContactHeader.getExpires() == 0;"
+ {:added "0.2.0"}
+ []
+ (.createContactHeader factory))
+
+(defn www-authenticate
+ "Creates a new WWWAuthenticateHeader based on the newly supplied scheme value."
+ {:added "0.2.0"}
+ [^String scheme, ^String realm, ^String nonce & {:keys [algorithm qop opaque domain stale]}]
+ (doto (.createWWWAuthenticateHeader factory scheme)
+ (.setRealm realm)
+ (.setNonce nonce)
+ (#(when (not (nil? algorithm)) (.setAlgorithm % algorithm)))
+ (#(when (not (nil? qop)) (.setQop % qop)))
+ (#(when (not (nil? opaque)) (.setOpaque % opaque)))
+ (#(when (not (nil? domain)) (.setDomain % domain)))
+ (#(when (not (nil? stale)) (.setStale % stale)))))
+
+(defn extension
+ "Creates a new Header based on the newly supplied name and value values."
+ {:added "0.2.0"}
+ [name value]
+ (.createHeader factory name (str value)))
+
+(defn get-address
+ "DEPRECATED: Use Java method 'getAddress' directly instead."
+ {:added "0.2.0"
+ :deprecated "0.4.0"}
+ [^HeaderAddress header]
+ (.getAddress header))
+
+(defn header?
+ "Check whether the object is a Header or not"
+ {:added "0.2.0"}
+ [object]
+ (instance? Header object))
+
+(defn sequence-number
+ "DEPRECATED: Use Java method 'getSequenceNumber' directly instead.
+ Get the sequence number from a CSeq header."
+ {:added "0.3.0"
+ :deprecated "0.4.0"}
+ [header]
+ (.getSequenceNumber header))
View
59 test/cljain/test/register.clj
@@ -1,29 +1,30 @@
-(ns ^{:author "ruiyun"}
- cljain.test.register
- (:import [javax.sip.header WWWAuthenticateHeader]
- [javax.sip.message Response]))
-
-(org.apache.log4j.PropertyConfigurator/configure "log4j.properties")
-
-(use 'cljain.dum)
-(require '[cljain.sip.core :as sip]
- '[cljain.sip.address :as addr]
- '[cljain.sip.header :as header])
-
-(defmethod handle-request :REGISTER [request transaction & _]
- (if (.getHeader request WWWAuthenticateHeader/NAME)
- (send-response! Response/OK :in transaction)
- (send-response! Response/UNAUTHORIZED :in transaction
- :more-headers [(header/authorization "Digest algorithm=MD5, realm=\"localhost\", nonce=\"34dd9dfd\"")])))
-
-(global-set-account :user "bob" :domain "localhost" :display-name "Bob" :password "123456")
-;(sip/global-bind-sip-provider! (sip/sip-provider! "my-app" "localhost" 6060 "udp" :outbound-proxy "127.0.0.1:5060"))
-(sip/global-bind-sip-provider! (sip/sip-provider! "my-app" "localhost" 5060 "udp"))
-(sip/set-listener! (dum-listener))
-(sip/start!)
-
-(register-to (addr/address "sip:localhost") 40
- :on-success #(prn "success")
- :on-failure #(prn "failure")
- :on-refreshed #(prn "refreshed")
- :on-refresh-failed #(prn "refresh failed"))
+(ns ^{:author "ruiyun"}
+ cljain.test.register
+ (:import [javax.sip.header WWWAuthenticateHeader AuthorizationHeader]
+ [javax.sip.message Response]))
+
+(org.apache.log4j.PropertyConfigurator/configure "log4j.properties")
+
+(use 'cljain.dum)
+(require '[cljain.sip.core :as sip]
+ '[cljain.sip.address :as addr]
+ '[cljain.sip.header :as header])
+
+(defmethod handle-request :REGISTER [request transaction & _]
+ (if (.getHeader request AuthorizationHeader/NAME)
+ (send-response! Response/OK :in transaction)
+ (send-response! Response/UNAUTHORIZED :in transaction
+ :more-headers [(header/www-authenticate "Digest" "localhost" "aa2f052b75d9ed32"
+ :algorithm "MD5" :stale false)])))
+
+(global-set-account :user "bob" :domain "localhost" :display-name "Bob" :password "123456")
+;(sip/global-bind-sip-provider! (sip/sip-provider! "my-app" "localhost" 6060 "udp" :outbound-proxy "127.0.0.1:5060"))
+(sip/global-bind-sip-provider! (sip/sip-provider! "my-app" "localhost" 5060 "udp"))
+(sip/set-listener! (dum-listener))
+(sip/start!)
+
+(register-to (addr/address "sip:localhost") 40
+ :on-success #(prn "success")
+ :on-failure #(prn "failure")
+ :on-refreshed #(prn "refreshed")
+ :on-refresh-failed #(prn "refresh failed"))
Please sign in to comment.
Something went wrong with that request. Please try again.