Always set http response codes for media retrieval endpoints

This commit is contained in:
emeric
2025-11-07 22:23:43 +01:00
parent 44308a8956
commit f9071db998
2 changed files with 69 additions and 19 deletions
+64 -19
View File
@@ -287,6 +287,24 @@ namespace lms::api::subsonic
if (core::stringUtils::stringEndsWith(requestPath, ".view"))
requestPath.resize(requestPath.length() - 5);
// First check for media retrieval endpoints
auto itStreamHandler{ mediaRetrievalHandlers.find(requestPath) };
if (itStreamHandler != mediaRetrievalHandlers.end())
{
try
{
LMS_SCOPED_TRACE_OVERVIEW("Subsonic", itStreamHandler->first);
handleMediaRetrievalRequest(itStreamHandler->second, request, response);
LMS_LOG(API_SUBSONIC, DEBUG, "Request " << requestId << " '" << requestPath << "' handled!");
}
catch (const Error& e)
{
LMS_LOG(API_SUBSONIC, ERROR, "Error while processing request '" << requestId << "', code = " << static_cast<int>(e.getCode()) << ", msg = '" << e.getMessage() << "'");
}
return;
}
// Optional parameters
const ResponseFormat format{ getParameterAs<std::string>(request.getParameterMap(), "f").value_or("xml") == "json" ? ResponseFormat::json : ResponseFormat::xml };
@@ -324,25 +342,6 @@ namespace lms::api::subsonic
return;
}
auto itStreamHandler{ mediaRetrievalHandlers.find(requestPath) };
if (itStreamHandler != mediaRetrievalHandlers.end())
{
LMS_SCOPED_TRACE_OVERVIEW("Subsonic", itStreamHandler->first);
// Media retrieval endpoints are always authenticated
// Optim: no need to reauth user for each continuation
db::User::pointer user;
if (!request.continuation())
user = getUserFromUserId(_db.getTLSSession(), authenticateUser(request));
RequestContext requestContext{ request, _db.getTLSSession(), user, _config };
protocolVersion = requestContext.getServerProtocolVersion();
itStreamHandler->second(requestContext, request, response);
LMS_LOG(API_SUBSONIC, DEBUG, "Request " << requestId << " '" << requestPath << "' handled!");
return;
}
// do not disclose unhandled commands for unauthenticated users
authenticateUser(request);
@@ -360,6 +359,52 @@ namespace lms::api::subsonic
}
}
void SubsonicResource::handleMediaRetrievalRequest(MediaRetrievalHandlerFunc handler, const Wt::Http::Request& request, Wt::Http::Response& response)
{
try
{
// Media retrieval endpoints are always authenticated
// Optimization: no need to reauth user for each continuation
db::User::pointer user;
if (!request.continuation())
user = getUserFromUserId(_db.getTLSSession(), authenticateUser(request));
RequestContext requestContext{ request, _db.getTLSSession(), user, _config };
handler(requestContext, request, response);
}
catch (const UserNotAuthorizedError&)
{
response.setStatus(401); // Unauthorized
throw;
}
catch (const RequiredParameterMissingError&)
{
response.setStatus(400); // Bad Request
throw;
}
catch (const BadParameterGenericError&)
{
response.setStatus(400); // Bad Request
throw;
}
catch (const RequestedDataNotFoundError&)
{
response.setStatus(404); // Not Found
throw;
}
catch (const InternalErrorGenericError&)
{
response.setStatus(500); // Internal Server Error
throw;
}
catch (const Error&)
{
response.setStatus(400); // Assume bad request
throw;
}
}
db::UserId SubsonicResource::authenticateUser(const Wt::Http::Request& request)
{
const auto& parameters{ request.getParameterMap() };
@@ -33,6 +33,8 @@ namespace lms::db
namespace lms::api::subsonic
{
class RequestContext;
class SubsonicResource final : public Wt::WResource
{
public:
@@ -41,6 +43,9 @@ namespace lms::api::subsonic
private:
void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override;
using MediaRetrievalHandlerFunc = std::function<void(RequestContext&, const Wt::Http::Request&, Wt::Http::Response&)>;
void handleMediaRetrievalRequest(MediaRetrievalHandlerFunc handler, const Wt::Http::Request& request, Wt::Http::Response& response);
db::UserId authenticateUser(const Wt::Http::Request& request);
const SubsonicResourceConfig _config;