@@ -664,7 +664,6 @@ void launchAsyncCheckFileInfo(
664
664
}
665
665
}
666
666
667
-
668
667
void ClientRequestDispatcher::handleIncomingMessage (SocketDisposition& disposition)
669
668
{
670
669
std::shared_ptr<StreamSocket> socket = _socket.lock ();
@@ -1112,6 +1111,291 @@ void ClientRequestDispatcher::handleIncomingMessage(SocketDisposition& dispositi
1112
1111
#endif // MOBILEAPP
1113
1112
}
1114
1113
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
+
1115
1399
#if !MOBILEAPP
1116
1400
bool ClientRequestDispatcher::handleRootRequest (const RequestDetails& requestDetails,
1117
1401
const std::shared_ptr<StreamSocket>& socket)
0 commit comments