bool TSqlTransaction::begin(QSqlDatabase &database)
{
    if (!database.isValid()) {
        tSystemError("Can not begin transaction. Invalid database: %s", qPrintable(database.connectionName()));
        return false;
    }

    if (!enabled)
        return true;
    
    bool ok;
    int id = database.connectionName().left(2).toInt(&ok);
    
    if (!ok || id < 0 || id >= databases.count()) {
        tSystemError("Internal Error  [%s:%d]", __FILE__, __LINE__);
        return false;
    }
    
    if (databases[id].isValid()) {
        tSystemWarn("Has begun transaction already. database:%s", qPrintable(database.connectionName()));
        return true;
    }

    if (database.transaction()) {
        tQueryLog("[BEGIN] [databaseId:%d]", id);
    }

    databases[id] = database;
    return true;
}
QList<THttpRequest> TActionThread::readRequest(THttpSocket *socket)
{
    QList<THttpRequest> reqs;
    while (!socket->canReadRequest()) {
        // Check idle timeout
        if (socket->idleTime() >= 10) {
            tSystemWarn("Reading a socket timed out after 10 seconds. Descriptor:%d", (int)socket->socketDescriptor());
            break;
        }

        if (socket->socketDescriptor() <= 0) {
            tSystemWarn("Invalid descriptor (disconnected) : %d", (int)socket->socketDescriptor());
            break;
        }

        socket->waitForReadyRead(10);
    }

    if (!socket->canReadRequest()) {
        socket->abort();
    } else {
        reqs = socket->read();
    }

    return reqs;
}
TSendmailMailer::~TSendmailMailer()
{
    T_TRACEFUNC("");
    if (!mailMessage.isEmpty()) {
        tSystemWarn("Mail not sent. Deleted it.");
    }
}
TSession TSessionCookieStore::find(const QByteArray &id, const QDateTime &)
{
    TSession session;
    if (id.isEmpty())
        return session;

    QList<QByteArray> balst = id.split('_');
    if (balst.count() == 2 && !balst.value(0).isEmpty() && !balst.value(1).isEmpty()) {
        QByteArray ba = QByteArray::fromHex(balst.value(0));
        QByteArray digest = QCryptographicHash::hash(ba + Tf::app()->appSettings().value("Session.Secret").toByteArray(),
                                                     QCryptographicHash::Sha1);
        
        if (digest != QByteArray::fromHex(balst.value(1))) {
            tSystemWarn("Recieved a tampered cookie or that of other web application.");
            //throw SecurityException("Tampered with cookie", __FILE__, __LINE__);
            return session;
        }

        QDataStream ds(&ba, QIODevice::ReadOnly);
        ds >> *static_cast<QVariantHash *>(&session);
        
        if (ds.status() != QDataStream::Ok) {
            tSystemError("Unable to load a session from the cookie store.");
            session.clear();
        }
    }
Exemplo n.º 5
0
bool TThreadApplicationServer::start(bool debugMode)
{
    if (isListening()) {
        return true;
    }

    bool res = loadLibraries();
    if (!res) {
        if (debugMode) {
            tSystemError("Failed to load application libraries.");
            return false;
        } else {
            tSystemWarn("Failed to load application libraries.");
        }
    }

    if (listenSocket <= 0 || !setSocketDescriptor(listenSocket)) {
        tSystemError("Failed to set socket descriptor: %d", listenSocket);
        return false;
    }

    // instantiate
    if (!debugMode) {
        TSystemBus::instantiate();
        TPublisher::instantiate();
    }
    TUrlRoute::instantiate();
    TSqlDatabasePool::instantiate();
    TKvsDatabasePool::instantiate();

    TStaticInitializeThread::exec();
    return true;
}
void TApplicationServer::nativeSocketInit()
{
    WSAData wsadata;
    if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) {
        tSystemWarn("WinSock v2.0 initialization failed");
    }
}
Exemplo n.º 7
0
void THttpRequest::parseBody(const QByteArray &body, const THttpRequestHeader &header)
{
    switch (method()) {
    case Tf::Post: {
        QString ctype = QString::fromLatin1(header.contentType().trimmed());
        if (ctype.startsWith("multipart/form-data", Qt::CaseInsensitive)) {
            // multipart/form-data
            d->multipartFormData = TMultipartFormData(body, boundary());
            d->formItems = d->multipartFormData.formItems();

        } else if (ctype.startsWith("application/json", Qt::CaseInsensitive)) {
#if QT_VERSION >= 0x050000
            d->jsonData = QJsonDocument::fromJson(body);
#else
            tSystemWarn("unsupported content-type: %s", qPrintable(ctype));
#endif
        } else {
            // 'application/x-www-form-urlencoded'
            if (!body.isEmpty()) {
                QList<QByteArray> formdata = body.split('&');
                for (QListIterator<QByteArray> i(formdata); i.hasNext(); ) {
                    QList<QByteArray> nameval = i.next().split('=');
                    if (!nameval.value(0).isEmpty()) {
                        // URL decode
                        QString key = THttpUtility::fromUrlEncoding(nameval.value(0));
                        QString val = THttpUtility::fromUrlEncoding(nameval.value(1));
                        d->formItems.insertMulti(key, val);
                        tSystemDebug("POST Hash << %s : %s", qPrintable(key), qPrintable(val));
                    }
                }
            }
        }
        /* FALL THROUGH */ }

    case Tf::Get: {
        // query parameter
        QList<QByteArray> data = d->header.path().split('?');
        QString getdata = data.value(1);
        if (!getdata.isEmpty()) {
            QStringList pairs = getdata.split('&', QString::SkipEmptyParts);
            for (QStringListIterator i(pairs); i.hasNext(); ) {
                QStringList s = i.next().split('=');
                if (!s.value(0).isEmpty()) {
                    QString key = THttpUtility::fromUrlEncoding(s.value(0).toLatin1());
                    QString val = THttpUtility::fromUrlEncoding(s.value(1).toLatin1());
                    d->queryItems.insertMulti(key, val);
                    tSystemDebug("GET Hash << %s : %s", qPrintable(key), qPrintable(val));
                }
            }
        }
        break; }

    default:
        // do nothing
        break;
    }
}
static void invokeStaticInitialize()
{
    // Calls staticInitialize()
    TDispatcher<TActionController> dispatcher("applicationcontroller");
    bool dispatched = dispatcher.invoke("staticInitialize");
    if (!dispatched) {
        tSystemWarn("No such method: staticInitialize() of ApplicationController");
    }
}
void ServerManager::readStandardError() const
{
    QProcess *server = qobject_cast<QProcess *>(sender());
    if (server) {
        QByteArray buf = server->readAllStandardError();
        tSystemWarn("treefrog stderr: %s", buf.constData());
        fprintf(stderr, "treefrog stderr: %s", buf.constData());
    }
}
void TApplicationServerBase::invokeStaticInitialize()
{
    // Calls staticInitialize()
    TDispatcher<TActionController> dispatcher("applicationcontroller");
    bool dispatched = dispatcher.invoke("staticInitialize", QStringList(), Qt::DirectConnection);
    if (!dispatched) {
        tSystemWarn("No such method: staticInitialize() of ApplicationController");
    }
}
/*!
  Returns true if the user \a user is allowed to access to the requested
  action; otherwise returns false.
*/
bool TAccessValidator::validate(const TAbstractUser *user) const
{
    bool ret = allowDefault;
    const TActionController *controller = Tf::currentContext()->currentController();
    Q_ASSERT(controller);

    if (accessRules.isEmpty()) {
        tWarn("No rule for access validation: %s", qPrintable(controller->className()));
        return ret;
    }

    if (!user || user->identityKey().isEmpty()) {
        // Searches a access rule for an unauthenticated user
        for (const auto &rule : accessRules) {
            if (rule.type == AccessRule::UnauthenticatedUser
                && rule.action == controller->activeAction()) {
                ret = rule.allow;
                break;
            }
        }
        if (ret) {
            tSystemDebug("Access '%s' action by an unauthenticated user : Allow", qPrintable(controller->activeAction()));
        } else {
            tSystemWarn("Access '%s' action by an unauthenticated user : Deny", qPrintable(controller->activeAction()));
        }

    } else {
        for (const auto &rule : accessRules) {
            if (rule.action == controller->activeAction()
                && ((rule.type == AccessRule::User && rule.key == user->identityKey())
                    || (!user->groupKey().isEmpty() && rule.key == user->groupKey()))) {
                ret = rule.allow;
                break;
            }
        }
        if (ret) {
            tSystemDebug("Access '%s' action by '%s' user : Allow", qPrintable(controller->activeAction()), qPrintable(user->identityKey()));
        } else {
            tSystemWarn("Access '%s' action by '%s' user : Deny", qPrintable(controller->activeAction()), qPrintable(user->identityKey()));
        }
    }

    return ret;
}
Exemplo n.º 12
0
bool THttpSocket::setSocketDescriptor(
#if QT_VERSION >= 0x050000
    qintptr socketDescriptor,
#else
    int socketDescriptor,
#endif
    SocketState socketState, OpenMode openMode)
{
    bool ret  = QTcpSocket::setSocketDescriptor(socketDescriptor, socketState, openMode);
    if (ret) {
        // Sets socket options
        QTcpSocket::setSocketOption(QAbstractSocket::LowDelayOption, 1);

        // Sets buffer size of socket
#if QT_VERSION >= 0x050300
        int val = QTcpSocket::socketOption(QAbstractSocket::SendBufferSizeSocketOption).toInt();
        if (val < SEND_BUF_SIZE) {
            QTcpSocket::setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, SEND_BUF_SIZE);
        }

        val = QTcpSocket::socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption).toInt();
        if (val < RECV_BUF_SIZE) {
            QTcpSocket::setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, RECV_BUF_SIZE);
        }
