@@ -97,6 +97,7 @@ void BackendServer::startProcess() {
9797 stdoutPipe_ = new uv_pipe_t {};
9898 stdoutPipe_->data = this ;
9999 uv_pipe_init (uv_default_loop (), stdoutPipe_, 0 );
100+ stdoutReadBuf_.clear ();
100101
101102 stderrPipe_ = new uv_pipe_t {};
102103 stderrPipe_->data = this ;
@@ -202,13 +203,23 @@ void BackendServer::onProcessDataReceived(uv_stream_t * stream, ssize_t nread, c
202203 return ;
203204 }
204205 if (buf->base ) {
206+ // print to debug log if there is any
207+ logger ()->debug (" RECV: {}" , std::string (buf->base , buf->len ));
208+
205209 // initial ready message from the backend server
206- if (buf->base [0 ] == ' \0 ' ) {
210+ if (!ready_ && buf->base [0 ] == ' \0 ' ) {
207211 ready_ = true ;
212+ // skip the first byte, and add the received data to our buffer
213+ // FIXME: this is not very reliable
214+ stdoutReadBuf_.append (buf->base + 1 , buf->len - 1 );
208215 }
209216 else {
210- handleBackendReply (buf->base , nread);
217+ // add the received data to our buffer
218+ stdoutReadBuf_.append (buf->base , buf->len );
211219 }
220+
221+ handleBackendReply ();
222+
212223 delete[] buf->base ;
213224 }
214225}
@@ -258,6 +269,7 @@ void BackendServer::closeStdioPipes() {
258269 delete reinterpret_cast <uv_pipe_t *>(handle);
259270 });
260271 stdoutPipe_ = nullptr ;
272+ stdoutReadBuf_.clear ();
261273 }
262274
263275 if (stderrPipe_ != nullptr ) {
@@ -268,44 +280,49 @@ void BackendServer::closeStdioPipes() {
268280 }
269281}
270282
271- void BackendServer::handleBackendReply (const char * readBuf, size_t len) {
272- // print to debug log if there is any
273- logger ()->debug (" RECV: {}" , std::string (readBuf, len));
283+ void BackendServer::handleBackendReply () {
284+ // each output message should be a full line ends with \n or \r\n, so we need to do buffering and
285+ // handle the messages line by line.
286+ auto lineStartPos = 0 ;
287+ for (;;) {
288+ auto lineEndPos = stdoutReadBuf_.find (' \n ' , lineStartPos);
289+ if (lineEndPos != stdoutReadBuf_.npos ) {
290+ auto lineLen = lineEndPos - lineStartPos;
291+ auto line = stdoutReadBuf_.c_str () + lineStartPos;
292+ auto lineEnd = line + lineLen;
274293
275- // pass the response back to the clients
276- auto line = readBuf;
277- auto buf_end = readBuf + len;
278- while (line < buf_end) {
279- // Format of each line: "PIMG_MSG|<client_id>|<reply JSON string>\n"
280- if (auto line_end = strchr (line, ' \n ' )) {
281294 // only handle lines prefixed with "PIME_MSG|" since other lines
282295 // might be debug messages printed by the backend.
296+ // Format of each message: "PIMG_MSG|<client_id>|<reply JSON string>\n"
283297 if (strncmp (line, " PIME_MSG|" , 9 ) == 0 ) {
284298 line += 9 ; // Skip the "PIME_MSG|" prefix
285-
286299 if (auto sep = strchr (line, ' |' )) {
287300 // split the client_id from the remaining json reply
288301 string clientId (line, sep - line);
289302 auto msg = sep + 1 ;
290- auto msg_len = line_end - msg;
303+ auto msgLen = lineEnd - msg;
291304 // because Windows uses CRLF "\r\n" for new lines, python and node.js
292305 // try to convert "\n" to "\r\n" sometimes. Let's remove the additional '\r'
293- if (msg_len > 0 && msg[msg_len - 1 ] == ' \r ' ) {
294- --msg_len ;
306+ if (msgLen > 0 && msg[msgLen - 1 ] == ' \r ' ) {
307+ --msgLen ;
295308 }
296309
297310 // send the reply message back to the client
298311 if (auto client = pipeServer_->clientFromId (clientId)) {
299- client->writePipe (msg, len );
312+ client->writePipe (msg, msgLen );
300313 }
301314 }
302315 }
303- line = line_end + 1 ;
316+
317+ // skip empty lines or additional CRLF, and go to the next non-empty line
318+ lineStartPos = lineEndPos + 1 ;
304319 }
305320 else {
306321 break ;
307322 }
308323 }
324+ // Leave remaining data not processed in the buffer, waiting for the next \n so it becomes a full line.
325+ stdoutReadBuf_ = stdoutReadBuf_.substr (lineStartPos);
309326}
310327
311328void BackendServer::startReadOutputPipe () {
0 commit comments