#include "root.h" #include #include #include #include #include extern "C" void Bun__tickWhilePaused(bool*); namespace Bun { using namespace JSC; template class BunInspectorConnection : public Inspector::FrontendChannel { public: using BunInspectorSocket = uWS::WebSocket; BunInspectorConnection(BunInspectorSocket* ws, JSC::JSGlobalObject* globalObject) : ws(ws) , globalObject(globalObject) , pendingMessages() { } ~BunInspectorConnection() { } Inspector::FrontendChannel::ConnectionType connectionType() const override { return Inspector::FrontendChannel::ConnectionType::Remote; } void onOpen(JSC::JSGlobalObject* globalObject) { this->globalObject = globalObject; this->globalObject->inspectorDebuggable().connect(*this); Inspector::JSGlobalObjectDebugger* debugger = reinterpret_cast(this->globalObject->debugger()); if (debugger) { debugger->runWhilePausedCallback = [](JSC::JSGlobalObject& globalObject, bool& isPaused) -> void { Bun__tickWhilePaused(&isPaused); }; } } void onClose() { this->globalObject->inspectorDebuggable().disconnect(*this); this->pendingMessages.clear(); } void sendMessageToFrontend(const String& message) override { send(message); } void send(const WTF::String& message) { if (ws->getBufferedAmount() == 0) { WTF::CString messageCString = message.utf8(); ws->send(std::string_view { messageCString.data(), messageCString.length() }, uWS::OpCode::TEXT); } else { pendingMessages.append(message); } } void onMessage(std::string_view message) { WTF::String messageString = WTF::String::fromUTF8(message.data(), message.length()); Inspector::JSGlobalObjectDebugger* debugger = reinterpret_cast(this->globalObject->debugger()); if (debugger) { debugger->runWhilePausedCallback = [](JSC::JSGlobalObject& globalObject, bool& done) -> void { Inspector::JSGlobalObjectDebugger* debugger = reinterpret_cast(globalObject.debugger()); Bun__tickWhilePaused(&done); }; } this->globalObject->inspectorDebuggable().dispatchMessageFromRemote(WTFMove(messageString)); } void drain() { if (pendingMessages.size() == 0) return; if (ws->getBufferedAmount() == 0) { ws->cork([&]() { for (auto& message : pendingMessages) { WTF::CString messageCString = message.utf8(); ws->send(std::string_view { messageCString.data(), messageCString.length() }, uWS::OpCode::TEXT); } pendingMessages.clear(); }); } } WTF::Vector pendingMessages; JSC::JSGlobalObject* globalObject; BunInspectorSocket* ws; }; using BunInspectorConnectionNoSSL = BunInspectorConnection; using SSLBunInspectorConnection = BunInspectorConnection; template static void addInspector(void* app, JSC::JSGlobalObject* globalObject) { if constexpr (isSSL) { auto handler = uWS::SSLApp::WebSocketBehavior { /* Settings */ .compression = uWS::DISABLED, .maxPayloadLength = 16 * 1024 * 1024, .idleTimeout = 960, .maxBackpressure = 16 * 1024 * 1024, .closeOnBackpressureLimit = false, .resetIdleTimeoutOnSend = true, .sendPingsAutomatically = true, /* Handlers */ .upgrade = nullptr, .open = [globalObject](auto* ws) { globalObject->setInspectable(true); *ws->getUserData() = new SSLBunInspectorConnection(ws, globalObject); SSLBunInspectorConnection* inspector = *ws->getUserData(); inspector->onOpen(globalObject); // }, .message = [](auto* ws, std::string_view message, uWS::OpCode opCode) { SSLBunInspectorConnection* inspector = *(SSLBunInspectorConnection**)ws->getUserData(); inspector->onMessage(message); // }, .drain = [](auto* ws) { SSLBunInspectorConnection* inspector = *(SSLBunInspectorConnection**)ws->getUserData(); inspector->drain(); // }, .ping = [](auto* /*ws*/, std::string_view) { /* Not implemented yet */ }, .pong = [](auto* /*ws*/, std::string_view) { /* Not implemented yet */ }, .close = [](auto* ws, int /*code*/, std::string_view /*message*/) { SSLBunInspectorConnection* inspector = *(SSLBunInspectorConnection**)ws->getUserData(); inspector->onClose(); delete inspector; } }; ((uWS::SSLApp*)app)->ws("/bun:inspect", std::move(handler)); } else { auto handler = uWS::App::WebSocketBehavior { /* Settings */ .compression = uWS::DISABLED, .maxPayloadLength = 16 * 1024 * 1024, .idleTimeout = 960, .maxBackpressure = 16 * 1024 * 1024, .closeOnBackpressureLimit = false, .resetIdleTimeoutOnSend = true, .sendPingsAutomatically = true, /* Handlers */ .upgrade = nullptr, .open = [globalObject](auto* ws) { globalObject->setInspectable(true); *ws->getUserData() = new BunInspectorConnectionNoSSL(ws, globalObject); BunInspectorConnectionNoSSL* inspector = *ws->getUserData(); inspector->onOpen(globalObject); // }, .message = [](auto* ws, std::string_view message, uWS::OpCode opCode) { BunInspectorConnectionNoSSL* inspector = *(BunInspectorConnectionNoSSL**)ws->getUserData(); inspector->onMessage(message); // }, .drain = [](auto* ws) { BunInspectorConnectionNoSSL* inspector = *(BunInspectorConnectionNoSSL**)ws->getUserData(); inspector->drain(); // }, .ping = [](auto* /*ws*/, std::string_view) { /* Not implemented yet */ }, .pong = [](auto* /*ws*/, std::string_view) { /* Not implemented yet */ }, .close = [](auto* ws, int /*code*/, std::string_view /*message*/) { BunInspectorConnectionNoSSL* inspector = *(BunInspectorConnectionNoSSL**)ws->getUserData(); inspector->onClose(); delete inspector; } }; ((uWS::App*)app)->ws("/bun:inspect", std::move(handler)); } } extern "C" void Bun__addInspector(bool isSSL, void* app, JSC::JSGlobalObject* globalObject) { if (isSSL) { addInspector((uWS::TemplatedApp*)app, globalObject); } else { addInspector((uWS::TemplatedApp*)app, globalObject); } }; }