net/Socket.cpp | 66 +++++++++++++++++++++++++++++++++++++ net/Socket.hpp | 19 +++++++++- wsd/LOOLWSD.cpp | 98 +++++++------------------------------------------------- 3 files changed, 96 insertions(+), 87 deletions(-)
New commits: commit b5a1af763cf0e2295286f3e27c767ec51551cf2f Author: Michael Meeks <[email protected]> Date: Tue May 1 14:57:17 2018 +0100 Share HTTP header parsing inside the StreamSocket. Change-Id: Id98e895a939d931ac10b7cd7403da4cbe822ee82 diff --git a/net/Socket.cpp b/net/Socket.cpp index 1bf847cba..9a07986c9 100644 --- a/net/Socket.cpp +++ b/net/Socket.cpp @@ -17,6 +17,8 @@ #include <Poco/DateTime.h> #include <Poco/DateTimeFormat.h> #include <Poco/DateTimeFormatter.h> +#include <Poco/MemoryStream.h> +#include <Poco/Net/HTTPRequest.h> #include <Poco/Net/HTTPResponse.h> #include <SigUtil.hpp> @@ -241,6 +243,70 @@ bool ServerSocket::bind(Type type, int port) return rc == 0; } +bool StreamSocket::parseHeader(const char *clientName, + Poco::MemoryInputStream &message, + Poco::Net::HTTPRequest &request, + size_t *requestSize) +{ + LOG_TRC("#" << getFD() << " handling incoming " << _inBuffer.size() << " bytes."); + + assert(!requestSize || *requestSize == 0); + + // Find the end of the header, if any. + static const std::string marker("\r\n\r\n"); + auto itBody = std::search(_inBuffer.begin(), _inBuffer.end(), + marker.begin(), marker.end()); + if (itBody == _inBuffer.end()) + { + LOG_TRC("#" << getFD() << " doesn't have enough data yet."); + return false; + } + + // Skip the marker. + itBody += marker.size(); + if (requestSize) + *requestSize = static_cast<size_t>(itBody - _inBuffer.begin()); + + try + { + request.read(message); + + Log::StreamLogger logger = Log::info(); + if (logger.enabled()) + { + logger << "#" << getFD() << ": " << clientName << " HTTP Request: " + << request.getMethod() << ' ' + << request.getURI() << ' ' + << request.getVersion(); + + for (const auto& it : request) + { + logger << " / " << it.first << ": " << it.second; + } + + LOG_END(logger); + } + + const std::streamsize contentLength = request.getContentLength(); + const auto offset = itBody - _inBuffer.begin(); + const std::streamsize available = _inBuffer.size() - offset; + + if (contentLength != Poco::Net::HTTPMessage::UNKNOWN_CONTENT_LENGTH && available < contentLength) + { + LOG_DBG("Not enough content yet: ContentLength: " << contentLength << ", available: " << available); + return false; + } + } + catch (const std::exception& exc) + { + // Probably don't have enough data just yet. + // TODO: timeout if we never get enough. + return false; + } + + return true; +} + namespace HttpHelper { diff --git a/net/Socket.hpp b/net/Socket.hpp index d3d48128f..84632a4f2 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -40,8 +40,10 @@ namespace Poco { + class MemoryInputStream; namespace Net { + class HTTPRequest; class HTTPResponse; } } @@ -833,6 +835,20 @@ public: return socket; } + /// Remove the first @count bytes from input buffer + void eraseFirstInputBytes(size_t count) + { + _inBuffer.erase(_inBuffer.begin(), _inBuffer.begin() + count); + } + + /// Detects if we have an HTTP header in the provided message and + /// populates a request for that. + bool parseHeader(const char *clientLoggingName, + Poco::MemoryInputStream &message, + Poco::Net::HTTPRequest &request, + size_t *requestSize = nullptr); + + /// Get input/output statistics on this stream void getIOStats(uint64_t &sent, uint64_t &recv) { sent = _bytesSent; @@ -1021,8 +1037,7 @@ namespace HttpHelper void sendFile(const std::shared_ptr<StreamSocket>& socket, const std::string& path, const std::string& mediaType, Poco::Net::HTTPResponse& response, bool noCache = false, bool deflate = false, const bool headerOnly = false); -}; - +} #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 9d39ba2ce..668916500 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -1608,42 +1608,16 @@ private: } std::shared_ptr<StreamSocket> socket = _socket.lock(); - std::vector<char>& in = socket->_inBuffer; - // Find the end of the header, if any. - static const std::string marker("\r\n\r\n"); - auto itBody = std::search(in.begin(), in.end(), - marker.begin(), marker.end()); - if (itBody == in.end()) - { - LOG_TRC("#" << socket->getFD() << " doesn't have enough data yet."); - return; - } - - // Skip the marker. - itBody += marker.size(); - - Poco::MemoryInputStream message(&in[0], in.size()); + Poco::MemoryInputStream message(&socket->_inBuffer[0], + socket->_inBuffer.size());; Poco::Net::HTTPRequest request; + size_t requestSize = 0; + try { - request.read(message); - - Log::StreamLogger logger = Log::info(); - if (logger.enabled()) - { - logger << "#" << socket->getFD() << ": Prisoner HTTP Request: " - << request.getMethod() << ' ' - << request.getURI() << ' ' - << request.getVersion(); - - for (const auto& it : request) - { - logger << " / " << it.first << ": " << it.second; - } - - LOG_END(logger); - } + if (!socket->parseHeader("Prisoner", message, request, &requestSize)) + return; LOG_TRC("Child connection with URI [" << request.getURI() << "]."); if (request.getURI().find(NEW_CHILD_URI) != 0) @@ -1684,7 +1658,7 @@ private: return; } - in.clear(); + socket->_inBuffer.clear(); LOG_INF("New child [" << pid << "], jailId: " << jailId << "."); @@ -1768,60 +1742,14 @@ private: void handleIncomingMessage(SocketDisposition &disposition) override { std::shared_ptr<StreamSocket> socket = _socket.lock(); - std::vector<char>& in = socket->_inBuffer; - LOG_TRC("#" << socket->getFD() << " handling incoming " << in.size() << " bytes."); - - // Find the end of the header, if any. - static const std::string marker("\r\n\r\n"); - auto itBody = std::search(in.begin(), in.end(), - marker.begin(), marker.end()); - if (itBody == in.end()) - { - LOG_DBG("#" << socket->getFD() << " doesn't have enough data yet."); - return; - } - // Skip the marker. - itBody += marker.size(); - - Poco::MemoryInputStream message(&in[0], in.size()); + Poco::MemoryInputStream message(&socket->_inBuffer[0], + socket->_inBuffer.size());; Poco::Net::HTTPRequest request; - try - { - request.read(message); - - Log::StreamLogger logger = Log::info(); - if (logger.enabled()) - { - logger << "#" << socket->getFD() << ": Client HTTP Request: " - << request.getMethod() << ' ' - << request.getURI() << ' ' - << request.getVersion(); - - for (const auto& it : request) - { - logger << " / " << it.first << ": " << it.second; - } - - LOG_END(logger); - } - - const std::streamsize contentLength = request.getContentLength(); - const auto offset = itBody - in.begin(); - const std::streamsize available = in.size() - offset; + size_t requestSize = 0; - if (contentLength != Poco::Net::HTTPMessage::UNKNOWN_CONTENT_LENGTH && available < contentLength) - { - LOG_DBG("Not enough content yet: ContentLength: " << contentLength << ", available: " << available); - return; - } - } - catch (const std::exception& exc) - { - // Probably don't have enough data just yet. - // TODO: timeout if we never get enough. + if (!socket->parseHeader("Client", message, request, &requestSize)) return; - } try { @@ -1949,12 +1877,12 @@ private: // NOTE: Check _wsState to choose between HTTP response or WebSocket (app-level) error. LOG_INF("#" << socket->getFD() << " Exception while processing incoming request: [" << - LOOLProtocol::getAbbreviatedMessage(in) << "]: " << exc.what()); + LOOLProtocol::getAbbreviatedMessage(socket->_inBuffer) << "]: " << exc.what()); } // if we succeeded - remove the request from our input buffer // we expect one request per socket - in.erase(in.begin(), itBody); + socket->eraseFirstInputBytes(requestSize); } int getPollEvents(std::chrono::steady_clock::time_point /* now */, _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
