api-net-spec.js 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647
  1. const assert = require('assert')
  2. const { remote } = require('electron')
  3. const { ipcRenderer } = require('electron')
  4. const http = require('http')
  5. const url = require('url')
  6. const { net } = remote
  7. const { session } = remote
  8. /* The whole net API doesn't use standard callbacks */
  9. /* eslint-disable standard/no-callback-literal */
  10. function randomBuffer (size, start, end) {
  11. start = start || 0
  12. end = end || 255
  13. const range = 1 + end - start
  14. const buffer = Buffer.allocUnsafe(size)
  15. for (let i = 0; i < size; ++i) {
  16. buffer[i] = start + Math.floor(Math.random() * range)
  17. }
  18. return buffer
  19. }
  20. function randomString (length) {
  21. const buffer = randomBuffer(length, '0'.charCodeAt(0), 'z'.charCodeAt(0))
  22. return buffer.toString()
  23. }
  24. const kOneKiloByte = 1024
  25. const kOneMegaByte = kOneKiloByte * kOneKiloByte
  26. describe('net module', () => {
  27. let server
  28. const connections = new Set()
  29. beforeEach((done) => {
  30. server = http.createServer()
  31. server.listen(0, '127.0.0.1', () => {
  32. server.url = `http://127.0.0.1:${server.address().port}`
  33. done()
  34. })
  35. server.on('connection', (connection) => {
  36. connections.add(connection)
  37. connection.once('close', () => {
  38. connections.delete(connection)
  39. })
  40. })
  41. })
  42. afterEach((done) => {
  43. for (const connection of connections) {
  44. connection.destroy()
  45. }
  46. server.close(() => {
  47. server = null
  48. done()
  49. })
  50. })
  51. describe('HTTP basics', () => {
  52. it('should be able to issue a basic GET request', (done) => {
  53. const requestUrl = '/requestUrl'
  54. server.on('request', (request, response) => {
  55. switch (request.url) {
  56. case requestUrl:
  57. assert.strictEqual(request.method, 'GET')
  58. response.end()
  59. break
  60. default:
  61. handleUnexpectedURL(request, response)
  62. }
  63. })
  64. const urlRequest = net.request(`${server.url}${requestUrl}`)
  65. urlRequest.on('response', (response) => {
  66. assert.strictEqual(response.statusCode, 200)
  67. response.pause()
  68. response.on('data', (chunk) => {
  69. })
  70. response.on('end', () => {
  71. done()
  72. })
  73. response.resume()
  74. })
  75. urlRequest.end()
  76. })
  77. it('should be able to issue a basic POST request', (done) => {
  78. const requestUrl = '/requestUrl'
  79. server.on('request', (request, response) => {
  80. switch (request.url) {
  81. case requestUrl:
  82. assert.strictEqual(request.method, 'POST')
  83. response.end()
  84. break
  85. default:
  86. handleUnexpectedURL(request, response)
  87. }
  88. })
  89. const urlRequest = net.request({
  90. method: 'POST',
  91. url: `${server.url}${requestUrl}`
  92. })
  93. urlRequest.on('response', (response) => {
  94. assert.strictEqual(response.statusCode, 200)
  95. response.pause()
  96. response.on('data', (chunk) => {
  97. })
  98. response.on('end', () => {
  99. done()
  100. })
  101. response.resume()
  102. })
  103. urlRequest.end()
  104. })
  105. it('should fetch correct data in a GET request', (done) => {
  106. const requestUrl = '/requestUrl'
  107. const bodyData = 'Hello World!'
  108. server.on('request', (request, response) => {
  109. switch (request.url) {
  110. case requestUrl:
  111. assert.strictEqual(request.method, 'GET')
  112. response.write(bodyData)
  113. response.end()
  114. break
  115. default:
  116. handleUnexpectedURL(request, response)
  117. }
  118. })
  119. const urlRequest = net.request(`${server.url}${requestUrl}`)
  120. urlRequest.on('response', (response) => {
  121. let expectedBodyData = ''
  122. assert.strictEqual(response.statusCode, 200)
  123. response.pause()
  124. response.on('data', (chunk) => {
  125. expectedBodyData += chunk.toString()
  126. })
  127. response.on('end', () => {
  128. assert.strictEqual(expectedBodyData, bodyData)
  129. done()
  130. })
  131. response.resume()
  132. })
  133. urlRequest.end()
  134. })
  135. it('should post the correct data in a POST request', (done) => {
  136. const requestUrl = '/requestUrl'
  137. const bodyData = 'Hello World!'
  138. server.on('request', (request, response) => {
  139. let postedBodyData = ''
  140. switch (request.url) {
  141. case requestUrl:
  142. assert.strictEqual(request.method, 'POST')
  143. request.on('data', (chunk) => {
  144. postedBodyData += chunk.toString()
  145. })
  146. request.on('end', () => {
  147. assert.strictEqual(postedBodyData, bodyData)
  148. response.end()
  149. })
  150. break
  151. default:
  152. handleUnexpectedURL(request, response)
  153. }
  154. })
  155. const urlRequest = net.request({
  156. method: 'POST',
  157. url: `${server.url}${requestUrl}`
  158. })
  159. urlRequest.on('response', (response) => {
  160. assert.strictEqual(response.statusCode, 200)
  161. response.pause()
  162. response.on('data', (chunk) => {})
  163. response.on('end', () => {
  164. done()
  165. })
  166. response.resume()
  167. })
  168. urlRequest.write(bodyData)
  169. urlRequest.end()
  170. })
  171. it('should support chunked encoding', (done) => {
  172. const requestUrl = '/requestUrl'
  173. server.on('request', (request, response) => {
  174. switch (request.url) {
  175. case requestUrl:
  176. response.statusCode = 200
  177. response.statusMessage = 'OK'
  178. response.chunkedEncoding = true
  179. assert.strictEqual(request.method, 'POST')
  180. assert.strictEqual(request.headers['transfer-encoding'], 'chunked')
  181. assert(!request.headers['content-length'])
  182. request.on('data', (chunk) => {
  183. response.write(chunk)
  184. })
  185. request.on('end', (chunk) => {
  186. response.end(chunk)
  187. })
  188. break
  189. default:
  190. handleUnexpectedURL(request, response)
  191. }
  192. })
  193. const urlRequest = net.request({
  194. method: 'POST',
  195. url: `${server.url}${requestUrl}`
  196. })
  197. let chunkIndex = 0
  198. const chunkCount = 100
  199. const sentChunks = []
  200. const receivedChunks = []
  201. urlRequest.on('response', (response) => {
  202. assert.strictEqual(response.statusCode, 200)
  203. response.pause()
  204. response.on('data', (chunk) => {
  205. receivedChunks.push(chunk)
  206. })
  207. response.on('end', () => {
  208. const sentData = Buffer.concat(sentChunks)
  209. const receivedData = Buffer.concat(receivedChunks)
  210. assert.strictEqual(sentData.toString(), receivedData.toString())
  211. assert.strictEqual(chunkIndex, chunkCount)
  212. done()
  213. })
  214. response.resume()
  215. })
  216. urlRequest.chunkedEncoding = true
  217. while (chunkIndex < chunkCount) {
  218. chunkIndex += 1
  219. const chunk = randomBuffer(kOneKiloByte)
  220. sentChunks.push(chunk)
  221. assert(urlRequest.write(chunk))
  222. }
  223. urlRequest.end()
  224. })
  225. })
  226. describe('ClientRequest API', () => {
  227. afterEach(() => {
  228. session.defaultSession.webRequest.onBeforeRequest(null)
  229. })
  230. it('request/response objects should emit expected events', (done) => {
  231. const requestUrl = '/requestUrl'
  232. const bodyData = randomString(kOneMegaByte)
  233. server.on('request', (request, response) => {
  234. switch (request.url) {
  235. case requestUrl:
  236. response.statusCode = 200
  237. response.statusMessage = 'OK'
  238. response.write(bodyData)
  239. response.end()
  240. break
  241. default:
  242. handleUnexpectedURL(request, response)
  243. }
  244. })
  245. let requestResponseEventEmitted = false
  246. let requestFinishEventEmitted = false
  247. let requestCloseEventEmitted = false
  248. let responseDataEventEmitted = false
  249. let responseEndEventEmitted = false
  250. function maybeDone (done) {
  251. if (!requestCloseEventEmitted || !responseEndEventEmitted) {
  252. return
  253. }
  254. assert(requestResponseEventEmitted)
  255. assert(requestFinishEventEmitted)
  256. assert(requestCloseEventEmitted)
  257. assert(responseDataEventEmitted)
  258. assert(responseEndEventEmitted)
  259. done()
  260. }
  261. const urlRequest = net.request({
  262. method: 'GET',
  263. url: `${server.url}${requestUrl}`
  264. })
  265. urlRequest.on('response', (response) => {
  266. requestResponseEventEmitted = true
  267. const statusCode = response.statusCode
  268. assert.strictEqual(statusCode, 200)
  269. const buffers = []
  270. response.pause()
  271. response.on('data', (chunk) => {
  272. buffers.push(chunk)
  273. responseDataEventEmitted = true
  274. })
  275. response.on('end', () => {
  276. const receivedBodyData = Buffer.concat(buffers)
  277. assert(receivedBodyData.toString() === bodyData)
  278. responseEndEventEmitted = true
  279. maybeDone(done)
  280. })
  281. response.resume()
  282. response.on('error', (error) => {
  283. assert.ifError(error)
  284. })
  285. response.on('aborted', () => {
  286. assert.fail('response aborted')
  287. })
  288. })
  289. urlRequest.on('finish', () => {
  290. requestFinishEventEmitted = true
  291. })
  292. urlRequest.on('error', (error) => {
  293. assert.ifError(error)
  294. })
  295. urlRequest.on('abort', () => {
  296. assert.fail('request aborted')
  297. })
  298. urlRequest.on('close', () => {
  299. requestCloseEventEmitted = true
  300. maybeDone(done)
  301. })
  302. urlRequest.end()
  303. })
  304. it('should be able to set a custom HTTP request header before first write', (done) => {
  305. const requestUrl = '/requestUrl'
  306. const customHeaderName = 'Some-Custom-Header-Name'
  307. const customHeaderValue = 'Some-Customer-Header-Value'
  308. server.on('request', (request, response) => {
  309. switch (request.url) {
  310. case requestUrl:
  311. assert.strictEqual(request.headers[customHeaderName.toLowerCase()],
  312. customHeaderValue)
  313. response.statusCode = 200
  314. response.statusMessage = 'OK'
  315. response.end()
  316. break
  317. default:
  318. handleUnexpectedURL(request, response)
  319. }
  320. })
  321. const urlRequest = net.request({
  322. method: 'GET',
  323. url: `${server.url}${requestUrl}`
  324. })
  325. urlRequest.on('response', (response) => {
  326. const statusCode = response.statusCode
  327. assert.strictEqual(statusCode, 200)
  328. response.pause()
  329. response.on('data', (chunk) => {
  330. })
  331. response.on('end', () => {
  332. done()
  333. })
  334. response.resume()
  335. })
  336. urlRequest.setHeader(customHeaderName, customHeaderValue)
  337. assert.strictEqual(urlRequest.getHeader(customHeaderName),
  338. customHeaderValue)
  339. assert.strictEqual(urlRequest.getHeader(customHeaderName.toLowerCase()),
  340. customHeaderValue)
  341. urlRequest.write('')
  342. assert.strictEqual(urlRequest.getHeader(customHeaderName),
  343. customHeaderValue)
  344. assert.strictEqual(urlRequest.getHeader(customHeaderName.toLowerCase()),
  345. customHeaderValue)
  346. urlRequest.end()
  347. })
  348. it('should be able to set a non-string object as a header value', (done) => {
  349. const requestUrl = '/requestUrl'
  350. const customHeaderName = 'Some-Integer-Value'
  351. const customHeaderValue = 900
  352. server.on('request', (request, response) => {
  353. switch (request.url) {
  354. case requestUrl:
  355. assert.strictEqual(request.headers[customHeaderName.toLowerCase()],
  356. customHeaderValue.toString())
  357. response.statusCode = 200
  358. response.statusMessage = 'OK'
  359. response.end()
  360. break
  361. default:
  362. assert.strictEqual(request.url, requestUrl)
  363. }
  364. })
  365. const urlRequest = net.request({
  366. method: 'GET',
  367. url: `${server.url}${requestUrl}`
  368. })
  369. urlRequest.on('response', (response) => {
  370. const statusCode = response.statusCode
  371. assert.strictEqual(statusCode, 200)
  372. response.pause()
  373. response.on('end', () => {
  374. done()
  375. })
  376. response.resume()
  377. })
  378. urlRequest.setHeader(customHeaderName, customHeaderValue)
  379. assert.strictEqual(urlRequest.getHeader(customHeaderName),
  380. customHeaderValue)
  381. assert.strictEqual(urlRequest.getHeader(customHeaderName.toLowerCase()),
  382. customHeaderValue)
  383. urlRequest.write('')
  384. assert.strictEqual(urlRequest.getHeader(customHeaderName),
  385. customHeaderValue)
  386. assert.strictEqual(urlRequest.getHeader(customHeaderName.toLowerCase()),
  387. customHeaderValue)
  388. urlRequest.end()
  389. })
  390. it('should not be able to set a custom HTTP request header after first write', (done) => {
  391. const requestUrl = '/requestUrl'
  392. const customHeaderName = 'Some-Custom-Header-Name'
  393. const customHeaderValue = 'Some-Customer-Header-Value'
  394. server.on('request', (request, response) => {
  395. switch (request.url) {
  396. case requestUrl:
  397. assert(!request.headers[customHeaderName.toLowerCase()])
  398. response.statusCode = 200
  399. response.statusMessage = 'OK'
  400. response.end()
  401. break
  402. default:
  403. handleUnexpectedURL(request, response)
  404. }
  405. })
  406. const urlRequest = net.request({
  407. method: 'GET',
  408. url: `${server.url}${requestUrl}`
  409. })
  410. urlRequest.on('response', (response) => {
  411. const statusCode = response.statusCode
  412. assert.strictEqual(statusCode, 200)
  413. response.pause()
  414. response.on('data', (chunk) => {
  415. })
  416. response.on('end', () => {
  417. done()
  418. })
  419. response.resume()
  420. })
  421. urlRequest.write('')
  422. assert.throws(() => {
  423. urlRequest.setHeader(customHeaderName, customHeaderValue)
  424. })
  425. assert(!urlRequest.getHeader(customHeaderName))
  426. urlRequest.end()
  427. })
  428. it('should be able to remove a custom HTTP request header before first write', (done) => {
  429. const requestUrl = '/requestUrl'
  430. const customHeaderName = 'Some-Custom-Header-Name'
  431. const customHeaderValue = 'Some-Customer-Header-Value'
  432. server.on('request', (request, response) => {
  433. switch (request.url) {
  434. case requestUrl:
  435. assert(!request.headers[customHeaderName.toLowerCase()])
  436. response.statusCode = 200
  437. response.statusMessage = 'OK'
  438. response.end()
  439. break
  440. default:
  441. handleUnexpectedURL(request, response)
  442. }
  443. })
  444. const urlRequest = net.request({
  445. method: 'GET',
  446. url: `${server.url}${requestUrl}`
  447. })
  448. urlRequest.on('response', (response) => {
  449. const statusCode = response.statusCode
  450. assert.strictEqual(statusCode, 200)
  451. response.pause()
  452. response.on('data', (chunk) => {
  453. })
  454. response.on('end', () => {
  455. done()
  456. })
  457. response.resume()
  458. })
  459. urlRequest.setHeader(customHeaderName, customHeaderValue)
  460. assert.strictEqual(urlRequest.getHeader(customHeaderName),
  461. customHeaderValue)
  462. urlRequest.removeHeader(customHeaderName)
  463. assert(!urlRequest.getHeader(customHeaderName))
  464. urlRequest.write('')
  465. urlRequest.end()
  466. })
  467. it('should not be able to remove a custom HTTP request header after first write', (done) => {
  468. const requestUrl = '/requestUrl'
  469. const customHeaderName = 'Some-Custom-Header-Name'
  470. const customHeaderValue = 'Some-Customer-Header-Value'
  471. server.on('request', (request, response) => {
  472. switch (request.url) {
  473. case requestUrl:
  474. assert.strictEqual(request.headers[customHeaderName.toLowerCase()],
  475. customHeaderValue)
  476. response.statusCode = 200
  477. response.statusMessage = 'OK'
  478. response.end()
  479. break
  480. default:
  481. handleUnexpectedURL(request, response)
  482. }
  483. })
  484. const urlRequest = net.request({
  485. method: 'GET',
  486. url: `${server.url}${requestUrl}`
  487. })
  488. urlRequest.on('response', (response) => {
  489. const statusCode = response.statusCode
  490. assert.strictEqual(statusCode, 200)
  491. response.pause()
  492. response.on('data', (chunk) => {
  493. })
  494. response.on('end', () => {
  495. done()
  496. })
  497. response.resume()
  498. })
  499. urlRequest.setHeader(customHeaderName, customHeaderValue)
  500. assert.strictEqual(urlRequest.getHeader(customHeaderName),
  501. customHeaderValue)
  502. urlRequest.write('')
  503. assert.throws(() => {
  504. urlRequest.removeHeader(customHeaderName)
  505. })
  506. assert.strictEqual(urlRequest.getHeader(customHeaderName),
  507. customHeaderValue)
  508. urlRequest.end()
  509. })
  510. it('should be able to set cookie header line', (done) => {
  511. const requestUrl = '/requestUrl'
  512. const cookieHeaderName = 'Cookie'
  513. const cookieHeaderValue = 'test=12345'
  514. const customSession = session.fromPartition('test-cookie-header')
  515. server.on('request', (request, response) => {
  516. switch (request.url) {
  517. case requestUrl:
  518. assert.strictEqual(request.headers[cookieHeaderName.toLowerCase()],
  519. cookieHeaderValue)
  520. response.statusCode = 200
  521. response.statusMessage = 'OK'
  522. response.end()
  523. break
  524. default:
  525. handleUnexpectedURL(request, response)
  526. }
  527. })
  528. customSession.cookies.set({
  529. url: `${server.url}`,
  530. name: 'test',
  531. value: '11111'
  532. }).then(() => { // resolved
  533. const urlRequest = net.request({
  534. method: 'GET',
  535. url: `${server.url}${requestUrl}`,
  536. session: customSession
  537. })
  538. urlRequest.on('response', (response) => {
  539. const statusCode = response.statusCode
  540. assert.strictEqual(statusCode, 200)
  541. response.pause()
  542. response.on('data', (chunk) => {})
  543. response.on('end', () => {
  544. done()
  545. })
  546. response.resume()
  547. })
  548. urlRequest.setHeader(cookieHeaderName, cookieHeaderValue)
  549. assert.strictEqual(urlRequest.getHeader(cookieHeaderName),
  550. cookieHeaderValue)
  551. urlRequest.end()
  552. }, (error) => {
  553. done(error)
  554. })
  555. })
  556. it('should be able to abort an HTTP request before first write', (done) => {
  557. const requestUrl = '/requestUrl'
  558. server.on('request', (request, response) => {
  559. response.end()
  560. assert.fail('Unexpected request event')
  561. })
  562. let requestAbortEventEmitted = false
  563. let requestCloseEventEmitted = false
  564. const urlRequest = net.request({
  565. method: 'GET',
  566. url: `${server.url}${requestUrl}`
  567. })
  568. urlRequest.on('response', (response) => {
  569. assert.fail('Unexpected response event')
  570. })
  571. urlRequest.on('finish', () => {
  572. assert.fail('Unexpected finish event')
  573. })
  574. urlRequest.on('error', () => {
  575. assert.fail('Unexpected error event')
  576. })
  577. urlRequest.on('abort', () => {
  578. requestAbortEventEmitted = true
  579. })
  580. urlRequest.on('close', () => {
  581. requestCloseEventEmitted = true
  582. assert(requestAbortEventEmitted)
  583. assert(requestCloseEventEmitted)
  584. done()
  585. })
  586. urlRequest.abort()
  587. assert(!urlRequest.write(''))
  588. urlRequest.end()
  589. })
  590. it('it should be able to abort an HTTP request before request end', (done) => {
  591. const requestUrl = '/requestUrl'
  592. let requestReceivedByServer = false
  593. server.on('request', (request, response) => {
  594. switch (request.url) {
  595. case requestUrl:
  596. requestReceivedByServer = true
  597. cancelRequest()
  598. break
  599. default:
  600. handleUnexpectedURL(request, response)
  601. }
  602. })
  603. let requestAbortEventEmitted = false
  604. let requestCloseEventEmitted = false
  605. const urlRequest = net.request({
  606. method: 'GET',
  607. url: `${server.url}${requestUrl}`
  608. })
  609. urlRequest.on('response', (response) => {
  610. assert.fail('Unexpected response event')
  611. })
  612. urlRequest.on('finish', () => {
  613. assert.fail('Unexpected finish event')
  614. })
  615. urlRequest.on('error', () => {
  616. assert.fail('Unexpected error event')
  617. })
  618. urlRequest.on('abort', () => {
  619. requestAbortEventEmitted = true
  620. })
  621. urlRequest.on('close', () => {
  622. requestCloseEventEmitted = true
  623. assert(requestReceivedByServer)
  624. assert(requestAbortEventEmitted)
  625. assert(requestCloseEventEmitted)
  626. done()
  627. })
  628. urlRequest.chunkedEncoding = true
  629. urlRequest.write(randomString(kOneKiloByte))
  630. function cancelRequest () {
  631. urlRequest.abort()
  632. }
  633. })
  634. it('it should be able to abort an HTTP request after request end and before response', (done) => {
  635. const requestUrl = '/requestUrl'
  636. let requestReceivedByServer = false
  637. server.on('request', (request, response) => {
  638. switch (request.url) {
  639. case requestUrl:
  640. requestReceivedByServer = true
  641. cancelRequest()
  642. process.nextTick(() => {
  643. response.statusCode = 200
  644. response.statusMessage = 'OK'
  645. response.end()
  646. })
  647. break
  648. default:
  649. handleUnexpectedURL(request, response)
  650. }
  651. })
  652. let requestAbortEventEmitted = false
  653. let requestFinishEventEmitted = false
  654. let requestCloseEventEmitted = false
  655. const urlRequest = net.request({
  656. method: 'GET',
  657. url: `${server.url}${requestUrl}`
  658. })
  659. urlRequest.on('response', (response) => {
  660. assert.fail('Unexpected response event')
  661. })
  662. urlRequest.on('finish', () => {
  663. requestFinishEventEmitted = true
  664. })
  665. urlRequest.on('error', () => {
  666. assert.fail('Unexpected error event')
  667. })
  668. urlRequest.on('abort', () => {
  669. requestAbortEventEmitted = true
  670. })
  671. urlRequest.on('close', () => {
  672. requestCloseEventEmitted = true
  673. assert(requestFinishEventEmitted)
  674. assert(requestReceivedByServer)
  675. assert(requestAbortEventEmitted)
  676. assert(requestCloseEventEmitted)
  677. done()
  678. })
  679. urlRequest.end(randomString(kOneKiloByte))
  680. function cancelRequest () {
  681. urlRequest.abort()
  682. }
  683. })
  684. it('it should be able to abort an HTTP request after response start', (done) => {
  685. const requestUrl = '/requestUrl'
  686. let requestReceivedByServer = false
  687. server.on('request', (request, response) => {
  688. switch (request.url) {
  689. case requestUrl:
  690. requestReceivedByServer = true
  691. response.statusCode = 200
  692. response.statusMessage = 'OK'
  693. response.write(randomString(kOneKiloByte))
  694. break
  695. default:
  696. handleUnexpectedURL(request, response)
  697. }
  698. })
  699. let requestFinishEventEmitted = false
  700. let requestResponseEventEmitted = false
  701. let requestAbortEventEmitted = false
  702. let requestCloseEventEmitted = false
  703. let responseAbortedEventEmitted = false
  704. const urlRequest = net.request({
  705. method: 'GET',
  706. url: `${server.url}${requestUrl}`
  707. })
  708. urlRequest.on('response', (response) => {
  709. requestResponseEventEmitted = true
  710. const statusCode = response.statusCode
  711. assert.strictEqual(statusCode, 200)
  712. response.pause()
  713. response.on('data', (chunk) => {
  714. })
  715. response.on('end', () => {
  716. assert.fail('Unexpected end event')
  717. })
  718. response.resume()
  719. response.on('error', () => {
  720. assert.fail('Unexpected error event')
  721. })
  722. response.on('aborted', () => {
  723. responseAbortedEventEmitted = true
  724. })
  725. urlRequest.abort()
  726. })
  727. urlRequest.on('finish', () => {
  728. requestFinishEventEmitted = true
  729. })
  730. urlRequest.on('error', () => {
  731. assert.fail('Unexpected error event')
  732. })
  733. urlRequest.on('abort', () => {
  734. requestAbortEventEmitted = true
  735. })
  736. urlRequest.on('close', () => {
  737. requestCloseEventEmitted = true
  738. assert(requestFinishEventEmitted, 'request should emit "finish" event')
  739. assert(requestReceivedByServer, 'request should be received by the server')
  740. assert(requestResponseEventEmitted, '"response" event should be emitted')
  741. assert(requestAbortEventEmitted, 'request should emit "abort" event')
  742. assert(responseAbortedEventEmitted, 'response should emit "aborted" event')
  743. assert(requestCloseEventEmitted, 'request should emit "close" event')
  744. done()
  745. })
  746. urlRequest.end(randomString(kOneKiloByte))
  747. })
  748. it('abort event should be emitted at most once', (done) => {
  749. const requestUrl = '/requestUrl'
  750. let requestReceivedByServer = false
  751. server.on('request', (request, response) => {
  752. switch (request.url) {
  753. case requestUrl:
  754. requestReceivedByServer = true
  755. cancelRequest()
  756. break
  757. default:
  758. handleUnexpectedURL(request, response)
  759. }
  760. })
  761. let requestFinishEventEmitted = false
  762. let requestAbortEventCount = 0
  763. let requestCloseEventEmitted = false
  764. const urlRequest = net.request({
  765. method: 'GET',
  766. url: `${server.url}${requestUrl}`
  767. })
  768. urlRequest.on('response', () => {
  769. assert.fail('Unexpected response event')
  770. })
  771. urlRequest.on('finish', () => {
  772. requestFinishEventEmitted = true
  773. })
  774. urlRequest.on('error', () => {
  775. assert.fail('Unexpected error event')
  776. })
  777. urlRequest.on('abort', () => {
  778. ++requestAbortEventCount
  779. urlRequest.abort()
  780. })
  781. urlRequest.on('close', () => {
  782. requestCloseEventEmitted = true
  783. // Let all pending async events to be emitted
  784. setTimeout(() => {
  785. assert(requestFinishEventEmitted)
  786. assert(requestReceivedByServer)
  787. assert.strictEqual(requestAbortEventCount, 1)
  788. assert(requestCloseEventEmitted)
  789. done()
  790. }, 500)
  791. })
  792. urlRequest.end(randomString(kOneKiloByte))
  793. function cancelRequest () {
  794. urlRequest.abort()
  795. urlRequest.abort()
  796. }
  797. })
  798. it('Requests should be intercepted by webRequest module', (done) => {
  799. const requestUrl = '/requestUrl'
  800. const redirectUrl = '/redirectUrl'
  801. let requestIsRedirected = false
  802. server.on('request', (request, response) => {
  803. switch (request.url) {
  804. case redirectUrl:
  805. requestIsRedirected = true
  806. response.end()
  807. break
  808. default:
  809. handleUnexpectedURL(request, response)
  810. }
  811. })
  812. let requestIsIntercepted = false
  813. session.defaultSession.webRequest.onBeforeRequest(
  814. (details, callback) => {
  815. if (details.url === `${server.url}${requestUrl}`) {
  816. requestIsIntercepted = true
  817. // Disabled due to false positive in StandardJS
  818. // eslint-disable-next-line standard/no-callback-literal
  819. callback({
  820. redirectURL: `${server.url}${redirectUrl}`
  821. })
  822. } else {
  823. callback({
  824. cancel: false
  825. })
  826. }
  827. })
  828. const urlRequest = net.request(`${server.url}${requestUrl}`)
  829. urlRequest.on('response', (response) => {
  830. assert.strictEqual(response.statusCode, 200)
  831. response.pause()
  832. response.on('data', (chunk) => {
  833. })
  834. response.on('end', () => {
  835. assert(requestIsRedirected, 'The server should receive a request to the forward URL')
  836. assert(requestIsIntercepted, 'The request should be intercepted by the webRequest module')
  837. done()
  838. })
  839. response.resume()
  840. })
  841. urlRequest.end()
  842. })
  843. it('Should throw when invalid filters are passed to a request', () => {
  844. assert.throws(() => {
  845. session.defaultSession.webRequest.onBeforeRequest(
  846. { urls: ['*://www.googleapis.com'] },
  847. (details, callback) => { callback({ cancel: false }) }
  848. )
  849. }, 'Invalid url pattern *://www.googleapis.com: Empty path.')
  850. assert.throws(() => {
  851. session.defaultSession.webRequest.onBeforeRequest(
  852. { urls: [ '*://www.googleapis.com/', '*://blahblah.dev' ] },
  853. (details, callback) => { callback({ cancel: false }) }
  854. )
  855. }, 'Invalid url pattern *://blahblah.dev: Empty path.')
  856. })
  857. it('should to able to create and intercept a request using a custom session object', (done) => {
  858. const requestUrl = '/requestUrl'
  859. const redirectUrl = '/redirectUrl'
  860. const customPartitionName = 'custom-partition'
  861. let requestIsRedirected = false
  862. server.on('request', (request, response) => {
  863. switch (request.url) {
  864. case redirectUrl:
  865. requestIsRedirected = true
  866. response.end()
  867. break
  868. default:
  869. handleUnexpectedURL(request, response)
  870. }
  871. })
  872. session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
  873. assert.fail('Request should not be intercepted by the default session')
  874. })
  875. const customSession = session.fromPartition(customPartitionName, { cache: false })
  876. let requestIsIntercepted = false
  877. customSession.webRequest.onBeforeRequest((details, callback) => {
  878. if (details.url === `${server.url}${requestUrl}`) {
  879. requestIsIntercepted = true
  880. // Disabled due to false positive in StandardJS
  881. // eslint-disable-next-line standard/no-callback-literal
  882. callback({
  883. redirectURL: `${server.url}${redirectUrl}`
  884. })
  885. } else {
  886. callback({
  887. cancel: false
  888. })
  889. }
  890. })
  891. const urlRequest = net.request({
  892. url: `${server.url}${requestUrl}`,
  893. session: customSession
  894. })
  895. urlRequest.on('response', (response) => {
  896. assert.strictEqual(response.statusCode, 200)
  897. response.pause()
  898. response.on('data', (chunk) => {
  899. })
  900. response.on('end', () => {
  901. assert(requestIsRedirected, 'The server should receive a request to the forward URL')
  902. assert(requestIsIntercepted, 'The request should be intercepted by the webRequest module')
  903. done()
  904. })
  905. response.resume()
  906. })
  907. urlRequest.end()
  908. })
  909. it('should throw if given an invalid redirect mode', () => {
  910. const requestUrl = '/requestUrl'
  911. const options = {
  912. url: `${server.url}${requestUrl}`,
  913. redirect: 'custom'
  914. }
  915. assert.throws(() => {
  916. net.request(options)
  917. }, 'redirect mode should be one of follow, error or manual')
  918. })
  919. it('should throw when calling getHeader without a name', () => {
  920. assert.throws(() => {
  921. net.request({ url: `${server.url}/requestUrl` }).getHeader()
  922. }, /`name` is required for getHeader\(name\)\./)
  923. assert.throws(() => {
  924. net.request({ url: `${server.url}/requestUrl` }).getHeader(null)
  925. }, /`name` is required for getHeader\(name\)\./)
  926. })
  927. it('should throw when calling removeHeader without a name', () => {
  928. assert.throws(() => {
  929. net.request({ url: `${server.url}/requestUrl` }).removeHeader()
  930. }, /`name` is required for removeHeader\(name\)\./)
  931. assert.throws(() => {
  932. net.request({ url: `${server.url}/requestUrl` }).removeHeader(null)
  933. }, /`name` is required for removeHeader\(name\)\./)
  934. })
  935. it('should follow redirect when no redirect mode is provided', (done) => {
  936. const requestUrl = '/301'
  937. server.on('request', (request, response) => {
  938. switch (request.url) {
  939. case '/301':
  940. response.statusCode = '301'
  941. response.setHeader('Location', '/200')
  942. response.end()
  943. break
  944. case '/200':
  945. response.statusCode = '200'
  946. response.end()
  947. break
  948. default:
  949. handleUnexpectedURL(request, response)
  950. }
  951. })
  952. const urlRequest = net.request({
  953. url: `${server.url}${requestUrl}`
  954. })
  955. urlRequest.on('response', (response) => {
  956. assert.strictEqual(response.statusCode, 200)
  957. done()
  958. })
  959. urlRequest.end()
  960. })
  961. it('should follow redirect chain when no redirect mode is provided', (done) => {
  962. const requestUrl = '/redirectChain'
  963. server.on('request', (request, response) => {
  964. switch (request.url) {
  965. case '/redirectChain':
  966. response.statusCode = '301'
  967. response.setHeader('Location', '/301')
  968. response.end()
  969. break
  970. case '/301':
  971. response.statusCode = '301'
  972. response.setHeader('Location', '/200')
  973. response.end()
  974. break
  975. case '/200':
  976. response.statusCode = '200'
  977. response.end()
  978. break
  979. default:
  980. handleUnexpectedURL(request, response)
  981. }
  982. })
  983. const urlRequest = net.request({
  984. url: `${server.url}${requestUrl}`
  985. })
  986. urlRequest.on('response', (response) => {
  987. assert.strictEqual(response.statusCode, 200)
  988. done()
  989. })
  990. urlRequest.end()
  991. })
  992. it('should not follow redirect when mode is error', (done) => {
  993. const requestUrl = '/301'
  994. server.on('request', (request, response) => {
  995. switch (request.url) {
  996. case '/301':
  997. response.statusCode = '301'
  998. response.setHeader('Location', '/200')
  999. response.end()
  1000. break
  1001. case '/200':
  1002. response.statusCode = '200'
  1003. response.end()
  1004. break
  1005. default:
  1006. handleUnexpectedURL(request, response)
  1007. }
  1008. })
  1009. const urlRequest = net.request({
  1010. url: `${server.url}${requestUrl}`,
  1011. redirect: 'error'
  1012. })
  1013. urlRequest.on('error', (error) => {
  1014. assert.strictEqual(error.message, 'Request cannot follow redirect with the current redirect mode')
  1015. })
  1016. urlRequest.on('close', () => {
  1017. done()
  1018. })
  1019. urlRequest.end()
  1020. })
  1021. it('should allow follow redirect when mode is manual', (done) => {
  1022. const requestUrl = '/redirectChain'
  1023. let redirectCount = 0
  1024. server.on('request', (request, response) => {
  1025. switch (request.url) {
  1026. case '/redirectChain':
  1027. response.statusCode = '301'
  1028. response.setHeader('Location', '/301')
  1029. response.end()
  1030. break
  1031. case '/301':
  1032. response.statusCode = '301'
  1033. response.setHeader('Location', '/200')
  1034. response.end()
  1035. break
  1036. case '/200':
  1037. response.statusCode = '200'
  1038. response.end()
  1039. break
  1040. default:
  1041. handleUnexpectedURL(request, response)
  1042. }
  1043. })
  1044. const urlRequest = net.request({
  1045. url: `${server.url}${requestUrl}`,
  1046. redirect: 'manual'
  1047. })
  1048. urlRequest.on('response', (response) => {
  1049. assert.strictEqual(response.statusCode, 200)
  1050. assert.strictEqual(redirectCount, 2)
  1051. done()
  1052. })
  1053. urlRequest.on('redirect', (status, method, url) => {
  1054. if (url === `${server.url}/301` || url === `${server.url}/200`) {
  1055. redirectCount += 1
  1056. urlRequest.followRedirect()
  1057. }
  1058. })
  1059. urlRequest.end()
  1060. })
  1061. it('should allow cancelling redirect when mode is manual', (done) => {
  1062. const requestUrl = '/redirectChain'
  1063. let redirectCount = 0
  1064. server.on('request', (request, response) => {
  1065. switch (request.url) {
  1066. case '/redirectChain':
  1067. response.statusCode = '301'
  1068. response.setHeader('Location', '/redirect/1')
  1069. response.end()
  1070. break
  1071. case '/redirect/1':
  1072. response.statusCode = '200'
  1073. response.setHeader('Location', '/redirect/2')
  1074. response.end()
  1075. break
  1076. case '/redirect/2':
  1077. response.statusCode = '200'
  1078. response.end()
  1079. break
  1080. default:
  1081. handleUnexpectedURL(request, response)
  1082. }
  1083. })
  1084. const urlRequest = net.request({
  1085. url: `${server.url}${requestUrl}`,
  1086. redirect: 'manual'
  1087. })
  1088. urlRequest.on('response', (response) => {
  1089. assert.strictEqual(response.statusCode, 200)
  1090. response.pause()
  1091. response.on('data', (chunk) => {
  1092. })
  1093. response.on('end', () => {
  1094. urlRequest.abort()
  1095. })
  1096. response.resume()
  1097. })
  1098. urlRequest.on('close', () => {
  1099. assert.strictEqual(redirectCount, 1)
  1100. done()
  1101. })
  1102. urlRequest.on('redirect', (status, method, url) => {
  1103. if (url === `${server.url}/redirect/1`) {
  1104. redirectCount += 1
  1105. urlRequest.followRedirect()
  1106. }
  1107. })
  1108. urlRequest.end()
  1109. })
  1110. it('should throw if given an invalid session option', (done) => {
  1111. const requestUrl = '/requestUrl'
  1112. try {
  1113. const urlRequest = net.request({
  1114. url: `${server.url}${requestUrl}`,
  1115. session: 1
  1116. })
  1117. // eslint-disable-next-line
  1118. urlRequest
  1119. } catch (exception) {
  1120. done()
  1121. }
  1122. })
  1123. it('should to able to create and intercept a request using a custom partition name', (done) => {
  1124. const requestUrl = '/requestUrl'
  1125. const redirectUrl = '/redirectUrl'
  1126. const customPartitionName = 'custom-partition'
  1127. let requestIsRedirected = false
  1128. server.on('request', (request, response) => {
  1129. switch (request.url) {
  1130. case redirectUrl:
  1131. requestIsRedirected = true
  1132. response.end()
  1133. break
  1134. default:
  1135. handleUnexpectedURL(request, response)
  1136. }
  1137. })
  1138. session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
  1139. assert.fail('Request should not be intercepted by the default session')
  1140. })
  1141. const customSession = session.fromPartition(customPartitionName, {
  1142. cache: false
  1143. })
  1144. let requestIsIntercepted = false
  1145. customSession.webRequest.onBeforeRequest((details, callback) => {
  1146. if (details.url === `${server.url}${requestUrl}`) {
  1147. requestIsIntercepted = true
  1148. callback({
  1149. redirectURL: `${server.url}${redirectUrl}`
  1150. })
  1151. } else {
  1152. callback({
  1153. cancel: false
  1154. })
  1155. }
  1156. })
  1157. const urlRequest = net.request({
  1158. url: `${server.url}${requestUrl}`,
  1159. partition: customPartitionName
  1160. })
  1161. urlRequest.on('response', (response) => {
  1162. assert.strictEqual(response.statusCode, 200)
  1163. response.pause()
  1164. response.on('data', (chunk) => {
  1165. })
  1166. response.on('end', () => {
  1167. assert(requestIsRedirected, 'The server should receive a request to the forward URL')
  1168. assert(requestIsIntercepted, 'The request should be intercepted by the webRequest module')
  1169. done()
  1170. })
  1171. response.resume()
  1172. })
  1173. urlRequest.end()
  1174. })
  1175. it('should throw if given an invalid partition option', (done) => {
  1176. const requestUrl = '/requestUrl'
  1177. try {
  1178. const urlRequest = net.request({
  1179. url: `${server.url}${requestUrl}`,
  1180. partition: 1
  1181. })
  1182. // eslint-disable-next-line
  1183. urlRequest
  1184. } catch (exception) {
  1185. done()
  1186. }
  1187. })
  1188. it('should be able to create a request with options', (done) => {
  1189. const requestUrl = '/'
  1190. const customHeaderName = 'Some-Custom-Header-Name'
  1191. const customHeaderValue = 'Some-Customer-Header-Value'
  1192. server.on('request', (request, response) => {
  1193. switch (request.url) {
  1194. case requestUrl:
  1195. assert.strictEqual(request.method, 'GET')
  1196. assert.strictEqual(request.headers[customHeaderName.toLowerCase()],
  1197. customHeaderValue)
  1198. response.statusCode = 200
  1199. response.statusMessage = 'OK'
  1200. response.end()
  1201. break
  1202. default:
  1203. handleUnexpectedURL(request, response)
  1204. }
  1205. })
  1206. const serverUrl = url.parse(server.url)
  1207. const options = {
  1208. port: serverUrl.port,
  1209. hostname: '127.0.0.1',
  1210. headers: {}
  1211. }
  1212. options.headers[customHeaderName] = customHeaderValue
  1213. const urlRequest = net.request(options)
  1214. urlRequest.on('response', (response) => {
  1215. assert.strictEqual(response.statusCode, 200)
  1216. response.pause()
  1217. response.on('data', (chunk) => {
  1218. })
  1219. response.on('end', () => {
  1220. done()
  1221. })
  1222. response.resume()
  1223. })
  1224. urlRequest.end()
  1225. })
  1226. it('should be able to pipe a readable stream into a net request', (done) => {
  1227. const nodeRequestUrl = '/nodeRequestUrl'
  1228. const netRequestUrl = '/netRequestUrl'
  1229. const bodyData = randomString(kOneMegaByte)
  1230. let netRequestReceived = false
  1231. let netRequestEnded = false
  1232. server.on('request', (request, response) => {
  1233. switch (request.url) {
  1234. case nodeRequestUrl:
  1235. response.write(bodyData)
  1236. response.end()
  1237. break
  1238. case netRequestUrl:
  1239. netRequestReceived = true
  1240. let receivedBodyData = ''
  1241. request.on('data', (chunk) => {
  1242. receivedBodyData += chunk.toString()
  1243. })
  1244. request.on('end', (chunk) => {
  1245. netRequestEnded = true
  1246. if (chunk) {
  1247. receivedBodyData += chunk.toString()
  1248. }
  1249. assert.strictEqual(receivedBodyData, bodyData)
  1250. response.end()
  1251. })
  1252. break
  1253. default:
  1254. handleUnexpectedURL(request, response)
  1255. }
  1256. })
  1257. const nodeRequest = http.request(`${server.url}${nodeRequestUrl}`)
  1258. nodeRequest.on('response', (nodeResponse) => {
  1259. const netRequest = net.request(`${server.url}${netRequestUrl}`)
  1260. netRequest.on('response', (netResponse) => {
  1261. assert.strictEqual(netResponse.statusCode, 200)
  1262. netResponse.pause()
  1263. netResponse.on('data', (chunk) => {})
  1264. netResponse.on('end', () => {
  1265. assert(netRequestReceived)
  1266. assert(netRequestEnded)
  1267. done()
  1268. })
  1269. netResponse.resume()
  1270. })
  1271. nodeResponse.pipe(netRequest)
  1272. })
  1273. nodeRequest.end()
  1274. })
  1275. it('should emit error event on server socket close', (done) => {
  1276. const requestUrl = '/requestUrl'
  1277. server.on('request', (request, response) => {
  1278. switch (request.url) {
  1279. case requestUrl:
  1280. request.socket.destroy()
  1281. break
  1282. default:
  1283. handleUnexpectedURL(request, response)
  1284. }
  1285. })
  1286. let requestErrorEventEmitted = false
  1287. const urlRequest = net.request(`${server.url}${requestUrl}`)
  1288. urlRequest.on('error', (error) => {
  1289. assert(error)
  1290. requestErrorEventEmitted = true
  1291. })
  1292. urlRequest.on('close', () => {
  1293. assert(requestErrorEventEmitted)
  1294. done()
  1295. })
  1296. urlRequest.end()
  1297. })
  1298. })
  1299. describe('IncomingMessage API', () => {
  1300. it('response object should implement the IncomingMessage API', (done) => {
  1301. const requestUrl = '/requestUrl'
  1302. const customHeaderName = 'Some-Custom-Header-Name'
  1303. const customHeaderValue = 'Some-Customer-Header-Value'
  1304. server.on('request', (request, response) => {
  1305. switch (request.url) {
  1306. case requestUrl:
  1307. response.statusCode = 200
  1308. response.statusMessage = 'OK'
  1309. response.setHeader(customHeaderName, customHeaderValue)
  1310. response.end()
  1311. break
  1312. default:
  1313. handleUnexpectedURL(request, response)
  1314. }
  1315. })
  1316. const urlRequest = net.request({
  1317. method: 'GET',
  1318. url: `${server.url}${requestUrl}`
  1319. })
  1320. urlRequest.on('response', (response) => {
  1321. const statusCode = response.statusCode
  1322. assert(typeof statusCode === 'number')
  1323. assert.strictEqual(statusCode, 200)
  1324. const statusMessage = response.statusMessage
  1325. assert(typeof statusMessage === 'string')
  1326. assert.strictEqual(statusMessage, 'OK')
  1327. const headers = response.headers
  1328. assert(typeof headers === 'object')
  1329. assert.deepStrictEqual(headers[customHeaderName.toLowerCase()],
  1330. [customHeaderValue])
  1331. const httpVersion = response.httpVersion
  1332. assert(typeof httpVersion === 'string')
  1333. assert(httpVersion.length > 0)
  1334. const httpVersionMajor = response.httpVersionMajor
  1335. assert(typeof httpVersionMajor === 'number')
  1336. assert(httpVersionMajor >= 1)
  1337. const httpVersionMinor = response.httpVersionMinor
  1338. assert(typeof httpVersionMinor === 'number')
  1339. assert(httpVersionMinor >= 0)
  1340. response.pause()
  1341. response.on('data', (chunk) => {
  1342. })
  1343. response.on('end', () => {
  1344. done()
  1345. })
  1346. response.resume()
  1347. })
  1348. urlRequest.end()
  1349. })
  1350. it('should be able to pipe a net response into a writable stream', (done) => {
  1351. const nodeRequestUrl = '/nodeRequestUrl'
  1352. const netRequestUrl = '/netRequestUrl'
  1353. const bodyData = randomString(kOneMegaByte)
  1354. server.on('request', (request, response) => {
  1355. switch (request.url) {
  1356. case netRequestUrl:
  1357. response.statusCode = 200
  1358. response.statusMessage = 'OK'
  1359. response.write(bodyData)
  1360. response.end()
  1361. break
  1362. case nodeRequestUrl:
  1363. let receivedBodyData = ''
  1364. request.on('data', (chunk) => {
  1365. receivedBodyData += chunk.toString()
  1366. })
  1367. request.on('end', (chunk) => {
  1368. if (chunk) {
  1369. receivedBodyData += chunk.toString()
  1370. }
  1371. assert.strictEqual(receivedBodyData, bodyData)
  1372. response.end()
  1373. })
  1374. break
  1375. default:
  1376. handleUnexpectedURL(request, response)
  1377. }
  1378. })
  1379. ipcRenderer.once('api-net-spec-done', () => {
  1380. done()
  1381. })
  1382. // Execute below code directly within the browser context without
  1383. // using the remote module.
  1384. ipcRenderer.send('eval', `
  1385. const {net} = require('electron')
  1386. const http = require('http')
  1387. const url = require('url')
  1388. const netRequest = net.request('${server.url}${netRequestUrl}')
  1389. netRequest.on('response', function (netResponse) {
  1390. const serverUrl = url.parse('${server.url}')
  1391. const nodeOptions = {
  1392. method: 'POST',
  1393. path: '${nodeRequestUrl}',
  1394. port: serverUrl.port
  1395. }
  1396. let nodeRequest = http.request(nodeOptions)
  1397. nodeRequest.on('response', function (nodeResponse) {
  1398. nodeResponse.on('data', (chunk) => {
  1399. })
  1400. nodeResponse.on('end', (chunk) => {
  1401. event.sender.send('api-net-spec-done')
  1402. })
  1403. })
  1404. netResponse.pipe(nodeRequest)
  1405. })
  1406. netRequest.end()
  1407. `)
  1408. })
  1409. it('should not emit any event after close', (done) => {
  1410. const requestUrl = '/requestUrl'
  1411. const bodyData = randomString(kOneKiloByte)
  1412. server.on('request', (request, response) => {
  1413. switch (request.url) {
  1414. case requestUrl:
  1415. response.statusCode = 200
  1416. response.statusMessage = 'OK'
  1417. response.write(bodyData)
  1418. response.end()
  1419. break
  1420. default:
  1421. handleUnexpectedURL(request, response)
  1422. }
  1423. })
  1424. let requestCloseEventEmitted = false
  1425. const urlRequest = net.request({
  1426. method: 'GET',
  1427. url: `${server.url}${requestUrl}`
  1428. })
  1429. urlRequest.on('response', (response) => {
  1430. assert(!requestCloseEventEmitted)
  1431. const statusCode = response.statusCode
  1432. assert.strictEqual(statusCode, 200)
  1433. response.pause()
  1434. response.on('data', () => {
  1435. })
  1436. response.on('end', () => {
  1437. })
  1438. response.resume()
  1439. response.on('error', () => {
  1440. assert(!requestCloseEventEmitted)
  1441. })
  1442. response.on('aborted', () => {
  1443. assert(!requestCloseEventEmitted)
  1444. })
  1445. })
  1446. urlRequest.on('finish', () => {
  1447. assert(!requestCloseEventEmitted)
  1448. })
  1449. urlRequest.on('error', () => {
  1450. assert(!requestCloseEventEmitted)
  1451. })
  1452. urlRequest.on('abort', () => {
  1453. assert(!requestCloseEventEmitted)
  1454. })
  1455. urlRequest.on('close', () => {
  1456. requestCloseEventEmitted = true
  1457. // Wait so that all async events get scheduled.
  1458. setTimeout(() => {
  1459. done()
  1460. }, 100)
  1461. })
  1462. urlRequest.end()
  1463. })
  1464. })
  1465. describe('Stability and performance', () => {
  1466. it('should free unreferenced, never-started request objects without crash', (done) => {
  1467. const requestUrl = '/requestUrl'
  1468. ipcRenderer.once('api-net-spec-done', () => {
  1469. done()
  1470. })
  1471. ipcRenderer.send('eval', `
  1472. const {net} = require('electron')
  1473. const urlRequest = net.request('${server.url}${requestUrl}')
  1474. process.nextTick(() => {
  1475. const v8Util = process.atomBinding('v8_util')
  1476. v8Util.requestGarbageCollectionForTesting()
  1477. event.sender.send('api-net-spec-done')
  1478. })
  1479. `)
  1480. })
  1481. it('should not collect on-going requests without crash', (done) => {
  1482. const requestUrl = '/requestUrl'
  1483. server.on('request', (request, response) => {
  1484. switch (request.url) {
  1485. case requestUrl:
  1486. response.statusCode = 200
  1487. response.statusMessage = 'OK'
  1488. response.write(randomString(kOneKiloByte))
  1489. ipcRenderer.once('api-net-spec-resume', () => {
  1490. response.write(randomString(kOneKiloByte))
  1491. response.end()
  1492. })
  1493. break
  1494. default:
  1495. handleUnexpectedURL(request, response)
  1496. }
  1497. })
  1498. ipcRenderer.once('api-net-spec-done', () => {
  1499. done()
  1500. })
  1501. // Execute below code directly within the browser context without
  1502. // using the remote module.
  1503. ipcRenderer.send('eval', `
  1504. const {net} = require('electron')
  1505. const urlRequest = net.request('${server.url}${requestUrl}')
  1506. urlRequest.on('response', (response) => {
  1507. response.on('data', () => {
  1508. })
  1509. response.on('end', () => {
  1510. event.sender.send('api-net-spec-done')
  1511. })
  1512. process.nextTick(() => {
  1513. // Trigger a garbage collection.
  1514. const v8Util = process.atomBinding('v8_util')
  1515. v8Util.requestGarbageCollectionForTesting()
  1516. event.sender.send('api-net-spec-resume')
  1517. })
  1518. })
  1519. urlRequest.end()
  1520. `)
  1521. })
  1522. it('should collect unreferenced, ended requests without crash', (done) => {
  1523. const requestUrl = '/requestUrl'
  1524. server.on('request', (request, response) => {
  1525. switch (request.url) {
  1526. case requestUrl:
  1527. response.statusCode = 200
  1528. response.statusMessage = 'OK'
  1529. response.end()
  1530. break
  1531. default:
  1532. handleUnexpectedURL(request, response)
  1533. }
  1534. })
  1535. ipcRenderer.once('api-net-spec-done', () => {
  1536. done()
  1537. })
  1538. ipcRenderer.send('eval', `
  1539. const {net} = require('electron')
  1540. const urlRequest = net.request('${server.url}${requestUrl}')
  1541. urlRequest.on('response', (response) => {
  1542. response.on('data', () => {
  1543. })
  1544. response.on('end', () => {
  1545. })
  1546. })
  1547. urlRequest.on('close', () => {
  1548. process.nextTick(() => {
  1549. const v8Util = process.atomBinding('v8_util')
  1550. v8Util.requestGarbageCollectionForTesting()
  1551. event.sender.send('api-net-spec-done')
  1552. })
  1553. })
  1554. urlRequest.end()
  1555. `)
  1556. })
  1557. })
  1558. })
  1559. function handleUnexpectedURL (request, response) {
  1560. response.statusCode = '500'
  1561. response.end()
  1562. assert.fail(`Unexpected URL: ${request.url}`)
  1563. }