void FrontEnd::httpConnectorOnNewClient(HttpServerTask *task, Interface *intf, NPT_Socket *client) { NPT_Result nr; NPT_InputStreamReference inputStream0; nr = client->GetInputStream(inputStream0); if (NPT_FAILED(nr)) { return; } NPT_OutputStreamReference outputStream; nr = client->GetOutputStream(outputStream); if (NPT_FAILED(nr)) { return; } NPT_BufferedInputStreamReference inputStream(new NPT_BufferedInputStream(inputStream0)); NPT_HttpRequest *req; nr = NPT_HttpRequest::Parse(*inputStream.AsPointer(), NULL, req); if (NPT_FAILED(nr)) { return; } // TODO: validate "HOST" ??? RequestContext reqCtx; reqCtx.clientHint = CH_Unknown; reqCtx.transferMode = TM_None; reqCtx.getcontentFeaturesReq = false; NPT_HttpHeader *hdrUserAgent = req->GetHeaders().GetHeader(NPT_HTTP_HEADER_USER_AGENT); if (hdrUserAgent) { if (hdrUserAgent->GetValue().Find("xbox", 0, true) >= 0) { NPT_LOG_INFO_1("XBox found [User-Agent: %s]", hdrUserAgent->GetValue().GetChars()); reqCtx.clientHint = CH_XBox; } } NPT_HttpHeader *hdrTransferMode = req->GetHeaders().GetHeader("transferMode.dlna.org"); if (hdrTransferMode) { const NPT_String& transferMode = hdrTransferMode->GetValue(); if (transferMode.Compare("Streaming", true) == 0) { reqCtx.transferMode = TM_Streaming; } else if (transferMode.Compare("Interactive", true) == 0) { reqCtx.transferMode = TM_Interactive; } else if (transferMode.Compare("Background", true) == 0) { reqCtx.transferMode = TM_Background; } else { reqCtx.transferMode = TM_Unknown; } } NPT_HttpHeader *hdrGetContentFeatures = req->GetHeaders().GetHeader("getcontentFeatures.dlna.org"); if (hdrGetContentFeatures) { NPT_String getContentFeatures = hdrGetContentFeatures->GetValue(); if (getContentFeatures.Trim().Compare("1") == 0) { reqCtx.getcontentFeaturesReq = true; } } NPT_SocketInfo si; client->GetInfo(si); onHttpRequestHeader(si, req); PtrHolder<NPT_HttpRequest> req1(req); NPT_String reqPath(req->GetUrl().GetPath()); NPT_TimeStamp ts; NPT_System::GetCurrentTimeStamp(ts); NPT_String dateStr = NPT_DateTime(ts).ToString(NPT_DateTime::FORMAT_RFC_1123); NPT_HttpResponse *resp = new NPT_HttpResponse(200, "OK", NPT_HTTP_PROTOCOL_1_1); PtrHolder<NPT_HttpResponse> resp1(resp); resp->GetHeaders().SetHeader(NPT_HTTP_HEADER_SERVER, m_serverHeader); resp->GetHeaders().SetHeader("Date", dateStr); resp->GetHeaders().SetHeader(NPT_HTTP_HEADER_CONTENT_LENGTH, "0"); resp->GetHeaders().SetHeader(NPT_HTTP_HEADER_CONTENT_TYPE, "text/xml"); HttpOutput *httpOutput = new HttpOutputImpl(this, si, outputStream); PtrHolder<HttpOutput> httpOutput1(httpOutput); { ReadLocker locker(m_cpLock); for (NPT_Ordinal i = 0; i < m_controlPointList.GetItemCount(); i++) { NPT_List<ControlPointInfo*>::Iterator it = m_controlPointList.GetItem(i); ControlPointInfo *info = *it; if (reqPath.StartsWith(info->m_context.m_httpRoot)) { NPT_InputStream *input = inputStream.AsPointer(); inputStream.Detach(); httpOutput1.detach(); resp1.detach(); req1.detach(); task->detach(); return info->m_controlPoint->processHttpRequest(&intf->m_context, reqPath.SubString(info->m_context.m_httpRoot.GetLength()), reqCtx, req, resp, input, httpOutput, client); } } } { ReadLocker locker(m_dsLock); for (NPT_Ordinal i = 0; i < m_deviceImplList.GetItemCount(); i++) { NPT_List<DeviceImplInfo*>::Iterator it = m_deviceImplList.GetItem(i); DeviceImplInfo *info = *it; if (reqPath.StartsWith(info->m_context.m_httpRoot)) { NPT_InputStream *input = inputStream.AsPointer(); inputStream.Detach(); httpOutput1.detach(); resp1.detach(); req1.detach(); task->detach(); return info->m_deviceImpl->processHttpRequest(&intf->m_context, reqPath.SubString(info->m_context.m_httpRoot.GetLength()), reqCtx, req, resp, input, httpOutput, client); } } } setStatusCode(*resp, 404); httpOutput->writeResponseHeader(*resp); httpOutput->flush(); }
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 TActionContext::execute() { T_TRACEFUNC(); 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 timeout if (!httpSocket->waitForReadyRead(10000)) { tWarn("Read timed out after 10 seconds."); break; } } if (!httpSocket->canReadRequest()) { httpSocket->abort(); delete httpSocket; httpSocket = 0; return; } THttpRequest httpRequest = httpSocket->read(); const THttpRequestHeader &hd = httpRequest.header(); // Access log QByteArray firstLine = hd.method() + ' ' + hd.path(); firstLine += QString(" HTTP/%1.%2").arg(hd.majorVersion()).arg(hd.minorVersion()).toLatin1(); TAccessLog accessLog(httpSocket->peerAddress().toString().toLatin1(), firstLine); tSystemDebug("method : %s", hd.method().data()); tSystemDebug("path : %s", hd.path().data()); Tf::HttpMethod method = httpRequest.method(); QString path = THttpUtility::fromUrlEncoding(hd.path().split('?').value(0)); // Routing info exists? Routing 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()->treefrogSettings().value(DIRECT_VIEW_RENDER_MODE).toBool()) { // Direct view setting rt.controller = "directcontroller"; rt.action = "show"; QString tmp = rt.params.join("/"); rt.params.clear(); rt.params << tmp; } else { if (!rt.params.value(0).isEmpty()) { rt.controller = rt.params.takeFirst().toLower().toLatin1() + "controller"; if (!rt.params.value(0).isEmpty()) { rt.action = rt.params.takeFirst().toLatin1(); } } tSystemDebug("Active Controller : %s", rt.controller.data()); } } // Call controller method TDispatcher<TActionController> ctlrDispatcher(rt.controller); TActionController *controller = ctlrDispatcher.object(); if (controller) { controller->setActionName(rt.action); controller->setHttpRequest(httpRequest); openDatabase(); // Session if (controller->sessionEnabled()) { TSession session; QByteArray sessionId = httpRequest.cookie(TSession::sessionName()); if (!sessionId.isEmpty()) { session = TSessionManager::instance().find(sessionId); } controller->setSession(session); // Exports flash-variant controller->exportAllFlashVariants(); } // Verify authenticity token if (controller->forgeryProtectionEnabled() && !controller->protectFromForgeryExcept().contains(rt.action)) { if (method == Tf::Post || method == Tf::Put || method == Tf::Delete) { if (!controller->verifyRequest(httpRequest)) { throw SecurityException("Invalid authenticity token", __FILE__, __LINE__); } } } // Sets the default status code of HTTP response int statusCode = 404; // Not Found // Re-generate session ID if (controller->sessionEnabled() && (controller->session().id().isEmpty() || Tf::app()->treefrogSettings().value(AUTO_ID_REGENERATION).toBool())) { controller->session().regenerateId(); } if (controller->transactionEnabled()) { // Begins a transaction on the database beginTransaction(); } // Do filters if (controller->preFilter()) { // Dispathes bool dispatched = ctlrDispatcher.invoke(rt.action, rt.params); if (dispatched) { autoRemoveFiles = controller->autoRemoveFiles; // Set auto remove files // Post fileter controller->postFilter(); if (controller->transactionEnabled() && !controller->rollbackEnabled()) { // Commits a transaction to the database commitTransaction(); } // Session store if (controller->sessionEnabled()) { bool stored = TSessionManager::instance().store(controller->session()); if (stored) { controller->addCookie(TSession::sessionName(), controller->session().id(), QDateTime(), "/"); } } // Sets status code statusCode = (!controller->response.isBodyNull()) ? controller->statusCode() : 500 /* Internal Server Error */; } } controller->response.header().setStatusLine(statusCode, THttpUtility::getResponseReasonPhrase(statusCode)); accessLog.statusCode = statusCode; accessLog.responseBytes = writeResponse(controller->response.header(), controller->response.bodyIODevice(), controller->response.bodyLength()); // Outputs access log tAccessLog(accessLog); } if (!controller) { // GET Method? if (method == Tf::Get) { path.remove(0, 1); QFile reqPath(Tf::app()->publicPath() + path); QFileInfo fi(reqPath); if (fi.isFile() && fi.isReadable()) { QByteArray type = Tf::app()->internetMediaType(fi.suffix()); writeResponse(200, type, &reqPath, reqPath.size()); } else { writeResponse(404); // Not Found } } else if (method == Tf::Post) { // file upload? } else { // HEAD, DELETE, ... } } } catch (ClientErrorException &e) { tWarn("Caught ClientErrorException: status code:%d", e.statusCode()); writeResponse(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"); } closeDatabase(); httpSocket->disconnectFromHost(); // Destorys the object in the thread which created it delete httpSocket; httpSocket = 0; }