@babblevoice/babble-drachtio-callmanager 3.7.21 → 3.7.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -162,6 +162,30 @@ npm link projectrtp
162
162
  npm link babble-drachtio-registrar
163
163
  npm link babble-drachtio-auth
164
164
 
165
+ If you want to test then projectrtp needs to be able to build locally, this uses the latest projectrtp docker image.
166
+
167
+ ```bash
168
+ docker run --rm -it \
169
+ -e HOME=/usr/src/app \
170
+ -v "$(pwd)":/usr/src/app \
171
+ -w /usr/src/app \
172
+ tinpotnick/projectrtp \
173
+ npm test
174
+
175
+ ```
176
+
177
+ or for a specific test
178
+
179
+ ```bash
180
+ docker run --rm -it \
181
+ -e HOME=/usr/src/app \
182
+ -v "$(pwd)":/usr/src/app \
183
+ -w /usr/src/app \
184
+ tinpotnick/projectrtp \
185
+ ./node_modules/mocha/bin/mocha --recursive --check-leaks --grep 'Create call and send 183 - early - SAVPF'
186
+
187
+ ```
188
+
165
189
  # References
166
190
 
167
191
  * SIP: Session Initiation Protocol [RFC 3261](https://tools.ietf.org/html/rfc3261)
package/lib/call.js CHANGED
@@ -74,6 +74,29 @@ function getheadervalue( headers, targetKey ) {
74
74
  }
75
75
  }
76
76
 
