fileserver.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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. 'use strict';
  18. var fs = require('fs'),
  19. http = require('http'),
  20. path = require('path'),
  21. url = require('url');
  22. var express = require('express');
  23. var multer = require('multer');
  24. var serveIndex = require('serve-index');
  25. var Server = require('./httpserver').Server,
  26. resources = require('./resources'),
  27. isDevMode = require('../devmode');
  28. var WEB_ROOT = '/common';
  29. var JS_ROOT = '/javascript';
  30. var baseDirectory = resources.locate(isDevMode ? 'common/src/web' : '.');
  31. var jsDirectory = resources.locate(isDevMode ? 'javascript' : '..');
  32. var Pages = (function() {
  33. var pages = {};
  34. function addPage(page, path) {
  35. pages.__defineGetter__(page, function() {
  36. return exports.whereIs(path);
  37. });
  38. }
  39. addPage('ajaxyPage', 'ajaxy_page.html');
  40. addPage('alertsPage', 'alerts.html');
  41. addPage('bodyTypingPage', 'bodyTypingTest.html');
  42. addPage('booleanAttributes', 'booleanAttributes.html');
  43. addPage('childPage', 'child/childPage.html');
  44. addPage('chinesePage', 'cn-test.html');
  45. addPage('clickJacker', 'click_jacker.html');
  46. addPage('clickEventPage', 'clickEventPage.html');
  47. addPage('clicksPage', 'clicks.html');
  48. addPage('colorPage', 'colorPage.html');
  49. addPage('deletingFrame', 'deletingFrame.htm');
  50. addPage('draggableLists', 'draggableLists.html');
  51. addPage('dragAndDropPage', 'dragAndDropTest.html');
  52. addPage('droppableItems', 'droppableItems.html');
  53. addPage('documentWrite', 'document_write_in_onload.html');
  54. addPage('dynamicallyModifiedPage', 'dynamicallyModifiedPage.html');
  55. addPage('dynamicPage', 'dynamic.html');
  56. addPage('echoPage', 'echo');
  57. addPage('errorsPage', 'errors.html');
  58. addPage('xhtmlFormPage', 'xhtmlFormPage.xhtml');
  59. addPage('formPage', 'formPage.html');
  60. addPage('formSelectionPage', 'formSelectionPage.html');
  61. addPage('framesetPage', 'frameset.html');
  62. addPage('grandchildPage', 'child/grandchild/grandchildPage.html');
  63. addPage('html5Page', 'html5Page.html');
  64. addPage('html5OfflinePage', 'html5/offline.html');
  65. addPage('iframePage', 'iframes.html');
  66. addPage('javascriptEnhancedForm', 'javascriptEnhancedForm.html');
  67. addPage('javascriptPage', 'javascriptPage.html');
  68. addPage('linkedImage', 'linked_image.html');
  69. addPage('longContentPage', 'longContentPage.html');
  70. addPage('macbethPage', 'macbeth.html');
  71. addPage('mapVisibilityPage', 'map_visibility.html');
  72. addPage('metaRedirectPage', 'meta-redirect.html');
  73. addPage('missedJsReferencePage', 'missedJsReference.html');
  74. addPage('mouseTrackerPage', 'mousePositionTracker.html');
  75. addPage('nestedPage', 'nestedElements.html');
  76. addPage('readOnlyPage', 'readOnlyPage.html');
  77. addPage('rectanglesPage', 'rectangles.html');
  78. addPage('redirectPage', 'redirect');
  79. addPage('resultPage', 'resultPage.html');
  80. addPage('richTextPage', 'rich_text.html');
  81. addPage('selectableItemsPage', 'selectableItems.html');
  82. addPage('selectPage', 'selectPage.html');
  83. addPage('simpleTestPage', 'simpleTest.html');
  84. addPage('simpleXmlDocument', 'simple.xml');
  85. addPage('sleepingPage', 'sleep');
  86. addPage('slowIframes', 'slow_loading_iframes.html');
  87. addPage('slowLoadingAlertPage', 'slowLoadingAlert.html');
  88. addPage('svgPage', 'svgPiechart.xhtml');
  89. addPage('tables', 'tables.html');
  90. addPage('underscorePage', 'underscore.html');
  91. addPage('unicodeLtrPage', 'utf8/unicode_ltr.html');
  92. addPage('uploadPage', 'upload.html');
  93. addPage('veryLargeCanvas', 'veryLargeCanvas.html');
  94. addPage('xhtmlTestPage', 'xhtmlTest.html');
  95. return pages;
  96. })();
  97. var Path = {
  98. BASIC_AUTH: WEB_ROOT + '/basicAuth',
  99. ECHO: WEB_ROOT + '/echo',
  100. GENERATED: WEB_ROOT + '/generated',
  101. MANIFEST: WEB_ROOT + '/manifest',
  102. REDIRECT: WEB_ROOT + '/redirect',
  103. PAGE: WEB_ROOT + '/page',
  104. SLEEP: WEB_ROOT + '/sleep',
  105. UPLOAD: WEB_ROOT + '/upload'
  106. };
  107. var app = express();
  108. app.get('/', sendIndex)
  109. .get('/favicon.ico', function(req, res) {
  110. res.writeHead(204);
  111. res.end();
  112. })
  113. .use(JS_ROOT, serveIndex(jsDirectory), express.static(jsDirectory))
  114. .post(Path.UPLOAD, handleUpload)
  115. .use(WEB_ROOT, serveIndex(baseDirectory), express.static(baseDirectory))
  116. .get(Path.ECHO, sendEcho)
  117. .get(Path.PAGE, sendInifinitePage)
  118. .get(Path.PAGE + '/*', sendInifinitePage)
  119. .get(Path.REDIRECT, redirectToResultPage)
  120. .get(Path.SLEEP, sendDelayedResponse)
  121. if (isDevMode) {
  122. var closureDir = resources.locate('third_party/closure/goog');
  123. app.use('/third_party/closure/goog',
  124. serveIndex(closureDir), express.static(closureDir));
  125. }
  126. var server = new Server(app);
  127. function redirectToResultPage(_, response) {
  128. response.writeHead(303, {
  129. Location: Pages.resultPage
  130. });
  131. return response.end();
  132. }
  133. function sendInifinitePage(request, response) {
  134. var pathname = url.parse(request.url).pathname;
  135. var lastIndex = pathname.lastIndexOf('/');
  136. var pageNumber =
  137. (lastIndex == -1 ? 'Unknown' : pathname.substring(lastIndex + 1));
  138. var body = [
  139. '<!DOCTYPE html>',
  140. '<title>Page', pageNumber, '</title>',
  141. 'Page number <span id="pageNumber">', pageNumber, '</span>',
  142. '<p><a href="../xhtmlTest.html" target="_top">top</a>'
  143. ].join('');
  144. response.writeHead(200, {
  145. 'Content-Length': Buffer.byteLength(body, 'utf8'),
  146. 'Content-Type': 'text/html; charset=utf-8'
  147. });
  148. response.end(body);
  149. }
  150. function sendDelayedResponse(request, response) {
  151. var duration = 0;
  152. var query = url.parse(request.url).query || '';
  153. var match = query.match(/\btime=(\d+)/);
  154. if (match) {
  155. duration = parseInt(match[1], 10);
  156. }
  157. setTimeout(function() {
  158. var body = [
  159. '<!DOCTYPE html>',
  160. '<title>Done</title>',
  161. '<body>Slept for ', duration, 's</body>'
  162. ].join('');
  163. response.writeHead(200, {
  164. 'Content-Length': Buffer.byteLength(body, 'utf8'),
  165. 'Content-Type': 'text/html; charset=utf-8',
  166. 'Cache-Control': 'no-cache',
  167. 'Pragma': 'no-cache',
  168. 'Expires': 0
  169. });
  170. response.end(body);
  171. }, duration * 1000);
  172. }
  173. function handleUpload(request, response) {
  174. let upload = multer({storage: multer.memoryStorage()}).any();
  175. upload(request, response, function(err) {
  176. if (err) {
  177. response.writeHead(500);
  178. response.end(err + '');
  179. } else {
  180. response.writeHead(200);
  181. response.write(request.files[0].buffer);
  182. response.end('<script>window.top.window.onUploadDone();</script>');
  183. }
  184. });
  185. }
  186. function sendEcho(request, response) {
  187. var body = [
  188. '<!DOCTYPE html>',
  189. '<title>Echo</title>',
  190. '<div class="request">',
  191. request.method, ' ', request.url, ' ', 'HTTP/', request.httpVersion,
  192. '</div>'
  193. ];
  194. for (var name in request.headers) {
  195. body.push('<div class="header ', name , '">',
  196. name, ': ', request.headers[name], '</div>');
  197. }
  198. body = body.join('');
  199. response.writeHead(200, {
  200. 'Content-Length': Buffer.byteLength(body, 'utf8'),
  201. 'Content-Type': 'text/html; charset=utf-8'
  202. });
  203. response.end(body);
  204. }
  205. /**
  206. * Responds to a request for the file server's main index.
  207. * @param {!http.ServerRequest} request The request object.
  208. * @param {!http.ServerResponse} response The response object.
  209. */
  210. function sendIndex(request, response) {
  211. var pathname = url.parse(request.url).pathname;
  212. var host = request.headers.host;
  213. if (!host) {
  214. host = server.host();
  215. }
  216. var requestUrl = ['http://' + host + pathname].join('');
  217. function createListEntry(path) {
  218. var url = requestUrl + path;
  219. return ['<li><a href="', url, '">', path, '</a>'].join('');
  220. }
  221. var data = ['<!DOCTYPE html><h1>/</h1><hr/><ul>',
  222. createListEntry('common')];
  223. if (isDevMode) {
  224. data.push(createListEntry('javascript'));
  225. }
  226. data.push('</ul>');
  227. data = data.join('');
  228. response.writeHead(200, {
  229. 'Content-Type': 'text/html; charset=UTF-8',
  230. 'Content-Length': Buffer.byteLength(data, 'utf8')
  231. });
  232. response.end(data);
  233. }
  234. // PUBLIC application
  235. /**
  236. * Starts the server on the specified port.
  237. * @param {number=} opt_port The port to use, or 0 for any free port.
  238. * @return {!Promise<Host>} A promise that will resolve
  239. * with the server host when it has fully started.
  240. */
  241. exports.start = server.start.bind(server);
  242. /**
  243. * Stops the server.
  244. * @return {!Promise} A promise that will resolve when the
  245. * server has closed all connections.
  246. */
  247. exports.stop = server.stop.bind(server);
  248. /**
  249. * Formats a URL for this server.
  250. * @param {string=} opt_pathname The desired pathname on the server.
  251. * @return {string} The formatted URL.
  252. * @throws {Error} If the server is not running.
  253. */
  254. exports.url = server.url.bind(server);
  255. /**
  256. * Builds the URL for a file in the //common/src/web directory of the
  257. * Selenium client.
  258. * @param {string} filePath A path relative to //common/src/web to compute a
  259. * URL for.
  260. * @return {string} The formatted URL.
  261. * @throws {Error} If the server is not running.
  262. */
  263. exports.whereIs = function(filePath) {
  264. filePath = filePath.replace(/\\/g, '/');
  265. if (!filePath.startsWith('/')) {
  266. filePath = '/' + filePath;
  267. }
  268. return server.url(WEB_ROOT + filePath);
  269. };
  270. exports.Pages = Pages;
  271. if (require.main === module) {
  272. server.start(2310).then(function() {
  273. console.log('Server running at ' + server.url());
  274. });
  275. }