19#include <system_error>
23#include <sys/socket.h>
40 bufferReceiveAllowed =
false;
48void ConnectionMock::release()
50 for(
auto i : receivedFds)
55 bufferReceiveAllowed =
false;
67 bufferReceiveAllowed =
false;
72 if ((!state) && receivedFds.size())
74 throw std::runtime_error(
"More buffer were received");
76 bufferReceiveAllowed = state;
79ssize_t ConnectionMock::read(
void * vbuffer,
size_t len)
81 unsigned char * buffer = (
unsigned char*) vbuffer;
82 ssize_t baseLength = 0;
83 if (len && (pendingChar != -1)) {
84 ((
char*)buffer)[0] = pendingChar;
100 struct cmsghdr cmsgh;
102 char control[CMSG_SPACE(256 *
sizeof(
int))];
108 msgh.msg_name = NULL;
109 msgh.msg_namelen = 0;
113 msgh.msg_control = control_un.control;
114 msgh.msg_controllen =
sizeof(control_un.control);
118 recvflag = MSG_CMSG_CLOEXEC;
123 auto size = recvmsg(fds[0], &msgh, recvflag);
129 for (
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg))
131 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
134 while(cmsg->cmsg_len >= CMSG_LEN((fdCount + 1) *
sizeof(
int)))
138 int * fds = (
int*)CMSG_DATA(cmsg);
139 for(
int i = 0; i < fdCount; ++i)
141 if (!bufferReceiveAllowed)
143 pendingData = std::string((
char*)buffer, size);
144 throw std::runtime_error(
"Received unexpected buffer");
147 fcntl(fds[i], F_SETFD, FD_CLOEXEC);
149 receivedFds.push_back(fds[i]);
153 return size + baseLength;
158 if (receivedFds.empty())
160 throw std::runtime_error(
"Buffer not received");
162 int fd = receivedFds.front();
163 receivedFds.pop_front();
169 ssize_t l = str.size();
170 std::vector<char> buff(l);
172 char * in = buff.data();
177 ssize_t rd = read(in, left);
180 throw std::runtime_error(
"Input closed while expecting " + str);
185 throw std::system_error(e, std::generic_category(),
"Read failed while expecting " + str);
190 if (strncmp(str.c_str(), buff.data(), l))
192 throw std::runtime_error(
"Received unexpected content while expecting " + str +
": " + std::string(buff.data(), l));
196char ConnectionMock::readChar(
const std::string &expected)
198 if (pendingChar != -1)
200 char c = pendingChar;
206 ssize_t rd = read(buff, 1);
209 throw std::runtime_error(
"Input closed while expecting " + expected);
214 throw std::system_error(e, std::generic_category(),
"Read failed while expecting " + expected);
219char ConnectionMock::peekChar(
const std::string & expected) {
220 if (pendingChar != -1)
224 char ret = readChar(expected);
229static std::string parseXmlFragmentFromString(
const std::string &str)
232 auto lambda = [&pos, &str]()->
char
237 while((
unsigned)pos < str.length() && str[pos] ==
'\n') pos++;
238 if ((
unsigned)pos != str.length())
240 throw std::runtime_error(
"Expected string contains garbage: " + str);
246std::string ConnectionMock::receiveMore()
249 auto currentFlag = fcntl(fds[0], F_GETFL, 0);
250 if (! (currentFlag & O_NONBLOCK))
252 fcntl(fds[0], F_SETFL, currentFlag | O_NONBLOCK);
256 auto r = read(buffer, 256);
257 if (! (currentFlag & O_NONBLOCK))
259 auto errno_cpy =
errno;
260 fcntl(fds[0], F_SETFL, currentFlag);
266 return pendingData + std::string(buffer, r);
268 perror(
"receiveMore");
276 char c = peekChar(
"base64");
279 result += readChar(
"base64");
288 std::string expectedCanonical = parseXmlFragmentFromString(expected);
290 std::string received;
291 auto readchar = [
this, expected, &received]()->
char
293 char c = readChar(expected);
300 if (fragment != expectedCanonical)
302 fprintf(stderr,
"canonicalized as %s\n", fragment.c_str());
303 throw std::runtime_error(
"xml fragment does not match");
306 catch(std::runtime_error &e)
308 received += receiveMore();
309 throw std::runtime_error(std::string(e.what()) +
"\nexpected: " + expected +
"\nReceived: " + received);
315 ssize_t l = str.size();
316 ssize_t wr = write(fds[1], str.c_str(), l);
320 throw std::system_error(e, std::generic_category(),
"Write failed while sending " + str);
324 throw std::runtime_error(
"Input closed while sending " + str);
330 if (fds[0] != fds[1]) {
331 if (rd && fds[0] != -1) {
333 perror(
"shutdown read fd");
336 if (wr && fds[1] != -1) {
338 perror(
"shutdown write fd");
342 if (!(rd || wr))
return;
344 int how = (rd && wr) ? SHUT_RDWR : rd ? SHUT_RD : SHUT_WR;
347 perror(
"shutdown rdwr");
349 perror(
"shutdown rd");
351 perror(
"shutdown wr");
360 while(buffers[fdCount]) fdCount++;
370 throw std::runtime_error(
"Can't attach buffer to empty message");
376 struct cmsghdr * cmsgh;
379 cmsghdrlength = CMSG_SPACE((fdCount *
sizeof(
int)));
380 cmsgh = (
struct cmsghdr*)malloc(cmsghdrlength);
381 memset(cmsgh, 0, cmsghdrlength);
384 cmsgh->cmsg_len = CMSG_LEN(
sizeof(
int));
385 cmsgh->cmsg_level = SOL_SOCKET;
386 cmsgh->cmsg_type = SCM_RIGHTS;
387 msgh.msg_control = cmsgh;
388 msgh.msg_controllen = cmsghdrlength;
389 for(
int i = 0; i < fdCount; ++i)
392 ((
int *) CMSG_DATA(CMSG_FIRSTHDR(&msgh)))[i] =
fd;
395 iov[0].iov_base = (
void*)str.data();
396 iov[0].iov_len = str.size();
399 msgh.msg_name = NULL;
400 msgh.msg_namelen = 0;
405 int ret = sendmsg(fds[1], &msgh, 0);
409 throw std::system_error(e, std::generic_category(),
"Write with buffer failed for " + str);
412 if ((
unsigned)ret < str.size())
414 throw std::runtime_error(
"Input closed while buffer sending " + str);
421 bufferPtrs[0] = &buffer;
422 bufferPtrs[1] =
nullptr;
423 send(str, bufferPtrs);
std::string parseXmlFragment(std::function< char()> readchar)
void allowBufferReceive(bool state)
void expectBuffer(SharedBuffer &fd)
void shutdown(bool rd, bool wr)
void expect(const std::string &content)
void send(const std::string &content)
void setFds(int rd, int wr)
void expectXml(const std::string &xml)
std::string expectBase64()
std::vector< uint8_t > buffer