blob: de599adc1b268ec2f8966b6aa30c1ea6585ad745 [file] [log] [blame]
Andrew Top286dd782018-10-02 16:52:45 -07001// Copyright (c) 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4var fs = require('fs');
5var http = require('http');
6var path = require('path');
7var parseURL = require('url').parse;
8
9var utils = require('../utils');
10
11const remoteDebuggingPort = parseInt(process.env.REMOTE_DEBUGGING_PORT, 10) || 9222;
12const serverPort = parseInt(process.env.PORT, 10) || 8090;
13const localProtocolPath = process.env.LOCAL_PROTOCOL_PATH;
14const devtoolsFolder = path.resolve(path.join(__dirname, '../..'));
15
16http.createServer(requestHandler).listen(serverPort);
17console.log(`Started hosted mode server at http://localhost:${serverPort}\n`);
18console.log('For info on using the hosted mode server, see our contributing docs:');
19console.log('https://bit.ly/devtools-contribution-guide');
20console.log('Tip: Look for the \'Development server options\' section\n');
21
22function requestHandler(request, response) {
23 var filePath = parseURL(request.url).pathname;
24 if (filePath === '/') {
25 var landingURL = `http://localhost:${remoteDebuggingPort}#custom=true&experiments=true`;
26 sendResponse(200, `<html>Please go to <a href="${landingURL}">${landingURL}</a></html>`);
27 return;
28 }
29
30 var proxiedFile = proxy(filePath, sendResponse);
31 if (proxiedFile) {
32 proxiedFile.then(data => sendResponse(200, data)).catch(handleProxyError);
33 return;
34 }
35
36 function handleProxyError(err) {
37 console.log(`Error serving the file ${filePath}:`, err);
38 console.log(`Make sure you opened Chrome with the flag "--remote-debugging-port=${remoteDebuggingPort}"`);
39 sendResponse(500, '500 - Internal Server Error');
40 }
41
42 var absoluteFilePath = path.join(process.cwd(), filePath);
43 if (!path.resolve(absoluteFilePath).startsWith(devtoolsFolder)) {
44 console.log(`File requested is outside of devtools folder: ${devtoolsFolder}`);
45 sendResponse(403, `403 - Access denied. File requested is outside of devtools folder: ${devtoolsFolder}`);
46 return;
47 }
48
49 fs.exists(absoluteFilePath, fsExistsCallback);
50
51 function fsExistsCallback(fileExists) {
52 if (!fileExists) {
53 console.log(`Cannot find file ${absoluteFilePath}`);
54 sendResponse(404, '404 - File not found');
55 return;
56 }
57 fs.readFile(absoluteFilePath, 'binary', readFileCallback);
58 }
59
60 function readFileCallback(err, file) {
61 if (err) {
62 console.log(`Unable to read local file ${absoluteFilePath}:`, err);
63 sendResponse(500, '500 - Internal Server Error');
64 return;
65 }
66 sendResponse(200, file);
67 }
68
69 function sendResponse(statusCode, data) {
Kaido Kertf585e262020-06-08 11:42:28 -070070 if (request.url.endsWith('.js')) {
71 response.setHeader('Content-Type', 'text/javascript');
72 }
73
Andrew Top286dd782018-10-02 16:52:45 -070074 response.writeHead(statusCode);
75 response.write(data, 'binary');
76 response.end();
77 }
78}
79
80var proxyFilePathToURL = {
81 '/front_end/SupportedCSSProperties.js': cloudURL.bind(null, 'SupportedCSSProperties.js'),
82 '/front_end/InspectorBackendCommands.js': cloudURL.bind(null, 'InspectorBackendCommands.js'),
83 '/favicon.ico': () => 'https://chrome-devtools-frontend.appspot.com/favicon.ico',
84 '/front_end/accessibility/ARIAProperties.js': cloudURL.bind(null, 'accessibility/ARIAProperties.js'),
85};
86
87function cloudURL(path, commitHash) {
88 return `https://chrome-devtools-frontend.appspot.com/serve_file/@${commitHash}/${path}`;
89}
90
91var proxyFileCache = new Map();
92
93function proxy(filePath) {
94 if (!(filePath in proxyFilePathToURL))
95 return null;
96 if (localProtocolPath && filePath === '/front_end/InspectorBackendCommands.js')
97 return serveLocalProtocolFile();
98 if (process.env.CHROMIUM_COMMIT)
99 return onProxyFileURL(proxyFilePathToURL[filePath](process.env.CHROMIUM_COMMIT));
100 return utils.fetch(`http://localhost:${remoteDebuggingPort}/json/version`)
101 .then(onBrowserMetadata)
102 .then(onProxyFileURL);
103
104 function serveLocalProtocolFile() {
105 return new Promise((resolve, reject) => {
106 fs.exists(localProtocolPath, fsExistsCallback);
107 function fsExistsCallback(fileExists) {
108 if (!fileExists) {
109 reject(new Error(`Cannot find local protocol file ${localProtocolPath}`));
110 return;
111 }
112 fs.readFile(localProtocolPath, 'binary', readFileCallback);
113 }
114 function readFileCallback(err, file) {
115 if (err) {
116 reject(new Error(`Unable to read local protocol file ${localProtocolPath}`));
117 return;
118 }
119 return resolve(file);
120 }
121 });
122 }
123
124 function onBrowserMetadata(metadata) {
125 var metadataObject = JSON.parse(metadata);
126 var match = metadataObject['WebKit-Version'].match(/\s\(@(\b[0-9a-f]{5,40}\b)/);
127 var commitHash = match[1];
128 var proxyFileURL = proxyFilePathToURL[filePath](commitHash);
129 return proxyFileURL;
130 }
131
132 function onProxyFileURL(proxyFileURL) {
133 if (proxyFileCache.has(proxyFileURL))
134 return Promise.resolve(proxyFileCache.get(proxyFileURL));
135 return utils.fetch(proxyFileURL).then(cacheProxyFile.bind(null, proxyFileURL)).catch(onMissingFile);
136 }
137
138 function onMissingFile() {
139 var isFullCheckout = utils.shellOutput('git config --get remote.origin.url') ===
140 'https://chromium.googlesource.com/chromium/src.git';
141 var earlierCommitHash;
142 var gitLogCommand = `git log --max-count=1 --grep="Commit-Position" --before="12 hours ago"`;
143 if (isFullCheckout) {
144 earlierCommitHash = utils.shellOutput(`${gitLogCommand} --pretty=format:"%H"`);
145 } else {
146 var commitMessage = utils.shellOutput(`${gitLogCommand}`);
147 earlierCommitHash = commitMessage.match(/Cr-Mirrored-Commit: (.*)/)[1];
148 }
149 var fallbackURL = proxyFilePathToURL[filePath](earlierCommitHash);
150 console.log('WARNING: Unable to fetch generated file based on browser\'s revision');
151 console.log('Fetching earlier revision of same file as fallback');
152 console.log('There may be a mismatch between the front-end and back-end');
153 console.log(fallbackURL, '\n');
154 return utils.fetch(fallbackURL).then(cacheProxyFile.bind(null, fallbackURL));
155 }
156
157 function cacheProxyFile(proxyFileURL, data) {
158 proxyFileCache.set(proxyFileURL, data);
159 return data;
160 }
161}