Skip to content

Commit 57038cf

Browse files
committed
foo
Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com> Change-Id: I5182f7c95750244f0b6af2241b80e7e6bfc61324
1 parent 1146c2c commit 57038cf

File tree

4 files changed

+298
-6
lines changed

4 files changed

+298
-6
lines changed

common/Unit.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,16 @@ class UnitBase
203203

204204
/// Custom response to a http request.
205205
virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& /*request*/,
206-
Poco::MemoryInputStream& /*message*/,
207-
std::shared_ptr<StreamSocket>& /*socket*/)
206+
std::istream& /*message*/,
207+
const std::shared_ptr<StreamSocket>& /*socket*/)
208208
{
209209
return false;
210210
}
211211

212212
virtual std::map<std::string, std::string>
213213
parallelizeCheckInfo(const Poco::Net::HTTPRequest& /*request*/,
214-
Poco::MemoryInputStream& /*message*/,
215-
std::shared_ptr<StreamSocket>& /*socket*/)
214+
std::istream& /*message*/,
215+
const std::shared_ptr<StreamSocket>& /*socket*/)
216216
{
217217
return {};
218218
}

net/Socket.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,7 @@ class StreamSocket : public Socket,
15561556
/// returns true if we did any re-sizing/movement of _inBuffer.
15571557
bool compactChunks(MessageMap& map);
15581558

1559-
ssize_t readHeader(const char* clientName, Poco::MemoryInputStream& message,
1559+
ssize_t readHeader(const char* clientName, std::istream& message,
15601560
Poco::Net::HTTPRequest& request,
15611561
std::chrono::duration<float, std::milli> delayMs);
15621562

wsd/ClientRequestDispatcher.cpp

Lines changed: 285 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,6 @@ void launchAsyncCheckFileInfo(
664664
}
665665
}
666666

