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
|
#include "Message.h"
#include <wx/tokenzr.h>
namespace
{
#define HEADER_CONTENT_LENGTH "Content-Length"
#define STATE_NORMAL 0
#define STATE_DOUBLE_QUOTES 1
#define STATE_ESCAPE 2
/// Since clangd might return a binary characters it breaks the wxString conversion
/// This means that the "Content-Lenght" value might point us into an invalid position
/// inside the position (i.e. broken json mssage)
/// So we dont rely on the header length, but instead we count the chars ourself
int FindCompleteMessage(const wxString& jsonMsg, int startIndex)
{
if(jsonMsg[startIndex] != '{') {
return wxNOT_FOUND;
}
int depth = 1;
int msglen = 1; // skip the '{'
int state = STATE_NORMAL;
size_t strLen = jsonMsg.length();
for(size_t i = (startIndex + 1); i < strLen; ++i, ++msglen) {
wxChar ch = jsonMsg[i];
switch(state) {
case STATE_NORMAL:
switch(ch) {
case '{':
case '[':
++depth;
break;
case ']':
case '}':
--depth;
if(depth == 0) {
return (msglen + 1); // include this char
}
break;
case '"':
state = STATE_DOUBLE_QUOTES;
break;
default:
break;
}
break;
case STATE_DOUBLE_QUOTES:
switch(ch) {
case '\\':
state = STATE_ESCAPE;
break;
case '"':
state = STATE_NORMAL;
break;
default:
break;
}
break;
case STATE_ESCAPE:
switch(ch) {
default:
state = STATE_DOUBLE_QUOTES;
break;
}
break;
}
}
return wxNOT_FOUND;
}
int ReadHeaders(const wxString& message, wxStringMap_t& headers)
{
int where = message.Find("\r\n\r\n");
if(where == wxNOT_FOUND) {
return wxNOT_FOUND;
}
wxString headerSection = message.Mid(0, where); // excluding the "\r\n\r\n"
wxArrayString lines = ::wxStringTokenize(headerSection, "\n", wxTOKEN_STRTOK);
for(wxString& header : lines) {
header.Trim().Trim(false);
wxString name = header.BeforeFirst(':');
wxString value = header.AfterFirst(':');
headers.insert({ name.Trim().Trim(false), value.Trim().Trim(false) });
}
// return the headers section + the separator
return (where + 4);
}
} // namespace
LSP::Message::Message() {}
LSP::Message::~Message() {}
JSONItem LSP::Message::ToJSON(const wxString& name) const
{
JSONItem json = JSONItem::createObject(name);
json.addProperty("jsonrpc", m_jsonrpc);
return json;
}
void LSP::Message::FromJSON(const JSONItem& json) { m_jsonrpc = json.namedObject("jsonrpc").toString(); }
int LSP::Message::GetNextID()
{
static int requestId = 0;
return ++requestId;
}
std::unique_ptr<JSON> LSP::Message::GetJSONPayload(wxString& network_buffer)
{
// Strip the headers
wxStringMap_t headers;
int headersSize = ReadHeaders(network_buffer, headers);
if(headersSize == wxNOT_FOUND) {
return nullptr;
}
if(headers.count(HEADER_CONTENT_LENGTH) == 0) {
return nullptr;
}
int msglen = FindCompleteMessage(network_buffer, headersSize);
if(msglen == wxNOT_FOUND) {
return nullptr;
}
if((headersSize + msglen) > (int)network_buffer.length()) {
return nullptr;
}
wxString json_str = network_buffer.Mid(0, headersSize + msglen);
// Remove the message from the network buffer
network_buffer.Remove(0, headersSize + msglen);
// remove the headers section from the message and construct a JSON object
json_str.Remove(0, headersSize);
std::unique_ptr<JSON> json(new JSON(json_str));
if(json->isOk()) {
return json;
}
return nullptr;
}
|