-
Notifications
You must be signed in to change notification settings - Fork 788
Subscribe support #711
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Subscribe support #711
Conversation
|
Hi @ikq, JsSIP code must have the same style in order to make it readable and maintainable. Comments before making a deeper review:
Please take a certain file (ie: RTCSession.js) as a reference and try to stick to its style. |
|
Thanks for the cleanup, 👍 We'll review the PR as we can. |
|
@ikq, Cosmetic comment, we use camelCase in API method arguments (not in 100% of the cases, I know, but we will get there). Could you please make such change? |
jmillan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made the first review.
Please go through all the comments and modify as needed. We'll go for sencond review afterwards.
|
I mostly fixed the issues. [I thought about dialog: About dialog, seems: dialog interface:
other functionality:
It's already implemented in Subscriber/Notifier. |
|
Hi, Super busy lately. Will come back to this when we have some time. |
|
H @ikq,
|
|
Hmm... I just did not consider this possibility before because I found INVITE/CANCEL/ACK specific code and properties in the Dialog (e.g. subscriber used different dialog states set). |
|
There is INVITE related code in Dialog because a dialog for INVITE method needs to behave in certain way, different to generic dialogs, but that's an implementation detail. Other clases should be able to create dialogs the same way RTCSession does. |
|
Hi Jose, I'll continue to test next week:
|
|
Testing of the current version has been completed successfully. |
|
subscribe authentication checked using special test. |
|
I see I should change debug/debugerror to logger... |
|
Hi @ikq, We are super busy lately. Please keep using your branch and update this PR whenever you find any issue/enhancement. Also, tests would accelerate the adoption. |
|
I already use this code for my work and so far no problem. Two browsers test can be taken from: |
|
@ikq, more than an external test I mean tests in JsSIP as part of this PR, testing the clases created here. |
|
Hi, is there some news? I'd like to test this functionality. |
IMHO it's stable code. The subscriber/notifier test can taken from https://github.com/ikq/subscribe_notify_test |
|
Hi, is there a specific reason why Subscriber and Notifier don't use the Contact header from the UA like RTCSession does? If the user provides a custom contact_uri in the UAConfiguration he would have to manually add it as a extra header to Subscriber and Notifier again, wouldn't he? |
I'd like to use the official repository, so I'd like if it will be merged ASAP |
Yes, I'm not using the Configuration.contact_uri In fact, I was in a hurry to do this work, and did not pay attention at all to that there is such a parameter in the configuration ! Let check, how the configuration used for INVITE: Thanks. P.S. |
|
@ikq if you don't mind external contributions i'd be willing to take a swing at writing unit tests for this project's test framework |
Thank you, I will gladly accept your help. I have provided an external test for 2 browsers: one is sending an SUBSCRIBE and the other is receiving it and sending a NOTIFY. Seems in unit test should be simulated 2 instances of JsSIP one send/other receive. |
@ikq Is there a specific reason to use Configuration.contact_uri? Why not just do it as in RTCSession, with |
|
Thank you markusatm for founding the problem. |
|
Hi, Yes, in order for this PR to be merged we need tests in JsSIP project. You don't need two JsSIP instances, just one instance and you should inject a transport that you control (a FAKE transport) that you use to send JsSIP UA SUBSCRIBE|NOTIFY messages and receive and check what comes from JsSIP. If you need some help please let us know. |
|
Hi,
The test SIP sequence is:
The unit test work silently, but if uncomment line JsSIP.debug.enable('JsSIP:*'); SUBSCRIBE and NOTIFY headers checking: |
b2ef69c to
d390a7c
Compare
Done |
|
Thanks, I'll take some time on a further, and hopefully last review. |
|
Currently UA fires If SUBSCRIBE request does not have a Contact header, the request WONT be responded, hence breaking the SIP protocol. Contact header is verified in Check a similar case in RtcSession: https://github.com/versatica/JsSIP/blob/master/lib/RTCSession.js#L3499 |
|
@orgads, there may be some duplicated suggestion on my review due to a problem between GH and my side, sorry for that. Please simply resolve those duplicated. |
fb75081 to
9c09c0f
Compare
|
I think all the comments should be now covered. |
|
(I can't Resolve comments since I'm not the owner 🙄) |
I will, no worries. |
src/Notifier.js
Outdated
| logger.debug(`emit "terminated" code=${C.SUBSCRIPTION_EXPIRED}, send final notify=true`); | ||
| this.emit('terminated', C.SUBSCRIPTION_EXPIRED, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not call this._terminateDialog(C.SUBSCRIPTION_EXPIRED) instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It passes true for sendFinalNotify, and also doesn't call this._dialog.terminate().
|
Hi @orgads, I'm doing some modernisation on the library and I've moved code to I don't plan to do any other change affecting this PR. |
9c09c0f to
ddfc457
Compare
Rebased. |
src/Notifier.js
Outdated
| } | ||
|
|
||
| logger.debug(`emit "terminated" code=${C.SUBSCRIPTION_EXPIRED}, send final notify=true`); | ||
| this.emit('terminated', C.SUBSCRIPTION_EXPIRED, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no type definitions (.d.ts) for events in Notifier.d.ts. Can you please add them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder why the last argument is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a subscription expires, final NOTIFY should be sent.
The purpose of this flag is to allow the client to send final notify with custom body on subscription expiry and call terminate (without JsSip destroying the dialog before that).
An alternative suggestion I discussed with @ikq is to add a callback named fillFinalNotify or so that will be called inside terminateDialog, allowing to fill custom body.
What do you prefer (or just send an empty NOTIFY, not allowing customization)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then rather than doing:
this.emit('terminated', C.SUBSCRIPTION_EXPIRED, true);
And letting the user call this.terminate() I would:
// Emit expired rather than terminate.
// The user can call `terminate()` within `expired` event callback.
this.emit('expired');
// Call `terminateDialog()`. It won't have any effect if the user already called `terminate()`.
this._terminateDialog(C.SUBSCRIPTION_EXPIRED);NOTE: _terminateDialog() should check this._terminated and return if set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding _terminateDialog, not exactly. terminate must set _state to TERMINATED, because it is sent to the subscriber in sendNotify. But it does check for this._dialog, which should be enough.
Also, instead of calling _terminateDialog, I call terminate, to ensure we send a final notify.
The rest is done.
| { | ||
| logger.debug('enqueue subscribe'); | ||
|
|
||
| this._queue.push({ body, headers: Utils.cloneArray(headers) }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please point me where in the standard is this behaviour defined? The fact that SUBSCRIBE requests should be queued until the dialog is created?
The only use casi I find is that we want to terminate the subscription before receiving the response to the initial SUBSCRIBE, but than can be achieved on accepted and other event handlers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done in order to support the case of multiple incoming SUBSCRIBE requests that are received before the other party responds. _dialog is initialized on response, and then we have to send an initial NOTIFY that matches the body of the subscribes.
So we need to store body and headers anyway. The queue is only needed to support multiple requests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nop, incoming SUBSCRIBE request will end up creating a Notifier, not a Subscriber. Subscriber is only created locally. Right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I misunderstood the issue. If I understand correctly, it is for repeated calls to subscribe before the other party replies with 200 OK. So the first call sends SUBSCRIBE, then the next call should send an in-dialog SUBSCRIBE, but there is no dialog yet. So we need to wait for 200 OK, and then we send all the pending SUBSCRIBEs.
src/Notifier.js
Outdated
| if (!this._dialog) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| logger.debug(`emit "terminated" code=${C.SUBSCRIPTION_EXPIRED}, send final notify=true`); | ||
| this.emit('terminated', C.SUBSCRIPTION_EXPIRED, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely think this should call this._terminateDialog( C.SUBSCRIPTION_EXPIRED). I see no reason not to do it. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #711 (comment)
ddfc457 to
c20b660
Compare
orgads
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4 comments still pending. I'll consult with @ikq.
273076d to
60a678e
Compare
src/Notifier.js
Outdated
| return C; | ||
| } | ||
|
|
||
| get C() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one after constructor please:
- first static methods.
- second constructor.
- Then all other methods.
- public firsts.
- private later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| { | ||
| logger.debug('enqueue subscribe'); | ||
|
|
||
| this._queue.push({ body, headers: Utils.cloneArray(headers) }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nop, incoming SUBSCRIBE request will end up creating a Notifier, not a Subscriber. Subscriber is only created locally. Right?
src/Notifier.js
Outdated
| } | ||
|
|
||
| logger.debug(`emit "terminated" code=${C.SUBSCRIPTION_EXPIRED}, send final notify=true`); | ||
| this.emit('terminated', C.SUBSCRIPTION_EXPIRED, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then rather than doing:
this.emit('terminated', C.SUBSCRIPTION_EXPIRED, true);
And letting the user call this.terminate() I would:
// Emit expired rather than terminate.
// The user can call `terminate()` within `expired` event callback.
this.emit('expired');
// Call `terminateDialog()`. It won't have any effect if the user already called `terminate()`.
this._terminateDialog(C.SUBSCRIPTION_EXPIRED);NOTE: _terminateDialog() should check this._terminated and return if set.
src/Subscriber.js
Outdated
|
|
||
| if (parsed === -1) | ||
| { | ||
| throw new TypeError('eventName - wrong format'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| throw new TypeError('eventName - wrong format'); | |
| throw new TypeError('Missing Event header field'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/Subscriber.js
Outdated
| } | ||
| } | ||
|
|
||
| _terminateDialog(terminationCode, reason = undefined, retryAfter = undefined) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please move this after terminate() definition?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/Notifier.js
Outdated
|
|
||
| static init_incoming(request, callback) | ||
| { | ||
| if (request.method !== JsSIP_C.SUBSCRIBE || !request.hasHeader('contact')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Event header field should also be verified here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
c5160d4 to
6c1cfd7
Compare
6c1cfd7 to
faff4fe
Compare
I added Subscriber.js Notifier.js to JsSIP
I tried write them according JsSIP style and lint errors.
Please take a look and let me know what needs to be fixed.
I already see that *.d.ts files are missing.
The work still in progress: I want add to SUBSCRIBE Contact +sip.instance
as you do in REGISTER