MCPcopy Create free account
hub / github.com/ElementsProject/elements / RecvUntilTerminator

Method RecvUntilTerminator

src/util/sock.cpp:215–296  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

213}
214
215std::string Sock::RecvUntilTerminator(uint8_t terminator,
216 std::chrono::milliseconds timeout,
217 CThreadInterrupt& interrupt,
218 size_t max_data) const
219{
220 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
221 std::string data;
222 bool terminator_found{false};
223
224 // We must not consume any bytes past the terminator from the socket.
225 // One option is to read one byte at a time and check if we have read a terminator.
226 // However that is very slow. Instead, we peek at what is in the socket and only read
227 // as many bytes as possible without crossing the terminator.
228 // Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
229 // one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
230 // at a time is about 50 times slower.
231
232 for (;;) {
233 if (data.size() >= max_data) {
234 throw std::runtime_error(
235 strprintf("Received too many bytes without a terminator (%u)", data.size()));
236 }
237
238 char buf[512];
239
240 const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
241
242 switch (peek_ret) {
243 case -1: {
244 const int err{WSAGetLastError()};
245 if (IOErrorIsPermanent(err)) {
246 throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
247 }
248 break;
249 }
250 case 0:
251 throw std::runtime_error("Connection unexpectedly closed by peer");
252 default:
253 auto end = buf + peek_ret;
254 auto terminator_pos = std::find(buf, end, terminator);
255 terminator_found = terminator_pos != end;
256
257 const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
258 static_cast<size_t>(peek_ret)};
259
260 const ssize_t read_ret{Recv(buf, try_len, 0)};
261
262 if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
263 throw std::runtime_error(
264 strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
265 "peek claimed %u bytes are available",
266 read_ret, try_len, peek_ret));
267 }
268
269 // Don't include the terminator in the output.
270 const size_t append_len{terminator_found ? try_len - 1 : try_len};
271
272 data.append(buf, buf + append_len);

Callers 4

AcceptMethod · 0.80
BOOST_AUTO_TEST_CASEFunction · 0.80
FUZZ_TARGET_INITFunction · 0.80

Calls 3

IOErrorIsPermanentFunction · 0.85
NetworkErrorStringFunction · 0.85
sizeMethod · 0.45

Tested by 2

BOOST_AUTO_TEST_CASEFunction · 0.64
FUZZ_TARGET_INITFunction · 0.64