#else
# ifdef Q_OS_UNIX
        int res, bufsize;

        bufsize = SEND_BUF_SIZE;
        res = setsockopt((int)socketDescriptor, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
        if (res < 0) {
            tSystemWarn("setsockopt error [SO_SNDBUF] fd:%d", (int)socketDescriptor);
        }

        bufsize = RECV_BUF_SIZE;
        res = setsockopt((int)socketDescriptor, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));
        if (res < 0) {
            tSystemWarn("setsockopt error [SO_RCVBUF] fd:%d", (int)socketDescriptor);
        }
# endif
#endif
    }
    return ret;
}
TAbstractWebSocket::~TAbstractWebSocket()
{
    if (!closing.load()) {
        tSystemWarn("Logic warning  [%s:%d]", __FILE__, __LINE__);
    }

    if (keepAliveTimer) {
        delete keepAliveTimer;
    }
}
Exemplo n.º 14
0
TSmtpMailer::~TSmtpMailer()
{
    T_TRACEFUNC("");
    if (!mailMessage.isEmpty()) {
        tSystemWarn("Mail not sent. Deleted it.");
    }

    if (pop)
        delete pop;

    delete socket;
}
Exemplo n.º 15
0
/*!
  Returns the module name for multi-processing that is set by the setting
  \a MultiProcessingModule in the application.ini.
*/
TWebApplication::MultiProcessingModule TWebApplication::multiProcessingModule() const
{
    if (mpm == Invalid) {
        QString str = Tf::appSettings()->value(Tf::MultiProcessingModule).toString().toLower();
        if (str == "thread") {
            mpm = Thread;
        } else if (str == "hybrid") {
#ifdef Q_OS_LINUX
            mpm = Hybrid;
#else
            tSystemWarn("Unsupported MPM: hybrid  (Linux only)");
            tWarn("Unsupported MPM: hybrid  (Linux only)");
            mpm = Thread;
#endif
        } else {
            tSystemWarn("Unsupported MPM: %s", qPrintable(str));
            tWarn("Unsupported MPM: %s", qPrintable(str));
            mpm = Thread;
        }
    }
    return mpm;
}
/*!
  Listen for connections on UNIX domain.
 */
