index.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /**
  2. * jsproxy cfworker api
  3. *
  4. * @update: 2019-05-07
  5. * @author: EtherDream
  6. * @see: https://github.com/EtherDream/jsproxy/
  7. */
  8. 'use strict'
  9. const PREFLIGHT_INIT = {
  10. status: 204,
  11. headers: new Headers({
  12. 'access-control-allow-origin': '*',
  13. 'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS',
  14. 'access-control-allow-headers': '--raw-info,--level,--url,--referer,--cookie,--origin,--ext,--aceh,--ver,--type,--mode,accept,accept-charset,accept-encoding,accept-language,accept-datetime,authorization,cache-control,content-length,content-type,date,if-match,if-modified-since,if-none-match,if-range,if-unmodified-since,max-forwards,pragma,range,te,upgrade,upgrade-insecure-requests,x-requested-with,chrome-proxy',
  15. 'access-control-max-age': '1728000',
  16. }),
  17. }
  18. const pairs = Object.entries
  19. addEventListener('fetch', e => {
  20. const ret = handler(e.request)
  21. .catch(err => new Response(err))
  22. e.respondWith(ret)
  23. })
  24. /**
  25. * @param {Request} req
  26. */
  27. async function handler(req) {
  28. const reqHdrRaw = req.headers
  29. if (reqHdrRaw.has('x-jsproxy')) {
  30. return Response.error()
  31. }
  32. // preflight
  33. if (req.method === 'OPTIONS' &&
  34. reqHdrRaw.has('access-control-request-headers')
  35. ) {
  36. return new Response(null, PREFLIGHT_INIT)
  37. }
  38. let url = ''
  39. let extHdrs = null
  40. let acehOld = false
  41. let rawSvr = ''
  42. let rawLen = ''
  43. let rawEtag = ''
  44. const reqHdrNew = new Headers(reqHdrRaw)
  45. reqHdrNew.set('x-jsproxy', '1')
  46. for (const [k, v] of reqHdrRaw.entries()) {
  47. if (!k.startsWith('--')) {
  48. continue
  49. }
  50. reqHdrNew.delete(k)
  51. const k2 = k.substr(2)
  52. switch (k2) {
  53. case 'url':
  54. url = v
  55. break
  56. case 'aceh':
  57. acehOld = true
  58. break
  59. case 'raw-info':
  60. // TODO: ,,
  61. [rawSvr, rawLen, rawEtag] = v.split(/,{1,2}/)
  62. break
  63. case 'level':
  64. case 'mode':
  65. case 'type':
  66. break
  67. case 'ext':
  68. extHdrs = JSON.parse(v)
  69. break
  70. default:
  71. if (v) {
  72. reqHdrNew.set(k2, v)
  73. } else {
  74. reqHdrNew.delete(k2)
  75. }
  76. break
  77. }
  78. }
  79. if (extHdrs) {
  80. for (const [k, v] of pairs(extHdrs)) {
  81. reqHdrNew.set(k, v)
  82. }
  83. }
  84. // proxy
  85. const res = await fetch(url, {
  86. method: req.method,
  87. headers: reqHdrNew,
  88. })
  89. // header filter
  90. const resHdrOld = res.headers
  91. const resHdrNew = new Headers(resHdrOld)
  92. let expose = '*'
  93. let vary = '--url'
  94. for (const [k, v] of resHdrOld.entries()) {
  95. if (k === 'access-control-allow-origin' ||
  96. k === 'access-control-expose-headers' ||
  97. k === 'location' ||
  98. k === 'set-cookie'
  99. ) {
  100. const x = '--' + k
  101. resHdrNew.set(x, v)
  102. if (acehOld) {
  103. expose = expose + ',' + x
  104. }
  105. resHdrNew.delete(k)
  106. }
  107. else if (k === 'vary') {
  108. vary = vary + ',' + v
  109. }
  110. else if (acehOld &&
  111. k !== 'cache-control' &&
  112. k !== 'content-language' &&
  113. k !== 'content-type' &&
  114. k !== 'expires' &&
  115. k !== 'last-modified' &&
  116. k !== 'pragma'
  117. ) {
  118. expose = expose + ',' + k
  119. }
  120. }
  121. if (acehOld) {
  122. expose = expose + ',--s'
  123. resHdrNew.set('--t', '1')
  124. }
  125. resHdrNew.set('access-control-expose-headers', expose)
  126. resHdrNew.set('access-control-allow-origin', '*')
  127. resHdrNew.set('vary', vary)
  128. resHdrNew.set('--s', res.status)
  129. // verify
  130. const newLen = resHdrOld.get('content-length') || ''
  131. const newEtag = resHdrOld.get('etag') || ''
  132. const badLen = (rawLen !== newLen)
  133. const badEtag = (rawEtag && rawEtag !== newEtag)
  134. // resHdrNew.set('--l', rawLen + ',' + newLen)
  135. // resHdrNew.set('--e', rawEtag + ',' + newEtag)
  136. let status = 200
  137. let body = res.body
  138. if (badLen) {
  139. status = 400
  140. body = `bad len (old: ${rawLen} new: ${newLen})`
  141. resHdrNew.set('cache-control', 'no-cache')
  142. }
  143. // else if (badEtag) {
  144. // status = 400
  145. // body = `bad etag (old: ${rawEtag} new: ${newEtag})`
  146. // }
  147. return new Response(body, {
  148. status,
  149. headers: resHdrNew,
  150. })
  151. }