loolwsd/LOOLWSD.cpp | 337 +++++++++++++++++++++++++++------------------------- 1 file changed, 175 insertions(+), 162 deletions(-)
New commits: commit f9fd4c917a3acb75ee86dfca29e084542a77f756 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> Date: Sun Jan 24 14:48:09 2016 -0500 loolwsd: refactored HTTP Request Handler Change-Id: I84685910afa04664639ae674fd66ff888962387e Reviewed-on: https://gerrit.libreoffice.org/21757 Reviewed-by: Ashod Nakashian <ashnak...@gmail.com> Tested-by: Ashod Nakashian <ashnak...@gmail.com> diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp index 138fde7..bbb4c84 100644 --- a/loolwsd/LOOLWSD.cpp +++ b/loolwsd/LOOLWSD.cpp @@ -329,206 +329,219 @@ void SocketProcessor(std::shared_ptr<WebSocket> ws, /// Handle a public connection from a client. class ClientRequestHandler: public HTTPRequestHandler { -public: +private: - void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) override + void handlePostRequest(HTTPServerRequest& request, HTTPServerResponse& response, const std::string& id) { - const auto id = LOOLWSD::GenSessionId(); - const std::string thread_name = "client_ws_" + id; - -#ifdef __linux - if (prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(thread_name.c_str()), 0, 0, 0) != 0) - Log::error("Cannot set thread name to " + thread_name + "."); -#endif - Log::debug("Thread [" + thread_name + "] started."); - - if (!(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0)) + Log::info("Post request."); + StringTokenizer tokens(request.getURI(), "/?"); + if (tokens.count() >= 2 && tokens[1] == "convert-to") { - Log::info("Post request."); - StringTokenizer tokens(request.getURI(), "/?"); - if (tokens.count() >= 2 && tokens[1] == "convert-to") + Log::info("Conversion request."); + std::string fromPath; + ConvertToPartHandler handler(fromPath); + Poco::Net::HTMLForm form(request, request.stream(), handler); + std::string format; + if (form.has("format")) + format = form.get("format"); + + bool sent = false; + if (!fromPath.empty()) { - Log::info("Conversion request."); - std::string fromPath; - ConvertToPartHandler handler(fromPath); - Poco::Net::HTMLForm form(request, request.stream(), handler); - std::string format; - if (form.has("format")) - format = form.get("format"); - - bool sent = false; - if (!fromPath.empty()) + if (!format.empty()) { - if (!format.empty()) - { - // Load the document. - std::shared_ptr<WebSocket> ws; - const LOOLSession::Kind kind = LOOLSession::Kind::ToClient; - auto session = std::make_shared<MasterProcessSession>(id, kind, ws); - const std::string filePrefix("file://"); - std::string encodedFrom; - URI::encode(filePrefix + fromPath, std::string(), encodedFrom); - const std::string load = "load url=" + encodedFrom; - session->handleInput(load.data(), load.size()); - - // Convert it to the requested format. - Path toPath(fromPath); - toPath.setExtension(format); - std::string toJailURL = filePrefix + JailedDocumentRoot + toPath.getFileName(); - std::string encodedTo; - URI::encode(toJailURL, std::string(), encodedTo); + // Load the document. + std::shared_ptr<WebSocket> ws; + const LOOLSession::Kind kind = LOOLSession::Kind::ToClient; + auto session = std::make_shared<MasterProcessSession>(id, kind, ws); + const std::string filePrefix("file://"); + std::string encodedFrom; + URI::encode(filePrefix + fromPath, std::string(), encodedFrom); + const std::string load = "load url=" + encodedFrom; + session->handleInput(load.data(), load.size()); + + // Convert it to the requested format. + Path toPath(fromPath); + toPath.setExtension(format); + std::string toJailURL = filePrefix + JailedDocumentRoot + toPath.getFileName(); + std::string encodedTo; + URI::encode(toJailURL, std::string(), encodedTo); std::string saveas = "saveas url=" + encodedTo + " format=" + format + " options="; - session->handleInput(saveas.data(), saveas.size()); + session->handleInput(saveas.data(), saveas.size()); - std::string toURL = session->getSaveAs(); - std::string resultingURL; - URI::decode(toURL, resultingURL); + std::string toURL = session->getSaveAs(); + std::string resultingURL; + URI::decode(toURL, resultingURL); - // Send it back to the client. - if (resultingURL.find(filePrefix) == 0) - resultingURL = resultingURL.substr(filePrefix.length()); - if (!resultingURL.empty()) - { - const std::string mimeType = "application/octet-stream"; - response.sendFile(resultingURL, mimeType); - sent = true; - } + // Send it back to the client. + if (resultingURL.find(filePrefix) == 0) + resultingURL = resultingURL.substr(filePrefix.length()); + if (!resultingURL.empty()) + { + const std::string mimeType = "application/octet-stream"; + response.sendFile(resultingURL, mimeType); + sent = true; } - - // Clean up the temporary directory the HTMLForm ctor created. - Path tempDirectory(fromPath); - tempDirectory.setFileName(""); - Util::removeFile(tempDirectory, /*recursive=*/true); } - if (!sent) - { - response.setStatus(HTTPResponse::HTTP_BAD_REQUEST); - response.setContentLength(0); - response.send(); - } + // Clean up the temporary directory the HTMLForm ctor created. + Path tempDirectory(fromPath); + tempDirectory.setFileName(""); + Util::removeFile(tempDirectory, /*recursive=*/true); } - else if (tokens.count() >= 2 && tokens[1] == "insertfile") + + if (!sent) { - Log::info("Insert file request."); - response.set("Access-Control-Allow-Origin", "*"); - response.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); - response.set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + response.setStatus(HTTPResponse::HTTP_BAD_REQUEST); + response.setContentLength(0); + response.send(); + } + } + else if (tokens.count() >= 2 && tokens[1] == "insertfile") + { + Log::info("Insert file request."); + response.set("Access-Control-Allow-Origin", "*"); + response.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); + response.set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); - std::string tmpPath; - ConvertToPartHandler handler(tmpPath); - Poco::Net::HTMLForm form(request, request.stream(), handler); + std::string tmpPath; + ConvertToPartHandler handler(tmpPath); + Poco::Net::HTMLForm form(request, request.stream(), handler); - bool goodRequest = form.has("childid") && form.has("name"); - std::string formChildid(form.get("childid")); - std::string formName(form.get("name")); + bool goodRequest = form.has("childid") && form.has("name"); + std::string formChildid(form.get("childid")); + std::string formName(form.get("name")); - // protect against attempts to inject something funny here - if (goodRequest && formChildid.find('/') != std::string::npos && formName.find('/') != std::string::npos) - goodRequest = false; + // protect against attempts to inject something funny here + if (goodRequest && formChildid.find('/') != std::string::npos && formName.find('/') != std::string::npos) + goodRequest = false; - if (goodRequest) + if (goodRequest) + { + try { - try - { - Log::info() << "Perform insertfile: " << formChildid << ", " << formName << Log::end; - const std::string dirPath = LOOLWSD::ChildRoot + formChildid - + JailedDocumentRoot + "insertfile"; - File(dirPath).createDirectories(); - std::string fileName = dirPath + Path::separator() + form.get("name"); - File(tmpPath).moveTo(fileName); - - response.setStatus(HTTPResponse::HTTP_OK); - response.send(); - } - catch (const IOException& exc) - { - Log::info() << "IOException: " << exc.message() << Log::end; - response.setStatus(HTTPResponse::HTTP_BAD_REQUEST); - response.send(); - } + Log::info() << "Perform insertfile: " << formChildid << ", " << formName << Log::end; + const std::string dirPath = LOOLWSD::ChildRoot + formChildid + + JailedDocumentRoot + "insertfile"; + File(dirPath).createDirectories(); + std::string fileName = dirPath + Path::separator() + form.get("name"); + File(tmpPath).moveTo(fileName); + + response.setStatus(HTTPResponse::HTTP_OK); + response.send(); } - else + catch (const IOException& exc) { + Log::info() << "IOException: " << exc.message() << Log::end; response.setStatus(HTTPResponse::HTTP_BAD_REQUEST); response.send(); } } - else if (tokens.count() >= 4) + else { - Log::info("File download request."); - // The user might request a file to download - const std::string dirPath = LOOLWSD::ChildRoot + tokens[1] - + JailedDocumentRoot + tokens[2]; - std::string fileName; - URI::decode(tokens[3], fileName); - const std::string filePath = dirPath + Path::separator() + fileName; - Log::info("HTTP request for: " + filePath); - File file(filePath); - if (file.exists()) - { - response.set("Access-Control-Allow-Origin", "*"); - Poco::Net::HTMLForm form(request); - std::string mimeType = "application/octet-stream"; - if (form.has("mime_type")) - mimeType = form.get("mime_type"); - response.sendFile(filePath, mimeType); - Util::removeFile(dirPath, true); - } - else - { - response.setStatus(HTTPResponse::HTTP_NOT_FOUND); - response.setContentLength(0); - response.send(); - } + response.setStatus(HTTPResponse::HTTP_BAD_REQUEST); + response.send(); + } + } + else if (tokens.count() >= 4) + { + Log::info("File download request."); + // The user might request a file to download + const std::string dirPath = LOOLWSD::ChildRoot + tokens[1] + + JailedDocumentRoot + tokens[2]; + std::string fileName; + URI::decode(tokens[3], fileName); + const std::string filePath = dirPath + Path::separator() + fileName; + Log::info("HTTP request for: " + filePath); + File file(filePath); + if (file.exists()) + { + response.set("Access-Control-Allow-Origin", "*"); + Poco::Net::HTMLForm form(request); + std::string mimeType = "application/octet-stream"; + if (form.has("mime_type")) + mimeType = form.get("mime_type"); + response.sendFile(filePath, mimeType); + Util::removeFile(dirPath, true); } else { - Log::info("Bad request."); - response.setStatus(HTTPResponse::HTTP_BAD_REQUEST); + response.setStatus(HTTPResponse::HTTP_NOT_FOUND); response.setContentLength(0); response.send(); } - return; } - - try + else { - Log::info("Get request."); - auto ws = std::make_shared<WebSocket>(request, response); - auto session = std::make_shared<MasterProcessSession>(id, LOOLSession::Kind::ToClient, ws); + Log::info("Bad request."); + response.setStatus(HTTPResponse::HTTP_BAD_REQUEST); + response.setContentLength(0); + response.send(); + } + } + + void handleGetRequest(HTTPServerRequest& request, HTTPServerResponse& response, const std::string& id) + { + Log::info("Get request."); + auto ws = std::make_shared<WebSocket>(request, response); + auto session = std::make_shared<MasterProcessSession>(id, LOOLSession::Kind::ToClient, ws); - // For ToClient sessions, we store incoming messages in a queue and have a separate - // thread that handles them. This is so that we can empty the queue when we get a - // "canceltiles" message. - BasicTileQueue queue; - QueueHandler handler(queue, session, "wsd_queue_" + session->getId()); + // For ToClient sessions, we store incoming messages in a queue and have a separate + // thread that handles them. This is so that we can empty the queue when we get a + // "canceltiles" message. + BasicTileQueue queue; + QueueHandler handler(queue, session, "wsd_queue_" + session->getId()); - Thread queueHandlerThread; - queueHandlerThread.start(handler); + Thread queueHandlerThread; + queueHandlerThread.start(handler); - SocketProcessor(ws, response, [&session, &queue](const char* data, const int size, const bool singleLine) + SocketProcessor(ws, response, [&session, &queue](const char* data, const int size, const bool singleLine) + { + // FIXME: There is a race here when a request A gets in the queue and + // is processed _after_ a later request B, because B gets processed + // synchronously and A is waiting in the queue thread. + // The fix is to push everything into the queue + // (i.e. change MessageQueue to vector<char>). + const std::string firstLine = getFirstLine(data, size); + if (singleLine || firstLine.find("paste") == 0) { - // FIXME: There is a race here when a request A gets in the queue and - // is processed _after_ a later request B, because B gets processed - // synchronously and A is waiting in the queue thread. - // The fix is to push everything into the queue - // (i.e. change MessageQueue to vector<char>). - const std::string firstLine = getFirstLine(data, size); - if (singleLine || firstLine.find("paste") == 0) - { - queue.put(std::string(data, size)); - return true; - } - else - { - return session->handleInput(data, size); - } - }); + queue.put(std::string(data, size)); + return true; + } + else + { + return session->handleInput(data, size); + } + }); + + queue.clear(); + queue.put("eof"); + queueHandlerThread.join(); + } + +public: + + void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) override + { + const auto id = LOOLWSD::GenSessionId(); + const std::string thread_name = "client_ws_" + id; - queue.clear(); - queue.put("eof"); - queueHandlerThread.join(); +#ifdef __linux + if (prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(thread_name.c_str()), 0, 0, 0) != 0) + Log::error("Cannot set thread name to " + thread_name + "."); +#endif + Log::debug("Thread [" + thread_name + "] started."); + + try + { + if (!(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0)) + { + handlePostRequest(request, response, id); + } + else + { + handleGetRequest(request, response, id); + } } catch (const Exception& exc) { _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits