void TActionForkProcess::start() { if (currentActionContext) return; currentActionContext = this; std::cerr << "_accepted" << std::flush; // send to tfmanager QList<THttpRequest> reqs; QEventLoop eventLoop; httpSocket = new THttpSocket; if (!httpSocket->setSocketDescriptor(TActionContext::socketDesc)) { emitError(httpSocket->error()); goto socket_error; } TActionContext::socketDesc = 0; for (;;) { reqs = TActionThread::readRequest(httpSocket); tSystemDebug("HTTP request count: %d", reqs.count()); if (reqs.isEmpty()) { break; } for (QMutableListIterator<THttpRequest> it(reqs); it.hasNext(); ) { THttpRequest &req = it.next(); TActionContext::execute(req); httpSocket->flush(); // Flush socket TActionContext::release(); } if (!httpSocket->waitForReadyRead(5000)) break; } closeHttpSocket(); // disconnect // For cleanup while (eventLoop.processEvents()) {} emit finished(); QCoreApplication::exit(1); socket_error: delete httpSocket; httpSocket = 0; }
void clHttp::stop() { closeThread = true; mutex.lock(); for(int i=0;i<listConnections.count();++i) { clProcessingHttp *l_clientThread = listConnections.at(i); delete l_clientThread; listConnections.replace(i, NULL); } listConnections.removeAll(NULL); mutex.unlock(); closeHttpSocket(); #ifdef Q_OS_WIN WSACleanup(); #endif wait(200); }
void TActionThread::run() { QList<THttpRequest> reqs; QEventLoop eventLoop; httpSocket = new THttpSocket; if (!httpSocket->setSocketDescriptor(TActionContext::socketDesc)) { emitError(httpSocket->error()); goto socket_error; } TActionContext::socketDesc = 0; for (;;) { reqs = readRequest(httpSocket); tSystemDebug("HTTP request count: %d", reqs.count()); if (reqs.isEmpty()) break; for (QMutableListIterator<THttpRequest> it(reqs); it.hasNext(); ) { THttpRequest &req = it.next(); TActionContext::execute(req); httpSocket->flush(); // Flush socket TActionContext::release(); } if (!httpSocket->waitForReadyRead(5000)) break; } closeHttpSocket(); // disconnect // For cleanup while (eventLoop.processEvents()) {} socket_error: delete httpSocket; httpSocket = 0; }
void TActionContext::execute() { T_TRACEFUNC(""); THttpResponseHeader responseHeader; accessLogger.open(); try { if (!readRequest()) { return; } const THttpRequestHeader &hdr = httpReq->header(); // Access log accessLogger.setTimestamp(QDateTime::currentDateTime()); QByteArray firstLine = hdr.method() + ' ' + hdr.path(); firstLine += QString(" HTTP/%1.%2").arg(hdr.majorVersion()).arg(hdr.minorVersion()).toLatin1(); accessLogger.setRequest(firstLine); accessLogger.setRemoteHost( (Tf::app()->appSettings().value(LISTEN_PORT).toUInt() > 0) ? clientAddress().toString().toLatin1() : QByteArray("(unix)") ); tSystemDebug("method : %s", hdr.method().data()); tSystemDebug("path : %s", hdr.path().data()); Tf::HttpMethod method = httpReq->method(); QString path = THttpUtility::fromUrlEncoding(hdr.path().mid(0, hdr.path().indexOf('?'))); // 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); // Session if (currController->sessionEnabled()) { TSession session; QByteArray sessionId = httpReq->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(*httpReq)) { 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 charset to the content-type QByteArray ctype = currController->response.header().contentType().toLower(); if (ctype.startsWith("text") && !ctype.contains("charset")) { ctype += "; charset="; ctype += Tf::app()->codecForHttpOutput()->name(); currController->response.header().setContentType(ctype); } // Sets the default status code of HTTP response accessLogger.setStatusCode( (!currController->response.isBodyNull()) ? currController->statusCode() : Tf::InternalServerError ); currController->response.header().setStatusLine(accessLogger.statusCode(), THttpUtility::getResponseReasonPhrase(accessLogger.statusCode())); // Writes a response and access log int bytes = writeResponse(currController->response.header(), currController->response.bodyIODevice(), currController->response.bodyLength()); accessLogger.setResponseBytes(bytes); // Session GC TSessionManager::instance().collectGarbage(); } else { accessLogger.setStatusCode( 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()); int bytes = writeResponse(Tf::OK, responseHeader, type, &reqPath, reqPath.size()); accessLogger.setResponseBytes( bytes ); } else { // Not send the data int bytes = writeResponse(Tf::NotModified, responseHeader); accessLogger.setResponseBytes( bytes ); } } else { int bytes = writeResponse(Tf::NotFound, responseHeader); accessLogger.setResponseBytes( bytes ); } accessLogger.setStatusCode( responseHeader.statusCode() ); } else if (method == Tf::Post) { // file upload? } else { // HEAD, DELETE, ... } } } catch (ClientErrorException &e) { tWarn("Caught ClientErrorException: status code:%d", e.statusCode()); int bytes = writeResponse(e.statusCode(), responseHeader); accessLogger.setResponseBytes( bytes ); accessLogger.setStatusCode( 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()); closeHttpSocket(); } 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()); closeHttpSocket(); } 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()); closeHttpSocket(); } 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()); closeHttpSocket(); } catch (...) { tError("Caught Exception"); tSystemError("Caught Exception"); closeHttpSocket(); } // Push to the pool TActionContext::releaseSqlDatabases(); TActionContext::releaseKvsDatabases(); TActionContext::accessLogger.write(); // Writes access log releaseHttpSocket(); }
void clHttp::run() { while(!closeThread) { sockHttp = socket(AF_INET,SOCK_STREAM,0); #ifdef Q_OS_UNIX if(sockHttp == -1) { #else if(sockHttp == INVALID_SOCKET) { #endif debug.add(ERR, "Error creating socket"); emit started(false); return; } int iBool = 1; if(setsockopt(sockHttp, SOL_SOCKET, SO_REUSEADDR, (char *)&iBool, sizeof(iBool)) != 0) { #ifdef Q_OS_UNIX debug.add(ERR, "Error setsockopt: " + QString::fromLocal8Bit(strerror(errno))); #else debug.add(ERR, "Error setsockopt: " + QString().setNum(WSAGetLastError())); #endif } #ifdef Q_OS_UNIX if(fcntl(sockHttp, F_SETFD, FD_CLOEXEC) == -1) { // Освобождает дискриптеры после закрытия сокета debug.add(ERR, "Error set FD_CLOEXEC socket: " + QString::fromLocal8Bit(strerror(errno))); } #endif sockaddr_in local_addr; local_addr.sin_family = AF_INET; local_addr.sin_addr.s_addr = INADDR_ANY; local_addr.sin_port = htons(port); if(bind(sockHttp, (sockaddr *)&local_addr, sizeof(local_addr))) { #ifdef Q_OS_UNIX debug.add(ERR, "Erorr bind port " + QString().setNum(port) + ": " + QString::fromLocal8Bit(strerror(errno))); #else debug.add(ERR, "Erorr bind port " + QString().setNum(port) + ": " + QString().setNum(WSAGetLastError())); #endif closeHttpSocket(); unsigned long time = 0; while(!closeThread && time < 3*1000) { msleep(200); time += 200; } continue; } if(listen(sockHttp, 10000) == -1) { #ifdef Q_OS_UNIX debug.add(ERR, "Can't listen on port " + QString().setNum(port) + ": " + QString::fromLocal8Bit(strerror(errno))); #else debug.add(ERR, "Can't listen on port " + QString().setNum(port) + ": " + QString().setNum(WSAGetLastError())); #endif closeHttpSocket(); unsigned long time = 0; while(!closeThread && time < 3*1000) { msleep(200); time += 200; } continue; } debug.add(NOTICE, "Started on port " + QString().setNum(htons(local_addr.sin_port)) + " (id: " + QString().setNum((unsigned long)QThread::currentThreadId()) + ")"); emit started(true); break; } //*********************** fd_set readSet, errorSet; FD_ZERO(&readSet); FD_ZERO(&errorSet); FD_SET(sockHttp, &errorSet); FD_SET(sockHttp, &readSet); int SelectTiming = 0; //************************ if ((SelectTiming = select(sockHttp+1, &readSet, 0, &errorSet, 0)) == -1) { #ifdef Q_OS_UNIX debug.add(ERR, "Error on select accept: " + QString::fromLocal8Bit(strerror(errno))); #else debug.add(ERR, "Error on select accept: " + QString().setNum(WSAGetLastError())); #endif closeHttpSocket(); emit started(false); return; } if(SelectTiming > 0) { if (FD_ISSET(sockHttp, &errorSet)) { debug.add(ERR, "Error connect server"); closeHttpSocket(); emit started(false); return; } if(FD_ISSET(sockHttp, &readSet)) { SOCKET client_socket; sockaddr_in client_addr; memset(&client_addr, 0, sizeof(sockaddr_in)); while(!closeThread) { int client_addr_size = sizeof(client_addr); #ifdef Q_OS_UNIX client_socket = accept(sockHttp, (sockaddr *)&client_addr, (socklen_t *)&client_addr_size); #else client_socket = accept(sockHttp, (sockaddr *)&client_addr, &client_addr_size); #endif if(client_socket > 0) { processingHttp = new clProcessingHttp(client_socket); connect(processingHttp, SIGNAL(disconnectClient(clProcessingHttp*)), this, SLOT(disconnectClient(clProcessingHttp*))); connect(processingHttp, SIGNAL(request(clHtmlData*)), this, SIGNAL(request(clHtmlData*))); connect(processingHttp, SIGNAL(error(QString,QString)), this, SIGNAL(error(QString,QString))); mutex.lock(); listConnections.append(processingHttp); mutex.unlock(); processingHttp->start(); memset(&client_addr, 0, sizeof(sockaddr_in)); } } } }