app.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. const http = require('http')
  2. const fsPromises = require('fs').promises
  3. const path = require('path')
  4. const crypto = require('crypto')
  5. const sleep = require('util').promisify(setTimeout)
  6. const argvIndexOfFile = process.argv.indexOf(__filename)
  7. let PORT = parseInt(process.argv[argvIndexOfFile + 1])
  8. if (isNaN(PORT)) {
  9. PORT = 8000
  10. }
  11. let ALLOW_QUERY_STRING_AND_EXTERNAL_LINKS = 0
  12. let SLEEP_TIME = 200
  13. let CACHE_MAX_AGE = 0
  14. let USE_WHITELIST = 0
  15. let INTENSITY = 65
  16. let USE_MINIFIED = 0
  17. function handleCookies(req) {
  18. const cookies = req.headers.cookie
  19. if (!cookies) {
  20. return
  21. }
  22. cookies.split('; ').map((cookie) => {
  23. const [key, value] = cookie.split('=')
  24. if (key != 'instantpage_test') {
  25. return
  26. }
  27. const cookieValueSplit = value.split(',').map((param) => parseInt(param))
  28. ALLOW_QUERY_STRING_AND_EXTERNAL_LINKS = cookieValueSplit[0]
  29. SLEEP_TIME = cookieValueSplit[1]
  30. CACHE_MAX_AGE = cookieValueSplit[2]
  31. USE_WHITELIST = cookieValueSplit[3]
  32. INTENSITY = cookieValueSplit[4]
  33. if (isNaN(INTENSITY)) {
  34. INTENSITY = value.split(',')[4]
  35. }
  36. USE_MINIFIED = cookieValueSplit[5]
  37. })
  38. }
  39. function sha384(data) {
  40. const hash = crypto.createHash('sha384')
  41. hash.update(data)
  42. return hash.digest('base64')
  43. }
  44. async function requestListener(req, res) {
  45. handleCookies(req)
  46. let headers = {
  47. 'Content-Type': 'text/html',
  48. }
  49. let pathString = req.url.substr(1)
  50. let page = parseInt(pathString)
  51. if (pathString == '') {
  52. page = 1
  53. }
  54. let content = ''
  55. const jsContent = await fsPromises.readFile(path.resolve(__dirname, `../${USE_MINIFIED ? 'instantpage.min.js' : 'instantpage.js'}`))
  56. const jsHash = sha384(jsContent)
  57. if (pathString == 'instantpage.js') {
  58. headers['Content-Type'] = 'text/javascript'
  59. content += jsContent
  60. }
  61. else if (!isNaN(page)) {
  62. await sleep(SLEEP_TIME)
  63. if (CACHE_MAX_AGE) {
  64. headers['Cache-Control'] = `max-age=${CACHE_MAX_AGE}`
  65. }
  66. content += await fsPromises.readFile(path.resolve(__dirname, 'header.html'))
  67. if (ALLOW_QUERY_STRING_AND_EXTERNAL_LINKS) {
  68. content = content.replace('<body>', '<body data-instant-allow-query-string data-instant-allow-external-links>')
  69. }
  70. if (USE_WHITELIST) {
  71. content = content.replace('<body', '<body data-instant-whitelist')
  72. }
  73. else if (INTENSITY != 65) {
  74. content = content.replace('<body', `<body data-instant-intensity="${INTENSITY}"`)
  75. }
  76. dataInstantAttribute = !ALLOW_QUERY_STRING_AND_EXTERNAL_LINKS || USE_WHITELIST ? `data-instant` : ``
  77. content = content.replace(':checked_aqsael', ALLOW_QUERY_STRING_AND_EXTERNAL_LINKS ? 'checked' : '')
  78. content = content.replace(':checked_whitelist', USE_WHITELIST ? 'checked' : '')
  79. content = content.replace(':value_sleep', `value="${SLEEP_TIME}"`)
  80. content = content.replace(':value_cacheAge', `value="${CACHE_MAX_AGE}"`)
  81. content = content.replace(':value_intensity', `value="${INTENSITY}"`)
  82. content = content.replace(':checked_minified', USE_MINIFIED ? 'checked' : '')
  83. content += `<h1>Page ${page}</h1>`
  84. for (let i = 1; i <= 3; i++) {
  85. if (page != i) {
  86. content += `<a href="/${i}?${Math.random()}" ${dataInstantAttribute}><span>Page ${i}</span></a>`
  87. }
  88. }
  89. content += `<a href="/${page}?${Math.random()}" target="_blank" ${dataInstantAttribute}><span>Opens in a new tab</span></a>`
  90. content += `<a href="/${page}?${Math.random()}#anchor" ${dataInstantAttribute}><span>Other page anchor</span></a>`
  91. content += `<a href="${req.url}#anchor" id="anchor"><span>Same-page anchor</span></a>`
  92. content += `<a href="/${page}?${Math.random()}" data-no-instant><span>Manually blacklisted link</span></a>`
  93. content += `<a href="/${page}?${Math.random()}"><span>Non-whitelisted link</span></a>`
  94. content += `<a href="https://www.google.com/" ${dataInstantAttribute}><span>External link</span></a>`
  95. content += `<a><span>&lt;a&gt; without <code>href</code></span></a>`
  96. content += `<a href="file:///C:/"><span>file: link</span></a>`
  97. let footer = await fsPromises.readFile(path.resolve(__dirname, 'footer.html'))
  98. footer = footer.toString().replace('__HASH__', jsHash)
  99. content += footer
  100. }
  101. res.writeHead(200, headers)
  102. res.write(content)
  103. res.end()
  104. }
  105. http.createServer(requestListener).listen(PORT)
  106. console.log(`-> Running on http://127.0.0.1:${PORT}/`)