667-
668667
void ClientRequestDispatcher::handleIncomingMessage(SocketDisposition& disposition)
669668
{
670669
std::shared_ptr<StreamSocket> socket = _socket.lock();
@@ -1112,6 +1111,291 @@ void ClientRequestDispatcher::handleIncomingMessage(SocketDisposition& dispositi
11121111
#endif // MOBILEAPP
11131112
}
11141113

1114+
ClientRequestDispatcher::MessageResult ClientRequestDispatcher::handleMessage(Poco::Net::HTTPRequest& request,
1115+
std::istream& message,
1116+
SocketDisposition& disposition,
1117+
const std::shared_ptr<StreamSocket>& socket,
1118+
ssize_t headerSize)
1119+
{
1120+
const bool closeConnection = !request.getKeepAlive(); // HTTP/1.1: closeConnection true w/ "Connection: close" only!
1121+
LOG_DBG("Handling request: " << request.getURI() << ", closeConnection " << closeConnection);
1122+
1123+
// denotes whether the request has been served synchronously
1124+
bool servedSync = false;
1125+
1126+
try
1127+
{
1128+
// update the read cursor - headers are not altered by chunks.
1129+
message.seekg(headerSize, std::ios::beg);
1130+
1131+
// re-write ServiceRoot and cache.
1132+
RequestDetails requestDetails(request, COOLWSD::ServiceRoot);
1133+
// LOG_TRC("Request details " << requestDetails.toString());
1134+
1135+
//fprintf(stderr, "size of message is %ld for %s\n", socket->getInBuffer().size(), requestDetails.toString().c_str());
1136+
1137+
// Config & security ...
1138+
if (requestDetails.isProxy())
1139+
{
1140+
if (!COOLWSD::IsProxyPrefixEnabled)
1141+
throw BadRequestException(
1142+
"ProxyPrefix present but net.proxy_prefix is not enabled");
1143+
1144+
if (!socket->isLocal())
1145+
throw BadRequestException("ProxyPrefix request from non-local socket");
1146+
}
1147+
1148+
CleanupRequestVettingStations();
1149+
1150+
// Routing
1151+
const bool isUnitTesting = UnitWSD::isUnitTesting();
1152+
bool handledByUnitTesting = false;
1153+
if (isUnitTesting)
1154+
{
1155+
LOG_DBG("Unit-Test: handleHttpRequest: " << request.getURI());
1156+
handledByUnitTesting = UnitWSD::get().handleHttpRequest(request, message, socket);
1157+
if (!handledByUnitTesting)
1158+
{
1159+
LOG_DBG("Unit-Test: parallelizeCheckInfo: " << request.getURI());
1160+
auto mapAccessDetails = UnitWSD::get().parallelizeCheckInfo(request, message, socket);
1161+
if (!mapAccessDetails.empty())
1162+
{
1163+
LOG_DBG("Unit-Test: launchAsyncCheckFileInfo: " << request.getURI());
1164+
auto accessDetails = FileServerRequestHandler::ResourceAccessDetails(
1165+
mapAccessDetails.at("wopiSrc"),
1166+
mapAccessDetails.at("accessToken"),
1167+
mapAccessDetails.at("permission"),
1168+
mapAccessDetails.at("configid"));
1169+
launchAsyncCheckFileInfo(_id, accessDetails, RequestVettingStations,
1170+
RvsHighWatermark);
1171+
}
1172+
}
1173+
}
1174+
1175+
if (handledByUnitTesting)
1176+
{
1177+
// Unit testing, nothing to do here
1178+
}
1179+
else if (requestDetails.equals(RequestDetails::Field::Type, "browser") ||
1180+
requestDetails.equals(RequestDetails::Field::Type, "wopi"))
1181+
{
1182+
// File server
1183+
assert(socket && "Must have a valid socket");
1184+
constexpr auto ProxyRemote = "/remote/";
1185+
constexpr auto ProxyRemoteLen = sizeof(ProxyRemote) - 1;
1186+
constexpr auto ProxyRemoteStatic = "/remote/static/";
1187+
const auto uri = requestDetails.getURI();
1188+
const auto pos = uri.find(ProxyRemoteStatic);
1189+
if (pos != std::string::npos)
1190+
{
1191+
if (uri.ends_with("lokit-extra-img.svg"))
1192+
{
1193+
std::string proxyRatingServer =
1194+
!isUnitTesting ? ProxyRequestHandler::getProxyRatingServer()
1195+
: UnitWSD::get().getProxyRatingServer();
1196+
ProxyRequestHandler::handleRequest(uri.substr(pos + ProxyRemoteLen), socket,
1197+
proxyRatingServer);
1198+
servedSync = true;
1199+
}
1200+
#if ENABLE_FEATURE_LOCK
1201+
else
1202+
{
1203+
const Poco::URI unlockImageUri =
1204+
CommandControl::LockManager::getUnlockImageUri();
1205+
if (!unlockImageUri.empty())
1206+
{
1207+
const std::string& serverUri =
1208+
unlockImageUri.getScheme() + "://" + unlockImageUri.getAuthority();
1209+
ProxyRequestHandler::handleRequest(
1210+
uri.substr(pos + sizeof("/remote/static") - 1), socket, serverUri);
1211+
servedSync = true;
1212+
}
1213+
}
1214+
#endif
1215+
if (!servedSync)
1216+
HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
1217+
}
1218+
else
1219+
{
1220+
FileServerRequestHandler::ResourceAccessDetails accessDetails;
1221+
servedSync = COOLWSD::FileRequestHandler->handleRequest(
1222+
request, requestDetails, message, socket, accessDetails);
1223+
if (accessDetails.isValid())
1224+
{
1225+
LOG_ASSERT_MSG(
1226+
Uri::decode(requestDetails.getField(RequestDetails::Field::WOPISrc)) ==
1227+
Uri::decode(accessDetails.wopiSrc()),
1228+
"Expected identical WOPISrc in the request as in cool.html");
1229+
1230+
launchAsyncCheckFileInfo(_id, accessDetails, RequestVettingStations,
1231+
RvsHighWatermark);
1232+
}
1233+
}
1234+
}
1235+
else if (requestDetails.equals(RequestDetails::Field::Type, "cool") &&
1236+
requestDetails.equals(1, "adminws"))
1237+
{
1238+
// Admin connections
1239+
LOG_INF("Admin request: " << request.getURI());
1240+
const ServerURL cnxDetails(requestDetails);
1241+
if (AdminSocketHandler::handleInitialRequest(_socket, request, cnxDetails.getWebServerUrl()))
1242+
{
1243+
// Hand the socket over to the Admin poll.
1244+
disposition.setTransfer(Admin::instance(),
1245+
[](const std::shared_ptr<Socket>& /*moveSocket*/) {});
1246+
}
1247+
else
1248+
HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
1249+
}
1250+
else if (requestDetails.equals(RequestDetails::Field::Type, "cool") &&
1251+
requestDetails.equals(1, "getMetrics"))
1252+
{
1253+
if (!COOLWSD::AdminEnabled)
1254+
throw Poco::FileAccessDeniedException("Admin console disabled");
1255+
1256+
// See metrics.txt
1257+
std::shared_ptr<http::Response> response =
1258+
std::make_shared<http::Response>(http::StatusCode::OK);
1259+
1260+
try
1261+
{
1262+
/* WARNING: security point, we may skip authentication */
1263+
bool skipAuthentication = ConfigUtil::getConfigValue<bool>(
1264+
"security.enable_metrics_unauthenticated", false);
1265+
if (!skipAuthentication)
1266+
if (!COOLWSD::FileRequestHandler->isAdminLoggedIn(request, *response))
1267+
throw Poco::Net::NotAuthenticatedException("Invalid admin login");
1268+
}
1269+
catch (const Poco::Net::NotAuthenticatedException& exc)
1270+
{
1271+
//LOG_ERR("FileServerRequestHandler::NotAuthenticated: " << exc.displayText());
1272+
http::Response httpResponse(http::StatusCode::Unauthorized);
1273+
httpResponse.set("Content-Type", "text/html charset=UTF-8");
1274+
httpResponse.set("WWW-authenticate", "Basic realm=\"online\"");
1275+
socket->sendAndShutdown(httpResponse);
1276+
socket->ignoreInput();
1277+
return MessageResult::Ignore;
1278+
}
1279+
1280+
FileServerRequestHandler::hstsHeaders(*response);
1281+
response->add("Last-Modified", Util::getHttpTimeNow());
1282+
// Ask UAs to block if they detect any XSS attempt
1283+
response->add("X-XSS-Protection", "1; mode=block");
1284+
// No referrer-policy
1285+
response->add("Referrer-Policy", "no-referrer");
1286+
response->add("X-Content-Type-Options", "nosniff");
1287+
1288+
disposition.setTransfer(Admin::instance(),
1289+
[response=std::move(response)](const std::shared_ptr<Socket>& moveSocket)
1290+
{
1291+
const std::shared_ptr<StreamSocket> streamSocket =
1292+
std::static_pointer_cast<StreamSocket>(moveSocket);
1293+
Admin::instance().sendMetrics(streamSocket, response);
1294+
});
1295+
}
1296+
else if (requestDetails.isGetOrHead("/"))
1297+
servedSync = handleRootRequest(requestDetails, socket);
1298+
1299+
else if (requestDetails.isGet("/favicon.ico"))
1300+
servedSync = handleFaviconRequest(requestDetails, socket);
1301+
1302+
else if (requestDetails.equals(0, "hosting"))
1303+
{
1304+
if (requestDetails.equals(1, "discovery"))
1305+
servedSync = handleWopiDiscoveryRequest(requestDetails, socket);
1306+
else if (requestDetails.equals(1, "capabilities"))
1307+
servedSync = handleCapabilitiesRequest(request, socket);
1308+
else if (requestDetails.equals(1, "wopiAccessCheck"))
1309+
handleWopiAccessCheckRequest(request, message, socket);
1310+
else
1311+
HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
1312+
}
1313+
else if (requestDetails.isGet("/robots.txt"))
1314+
servedSync = handleRobotsTxtRequest(request, socket);
1315+
1316+
else if (requestDetails.equals(RequestDetails::Field::Type, "cool") &&
1317+
requestDetails.equals(1, "media"))
1318+
servedSync = handleMediaRequest(request, disposition, socket);
1319+
1320+
else if (requestDetails.equals(RequestDetails::Field::Type, "cool") &&
1321+
requestDetails.equals(1, "clipboard"))
1322+
{
1323+
// HexUtil::dumpHex(std::cerr, socket->getInBuffer(), "clipboard:\n"); // lots of data ...
1324+
servedSync = handleClipboardRequest(request, message, disposition, socket);
1325+
}
1326+
else if (requestDetails.equals(RequestDetails::Field::Type, "cool") &&
1327+
requestDetails.equals(1, "signature"))
1328+
{
1329+
servedSync = handleSignatureRequest(request, socket);
1330+
}
1331+
1332+
else if (requestDetails.isProxy() && requestDetails.equals(2, "ws"))
1333+
servedSync = handleClientProxyRequest(request, requestDetails, message, disposition);
1334+
else if (requestDetails.equals(RequestDetails::Field::Type, "cool") &&
1335+
requestDetails.equals(2, "ws") && requestDetails.isWebSocket())
1336+
servedSync = handleClientWsUpgrade(request, requestDetails, disposition, socket);
1337+
1338+
else if (!requestDetails.isWebSocket() &&
1339+
(requestDetails.equals(RequestDetails::Field::Type, "cool") ||
1340+
requestDetails.equals(RequestDetails::Field::Type, "lool")))
1341+
{
1342+
// All post requests have url prefix 'cool', except when the prefix
1343+
// is 'lool' e.g. when integrations use the old /lool/convert-to endpoint
1344+
servedSync = handlePostRequest(requestDetails, request, message, disposition, socket);
1345+
}
1346+
else if (requestDetails.equals(RequestDetails::Field::Type, "wasm"))
1347+
{
1348+
if (COOLWSD::WASMState == COOLWSD::WASMActivationState::Disabled)
1349+
{
1350+
LOG_ERR(
1351+
"WASM document request while WASM is disabled: " << requestDetails.toString());
1352+
1353+
// Bad request.
1354+
HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
1355+
return MessageResult::Ignore;
1356+
}
1357+
1358+
// Tunnel to WASM.
1359+
_wopiProxy = std::make_unique<WopiProxy>(_id, requestDetails, socket);
1360+
_wopiProxy->handleRequest(COOLWSD::getWebServerPoll(), disposition);
1361+
}
1362+
else
1363+
{
1364+
LOG_WRN("Unknown resource: " << requestDetails.toString());
1365+
1366+
// Bad request.
1367+
HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
1368+
return MessageResult::Ignore;
1369+
}
1370+
}
1371+
catch (const BadRequestException& ex)
1372+
{
1373+
LOG_ERR('#' << socket->getFD() << " bad request: ["
1374+
<< COOLProtocol::getAbbreviatedMessage(socket->getInBuffer())
1375+
<< "]: " << ex.what());
1376+
1377+
// Bad request.
1378+
HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
1379+
return MessageResult::Ignore;
1380+
}
1381+
catch (const std::exception& exc)
1382+
{
1383+
LOG_ERR('#' << socket->getFD() << " Exception while processing incoming request: ["
1384+
<< COOLProtocol::getAbbreviatedMessage(socket->getInBuffer())
1385+
<< "]: " << exc.what());
1386+
1387+
// Bad request.
1388+
// NOTE: Check _wsState to choose between HTTP response or WebSocket (app-level) error.
1389+
http::Response httpResponse(http::StatusCode::BadRequest);
1390+
httpResponse.set("Content-Length", "0");
1391+
socket->sendAndShutdown(httpResponse);
1392+
socket->ignoreInput();
1393+
return MessageResult::Ignore;
1394+
}
1395+
1396+
return servedSync ? MessageResult::ServedSync : MessageResult::ServedAsync;
1397+
}
1398+
11151399
#if !MOBILEAPP
11161400
bool ClientRequestDispatcher::handleRootRequest(const RequestDetails& requestDetails,
11171401
const std::shared_ptr<StreamSocket>& socket)

wsd/ClientRequestDispatcher.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ class ClientRequestDispatcher final : public SimpleSocketHandler
118118

119119
void sendResult(const std::shared_ptr<StreamSocket>& socket, CheckStatus result);
120120

121+
enum MessageResult { ServedAsync, ServedSync, Ignore };
122+
123+
MessageResult handleMessage(Poco::Net::HTTPRequest& request,
124+
std::istream& message,
125+
SocketDisposition& disposition,
126+
const std::shared_ptr<StreamSocket>& socket,
127+
ssize_t headerSize);
128+
121129
#endif // !MOBILEAPP
122130

123131
/// @return true if request has been handled synchronously and response sent, otherwise false

0 commit comments

Comments
 (0)