Always set http response codes for media retrieval endpoints
This commit is contained in:
@@ -287,6 +287,24 @@ namespace lms::api::subsonic
|
|||||||
if (core::stringUtils::stringEndsWith(requestPath, ".view"))
|
if (core::stringUtils::stringEndsWith(requestPath, ".view"))
|
||||||
requestPath.resize(requestPath.length() - 5);
|
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
|
// Optional parameters
|
||||||
const ResponseFormat format{ getParameterAs<std::string>(request.getParameterMap(), "f").value_or("xml") == "json" ? ResponseFormat::json : ResponseFormat::xml };
|
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;
|
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
|
// do not disclose unhandled commands for unauthenticated users
|
||||||
authenticateUser(request);
|
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)
|
db::UserId SubsonicResource::authenticateUser(const Wt::Http::Request& request)
|
||||||
{
|
{
|
||||||
const auto& parameters{ request.getParameterMap() };
|
const auto& parameters{ request.getParameterMap() };
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ namespace lms::db
|
|||||||
|
|
||||||
namespace lms::api::subsonic
|
namespace lms::api::subsonic
|
||||||
{
|
{
|
||||||
|
class RequestContext;
|
||||||
|
|
||||||
class SubsonicResource final : public Wt::WResource
|
class SubsonicResource final : public Wt::WResource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -41,6 +43,9 @@ namespace lms::api::subsonic
|
|||||||
private:
|
private:
|
||||||
void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override;
|
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);
|
db::UserId authenticateUser(const Wt::Http::Request& request);
|
||||||
|
|
||||||
const SubsonicResourceConfig _config;
|
const SubsonicResourceConfig _config;
|
||||||
|
|||||||
Reference in New Issue
Block a user