inspector: report client-visible host and port

Node instance may not know the real host and port user sees when
debug frontend connects through the SSH tunnel. This change fixes
'/json/list' response by using the value client provided in the host
header.

PR-URL: https://github.com/nodejs/node/pull/19664
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
This commit is contained in:
Eugene Ostroukhov 2018-03-05 19:18:17 -08:00
parent 6de1a12e49
commit a9a1f12b42
8 changed files with 88 additions and 45 deletions

View File

@ -495,12 +495,12 @@ class HttpHandler : public ProtocolHandler {
CancelHandshake();
return;
} else if (!event.upgrade) {
delegate()->OnHttpGet(event.path);
delegate()->OnHttpGet(event.host, event.path);
} else if (event.ws_key.empty()) {
CancelHandshake();
return;
} else {
delegate()->OnSocketUpgrade(event.path, event.ws_key);
delegate()->OnSocketUpgrade(event.host, event.path, event.ws_key);
}
}
}

View File

@ -17,8 +17,10 @@ class InspectorSocket {
public:
class Delegate {
public:
virtual void OnHttpGet(const std::string& path) = 0;
virtual void OnSocketUpgrade(const std::string& path,
virtual void OnHttpGet(const std::string& host,
const std::string& path) = 0;
virtual void OnSocketUpgrade(const std::string& host,
const std::string& path,
const std::string& accept_key) = 0;
virtual void OnWsFrame(const std::vector<char>& frame) = 0;
virtual ~Delegate() {}

View File

@ -16,23 +16,7 @@ namespace inspector {
// depend on inspector_socket_server.h
std::string FormatWsAddress(const std::string& host, int port,
const std::string& target_id,
bool include_protocol) {
// Host is valid (socket was bound) so colon means it's a v6 IP address
bool v6 = host.find(':') != std::string::npos;
std::ostringstream url;
if (include_protocol)
url << "ws://";
if (v6) {
url << '[';
}
url << host;
if (v6) {
url << ']';
}
url << ':' << port << '/' << target_id;
return url.str();
}
bool include_protocol);
namespace {
static const uint8_t PROTOCOL_JSON[] = {
@ -45,6 +29,31 @@ void Escape(std::string* string) {
}
}
std::string FormatHostPort(const std::string& host, int port) {
// Host is valid (socket was bound) so colon means it's a v6 IP address
bool v6 = host.find(':') != std::string::npos;
std::ostringstream url;
if (v6) {
url << '[';
}
url << host;
if (v6) {
url << ']';
}
url << ':' << port;
return url.str();
}
std::string FormatAddress(const std::string& host,
const std::string& target_id,
bool include_protocol) {
std::ostringstream url;
if (include_protocol)
url << "ws://";
url << host << '/' << target_id;
return url.str();
}
std::string MapToString(const std::map<std::string, std::string>& object) {
bool first = true;
std::ostringstream json;
@ -141,6 +150,11 @@ void SendProtocolJson(InspectorSocket* socket) {
}
} // namespace
std::string FormatWsAddress(const std::string& host, int port,
const std::string& target_id,
bool include_protocol) {
return FormatAddress(FormatHostPort(host, port), target_id, include_protocol);
}
class Closer {
public:
@ -213,8 +227,8 @@ class SocketSession {
~Delegate() {
server_->SessionTerminated(session_id_);
}
void OnHttpGet(const std::string& path) override;
void OnSocketUpgrade(const std::string& path,
void OnHttpGet(const std::string& host, const std::string& path) override;
void OnSocketUpgrade(const std::string& host, const std::string& path,
const std::string& ws_key) override;
void OnWsFrame(const std::vector<char>& data) override;
@ -320,6 +334,7 @@ void InspectorSocketServer::SessionTerminated(int session_id) {
}
bool InspectorSocketServer::HandleGetRequest(int session_id,
const std::string& host,
const std::string& path) {
SocketSession* session = Session(session_id);
InspectorSocket* socket = session->ws_socket();
@ -328,7 +343,7 @@ bool InspectorSocketServer::HandleGetRequest(int session_id,
return false;
if (MatchPathSegment(command, "list") || command[0] == '\0') {
SendListResponse(socket, session);
SendListResponse(socket, host, session);
return true;
} else if (MatchPathSegment(command, "protocol")) {
SendProtocolJson(socket);
@ -336,17 +351,12 @@ bool InspectorSocketServer::HandleGetRequest(int session_id,
} else if (MatchPathSegment(command, "version")) {
SendVersionResponse(socket);
return true;
} else if (const char* target_id = MatchPathSegment(command, "activate")) {
if (TargetExists(target_id)) {
SendHttpResponse(socket, "Target activated");
return true;
}
return false;
}
return false;
}
void InspectorSocketServer::SendListResponse(InspectorSocket* socket,
const std::string& host,
SocketSession* session) {
std::vector<std::map<std::string, std::string>> response;
for (const std::string& id : delegate_->GetTargetIds()) {
@ -371,15 +381,18 @@ void InspectorSocketServer::SendListResponse(InspectorSocket* socket,
}
}
if (!connected) {
std::string host = socket->GetHost();
int port = session->server_port();
std::string detected_host = host;
if (detected_host.empty()) {
detected_host = FormatHostPort(socket->GetHost(),
session->server_port());
}
std::ostringstream frontend_url;
frontend_url << "chrome-devtools://devtools/bundled";
frontend_url << "/inspector.html?experiments=true&v8only=true&ws=";
frontend_url << FormatWsAddress(host, port, id, false);
frontend_url << FormatAddress(detected_host, id, false);
target_map["devtoolsFrontendUrl"] += frontend_url.str();
target_map["webSocketDebuggerUrl"] =
FormatWsAddress(host, port, id, true);
FormatAddress(detected_host, id, true);
}
}
SendHttpResponse(socket, MapsToString(response));
@ -531,12 +544,14 @@ void SocketSession::Send(const std::string& message) {
ws_socket_->Write(message.data(), message.length());
}
void SocketSession::Delegate::OnHttpGet(const std::string& path) {
if (!server_->HandleGetRequest(session_id_, path))
void SocketSession::Delegate::OnHttpGet(const std::string& host,
const std::string& path) {
if (!server_->HandleGetRequest(session_id_, host, path))
Session()->ws_socket()->CancelHandshake();
}
void SocketSession::Delegate::OnSocketUpgrade(const std::string& path,
void SocketSession::Delegate::OnSocketUpgrade(const std::string& host,
const std::string& path,
const std::string& ws_key) {
std::string id = path.empty() ? path : path.substr(1);
server_->SessionStarted(session_id_, id, ws_key);

View File

@ -67,7 +67,8 @@ class InspectorSocketServer {
// Session connection lifecycle
void Accept(int server_port, uv_stream_t* server_socket);
bool HandleGetRequest(int session_id, const std::string& path);
bool HandleGetRequest(int session_id, const std::string& host,
const std::string& path);
void SessionStarted(int session_id, const std::string& target_id,
const std::string& ws_id);
void SessionTerminated(int session_id);
@ -77,7 +78,8 @@ class InspectorSocketServer {
SocketSession* Session(int session_id);
private:
void SendListResponse(InspectorSocket* socket, SocketSession* session);
void SendListResponse(InspectorSocket* socket, const std::string& host,
SocketSession* session);
bool TargetExists(const std::string& id);
enum class ServerState {kNew, kRunning, kStopping, kStopped};

View File

@ -112,11 +112,11 @@ class TestInspectorDelegate : public InspectorSocket::Delegate {
delegate = nullptr;
}
void OnHttpGet(const std::string& path) override {
void OnHttpGet(const std::string& host, const std::string& path) override {
process(kInspectorHandshakeHttpGet, path);
}
void OnSocketUpgrade(const std::string& path,
void OnSocketUpgrade(const std::string& host, const std::string& path,
const std::string& ws_key) override {
ws_key_ = ws_key;
process(kInspectorHandshakeUpgraded, path);

View File

@ -365,10 +365,11 @@ class NodeInstance {
}
}
httpGet(host, path) {
httpGet(host, path, hostHeaderValue) {
console.log('[test]', `Testing ${path}`);
const headers = hostHeaderValue ? { 'Host': hostHeaderValue } : null;
return this.portPromise.then((port) => new Promise((resolve, reject) => {
const req = http.get({ host, port, path }, (res) => {
const req = http.get({ host, port, path, headers }, (res) => {
let response = '';
res.setEncoding('utf8');
res

View File

@ -0,0 +1,22 @@
// Flags: --expose-internals
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { NodeInstance } = require('../common/inspector-helper.js');
common.crashOnUnhandledRejection();
async function test() {
const madeUpHost = '111.111.111.111:11111';
const child = new NodeInstance(undefined, 'var a = 1');
const response = await child.httpGet(null, '/json', madeUpHost);
assert.ok(
response[0].webSocketDebuggerUrl.startsWith(`ws://${madeUpHost}`),
response[0].webSocketDebuggerUrl);
child.kill();
}
test();

View File

@ -11,8 +11,9 @@ function checkListResponse(response) {
assert.strictEqual(1, response.length);
assert.ok(response[0].devtoolsFrontendUrl);
assert.ok(
/ws:\/\/127\.0\.0\.1:\d+\/[0-9A-Fa-f]{8}-/
.test(response[0].webSocketDebuggerUrl));
/ws:\/\/localhost:\d+\/[0-9A-Fa-f]{8}-/
.test(response[0].webSocketDebuggerUrl),
response[0].webSocketDebuggerUrl);
}
function checkVersion(response) {