|
|
@ -4,6 +4,15 @@ const zlib = require('zlib')
|
|
|
|
const url = require('url')
|
|
|
|
const url = require('url')
|
|
|
|
|
|
|
|
|
|
|
|
var httpUtil = {}
|
|
|
|
var httpUtil = {}
|
|
|
|
|
|
|
|
httpUtil.getCharset = function (res) {
|
|
|
|
|
|
|
|
const contentType = res.getHeader('content-type')
|
|
|
|
|
|
|
|
const reg = /charset=(.*)/
|
|
|
|
|
|
|
|
const matched = contentType.match(reg)
|
|
|
|
|
|
|
|
if (matched) {
|
|
|
|
|
|
|
|
return matched[1]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'utf-8'
|
|
|
|
|
|
|
|
}
|
|
|
|
httpUtil.isGzip = function (res) {
|
|
|
|
httpUtil.isGzip = function (res) {
|
|
|
|
var contentEncoding = res.headers['content-encoding']
|
|
|
|
var contentEncoding = res.headers['content-encoding']
|
|
|
|
return !!(contentEncoding && contentEncoding.toLowerCase() === 'gzip')
|
|
|
|
return !!(contentEncoding && contentEncoding.toLowerCase() === 'gzip')
|
|
|
@ -12,34 +21,63 @@ httpUtil.isHtml = function (res) {
|
|
|
|
var contentType = res.headers['content-type']
|
|
|
|
var contentType = res.headers['content-type']
|
|
|
|
return (typeof contentType !== 'undefined') && /text\/html|application\/xhtml\+xml/.test(contentType)
|
|
|
|
return (typeof contentType !== 'undefined') && /text\/html|application\/xhtml\+xml/.test(contentType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const HEAD = Buffer.from('</head>')
|
|
|
|
|
|
|
|
const HEAD_UP = Buffer.from('</HEAD>')
|
|
|
|
|
|
|
|
const BODY = Buffer.from('</body>')
|
|
|
|
|
|
|
|
const BODY_UP = Buffer.from('</BODY>')
|
|
|
|
|
|
|
|
|
|
|
|
function injectScriptIntoHeadHtml (html, script) {
|
|
|
|
function chunkByteReplace (_this, chunk, enc, callback, append) {
|
|
|
|
html = html.replace(/(<\/head>)/i, function (match) {
|
|
|
|
|
|
|
|
return script + match
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
return html
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function injectScriptIntoBodyHtml (html, script) {
|
|
|
|
|
|
|
|
html = html.replace(/(<\/body>)/i, function (match) {
|
|
|
|
|
|
|
|
return script + match
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
return html
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function chunkReplace (_this, chunk, enc, callback, append) {
|
|
|
|
|
|
|
|
let chunkString = chunk.toString()
|
|
|
|
|
|
|
|
if (append && append.head) {
|
|
|
|
if (append && append.head) {
|
|
|
|
chunkString = injectScriptIntoHeadHtml(chunkString, append.head)
|
|
|
|
const ret = injectScriptIntoHtml([HEAD, HEAD_UP], chunk, append.head)
|
|
|
|
|
|
|
|
if (ret != null) {
|
|
|
|
|
|
|
|
chunk = ret
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (append && append.body) {
|
|
|
|
if (append && append.body) {
|
|
|
|
chunkString = injectScriptIntoBodyHtml(chunkString, append.body)
|
|
|
|
const ret = injectScriptIntoHtml([BODY, BODY_UP], chunk, append.body)
|
|
|
|
|
|
|
|
if (ret != null) {
|
|
|
|
|
|
|
|
chunk = ret
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_this.push(Buffer.from(chunkString))
|
|
|
|
_this.push(chunk)
|
|
|
|
callback()
|
|
|
|
callback()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function injectScriptIntoHtml (tags, chunk, script) {
|
|
|
|
|
|
|
|
for (const tag of tags) {
|
|
|
|
|
|
|
|
const index = chunk.indexOf(tag)
|
|
|
|
|
|
|
|
if (index < 0) {
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const scriptBuf = Buffer.from(script)
|
|
|
|
|
|
|
|
const chunkNew = Buffer.alloc(chunk.length + scriptBuf.length)
|
|
|
|
|
|
|
|
chunk.copy(chunkNew, 0, 0, index)
|
|
|
|
|
|
|
|
scriptBuf.copy(chunkNew, index, 0)
|
|
|
|
|
|
|
|
chunk.copy(chunkNew, index + scriptBuf.length, index)
|
|
|
|
|
|
|
|
return chunkNew
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const contextPath = '/____ds_script____/'
|
|
|
|
|
|
|
|
const monkey = require('../../monkey')
|
|
|
|
module.exports = {
|
|
|
|
module.exports = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requestIntercept (context, req, res, ssl, next) {
|
|
|
|
|
|
|
|
const { rOptions, log } = context
|
|
|
|
|
|
|
|
if (rOptions.path.indexOf(contextPath) !== 0) {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const urlPath = rOptions.path
|
|
|
|
|
|
|
|
const filename = urlPath.replace(contextPath, '')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const script = monkey.get()[filename]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info('ds_script', filename, script != null)
|
|
|
|
|
|
|
|
res.writeHead(200)
|
|
|
|
|
|
|
|
res.write(script.script)
|
|
|
|
|
|
|
|
res.end()
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
},
|
|
|
|
responseInterceptor (req, res, proxyReq, proxyRes, ssl, next, append) {
|
|
|
|
responseInterceptor (req, res, proxyReq, proxyRes, ssl, next, append) {
|
|
|
|
if (!append.head && !append.body) {
|
|
|
|
if (!append.head && !append.body) {
|
|
|
|
next()
|
|
|
|
next()
|
|
|
@ -52,6 +90,7 @@ module.exports = {
|
|
|
|
})()
|
|
|
|
})()
|
|
|
|
if (!isHtml || contentLengthIsZero) {
|
|
|
|
if (!isHtml || contentLengthIsZero) {
|
|
|
|
next()
|
|
|
|
next()
|
|
|
|
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Object.keys(proxyRes.headers).forEach(function (key) {
|
|
|
|
Object.keys(proxyRes.headers).forEach(function (key) {
|
|
|
|
if (proxyRes.headers[key] !== undefined) {
|
|
|
|
if (proxyRes.headers[key] !== undefined) {
|
|
|
@ -88,11 +127,11 @@ module.exports = {
|
|
|
|
if (isGzip) {
|
|
|
|
if (isGzip) {
|
|
|
|
proxyRes.pipe(new zlib.Gunzip())
|
|
|
|
proxyRes.pipe(new zlib.Gunzip())
|
|
|
|
.pipe(through(function (chunk, enc, callback) {
|
|
|
|
.pipe(through(function (chunk, enc, callback) {
|
|
|
|
chunkReplace(this, chunk, enc, callback, append)
|
|
|
|
chunkByteReplace(this, chunk, enc, callback, append)
|
|
|
|
})).pipe(new zlib.Gzip()).pipe(res)
|
|
|
|
})).pipe(new zlib.Gzip()).pipe(res)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
proxyRes.pipe(through(function (chunk, enc, callback) {
|
|
|
|
proxyRes.pipe(through(function (chunk, enc, callback) {
|
|
|
|
chunkReplace(this, chunk, enc, callback, append)
|
|
|
|
chunkByteReplace(this, chunk, enc, callback, append)
|
|
|
|
})).pipe(res)
|
|
|
|
})).pipe(res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|