source: trip-planner-front/node_modules/karma/lib/middleware/karma.js@ ceaed42

Last change on this file since ceaed42 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 9.7 KB
RevLine 
[6a3a178]1/**
2 * Karma middleware is responsible for serving:
3 * - client.html (the entrypoint for capturing a browser)
4 * - debug.html
5 * - context.html (the execution context, loaded within an iframe)
6 * - karma.js
7 *
8 * The main part is generating context.html, as it contains:
9 * - generating mappings
10 * - including <script> and <link> tags
11 * - setting propert caching headers
12 */
13
14const url = require('url')
15
16const log = require('../logger').create('middleware:karma')
17const stripHost = require('./strip_host').stripHost
18const common = require('./common')
19
20const VERSION = require('../constants').VERSION
21const SCRIPT_TYPE = {
22 js: 'text/javascript',
23 module: 'module'
24}
25const FILE_TYPES = [
26 'css',
27 'html',
28 'js',
29 'module',
30 'dom'
31]
32
33function filePathToUrlPath (filePath, basePath, urlRoot, proxyPath) {
34 if (filePath.startsWith(basePath)) {
35 return proxyPath + urlRoot.substr(1) + 'base' + filePath.substr(basePath.length)
36 }
37 return proxyPath + urlRoot.substr(1) + 'absolute' + filePath
38}
39
40function getQuery (urlStr) {
41 // eslint-disable-next-line node/no-deprecated-api
42 return url.parse(urlStr, true).query || {}
43}
44
45function getXUACompatibleMetaElement (url) {
46 const query = getQuery(url)
47 if (query['x-ua-compatible']) {
48 return `\n<meta http-equiv="X-UA-Compatible" content="${query['x-ua-compatible']}"/>`
49 }
50 return ''
51}
52
53function getXUACompatibleUrl (url) {
54 const query = getQuery(url)
55 if (query['x-ua-compatible']) {
56 return '?x-ua-compatible=' + encodeURIComponent(query['x-ua-compatible'])
57 }
58 return ''
59}
60
61function createKarmaMiddleware (
62 filesPromise,
63 serveStaticFile,
64 serveFile,
65 injector,
66 basePath,
67 urlRoot,
68 upstreamProxy,
69 browserSocketTimeout
70) {
71 const proxyPath = upstreamProxy ? upstreamProxy.path : '/'
72 return function (request, response, next) {
73 // These config values should be up to date on every request
74 const client = injector.get('config.client')
75 const customContextFile = injector.get('config.customContextFile')
76 const customDebugFile = injector.get('config.customDebugFile')
77 const customClientContextFile = injector.get('config.customClientContextFile')
78 const includeCrossOriginAttribute = injector.get('config.crossOriginAttribute')
79
80 const normalizedUrl = stripHost(request.url) || request.url
81 // For backwards compatibility in middleware plugins, remove in v4.
82 request.normalizedUrl = normalizedUrl
83
84 let requestUrl = normalizedUrl.replace(/\?.*/, '')
85 const requestedRangeHeader = request.headers.range
86
87 // redirect /__karma__ to /__karma__ (trailing slash)
88 if (requestUrl === urlRoot.substr(0, urlRoot.length - 1)) {
89 response.setHeader('Location', proxyPath + urlRoot.substr(1))
90 response.writeHead(301)
91 return response.end('MOVED PERMANENTLY')
92 }
93
94 // ignore urls outside urlRoot
95 if (!requestUrl.startsWith(urlRoot)) {
96 return next()
97 }
98
99 // remove urlRoot prefix
100 requestUrl = requestUrl.substr(urlRoot.length - 1)
101
102 // serve client.html
103 if (requestUrl === '/') {
104 // redirect client_with_context.html
105 if (!client.useIframe && client.runInParent) {
106 requestUrl = '/client_with_context.html'
107 } else { // serve client.html
108 return serveStaticFile('/client.html', requestedRangeHeader, response, (data) =>
109 data
110 .replace('\n%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url))
111 .replace('%X_UA_COMPATIBLE_URL%', getXUACompatibleUrl(request.url)))
112 }
113 }
114
115 if (['/karma.js', '/context.js', '/debug.js'].includes(requestUrl)) {
116 return serveStaticFile(requestUrl, requestedRangeHeader, response, (data) =>
117 data
118 .replace('%KARMA_URL_ROOT%', urlRoot)
119 .replace('%KARMA_VERSION%', VERSION)
120 .replace('%KARMA_PROXY_PATH%', proxyPath)
121 .replace('%BROWSER_SOCKET_TIMEOUT%', browserSocketTimeout))
122 }
123
124 // serve the favicon
125 if (requestUrl === '/favicon.ico') {
126 return serveStaticFile(requestUrl, requestedRangeHeader, response)
127 }
128
129 // serve context.html - execution context within the iframe
130 // or debug.html - execution context without channel to the server
131 const isRequestingContextFile = requestUrl === '/context.html'
132 const isRequestingDebugFile = requestUrl === '/debug.html'
133 const isRequestingClientContextFile = requestUrl === '/client_with_context.html'
134 if (isRequestingContextFile || isRequestingDebugFile || isRequestingClientContextFile) {
135 return filesPromise.then((files) => {
136 let fileServer
137 let requestedFileUrl
138 log.debug('custom files', customContextFile, customDebugFile, customClientContextFile)
139 if (isRequestingContextFile && customContextFile) {
140 log.debug(`Serving customContextFile ${customContextFile}`)
141 fileServer = serveFile
142 requestedFileUrl = customContextFile
143 } else if (isRequestingDebugFile && customDebugFile) {
144 log.debug(`Serving customDebugFile ${customDebugFile}`)
145 fileServer = serveFile
146 requestedFileUrl = customDebugFile
147 } else if (isRequestingClientContextFile && customClientContextFile) {
148 log.debug(`Serving customClientContextFile ${customClientContextFile}`)
149 fileServer = serveFile
150 requestedFileUrl = customClientContextFile
151 } else {
152 log.debug(`Serving static request ${requestUrl}`)
153 fileServer = serveStaticFile
154 requestedFileUrl = requestUrl
155 }
156
157 fileServer(requestedFileUrl, requestedRangeHeader, response, function (data) {
158 common.setNoCacheHeaders(response)
159
160 const scriptTags = []
161 for (const file of files.included) {
162 let filePath = file.path
163 const fileType = file.type || file.detectType()
164
165 if (!FILE_TYPES.includes(fileType)) {
166 if (file.type == null) {
167 log.warn(
168 'Unable to determine file type from the file extension, defaulting to js.\n' +
169 ` To silence the warning specify a valid type for ${file.originalPath} in the configuration file.\n` +
170 ' See https://karma-runner.github.io/latest/config/files.html'
171 )
172 } else {
173 log.warn(`Invalid file type (${file.type || 'empty string'}), defaulting to js.`)
174 }
175 }
176
177 if (!file.isUrl) {
178 filePath = filePathToUrlPath(filePath, basePath, urlRoot, proxyPath)
179
180 if (requestUrl === '/context.html') {
181 filePath += '?' + file.sha
182 }
183 }
184
185 if (fileType === 'css') {
186 scriptTags.push(`<link type="text/css" href="${filePath}" rel="stylesheet">`)
187 } else if (fileType === 'dom') {
188 scriptTags.push(file.content)
189 } else if (fileType === 'html') {
190 scriptTags.push(`<link href="${filePath}" rel="import">`)
191 } else {
192 const scriptType = (SCRIPT_TYPE[fileType] || 'text/javascript')
193 const crossOriginAttribute = includeCrossOriginAttribute ? 'crossorigin="anonymous"' : ''
194 if (fileType === 'module') {
195 scriptTags.push(`<script onerror="throw 'Error loading ${filePath}'" type="${scriptType}" src="${filePath}" ${crossOriginAttribute}></script>`)
196 } else {
197 scriptTags.push(`<script type="${scriptType}" src="${filePath}" ${crossOriginAttribute}></script>`)
198 }
199 }
200 }
201
202 const scriptUrls = []
203 // For client_with_context, html elements are not added directly through an iframe.
204 // Instead, scriptTags is stored to window.__karma__.scriptUrls first. Later, the
205 // client will read window.__karma__.scriptUrls and dynamically add them to the DOM
206 // using DOMParser.
207 if (requestUrl === '/client_with_context.html') {
208 for (const script of scriptTags) {
209 scriptUrls.push(
210 // Escape characters with special roles (tags) in HTML. Open angle brackets are parsed as tags
211 // immediately, even if it is within double quotations in browsers
212 script.replace(/</g, '\\x3C').replace(/>/g, '\\x3E'))
213 }
214 }
215
216 const mappings = data.includes('%MAPPINGS%') ? files.served.map((file) => {
217 const filePath = filePathToUrlPath(file.path, basePath, urlRoot, proxyPath)
218 .replace(/\\/g, '\\\\') // Windows paths contain backslashes and generate bad IDs if not escaped
219 .replace(/'/g, '\\\'') // Escape single quotes - double quotes should not be allowed!
220
221 return ` '${filePath}': '${file.sha}'`
222 }) : []
223
224 return data
225 .replace('%SCRIPTS%', () => scriptTags.join('\n'))
226 .replace('%CLIENT_CONFIG%', 'window.__karma__.config = ' + JSON.stringify(client) + ';\n')
227 .replace('%SCRIPT_URL_ARRAY%', () => 'window.__karma__.scriptUrls = ' + JSON.stringify(scriptUrls) + ';\n')
228 .replace('%MAPPINGS%', () => 'window.__karma__.files = {\n' + mappings.join(',\n') + '\n};\n')
229 .replace('\n%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url))
230 })
231 })
232 } else if (requestUrl === '/context.json') {
233 return filesPromise.then((files) => {
234 common.setNoCacheHeaders(response)
235 response.writeHead(200)
236 response.end(JSON.stringify({
237 files: files.included.map((file) => filePathToUrlPath(file.path + '?' + file.sha, basePath, urlRoot, proxyPath))
238 }))
239 })
240 }
241
242 return next()
243 }
244}
245
246createKarmaMiddleware.$inject = [
247 'filesPromise',
248 'serveStaticFile',
249 'serveFile',
250 'injector',
251 'config.basePath',
252 'config.urlRoot',
253 'config.upstreamProxy',
254 'config.browserSocketTimeout'
255]
256
257// PUBLIC API
258exports.create = createKarmaMiddleware
Note: See TracBrowser for help on using the repository browser.