static guint
append_header (GString *string,
               const gchar *name,
               const gchar *value)
{
  if (value)
    {
      g_return_val_if_fail (cockpit_web_response_is_simple_token (name), 0);
      g_return_val_if_fail (cockpit_web_response_is_header_value (value), 0);
      g_string_append_printf (string, "%s: %s\r\n", name, value);
    }
  if (g_ascii_strcasecmp ("Content-Type", name) == 0)
    return HEADER_CONTENT_TYPE;
  if (g_ascii_strcasecmp ("Cache-Control", name) == 0)
    return HEADER_CACHE_CONTROL;
  if (g_ascii_strcasecmp ("Vary", name) == 0)
    return HEADER_VARY;
  if (g_ascii_strcasecmp ("Content-Encoding", name) == 0)
    return HEADER_CONTENT_ENCODING;
  else if (g_ascii_strcasecmp ("Content-Length", name) == 0)
    g_critical ("Don't set Content-Length manually. This is a programmer error.");
  else if (g_ascii_strcasecmp ("Connection", name) == 0)
    g_critical ("Don't set Connection header manually. This is a programmer error.");
  return 0;
}
gboolean
cockpit_web_service_parse_external (JsonObject *options,
                                    const gchar **content_type,
                                    const gchar **content_encoding,
                                    const gchar **content_disposition,
                                    gchar ***protocols)
{
  JsonObject *external;
  const gchar *value;
  JsonNode *node;

  g_return_val_if_fail (options != NULL, FALSE);

  if (!cockpit_json_get_string (options, "channel", NULL, &value) || value != NULL)
    {
      g_message ("don't specify \"channel\" on external channel");
      return FALSE;
    }
  if (!cockpit_json_get_string (options, "command", NULL, &value) || value != NULL)
    {
      g_message ("don't specify \"command\" on external channel");
      return FALSE;
    }

  node = json_object_get_member (options, "external");
  if (node == NULL)
    {
      if (content_disposition)
        *content_disposition = NULL;
      if (content_type)
        *content_type = NULL;
      if (content_encoding)
        *content_encoding = NULL;
      if (protocols)
        *protocols = NULL;
      return TRUE;
    }

  if (!JSON_NODE_HOLDS_OBJECT (node))
    {
      g_message ("invalid \"external\" option");
      return FALSE;
    }

  external = json_node_get_object (node);

  if (!cockpit_json_get_string (external, "content-disposition", NULL, &value) ||
      (value && !cockpit_web_response_is_header_value (value)))
    {
      g_message ("invalid \"content-disposition\" external option");
      return FALSE;
    }
  if (content_disposition)
    *content_disposition = value;

  if (!cockpit_json_get_string (external, "content-type", NULL, &value) ||
      (value && !cockpit_web_response_is_header_value (value)))
    {
      g_message ("invalid \"content-type\" external option");
      return FALSE;
    }
  if (content_type)
    *content_type = value;

  if (!cockpit_json_get_string (external, "content-encoding", NULL, &value) ||
      (value && !cockpit_web_response_is_header_value (value)))
    {
      g_message ("invalid \"content-encoding\" external option");
      return FALSE;
    }
  if (content_encoding)
    *content_encoding = value;

  if (!cockpit_json_get_strv (external, "protocols", NULL, protocols))
    {
      g_message ("invalid \"protocols\" external option");
      return FALSE;
    }

  return TRUE;
}
Exemple #3
0
static void
send_http_request (CockpitHttpStream *self)
{
  CockpitChannel *channel = COCKPIT_CHANNEL (self);
  JsonObject *options;
  gboolean had_host;
  gboolean had_encoding;
  const gchar *method;
  const gchar *path;
  GString *string = NULL;
  JsonNode *node;
  JsonObject *headers;
  const gchar *header;
  const gchar *value;
  GList *request = NULL;
  GList *names = NULL;
  GBytes *bytes;
  GList *l;
  gsize total;

  options = cockpit_channel_get_options (channel);

  /*
   * The checks we do here for token validity are just enough to be able
   * to format an HTTP response, without leaking across lines etc.
   */

  if (!cockpit_json_get_string (options, "path", NULL, &path))
    {
      cockpit_channel_fail (channel, "protocol-error",
                            "%s: bad \"path\" field in HTTP stream request", self->name);
      goto out;
    }
  else if (path == NULL)
    {
      cockpit_channel_fail (channel, "protocol-error",
                            "%s: missing \"path\" field in HTTP stream request", self->name);
      goto out;
    }
  else if (!cockpit_web_response_is_simple_token (path))
    {
      cockpit_channel_fail (channel, "protocol-error",
                            "%s: invalid \"path\" field in HTTP stream request", self->name);
      goto out;
    }

  if (!cockpit_json_get_string (options, "method", NULL, &method))
    {
      cockpit_channel_fail (channel, "protocol-error",
                            "%s: bad \"method\" field in HTTP stream request", self->name);
      goto out;
    }
  else if (method == NULL)
    {
      cockpit_channel_fail (channel, "protocol-error",
                            "%s: missing \"method\" field in HTTP stream request", self->name);
      goto out;
    }
  else if (!cockpit_web_response_is_simple_token (method))
    {
      cockpit_channel_fail (channel, "protocol-error",
                            "%s: invalid \"method\" field in HTTP stream request", self->name);
      goto out;
    }

  g_debug ("%s: sending %s request", self->name, method);

  string = g_string_sized_new (128);
  g_string_printf (string, "%s %s HTTP/1.1\r\n", method, path);

  had_encoding = had_host = FALSE;

  node = json_object_get_member (options, "headers");
  if (node)
    {
      if (!JSON_NODE_HOLDS_OBJECT (node))
        {
          cockpit_channel_fail (channel, "protocol-error",
                                "%s: invalid \"headers\" field in HTTP stream request", self->name);
          goto out;
        }

      headers = json_node_get_object (node);
      names = json_object_get_members (headers);
      for (l = names; l != NULL; l = g_list_next (l))
        {
          header = l->data;
          if (!cockpit_web_response_is_simple_token (header))
            {
              cockpit_channel_fail (channel, "protocol-error",
                                    "%s: invalid header in HTTP stream request: %s", self->name, header);
              goto out;
            }
          node = json_object_get_member (headers, header);
          if (!node || !JSON_NODE_HOLDS_VALUE (node) || json_node_get_value_type (node) != G_TYPE_STRING)
            {
              cockpit_channel_fail (channel, "protocol-error",
                                    "%s: invalid header value in HTTP stream request: %s", self->name, header);
              goto out;
            }
          value = json_node_get_string (node);
          if (disallowed_header (header, value, self->binary))
            {
              cockpit_channel_fail (channel, "protocol-error",
                                    "%s: disallowed header in HTTP stream request: %s", self->name, header);
              goto out;
            }
          if (!cockpit_web_response_is_header_value (value))
            {
              cockpit_channel_fail (channel, "protocol-error",
                                    "%s: invalid header value in HTTP stream request: %s", self->name, header);
              goto out;
            }

          g_string_append_printf (string, "%s: %s\r\n", (gchar *)l->data, value);
          g_debug ("%s: sending header: %s %s", self->name, (gchar *)l->data, value);

          if (g_ascii_strcasecmp (l->data, "Host") == 0)
            had_host = TRUE;
          if (g_ascii_strcasecmp (l->data, "Accept-Encoding") == 0)
            had_encoding = TRUE;
        }
    }

  if (!had_host)
    {
      g_string_append (string, "Host: ");
      g_string_append_uri_escaped (string, self->client->connectable->name, "[]!%$&()*+,-.:;=\\_~", FALSE);
      g_string_append (string, "\r\n");
    }
  if (!had_encoding)
    g_string_append (string, "Accept-Encoding: identity\r\n");

  if (!self->binary)
    g_string_append (string, "Accept-Charset: UTF-8\r\n");

  request = g_list_reverse (self->request);
  self->request = NULL;

  /* Calculate how much data we have to send */
  total = 0;
  for (l = request; l != NULL; l = g_list_next (l))
    total += g_bytes_get_size (l->data);

  if (request || g_ascii_strcasecmp (method, "POST") == 0)
    g_string_append_printf (string, "Content-Length: %" G_GSIZE_FORMAT "\r\n", total);
  g_string_append (string, "\r\n");

  bytes = g_string_free_to_bytes (string);
  string = NULL;

  cockpit_stream_write (self->stream, bytes);
  g_bytes_unref (bytes);

  /* Now send all the data */
  for (l = request; l != NULL; l = g_list_next (l))
    cockpit_stream_write (self->stream, l->data);

out:
  g_list_free (names);
  g_list_free_full (request, (GDestroyNotify)g_bytes_unref);
  if (string)
    g_string_free (string, TRUE);
}