Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/javascript/selenium-webdriver/lib/test/fileserver.js
2884 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License. You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied. See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
'use strict'
19
20
const path = require('node:path')
21
const url = require('node:url')
22
23
const express = require('express')
24
const multer = require('multer')
25
const serveIndex = require('serve-index')
26
27
const { isDevMode } = require('./build')
28
const resources = require('./resources')
29
const { Server } = require('./httpserver')
30
31
const WEB_ROOT = '/common'
32
const DATA_ROOT = '/data'
33
const JS_ROOT = '/javascript'
34
35
const baseDirectory = resources.locate('common/src/web')
36
const dataDirectory = path.join(__dirname, 'data')
37
const jsDirectory = resources.locate('javascript')
38
39
const Pages = (function () {
40
let pages = {}
41
42
function addPage(page, path) {
43
pages.__defineGetter__(page, function () {
44
return exports.whereIs(path)
45
})
46
}
47
48
addPage('ajaxyPage', 'ajaxy_page.html')
49
addPage('alertsPage', 'alerts.html')
50
addPage('basicAuth', 'basicAuth')
51
addPage('blankPage', 'blank.html')
52
addPage('bodyTypingPage', 'bodyTypingTest.html')
53
addPage('booleanAttributes', 'booleanAttributes.html')
54
addPage('childPage', 'child/childPage.html')
55
addPage('chinesePage', 'cn-test.html')
56
addPage('clickJacker', 'click_jacker.html')
57
addPage('clickEventPage', 'clickEventPage.html')
58
addPage('clicksPage', 'clicks.html')
59
addPage('colorPage', 'colorPage.html')
60
addPage('deletingFrame', 'deletingFrame.htm')
61
addPage('draggableLists', 'draggableLists.html')
62
addPage('dragAndDropPage', 'dragAndDropTest.html')
63
addPage('droppableItems', 'droppableItems.html')
64
addPage('documentWrite', 'document_write_in_onload.html')
65
addPage('dynamicallyModifiedPage', 'dynamicallyModifiedPage.html')
66
addPage('dynamicPage', 'dynamic.html')
67
addPage('echoPage', 'echo')
68
addPage('errorsPage', 'errors.html')
69
addPage('xhtmlFormPage', 'xhtmlFormPage.xhtml')
70
addPage('formPage', 'formPage.html')
71
addPage('formSelectionPage', 'formSelectionPage.html')
72
addPage('framesetPage', 'frameset.html')
73
addPage('grandchildPage', 'child/grandchild/grandchildPage.html')
74
addPage('html5Page', 'html5Page.html')
75
addPage('html5OfflinePage', 'html5/offline.html')
76
addPage('iframePage', 'iframes.html')
77
addPage('javascriptEnhancedForm', 'javascriptEnhancedForm.html')
78
addPage('javascriptPage', 'javascriptPage.html')
79
addPage('linkedImage', 'linked_image.html')
80
addPage('longContentPage', 'longContentPage.html')
81
addPage('macbethPage', 'macbeth.html')
82
addPage('mapVisibilityPage', 'map_visibility.html')
83
addPage('metaRedirectPage', 'meta-redirect.html')
84
addPage('missedJsReferencePage', 'missedJsReference.html')
85
addPage('mouseTrackerPage', 'mousePositionTracker.html')
86
addPage('nestedPage', 'nestedElements.html')
87
addPage('readOnlyPage', 'readOnlyPage.html')
88
addPage('rectanglesPage', 'rectangles.html')
89
addPage('relativeLocators', 'relative_locators.html')
90
addPage('redirectPage', 'redirect')
91
addPage('resultPage', 'resultPage.html')
92
addPage('richTextPage', 'rich_text.html')
93
addPage('printPage', 'printPage.html')
94
addPage('scrollingPage', 'scrollingPage.html')
95
addPage('selectableItemsPage', 'selectableItems.html')
96
addPage('selectPage', 'selectPage.html')
97
addPage('selectSpacePage', 'select_space.html')
98
addPage('simpleTestPage', 'simpleTest.html')
99
addPage('simpleXmlDocument', 'simple.xml')
100
addPage('sleepingPage', 'sleep')
101
addPage('slowIframes', 'slow_loading_iframes.html')
102
addPage('slowLoadingAlertPage', 'slowLoadingAlert.html')
103
addPage('svgPage', 'svgPiechart.xhtml')
104
addPage('tables', 'tables.html')
105
addPage('underscorePage', 'underscore.html')
106
addPage('unicodeLtrPage', 'utf8/unicode_ltr.html')
107
addPage('uploadPage', 'upload.html')
108
addPage('veryLargeCanvas', 'veryLargeCanvas.html')
109
addPage('webComponents', 'webComponents.html')
110
addPage('xhtmlTestPage', 'xhtmlTest.html')
111
addPage('uploadInvisibleTestPage', 'upload_invisible.html')
112
addPage('userpromptPage', 'userprompt.html')
113
addPage('virtualAuthenticator', 'virtual-authenticator.html')
114
addPage('logEntryAdded', 'bidi/logEntryAdded.html')
115
addPage('scriptTestAccessProperty', 'bidi/scriptTestAccessProperty.html')
116
addPage('scriptTestRemoveProperty', 'bidi/scriptTestRemoveProperty.html')
117
addPage('emptyPage', 'bidi/emptyPage.html')
118
addPage('emptyText', 'bidi/emptyText.txt')
119
addPage('redirectedHttpEquiv', 'bidi/redirected_http_equiv.html')
120
addPage('releaseAction', 'bidi/release_action.html')
121
addPage('fedcm', 'fedcm/fedcm_async_js.html')
122
return pages
123
})()
124
125
const Path = {
126
BASIC_AUTH: WEB_ROOT + '/basicAuth',
127
ECHO: WEB_ROOT + '/echo',
128
GENERATED: WEB_ROOT + '/generated',
129
MANIFEST: WEB_ROOT + '/manifest',
130
REDIRECT: WEB_ROOT + '/redirect',
131
PAGE: WEB_ROOT + '/page',
132
SLEEP: WEB_ROOT + '/sleep',
133
UPLOAD: WEB_ROOT + '/upload',
134
}
135
136
var app = express()
137
138
app
139
.get('/', sendIndex)
140
.get('/favicon.ico', function (_req, res) {
141
res.writeHead(204)
142
res.end()
143
})
144
.use(JS_ROOT, serveIndex(jsDirectory), express.static(jsDirectory))
145
.post(Path.UPLOAD, handleUpload)
146
.use(WEB_ROOT, serveIndex(baseDirectory), express.static(baseDirectory))
147
.use(DATA_ROOT, serveIndex(dataDirectory), express.static(dataDirectory))
148
.get(Path.ECHO, sendEcho)
149
.get(Path.PAGE, sendInifinitePage)
150
.get(Path.PAGE + '/*', sendInifinitePage)
151
.get(Path.REDIRECT, redirectToResultPage)
152
.get(Path.SLEEP, sendDelayedResponse)
153
.get(Path.BASIC_AUTH, sendBasicAuth)
154
155
if (isDevMode()) {
156
var closureDir = resources.locate('third_party/closure/goog')
157
app.use('/third_party/closure/goog', serveIndex(closureDir), express.static(closureDir))
158
}
159
var server = new Server(app)
160
161
function redirectToResultPage(_, response) {
162
response.writeHead(303, {
163
Location: Pages.resultPage,
164
})
165
return response.end()
166
}
167
168
function sendInifinitePage(request, response) {
169
// eslint-disable-next-line n/no-deprecated-api
170
var pathname = url.parse(request.url).pathname
171
var lastIndex = pathname.lastIndexOf('/')
172
var pageNumber = lastIndex == -1 ? 'Unknown' : pathname.substring(lastIndex + 1)
173
var body = [
174
'<!DOCTYPE html>',
175
'<title>Page',
176
pageNumber,
177
'</title>',
178
'Page number <span id="pageNumber">',
179
pageNumber,
180
'</span>',
181
'<p><a href="../xhtmlTest.html" target="_top">top</a>',
182
].join('')
183
response.writeHead(200, {
184
'Content-Length': Buffer.byteLength(body, 'utf8'),
185
'Content-Type': 'text/html; charset=utf-8',
186
})
187
response.end(body)
188
}
189
190
function sendBasicAuth(request, response) {
191
const denyAccess = function () {
192
response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="test"' })
193
response.end('Access denied')
194
}
195
196
const basicAuthRegExp = /^\s*basic\s+([a-z0-9\-._~+/]+)=*\s*$/i
197
const auth = request.headers.authorization
198
const match = basicAuthRegExp.exec(auth || '')
199
if (!match) {
200
denyAccess()
201
return
202
}
203
204
const userNameAndPass = Buffer.from(match[1], 'base64').toString()
205
const parts = userNameAndPass.split(':', 2)
206
if (parts[0] !== 'genie' || parts[1] !== 'bottle') {
207
denyAccess()
208
return
209
}
210
211
response.writeHead(200, { 'content-type': 'text/plain' })
212
response.end('Access granted!')
213
}
214
215
function sendDelayedResponse(request, response) {
216
var duration = 0
217
// eslint-disable-next-line n/no-deprecated-api
218
var query = url.parse(request.url).search.substr(1) || ''
219
var match = query.match(/\btime=(\d+)/)
220
if (match) {
221
duration = parseInt(match[1], 10)
222
}
223
224
setTimeout(function () {
225
var body = ['<!DOCTYPE html>', '<title>Done</title>', '<body>Slept for ', duration, 's</body>'].join('')
226
response.writeHead(200, {
227
'Content-Length': Buffer.byteLength(body, 'utf8'),
228
'Content-Type': 'text/html; charset=utf-8',
229
'Cache-Control': 'no-cache',
230
Pragma: 'no-cache',
231
Expires: 0,
232
})
233
response.end(body)
234
}, duration * 1000)
235
}
236
237
function handleUpload(request, response) {
238
let upload = multer({ storage: multer.memoryStorage() }).any()
239
upload(request, response, function (err) {
240
if (err) {
241
response.writeHead(500)
242
response.end(err + '')
243
} else {
244
if (!request.files) {
245
return response.status(400).send('No files were uploaded')
246
}
247
248
let files = []
249
let keys = Object.keys(request.files)
250
251
keys.forEach((file) => {
252
files.push(request.files[file].originalname)
253
})
254
255
response
256
.status(200)
257
.contentType('html')
258
.send(files.join('\n') + '\n<script>window.top.window.onUploadDone();</script>')
259
}
260
})
261
}
262
263
function sendEcho(request, response) {
264
if (request.query['html']) {
265
const html = request.query['html']
266
if (html) {
267
response.writeHead(200, {
268
'Content-Length': Buffer.byteLength(html, 'utf8'),
269
'Content-Type': 'text/html; charset=utf-8',
270
})
271
response.end(html)
272
return
273
}
274
}
275
276
var body = [
277
'<!DOCTYPE html>',
278
'<title>Echo</title>',
279
'<div class="request">',
280
request.method,
281
' ',
282
request.url,
283
' ',
284
'HTTP/',
285
request.httpVersion,
286
'</div>',
287
]
288
for (var name in request.headers) {
289
body.push('<div class="header ', name, '">', name, ': ', request.headers[name], '</div>')
290
}
291
body = body.join('')
292
response.writeHead(200, {
293
'Content-Length': Buffer.byteLength(body, 'utf8'),
294
'Content-Type': 'text/html; charset=utf-8',
295
})
296
response.end(body)
297
}
298
299
/**
300
* Responds to a request for the file server's main index.
301
* @param {!http.ServerRequest} request The request object.
302
* @param {!http.ServerResponse} response The response object.
303
*/
304
function sendIndex(request, response) {
305
// eslint-disable-next-line n/no-deprecated-api
306
var pathname = url.parse(request.url).pathname
307
308
var host = request.headers.host
309
if (!host) {
310
host = server.host()
311
}
312
313
var requestUrl = ['http://' + host + pathname].join('')
314
315
function createListEntry(path) {
316
var url = requestUrl + path
317
return ['<li><a href="', url, '">', path, '</a>'].join('')
318
}
319
320
var data = ['<!DOCTYPE html><h1>/</h1><hr/><ul>', createListEntry('common'), createListEntry('data')]
321
if (isDevMode()) {
322
data.push(createListEntry('javascript'))
323
}
324
data.push('</ul>')
325
data = data.join('')
326
327
response.writeHead(200, {
328
'Content-Type': 'text/html; charset=UTF-8',
329
'Content-Length': Buffer.byteLength(data, 'utf8'),
330
})
331
response.end(data)
332
}
333
334
/**
335
* Detects the hostname.
336
* @return {string} The detected hostname or 'localhost' if not found.
337
*/
338
function getHostName() {
339
const hostnameFromEnv = process.env.HOSTNAME
340
return hostnameFromEnv ? hostnameFromEnv : 'localhost'
341
}
342
343
// PUBLIC application
344
345
/**
346
* Starts the server on the specified port.
347
* @param {number=} opt_port The port to use, or 0 for any free port.
348
* @return {!Promise<Host>} A promise that will resolve
349
* with the server host when it has fully started.
350
*/
351
exports.start = server.start.bind(server)
352
353
/**
354
* Stops the server.
355
* @return {!Promise} A promise that will resolve when the
356
* server has closed all connections.
357
*/
358
exports.stop = server.stop.bind(server)
359
360
/**
361
* Formats a URL for this server.
362
* @param {string=} opt_pathname The desired pathname on the server.
363
* @return {string} The formatted URL.
364
* @throws {Error} If the server is not running.
365
*/
366
exports.url = server.url.bind(server)
367
368
/**
369
* Builds the URL for a file in the //common/src/web directory of the
370
* Selenium client.
371
* @param {string} filePath A path relative to //common/src/web to compute a
372
* URL for.
373
* @return {string} The formatted URL.
374
* @throws {Error} If the server is not running.
375
*/
376
exports.whereIs = function (filePath) {
377
filePath = filePath.replace(/\\/g, '/')
378
if (!filePath.startsWith('/')) {
379
filePath = `${WEB_ROOT}/${filePath}`
380
}
381
return server.url(filePath)
382
}
383
384
exports.getHostName = getHostName
385
386
exports.Pages = Pages
387
388
if (require.main === module) {
389
server.start(2310).then(function () {
390
console.log('Server running at ' + server.url())
391
})
392
}
393
394