77
+
78
+ /**
79
+ * Used in caller id etc
80
+ * @param { string } input
81
+ * @returns { string }
82
+ */
83
+ function sanitizecalleridnumber( input ) {
84
+ if (typeof input !== "string") return ""
85
+ // Digits, +, #, *
86
+ return input.replace( /[^a-zA-Z0-9+*#._-]/g, "").slice( 0, 32 )
87
+ }
88
+
89
+ /**
90
+ * used in caller id etc
91
+ * @param { string } input
92
+ * @returns { string }
93
+ */
94
+ function sanitizecalleridname( input ) {
95
+ if (typeof input !== "string") return ""
96
+ // Allow letters, numbers, space, dash, underscore, dot
97
+ return input.replace( /[^a-zA-Z0-9 \-_.',()/+*#&]/g, "" ).slice( 0, 40 )
98
+ }
99
+
77
100
  /*
78
101
  Enum for different reasons for hangup.
79
102
  */
@@ -213,7 +236,10 @@ class call {
213
236
  }
214
237
 
215
238
  this.counters = {
216
- "newuac": 0
239
+ "newuac": 0,
240
+ "adoptions": 0,
241
+ "adoptees": 0,
242
+ "mixes": 0
217
243
  }
218
244
 
219
245
  /**
@@ -882,7 +908,7 @@ class call {
882
908
  if( !this.options.callerid ) this.options.callerid = {}
883
909
  if( !this.options.callerid.number ) this.options.callerid.number = ""
884
910
 
885
- this.options.callerid.number = c
911
+ this.options.callerid.number = sanitizecalleridnumber( c )
886
912
  }
887
913
 
888
914
  /**
@@ -895,7 +921,7 @@ class call {
895
921
  if( !this.options.callerid ) this.options.callerid = {}
896
922
  if( !this.options.callerid.name ) this.options.callerid.name = ""
897
923
 
898
- this.options.callerid.name = c
924
+ this.options.callerid.name = sanitizecalleridname( c )
899
925
  }
900
926
 
901
927
  /**
@@ -1312,6 +1338,9 @@ class call {
1312
1338
  this.children.add( other )
1313
1339
  other.moh = this.moh
1314
1340
 
1341
+ this.counters.adoptions += 1
1342
+ other.counters.adoptees += 1
1343
+
1315
1344
  /* maintain privacy */
1316
1345
  if ( this.options.privacy )
1317
1346
  other.options.privacy = this.options.privacy
@@ -2412,6 +2441,9 @@ class call {
2412
2441
 
2413
2442
  this.epochs.mix = Math.floor( +new Date() / 1000 )
2414
2443
  othercall.epochs.mix = Math.floor( +new Date() / 1000 )
2444
+
2445
+ this.counters.mixes += 1
2446
+ othercall.counters.mixes += 1
2415
2447
  }
2416
2448
 
2417
2449
  /**
@@ -3617,6 +3649,7 @@ class call {
3617
3649
  if( contact.contact && "string" == typeof contact.contact ) {
3618
3650
  numcontacts++
3619
3651
  newoptions.contact = contact.contact
3652
+ // @ts-ignore
3620
3653
  call.newuac( newoptions, ourcallbacks )
3621
3654
  }
3622
3655
  }
@@ -3814,11 +3847,10 @@ class call {
3814
3847
  * @returns { object }
3815
3848
  */
3816
3849
  #configcalleridfornewuac() {
3817
-
3818
3850
  let callerid, calleridrem
3819
3851
  let party = ";party=calling"
3820
3852
 
3821
- if( "inbound" === this.direction ) {
3853
+ if( this.direction === "inbound" ) {
3822
3854
  callerid = this.calledid
3823
3855
  calleridrem = this.calledid
3824
3856
  } else {
@@ -3827,11 +3859,23 @@ class call {
3827
3859
  party = ";party=called"
3828
3860
  }
3829
3861
 
3830
- const calleridstr = `"${callerid.name?callerid.name:callerid.user}" <sip:${callerid.user}@${callerid.host}>`
3831
- const calleridstrrem = `"${calleridrem.name?calleridrem.name:calleridrem.user}" <sip:${calleridrem.user}@${calleridrem.host}>`
3862
+ // pull out values and sanitise
3863
+ const callername = sanitizecalleridname( callerid.name ? callerid.name : callerid.user )
3832
3864
 
3865
+ const callernumber = sanitizecalleridnumber( callerid.user )
3866
+ const callerhost = callerid.host
3867
+
3868
+ const callernamerem = sanitizecalleridname( calleridrem.name ? calleridrem.name : calleridrem.user )
3869
+ const callernumberrem = sanitizecalleridnumber( calleridrem.user )
3870
+ const callerhostrem = calleridrem.host
3871
+
3872
+ // build header strings
3873
+ const calleridstr = `"${callername}" <sip:${callernumber}@${callerhost}>`
3874
+ const calleridstrrem = `"${callernamerem}" <sip:${callernumberrem}@${callerhostrem}>`
3875
+
3876
+ // handle privacy
3833
3877
  let privacy = ""
3834
- if( true === this.options.privacy ) {
3878
+ if( this.options.privacy === true ) {
3835
3879
  privacy = ";privacy=full"
3836
3880
  }
3837
3881
 
@@ -3842,7 +3886,7 @@ class call {
3842
3886
  this.options.headers[ "remote-party-id" ] = calleridstrrem + party + ";screen=yes" + privacy
3843
3887
  this.options.headers[ "from" ] = calleridstr
3844
3888
 
3845
- return { user: callerid.user, realm: callerid.host }
3889
+ return { user: callernumber, realm: callerhost }
3846
3890
  }
3847
3891
 
3848
3892
 
@@ -4306,6 +4350,8 @@ class call {
4306
4350
  set uactimeout( ms ) {
4307
4351
  if( this.established ) return
4308
4352
 
4353
+ ms = Math.min( ms, 30 * 60 * 1000 )
4354
+
4309
4355
  clearTimeout( this._timers.newuac )
4310
4356
  this._timers.newuac = setTimeout( () => {
4311
4357
  this.hangup( hangupcodes.REQUEST_TIMEOUT )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/babble-drachtio-callmanager",
3
- "version": "3.7.21",
3
+ "version": "3.7.23",
4
4
  "description": "Call processing to create a PBX",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -25,17 +25,17 @@
25
25
  "uuid": "^8.3.2"
26
26
  },
27
27
  "optionalDependencies": {
28
- "@babblevoice/projectrtp": "^2.5.28"
28
+ "@babblevoice/projectrtp": "^2.6.2"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@typescript-eslint/eslint-plugin": "^5.48.2",
32
+ "chai": "^4.3.6",
32
33
  "eslint": "^8.32.0",
33
34
  "eslint-config-standard-with-typescript": "^30.0.0",
34
35
  "eslint-plugin-import": "^2.27.5",
35
36
  "eslint-plugin-n": "^15.6.1",
36
37
  "eslint-plugin-promise": "^6.1.1",
37
38
  "mocha": "^9.2.2",
38
- "chai": "^4.3.6",
39
39
  "typescript": "^4.9.4"
40
40
  },
41
41
  "bugs": {
@@ -258,7 +258,7 @@ describe( "call object", function() {
258
258
  child.update()
259
259
 
260
260
  expect( requestoptions.method ).to.equal( "UPDATE" )
261
- expect( requestoptions.headers[ "remote-party-id" ] ).to.equal( "\"0123456789\" <sip:0123456789@someotherrealm.com>;party=calling;screen=yes" )
261
+ expect( requestoptions.headers[ "remote-party-id" ] ).to.equal( "\"0123456789\" <sip:0123456789@someotherrealm.com>;party=called;screen=yes" )
262
262
 
263
263
  /* simulate wire to propogate hangup */
264
264
  await call.hangup( call.hangupcodes.NORMAL_CLEARING, false, "wire" )
@@ -790,7 +790,7 @@ describe( "call object", function() {
790
790
 
791
791
  await call.newuac( options, { "early": ( c ) => c.hangup() } )
792
792
 
793
- expect( createuacoptions.headers[ "remote-party-id" ] ).to.equal( "\"0000000000\" <sip:0000000000@localhost.localdomain>;party=calling;screen=yes" )
793
+ expect( createuacoptions.headers[ "remote-party-id" ] ).to.equal( "\"0000000000\" <sip:0000000000@localhost.localdomain>;party=called;screen=yes" )
794
794
  expect( createuacoptions.late ).to.be.true
795
795
  } )
796
796
 
@@ -850,7 +850,7 @@ describe( "call object", function() {
850
850
 
851
851
  expect( requestoptions.method ).to.equal( "UPDATE" )
852
852
  expect( requestoptions.body ).to.be.a( "string" )
853
- expect( requestoptions.headers[ "remote-party-id" ] ).to.equal( "\"Kermit\" <sip:kermy@localhost.localdomain>;party=calling;screen=yes" )
853
+ expect( requestoptions.headers[ "remote-party-id" ] ).to.equal( "\"Kermit\" <sip:kermy@localhost.localdomain>;party=called;screen=yes" )
854
854
 
855
855
  } )
856
856
 
@@ -1068,20 +1068,15 @@ describe( "call object", function() {
1068
1068
  setTimeout( () => connection.write( projectrtpmessage.createmessage( {"id": msgid,"uuid":"6d8ba7bb-44b9-4989-9aaf-5d938b496c49","action":"play","event":"start","reason":"new","status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d5c94"}} ) ), 1 )
1069
1069
  setTimeout( () => connection.write( projectrtpmessage.createmessage( {"id": msgid,"uuid":"6d8ba7bb-44b9-4989-9aaf-5d938b496c49","action":"play","event":"end","reason":"completed","status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d5c94"}} ) ), 10 )
1070
1070
  c.channels.audio.play( { "files": [ { "wav": "/voicemail/greeting.wav", "alt": "greeting" } ] } )
1071
- let ev = await c.waitforanyevent( { "action": "play", "event": "end" } )
1072
- expect( ev.action ).to.equal( "play" )
1073
- expect( ev.event ).to.equal( "end" )
1074
- expect( ev.reason ).to.equal( "completed" )
1071
+ const playgreetendev = await c.waitforanyevent( { "action": "play", "event": "end" } )
1075
1072
 
1076
1073
  setTimeout( () => connection.write( projectrtpmessage.createmessage( {"id": msgid,"uuid":"6d8ba7bb-44b9-4989-9aaf-5d938b496c49","action":"play","event":"start","reason":"new","status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d5c94"}} ) ) )
1077
1074
  setTimeout( () => connection.write( projectrtpmessage.createmessage( {"id": msgid,"uuid":"6d8ba7bb-44b9-4989-9aaf-5d938b496c49","action":"play","event":"end","reason":"completed","status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d5c94"}} ) ) )
1078
1075
  c.channels.audio.play( { "files": [ { "wav": "/voicemail/boing.wav", "alt": "" } ] } )
1079
- ev = await c.waitforanyevent( { "action": "play", "event": "end" } )
1080
- expect( ev.action ).to.equal( "play" )
1081
- expect( ev.event ).to.equal( "end" )
1082
- expect( ev.reason ).to.equal( "completed" )
1076
+ const playendev = await c.waitforanyevent( { "action": "play", "event": "end" } )
1083
1077
 
1084
1078
  setTimeout( () => connection.write( projectrtpmessage.createmessage( {"id": msgid,"uuid":"6d8ba7bb-44b9-4989-9aaf-5d938b496c49","action":"record","file":"/tmp/voicemail/recording/03039cdb-1949-407d-91d6-15ba6894955c.wav","event":"recording","status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d5c94"}} ) ), 5 )
1079
+ setTimeout( () => connection.write( projectrtpmessage.createmessage( {"id": msgid,"uuid":"6d8ba7bb-44b9-4989-9aaf-5d938b496c49","action":"record","file":"/tmp/voicemail/recording/03039cdb-1949-407d-91d6-15ba6894955c.wav","event":"finished","event":"finished.channelclosed", "transcription": "test recording for voicemail", "emailed": true, "filesize": 160684, "status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d5c94"}} ) ), 10 )
1085
1080
 
1086
1081
  c.channels.audio.record( {
1087
1082
  "file": "/voicemail/jhjhjgjhgjg.wav",
@@ -1100,15 +1095,25 @@ describe( "call object", function() {
1100
1095
  c._onhangup( "wire" )
1101
1096
  }, 50 )
1102
1097
 
1103
- ev = await c.waitforanyevent( { "action": "record", "event": /finished.*|\*/ }, 10000 )
1104
- expect( ev.action ).to.equal( "record" )
1105
- expect( ev.event ).to.equal( "finished.channelclosed" )
1106
- expect( ev.transcription ).to.equal( "test recording for voicemail" )
1107
- expect( ev.emailed ).to.be.true
1108
- expect( ev.filesize ).to.equal( 160684 )
1098
+ const recordfinishev = await c.waitforanyevent( { "action": "record", "event": /finished.*|\*/ }, 10000 )
1109
1099
 
1110
1100
  connection.destroy()
1111
1101
  await rtpserver.destroy()
1102
+
1103
+ expect( playgreetendev.action ).to.equal( "play" )
1104
+ expect( playgreetendev.event ).to.equal( "end" )
1105
+ expect( playgreetendev.reason ).to.equal( "completed" )
1106
+
1107
+ expect( playendev.action ).to.equal( "play" )
1108
+ expect( playendev.event ).to.equal( "end" )
1109
+ expect( playendev.reason ).to.equal( "completed" )
1110
+
1111
+ expect( recordfinishev.action ).to.equal( "record" )
1112
+ expect( recordfinishev.event ).to.equal( "finished.channelclosed" )
1113
+ expect( recordfinishev.transcription ).to.equal( "test recording for voicemail" )
1114
+ expect( recordfinishev.emailed ).to.be.true
1115
+ expect( recordfinishev.filesize ).to.equal( 160684 )
1116
+
1112
1117
  } )
1113
1118
 
1114
1119
  it( "set get moh", async function() {
@@ -1152,7 +1157,7 @@ describe( "call object", function() {
1152
1157
  const c = await call.newuac( options )
1153
1158
 
1154
1159
  /* no default configured */
1155
- expect( c.options.headers[ "remote-party-id" ] ).to.equal( "\"Hello\" <sip:0000000000@localhost.localdomain>;party=calling;screen=yes" )
1160
+ expect( c.options.headers[ "remote-party-id" ] ).to.equal( "\"Hello\" <sip:0000000000@localhost.localdomain>;party=called;screen=yes" )
1156
1161
 
1157
1162
  c._onhangup( "wire" )
1158
1163
 
@@ -1173,7 +1178,7 @@ describe( "call object", function() {
1173
1178
 
1174
1179
 
1175
1180
  /* no default configured */
1176
- expect( c.options.headers[ "remote-party-id" ] ).to.equal( "\"012345789\" <sip:012345789@localhost.localdomain>;party=calling;screen=yes" )
1181
+ expect( c.options.headers[ "remote-party-id" ] ).to.equal( "\"012345789\" <sip:012345789@localhost.localdomain>;party=called;screen=yes" )
1177
1182
 
1178
1183
  c._onhangup( "wire" )
1179
1184
 
@@ -52,6 +52,9 @@ describe( "call early", function() {
52
52
  const messagestate = projectrtpmessage.newstate()
53
53
  const channelmessages = []
54
54
  let opencount = 0
55
+
56
+ let ourclosepromise
57
+ const closepromise = new Promise( resolve => ourclosepromise = resolve )
55
58
 
56
59
  connection.on( "data", ( data ) => {
57
60
  projectrtpmessage.parsemessage( messagestate, data, ( msg ) => {
@@ -87,6 +90,7 @@ describe( "call early", function() {
87
90
  } else if ( "close" === msg.channel ) {
88
91
  connection.write( projectrtpmessage.createmessage( {"id": msg.id,"uuid":msg.uuid,"action":"close","reason":"requested","stats":{"in":{"mos":4.5,"count":586,"dropped":0,"skip":0},"out":{"count":303,"skip":0},"tick":{"meanus":124,"maxus":508,"count":597}}, "status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
89
92
  } ) )
93
+ ourclosepromise()
90
94
  } else if ( "mix" === msg.channel ) {
91
95
  mixing = true
92
96
  }
@@ -140,6 +144,12 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
140
144
  const newcall = await call.newuac( { "contact": "callto" } )
141
145
 
142
146
  await call._onhangup( "wire" )
147
+
148
+ await closepromise
149
+
150
+ connection.destroy()
151
+ rtpserver.destroy()
152
+
143
153
  expect( newcall.state.early ).to.be.true
144
154
  expect( call.state.early ).to.be.true
145
155
  expect( mixing ).to.be.true
@@ -158,8 +168,6 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
158
168
 
159
169
  expect( msginfo.body ).to.include( "audio 10010 RTP/AVP" )
160
170
 
161
- connection.destroy()
162
- rtpserver.destroy()
163
171
  } )
164
172
 
165
173
 
@@ -181,6 +189,9 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
181
189
  const srfscenario = new srf.srfscenario()
182
190
  const rtpserver = callmanager.projectrtp.proxy.addnode( { host: "127.0.0.1", port: 9002 } )
183
191
 
192
+ let ourclosepromise
193
+ const closepromise = new Promise( resolve => ourclosepromise = resolve )
194
+
184
195
  let connection
185
196
  const mockrtp = net.createServer()
186
197
  mockrtp.on( "connection", ( c ) => {
@@ -220,6 +231,7 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
220
231
  } else if ( "close" === msg.channel ) {
221
232
  connection.write( projectrtpmessage.createmessage( {"id": msg.id,"uuid":msg.uuid,"action":"close","reason":"requested","stats":{"in":{"mos":4.5,"count":586,"dropped":0,"skip":0},"out":{"count":303,"skip":0},"tick":{"meanus":124,"maxus":508,"count":597}}, "status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
222
233
  } ) )
234
+ ourclosepromise()
223
235
  } else if ( "mix" === msg.channel ) {
224
236
  mixing = true
225
237
  }
@@ -281,6 +293,9 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
281
293
  const newcall = await call.newuac( { "contact": "callto" } )
282
294
 
283
295
  await call._onhangup( "wire" )
296
+
297
+ await closepromise
298
+
284
299
  expect( newcall.state.early ).to.be.true
285
300
  expect( call.state.early ).to.be.true
286
301
  expect( mixing ).to.be.true
@@ -332,6 +347,9 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
332
347
  const messagestate = projectrtpmessage.newstate()
333
348
  const channelmessages = []
334
349
  let opencount = 0
350
+
351
+ let ourclosepromise
352
+ const closepromise = new Promise( resolve => ourclosepromise = resolve )
335
353
 
336
354
  connection.on( "data", ( data ) => {
337
355
  projectrtpmessage.parsemessage( messagestate, data, ( msg ) => {
@@ -366,6 +384,7 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
366
384
  opencount++
367
385
  } else if ( "close" === msg.channel ) {
368
386
  connection.write( projectrtpmessage.createmessage( {"id": msg.id,"uuid":msg.uuid,"action":"close","reason":"requested","stats":{"in":{"mos":4.5,"count":586,"dropped":0,"skip":0},"out":{"count":303,"skip":0},"tick":{"meanus":124,"maxus":508,"count":597}}, "status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}}))
387
+ ourclosepromise()
369
388
  } else if ( "mix" === msg.channel ) {
370
389
  mixing = true
371
390
  }
@@ -427,7 +446,8 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
427
446
  const newcall = await call.newuac( { "contact": "callto" } )
428
447
 
429
448
  await call._onhangup( "wire" )
430
-
449
+ await closepromise
450
+
431
451
  expect( newcall.state.early ).to.be.true
432
452
  expect( call.state.early ).to.be.true
433
453
  expect( mixing ).to.be.true
@@ -485,6 +505,9 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
485
505
  const messagestate = projectrtpmessage.newstate()
486
506
  const channelmessages = []
487
507
  let opencount = 0
508
+
509
+ let ourclosepromise
510
+ const closepromise = new Promise( resolve => ourclosepromise = resolve )
488
511
 
489
512
  connection.on( "data", ( data ) => {
490
513
  projectrtpmessage.parsemessage( messagestate, data, ( msg ) => {
@@ -519,6 +542,7 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
519
542
  opencount++
520
543
  } else if ( "close" === msg.channel ) {
521
544
  connection.write( projectrtpmessage.createmessage( {"id": msg.id,"uuid":msg.uuid,"action":"close","reason":"requested","stats":{"in":{"mos":4.5,"count":586,"dropped":0,"skip":0},"out":{"count":303,"skip":0},"tick":{"meanus":124,"maxus":508,"count":597}}, "status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}} ) )
545
+ ourclosepromise()
522
546
  } else if ( "mix" === msg.channel ) {
523
547
  mixing = true
524
548
  }
@@ -580,6 +604,10 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
580
604
 
581
605
  await new Promise( ( resolve ) => setTimeout( () => resolve(), 500 ) )
582
606
  await call._onhangup( "wire" )
607
+ await closepromise
608
+
609
+ connection.destroy()
610
+ rtpserver.destroy()
583
611
 
584
612
  expect( newcall.state.early ).to.be.true
585
613
  expect( call.state.early ).to.be.true
@@ -600,8 +628,6 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
600
628
 
601
629
  expect( msginfo.body ).to.include( "UDP/TLS/RTP/SAVPF" )
602
630
 
603
- connection.destroy()
604
- rtpserver.destroy()
605
631
  } )
606
632
 
607
633
  } )