Add asynchronous socket 24/27524/28
authorMarcin Niesluchowski <m.niesluchow@samsung.com>
Mon, 15 Sep 2014 09:42:42 +0000 (11:42 +0200)
committerMarcin Niesluchowski <m.niesluchow@samsung.com>
Mon, 13 Oct 2014 17:12:45 +0000 (19:12 +0200)
Change-Id: I1ca062fee144b8244fba88ae3155096df043c61e

13 files changed:
src/admin/logic/Logic.cpp
src/admin/logic/Logic.h
src/client-async/CMakeLists.txt
src/client-async/sockets/SocketClientAsync.cpp [new file with mode: 0644]
src/client-async/sockets/SocketClientAsync.h [new file with mode: 0644]
src/client-common/exceptions/TryCatch.h
src/client/logic/Logic.cpp
src/client/logic/Logic.h
src/common/exceptions/NoMemoryException.h [moved from src/common/exceptions/ServerConnectionErrorException.h with 52% similarity]
src/common/sockets/Socket.cpp
src/common/sockets/Socket.h
src/common/sockets/SocketClient.cpp
src/common/sockets/SocketClient.h

index 89c2d2c..6906056 100644 (file)
@@ -25,7 +25,8 @@
 
 #include <cynara-admin-error.h>
 #include <common.h>
-#include <exceptions/ServerConnectionErrorException.h>
+#include <exceptions/Exception.h>
+#include <exceptions/UnexpectedErrorException.h>
 #include <log/log.h>
 #include <protocol/Protocol.h>
 #include <protocol/ProtocolAdmin.h>
@@ -55,19 +56,30 @@ ProtocolFrameSequenceNumber generateSequenceNumber(void) {
     return ++sequenceNumber;
 }
 
