botium-core 1.14.10 → 1.15.2

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/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.14.10",
3
+ "version": "1.15.2",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
7
7
  "engines": {
8
- "node": ">=14.0.0"
8
+ "node": ">=18.17"
9
9
  },
10
10
  "scripts": {
11
11
  "postinstall": "node ./report.js",
@@ -54,7 +54,6 @@
54
54
  "promise-retry": "^2.0.1",
55
55
  "promise.allsettled": "^1.0.7",
56
56
  "randomatic": "^3.1.1",
57
- "request": "^2.88.2",
58
57
  "rimraf": "^5.0.5",
59
58
  "sanitize-filename": "^1.6.3",
60
59
  "slugify": "^1.6.6",
@@ -64,6 +63,7 @@
64
63
  "swagger-jsdoc": "^6.2.8",
65
64
  "swagger-ui-express": "^5.0.0",
66
65
  "tinyglobby": "^0.2.10",
66
+ "undici": "^6.21.0",
67
67
  "uuid": "^9.0.1",
68
68
  "word-error-rate": "0.0.7",
69
69
  "write-yaml": "^1.0.0",
@@ -87,7 +87,7 @@
87
87
  "eslint-plugin-promise": "^6.1.1",
88
88
  "eslint-plugin-standard": "^4.1.0",
89
89
  "mocha": "^10.3.0",
90
- "nock": "^13.5.1",
90
+ "nock": "^14.0.0-beta.19",
91
91
  "npm-check-updates": "^16.14.15",
92
92
  "nyc": "^15.1.0",
93
93
  "rollup": "2.79.1",
@@ -1,4 +1,3 @@
1
- const request = require('request')
2
1
  const express = require('express')
3
2
  const bodyParser = require('body-parser')
4
3
  const _ = require('lodash')
@@ -13,32 +12,39 @@ const jokes = [
13
12
  'A blonde was bragging about her knowledge of state capitals. She proudly says, Go ahead, ask me, I know all of them. A friend says, OK, what\'s the capital of Wisconsin? The blonde replies, Oh, that\'s easy: W.'
14
13
  ]
15
14
 
16
- sendAsyncText = async (req, res, text) => {
15
+ const sendAsyncText = async (req, res, text) => {
17
16
  const callbackUri = req.body.callbackUri
18
17
 
19
- return new Promise((resolve, reject) => {
20
- request({
18
+ try {
19
+ const response = await fetch(callbackUri, {
21
20
  method: 'POST',
22
- uri: callbackUri,
23
- body: {
21
+ headers: {
22
+ 'Content-Type': 'application/json'
23
+ },
24
+ body: JSON.stringify({
24
25
  conversationId: req.body.conversationId,
25
26
  responses: [
26
27
  {
27
28
  text
28
29
  }
29
30
  ]
30
- },
31
- json: true
32
- }, (err, response, body) => {
33
- if (err) {
34
- console.log('failed async response: ' + err)
35
- reject(err)
36
- } else {
37
- console.log('async response got response ' + response.statusCode)
38
- resolve()
39
- }
31
+ })
40
32
  })
41
- })
33
+
34
+ if (!response.ok) {
35
+ const error = `Failed async response: ${response.status} ${response.statusText}`
36
+ console.log(error)
37
+ // just backward compatibility. Currently I get 401 error, but the chat itself is working.
38
+ // If I activate this, then the chat will not work at all.
39
+ // I suppose api key is not good,
40
+ // throw new Error(error);
41
+ } else {
42
+ console.log('Async response got response ' + response.status)
43
+ }
44
+ } catch (err) {
45
+ console.error('Error in sendAsyncText:', err.message)
46
+ throw err
47
+ }
42
48
  }
43
49
 
44
50
  app.post('/joke', async (req, res) => {
@@ -55,7 +61,7 @@ app.post('/joke', async (req, res) => {
55
61
  responses: [
56
62
  {
57
63
  text: 'ok, not funny'
58
- },
64
+ }
59
65
  ]
60
66
  })
61
67
  })
@@ -56,6 +56,7 @@ module.exports = {
56
56
  SIMPLEREST_HEADERS_TEMPLATE: 'SIMPLEREST_HEADERS_TEMPLATE',
57
57
  SIMPLEREST_BODY_TEMPLATE: 'SIMPLEREST_BODY_TEMPLATE',
58
58
  SIMPLEREST_BODY_RAW: 'SIMPLEREST_BODY_RAW',
59
+ SIMPLEREST_BODY_FROM_JSON: 'SIMPLEREST_BODY_FROM_JSON',
59
60
  SIMPLEREST_START_HOOK: 'SIMPLEREST_START_HOOK',
60
61
  SIMPLEREST_STOP_HOOK: 'SIMPLEREST_STOP_HOOK',
61
62
  SIMPLEREST_REQUEST_HOOK: 'SIMPLEREST_REQUEST_HOOK',
@@ -133,6 +134,8 @@ module.exports = {
133
134
  SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER: 'SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER',
134
135
  SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY: 'SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY',
135
136
  SCRIPTING_NORMALIZE_TEXT: 'SCRIPTING_NORMALIZE_TEXT',
137
+ SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES: 'SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES',
138
+ SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP: 'SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP',
136
139
  SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY',
137
140
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
138
141
  SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS: 'SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS',
@@ -3,7 +3,6 @@ const async = require('async')
3
3
  const rimraf = require('rimraf')
4
4
  const Bottleneck = require('bottleneck')
5
5
  const _ = require('lodash')
6
- const request = require('request')
7
6
  const debug = require('debug')('botium-connector-BaseContainer')
8
7
 
9
8
  const Events = require('../Events')
@@ -239,7 +238,7 @@ module.exports = class BaseContainer {
239
238
 
240
239
  async _RunCustomHook (name, hook, args) {
241
240
  try {
242
- await executeHook(this.caps, hook, Object.assign({ container: this, request }, args))
241
+ await executeHook(this.caps, hook, Object.assign({ container: this, fetch }, args))
243
242
  } catch (err) {
244
243
  debug(`_RunCustomHook ${name} finished with error: ${err.message || util.inspect(err)}`)
245
244
  }
@@ -1,6 +1,5 @@
1
1
  const util = require('util')
2
2
  const async = require('async')
3
- const request = require('request')
4
3
  const Mustache = require('mustache')
5
4
  const jp = require('jsonpath')
6
5
  const mime = require('mime-types')
@@ -8,6 +7,7 @@ const { v4: uuidv4 } = require('uuid')
8
7
  const Redis = require('ioredis')
9
8
  const _ = require('lodash')
10
9
  const debug = require('debug')('botium-connector-simplerest')
10
+ const { ProxyAgent, Agent } = require('undici')
11
11
 
12
12
  const { startProxy } = require('../../grid/inbound/proxy')
13
13
  const botiumUtils = require('../../helpers/Utils')
@@ -77,6 +77,17 @@ module.exports = class SimpleRestContainer {
77
77
  }
78
78
 
79
79
  Build () {
80
+ const agentOptions = {
81
+ connect: {
82
+ rejectUnauthorized: !!this.caps[Capabilities.SIMPLEREST_STRICT_SSL] // Equivalent to strictSSL of request module
83
+ }
84
+ }
85
+ if (this.caps[Capabilities.SIMPLEREST_PROXY_URL]) {
86
+ this.dispatcher = new ProxyAgent(this.caps[Capabilities.SIMPLEREST_PROXY_URL], agentOptions)
87
+ } else {
88
+ this.dispatcher = new Agent(agentOptions)
89
+ }
90
+
80
91
  return this._buildInbound()
81
92
  }
82
93
 
@@ -235,6 +246,31 @@ module.exports = class SimpleRestContainer {
235
246
  return this._cleanInbound()
236
247
  }
237
248
 
249
+ _fetchify (requestOptions) {
250
+ requestOptions.signal = AbortSignal.timeout(requestOptions.timeout)
251
+ delete requestOptions.timeout
252
+
253
+ if (requestOptions.body && !_.isString(requestOptions.body)) {
254
+ requestOptions.body = JSON.stringify(requestOptions.body)
255
+ if (!requestOptions.headers) requestOptions.headers = {}
256
+ if (!requestOptions.headers['Content-Type'] && !requestOptions.headers['content-type']) {
257
+ requestOptions.headers['Content-Type'] = 'application/json'
258
+ }
259
+ }
260
+
261
+ if (requestOptions.form) {
262
+ if (requestOptions.body) {
263
+ debug('Request.form and request.body are mutually exclusive')
264
+ }
265
+ requestOptions.body = new URLSearchParams(requestOptions.form).toString()
266
+ // it is set automatically by fetch
267
+ // requestOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded'
268
+ delete requestOptions.form
269
+ }
270
+
271
+ return requestOptions
272
+ }
273
+
238
274
  // Separated just for better module testing
239
275
  async _processBodyAsync (body, headers, isFromUser, updateContext) {
240
276
  const p = async () => {
@@ -511,12 +547,25 @@ module.exports = class SimpleRestContainer {
511
547
 
512
548
  this.waitProcessQueue = []
513
549
 
514
- request(requestOptions, async (err, response, body) => {
515
- if (err) {
516
- return reject(new Error(`rest request failed: ${err.message}`))
517
- } else {
518
- if (response.statusCode >= 400) {
519
- debug(`got error response: ${response.statusCode}/${response.statusMessage}`)
550
+ try {
551
+ fetch(requestOptions.uri, requestOptions).then(async (bodyRaw) => {
552
+ let body
553
+ try {
554
+ if (bodyRaw.headers.get('content-type').includes('application/json')) {
555
+ try {
556
+ body = await bodyRaw.json()
557
+ } catch (err) {
558
+ body = await bodyRaw.text()
559
+ debug(`failed to parse body as text, using text instead: ${body}`)
560
+ }
561
+ } else {
562
+ body = await bodyRaw.text()
563
+ }
564
+ } catch (err) {
565
+ return reject(new Error(`cant parse body: ${err.message}`))
566
+ }
567
+ if (!bodyRaw.ok) {
568
+ debug(`got error response: ${bodyRaw.status}/${bodyRaw.statusText}`)
520
569
  if (debug.enabled && body) {
521
570
  debug(botiumUtils.shortenJsonString(body))
522
571
  }
@@ -524,40 +573,45 @@ module.exports = class SimpleRestContainer {
524
573
  const jsonBody = botiumUtils.toJsonWeak(body)
525
574
  const errKey = Object.keys(jsonBody).find(k => k.startsWith('err') || k.startsWith('fail'))
526
575
  if (errKey) {
527
- return reject(new BotiumError(`got error response: ${response.statusCode}/${response.statusMessage} - ${jsonBody[errKey]}`, {
576
+ return reject(new BotiumError(`got error response: ${bodyRaw.status}/${bodyRaw.statusText} - ${jsonBody[errKey]}`, {
528
577
  message: botiumUtils.shortenJsonString(body)
529
578
  }))
530
579
  }
531
580
  }
532
- return reject(new Error(`got error response: ${response.statusCode}/${response.statusMessage}`))
581
+ return reject(new Error(`got error response: ${bodyRaw.status}/${bodyRaw.statusText}`))
533
582
  }
534
-
535
583
  if (body) {
536
- debug(`got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}, headers: ${botiumUtils.shortenJsonString(response.headers)}`)
537
- this._storeCookiesFromResponse(response)
538
- try {
539
- body = await this._parseResponseBody(body)
540
- } catch (err) {
541
- debug(`ignoring not JSON formatted response body: ${err.message}`)
542
- resolve(this)
543
- this._emptyWaitProcessQueue()
544
- return
545
- }
546
-
547
- if (body) {
548
- this._processBodyAsync(body, response.headers, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue())
549
- } else {
550
- debug('ignoring response body (no string and no JSON object)')
551
- resolve(this)
552
- this._emptyWaitProcessQueue()
553
- }
584
+ debug(`got response code: ${bodyRaw.status}/${bodyRaw.statusText}, body: ${botiumUtils.shortenJsonString(body)}, headers: ${botiumUtils.shortenJsonString(bodyRaw.headers)}`)
585
+ this._storeCookiesFromResponse(bodyRaw)
586
+
587
+ this._parseResponseBody(body)
588
+ .then((parsedBody) => {
589
+ body = parsedBody
590
+ if (body) {
591
+ this._processBodyAsync(body, bodyRaw.headers, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue())
592
+ } else {
593
+ debug('ignoring response body (no string and no JSON object)')
594
+ resolve(this)
595
+ this._emptyWaitProcessQueue()
596
+ }
597
+ })
598
+ .catch((err) => {
599
+ debug(`ignoring not JSON formatted response body: ${err.message}`)
600
+ resolve(this)
601
+ this._emptyWaitProcessQueue()
602
+ })
554
603
  } else {
555
- debug(`got response code: ${response.statusCode}, empty body`)
604
+ debug(`got response code: ${bodyRaw.status}/${bodyRaw.statusText}, empty body`)
556
605
  resolve(this)
557
606
  this._emptyWaitProcessQueue()
558
607
  }
559
- }
560
- })
608
+ }).catch((err) => {
609
+ // rest request failed: fetch failed - Cause code: ECONNREFUSED - Cause message: connect ECONNREFUSED 127.0.0.1:3006
610
+ return reject(new Error(`rest request failed: ${err.message}${err.cause && err.cause.code ? ' - Cause code: ' + err.cause.code : ''}${err.cause && err.cause.message ? ' - Cause message: ' + err.cause.message : ''}`))
611
+ })
612
+ } catch (err) {
613
+ return reject(new Error(`rest request failed: ${err.message}`))
614
+ }
561
615
  }))
562
616
  }
563
617
 
@@ -580,7 +634,6 @@ module.exports = class SimpleRestContainer {
580
634
  const requestOptions = {
581
635
  uri,
582
636
  method: this._getCapValue(Capabilities.SIMPLEREST_VERB) || this._getCapValue(Capabilities.SIMPLEREST_METHOD),
583
- followAllRedirects: true,
584
637
  timeout
585
638
  }
586
639
 
@@ -592,7 +645,14 @@ module.exports = class SimpleRestContainer {
592
645
  throw new Error(`composing headers from SIMPLEREST_HEADERS_TEMPLATE failed (${err.message})`)
593
646
  }
594
647
  }
595
- if (this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]) {
648
+ // It is possible to mix json, and non-json request messages. So it can happen that both
649
+ // SIMPLEREST_BODY_FROM_JSON and SIMPLEREST_BODY_TEMPLATE are set
650
+ if (this.caps[Capabilities.SIMPLEREST_BODY_FROM_JSON] && msg.sourceData && Object.keys(msg.sourceData).length > 0) {
651
+ requestOptions.body = msg.sourceData
652
+ requestOptions.json = true
653
+ if (!requestOptions.headers) requestOptions.headers = {}
654
+ requestOptions.headers['Content-Type'] = 'application/json'
655
+ } else if (this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]) {
596
656
  const bodyRaw = this._getCapValue(Capabilities.SIMPLEREST_BODY_RAW)
597
657
  if (bodyRaw) {
598
658
  this.view.msg.messageText = nonEncodedMessage
@@ -605,6 +665,10 @@ module.exports = class SimpleRestContainer {
605
665
  if (requestOptions.json && (!requestOptions.body || Object.keys(requestOptions.body).length === 0)) {
606
666
  debug(`warning: requestOptions.body content seems to be empty - ${requestOptions.body} - capability: "${this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]}"`)
607
667
  }
668
+ if (!bodyRaw) {
669
+ if (!requestOptions.headers) requestOptions.headers = {}
670
+ requestOptions.headers['Content-Type'] = 'application/json'
671
+ }
608
672
  } catch (err) {
609
673
  throw new Error(`composing body from SIMPLEREST_BODY_TEMPLATE failed (${err.message})`)
610
674
  }
@@ -646,48 +710,53 @@ module.exports = class SimpleRestContainer {
646
710
  }
647
711
  }
648
712
  this._addRequestOptions(requestOptions)
713
+ this._fetchify(requestOptions)
649
714
  await executeHook(this.caps, this.requestHook, Object.assign({ requestOptions }, this.view))
650
715
  this._addRequestCookies(requestOptions)
651
716
 
652
717
  return requestOptions
653
718
  }
654
719
 
655
- async _waitForUrlResponse (pingConfig, retries) {
720
+ async _waitForUrlResponse (httpConfig, retries) {
656
721
  const timeout = ms => new Promise(resolve => setTimeout(resolve, ms))
657
722
 
658
723
  let tries = 0
659
724
 
660
725
  while (true) {
661
- debug(`_waitForUrlResponse checking url ${pingConfig.uri} before proceed`)
726
+ debug(`_waitForUrlResponse checking url ${httpConfig.uri} before proceed`)
662
727
  if (tries > retries) {
663
728
  throw new Error(`Failed to ping bot after ${retries} retries`)
664
729
  }
665
730
  tries++
666
- const { err, response, body } = await this.bottleneck(() => new Promise((resolve) => {
667
- request(pingConfig, (err, response, body) => {
668
- resolve({ err, response, body })
669
- })
670
- }))
731
+ let bodyRaw
732
+ let body
733
+ let err
734
+ try {
735
+ bodyRaw = await this.bottleneck(() => fetch(httpConfig.uri, httpConfig))
736
+ body = await ((httpConfig.json) ? bodyRaw.json() : bodyRaw.text())
737
+ } catch (e) {
738
+ err = e
739
+ }
671
740
  if (err) {
672
- debug(`_waitForUrlResponse error on url check ${pingConfig.uri}: ${err}`)
741
+ debug(`_waitForUrlResponse error on url check ${httpConfig.uri}: ${err}`)
673
742
  if (tries <= retries) {
674
- await timeout(pingConfig.timeout)
743
+ await timeout(httpConfig.timeout)
675
744
  }
676
- } else if (response.statusCode >= 400) {
677
- debug(`_waitForUrlResponse on url check ${pingConfig.uri} got error response: ${response.statusCode}/${response.statusMessage}`)
745
+ } else if (!bodyRaw.ok) {
746
+ debug(`_waitForUrlResponse on url check ${httpConfig.uri} got error response: ${bodyRaw.status}/${bodyRaw.statusText}`)
678
747
  if (debug.enabled && body) {
679
748
  debug(botiumUtils.shortenJsonString(body))
680
749
  }
681
750
  if (tries <= retries) {
682
- await timeout(pingConfig.timeout)
751
+ await timeout(httpConfig.timeout)
683
752
  }
684
753
  } else {
685
- debug(`_waitForUrlResponse success on url check ${pingConfig.uri}: ${response.statusCode}/${response.statusMessage}`)
686
- this._storeCookiesFromResponse(response)
754
+ debug(`_waitForUrlResponse success on url check ${httpConfig.uri}: ${bodyRaw.status}/${bodyRaw.statusText}`)
755
+ this._storeCookiesFromResponse(bodyRaw)
687
756
  if (debug.enabled && body) {
688
- debug(`body: ${botiumUtils.shortenJsonString(body)}, headers: ${botiumUtils.shortenJsonString(response.headers)}`)
757
+ debug(`body: ${botiumUtils.shortenJsonString(body)}, headers: ${botiumUtils.shortenJsonString(bodyRaw.headers)}`)
689
758
  }
690
- return { body, headers: response.headers }
759
+ return { body, headers: bodyRaw.headers }
691
760
  }
692
761
  }
693
762
  }
@@ -848,7 +917,6 @@ module.exports = class SimpleRestContainer {
848
917
  const pollConfig = {
849
918
  method: verb,
850
919
  uri,
851
- followAllRedirects: true,
852
920
  timeout
853
921
  }
854
922
  if (this.caps[Capabilities.SIMPLEREST_POLL_HEADERS]) {
@@ -864,13 +932,17 @@ module.exports = class SimpleRestContainer {
864
932
  try {
865
933
  pollConfig.body = this._getMustachedCap(Capabilities.SIMPLEREST_POLL_BODY, !bodyRaw)
866
934
  pollConfig.json = !bodyRaw
935
+ if (!bodyRaw) {
936
+ if (!pollConfig.headers) pollConfig.headers = {}
937
+ pollConfig.headers['Content-Type'] = 'application/json'
938
+ }
867
939
  } catch (err) {
868
940
  debug(`_runPolling: composing body from SIMPLEREST_POLL_BODY failed (${err.message})`)
869
941
  return
870
942
  }
871
943
  }
872
944
  this._addRequestOptions(pollConfig)
873
-
945
+ this._fetchify(pollConfig)
874
946
  try {
875
947
  await executeHook(this.caps, this.pollRequestHook, Object.assign({ requestOptions: pollConfig }, this.view))
876
948
  } catch (err) {
@@ -879,34 +951,34 @@ module.exports = class SimpleRestContainer {
879
951
  }
880
952
  this._addRequestCookies(pollConfig)
881
953
 
882
- request(pollConfig, async (err, response, body) => {
883
- if (err) {
884
- debug(`_runPolling: rest request failed: ${err.message}, request: ${JSON.stringify(pollConfig)}`)
885
- } else {
886
- if (response.statusCode >= 400) {
887
- debug(`_runPolling: got error response: ${response.statusCode}/${response.statusMessage}, request: ${JSON.stringify(pollConfig)}`)
888
- if (debug.enabled && body) {
889
- debug(botiumUtils.shortenJsonString(body))
890
- }
891
- } else if (body) {
892
- debug(`_runPolling: got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}, headers: ${botiumUtils.shortenJsonString(response.headers)}`)
893
- this._storeCookiesFromResponse(response)
894
- try {
895
- body = await this._parseResponseBody(body)
896
- } catch (err) {
897
- debug(`_runPolling: ignoring not JSON formatted response body: ${err.message}`)
898
- return
899
- }
900
- if (body) {
901
- setTimeout(() => this._processBodyAsync(body, response.headers, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0)
902
- } else {
903
- debug('_runPolling: ignoring response body (no string and no JSON object)')
904
- }
954
+ try {
955
+ const bodyRaw = await fetch(pollConfig.uri, pollConfig)
956
+ let body = await ((pollConfig.json) ? bodyRaw.json() : bodyRaw.text())
957
+ if (!bodyRaw.ok) {
958
+ debug(`_runPolling: got error response: ${bodyRaw.status}/${bodyRaw.statusText}, request: ${JSON.stringify(pollConfig)}`)
959
+ if (debug.enabled && body) {
960
+ debug(botiumUtils.shortenJsonString(body))
961
+ }
962
+ } else if (body) {
963
+ debug(`_runPolling: got response code: ${bodyRaw.status}/${bodyRaw.statusText}, body: ${botiumUtils.shortenJsonString(body)}, headers: ${botiumUtils.shortenJsonString(bodyRaw.headers)}`)
964
+ this._storeCookiesFromResponse(bodyRaw)
965
+ try {
966
+ body = await this._parseResponseBody(body)
967
+ } catch (err) {
968
+ debug(`_runPolling: ignoring not JSON formatted response body: ${err.message}`)
969
+ return
970
+ }
971
+ if (body) {
972
+ setTimeout(() => this._processBodyAsync(body, bodyRaw.headers, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0)
905
973
  } else {
906
- debug(`_runPolling: got response code: ${response.statusCode}, empty body`)
974
+ debug('_runPolling: ignoring response body (no string and no JSON object)')
907
975
  }
976
+ } else {
977
+ debug(`_runPolling: got response code: ${bodyRaw.status}/${bodyRaw.statusText}, empty body`)
908
978
  }
909
- })
979
+ } catch (err) {
980
+ debug(`_runPolling: rest request failed: ${err.message}, request: ${JSON.stringify(pollConfig)}`)
981
+ }
910
982
  }
911
983
  }
912
984
 
@@ -932,7 +1004,6 @@ module.exports = class SimpleRestContainer {
932
1004
  const httpConfig = {
933
1005
  method: verb,
934
1006
  uri,
935
- followAllRedirects: true,
936
1007
  timeout
937
1008
  }
938
1009
  if (this.caps[`${capPrefix}_HEADERS`]) {
@@ -952,6 +1023,7 @@ module.exports = class SimpleRestContainer {
952
1023
  }
953
1024
  }
954
1025
  this._addRequestOptions(httpConfig)
1026
+ this._fetchify(httpConfig)
955
1027
  await executeHook(this.caps, this.requestHooks[capPrefix], Object.assign({ requestOptions: httpConfig }, this.view))
956
1028
  this._addRequestCookies(httpConfig)
957
1029
 
@@ -962,9 +1034,8 @@ module.exports = class SimpleRestContainer {
962
1034
  }
963
1035
 
964
1036
  _addRequestOptions (httpConfig) {
965
- httpConfig.strictSSL = !!this.caps[Capabilities.SIMPLEREST_STRICT_SSL]
966
- if (this.caps[Capabilities.SIMPLEREST_PROXY_URL]) {
967
- httpConfig.proxy = this.caps[Capabilities.SIMPLEREST_PROXY_URL]
1037
+ if (this.dispatcher) {
1038
+ httpConfig.dispatcher = this.dispatcher
968
1039
  }
969
1040
  if (this.caps[Capabilities.SIMPLEREST_EXTRA_OPTIONS]) {
970
1041
  _.merge(httpConfig, this.caps[Capabilities.SIMPLEREST_EXTRA_OPTIONS])
@@ -979,25 +1050,23 @@ module.exports = class SimpleRestContainer {
979
1050
  if (!requestOptions.headers) {
980
1051
  requestOptions.headers = {}
981
1052
  }
982
- requestOptions.headers.Cookie = requestOptions.headers.Cookie ? `${requestOptions.headers.Cookie}; ${this.cookies[url.host]}` : this.cookies[url.host]
1053
+ if (this.cookies[url.host]) {
1054
+ requestOptions.headers.Cookie = requestOptions.headers.Cookie ? `${requestOptions.headers.Cookie}; ${this.cookies[url.host]}` : this.cookies[url.host]
1055
+ }
983
1056
  }
984
1057
 
985
1058
  _storeCookiesFromResponse (response) {
986
1059
  if (!this.caps[Capabilities.SIMPLEREST_COOKIE_REPLICATION] || !response) {
987
1060
  return
988
1061
  }
989
- const responseCookies = response.headers['set-cookie']
1062
+ const responseCookies = response.headers.get('set-cookie')
990
1063
  if (!responseCookies) {
991
1064
  return
992
1065
  }
993
- const host = _.get(response, 'request.uri.host')
994
- let cookie
995
- responseCookies.forEach(cookieString => {
996
- cookie = cookie ? `${cookie}; ${cookieString}` : cookieString
997
- })
1066
+ const host = response?.url ? new URL(response.url).host : null
998
1067
 
999
- if (cookie) {
1000
- this.cookies[host] = cookie
1068
+ if (host) {
1069
+ this.cookies[host] = responseCookies
1001
1070
  }
1002
1071
  }
1003
1072
  }
@@ -825,7 +825,7 @@ class Convo {
825
825
  }
826
826
 
827
827
  _checkNormalizeText (container, str) {
828
- return normalizeText(str, !!container.caps[Capabilities.SCRIPTING_NORMALIZE_TEXT])
828
+ return normalizeText(str, container.caps)
829
829
  }
830
830
 
831
831
  expandPartialConvos () {