1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
#include "NodeJSSocket.h"
#include "NodeJSDebugger.h"
#include "file_logger.h"
#include "NodeJSHandlerBase.h"
#include <wx/regex.h>
NodeJSSocket::NodeJSSocket(NodeJSDebugger* debugger)
: m_socket(this)
, m_connected(false)
, m_sequence(0)
, m_debugger(debugger)
, m_firstTimeConnected(true)
{
Bind(wxEVT_ASYNC_SOCKET_CONNECTED, &NodeJSSocket::OnSocketConnected, this);
Bind(wxEVT_ASYNC_SOCKET_ERROR, &NodeJSSocket::OnSocketError, this);
Bind(wxEVT_ASYNC_SOCKET_CONNECTION_LOST, &NodeJSSocket::OnSocketConnectionLost, this);
Bind(wxEVT_ASYNC_SOCKET_INPUT, &NodeJSSocket::OnSocketInput, this);
Bind(wxEVT_ASYNC_SOCKET_CONNECT_ERROR, &NodeJSSocket::OnSocketConnectError, this);
// set of commands that we don't ask for backtrace
// from Node.js after their execution
m_noBacktraceCommands.insert("backtrace");
m_noBacktraceCommands.insert("setbreakpoint");
m_noBacktraceCommands.insert("frame");
m_noBacktraceCommands.insert("source");
m_noBacktraceCommands.insert("evaluate");
m_noBacktraceCommands.insert("lookup");
}
NodeJSSocket::~NodeJSSocket() { Destroy(); }
void NodeJSSocket::OnSocketConnected(clCommandEvent& event)
{
CL_DEBUG("CodeLite >>>> Connection established with Node.js");
// socket is now connected
m_debugger->CallAfter(&NodeJSDebugger::ConnectionEstablished);
m_connected = true;
}
void NodeJSSocket::OnSocketError(clCommandEvent& event)
{
m_errorString = event.GetString();
CL_DEBUG("Socket error: %s", m_errorString);
Destroy();
m_debugger->CallAfter(&NodeJSDebugger::ConnectionLost, m_errorString);
}
void NodeJSSocket::OnSocketConnectionLost(clCommandEvent& event)
{
CL_DEBUG("CodeLite >>>> Lost connection to Node.js");
Destroy();
m_debugger->CallAfter(&NodeJSDebugger::ConnectionLost, m_errorString);
}
void NodeJSSocket::OnSocketInput(clCommandEvent& event)
{
CL_DEBUG("CodeLite >>>> Got something on the socket...");
m_inBuffer << event.GetString();
CL_DEBUG("Node.js <<<< %s", m_inBuffer);
ProcessInputBuffer();
}
void NodeJSSocket::OnSocketConnectError(clCommandEvent& event)
{
CL_DEBUG("CodeLite >>>> connect error");
m_errorString = event.GetString();
m_debugger->CallAfter(&NodeJSDebugger::ConnectError, m_errorString);
Destroy();
}
void NodeJSSocket::Destroy()
{
Unbind(wxEVT_ASYNC_SOCKET_CONNECTED, &NodeJSSocket::OnSocketConnected, this);
Unbind(wxEVT_ASYNC_SOCKET_ERROR, &NodeJSSocket::OnSocketError, this);
Unbind(wxEVT_ASYNC_SOCKET_CONNECTION_LOST, &NodeJSSocket::OnSocketConnectionLost, this);
Unbind(wxEVT_ASYNC_SOCKET_INPUT, &NodeJSSocket::OnSocketInput, this);
Unbind(wxEVT_ASYNC_SOCKET_CONNECT_ERROR, &NodeJSSocket::OnSocketInput, this);
m_socket.Disconnect();
m_firstTimeConnected = true;
m_connected = false;
CL_DEBUG("CodeLite >>>> Cleaning up socket with Node.js");
}
void NodeJSSocket::WriteReply(const wxString& reply)
{
if(!IsConnected()) return;
wxString content;
content << "Content-Length:" << reply.length() << "\r\n\r\n";
content << reply;
m_socket.Send(content);
}
void NodeJSSocket::ProcessInputBuffer()
{
if(m_firstTimeConnected) {
m_firstTimeConnected = false;
// Apply breakpoints
m_debugger->SetBreakpoints();
// When an uncaught exception is thrown, break
m_debugger->BreakOnException();
m_inBuffer.Clear();
// Let codelite know that we have control
m_debugger->GotControl(true);
} else {
wxString buffer = GetResponse();
while(!buffer.IsEmpty()) {
JSONRoot root(buffer);
JSONElement json = root.toElement();
int reqSeq = json.namedObject("request_seq").toInt();
if(reqSeq != wxNOT_FOUND) {
std::map<size_t, NodeJSHandlerBase::Ptr_t>::iterator iter = m_handlers.find((size_t)reqSeq);
if(iter != m_handlers.end()) {
NodeJSHandlerBase::Ptr_t handler = iter->second;
handler->Process(m_debugger, buffer);
m_handlers.erase(iter);
}
if(json.hasNamedObject("running") && !json.namedObject("running").toBool()) {
wxString responseCommand = json.namedObject("command").toString();
m_debugger->GotControl((m_noBacktraceCommands.count(responseCommand) == 0));
} else {
m_debugger->SetCanInteract(false);
}
} else {
// Notify the debugger that we got control
if((json.namedObject("type").toString() == "event")) {
if(json.namedObject("event").toString() == "break") {
// breakpoint hit, notify we got control + request for backtrace
m_debugger->GotControl(true);
} else if(json.namedObject("event").toString() == "exception") {
JSONElement body = json.namedObject("body");
NodeJSDebuggerException exc;
exc.message = body.namedObject("exception").namedObject("text").toString();
;
exc.line = body.namedObject("sourceLine").toInt();
exc.script = body.namedObject("script").namedObject("name").toString();
exc.column = body.namedObject("sourceColumn").toInt();
// the vm execution stopped due to an exception
m_debugger->ExceptionThrown(exc);
}
} else {
m_debugger->SetCanInteract(false);
}
}
// Check to see if we got more reponses in the in-buffer
buffer = GetResponse();
}
}
}
void NodeJSSocket::Connect(const wxString& ip, int port)
{
m_socket.Connect((wxString() << "tcp://" << ip << ":" << port), "");
}
void NodeJSSocket::WriteRequest(JSONElement& request, NodeJSHandlerBase::Ptr_t handler)
{
if(!IsConnected()) return;
size_t seq = NextSequence();
request.addProperty("seq", seq);
wxString content, str;
str = request.format();
content << "Content-Length:" << str.length() << "\r\n\r\n";
content << str;
CL_DEBUG("CodeLite >>>> %s", content);
m_socket.Send(content);
// Keep the handler
if(handler) {
m_handlers.insert(std::make_pair(seq, handler));
}
}
wxString NodeJSSocket::GetResponse()
{
wxRegEx re("Content-Length:[ ]*([0-9]+)", wxRE_ADVANCED);
if(re.Matches(m_inBuffer)) {
size_t start, wholeline_len;
if(!re.GetMatch(&start, &wholeline_len, 0)) {
return "";
}
// Remove anything before the 'start' position
m_inBuffer = m_inBuffer.Mid(start);
// At this point, start = 0
start = 0;
wxString wholine = m_inBuffer.Mid(start, wholeline_len);
// wholine should now contains a string like:
// Content-Length: 1234
// Extract the message length
wxRegEx reContentLen("[0-9]+");
if(!reContentLen.Matches(wholine)) return ""; // Can't happen...
long messageLength;
if(!reContentLen.GetMatch(wholine).ToCLong(&messageLength)) {
// just to be safe
return "";
}
// Remove the "Content-Length: NN\r\n\r\n"
size_t headerLen = wholeline_len + 4;
// Did we read enough from the socket to process?
if(m_inBuffer.length() >= (messageLength + headerLen)) {
m_inBuffer = m_inBuffer.Mid(headerLen);
wxString response = m_inBuffer.Mid(0, messageLength);
// Remove this message from the input buffer
m_inBuffer = m_inBuffer.Mid(messageLength);
return response;
} else {
return "";
}
}
return "";
}
void NodeJSSocket::Shutdown() { m_socket.Disconnect(); }
|