rtsp-client: handle Require headers and respond with OPTION_NOT_SUPPORTED
authorTim-Philipp Müller <tim@centricular.com>
Fri, 2 May 2014 18:59:23 +0000 (19:59 +0100)
committerTim-Philipp Müller <tim@centricular.com>
Fri, 9 May 2014 12:55:21 +0000 (13:55 +0100)
Servers must handle Require headers and must report a failure
if they don't handle any of the Required options, see RFC 2326,
section 12.32: https://tools.ietf.org/html/rfc2326#page-54

https://bugzilla.gnome.org/show_bug.cgi?id=729426

gst/rtsp-server/rtsp-client.c

index d42f933..93d3b3f 100644 (file)
@@ -502,6 +502,25 @@ send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
   send_message (client, ctx, ctx->response, FALSE);
 }
 
+static void
+send_option_not_supported_response (GstRTSPClient * client,
+    GstRTSPContext * ctx, const gchar * unsupported_options)
+{
+  GstRTSPStatusCode code = GST_RTSP_STS_OPTION_NOT_SUPPORTED;
+
+  gst_rtsp_message_init_response (ctx->response, code,
+      gst_rtsp_status_as_text (code), ctx->request);
+
+  if (unsupported_options != NULL) {
+    gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_UNSUPPORTED,
+        unsupported_options);
+  }
+
+  ctx->session = NULL;
+
+  send_message (client, ctx, ctx->response, FALSE);
+}
+
 static gboolean
 paths_are_equal (const gchar * path1, const gchar * path2, gint len2)
 {
@@ -2169,6 +2188,55 @@ client_session_finalized (GstRTSPClient * client, GstRTSPSession * session)
   }
 }
 
+/* Returns TRUE if there are no Require headers, otherwise returns FALSE
+ * and also returns a newly-allocated string of (comma-separated) unsupported
+ * options in the unsupported_reqs variable .
+ *
+ * There may be multiple Require headers, but we must send one single
+ * Unsupported header with all the unsupported options as response. If
+ * an incoming Require header contained a comma-separated list of options
+ * GstRtspConnection will already have split that list up into multiple
+ * headers.
+ *
+ * TODO: allow the application to decide what features are supported
+ */
+static gboolean
+check_request_requirements (GstRTSPMessage * msg, gchar ** unsupported_reqs)
+{
+  GstRTSPResult res;
+  GPtrArray *arr = NULL;
+  gchar *reqs = NULL;
+  gint i;
+
+  i = 0;
+  do {
+    res = gst_rtsp_message_get_header (msg, GST_RTSP_HDR_REQUIRE, &reqs, i++);
+
+    if (res == GST_RTSP_ENOTIMPL)
+      break;
+
+    if (arr == NULL)
+      arr = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+
+    g_ptr_array_add (arr, g_strdup (reqs));
+  }
+  while (TRUE);
+
+  /* if we don't have any Require headers at all, all is fine */
+  if (i == 1)
+    return TRUE;
+
+  /* otherwise we've now processed at all the Require headers */
+  g_ptr_array_add (arr, NULL);
+
+  /* for now we don't commit to supporting anything, so will just report
+   * all of the required options as unsupported */
+  *unsupported_reqs = g_strjoinv (", ", (gchar **) arr->pdata);
+
+  g_ptr_array_unref (arr);
+  return FALSE;
+}
+
 static void
 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
 {
@@ -2181,6 +2249,7 @@ handle_request (GstRTSPClient * client, GstRTSPMessage * request)
   GstRTSPSession *session = NULL;
   GstRTSPContext sctx = { NULL }, *ctx;
   GstRTSPMessage response = { 0 };
+  gchar *unsupported_reqs = NULL;
   gchar *sessid;
 
   if (!(ctx = gst_rtsp_context_get_current ())) {
@@ -2268,6 +2337,10 @@ handle_request (GstRTSPClient * client, GstRTSPMessage * request)
   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_URL))
     goto not_authorized;
 
+  /* handle any 'Require' headers */
+  if (!check_request_requirements (ctx->request, &unsupported_reqs))
+    goto unsupported_requirement;
+
   /* now see what is asked and dispatch to a dedicated handler */
   switch (method) {
     case GST_RTSP_OPTIONS:
@@ -2344,6 +2417,14 @@ not_authorized:
     /* error reply is already sent */
     goto done;
   }
+unsupported_requirement:
+  {
+    GST_ERROR ("client %p: Required option is not supported (%s)", client,
+        unsupported_reqs);
+    send_option_not_supported_response (client, ctx, unsupported_reqs);
+    g_free (unsupported_reqs);
+    goto done;
+  }
 not_implemented:
   {
     GST_ERROR ("client %p: method %d not implemented", client, method);