Files
react-native-devtools-frontend/scripts/hosted_mode/server.js
T
Paul Lewis fc33efbfc6 [screenshot] Updates server encoding
Change-Id: I80bf3714ff2740f0dd3e63d3f25d5c152a6bb322
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2072385
Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org>
Commit-Queue: Paul Lewis <aerotwist@chromium.org>
2020-02-26 08:49:54 +00:00

217 lines
7.6 KiB
JavaScript

// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var fs = require('fs');
var http = require('http');
var path = require('path');
var parseURL = require('url').parse;
var utils = require('../utils');
const remoteDebuggingPort = parseInt(process.env.REMOTE_DEBUGGING_PORT, 10) || 9222;
const serverPort = parseInt(process.env.PORT, 10) || 8090;
const localProtocolPath = process.env.LOCAL_PROTOCOL_PATH;
const devtoolsFolder = path.resolve(path.join(__dirname, '../..'));
http.createServer(requestHandler).listen(serverPort);
console.log(`Started hosted mode server at http://localhost:${serverPort}\n`);
console.log('For info on using the hosted mode server, see our contributing docs:');
console.log('https://bit.ly/devtools-contribution-guide');
console.log('Tip: Look for the \'Development server options\' section\n');
function requestHandler(request, response) {
var filePath = parseURL(request.url).pathname;
if (filePath === '/') {
var landingURL = `http://localhost:${remoteDebuggingPort}#custom=true`;
sendResponse(200, `<html>Please go to <a href="${landingURL}">${landingURL}</a></html>`);
return;
}
var proxiedFile = proxy(filePath, sendResponse);
if (proxiedFile) {
proxiedFile.then(data => sendResponse(200, data)).catch(handleProxyError);
return;
}
function handleProxyError(err) {
console.log(`Error serving the file ${filePath}:`, err);
console.log(`Make sure you opened Chrome with the flag "--remote-debugging-port=${remoteDebuggingPort}"`);
sendResponse(500, '500 - Internal Server Error');
}
var absoluteFilePath = path.join(devtoolsFolder, filePath);
if (!path.resolve(absoluteFilePath).startsWith(devtoolsFolder)) {
console.log(`File requested (${absoluteFilePath}) is outside of devtools folder: ${devtoolsFolder}`);
sendResponse(403, `403 - Access denied. File requested is outside of devtools folder: ${devtoolsFolder}`);
return;
}
fs.exists(absoluteFilePath, fsExistsCallback);
function fsExistsCallback(fileExists) {
if (!fileExists) {
console.log(`Cannot find file ${absoluteFilePath}`);
sendResponse(404, '404 - File not found');
return;
}
let encoding = 'utf8';
if (absoluteFilePath.endsWith('.png') || absoluteFilePath.endsWith('.jpg')) {
encoding = 'binary';
}
fs.readFile(absoluteFilePath, encoding, readFileCallback);
}
function readFileCallback(err, file) {
if (err) {
console.log(`Unable to read local file ${absoluteFilePath}:`, err);
sendResponse(500, '500 - Internal Server Error');
return;
}
sendResponse(200, file);
}
function sendResponse(statusCode, data) {
const path = parseURL(request.url).pathname;
if (path.endsWith('.rawresponse')) {
sendRawResponse(data);
return;
}
let encoding = 'utf8';
if (path.endsWith('.js')) {
response.setHeader('Content-Type', 'text/javascript; charset=utf-8');
} else if (path.endsWith('.wasm')) {
response.setHeader('Content-Type', 'application/wasm');
encoding = 'binary'
} else if (path.endsWith('.svg')) {
response.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
} else if (path.endsWith('.png')) {
response.setHeader('Content-Type', 'image/png');
encoding = 'binary';
} else if (path.endsWith('.jpg')) {
response.setHeader('Content-Type', 'image/jpg');
encoding = 'binary';
}
response.writeHead(statusCode);
response.write(data, encoding);
response.end();
}
function sendRawResponse(data) {
const lines = data.split('\n');
let isHeader = true;
let line = lines.shift();
const statusCode = parseInt(line);
while ((line = lines.shift()) !== undefined) {
if (line.trim() === '') {
isHeader = false;
if (request.headers['if-none-match'] && response.getHeader('ETag') === request.headers['if-none-match']) {
response.writeHead(304);
response.end();
return;
}
response.writeHead(statusCode);
continue;
}
if (isHeader) {
const firstColon = line.indexOf(':');
response.setHeader(line.substring(0, firstColon), line.substring(firstColon + 1).trim());
} else {
response.write(line);
}
}
response.end();
}
}
var proxyFilePathToURL = {
'/front_end/SupportedCSSProperties.js': cloudURL.bind(null, 'SupportedCSSProperties.js'),
'/front_end/InspectorBackendCommands.js': cloudURL.bind(null, 'InspectorBackendCommands.js'),
'/favicon.ico': () => 'https://chrome-devtools-frontend.appspot.com/favicon.ico',
'/front_end/accessibility/ARIAProperties.js': cloudURL.bind(null, 'accessibility/ARIAProperties.js'),
};
function cloudURL(path, commitHash) {
return `https://chrome-devtools-frontend.appspot.com/serve_file/@${commitHash}/${path}`;
}
var proxyFileCache = new Map();
function proxy(filePath) {
if (!(filePath in proxyFilePathToURL))
return null;
if (localProtocolPath && filePath === '/front_end/InspectorBackendCommands.js')
return serveLocalProtocolFile();
if (process.env.CHROMIUM_COMMIT)
return onProxyFileURL(proxyFilePathToURL[filePath](process.env.CHROMIUM_COMMIT));
return utils.fetch(`http://localhost:${remoteDebuggingPort}/json/version`)
.then(onBrowserMetadata)
.then(onProxyFileURL);
function serveLocalProtocolFile() {
return new Promise((resolve, reject) => {
fs.exists(localProtocolPath, fsExistsCallback);
function fsExistsCallback(fileExists) {
if (!fileExists) {
reject(new Error(`Cannot find local protocol file ${localProtocolPath}`));
return;
}
fs.readFile(localProtocolPath, 'binary', readFileCallback);
}
function readFileCallback(err, file) {
if (err) {
reject(new Error(`Unable to read local protocol file ${localProtocolPath}`));
return;
}
return resolve(file);
}
});
}
function onBrowserMetadata(metadata) {
var metadataObject = JSON.parse(metadata);
var match = metadataObject['WebKit-Version'].match(/\s\(@(\b[0-9a-f]{5,40}\b)/);
var commitHash = match[1];
var proxyFileURL = proxyFilePathToURL[filePath](commitHash);
return proxyFileURL;
}
function onProxyFileURL(proxyFileURL) {
if (proxyFileCache.has(proxyFileURL))
return Promise.resolve(proxyFileCache.get(proxyFileURL));
return utils.fetch(proxyFileURL).then(cacheProxyFile.bind(null, proxyFileURL)).catch(onMissingFile);
}
function onMissingFile() {
var isFullCheckout = utils.shellOutput('git config --get remote.origin.url') ===
'https://chromium.googlesource.com/chromium/src.git';
var earlierCommitHash;
var gitLogCommand = `git log --max-count=1 --grep="Commit-Position" --before="12 hours ago"`;
if (isFullCheckout) {
earlierCommitHash = utils.shellOutput(`${gitLogCommand} --pretty=format:"%H"`);
} else {
var commitMessage = utils.shellOutput(`${gitLogCommand}`);
earlierCommitHash = commitMessage.match(/Cr-Mirrored-Commit: (.*)/)[1];
}
var fallbackURL = proxyFilePathToURL[filePath](earlierCommitHash);
console.log('WARNING: Unable to fetch generated file based on browser\'s revision');
console.log('Fetching earlier revision of same file as fallback');
console.log('There may be a mismatch between the front-end and back-end');
console.log(fallbackURL, '\n');
return utils.fetch(fallbackURL).then(cacheProxyFile.bind(null, fallbackURL));
}
function cacheProxyFile(proxyFileURL, data) {
proxyFileCache.set(proxyFileURL, data);
return data;
}
}