+bool Logic::ensureConnection(void) {
+    return m_socketClient->isConnected() || m_socketClient->connect();
+}
+
 template<typename T, typename... Args>
 int Logic::askCynaraAndInterpreteCodeResponse(Args... args) {
-    ProtocolFrameSequenceNumber sequenceNumber = generateSequenceNumber();
-
-    //Ask cynara service
-    CodeResponsePtr codeResponse;
     try {
-        RequestPtr request = std::make_shared<T>(args..., sequenceNumber);
-        ResponsePtr response = m_socketClient->askCynaraServer(request);
-        if (!response) {
-            LOGW("Disconnected by cynara server.");
+        if (!ensureConnection()) {
+            LOGE("Cannot connect to cynara. Service not available.");
             return CYNARA_ADMIN_API_SERVICE_NOT_AVAILABLE;
         }
+
+        ProtocolFrameSequenceNumber sequenceNumber = generateSequenceNumber();
+
+        //Ask cynara service
+        CodeResponsePtr codeResponse;
+
+        RequestPtr request = std::make_shared<T>(args..., sequenceNumber);
+        ResponsePtr response;
+        while (!(response = m_socketClient->askCynaraServer(request))) {
+            if (!m_socketClient->connect())
+                return CYNARA_ADMIN_API_SERVICE_NOT_AVAILABLE;
+        }
+
         codeResponse = std::dynamic_pointer_cast<CodeResponse>(response);
         if (!codeResponse) {
             LOGC("Critical error. Casting Response to CodeResponse failed.");
@@ -90,9 +102,6 @@ int Logic::askCynaraAndInterpreteCodeResponse(Args... args) {
                      static_cast<int>(codeResponse->m_code));
                 return CYNARA_ADMIN_API_UNEXPECTED_CLIENT_ERROR;
         }
-    } catch (const ServerConnectionErrorException &ex) {
-        LOGE("Cynara service not available.");
-        return CYNARA_ADMIN_API_SERVICE_NOT_AVAILABLE;
     } catch (const std::bad_alloc &ex) {
         LOGE("Cynara admin client out of memory.");
         return CYNARA_ADMIN_API_OUT_OF_MEMORY;
@@ -118,19 +127,25 @@ int Logic::removeBucket(const PolicyBucketId &bucket) noexcept {
 
 int Logic::adminCheck(const PolicyBucketId &startBucket, bool recursive, const PolicyKey &key,
                       PolicyResult &result) noexcept {
+    try {
+        if (!ensureConnection()) {
+            LOGE("Cannot connect to cynara. Service not available.");
+            return CYNARA_ADMIN_API_SERVICE_NOT_AVAILABLE;
+        }
 
-    ProtocolFrameSequenceNumber sequenceNumber = generateSequenceNumber();
+        ProtocolFrameSequenceNumber sequenceNumber = generateSequenceNumber();
+
+        //Ask cynara service
+        CheckResponsePtr checkResponse;
 
-    //Ask cynara service
-    CheckResponsePtr checkResponse;
-    try {
         RequestPtr request = std::make_shared<AdminCheckRequest>(key, startBucket, recursive,
                                                                  sequenceNumber);
-        ResponsePtr response = m_socketClient->askCynaraServer(request);
-        if (!response) {
-            LOGW("Disconnected by cynara server.");
-            return CYNARA_ADMIN_API_SERVICE_NOT_AVAILABLE;
+        ResponsePtr response;
+        while (!(response = m_socketClient->askCynaraServer(request))) {
+            if (!m_socketClient->connect())
+                return CYNARA_ADMIN_API_SERVICE_NOT_AVAILABLE;
         }
+
         checkResponse = std::dynamic_pointer_cast<CheckResponse>(response);
         if (!checkResponse) {
             LOGC("Casting Response to CheckResponse failed.");
@@ -140,9 +155,12 @@ int Logic::adminCheck(const PolicyBucketId &startBucket, bool recursive, const P
         LOGD("checkResponse: policyType [%" PRIu16 "], metadata <%s>",
              checkResponse->m_resultRef.policyType(),
              checkResponse->m_resultRef.metadata().c_str());
-    } catch (const ServerConnectionErrorException &ex) {
-        LOGE("Cynara service not available.");
-        return CYNARA_ADMIN_API_SERVICE_NOT_AVAILABLE;
+
+        result = checkResponse->m_resultRef;
+        return CYNARA_ADMIN_API_SUCCESS;
+    } catch (const UnexpectedErrorException &ex) {
+        LOGE(ex.what());
+        return CYNARA_ADMIN_API_UNEXPECTED_CLIENT_ERROR;
     } catch (const std::bad_alloc &ex) {
         LOGE("Cynara admin client out of memory.");
         return CYNARA_ADMIN_API_OUT_OF_MEMORY;
@@ -150,9 +168,6 @@ int Logic::adminCheck(const PolicyBucketId &startBucket, bool recursive, const P
         LOGE("Unexpected client error: <%s>", ex.what());
         return CYNARA_ADMIN_API_UNEXPECTED_CLIENT_ERROR;
     }
-
-    result = checkResponse->m_resultRef;
-    return CYNARA_ADMIN_API_SUCCESS;
 }
 
 } // namespace Cynara
index 8a274ff..527e992 100644 (file)
@@ -35,6 +35,7 @@ class Logic : public ApiInterface {
 private:
     SocketClientPtr m_socketClient;
 
+    bool ensureConnection(void);
     template<typename T, typename... Args>
     int askCynaraAndInterpreteCodeResponse(Args... args);
 
index 3a4ce35..1326f8d 100644 (file)
@@ -31,6 +31,7 @@ INCLUDE_DIRECTORIES(
 SET(LIB_CYNARA_ASYNC_SOURCES
     ${CYNARA_LIB_CYNARA_ASYNC_PATH}/api/client-async-api.cpp
     ${CYNARA_LIB_CYNARA_ASYNC_PATH}/logic/Logic.cpp
+    ${CYNARA_LIB_CYNARA_ASYNC_PATH}/sockets/SocketClientAsync.cpp
     )
 
 ADD_LIBRARY(${TARGET_LIB_CYNARA_ASYNC} SHARED ${LIB_CYNARA_ASYNC_SOURCES})
diff --git a/src/client-async/sockets/SocketClientAsync.cpp b/src/client-async/sockets/SocketClientAsync.cpp
new file mode 100644 (file)
index 0000000..e3ffbd4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/**
+ * @file        src/client-async/sockets/SocketClientAsync.cpp
+ * @author      Marcin Niesluchowski <m.niesluchow@samsung.com>
+ * @version     1.0
+ * @brief       This file contains definition of cynara's socket asynchronous
+ *              client
+ */
+
+#include <request/Request.h>
+#include <request/RequestContext.h>
+
+#include "SocketClientAsync.h"
+
+namespace Cynara {
+
+SocketClientAsync::SocketClientAsync(const std::string &socketPath, ProtocolPtr protocol)
+    : m_socket(socketPath, 0), m_protocol(protocol) {
+}
+
+Socket::ConnectionStatus SocketClientAsync::connect(void) {
+    return m_socket.connect();
+}
+
+Socket::ConnectionStatus SocketClientAsync::completeConnection(void) {
+    return m_socket.completeConnection();
+}
+
+int SocketClientAsync::getSockFd(void) {
+    return m_socket.getSockFd();
+}
+
+bool SocketClientAsync::isConnected(void) {
+    return m_socket.isConnected();
+}
+
+void SocketClientAsync::appendRequest(RequestPtr request) {
+    RequestContextPtr context = std::make_shared<RequestContext>(ResponseTakerPtr(), m_writeQueue);
+    request->execute(request, m_protocol, context);
+}
+
+bool SocketClientAsync::isDataToSend(void) {
+    return m_socket.isDataToSend() || !m_writeQueue.empty();
+}
+
+Socket::SendStatus SocketClientAsync::sendToCynara(void) {
+    return m_socket.sendToServer(m_writeQueue);
+}
+
+bool SocketClientAsync::receiveFromCynara(void) {
+    return m_socket.receiveFromServer(m_readQueue);
+}
+
+ResponsePtr SocketClientAsync::getResponse(void) {
+    return m_protocol->extractResponseFromBuffer(m_readQueue);
+}
+
+} // namespace Cynara
diff --git a/src/client-async/sockets/SocketClientAsync.h b/src/client-async/sockets/SocketClientAsync.h
new file mode 100644 (file)
index 0000000..5fb0543
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/**
+ * @file        src/client-async/sockets/SocketClientAsync.h
+ * @author      Marcin Niesluchowski <m.niesluchow@samsung.com>
+ * @version     1.0
+ * @brief       This file contains declaration of cynara's socket asynchronous
+                client
+ */
+
+#ifndef SRC_CLIENT_ASYNC_SOCKETS_SOCKETCLIENTASYNC_H_
+#define SRC_CLIENT_ASYNC_SOCKETS_SOCKETCLIENTASYNC_H_
+
+#include <memory>
+#include <string>
+
+#include <containers/BinaryQueue.h>
+#include <protocol/Protocol.h>
+#include <request/pointers.h>
+#include <response/pointers.h>
+#include <sockets/Socket.h>
+
+namespace Cynara {
+
+class SocketClientAsync;
+typedef std::shared_ptr<SocketClientAsync> SocketClientAsyncPtr;
+
+class SocketClientAsync {
+public:
+    SocketClientAsync(const std::string &socketPath, ProtocolPtr protocol);
+    ~SocketClientAsync() {};
+
+    Socket::ConnectionStatus connect(void);
+    Socket::ConnectionStatus completeConnection(void);
+    int getSockFd(void);
+    bool isConnected(void);
+    void appendRequest(RequestPtr request);
+    bool isDataToSend(void);
+    Socket::SendStatus sendToCynara(void);
+    bool receiveFromCynara(void);
+    ResponsePtr getResponse(void);
+
+private:
+    Socket m_socket;
+    ProtocolPtr m_protocol;
+    BinaryQueue m_readQueue;
+    BinaryQueue m_writeQueue;
+};
+
+} // namespace Cynara
+
+#endif /* SRC_CLIENT_ASYNC_SOCKETS_SOCKETCLIENTASYNC_H_ */
index 8d1fb20..6dc4d0b 100644 (file)
@@ -27,6 +27,7 @@
 #include <functional>
 #include <new>
 
+#include <exceptions/NoMemoryException.h>
 #include <log/log.h>
 
 #include <cynara-client-error.h>
@@ -39,6 +40,9 @@ int tryCatch(const std::function<int(void)> &func) {
     } catch (const std::bad_alloc &e) {
         LOGE(e.what());
         return CYNARA_API_OUT_OF_MEMORY;
+    } catch (const NoMemoryException &e) {
+        LOGE(e.what());
+        return CYNARA_API_OUT_OF_MEMORY;
     } catch (const std::exception &e) {
         LOGE(e.what());
         return CYNARA_API_UNKNOWN_ERROR;
index d856042..f375e9e 100644 (file)
@@ -25,7 +25,8 @@
 #include <cache/CapacityCache.h>
 #include <common.h>
 #include <cynara-client-error.h>
-#include <exceptions/ServerConnectionErrorException.h>
+#include <exceptions/Exception.h>
+#include <exceptions/UnexpectedErrorException.h>
 #include <log/log.h>
 #include <plugins/NaiveInterpreter.h>
 #include <protocol/Protocol.h>
@@ -56,14 +57,12 @@ Logic::Logic() {
 }
 
 int Logic::check(const std::string &client, const ClientSession &session, const std::string &user,
-                 const std::string &privilege)
-{
-    if (!m_socket->isConnected()){
-        onDisconnected();
-    }
+                 const std::string &privilege) {
+    if (!ensureConnection())
+        return CYNARA_API_SERVICE_NOT_AVAILABLE;
 
     PolicyKey key(client, user, privilege);
-    auto ret = m_cache->get(session, key);
+    int ret = m_cache->get(session, key);
     //Any other situation than cache miss
     if (ret != CYNARA_API_CACHE_MISS) {
         return ret;
@@ -80,32 +79,39 @@ int Logic::check(const std::string &client, const ClientSession &session, const
     return m_cache->update(session, key, result);
 }
 
+bool Logic::ensureConnection(void) {
+    if (m_socket->isConnected())
+        return true;
+    onDisconnected();
+    if (m_socket->connect())
+        return true;
+    LOGW("Cannot connect to cynara. Service not available.");
+    return false;
+}
+
 int Logic::requestResult(const PolicyKey &key, PolicyResult &result) {
     ProtocolFrameSequenceNumber sequenceNumber = generateSequenceNumber();
 
     //Ask cynara service
     CheckResponsePtr checkResponse;
-    try {
-        RequestPtr request = std::make_shared<CheckRequest>(key, sequenceNumber);
-        ResponsePtr response = m_socket->askCynaraServer(request);
-        if (!response) {
-            LOGW("Disconnected by cynara server.");
+    RequestPtr request = std::make_shared<CheckRequest>(key, sequenceNumber);
+    ResponsePtr response;
+    while (!(response = m_socket->askCynaraServer(request))) {
+        onDisconnected();
+        if (!m_socket->connect())
             return CYNARA_API_SERVICE_NOT_AVAILABLE;
-        }
-        checkResponse = std::dynamic_pointer_cast<CheckResponse>(response);
-        if (!checkResponse) {
-            LOGC("Critical error. Casting Response to CheckResponse failed.");
-            return CYNARA_API_ACCESS_DENIED;
-        }
-
-        LOGD("checkResponse: policyType = %" PRIu16 ", metadata = %s",
-             checkResponse->m_resultRef.policyType(),
-             checkResponse->m_resultRef.metadata().c_str());
-    } catch (const ServerConnectionErrorException &ex) {
-        LOGE("Cynara service not available.");
-        return CYNARA_API_SERVICE_NOT_AVAILABLE;
     }
 
+    checkResponse = std::dynamic_pointer_cast<CheckResponse>(response);
+    if (!checkResponse) {
+        LOGC("Critical error. Casting Response to CheckResponse failed.");
+        return CYNARA_API_ACCESS_DENIED;
+    }
+
+    LOGD("checkResponse: policyType = %" PRIu16 ", metadata = %s",
+         checkResponse->m_resultRef.policyType(),
+         checkResponse->m_resultRef.metadata().c_str());
+
     result = checkResponse->m_resultRef;
     return CYNARA_API_SUCCESS;
 }
index 36076fc..c72309c 100644 (file)
@@ -40,6 +40,7 @@ private:
     PluginCachePtr m_cache;
 
     void onDisconnected(void);
+    bool ensureConnection(void);
     int requestResult(const PolicyKey &key, PolicyResult &result);
 public:
     Logic();
  *    limitations under the License.
  */
 /**
- * @file        src/common/exceptions/ServerConnectionErrorException.h
- * @author      Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @file        src/common/exceptions/NoMemoryException.h
+ * @author      Marcin Niesluchowski <m.niesluchow@samsung.com>
  * @version     1.0
- * @brief       Implementation of ServerConnectionErrorException
+ * @brief       Implementation of NoMemoryException
  */
 
-#ifndef SRC_COMMON_EXCEPTIONS_SERVERCONNECTIONERROREXCEPTION_H_
-#define SRC_COMMON_EXCEPTIONS_SERVERCONNECTIONERROREXCEPTION_H_
+#ifndef SRC_COMMON_EXCEPTIONS_NOMEMORYEXCEPTION_H_
+#define SRC_COMMON_EXCEPTIONS_NOMEMORYEXCEPTION_H_
 
-#include "Exception.h"
+#include <sstream>
+#include <string>
 
-#include <exception>
+#include "Exception.h"
 
 namespace Cynara {
 
-class ServerConnectionErrorException : public Exception {
+class NoMemoryException : public Exception {
 public:
-    ServerConnectionErrorException() = default;
-    virtual ~ServerConnectionErrorException() noexcept {};
+    NoMemoryException() = delete;
+    NoMemoryException(const std::string &errorMsg) {
+        m_whatMessage = "NoMemoryException with message <" + errorMsg + ">";
+    }
+
+    virtual ~NoMemoryException() noexcept {};
+
     virtual const std::string message(void) const {
-        return "ServerConnectionError";
+        return m_whatMessage;
     }
+
+private:
+    std::string m_whatMessage;
 };
 
-} /* namespace Cynara */
+} // namespace Cynara
 
-#endif /* SRC_COMMON_EXCEPTIONS_SERVERCONNECTIONERROREXCEPTION_H_ */
+#endif /* SRC_COMMON_EXCEPTIONS_NOMEMORYEXCEPTION_H_ */
index e3b91dc..0c33a7e 100644 (file)
 #include <sys/un.h>
 #include <unistd.h>
 
-#include <containers/BinaryQueue.h>
-#include <containers/RawBuffer.h>
 #include <exceptions/InitException.h>
-#include <exceptions/ServerConnectionErrorException.h>
+#include <exceptions/NoMemoryException.h>
 #include <exceptions/UnexpectedErrorException.h>
 #include <log/log.h>
 
@@ -42,8 +40,9 @@
 
 namespace Cynara {
 
-Socket::Socket(const std::string &socketPath, int timeoutMiliseconds) : m_sock(-1),
-    m_socketPath(socketPath), m_pollTimeout(timeoutMiliseconds) {
+Socket::Socket(const std::string &socketPath, int timeoutMiliseconds)
+    : m_sock(-1), m_connectionInProgress(false), m_socketPath(socketPath),
+      m_pollTimeout(timeoutMiliseconds), m_sendBufferPos(0), m_sendBufferEnd(0) {
 }
 
 Socket::~Socket() {
@@ -54,6 +53,9 @@ void Socket::close(void) {
     if (m_sock > -1)
         ::close(m_sock);
     m_sock = -1;
+    m_sendBufferPos = 0;
+    m_sendBufferEnd = 0;
+    m_sendQueue.clear();
 }
 
 bool Socket::waitForSocket(int event) {
@@ -62,12 +64,14 @@ bool Socket::waitForSocket(int event) {
     desc[0].fd = m_sock;
     desc[0].events = event;
 
-    ret = TEMP_FAILURE_RETRY(poll(desc, 1, m_pollTimeout));
+    if (event != POLLHUP)
+        ret = TEMP_FAILURE_RETRY(poll(desc, 1, m_pollTimeout));
+    else
+        ret = TEMP_FAILURE_RETRY(poll(desc, 1, 0));
 
     if (ret == -1) {
         int err = errno;
         LOGE("'poll' function error [%d] : <%s>", err, strerror(err));
-        close();
         throw UnexpectedErrorException(err, strerror(err));
     } else if (ret == 0) {
         LOGD("Poll timeout");
@@ -82,7 +86,6 @@ int Socket::getSocketError(void) {
     int ret = getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &err, &len);
     if (ret < 0) {
         int err = errno;
-        close();
         LOGE("'getsockopt' function error [%d] : <%s>", err, strerror(err));
         throw UnexpectedErrorException(err, strerror(err));
     }
@@ -90,26 +93,21 @@ int Socket::getSocketError(void) {
 }
 
 bool Socket::isConnected(void) {
+    if (m_connectionInProgress)
+        return true;
+
     if (m_sock < 0)
         return false;
 
-    if (getSocketError() != 0) {
-        close();
+    if (getSocketError() != 0)
         return false;
-    }
 
-    return true;
+    return !waitForSocket(POLLHUP);
 }
 
-bool Socket::connect(void) {
-    sockaddr_un clientAddr;
+void Socket::createSocket(void) {
     int flags;
 
-    if (isConnected())
-        return true;
-
-    close();
-
     m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
     if (m_sock < 0) {
         int err = errno;
@@ -125,6 +123,10 @@ bool Socket::connect(void) {
         LOGE("'fcntl' function error [%d] : <%s>", err, strerror(err));
         throw UnexpectedErrorException(err, strerror(err));
     }
+}
+
+Socket::ConnectionStatus Socket::connectSocket(void) {
+    sockaddr_un clientAddr;
 
     memset(&clientAddr, 0, sizeof(clientAddr));
 
@@ -145,111 +147,136 @@ bool Socket::connect(void) {
                                             SUN_LEN(&clientAddr)));
     if (retval == -1) {
         int err = errno;
-        if (err == EINPROGRESS) {
-            if (!waitForSocket(POLLOUT)) {
-                return false;
-            }
-            err = getSocketError();
+        switch (err) {
+            case EINPROGRESS:
+                m_connectionInProgress = true;
+                return ConnectionStatus::CONNECTION_IN_PROGRESS;
+            case ECONNREFUSED:
+                //no one is listening
+                return ConnectionStatus::CONNECTION_FAILED;
+            default:
+                close();
+                LOGE("'connect' function error [%d] : <%s>", err, strerror(err));
+                throw UnexpectedErrorException(err, strerror(err));
         }
-        if (err == ECONNREFUSED) {
-            //no one is listening
-            return false;
+    }
+    return ConnectionStatus::CONNECTION_SUCCEEDED;
+}
+
+Socket::SendStatus Socket::sendBuffer(void) {
+    while (m_sendBufferEnd != m_sendBufferPos) {
+        if (!waitForSocket(POLLOUT)) {
+            LOGD("No POLLOUT event");
+            return SendStatus::PARTIAL_DATA_SENT;
         }
-        close();
-        LOGE("'connect' function error [%d] : <%s>", err, strerror(err));
-        throw UnexpectedErrorException(err, strerror(err));
+
+        ssize_t t = TEMP_FAILURE_RETRY(send(m_sock, m_sendBuffer.data() + m_sendBufferPos,
+                                            m_sendBufferEnd - m_sendBufferPos, MSG_NOSIGNAL));
+        if (t == -1) {
+            int err = errno;
+            switch (err) {
+                case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+                case EWOULDBLOCK:
+#endif
+                    continue;
+                case ENOMEM:
+                    throw NoMemoryException("'send' function failed due to ENOMEM");
+                case EPIPE:
+                    LOGN("Connection closed by server");
+                    return SendStatus::CONNECTION_LOST;
+                default:
+                    LOGE("'send' function error [%d] : <%s>", err, strerror(err));
+                    throw UnexpectedErrorException(err, strerror(err));
+            }
+        }
+        m_sendBufferPos += static_cast<size_t>(t);
     }
+    return SendStatus::ALL_DATA_SENT;
+}
+
+Socket::ConnectionStatus Socket::connect(void) {
+    close();
+
+    createSocket();
 
-    return isConnected();
+    ConnectionStatus status = connectSocket();
+    if (status != ConnectionStatus::CONNECTION_SUCCEEDED)
+        return status;
+
+    return isConnected() ? ConnectionStatus::CONNECTION_SUCCEEDED
+                         : ConnectionStatus::CONNECTION_FAILED;
 }
 
-bool Socket::sendToServer(BinaryQueue &queue) {
-    bool retry = false;
+Socket::ConnectionStatus Socket::completeConnection(void) {
+    if (!m_connectionInProgress)
+        return ConnectionStatus::ALREADY_CONNECTED;
 
-    RawBuffer buffer(queue.size());
-    queue.flattenConsume(buffer.data(), queue.size());
+    if (!waitForSocket(POLLOUT))
+        return ConnectionStatus::CONNECTION_IN_PROGRESS;
 
-    do {
-        if (!connect()) {
-            LOGE("Error connecting to socket");
-            throw ServerConnectionErrorException();
-        }
+    m_connectionInProgress = false;
+    return isConnected() ? ConnectionStatus::CONNECTION_SUCCEEDED
+                         : ConnectionStatus::CONNECTION_FAILED;
+}
 
-        retry = false;
-        ssize_t done = 0;
-        while ((buffer.size() - done) > 0) {
-            if (! waitForSocket(POLLOUT)) {
-                LOGE("Error in poll(POLLOUT)");
-                throw ServerConnectionErrorException();
-            }
-            ssize_t t = TEMP_FAILURE_RETRY(send(m_sock, buffer.data() + done,
-                                           buffer.size() - done, MSG_NOSIGNAL));
-            if (t == -1) {
-                int err = errno;
-                if (err == EPIPE) {
-                    close();
-                    LOGN("Connection closed by server. Retrying to connect.");
-                    retry = true;
-                    break;
-                }
-                close();
-                LOGE("'write' function error [%d] : <%s>", err, strerror(err));
-                throw UnexpectedErrorException(err, strerror(err));
-            }
-            done += t;
-        }
-    } while (retry);
+int Socket::getSockFd(void) {
+    return m_sock;
+}
 
-    return true;
+bool Socket::isDataToSend(void) {
+    return !m_sendQueue.empty() || m_sendBufferEnd != 0;
 }
 
-bool Socket::waitAndReceiveFromServer(BinaryQueue &queue)
-{
-    if (!waitForSocket(POLLIN)) {
-        LOGE("Error in poll(POLLIN)");
-        throw ServerConnectionErrorException();
-    }
+Socket::SendStatus Socket::sendToServer(BinaryQueue &queue) {
+    m_sendQueue.appendMoveFrom(queue);
 
-    RawBuffer readBuffer(BUFSIZ);
-    ssize_t size = TEMP_FAILURE_RETRY(read(m_sock, readBuffer.data(), BUFSIZ));
+    SendStatus status = sendBuffer();
+    if (status != SendStatus::ALL_DATA_SENT)
+        return status;
 
-    if (size == -1) {
-        int err = errno;
-        LOGE("'read' function error [%d] : <%s>", err, strerror(err));
-        throw UnexpectedErrorException(err, strerror(err));
-    }
+    if (m_sendQueue.size() > m_sendBuffer.size())
+        m_sendBuffer.resize(m_sendQueue.size());
 
-    if (size == 0) {
-        LOGW("read return 0 / Connection closed by server.");
-        return false;
-    }
-    queue.appendCopy(readBuffer.data(), size);
+    m_sendBufferEnd = m_sendQueue.size();
+    m_sendBufferPos = 0;
+
+    m_sendQueue.flattenConsume(m_sendBuffer.data(), m_sendQueue.size());
 
-    return true;
+    return sendBuffer();
 }
 
-bool Socket::receiveFromServer(BinaryQueue &queue)
-{
-    RawBuffer readBuffer(BUFSIZ);
-    ssize_t size = TEMP_FAILURE_RETRY(read(m_sock, readBuffer.data(), BUFSIZ));
+bool Socket::receiveFromServer(BinaryQueue &queue) {
+    if (!waitForSocket(POLLIN)) {
+        LOGD("No POLLIN event");
+        return true;
+    }
 
-    if (size == -1) {
-        int err = errno;
-        if (err == EAGAIN) {
-            LOGD("is connected, but no data available");
-            return true;
+    RawBuffer buffer(BUFSIZ);
+    ssize_t size = 0;
+    while (true) {
+        size = TEMP_FAILURE_RETRY(read(m_sock, buffer.data(), BUFSIZ));
+        if (size == 0) {
+            LOGW("read return 0 / Connection closed by server.");
+            return false;
         }
-        LOGE("'read' function error [%d] : <%s>", err, strerror(err));
-        throw UnexpectedErrorException(err, strerror(err));
-    }
 
-    if (size == 0) {
-        LOGW("read return 0 / Connection closed by server.");
-        return false;
-    }
-    queue.appendCopy(readBuffer.data(), size);
+        if (size == -1) {
+            int err = errno;
+            switch (err) {
+                case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+                case EWOULDBLOCK:
+#endif
+                    return true;
+                default:
+                    LOGE("'read' function error [%d] : <%s>", err, strerror(err));
+                    throw UnexpectedErrorException(err, strerror(err));
+            }
+        }
 
-    return true;
+        queue.appendCopy(buffer.data(), static_cast<size_t>(size));
+    }
 }
 
 } // namespace Cynara
index 3c5c37b..0309f41 100644 (file)
@@ -17,6 +17,7 @@
  * @file        src/common/sockets/Socket.h
  * @author      Bartlomiej Grzelewski <b.grzelewski@samsung.com>
  * @author      Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @author      Marcin Niesluchowski <m.niesluchow@samsung.com>
  * @version     1.0
  * @brief       This file contains definition of UNIX client socket class
  */
 #include <string>
 
 #include <containers/BinaryQueue.h>
+#include <containers/RawBuffer.h>
 
 namespace Cynara {
 
 class Socket {
+public:
+    enum class ConnectionStatus {
+        ALREADY_CONNECTED,
+        CONNECTION_SUCCEEDED,
+        CONNECTION_IN_PROGRESS,
+        CONNECTION_FAILED
+    };
+
+    enum class SendStatus {
+        PARTIAL_DATA_SENT,
+        ALL_DATA_SENT,
+        CONNECTION_LOST
+    };
+
 private:
     int m_sock;
+    bool m_connectionInProgress;
 
     std::string m_socketPath;
     int m_pollTimeout;
 
+    RawBuffer m_sendBuffer;
+    size_t m_sendBufferPos;
+    size_t m_sendBufferEnd;
+    BinaryQueue m_sendQueue;
+
     void close(void);
 
     //returns true      if socket is ready
@@ -48,6 +70,21 @@ private:
     //throws            in critical situations
     int getSocketError(void);
 
+    //throws            in critical situations
+    void createSocket(void);
+
+    //returns ConnectionStatus::CONNECTION_SUCCEEDED           if connection succeeded
+    //returns ConnectionStatus::CONNECTION_IN_PROGRESS         if connection in progress
+    //returns ConnectionStatus::CONNECTION_FAILED              if connection failed
+    //throws                                                   in critical situations
+    ConnectionStatus connectSocket(void);
+
+    //returns SendStatus::PARTIAL_DATA_SENT         if no data to send is available
+    //returns SendStatus::ALL_DATA_SENT             if no additional data to send
+    //returns SendStatus::CONNECTION_LOST           if connection was lost
+    //throws                                        in critical situations
+    SendStatus sendBuffer(void);
+
 public:
     Socket(const std::string &socketPath, int timeoutMiliseconds = -1);
     ~Socket();
@@ -56,26 +93,36 @@ public:
     //throws            in critical situations
     bool isConnected(void);
 
-    //returns true      if connection succeeded
-    //returns false     if connection was timeout or no one is listening
-    //throws            in critical situations
-    bool connect(void);
-
-    //returns true                              if data was successfully send to server
-    //returns false                             if connection was lost
-    //throws ServerConnectionErrorException     if cannot connect server (or timeout)
-    //throws other exceptions                   in critical situations
-    bool sendToServer(BinaryQueue &queue);
-
-    //returns true                              if data was successfully read from server
-    //returns false                             if connection was lost
-    //throws ServerConnectionErrorException     if cannot connect server (or timeout)
-    //throws other exceptions                   in critical situations
-    bool waitAndReceiveFromServer(BinaryQueue &queue);
+    //returns ConnectionStatus::CONNECTION_SUCCEEDED           if connection succeeded
+    //returns ConnectionStatus::CONNECTION_IN_PROGRESS         if connection in progress
+    //returns ConnectionStatus::CONNECTION_FAILED              if connection failed
+    //throws                                                   in critical situations
+    ConnectionStatus connect(void);
+
+    //returns ConnectionStatus::ALREADY_CONNECTED              if was already connected
+    //returns ConnectionStatus::CONNECTION_SUCCEEDED           if connection succeeded
+    //returns ConnectionStatus::CONNECTION_IN_PROGRESS         if connection in progress
+    //returns ConnectionStatus::CONNECTION_FAILED              if connection failed
+    //throws                                                   in critical situations
+    ConnectionStatus completeConnection(void);
+
+    //returns socket descriptor
+    //returns -1                if socket descriptor no present
+    int getSockFd(void);
+
+    //returns true          There is still data to send
+    //returns false         No data to send
+    bool isDataToSend(void);
+
+    //returns SendStatus::PARTIAL_DATA_SENT         if no all data sent
+    //returns SendStatus::ALL_DATA_SENT             if all data was sent
+    //returns SendStatus::CONNECTION_LOST           if connection was lost
+    //throws                                        in critical situations
+    SendStatus sendToServer(BinaryQueue &queue);
 
     //returns true                              if data was successfully read from server
     //returns false                             if connection was lost
-    //throws other exceptions                   in critical situations
+    //throws                                    in critical situations
     bool receiveFromServer(BinaryQueue &queue);
 };
 
index a23c438..6db91d6 100644 (file)
@@ -39,21 +39,40 @@ SocketClient::SocketClient(const std::string &socketPath, ProtocolPtr protocol)
         : m_socket(socketPath), m_protocol(protocol) {
 }
 
+bool SocketClient::connect(void) {
+    switch (m_socket.connect()) {
+        case Socket::ConnectionStatus::CONNECTION_FAILED:
+            LOGW("Error connecting to Cynara. Service not available.");
+            return false;
+        case Socket::ConnectionStatus::CONNECTION_IN_PROGRESS:
+            if (m_socket.completeConnection() == Socket::ConnectionStatus::CONNECTION_FAILED) {
+                LOGW("Error connecting to Cynara. Service not available.");
+                return false;
+            }
+        default:
+            return true;
+    }
+}
+
+bool SocketClient::isConnected(void) {
+    return m_socket.isConnected();
+}
+
 ResponsePtr SocketClient::askCynaraServer(RequestPtr request) {
     //pass request to protocol
     RequestContextPtr context = std::make_shared<RequestContext>(ResponseTakerPtr(), m_writeQueue);
     request->execute(request, m_protocol, context);
 
     //send request to cynara
-    if (!m_socket.sendToServer(m_writeQueue)) {
-        LOGW("Error sending request to Cynara. Service not available.");
+    if (m_socket.sendToServer(m_writeQueue) == Socket::SendStatus::CONNECTION_LOST) {
+        LOGW("Disconnected while sending request to Cynara.");
         return nullptr;
     }
 
     // receive response from cynara
     while (true) {
-        if (!m_socket.waitAndReceiveFromServer(m_readQueue)) {
-            LOGW("Error receiving response from Cynara. Service not available.");
+        if (!m_socket.receiveFromServer(m_readQueue)) {
+            LOGW("Disconnected while receiving response from Cynara.");
             return nullptr;
         }
         ResponsePtr response = m_protocol->extractResponseFromBuffer(m_readQueue);
@@ -63,8 +82,4 @@ ResponsePtr SocketClient::askCynaraServer(RequestPtr request) {
     }
 }
 
-bool SocketClient::isConnected(void) {
-    return m_socket.isConnected() && m_socket.receiveFromServer(m_readQueue);
-}
-
 } // namespace Cynara
index a96107e..9f9737f 100644 (file)
@@ -48,11 +48,12 @@ public:
     SocketClient(const std::string &socketPath, ProtocolPtr protocol);
     virtual ~SocketClient() {};
 
+    bool connect(void);
+    bool isConnected(void);
+
     //returns pointer to response
     //        or nullptr when connection to cynara service is lost
     ResponsePtr askCynaraServer(RequestPtr request);
-
-    bool isConnected(void);
 };
 
 } // namespace Cynara