|
1 | 1 | #include "network_agent.h" |
| 2 | +#include <string> |
| 3 | +#include "env-inl.h" |
2 | 4 | #include "inspector/protocol_helper.h" |
3 | 5 | #include "network_inspector.h" |
| 6 | +#include "node_metadata.h" |
4 | 7 | #include "util-inl.h" |
| 8 | +#include "uv.h" |
| 9 | +#include "uv/unix.h" |
| 10 | +#include "v8-context.h" |
5 | 11 | #include "v8.h" |
6 | 12 |
|
7 | 13 | namespace node { |
@@ -177,8 +183,13 @@ std::unique_ptr<protocol::Network::Response> createResponseFromObject( |
177 | 183 | } |
178 | 184 |
|
179 | 185 | NetworkAgent::NetworkAgent(NetworkInspector* inspector, |
180 | | - v8_inspector::V8Inspector* v8_inspector) |
181 | | - : inspector_(inspector), v8_inspector_(v8_inspector) { |
| 186 | + v8_inspector::V8Inspector* v8_inspector, |
| 187 | + Environment* env, |
| 188 | + std::shared_ptr<protocol::IoAgent> io_agent) |
| 189 | + : inspector_(inspector), |
| 190 | + v8_inspector_(v8_inspector), |
| 191 | + env_(env), |
| 192 | + io_agent_(io_agent) { |
182 | 193 | event_notifier_map_["requestWillBeSent"] = &NetworkAgent::requestWillBeSent; |
183 | 194 | event_notifier_map_["responseReceived"] = &NetworkAgent::responseReceived; |
184 | 195 | event_notifier_map_["loadingFailed"] = &NetworkAgent::loadingFailed; |
@@ -211,6 +222,167 @@ protocol::DispatchResponse NetworkAgent::disable() { |
211 | 222 | return protocol::DispatchResponse::Success(); |
212 | 223 | } |
213 | 224 |
|
| 225 | +std::tuple<int, std::string, std::string> NetworkAgent::spawnFetchProcess( |
| 226 | + std::string_view code, Environment* env, std::string_view url) { |
| 227 | + std::string stdout_result; |
| 228 | + std::string stderr_result; |
| 229 | + uv_loop_t* loop = new uv_loop_t; |
| 230 | + uv_loop_init(loop); |
| 231 | + uv_process_t child; |
| 232 | + uv_pipe_t stdout_pipe; |
| 233 | + uv_pipe_init(loop, &stdout_pipe, 0); |
| 234 | + uv_pipe_t stderr_pipe; |
| 235 | + uv_pipe_init(loop, &stderr_pipe, 0); |
| 236 | + |
| 237 | + uv_process_options_t uv_process_options; |
| 238 | + std::string command = |
| 239 | + env->exec_path() + " --eval \"" + code.data() + "\" -- " + url.data(); |
| 240 | + |
| 241 | +#ifdef _WIN32 |
| 242 | + const char* file = "cmd.exe"; |
| 243 | + char* args[] = { |
| 244 | + const_cast<char*>(file), |
| 245 | + const_cast<char*>("/d"), |
| 246 | + const_cast<char*>("/s"), |
| 247 | + const_cast<char*>("/c"), |
| 248 | + reinterpret_cast<char*>(const_cast<char*>(command.c_str())), |
| 249 | + nullptr |
| 250 | + }; |
| 251 | +#elif defined(__ANDROID__) |
| 252 | + const char* file = "/system/bin/sh"; |
| 253 | + char* args[] = { |
| 254 | + const_cast<char*>(file), |
| 255 | + const_cast<char*>("-c"), |
| 256 | + reinterpret_cast<char*>(const_cast<char*>(command.c_str())), |
| 257 | + nullptr |
| 258 | + }; |
| 259 | +#else |
| 260 | + const char* file = "/bin/sh"; |
| 261 | + char* args[] = { |
| 262 | + const_cast<char*>(file), |
| 263 | + const_cast<char*>("-c"), |
| 264 | + reinterpret_cast<char*>(const_cast<char*>(command.c_str())), |
| 265 | + nullptr |
| 266 | + }; |
| 267 | +#endif |
| 268 | + |
| 269 | + uv_stdio_container_t stdio[3]; |
| 270 | + uv_process_options.file = file; |
| 271 | + uv_process_options.args = args; |
| 272 | + uv_process_options.flags = 0; |
| 273 | + uv_process_options.stdio_count = 3; |
| 274 | + uv_process_options.stdio = stdio; |
| 275 | + uv_process_options.cwd = nullptr; |
| 276 | + uv_process_options.env = nullptr; |
| 277 | + |
| 278 | + uv_process_options.exit_cb = |
| 279 | + [](uv_process_t* req, int64_t exit_status, int term_signal) { |
| 280 | + uv_close(reinterpret_cast<uv_handle_t*>(req), nullptr); |
| 281 | + }; |
| 282 | + |
| 283 | + stdio[0].flags = UV_INHERIT_FD; |
| 284 | + stdio[0].data.fd = 0; |
| 285 | + stdio[1].flags = |
| 286 | + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); |
| 287 | + stdio[1].data.stream = reinterpret_cast<uv_stream_t*>(&stdout_pipe); |
| 288 | + stdio[2].flags = |
| 289 | + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); |
| 290 | + stdio[2].data.stream = reinterpret_cast<uv_stream_t*>(&stderr_pipe); |
| 291 | + |
| 292 | + int r = uv_spawn(loop, &child, &uv_process_options); |
| 293 | + |
| 294 | + if (r != 0) { |
| 295 | + uv_loop_close(loop); |
| 296 | + delete loop; |
| 297 | + return {r, stdout_result, stderr_result}; |
| 298 | + } |
| 299 | + |
| 300 | + auto alloc_cb = |
| 301 | + [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { |
| 302 | + buf->base = static_cast<char*>(malloc(suggested_size)); |
| 303 | + buf->len = suggested_size; |
| 304 | + }; |
| 305 | + |
| 306 | + auto read_cb = [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { |
| 307 | + auto* response = static_cast<std::string*>(stream->data); |
| 308 | + if (nread > 0) { |
| 309 | + response->append(buf->base, nread); |
| 310 | + } else if (nread < 0) { |
| 311 | + if (!response->empty() && response->back() == '\n') { |
| 312 | + response->pop_back(); |
| 313 | + } |
| 314 | + uv_close(reinterpret_cast<uv_handle_t*>(stream), nullptr); |
| 315 | + } |
| 316 | + if (buf->base) free(buf->base); |
| 317 | + }; |
| 318 | + |
| 319 | + stdout_pipe.data = &stdout_result; |
| 320 | + uv_read_start( |
| 321 | + reinterpret_cast<uv_stream_t*>(&stdout_pipe), alloc_cb, read_cb); |
| 322 | + |
| 323 | + stderr_pipe.data = &stderr_result; |
| 324 | + uv_read_start( |
| 325 | + reinterpret_cast<uv_stream_t*>(&stderr_pipe), alloc_cb, read_cb); |
| 326 | + |
| 327 | + uv_run(loop, UV_RUN_DEFAULT); |
| 328 | + |
| 329 | + uv_walk( |
| 330 | + loop, |
| 331 | + [](uv_handle_t* handle, void*) { |
| 332 | + if (!uv_is_closing(handle)) { |
| 333 | + uv_close(handle, nullptr); |
| 334 | + } |
| 335 | + }, |
| 336 | + nullptr); |
| 337 | + |
| 338 | + uv_run(loop, UV_RUN_DEFAULT); |
| 339 | + |
| 340 | + uv_loop_close(loop); |
| 341 | + delete loop; |
| 342 | + return {r, stdout_result, stderr_result}; |
| 343 | +} |
| 344 | + |
| 345 | +protocol::DispatchResponse NetworkAgent::loadNetworkResource( |
| 346 | + const protocol::String& in_url, |
| 347 | + std::unique_ptr<protocol::Network::LoadNetworkResourcePageResult>* |
| 348 | + out_resource) { |
| 349 | + if (!env_->options()->experimental_inspector_network_resource) { |
| 350 | + return protocol::DispatchResponse::MethodNotFound( |
| 351 | + "Network.loadNetworkResource is not supported in this environment. " |
| 352 | + "Please enable the experimental-inspector-network-resource option."); |
| 353 | + } |
| 354 | + DCHECK(io_agent_); |
| 355 | + |
| 356 | + std::string code = R"( |
| 357 | + fetch(process.argv[1], {signal: AbortSignal.timeout(2000) }).then(res => { |
| 358 | + if (res.ok) { |
| 359 | + res.text().then(console.log) |
| 360 | + } else { |
| 361 | + throw new Error('Network error: ' + res.status); |
| 362 | + } |
| 363 | + }) |
| 364 | + )"; |
| 365 | + |
| 366 | + auto [r, response, err] = spawnFetchProcess(code, env_, in_url); |
| 367 | + if (r == 0 && err.empty()) { |
| 368 | + std::string uuid = std::to_string(load_id_counter_); |
| 369 | + load_id_counter_++; |
| 370 | + io_agent_->setData(uuid, response); |
| 371 | + auto result = protocol::Network::LoadNetworkResourcePageResult::create() |
| 372 | + .setSuccess(true) |
| 373 | + .setStream(uuid) |
| 374 | + .build(); |
| 375 | + out_resource->reset(result.release()); |
| 376 | + } else { |
| 377 | + auto result = protocol::Network::LoadNetworkResourcePageResult::create() |
| 378 | + .setSuccess(false) |
| 379 | + .build(); |
| 380 | + out_resource->reset(result.release()); |
| 381 | + } |
| 382 | + |
| 383 | + return protocol::DispatchResponse::Success(); |
| 384 | +} |
| 385 | + |
214 | 386 | void NetworkAgent::requestWillBeSent(v8::Local<v8::Context> context, |
215 | 387 | v8::Local<v8::Object> params) { |
216 | 388 | protocol::String request_id; |
|
0 commit comments