[Libreoffice-commits] online.git: 2 commits - common/Common.hpp net/Socket.hpp wsd/LOOLWSD.cpp
Michael Meeks
michael.meeks at collabora.com
Tue Apr 25 21:49:49 UTC 2017
common/Common.hpp | 4
net/Socket.hpp | 39 ++++++
wsd/LOOLWSD.cpp | 312 ++++++++++++++++++++++++------------------------------
3 files changed, 181 insertions(+), 174 deletions(-)
New commits:
commit faf24f7888badcca4d851d289a7b9e31da9d4e5c
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Tue Apr 25 22:12:07 2017 +0100
SocketDisposition - controls socket movement & lifecyle.
It is important that moving sockets between polls happens in a
safe and readable fashion, this enforces that.
diff --git a/net/Socket.hpp b/net/Socket.hpp
index 014f023d..694e82a5 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -198,6 +198,11 @@ public:
}
}
+ const std::thread::id &getThreadOwner()
+ {
+ return _owner;
+ }
+
/// Asserts in the debug builds, otherwise just logs.
void assertCorrectThread()
{
@@ -250,7 +255,6 @@ private:
std::thread::id _owner;
};
-
/// Handles non-blocking socket event polling.
/// Only polls on N-Sockets and invokes callback and
/// doesn't manage buffers or client data.
@@ -913,6 +917,39 @@ protected:
friend class SimpleResponseClient;
};
+/// Helper to allow us to easily defer the movement of a socket
+/// between polls to clarify thread ownership.
+class SocketDisposition
+{
+ std::shared_ptr<StreamSocket> _socket;
+ typedef std::function<void(const std::shared_ptr<StreamSocket> &)> MoveFunction;
+ MoveFunction _socketMove;
+ SocketHandlerInterface::SocketOwnership _socketOwnership;
+public:
+ SocketDisposition(const std::shared_ptr<StreamSocket> &socket) :
+ _socket(socket),
+ _socketOwnership(SocketHandlerInterface::SocketOwnership::UNCHANGED)
+ {}
+ ~SocketDisposition()
+ {
+ assert (!_socketMove);
+ }
+ void setMove(MoveFunction moveFn)
+ {
+ _socketMove = moveFn;
+ _socketOwnership = SocketHandlerInterface::SocketOwnership::MOVED;
+ }
+ SocketHandlerInterface::SocketOwnership execute()
+ {
+ // We should have hard ownership of this socket.
+ assert(_socket->getThreadOwner() == std::this_thread::get_id());
+ if (_socketMove)
+ _socketMove(_socket);
+ _socketMove = nullptr;
+ return _socketOwnership;
+ }
+};
+
namespace HttpHelper
{
/// Sends file as HTTP response.
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 6071c85e..a783b269 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1591,7 +1591,7 @@ private:
return SocketHandlerInterface::SocketOwnership::UNCHANGED;
}
- SocketHandlerInterface::SocketOwnership socketOwnership = SocketHandlerInterface::SocketOwnership::UNCHANGED;
+ SocketDisposition tailDisposition(socket);
try
{
// Routing
@@ -1607,12 +1607,13 @@ private:
// Admin connections
else if (reqPathSegs.size() >= 2 && reqPathSegs[0] == "lool" && reqPathSegs[1] == "adminws")
{
- LOG_ERR("Admin request: " << request.getURI());
+ LOG_INF("Admin request: " << request.getURI());
if (AdminSocketHandler::handleInitialRequest(_socket, request))
{
- // Hand the socket over to the Admin poll.
- Admin::instance().insertNewSocket(socket);
- socketOwnership = SocketHandlerInterface::SocketOwnership::MOVED;
+ tailDisposition.setMove([](const std::shared_ptr<StreamSocket> &moveSocket){
+ // Hand the socket over to the Admin poll.
+ Admin::instance().insertNewSocket(moveSocket);
+ });
}
}
// Client post and websocket connections
@@ -1637,12 +1638,12 @@ private:
reqPathTokens.count() > 0 && reqPathTokens[0] == "lool")
{
// All post requests have url prefix 'lool'.
- socketOwnership = handlePostRequest(request, message);
+ handlePostRequest(request, message, tailDisposition);
}
else if (reqPathTokens.count() > 2 && reqPathTokens[0] == "lool" && reqPathTokens[2] == "ws" &&
request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0)
{
- socketOwnership = handleClientWsUpgrade(request, reqPathTokens[1]);
+ handleClientWsUpgrade(request, reqPathTokens[1], tailDisposition);
}
else
{
@@ -1671,7 +1672,7 @@ private:
// if we succeeded - remove the request from our input buffer
// we expect one request per socket
in.erase(in.begin(), itBody);
- return socketOwnership;
+ return tailDisposition.execute();
}
int getPollEvents(std::chrono::steady_clock::time_point /* now */,
@@ -1806,14 +1807,14 @@ private:
return "application/octet-stream";
}
- SocketHandlerInterface::SocketOwnership handlePostRequest(const Poco::Net::HTTPRequest& request, Poco::MemoryInputStream& message)
+ void handlePostRequest(const Poco::Net::HTTPRequest& request, Poco::MemoryInputStream& message,
+ SocketDisposition &tailDisposition)
{
LOG_INF("Post request: [" << request.getURI() << "]");
Poco::Net::HTTPResponse response;
auto socket = _socket.lock();
- SocketHandlerInterface::SocketOwnership socketOwnership = SocketHandlerInterface::SocketOwnership::UNCHANGED;
StringTokenizer tokens(request.getURI(), "/?");
if (tokens.count() >= 3 && tokens[2] == "convert-to")
{
@@ -1851,21 +1852,22 @@ private:
auto clientSession = createNewClientSession(nullptr, _id, uriPublic, docBroker, isReadOnly);
if (clientSession)
{
- // Transfer the client socket to the DocumentBroker.
- socketOwnership = SocketHandlerInterface::SocketOwnership::MOVED;
+ tailDisposition.setMove([docBroker, clientSession, format]
+ (const std::shared_ptr<StreamSocket> &moveSocket)
+ { // Perform all of this after removing the socket
// Make sure the thread is running before adding callback.
docBroker->startThread();
// We no longer own this socket.
- socket->setThreadOwner(std::thread::id(0));
+ moveSocket->setThreadOwner(std::thread::id(0));
- docBroker->addCallback([docBroker, socket, clientSession, format]()
+ docBroker->addCallback([docBroker, moveSocket, clientSession, format]()
{
- clientSession->setSaveAsSocket(socket);
+ clientSession->setSaveAsSocket(moveSocket);
// Move the socket into DocBroker.
- docBroker->addSocketToPoll(socket);
+ docBroker->addSocketToPoll(moveSocket);
// First add and load the session.
docBroker->addSession(clientSession);
@@ -1889,6 +1891,7 @@ private:
std::vector<char> saveasRequest(saveas.begin(), saveas.end());
clientSession->handleMessage(true, WebSocketHandler::WSOpCode::Text, saveasRequest);
});
+ });
sent = true;
}
@@ -1902,8 +1905,6 @@ private:
// TODO: We should differentiate between bad request and failed conversion.
throw BadRequestException("Failed to convert and send file.");
}
-
- return socketOwnership;
}
else if (tokens.count() >= 4 && tokens[3] == "insertfile")
{
@@ -1943,7 +1944,7 @@ private:
File(tmpPath).moveTo(fileName);
response.setContentLength(0);
socket->send(response);
- return socketOwnership;
+ return;
}
}
}
@@ -2010,21 +2011,20 @@ private:
{
LOG_ERR("Download file [" << filePath.toString() << "] not found.");
}
-
(void)responded;
- return socketOwnership;
}
throw BadRequestException("Invalid or unknown request.");
}
- SocketHandlerInterface::SocketOwnership handleClientWsUpgrade(const Poco::Net::HTTPRequest& request, const std::string& url)
+ void handleClientWsUpgrade(const Poco::Net::HTTPRequest& request, const std::string& url,
+ SocketDisposition &tailDisposition)
{
auto socket = _socket.lock();
if (!socket)
{
LOG_WRN("No socket to handle client WS upgrade for request: " << request.getURI() << ", url: " << url);
- return SocketHandlerInterface::SocketOwnership::UNCHANGED;
+ return;
}
LOG_INF("Client WS request: " << request.getURI() << ", url: " << url << ", socket #" << socket->getFD());
@@ -2036,7 +2036,7 @@ private:
{
LOG_ERR("Limit on maximum number of connections of " << MAX_CONNECTIONS << " reached.");
shutdownLimitReached(ws);
- return SocketHandlerInterface::SocketOwnership::UNCHANGED;
+ return;
}
LOG_INF("Starting GET request handler for session [" << _id << "] on url [" << url << "].");
@@ -2064,8 +2064,6 @@ private:
LOG_INF("URL [" << url << "] is " << (isReadOnly ? "readonly" : "writable") << ".");
- SocketHandlerInterface::SocketOwnership socketOwnership = SocketHandlerInterface::SocketOwnership::UNCHANGED;
-
// Request a kit process for this doc.
auto docBroker = findOrCreateDocBroker(ws, url, docKey, _id, uriPublic);
if (docBroker)
@@ -2073,28 +2071,28 @@ private:
auto clientSession = createNewClientSession(&ws, _id, uriPublic, docBroker, isReadOnly);
if (clientSession)
{
- // Transfer the client socket to the DocumentBroker.
-
- // Remove from current poll as we're moving ownership.
- socketOwnership = SocketHandlerInterface::SocketOwnership::MOVED;
-
- // Make sure the thread is running before adding callback.
- docBroker->startThread();
+ // Transfer the client socket to the DocumentBroker when we get back to the poll:
+ tailDisposition.setMove([docBroker, clientSession]
+ (const std::shared_ptr<StreamSocket> &moveSocket)
+ {
+ // Make sure the thread is running before adding callback.
+ docBroker->startThread();
- // We no longer own this socket.
- socket->setThreadOwner(std::thread::id(0));
+ // We no longer own this socket.
+ moveSocket->setThreadOwner(std::thread::id(0));
- docBroker->addCallback([docBroker, socket, clientSession]()
- {
- // Set the ClientSession to handle Socket events.
- socket->setHandler(clientSession);
- LOG_DBG("Socket #" << socket->getFD() << " handler is " << clientSession->getName());
+ docBroker->addCallback([docBroker, moveSocket, clientSession]()
+ {
+ // Set the ClientSession to handle Socket events.
+ moveSocket->setHandler(clientSession);
+ LOG_DBG("Socket #" << moveSocket->getFD() << " handler is " << clientSession->getName());
- // Move the socket into DocBroker.
- docBroker->addSocketToPoll(socket);
+ // Move the socket into DocBroker.
+ docBroker->addSocketToPoll(moveSocket);
- // Add and load the session.
- docBroker->addSession(clientSession);
+ // Add and load the session.
+ docBroker->addSession(clientSession);
+ });
});
}
else
@@ -2105,8 +2103,6 @@ private:
}
else
LOG_WRN("Failed to create DocBroker with docKey [" << docKey << "].");
-
- return socketOwnership;
}
private:
commit c8d1c18cb54acf82f8b6de2252365eac93e90a58
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Tue Apr 25 21:35:20 2017 +0100
Revert "wsd: correctly remove request from socket buffer"
This reverts commit c851c3e93be565dfa14906febbdf6208e9d422cd.
diff --git a/common/Common.hpp b/common/Common.hpp
index b0b018b2..09bafad3 100644
--- a/common/Common.hpp
+++ b/common/Common.hpp
@@ -34,8 +34,8 @@ constexpr auto CHILD_URI = "/loolws/child?";
constexpr auto NEW_CHILD_URI = "/loolws/newchild?";
constexpr auto LO_JAIL_SUBPATH = "lo";
-/// The HTTP response User-Agent.
-constexpr auto HTTP_AGENT_STRING = "LOOLWSD Agent " LOOLWSD_VERSION;
+/// The HTTP response User-Agent. TODO: Include version.
+constexpr auto HTTP_AGENT_STRING = "LOOLWSD Agent";
// The client port number, both loolwsd and the kits have this.
extern int ClientPortNumber;
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 4e136d4e..6071c85e 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1598,47 +1598,46 @@ private:
Poco::URI requestUri(request.getURI());
std::vector<std::string> reqPathSegs;
requestUri.getPathSegments(reqPathSegs);
- const StringTokenizer reqPathTokens(request.getURI(), "/?", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
- if (request.getMethod() == HTTPRequest::HTTP_GET ||
- request.getMethod() == HTTPRequest::HTTP_HEAD)
+ // File server
+ if (reqPathSegs.size() >= 1 && reqPathSegs[0] == "loleaflet")
{
- // Clear the request header already consumed.
- in.erase(in.begin(), itBody);
-
- // File server
- if (!(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0) &&
- reqPathTokens.count() >= 5 && reqPathTokens[0] == "lool")
- {
- // All post requests have url prefix 'lool'.
- handleFileDownloadRequest(request);
- }
- else if (reqPathSegs.size() >= 1 && reqPathSegs[0] == "loleaflet")
- {
- handleFileServerRequest(request, message);
- }
- else if (reqPathSegs.size() >= 2 && reqPathSegs[0] == "lool" && reqPathSegs[1] == "adminws")
- {
- // Admin connections
- LOG_INF("Admin request: " << request.getURI());
- if (AdminSocketHandler::handleInitialRequest(_socket, request))
- {
- // Hand the socket over to the Admin poll.
- Admin::instance().insertNewSocket(socket);
- socketOwnership = SocketHandlerInterface::SocketOwnership::MOVED;
- }
- }
- else if (request.getURI() == "/")
- {
- handleRootRequest(request);
- }
- else if (request.getURI() == "/favicon.ico")
+ handleFileServerRequest(request, message);
+ }
+ // Admin connections
+ else if (reqPathSegs.size() >= 2 && reqPathSegs[0] == "lool" && reqPathSegs[1] == "adminws")
+ {
+ LOG_ERR("Admin request: " << request.getURI());
+ if (AdminSocketHandler::handleInitialRequest(_socket, request))
{
- handleFaviconRequest(request);
+ // Hand the socket over to the Admin poll.
+ Admin::instance().insertNewSocket(socket);
+ socketOwnership = SocketHandlerInterface::SocketOwnership::MOVED;
}
- else if (request.getURI() == "/hosting/discovery")
+ }
+ // Client post and websocket connections
+ else if ((request.getMethod() == HTTPRequest::HTTP_GET ||
+ request.getMethod() == HTTPRequest::HTTP_HEAD) &&
+ request.getURI() == "/")
+ {
+ handleRootRequest(request);
+ }
+ else if (request.getMethod() == HTTPRequest::HTTP_GET && request.getURI() == "/favicon.ico")
+ {
+ handleFaviconRequest(request);
+ }
+ else if (request.getMethod() == HTTPRequest::HTTP_GET && request.getURI() == "/hosting/discovery")
+ {
+ handleWopiDiscoveryRequest(request);
+ }
+ else
+ {
+ StringTokenizer reqPathTokens(request.getURI(), "/?", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+ if (!(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0) &&
+ reqPathTokens.count() > 0 && reqPathTokens[0] == "lool")
{
- handleWopiDiscoveryRequest(request);
+ // All post requests have url prefix 'lool'.
+ socketOwnership = handlePostRequest(request, message);
}
else if (reqPathTokens.count() > 2 && reqPathTokens[0] == "lool" && reqPathTokens[2] == "ws" &&
request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0)
@@ -1647,31 +1646,19 @@ private:
}
else
{
- throw std::runtime_error("Bad request.");
- }
-
- return socketOwnership;
- }
- else if (request.getMethod() == HTTPRequest::HTTP_POST)
- {
- if (reqPathSegs.size() >= 1 && reqPathSegs[0] == "loleaflet")
- {
- handleFileServerRequest(request, message);
- return socketOwnership;
- }
-
- if (!(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0) &&
- reqPathTokens.count() > 0 && reqPathTokens[0] == "lool")
- {
- // All post requests have url prefix 'lool'.
- socketOwnership = handlePostRequest(request, message);
- in.erase(in.begin(), itBody);
- return socketOwnership;
+ LOG_ERR("Unknown resource: " << request.getURI());
+
+ // Bad request.
+ std::ostringstream oss;
+ oss << "HTTP/1.1 400\r\n"
+ << "Date: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
+ << "User-Agent: LOOLWSD WOPI Agent\r\n"
+ << "Content-Length: 0\r\n"
+ << "\r\n";
+ socket->send(oss.str());
+ socket->shutdown();
}
}
-
- LOG_ERR("#" << socket->getFD() << " Unknown request: [" <<
- LOOLProtocol::getAbbreviatedMessage(in) << "].");
}
catch (const std::exception& exc)
{
@@ -1681,17 +1668,10 @@ private:
LOOLProtocol::getAbbreviatedMessage(in) << "]: " << exc.what());
}
- // Bad request.
- std::ostringstream oss;
- oss << "HTTP/1.1 400\r\n"
- << "Date: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
- << "User-Agent: " << HTTP_AGENT_STRING << "\r\n"
- << "Content-Length: 0\r\n"
- << "\r\n";
- socket->send(oss.str());
- socket->shutdown();
-
- return SocketHandlerInterface::SocketOwnership::UNCHANGED;
+ // if we succeeded - remove the request from our input buffer
+ // we expect one request per socket
+ in.erase(in.begin(), itBody);
+ return socketOwnership;
}
int getPollEvents(std::chrono::steady_clock::time_point /* now */,
@@ -1967,81 +1947,75 @@ private:
}
}
}
-
- throw BadRequestException("Invalid or unknown request.");
- }
-
- void handleFileDownloadRequest(const Poco::Net::HTTPRequest& request)
- {
- LOG_INF("File download request.");
-
- Poco::Net::HTTPResponse response;
- auto socket = _socket.lock();
-
- StringTokenizer tokens(request.getURI(), "/?");
- // TODO: Check that the user in question has access to this file!
-
- // 1. Validate the dockey
- std::string decodedUri;
- URI::decode(tokens[2], decodedUri);
- const auto docKey = DocumentBroker::getDocKey(DocumentBroker::sanitizeURI(decodedUri));
- std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex);
- auto docBrokerIt = DocBrokers.find(docKey);
- if (docBrokerIt == DocBrokers.end())
+ else if (tokens.count() >= 6)
{
- throw BadRequestException("DocKey [" + docKey + "] is invalid.");
- }
+ LOG_INF("File download request.");
+ // TODO: Check that the user in question has access to this file!
- // 2. Cross-check if received child id is correct
- if (docBrokerIt->second->getJailId() != tokens[3])
- {
- throw BadRequestException("ChildId does not correspond to docKey");
- }
+ // 1. Validate the dockey
+ std::string decodedUri;
+ URI::decode(tokens[2], decodedUri);
+ const auto docKey = DocumentBroker::getDocKey(DocumentBroker::sanitizeURI(decodedUri));
+ std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex);
+ auto docBrokerIt = DocBrokers.find(docKey);
+ if (docBrokerIt == DocBrokers.end())
+ {
+ throw BadRequestException("DocKey [" + docKey + "] is invalid.");
+ }
- // 3. Don't let user download the file in main doc directory containing
- // the document being edited otherwise we will end up deleting main directory
- // after download finishes
- if (docBrokerIt->second->getJailId() == tokens[4])
- {
- throw BadRequestException("RandomDir cannot be equal to ChildId");
- }
- docBrokersLock.unlock();
-
- std::string fileName;
- bool responded = false;
- URI::decode(tokens[5], fileName);
- const Path filePath(LOOLWSD::ChildRoot + tokens[3]
- + JAILED_DOCUMENT_ROOT + tokens[4] + "/" + fileName);
- LOG_INF("HTTP request for: " << filePath.toString());
- if (filePath.isAbsolute() && File(filePath).exists())
- {
- std::string contentType = getContentType(fileName);
- if (Poco::Path(fileName).getExtension() == "pdf")
+ // 2. Cross-check if received child id is correct
+ if (docBrokerIt->second->getJailId() != tokens[3])
{
- contentType = "application/pdf";
- response.set("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
+ throw BadRequestException("ChildId does not correspond to docKey");
}
- try
+ // 3. Don't let user download the file in main doc directory containing
+ // the document being edited otherwise we will end up deleting main directory
+ // after download finishes
+ if (docBrokerIt->second->getJailId() == tokens[4])
{
- HttpHelper::sendFile(socket, filePath.toString(), contentType, response);
- responded = true;
+ throw BadRequestException("RandomDir cannot be equal to ChildId");
+ }
+ docBrokersLock.unlock();
+
+ std::string fileName;
+ bool responded = false;
+ URI::decode(tokens[5], fileName);
+ const Path filePath(LOOLWSD::ChildRoot + tokens[3]
+ + JAILED_DOCUMENT_ROOT + tokens[4] + "/" + fileName);
+ LOG_INF("HTTP request for: " << filePath.toString());
+ if (filePath.isAbsolute() && File(filePath).exists())
+ {
+ std::string contentType = getContentType(fileName);
+ if (Poco::Path(fileName).getExtension() == "pdf")
+ {
+ contentType = "application/pdf";
+ response.set("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
+ }
+
+ try
+ {
+ HttpHelper::sendFile(socket, filePath.toString(), contentType, response);
+ responded = true;
+ }
+ catch (const Exception& exc)
+ {
+ LOG_ERR("Error sending file to client: " << exc.displayText() <<
+ (exc.nested() ? " (" + exc.nested()->displayText() + ")" : ""));
+ }
+
+ FileUtil::removeFile(File(filePath.parent()).path(), true);
}
- catch (const Exception& exc)
+ else
{
- LOG_ERR("Error sending file to client: " << exc.displayText() <<
- (exc.nested() ? " (" + exc.nested()->displayText() + ")" : ""));
+ LOG_ERR("Download file [" << filePath.toString() << "] not found.");
}
- FileUtil::removeFile(File(filePath.parent()).path(), true);
- }
- else
- {
- LOG_ERR("Download file [" << filePath.toString() << "] not found.");
+ (void)responded;
+ return socketOwnership;
}
- // TODO: Use this to send failure response.
- (void)responded;
+ throw BadRequestException("Invalid or unknown request.");
}
SocketHandlerInterface::SocketOwnership handleClientWsUpgrade(const Poco::Net::HTTPRequest& request, const std::string& url)
More information about the Libreoffice-commits
mailing list