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(); } }
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"); } }
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; }
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; } }
TSmtpMailer::~TSmtpMailer() { T_TRACEFUNC(""); if (!mailMessage.isEmpty()) { tSystemWarn("Mail not sent. Deleted it."); } if (pop) delete pop; delete socket; }
/*! 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; } }
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; } }
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; }
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; }