|
33 | 33 |
|
34 | 34 | #include "llapr.h" |
35 | 35 |
|
36 | | -static const char MESSAGE_DELIMITER = '\0'; |
| 36 | +// Each message on the wire is framed with a 4-byte big-endian length prefix |
| 37 | +// followed by exactly that many payload bytes. A single delimiter byte can't |
| 38 | +// be used because the payload is now binary LLSD, which contains embedded NULs |
| 39 | +// (and every other byte value) in its length/size fields. |
| 40 | +static constexpr size_t MESSAGE_HEADER_SIZE = 4; |
37 | 41 |
|
38 | 42 | LLPluginMessagePipeOwner::LLPluginMessagePipeOwner() : |
39 | 43 | mMessagePipe(NULL), |
@@ -121,8 +125,16 @@ bool LLPluginMessagePipe::addMessage(const std::string &message) |
121 | 125 | mOutputStartIndex = 0; |
122 | 126 | } |
123 | 127 |
|
| 128 | + // Frame the message with a 4-byte big-endian length prefix. |
| 129 | + U32 msg_size = static_cast<U32>(message.size()); |
| 130 | + const char header[MESSAGE_HEADER_SIZE] = { |
| 131 | + static_cast<char>((msg_size >> 24) & 0xFF), |
| 132 | + static_cast<char>((msg_size >> 16) & 0xFF), |
| 133 | + static_cast<char>((msg_size >> 8) & 0xFF), |
| 134 | + static_cast<char>(msg_size & 0xFF) |
| 135 | + }; |
| 136 | + mOutput.append(header, MESSAGE_HEADER_SIZE); |
124 | 137 | mOutput += message; |
125 | | - mOutput += MESSAGE_DELIMITER; // message separator |
126 | 138 |
|
127 | 139 | return true; |
128 | 140 | } |
@@ -177,9 +189,10 @@ bool LLPluginMessagePipe::pumpOutput() |
177 | 189 |
|
178 | 190 | LLMutexLock lock(&mOutputMutex); |
179 | 191 |
|
180 | | - const char * output_data = &(mOutput.data()[mOutputStartIndex]); |
181 | | - if(*output_data != '\0') |
| 192 | + if(mOutput.size() > mOutputStartIndex) |
182 | 193 | { |
| 194 | + const char * output_data = &(mOutput.data()[mOutputStartIndex]); |
| 195 | + |
183 | 196 | // write any outgoing messages |
184 | 197 | in_size = (apr_size_t) (mOutput.size() - mOutputStartIndex); |
185 | 198 | out_size = in_size; |
@@ -370,27 +383,39 @@ bool LLPluginMessagePipe::pumpInput(F64 timeout) |
370 | 383 |
|
371 | 384 | void LLPluginMessagePipe::processInput(void) |
372 | 385 | { |
373 | | - // Look for input delimiter(s) in the input buffer. |
374 | | - size_t delim; |
375 | 386 | mInputMutex.lock(); |
376 | | - while((delim = mInput.find(MESSAGE_DELIMITER)) != std::string::npos) |
| 387 | + // Each message is a 4-byte big-endian length followed by that many payload |
| 388 | + // bytes. Dispatch complete messages, leaving any trailing partial message |
| 389 | + // in the buffer to be completed by a later read. |
| 390 | + while(mInput.size() >= MESSAGE_HEADER_SIZE) |
377 | 391 | { |
378 | | - // Let the owner process this message |
379 | | - if (mOwner) |
| 392 | + const U8* buf = reinterpret_cast<const U8*>(mInput.data()); |
| 393 | + size_t msg_size = |
| 394 | + (static_cast<size_t>(buf[0]) << 24) | |
| 395 | + (static_cast<size_t>(buf[1]) << 16) | |
| 396 | + (static_cast<size_t>(buf[2]) << 8) | |
| 397 | + static_cast<size_t>(buf[3]); |
| 398 | + |
| 399 | + if(mInput.size() < MESSAGE_HEADER_SIZE + msg_size) |
380 | 400 | { |
381 | | - // Pull the message out of the input buffer before calling receiveMessageRaw. |
382 | | - // It's now possible for this function to get called recursively (in the case where the plugin makes a blocking request) |
383 | | - // and this guarantees that the messages will get dequeued correctly. |
384 | | - std::string message(mInput, 0, delim); |
385 | | - mInput.erase(0, delim + 1); |
386 | | - mInputMutex.unlock(); |
387 | | - mOwner->receiveMessageRaw(message); |
388 | | - mInputMutex.lock(); |
| 401 | + // The full message body hasn't arrived yet; wait for more. |
| 402 | + break; |
389 | 403 | } |
390 | | - else |
| 404 | + |
| 405 | + if(!mOwner) |
391 | 406 | { |
392 | 407 | LL_WARNS("Plugin") << "!mOwner" << LL_ENDL; |
| 408 | + break; |
393 | 409 | } |
| 410 | + |
| 411 | + // Pull the message out of the input buffer before calling receiveMessageRaw. |
| 412 | + // It's now possible for this function to get called recursively (in the case where the plugin makes a blocking request) |
| 413 | + // and this guarantees that the messages will get dequeued correctly. |
| 414 | + std::string message(mInput, MESSAGE_HEADER_SIZE, msg_size); |
| 415 | + mInput.erase(0, MESSAGE_HEADER_SIZE + msg_size); |
| 416 | + mInputMutex.unlock(); |
| 417 | + mOwner->receiveMessageRaw(message); |
| 418 | + mInputMutex.lock(); |
394 | 419 | } |
395 | 420 | mInputMutex.unlock(); |
396 | 421 | } |
|
0 commit comments