int TApplicationServerBase::nativeListen(const QString &fileDomain, OpenFlag flag)
{
    int sd = -1;
    struct sockaddr_un addr;

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = PF_UNIX;
    if (sizeof(addr.sun_path) < (uint)fileDomain.toLatin1().size() + 1) {
        tSystemError("too long name for UNIX domain socket  [%s:%d]", __FILE__, __LINE__);
        return sd;
    }
    strncpy(addr.sun_path, fileDomain.toLatin1().data(), sizeof(addr.sun_path));

    // create unix domain socket
    sd = ::socket(PF_UNIX, SOCK_STREAM, 0);
    if (sd < 0) {
        tSystemError("Socket create failed  [%s:%d]", __FILE__, __LINE__);
        return sd;
    }

    if (flag == CloseOnExec) {
        ::fcntl(sd, F_SETFD, FD_CLOEXEC); // set close-on-exec flag
    }
    ::fcntl(sd, F_SETFL, ::fcntl(sd, F_GETFL) | O_NONBLOCK);  // non-block

    QFile file(fileDomain);
    if (file.exists()) {
        file.remove();
        tSystemWarn("File for UNIX domain socket removed: %s", qPrintable(fileDomain));
    }

    // Bind
    if (::bind(sd, (sockaddr *)&addr, sizeof(sockaddr_un)) < 0) {
        tSystemError("Bind failed  [%s:%d]", __FILE__, __LINE__);
        goto socket_error;
    }
    file.setPermissions((QFile::Permissions)0x777);

    // Listen
    if (::listen(sd, 50) < 0) {
        tSystemError("Listen failed  [%s:%d]", __FILE__, __LINE__);
        goto socket_error;
    }

    return sd;

socket_error:
    nativeClose(sd);
    return -1;
}
bool TApplicationServerBase::loadLibraries()
{
    T_TRACEFUNC("");

    // Loads libraries
    if (libsLoaded.isEmpty()) {
        // Sets work directory
        QString libPath = Tf::app()->libPath();
        if (QDir(libPath).exists()) {
            // To resolve the symbols in the app libraries
            QDir::setCurrent(libPath);
        } else {
            tSystemError("lib directory not found");
            return false;
        }

        loadedTimestamp = latestLibraryTimestamp();

#if defined(Q_OS_WIN)
        QStringList libs = { "controller", "view" };
#elif defined(Q_OS_LINUX)
        QStringList libs = { "libcontroller.so", "libview.so" };
#elif defined(Q_OS_DARWIN)
        QStringList libs = { "libcontroller.dylib", "libview.dylib" };
#else
        QStringList libs = { "libcontroller.so", "libview.so" };
#endif

        for (const auto &libname : libs) {
            auto lib = new QLibrary(libname);
            if (lib->load()) {
                tSystemDebug("Library loaded: %s", qPrintable(lib->fileName()));
                libsLoaded << lib;
            } else {
                tSystemWarn("%s", qPrintable(lib->errorString()));
            }
        }

        QStringList controllers = TActionController::availableControllers();
        tSystemDebug("Available controllers: %s", qPrintable(controllers.join(" ")));
    }
    QDir::setCurrent(Tf::app()->webRootPath());

    TSystemBus::instantiate();
    TPublisher::instantiate();
    TUrlRoute::instantiate();
    TSqlDatabasePool::instantiate();
    TKvsDatabasePool::instantiate();
    return true;
}
static void setSocketOption(int fd)
{
    int ret, flag, bufsize;

    // Disable the Nagle (TCP No Delay) algorithm
    flag = 1;
    ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));
    if (ret < 0) {
        tSystemWarn("setsockopt error [TCP_NODELAY] fd:%d", fd);
    }

    bufsize = SEND_BUF_SIZE;
    ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
    if (ret < 0) {
        tSystemWarn("setsockopt error [SO_SNDBUF] fd:%d", fd);
    }

    bufsize = RECV_BUF_SIZE;
    ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));
    if (ret < 0) {
        tSystemWarn("setsockopt error [SO_RCVBUF] fd:%d", fd);
    }
}
static void WINAPI serviceHandler(DWORD ctrl)
{
    switch (ctrl) {
    case SERVICE_CONTROL_STOP:
    case SERVICE_CONTROL_SHUTDOWN:
        tSystemInfo("Windows service: Received a stop-service request.");
        serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        serviceStatus.dwWaitHint     = 30000;
        SetServiceStatus(statusHandle, &serviceStatus);
        Tf::app()->exit(0);
        break;
        
    case SERVICE_CONTROL_PAUSE:
    case SERVICE_CONTROL_CONTINUE:
    case SERVICE_CONTROL_INTERROGATE:
        tSystemWarn("Windows service: Received ctrl code: %ld ", ctrl);
        SetServiceStatus(statusHandle, &serviceStatus);
        break;
        
    default:
        tSystemWarn("Windows service: Invalid ctrl code: %ld ", ctrl);
        break;
    }
}
Exemplo n.º 20
0
static void messageOutput(QtMsgType type, const char *msg)
{
    switch (type) {
    case QtFatalMsg:
    case QtCriticalMsg:
        tSystemError("%s", msg);
        break;
    case QtWarningMsg:
        tSystemWarn("%s", msg);
        break;
    case QtDebugMsg:
        tSystemDebug("%s", msg);
        break;
    default:
        break;
    }
}
Exemplo n.º 21
0
void TScheduler::run()
{
    _rollback = false;
    TDatabaseContext::setCurrentDatabaseContext(this);

    try {
        // Executes the job
        job();

        if (_rollback) {
            TDatabaseContext::rollbackTransactions();
        } else {
            TDatabaseContext::commitTransactions();
        }

    } catch (ClientErrorException &e) {
        tWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        tSystemWarn("Caught ClientErrorException: status code:%d", e.statusCode());
    } catch (SqlException &e) {
        tError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (KvsException &e) {
        tError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (SecurityException &e) {
        tError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (RuntimeException &e) {
        tError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (StandardException &e) {
        tError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (std::exception &e) {
        tError("Caught Exception: %s", e.what());
        tSystemError("Caught Exception: %s", e.what());
    }

    TDatabaseContext::release();
    TDatabaseContext::setCurrentDatabaseContext(nullptr);

    if (_autoDelete && !_timer->isActive()) {
        connect(this, &TScheduler::finished, this, &QObject::deleteLater);
    }
}
bool TFileAioLogger::open()
{
    QMutexLocker locker(&d->mutex);

    if (d->fileName.isEmpty()) {
        tSystemWarn("Empty file name for log.");
        return false;
    }

    if (d->fileDescriptor <= 0) {
        d->fileDescriptor = ::open(qPrintable(d->fileName), (O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC), 0666);
        if (d->fileDescriptor < 0) {
            tSystemError("file open failed: %s", qPrintable(d->fileName));
        }
    }

    return (d->fileDescriptor > 0);
}
/*!
  \~english
  Logs the user \a user in to the system.
  
  This is a virtual function.
  \~japanese
  ユーザ \a user をシステムへログインさせる
  
  \~
  \sa userLogout()
*/
bool TActionController::userLogin(const TAbstractUser *user)
{
    if (!user) {
        tSystemError("userLogin: null specified");
        return false;
    }
    
    if (user->identityKey().isEmpty()) {
        tSystemError("userLogin: identityKey empty");
        return false;
    }
    
    if (isUserLoggedIn()) {
        tSystemWarn("userLogin: Duplicate login detected. Force logout [user:%s]", qPrintable(identityKeyOfLoginUser()));
    }
    
    session().insert(LOGIN_USER_NAME_KEY, user->identityKey());
    return true;
}
void TEpollHttpSocket::parse()
{
    if (Q_UNLIKELY(limitBodyBytes < 0)) {
        limitBodyBytes = Tf::appSettings()->value(Tf::LimitRequestBody, "0").toInt();
    }

    if (Q_LIKELY(lengthToRead < 0)) {
        int idx = httpBuffer.indexOf("\r\n\r\n");
        if (idx > 0) {
            THttpRequestHeader header(httpBuffer);
            tSystemDebug("content-length: %d", header.contentLength());

            if (limitBodyBytes > 0 && header.contentLength() > (uint)limitBodyBytes) {
                throw ClientErrorException(413);  // Request Entity Too Large
            }

            lengthToRead = qMax(idx + 4 + (qint64)header.contentLength() - httpBuffer.length(), 0LL);
            tSystemDebug("lengthToRead: %d", (int)lengthToRead);
        }
    } else {
        tSystemWarn("Unreachable code in normal communication");
    }
}
void TActionContext::execute()
{
    T_TRACEFUNC("");
    TAccessLog accessLog;
    THttpResponseHeader responseHeader;

    try {
        httpSocket = new THttpSocket;
        if (!httpSocket->setSocketDescriptor(socketDesc)) {
            emitError(httpSocket->error());
            delete httpSocket;
            httpSocket = 0;
            return;
        } else {
            socketDesc = 0;
        }

        while (!httpSocket->canReadRequest()) {
            if (stopped) {
                tSystemDebug("Detected stop request");
                break;
            }

            // Check idle timeout
            if (httpSocket->idleTime() >= 10) {
                tSystemWarn("Reading a socket timed out after 10 seconds. Descriptor:%d", (int)httpSocket->socketDescriptor());
                break;
            }
            httpSocket->waitForReadyRead(100);
        }
        
        if (!httpSocket->canReadRequest()) {
            httpSocket->abort();
            delete httpSocket;
            httpSocket = 0;
            return;
        }

        THttpRequest httpRequest = httpSocket->read();
        const THttpRequestHeader &hdr = httpRequest.header();

        // Access log
        QByteArray firstLine = hdr.method() + ' ' + hdr.path();
        firstLine += QString(" HTTP/%1.%2").arg(hdr.majorVersion()).arg(hdr.minorVersion()).toLatin1();
        accessLog.request = firstLine;
        accessLog.remoteHost = (Tf::app()->appSettings().value(LISTEN_PORT).toUInt() > 0) ? httpSocket->peerAddress().toString().toLatin1() : QByteArray("(unix)");

        tSystemDebug("method : %s", hdr.method().data());
        tSystemDebug("path : %s", hdr.path().data());

        Tf::HttpMethod method = httpRequest.method();
        QString path = THttpUtility::fromUrlEncoding(hdr.path().split('?').value(0));

        // Routing info exists?
        TRouting rt = TUrlRoute::instance().findRouting(method, path);
        tSystemDebug("Routing: controller:%s  action:%s", rt.controller.data(),
                     rt.action.data());
        
        if (rt.isEmpty()) {
            // Default URL routing
            rt.params = path.split('/');
            if (path.startsWith(QLatin1Char('/')) && !rt.params.isEmpty()) {
                rt.params.removeFirst();  // unuse first item
            }
            if (path.endsWith(QLatin1Char('/')) && !rt.params.isEmpty()) {
                rt.params.removeLast();  // unuse last item
            }

            // Direct view render mode?
            if (Tf::app()->appSettings().value(DIRECT_VIEW_RENDER_MODE).toBool()) {
                // Direct view setting
                rt.controller = "directcontroller";
                rt.action = "show";
            } else {
                if (!rt.params.value(0).isEmpty()) {
                    rt.controller = rt.params.takeFirst().toLower().toLatin1() + "controller";

                    if (rt.controller == "applicationcontroller") {
                        rt.controller.clear();  // Can not call 'ApplicationController'
                    }

                    // Default action: index
                    rt.action = rt.params.value(0, QLatin1String("index")).toLatin1();
                    if (!rt.params.isEmpty()) {
                        rt.params.takeFirst();
                    }
                }
                tSystemDebug("Active Controller : %s", rt.controller.data());
            }
        }

        // Call controller method
        TDispatcher<TActionController> ctlrDispatcher(rt.controller);
        currController = ctlrDispatcher.object();
        if (currController) {
            currController->setActionName(rt.action);
            currController->setHttpRequest(httpRequest);
            
            // Session
            if (currController->sessionEnabled()) {
                TSession session;
                QByteArray sessionId = httpRequest.cookie(TSession::sessionName());
                if (!sessionId.isEmpty()) {
                    // Finds a session
                    session = TSessionManager::instance().findSession(sessionId);
                }
                currController->setSession(session);
                
                // Exports flash-variant
                currController->exportAllFlashVariants();
            }
            
            // Verify authenticity token
            if (Tf::app()->appSettings().value(ENABLE_CSRF_PROTECTION_MODULE, true).toBool()
                && currController->csrfProtectionEnabled() && !currController->exceptionActionsOfCsrfProtection().contains(rt.action)) {

                if (method == Tf::Post || method == Tf::Put || method == Tf::Delete) {
                    if (!currController->verifyRequest(httpRequest)) {
                        throw SecurityException("Invalid authenticity token", __FILE__, __LINE__);
                    }
                }
            }

            if (currController->sessionEnabled()) {
                if (currController->session().id().isEmpty() || Tf::app()->appSettings().value(AUTO_ID_REGENERATION).toBool()) {
                    TSessionManager::instance().remove(currController->session().sessionId); // Removes the old session
                    // Re-generate session ID
                    currController->session().sessionId = TSessionManager::instance().generateId();
                    tSystemDebug("Re-generate session ID: %s", currController->session().sessionId.data());
                }                
                // Sets CSRF protection informaion
                TActionController::setCsrfProtectionInto(currController->session());
            }

            // Database Transaction
            transactions.setEnabled(currController->transactionEnabled());
            
            // Do filters
            if (currController->preFilter()) {
                
                // Dispathes
                bool dispatched = ctlrDispatcher.invoke(rt.action, rt.params);
                if (dispatched) {
                    autoRemoveFiles << currController->autoRemoveFiles;  // Adds auto-remove files
                    
                    // Post fileter
                    currController->postFilter();
                    
                    if (currController->rollbackRequested()) {
                        rollbackTransactions();
                    } else {
                        // Commits a transaction to the database
                        commitTransactions();
                    }
                    
                    // Session store
                    if (currController->sessionEnabled()) {
                        bool stored = TSessionManager::instance().store(currController->session());
                        if (stored) {
                            QDateTime expire;
                            if (TSessionManager::sessionLifeTime() > 0) {
                                expire = QDateTime::currentDateTime().addSecs(TSessionManager::sessionLifeTime());
                            }
                            
                            // Sets the path in the session cookie
                            QString cookiePath = Tf::app()->appSettings().value(SESSION_COOKIE_PATH).toString();
                            currController->addCookie(TSession::sessionName(), currController->session().id(), expire, cookiePath);
                        }
                    }
                }
            }
            
            // Sets the default status code of HTTP response
            accessLog.statusCode = (!currController->response.isBodyNull()) ? currController->statusCode() : Tf::InternalServerError;
            currController->response.header().setStatusLine(accessLog.statusCode, THttpUtility::getResponseReasonPhrase(accessLog.statusCode));

            // Writes a response and access log
            accessLog.responseBytes = writeResponse(currController->response.header(), currController->response.bodyIODevice(),
                                                    currController->response.bodyLength());
            
            httpSocket->disconnectFromHost();

            // Session GC
            TSessionManager::instance().collectGarbage();
        
        } else {
            accessLog.statusCode = Tf::BadRequest;

            if (method == Tf::Get) {  // GET Method
                path.remove(0, 1);
                QFile reqPath(Tf::app()->publicPath() + path);
                QFileInfo fi(reqPath);

                if (fi.isFile() && fi.isReadable()) {
                    // Check "If-Modified-Since" header for caching
                    bool sendfile = true;
                    QByteArray ifModifiedSince = hdr.rawHeader("If-Modified-Since");

                    if (!ifModifiedSince.isEmpty()) {
                        QDateTime dt = THttpUtility::fromHttpDateTimeString(ifModifiedSince);
                        sendfile = (!dt.isValid() || dt != fi.lastModified());
                    }

                    if (sendfile) {
                        // Sends a request file
                        responseHeader.setRawHeader("Last-Modified", THttpUtility::toHttpDateTimeString(fi.lastModified()));
                        QByteArray type = Tf::app()->internetMediaType(fi.suffix());
                        accessLog.responseBytes = writeResponse(Tf::OK, responseHeader, type, &reqPath, reqPath.size());
                    } else {
                        // Not send the data
                        accessLog.responseBytes = writeResponse(Tf::NotModified, responseHeader);
                    }
                } else {
                    accessLog.responseBytes = writeResponse(Tf::NotFound, responseHeader);
                }
                accessLog.statusCode = responseHeader.statusCode();

            } else if (method == Tf::Post) {
                // file upload?
            } else {
                // HEAD, DELETE, ...
            }
        }

    } catch (ClientErrorException &e) {
        tWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        accessLog.responseBytes = writeResponse(e.statusCode(), responseHeader);   
        accessLog.statusCode = e.statusCode();
    } catch (SqlException &e) {
        tError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (SecurityException &e) {
        tError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (RuntimeException &e) {
        tError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (...) {
        tError("Caught Exception");
    }

    accessLog.timestamp = QDateTime::currentDateTime();
    writeAccessLog(accessLog);  // Writes access log

    // Push to the pool
    TActionContext::releaseDatabases();

    httpSocket->disconnectFromHost();
    // Destorys the object in the thread which created it
    delete httpSocket;
    httpSocket = 0;
}
void TMultiplexingServer::run()
{
    // Listen socket
    quint16 port = Tf::app()->appSettings().value("ListenPort").toUInt();
    int sock = TApplicationServerBase::nativeListen(QHostAddress::Any, port);
    if (sock > 0) {
        tSystemDebug("listen successfully.  port:%d", port);
    } else {
        tSystemError("Failed to set socket descriptor: %d", sock);
        TApplicationServerBase::nativeClose(sock);
        return;
    }

    listenSocket = sock;
    setSocketOption(listenSocket);

    maxWorkers = Tf::app()->maxNumberOfServers(10);
    tSystemDebug("MaxWorkers: %d", maxWorkers);

    // Get send buffer size and recv buffer size
    int res, sendBufSize, recvBufSize;
    socklen_t optlen = sizeof(int);
    res = getsockopt(listenSocket, SOL_SOCKET, SO_SNDBUF, &sendBufSize, &optlen);
    if (res < 0)
        tSystemDebug("SO_SNDBUF: %d", sendBufSize);

    res = getsockopt(listenSocket, SOL_SOCKET, SO_RCVBUF, &recvBufSize, &optlen);
    if (res < 0)
        tSystemDebug("SO_RCVBUF: %d", recvBufSize);

    const int MaxEvents = 128;
    struct epoll_event events[MaxEvents];
    sendBufSize *= 0.8;
    char *sndbuffer = new char[sendBufSize];
    char *rcvbuffer = new char[recvBufSize];

    // Create epoll
    epollFd = epoll_create(1);
    if (epollFd < 0) {
        tSystemError("Failed epoll_create()");
        goto socket_error;
    }

    if (epollAdd(listenSocket, EPOLLIN) < 0) {
        tSystemError("Failed epoll_ctl()");
        goto epoll_error;
    }

    for (;;) {
        // Get send-request
        getSendRequest();

        // Check pending requests
        while (!pendingRequests.isEmpty() && (int)threadCounter < maxWorkers) {
            int fd = pendingRequests.takeFirst();
            THttpBuffer &recvbuf = recvBuffers[fd];
            emitIncomingRequest(fd, recvbuf);
        }

        // Poll Sending/Receiving/Incoming
        int timeout = ((int)threadCounter > 0) ? 1 : 100;
        int nfd = tf_epoll_wait(epollFd, events, MaxEvents, timeout);
        int err = errno;
        if (nfd < 0) {
            tSystemError("Failed epoll_wait() : errno:%d", err);
            break;
        }

        for (int i = 0; i < nfd; ++i) {
            if (events[i].data.fd == listenSocket) {
                if (!pendingRequests.isEmpty())
                    continue;

                // Incoming connection
                struct sockaddr_storage addr;
                socklen_t addrlen = sizeof(addr);

                int clt = ::accept(events[i].data.fd, (sockaddr *)&addr, &addrlen);
                if (clt < 0) {
                    tSystemWarn("Failed accept");
                    continue;
                }

                setNonBlocking(clt);
                if (epollAdd(clt, EPOLLIN) == 0) {
                    THttpBuffer &recvbuf = recvBuffers[clt];
                    recvbuf.clear();
                    recvbuf.setClientAddress(QHostAddress((sockaddr *)&addr));
                }

            } else {
                int cltfd = events[i].data.fd;

                if ( (events[i].events & EPOLLIN) ) {
                    // Receive data
                    int len = ::recv(cltfd, rcvbuffer, recvBufSize, 0);
                    err = errno;
                    if (len > 0) {
                        // Read successfully
                        THttpBuffer &recvbuf = recvBuffers[cltfd];
                        recvbuf.write(rcvbuffer, len);

                        if (recvbuf.canReadHttpRequest()) {
                            // Incoming a request
                            if ((int)threadCounter >= maxWorkers) {
                                pendingRequests << cltfd;
                            } else {
                                emitIncomingRequest(cltfd, recvbuf);
                            }
                        }

                    } else {
                        if (len < 0 && err != ECONNRESET) {
                            tSystemError("Failed recv : errno:%d", err);
                        }

                        // Disconnect
                        epollClose(cltfd);
                        continue;
                    }
                }

                if ( (events[i].events & EPOLLOUT) ) {
                    // Send data
                    THttpSendBuffer *sendbuf = sendBuffers[cltfd].first();
                    if (!sendbuf) {
                        tSystemError("Not found send-buffer");
                        epollClose(cltfd);
                        continue;
                    }

                    int len = sendbuf->read(sndbuffer, sendBufSize);
                    int sentlen = ::send(cltfd, sndbuffer, len, 0);
                    err = errno;
                    TAccessLogger &logger = sendbuf->accessLogger();

                    if (sentlen <= 0) {
                        if (err != ECONNRESET) {
                            tSystemError("Failed send : errno:%d", err);
                        }
                        // Access log
                        logger.setResponseBytes(-1);
                        logger.write();

                        epollClose(cltfd);
                        continue;
                    } else {
                        logger.setResponseBytes(logger.responseBytes() + sentlen);

                        if (len > sentlen) {
                            tSystemDebug("sendbuf prepend: len:%d", len - sentlen);
                            sendbuf->prepend(sndbuffer + sentlen, len - sentlen);
                        }

                        if (sendbuf->atEnd()) {
                            logger.write();  // Writes access log
                            sendbuf->release();

                            QQueue<THttpSendBuffer*> &que = sendBuffers[cltfd];
                            delete que.dequeue(); // delete send-buffer obj

                            // Prepare recv
                            if (que.isEmpty())
                                epollModify(cltfd, EPOLLIN);
                        }
                    }
                }
            }
        }

        // Check stop flag
        if (stopped) {
            if (listenSocket > 0) {
                // Close the listen-socket
                epollClose(listenSocket);
                listenSocket = 0;
            }

            if (!recvBuffers.isEmpty()) {
                for (QMapIterator<int, THttpBuffer> it(recvBuffers); it.hasNext(); ) {
                    it.next();
                    epollClose(it.key());
                }
            }

            if (recvBuffers.isEmpty() && sendBuffers.isEmpty()) {
                break;
            }
        }
    }

epoll_error:
    TF_CLOSE(epollFd);
    epollFd = 0;

socket_error:
    if (listenSocket > 0)
        TF_CLOSE(listenSocket);
    listenSocket = 0;
    delete sndbuffer;
    delete rcvbuffer;
}
Exemplo n.º 27
0
TRouting TUrlRoute::findRouting(Tf::HttpMethod method, const QString &path) const
{
    QStringList params;
    QStringList components = path.split('/');
    components.takeFirst();
    components.takeLast();

    for (QListIterator<TRoute> i(routes); i.hasNext(); ) {
        const TRoute &rt = i.next();

        //Check if we have a good http verb
        switch(rt.method)
        {
            case TRoute::Match:
                //We match anything here
                break;
            case TRoute::Get:
                if (method != Tf::Get) continue;
                break;
            case TRoute::Post:
                if (method != Tf::Post) continue;
                break;
            case TRoute::Patch:
                if (method != Tf::Patch) continue;
                break;
            case TRoute::Put:
                if (method != Tf::Put) continue;
                break;
            case TRoute::Delete:
                if (method != Tf::Delete) continue;
                break;
            default:
                tSystemWarn("Unkown route method in findRouting: %d", rt.method);
                continue;
                break;
        }

        //To short?
        if (components.length() < rt.components.length()) continue;

        //Parse any parameters
        for(int j=0; j < rt.components.length(); j++)
        {
            if (rt.components[j] == components[j]) continue;

            if (rt.components[j] == ":param") {
                params << components[j];
                continue;
            }

            goto trynext;
        }

        //Add any variable params
        if (rt.has_variable_params)
            params << components.mid(rt.components.length());

        return TRouting(rt.controller, rt.action, params);

trynext:
        continue;
    }

    return TRouting();  // Not found routing info
}
void TWebSocketWorker::execute(int opcode, const QByteArray &payload)
{
    bool sendTask = false;
    QString es = TUrlRoute::splitPath(_requestPath).value(0).toLower() + "endpoint";
    TDispatcher<TWebSocketEndpoint> dispatcher(es);
    TWebSocketEndpoint *endpoint = dispatcher.object();

    if (!endpoint) {
        return;
    }

    try {
        tSystemDebug("Found endpoint: %s", qPrintable(es));
        tSystemDebug("TWebSocketWorker opcode: %d", opcode);

        endpoint->sessionStore = _socket->session(); // Sets websocket session
        endpoint->uuid = _socket->socketUuid();
        // Database Transaction
        setTransactionEnabled(endpoint->transactionEnabled());

        switch (_mode) {
        case Opening: {
            bool res = endpoint->onOpen(_httpSession);
            if (res) {
                // For switch response
                endpoint->taskList.prepend(qMakePair((int)TWebSocketEndpoint::OpenSuccess, QVariant()));

                if (endpoint->keepAliveInterval() > 0) {
                    endpoint->startKeepAlive(endpoint->keepAliveInterval());
                }
            } else {
                endpoint->taskList.prepend(qMakePair((int)TWebSocketEndpoint::OpenError, QVariant()));
            }
            break; }

        case Closing:
            if (!_socket->closing.exchange(true)) {
                endpoint->onClose(Tf::GoingAway);
                endpoint->unsubscribeFromAll();
            }
            break;

        case Receiving: {

            switch (opcode) {
            case TWebSocketFrame::TextFrame:
                endpoint->onTextReceived(QString::fromUtf8(payload));
                break;

            case TWebSocketFrame::BinaryFrame:
                endpoint->onBinaryReceived(payload);
                break;

            case TWebSocketFrame::Close: {
                quint16 closeCode = Tf::GoingAway;
                if (payload.length() >= 2) {
                    QDataStream ds(payload);
                    ds.setByteOrder(QDataStream::BigEndian);
                    ds >> closeCode;
                }

                if (!_socket->closing.exchange(true)) {
                    endpoint->onClose(closeCode);
                    endpoint->unsubscribeFromAll();
                }
                endpoint->close(closeCode);  // close response or disconnect
                break; }

            case TWebSocketFrame::Ping:
                endpoint->onPing(payload);
                break;

            case TWebSocketFrame::Pong:
                endpoint->onPong(payload);
                break;

            default:
                tSystemWarn("Invalid opcode: 0x%x  [%s:%d]", (int)opcode, __FILE__, __LINE__);
                break;
            }
            break; }

        default:
            break;
        }

        // Sets session to the websocket
        _socket->setSession(endpoint->session());

        for (auto &p : endpoint->taskList) {
            const QVariant &taskData = p.second;

            switch (p.first) {
            case TWebSocketEndpoint::OpenSuccess:
                _socket->sendHandshakeResponse();
                break;

            case TWebSocketEndpoint::OpenError:
                _socket->closing = true;
                _socket->closeSent = true;
                _socket->disconnect();
                goto open_error;
                break;

            case TWebSocketEndpoint::SendText:
                _socket->sendText(taskData.toString());
                sendTask = true;
                break;

            case TWebSocketEndpoint::SendBinary:
                _socket->sendBinary(taskData.toByteArray());
                sendTask = true;
                break;

            case TWebSocketEndpoint::SendClose:
                if (_socket->closing.load() && _socket->closeSent.load()) {
                    // close-frame sent and received
                    _socket->disconnect();
                } else {
                    uint closeCode = taskData.toUInt();
                    _socket->sendClose(closeCode);
                    sendTask = true;
                }
                break;

            case TWebSocketEndpoint::SendPing:
                _socket->sendPing(taskData.toByteArray());
                sendTask = true;
                break;

            case TWebSocketEndpoint::SendPong:
                _socket->sendPong(taskData.toByteArray());
                sendTask = true;
                break;

            case TWebSocketEndpoint::SendTextTo: {
                QVariantList lst = taskData.toList();
                TAbstractWebSocket *websocket = _socket->searchPeerSocket(lst[0].toByteArray());
                if (websocket) {
                    websocket->sendText(lst[1].toString());
                }
                break; }

            case TWebSocketEndpoint::SendBinaryTo: {
                QVariantList lst = taskData.toList();
                TAbstractWebSocket *websocket = _socket->searchPeerSocket(lst[0].toByteArray());
                if (websocket) {
                    websocket->sendBinary(lst[1].toByteArray());
                }
                break; }

            case TWebSocketEndpoint::SendCloseTo: {
                QVariantList lst = taskData.toList();
                TAbstractWebSocket *websocket = _socket->searchPeerSocket(lst[0].toByteArray());
                if (websocket) {
                    websocket->sendClose(lst[1].toInt());
                }
                break; }

            case TWebSocketEndpoint::Subscribe: {
                QVariantList lst = taskData.toList();
                TPublisher::instance()->subscribe(lst[0].toString(), lst[1].toBool(), _socket);
                break; }

            case TWebSocketEndpoint::Unsubscribe:
                TPublisher::instance()->unsubscribe(taskData.toString(), _socket);
                break;

            case TWebSocketEndpoint::UnsubscribeFromAll:
                TPublisher::instance()->unsubscribeFromAll(_socket);
                break;

            case TWebSocketEndpoint::PublishText: {
                QVariantList lst = taskData.toList();
                TPublisher::instance()->publish(lst[0].toString(), lst[1].toString(), _socket);
                break; }

            case TWebSocketEndpoint::PublishBinary: {
                QVariantList lst = taskData.toList();
                TPublisher::instance()->publish(lst[0].toString(), lst[1].toByteArray(), _socket);
                break; }

            case TWebSocketEndpoint::StartKeepAlive:
                _socket->startKeepAlive(taskData.toInt());
                break;

            case TWebSocketEndpoint::StopKeepAlive:
                _socket->stopKeepAlive();
                break;

            default:
                tSystemError("Invalid logic  [%s:%d]",  __FILE__, __LINE__);
                break;
            }
        }

        if (!sendTask) {
            // Receiving but not sending, so renew keep-alive
            _socket->renewKeepAlive();
        }

    open_error:
        // transaction
        if (Q_UNLIKELY(endpoint->rollbackRequested())) {
            rollbackTransactions();
        } else {
            // Commits a transaction to the database
            commitTransactions();
        }

    } catch (ClientErrorException &e) {
int TAbstractWebSocket::parse(QByteArray &recvData)
{
    tSystemDebug("parse enter  data len:%d  sid:%d", recvData.length(), socketId());
    if (websocketFrames().isEmpty()) {
        websocketFrames().append(TWebSocketFrame());
    } else {
        const TWebSocketFrame &f = websocketFrames().last();
        if (f.state() == TWebSocketFrame::Completed) {
            websocketFrames().append(TWebSocketFrame());
        }
    }

    TWebSocketFrame *pfrm = &websocketFrames().last();
    quint8  b;
    quint16 w;
    quint32 n;
    quint64 d;

    QDataStream ds(recvData);
    ds.setByteOrder(QDataStream::BigEndian);
    QIODevice *dev = ds.device();
    QByteArray hdr;

    while (!ds.atEnd()) {
        switch (pfrm->state()) {
        case TWebSocketFrame::Empty: {
            hdr = dev->peek(14);
            QDataStream dshdr(hdr);
            dshdr.setByteOrder(QDataStream::BigEndian);
            QIODevice *devhdr = dshdr.device();

            if (Q_UNLIKELY(devhdr->bytesAvailable() < 2)) {
                goto parse_end;
            }

            dshdr >> b;
            pfrm->setFirstByte(b);
            dshdr >> b;
            bool maskFlag = b & 0x80;
            quint8 len = b & 0x7f;

            // payload length
            switch (len) {
            case 126:
                if (Q_UNLIKELY(devhdr->bytesAvailable() < (int)sizeof(w))) {
                    goto parse_end;
                }
                dshdr >> w;
                if (Q_UNLIKELY(w < 126)) {
                    tSystemError("WebSocket protocol error  [%s:%d]", __FILE__, __LINE__);
                    return -1;
                }
                pfrm->setPayloadLength( w );
                break;

            case 127:
                if (Q_UNLIKELY(devhdr->bytesAvailable() < (int)sizeof(d))) {
                    goto parse_end;
                }
                dshdr >> d;
                if (Q_UNLIKELY(d <= 0xFFFF)) {
                    tSystemError("WebSocket protocol error  [%s:%d]", __FILE__, __LINE__);
                    return -1;
                }
                pfrm->setPayloadLength( d );
                break;

            default:
                pfrm->setPayloadLength( len );
                break;
            }

            // Mask key
            if (maskFlag) {
                if (Q_UNLIKELY(devhdr->bytesAvailable() < (int)sizeof(n))) {
                    goto parse_end;
                }
                dshdr >> n;
                pfrm->setMaskKey( n );
            }

            if (pfrm->payloadLength() == 0) {
                pfrm->setState(TWebSocketFrame::Completed);
            } else {
                pfrm->setState(TWebSocketFrame::HeaderParsed);
                if (pfrm->payloadLength() >= 2 * 1024 * 1024 * 1024ULL) {
                    tSystemError("Too big frame  [%s:%d]", __FILE__, __LINE__);
                    pfrm->clear();
                } else {
                    pfrm->payload().reserve(pfrm->payloadLength());
                }
            }

            tSystemDebug("WebSocket parse header pos: %lld", devhdr->pos());
            tSystemDebug("WebSocket payload length:%lld", pfrm->payloadLength());

            int hdrlen = hdr.length() - devhdr->bytesAvailable();
            ds.skipRawData(hdrlen);  // Forwards the pos
            break; }

        case TWebSocketFrame::HeaderParsed:  // fall through
        case TWebSocketFrame::MoreData: {
            tSystemDebug("WebSocket reading payload:  available length:%lld", dev->bytesAvailable());
            tSystemDebug("WebSocket parsing  length to read:%llu  current buf len:%d", pfrm->payloadLength(), pfrm->payload().size());
            quint64 size = qMin((pfrm->payloadLength() - pfrm->payload().size()), (quint64)dev->bytesAvailable());
            if (Q_UNLIKELY(size == 0)) {
                Q_ASSERT(0);
                break;
            }

            char *p = pfrm->payload().data() + pfrm->payload().size();
            size = ds.readRawData(p, size);

            if (pfrm->maskKey()) {
                // Unmask
                const quint8 mask[4] = { quint8((pfrm->maskKey() & 0xFF000000) >> 24),
                                         quint8((pfrm->maskKey() & 0x00FF0000) >> 16),
                                         quint8((pfrm->maskKey() & 0x0000FF00) >> 8),
                                         quint8((pfrm->maskKey() & 0x000000FF)) };

                int i = pfrm->payload().size();
                const char *end = p + size;
                while (p < end) {
                    *p++ ^= mask[i++ % 4];
                }
            }
            pfrm->payload().resize( pfrm->payload().size() + size );
            tSystemDebug("WebSocket payload curent buf len: %d", pfrm->payload().length());

            if ((quint64)pfrm->payload().size() == pfrm->payloadLength()) {
                pfrm->setState(TWebSocketFrame::Completed);
                tSystemDebug("Parse Completed   payload len: %d", pfrm->payload().size());
            } else {
                pfrm->setState(TWebSocketFrame::MoreData);
                tSystemDebug("Parse MoreData   payload len: %d", pfrm->payload().size());
            }
            break; }

        case TWebSocketFrame::Completed:  // fall through
        default:
            Q_ASSERT(0);
            break;
        }

        if (pfrm->state() == TWebSocketFrame::Completed) {
            if (Q_UNLIKELY(!pfrm->validate())) {
                pfrm->clear();
                continue;
            }

            // Fragmented message validation
            if (pfrm->opCode() == TWebSocketFrame::Continuation) {
                if (websocketFrames().count() >= 2) {
                    const TWebSocketFrame &before = websocketFrames()[websocketFrames().count() - 2];
                    if (before.isFinalFrame() || before.isControlFrame()) {
                        pfrm->clear();
                        tSystemWarn("Invalid continuation frame detected  [%s:%d]", __FILE__, __LINE__);
                        continue;
                    }
                }
            }

            // In case of control frame, moves forward after previous control frames
            if (pfrm->isControlFrame()) {
                if (websocketFrames().count() >= 2) {
                    TWebSocketFrame frm = websocketFrames().takeLast();
                    QMutableListIterator<TWebSocketFrame> it(websocketFrames());
                    while (it.hasNext()) {
                        TWebSocketFrame &f = it.next();
                        if (!f.isControlFrame()) {
                            break;
                        }
                    }

                    it.insert(frm);
                }
            }

            if (!ds.atEnd()) {
                // Prepare next frame
                websocketFrames().append(TWebSocketFrame());
                pfrm = &websocketFrames().last();
            } else {
                break;
            }
        }
    }

parse_end:
    int parsedlen = recvData.size() - dev->bytesAvailable();
    recvData.remove(0, parsedlen);
    return parsedlen;
}