mitm.html 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <!-- 中间层页面,专门用于下载,在此页面中注册 serviceWorker,并实现拦截 -->
  2. <!-- 本页面存在的意义,在于隔离 web 主进程的请求,让 serviceWorker 仅拦截本页面的请求 -->
  3. <!-- serviceWorker 完全异步,全程 API 均使用 Promise 完成异步操作 -->
  4. <script>
  5. // 保活,每 10s 执行一次 ping,避免 serviceWorker 被删除
  6. let keepAlive = () => {
  7. keepAlive = () => { }
  8. var ping = location.href.substr(0, location.href.lastIndexOf('/')) + '/ping'
  9. var interval = setInterval(() => {
  10. if (serviceWorker) {
  11. serviceWorker.postMessage('ping')
  12. } else {
  13. fetch(ping).then(res => res.text(!res.ok && clearInterval(interval)))
  14. }
  15. }, 10000)
  16. }
  17. let scope = '' // 当前 serviceWorker 所在域,是其唯一标识符
  18. let serviceWorker = null // serviceWorker 实例
  19. let tempMessageStore = [] // 在处理函数未准备好之前,临时存储的 message
  20. // 进入页面马上进行消息监听,再后续处理函数 ready 后,再触发这些 message 的处理
  21. window.onmessage = evt => tempMessageStore.push(evt)
  22. // 注册 serviceWorker,检测是否有旧的实例,进行复用。
  23. function registerWorker() {
  24. // 获取 ./ 域下已经注册过的 serviceWorker,getRegistration 返回单个,getRegistrations 返回所有
  25. return navigator.serviceWorker.getRegistration('./')
  26. .then(serviceWorkerRegistration => {
  27. // 如果已经存在注册过的 serviceWorkerRegistration,则直接返回,否则产生新的一个
  28. return serviceWorkerRegistration || navigator.serviceWorker.register('serviceWorker.js', { scope: './' })
  29. }).then(serviceWorkerRegistration => {
  30. scope = serviceWorkerRegistration.scope // 保存所在域
  31. // 如果注册已就绪,则直接赋值并返回
  32. if (serviceWorkerRegistration.active) {
  33. serviceWorker = serviceWorkerRegistration.active
  34. return
  35. }
  36. // 如果处于注册中,返回 promise,并监听其状态变更,等待其就绪状态
  37. const swRegTmp = serviceWorkerRegistration.installing || serviceWorkerRegistration.waiting
  38. return new Promise(resolve => {
  39. const onStatechange = () => {
  40. if (swRegTmp.state === 'activated') {
  41. swRegTmp.removeEventListener('statechange', onStatechange)
  42. serviceWorker = serviceWorkerRegistration.active
  43. resolve()
  44. }
  45. }
  46. swRegTmp.addEventListener('statechange', onStatechange)
  47. })
  48. })
  49. }
  50. // 消息监听,监听 web 主进程发送过来的消息,并进行数据中转,及数据的处理与转译
  51. function onMessage(event) {
  52. let {
  53. data, // 数据
  54. ports, // channel 所在渠道
  55. origin // 消息作用域
  56. } = event
  57. console.log('onMessage', event)
  58. // 检测消息通道
  59. if (!ports || !ports.length) {
  60. throw new TypeError("[StreamSaver] You didn't send a messageChannel")
  61. }
  62. // 检测接受的数据实体
  63. if (typeof data !== 'object') {
  64. throw new TypeError("[StreamSaver] You didn't send a object")
  65. }
  66. // 检查 readableStream
  67. if (data.readableStream) {
  68. console.warn("[StreamSaver] You should send the readableStream in the messageChannel, not throught mitm")
  69. }
  70. // 检查 pathname
  71. if (!data.pathname) {
  72. console.warn("[StreamSaver] Please send `data.pathname` (eg: /pictures/summer.jpg)")
  73. data.pathname = Math.random().toString().slice(-6) + '/' + data.filename
  74. }
  75. /** @since v2.0.0 */
  76. if (!data.headers) {
  77. console.warn("[StreamSaver] pass `data.headers` that you would like to pass along to the service worker\nit should be a 2D array or a key/val object that fetch's Headers api accepts")
  78. } else {
  79. // test if it's correct
  80. // should throw a typeError if not
  81. new Headers(data.headers)
  82. }
  83. // the default public service worker for StreamSaver is shared among others.
  84. // so all download links needs to be prefixed to avoid any other conflict
  85. data.origin = origin
  86. // if we ever (in some feature versoin of streamsaver) would like to
  87. // redirect back to the page of who initiated a http request
  88. data.referrer = data.referrer || document.referrer || origin
  89. // 删除所有前导斜杠
  90. data.pathname = data.pathname.replace(/^\/+/g, '')
  91. // remove protocol
  92. // 去除协议
  93. let org = origin.replace(/(^\w+:|^)\/\//, '')
  94. // 设置绝对路径,以用于下载
  95. data.url = new URL(`${scope + org}/${data.pathname}`).toString()
  96. // 检查路径是否合法
  97. if (!data.url.startsWith(`${scope + org}/`)) {
  98. throw new TypeError('[StreamSaver] bad `data.pathname`')
  99. }
  100. // This sends the message data as well as transferring
  101. // messageChannel.port2 to the service worker. The service worker can
  102. // then use the transferred port to reply via postMessage(), which
  103. // will in turn trigger the onmessage handler on messageChannel.port1.
  104. const transferable = data.readableStream
  105. ? [ports[0], data.readableStream]
  106. : [ports[0]]
  107. if (!(data.readableStream || data.transferringReadable)) {
  108. keepAlive()
  109. }
  110. // 将从 web 主进程接收到的数据,传输给 serviceWorker 接收
  111. return serviceWorker.postMessage(data, transferable)
  112. }
  113. // 消息回调,告知主进程,本页面已准备就绪
  114. if (window.opener) {
  115. window.opener.postMessage('StreamSaver::loadedPopup', '*')
  116. }
  117. // 注册完成,并进行消息处理
  118. if (navigator.serviceWorker) {
  119. registerWorker().then(() => {
  120. window.onmessage = onMessage
  121. tempMessageStore.forEach(window.onmessage) // 将之前临时存储的 message 放入处理函数中执行
  122. })
  123. } else {
  124. keepAlive()
  125. }
  126. </script>