File indexing completed on 2024-05-12 17:15:22

0001 /*
0002    Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LGPL.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 
0019    Alternatively, this file is available under the Mozilla Public License
0020    Version 1.1.  You may obtain a copy of the License at
0021    http://www.mozilla.org/MPL/
0022 */
0023 
0024 #include "arguments.h"
0025 #include "connectaddress.h"
0026 #include "error.h"
0027 #include "eventdispatcher.h"
0028 #include "imessagereceiver.h"
0029 #include "message.h"
0030 #include "pendingreply.h"
0031 #include "testutil.h"
0032 #include "connection.h"
0033 
0034 #include <fcntl.h>
0035 #include <unistd.h>
0036 
0037 #include <cstring>
0038 #include <iostream>
0039 
0040 static void test_signatureHeader()
0041 {
0042     Message msg;
0043     Arguments::Writer writer;
0044     writer.writeByte(123);
0045     writer.writeUint64(1);
0046     msg.setArguments(writer.finish());
0047     TEST(msg.signature() == "yt");
0048 }
0049 
0050 class PrintAndTerminateClient : public IMessageReceiver
0051 {
0052 public:
0053     void handleSpontaneousMessageReceived(Message msg, Connection *connection) override
0054     {
0055         std::cout << msg.prettyPrint();
0056         connection->eventDispatcher()->interrupt();
0057     }
0058 };
0059 
0060 class PrintAndReplyClient : public IMessageReceiver
0061 {
0062 public:
0063     void handleSpontaneousMessageReceived(Message msg, Connection *connection) override
0064     {
0065         std::cout << msg.prettyPrint();
0066         connection->sendNoReply(Message::createErrorReplyTo(msg, "Unable to get out of hammock!"));
0067         //connection->eventDispatcher()->interrupt();
0068     }
0069 };
0070 
0071 // used during implementation, is supposed to not crash and be valgrind-clean afterwards
0072 void testBasic(const ConnectAddress &clientAddress)
0073 {
0074     EventDispatcher dispatcher;
0075 
0076     ConnectAddress serverAddress = clientAddress;
0077     serverAddress.setRole(ConnectAddress::Role::PeerServer);
0078 
0079     Connection serverConnection(&dispatcher, serverAddress);
0080     std::cout << "Created server connection. " << &serverConnection << std::endl;
0081     Connection clientConnection(&dispatcher, clientAddress);
0082     std::cout << "Created client connection. " << &clientConnection << std::endl;
0083 
0084     PrintAndReplyClient printAndReplyClient;
0085     serverConnection.setSpontaneousMessageReceiver(&printAndReplyClient);
0086 
0087     PrintAndTerminateClient printAndTerminateClient;
0088     clientConnection.setSpontaneousMessageReceiver(&printAndTerminateClient);
0089 
0090     Message msg = Message::createCall("/foo", "org.foo.interface", "laze");
0091     Arguments::Writer writer;
0092     writer.writeString("couch");
0093     msg.setArguments(writer.finish());
0094 
0095     clientConnection.sendNoReply(std::move(msg));
0096 
0097     while (dispatcher.poll()) {
0098     }
0099 }
0100 
0101 void testMessageLength()
0102 {
0103     static const uint32 bufferSize = Arguments::MaxArrayLength + 1024;
0104     byte *buffer = static_cast<byte *>(malloc(bufferSize));
0105     memset(buffer, 0, bufferSize);
0106     for (int i = 0; i < 2; i++) {
0107         const bool makeTooLong = i == 1;
0108 
0109         Arguments::Writer writer;
0110         writer.writePrimitiveArray(Arguments::Byte, chunk(buffer, Arguments::MaxArrayLength));
0111 
0112         // Our minimal Message is going to have the following variable headers (in that order):
0113         // Array: 4 byte length prefix
0114         // PathHeader: 4 byte length prefix
0115         // MethodHeader: 4 byte length prefix
0116         // SignatureHeader: 1 byte length prefix
0117 
0118         // This is VERY tedious to calculate, so let's just take it as an experimentally determined value
0119         uint32 left = Arguments::MaxMessageLength - Arguments::MaxArrayLength - 72;
0120         if (makeTooLong) {
0121             left += 1;
0122         }
0123         writer.writePrimitiveArray(Arguments::Byte, chunk(buffer, left));
0124 
0125         Message msg = Message::createCall("/a", "x");
0126         msg.setSerial(1);
0127         msg.setArguments(writer.finish());
0128         std::vector<byte> saved = msg.save();
0129         TEST(msg.error().isError() == makeTooLong);
0130     }
0131 }
0132 
0133 enum {
0134     // a small integer could be confused with an index into the fd array (in the implementation),
0135     // so make it large
0136     DummyFdOffset = 1000000
0137 };
0138 
0139 #ifdef __unix__
0140 static Arguments createArgumentsWithDummyFileDescriptors(uint fdCount)
0141 {
0142     Arguments::Writer writer;
0143     for (uint i = 0; i < fdCount; i++) {
0144         writer.writeUnixFd(DummyFdOffset - i);
0145     }
0146     return writer.finish();
0147 }
0148 
0149 void testFileDescriptorsInArguments()
0150 {
0151     // Note: This replaces round-trip tests with file descriptors in tst_arguments.
0152     // A full roundtrip test must go through Message due to the out-of-band way that file
0153     // descriptors are stored (which is so because they are also transmitted out-of-band).
0154     Message msg = Message::createCall("/foo", "org.foo.interface", "doNothing");
0155     for (uint i = 0; i < 4; i++) {
0156         msg.setArguments(createArgumentsWithDummyFileDescriptors(i));
0157         {
0158             // const ref to arguments
0159             const Arguments &args = msg.arguments();
0160             Arguments::Reader reader(args);
0161             for (uint j = 0; j < i; j++) {
0162                 TEST(reader.readUnixFd() == int(DummyFdOffset - j));
0163                 TEST(reader.isValid());
0164             }
0165             TEST(reader.isFinished());
0166         }
0167         {
0168             // copy of arguments
0169             Arguments args = msg.arguments();
0170             Arguments::Reader reader(args);
0171             for (uint j = 0; j < i; j++) {
0172                 TEST(reader.readUnixFd() == int(DummyFdOffset - j));
0173                 TEST(reader.isValid());
0174             }
0175             TEST(reader.isFinished());
0176         }
0177     }
0178 }
0179 
0180 void testTooManyFileDescriptors()
0181 {
0182     // TODO re-think what is the best place to catch too many file descriptors...
0183     Arguments::Writer writer;
0184 }
0185 
0186 void testFileDescriptorsHeader()
0187 {
0188     Message msg = Message::createCall("/foo", "org.foo.interface", "doNothing");
0189     for (uint i = 0; i < 4; i++) {
0190         msg.setArguments(createArgumentsWithDummyFileDescriptors(i));
0191         TEST(msg.unixFdCount() == i);
0192     }
0193 }
0194 
0195 enum {
0196     // for pipe2() file descriptor array
0197     ReadSide = 0,
0198     WriteSide = 1,
0199     // how many file descriptors to send in test
0200     FdCountToSend = 10
0201 };
0202 
0203 class FileDescriptorTestReceiver : public IMessageReceiver
0204 {
0205 public:
0206     void handleSpontaneousMessageReceived(Message msg, Connection *connection) override
0207     {
0208         // we're on the session bus, so we'll receive all kinds of notifications we don't care about here
0209         if (msg.type() != Message::MethodCallMessage
0210             || msg.method() != "testFileDescriptorsForDataTransfer") {
0211             return;
0212         }
0213 
0214         Arguments::Reader reader(msg.arguments());
0215         for (uint i = 0; i < FdCountToSend; i++) {
0216             int fd = reader.readUnixFd();
0217             uint readBuf = 12345;
0218             ::read(fd, &readBuf, sizeof(uint));
0219             ::close(fd);
0220             TEST(readBuf == i);
0221         }
0222         Message reply = Message::createReplyTo(msg);
0223         connection->sendNoReply(std::move(reply));
0224     }
0225 };
0226 
0227 void testFileDescriptorsForDataTransfer()
0228 {
0229     EventDispatcher eventDispatcher;
0230     Connection conn(&eventDispatcher, ConnectAddress::StandardBus::Session);
0231     conn.waitForConnectionEstablished();
0232     TEST(conn.isConnected());
0233 
0234     int pipeFds[2 * FdCountToSend];
0235 
0236     Message msg = Message::createCall("/foo", "org.foo.interface", "testFileDescriptorsForDataTransfer");
0237     msg.setDestination(conn.uniqueName());
0238 
0239     Arguments::Writer writer;
0240     for (uint i = 0; i < FdCountToSend; i++) {
0241         TEST(pipe2(pipeFds + 2 * i, O_NONBLOCK) == 0);
0242         // write into write side of the pipe... will be read when the message is received back from bus
0243         ::write(pipeFds[2 * i + WriteSide], &i, sizeof(uint));
0244 
0245         writer.writeUnixFd(pipeFds[2 * i + ReadSide]);
0246     }
0247 
0248     msg.setArguments(writer.finish());
0249 
0250     PendingReply reply = conn.send(std::move(msg), 500 /* fail quickly */);
0251     FileDescriptorTestReceiver fdTestReceiver;
0252     conn.setSpontaneousMessageReceiver(&fdTestReceiver);
0253 
0254     while (!reply.isFinished()) {
0255         eventDispatcher.poll();
0256     }
0257 
0258     if (conn.supportedFileDescriptorsPerMessage() >= FdCountToSend) {
0259         TEST(reply.hasNonErrorReply()); // otherwise timeout, the message exchange failed somehow
0260     } else {
0261         TEST(!reply.hasNonErrorReply());
0262         TEST(reply.error().code() == Error::SendingTooManyUnixFds);
0263         for (uint i = 0; i < FdCountToSend; i++) {
0264             ::close(pipeFds[2 * i + ReadSide]);
0265         }
0266     }
0267 
0268     for (uint i = 0; i < FdCountToSend; i++) {
0269         ::close(pipeFds[2 * i + WriteSide]);
0270     }
0271 }
0272 #endif
0273 
0274 void testAssignment()
0275 {
0276     Message msg1 = Message::createCall("/foo", "org.foo.bar", "someMethod");
0277     msg1.setSender("sender1");
0278     Message msg2 = Message::createSignal("/bar", "org.xyz.abc", "thingHappened");
0279     msg2.setReplySerial(1234);
0280 
0281     msg2 = msg1;
0282     TEST(msg2.type() == Message::MethodCallMessage);
0283     TEST(msg2.path() == "/foo");
0284     TEST(msg2.interface() == "org.foo.bar");
0285     TEST(msg2.method() == "someMethod");
0286     TEST(msg2.sender() == "sender1");
0287     TEST(msg2.replySerial() == 0);
0288 }
0289 
0290 int main(int, char *[])
0291 {
0292     test_signatureHeader();
0293 #ifdef __linux__
0294     {
0295         ConnectAddress clientAddress;
0296         clientAddress.setType(ConnectAddress::Type::AbstractUnixPath);
0297         clientAddress.setRole(ConnectAddress::Role::PeerClient);
0298         clientAddress.setPath("dferry.Test.Message");
0299         testBasic(clientAddress);
0300     }
0301 #endif
0302     // TODO: SocketType::Unix works on any Unix-compatible OS, but we'll need to construct a path
0303     {
0304         ConnectAddress clientAddress;
0305         clientAddress.setType(ConnectAddress::Type::Tcp);
0306         clientAddress.setPort(6800);
0307         clientAddress.setRole(ConnectAddress::Role::PeerClient);
0308         testBasic(clientAddress);
0309     }
0310 
0311     testMessageLength();
0312 
0313 #ifdef __unix__
0314     testFileDescriptorsInArguments();
0315     testTooManyFileDescriptors();
0316     testFileDescriptorsHeader();
0317     testFileDescriptorsForDataTransfer();
0318 #endif
0319     testAssignment();
0320 
0321     // TODO testSaveLoad();
0322     // TODO testDeepCopy();
0323     std::cout << "\nNote that the hammock error is part of the test.\nPassed!\n";
0324 }