From 90ec1c4baed3a3c999fcf33db8574212375fbcf5 Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Fri, 10 Dec 2021 11:39:23 +0000 Subject: [PATCH] Add application/sdp+json as the precise media type for the experimental SDP-as-JSON response --- Development/nmos/api_utils.cpp | 5 +++-- Development/nmos/connection_api.cpp | 19 ++++++++++++++----- Development/nmos/connection_resources.cpp | 3 ++- Development/nmos/media_type.h | 11 +++++++++++ Development/nmos/node_api_target_handler.cpp | 7 ++++--- Development/nmos/schemas_api.cpp | 5 +++-- 6 files changed, 37 insertions(+), 13 deletions(-) diff --git a/Development/nmos/api_utils.cpp b/Development/nmos/api_utils.cpp index 5033e138a..221413b74 100644 --- a/Development/nmos/api_utils.cpp +++ b/Development/nmos/api_utils.cpp @@ -8,6 +8,7 @@ #include "cpprest/uri_schemes.h" #include "cpprest/ws_utils.h" #include "nmos/api_version.h" +#include "nmos/media_type.h" #include "nmos/slog.h" #include "nmos/type.h" #include "nmos/version.h" @@ -190,7 +191,7 @@ namespace nmos const auto accept = req.headers().find(web::http::header_names::accept); return req.headers().end() != accept && !boost::algorithm::contains(accept->second, mime_type) - && boost::algorithm::contains(accept->second, U("text/html")); + && boost::algorithm::contains(accept->second, nmos::media_types::text_html.name); } web::json::value make_html_response_a_tag(const web::uri& href, const web::json::value& value) @@ -563,7 +564,7 @@ namespace nmos if (web::http::details::is_mime_type_json(mime_type) && experimental::details::is_html_response_preferred(req, mime_type)) { res.set_body(nmos::experimental::make_html_response_body(res, gate)); - res.headers().set_content_type(U("text/html; charset=utf-8")); + res.headers().set_content_type(nmos::media_types::text_html.name + U("; charset=utf-8")); } slog::detail::logw(gate, slog::severities::more_info, SLOG_FLF) << nmos::stash_categories({ nmos::categories::access }) << nmos::common_log_stash(req, res) << "Sending response after " << processing_dur << "ms"; diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index 48ac3f5db..ae41ee8a1 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -11,6 +11,7 @@ #include "nmos/is04_versions.h" #include "nmos/is05_versions.h" #include "nmos/json_schema.h" +#include "nmos/media_type.h" #include "nmos/model.h" #include "nmos/sdp_utils.h" #include "nmos/slog.h" @@ -634,16 +635,22 @@ namespace nmos { slog::log(gate, SLOG_FLF) << "Returning transport file for " << id_type; + const auto transportfile_type = nmos::fields::transportfile_type(transportfile); + // hmm, parsing of the Accept header could be much better and should take account of quality values - if (!accept.empty() && web::http::details::mime_types::application_json == web::http::details::get_mime_type(accept) && U("application/sdp") == nmos::fields::transportfile_type(transportfile)) + const auto accept_type = web::http::details::get_mime_type(accept); + // allow application/json as well as the more precise application/sdp+json + const std::set sdp_json_types{ nmos::media_types::application_json.name, nmos::media_types::application_sdp_json.name }; + if (nmos::media_types::application_sdp.name == transportfile_type && 0 != sdp_json_types.count(accept_type)) { // Experimental extension - SDP as JSON + res.headers().set_content_type(accept_type); set_reply(res, status_codes::OK, sdp::parse_session_description(utility::us2s(data.as_string()))); } else { // This automatically performs conversion to UTF-8 if required (i.e. on Windows) - set_reply(res, status_codes::OK, data.as_string(), nmos::fields::transportfile_type(transportfile)); + set_reply(res, status_codes::OK, data.as_string(), transportfile_type); } // "It is strongly recommended that the following caching headers are included via the /transportfile endpoint (or whatever this endpoint redirects to). @@ -803,7 +810,7 @@ namespace nmos // Validate and parse the specified transport file for the specified receiver web::json::value parse_rtp_transport_file(const nmos::resource& receiver, const nmos::resource& connection_receiver, const utility::string_t& transport_file_type, const utility::string_t& transport_file_data, slog::base_gate& gate) { - if (transport_file_type != U("application/sdp")) + if (transport_file_type != nmos::media_types::application_sdp.name) { throw std::runtime_error("unexpected type: " + utility::us2s(transport_file_type)); } @@ -1185,12 +1192,14 @@ namespace nmos // hmm, parsing of the Accept header could be much better and should take account of quality values const auto accept = req.headers().find(web::http::header_names::accept); - if (req.headers().end() != accept && U("application/schema+json") == web::http::details::get_mime_type(accept->second)) + const auto accept_or_empty = req.headers().end() != accept ? accept->second : utility::string_t{}; + const auto accept_type = web::http::details::get_mime_type(accept_or_empty); + if (nmos::media_types::application_schema_json.name == accept_type) { // Experimental extension - constraints as JSON Schema const nmos::transport transport_subclassification(nmos::fields::transport(matching_resource->data)); - res.headers().set_content_type(U("application/schema+json")); + res.headers().set_content_type(accept_type); set_reply(res, status_codes::OK, nmos::details::make_constraints_schema(resource->type, nmos::fields::endpoint_constraints(resource->data), nmos::transport_base(transport_subclassification))); } else diff --git a/Development/nmos/connection_resources.cpp b/Development/nmos/connection_resources.cpp index 7dcc8765f..c3d1df59b 100644 --- a/Development/nmos/connection_resources.cpp +++ b/Development/nmos/connection_resources.cpp @@ -6,6 +6,7 @@ #include "nmos/api_utils.h" // for nmos::http_scheme #include "nmos/is05_versions.h" #include "nmos/is07_versions.h" +#include "nmos/media_type.h" // for nmos::media_types::application_sdp #include "nmos/resource.h" namespace nmos @@ -171,7 +172,7 @@ namespace nmos return value_of({ { nmos::fields::transportfile_data, transportfile }, - { nmos::fields::transportfile_type, U("application/sdp") } + { nmos::fields::transportfile_type, nmos::media_types::application_sdp.name } }); } diff --git a/Development/nmos/media_type.h b/Development/nmos/media_type.h index 68f14de67..c29c3d450 100644 --- a/Development/nmos/media_type.h +++ b/Development/nmos/media_type.h @@ -43,6 +43,17 @@ namespace nmos // See SMPTE ST 2022-8:2019 const media_type video_SMPTE2022_6{ U("video/SMPTE2022-6") }; + + // Additional media types for NMOS responses + + const media_type application_sdp{ U("application/sdp") }; + + // experimental extension, to support HTML rendering of NMOS responses + const media_type text_html{ U("text/html") }; + + // experimental extension, to support JSON rendering in NMOS responses + const media_type application_schema_json{ U("application/schema+json") }; + const media_type application_sdp_json{ U("application/sdp+json") }; } } diff --git a/Development/nmos/node_api_target_handler.cpp b/Development/nmos/node_api_target_handler.cpp index 03dcc25a1..e3d64cf17 100644 --- a/Development/nmos/node_api_target_handler.cpp +++ b/Development/nmos/node_api_target_handler.cpp @@ -6,6 +6,7 @@ #include "nmos/client_utils.h" #include "nmos/is05_versions.h" #include "nmos/json_fields.h" +#include "nmos/media_type.h" // for nmos::media_types::application_sdp #include "nmos/model.h" #include "nmos/slog.h" @@ -47,9 +48,9 @@ namespace nmos { slog::log(gate, SLOG_FLF) << "Missing Content-Type: should be application/sdp"; } - else if (U("application/sdp") != content_type) + else if (nmos::media_types::application_sdp.name != content_type) { - throw web::http::http_exception(U("Incorrect Content-Type: ") + content_type + U(", should be application/sdp")); + throw web::http::http_exception(U("Incorrect Content-Type: ") + content_type + U(", should be ") + nmos::media_types::application_sdp.name); } return res.extract_string(true); @@ -66,7 +67,7 @@ namespace nmos })}, { nmos::fields::transport_file, value_of({ { nmos::fields::data, sdp }, - { nmos::fields::type, U("application/sdp") } + { nmos::fields::type, nmos::media_types::application_sdp.name } })} }); diff --git a/Development/nmos/schemas_api.cpp b/Development/nmos/schemas_api.cpp index b5cb3a080..bf4b49767 100644 --- a/Development/nmos/schemas_api.cpp +++ b/Development/nmos/schemas_api.cpp @@ -6,6 +6,7 @@ #include "nmos/api_utils.h" #include "nmos/json_schema.h" #include "nmos/log_manip.h" +#include "nmos/media_type.h" // for nmos::media_types::application_schema_json namespace nmos { @@ -185,10 +186,10 @@ namespace nmos if (schemas.end() != found) { - res.headers().set_content_type(U("application/schema+json")); + res.headers().set_content_type(nmos::media_types::application_schema_json.name); // experimental extension, to support human-readable HTML rendering of NMOS responses - if (experimental::details::is_html_response_preferred(req, U("application/schema+json"))) + if (experimental::details::is_html_response_preferred(req, nmos::media_types::application_schema_json.name)) { const auto base_uri = web::uri_builder().set_path(U("/schemas/") + repository + U("/") + tag + U("/")).to_uri(); set_reply(res, status_codes::OK, details::make_json_schema_html_response_body(base_uri, found->second));