static void
on_web_socket_open (WebSocketConnection *connection,
                    gpointer user_data)
{
  CockpitWebSocketStream *self = COCKPIT_WEB_SOCKET_STREAM (user_data);
  CockpitChannel *channel = COCKPIT_CHANNEL (user_data);
  JsonObject *object;
  JsonObject *headers;
  GHashTableIter iter;
  gpointer key, value;

  headers = json_object_new ();

  g_hash_table_iter_init (&iter, web_socket_client_get_headers (WEB_SOCKET_CLIENT (self->client)));
  while (g_hash_table_iter_next (&iter, &key, &value))
    json_object_set_string_member (headers, key, value);

  object = json_object_new ();
  json_object_set_object_member (object, "headers", headers);

  cockpit_channel_control (channel, "response", object);
  json_object_unref (object);

  cockpit_channel_ready (channel, NULL);
}
static void
on_socket_close (WebSocketConnection *socket,
                 gpointer user_data)
{
  CockpitChannelSocket *self = COCKPIT_CHANNEL_SOCKET (user_data);
  CockpitChannel *channel = COCKPIT_CHANNEL (user_data);
  const gchar *problem = NULL;
  gushort code;

  if (self->closed)
    return;

  code = web_socket_connection_get_close_code (socket);
  if (code == WEB_SOCKET_CLOSE_NORMAL)
    {
      cockpit_channel_control (channel, "done", NULL);
    }
  else
    {
      problem = web_socket_connection_get_close_data (socket);
      if (problem == NULL)
        problem = "disconnected";
    }

  cockpit_channel_close (channel, problem);
}
static gboolean
on_web_socket_closing (WebSocketConnection *connection,
                       gpointer user_data)
{
  CockpitChannel *channel = COCKPIT_CHANNEL (user_data);
  cockpit_channel_control (channel, "done", NULL);
  return TRUE;
}
Esempio n. 4
0
/**
 * cockpit_channel_ready:
 * @self: a pipe
 * @message: an optional control message, or NULL
 *
 * Called by channel implementations to signal when they're
 * ready. Any messages received before the channel was ready
 * will be delivered to the channel's recv() vfunc in the order
 * that they were received.
 *
 * If this is called immediately after or during construction then
 * the closing will happen after the main loop so that handlers
 * can connect appropriately.
 */
void
cockpit_channel_ready (CockpitChannel *self,
                       JsonObject *message)
{
  g_object_ref (self);

  cockpit_transport_thaw (self->priv->transport, self->priv->id);
  cockpit_channel_control (self, "ready", message);

  g_object_unref (self);
}
Esempio n. 5
0
static void
cockpit_channel_actual_send (CockpitChannel *self,
                             GBytes *payload,
                             gboolean trust_is_utf8)
{
  GBytes *validated = NULL;
  guint64 out_sequence;
  JsonObject *ping;
  gsize size;

  g_return_if_fail (self->priv->out_buffer == NULL);
  g_return_if_fail (self->priv->buffer_timeout == 0);

  if (!trust_is_utf8)
    {
      if (!self->priv->binary_ok)
         payload = validated = cockpit_unicode_force_utf8 (payload);
    }

  cockpit_transport_send (self->priv->transport, self->priv->id, payload);

  /* A wraparound of our gint64 size? */
  if (self->priv->flow_control)
    {
      size = g_bytes_get_size (payload);
      g_return_if_fail (G_MAXINT64 - size > self->priv->out_sequence);

      /* How many bytes have been sent (queued) */
      out_sequence = self->priv->out_sequence + size;

      /* Every CHANNEL_FLOW_PING bytes we send a ping */
      if (out_sequence / CHANNEL_FLOW_PING != self->priv->out_sequence / CHANNEL_FLOW_PING)
        {
          ping = json_object_new ();
          json_object_set_int_member (ping, "sequence", out_sequence);
          cockpit_channel_control (self, "ping", ping);
          g_debug ("%s: sending ping with sequence: %" G_GINT64_FORMAT, self->priv->id, out_sequence);
          json_object_unref (ping);
        }

      /* If we've sent more than the window, apply back pressure */
      self->priv->out_sequence = out_sequence;
      if (self->priv->out_sequence > self->priv->out_window)
        {
          g_debug ("%s: sent too much data without acknowledgement, emitting back pressure until %"
                   G_GINT64_FORMAT, self->priv->id, self->priv->out_window);
          cockpit_flow_emit_pressure (COCKPIT_FLOW (self), TRUE);
        }
    }

  if (validated)
    g_bytes_unref (validated);
}
Esempio n. 6
0
/**
 * cockpit_channel_ready:
 * @self: a pipe
 *
 * Called by channel implementations to signal when they're
 * ready. Any messages received before the channel was ready
 * will be delivered to the channel's recv() vfunc in the order
 * that they were received.
 *
 * If this is called immediately after or during construction then
 * the closing will happen after the main loop so that handlers
 * can connect appropriately.
 */
void
cockpit_channel_ready (CockpitChannel *self)
{
  CockpitChannelClass *klass;
  GBytes *decoded;
  GBytes *payload;
  GQueue *queue;

  klass = COCKPIT_CHANNEL_GET_CLASS (self);
  g_assert (klass->recv != NULL);
  g_assert (klass->close != NULL);

  g_object_ref (self);
  while (self->priv->received)
    {
      queue = self->priv->received;
      self->priv->received = NULL;
      for (;;)
        {
          payload = g_queue_pop_head (queue);
          if (payload == NULL)
            break;
          if (self->priv->base64_encoding)
            {
              decoded = base64_decode (payload);
              g_bytes_unref (payload);
              payload = decoded;
            }
          (klass->recv) (self, payload);
          g_bytes_unref (payload);
        }
      g_queue_free (queue);
    }

  cockpit_channel_control (self, "ready", NULL);
  self->priv->ready = TRUE;

  /* No more data coming? */
  if (self->priv->received_done)
    {
      if (klass->control)
        (klass->control) (self, "done", NULL);
    }

  g_object_unref (self);
}
static void
on_socket_open (WebSocketConnection *connection,
                gpointer user_data)
{
  CockpitChannel *channel = COCKPIT_CHANNEL (user_data);
  JsonObject *open;

  /*
   * Actually open the channel. We wait until the WebSocket is open
   * before doing this, so we don't receive messages from the bridge
   * before the websocket is open.
   */

  open = cockpit_channel_get_options (channel);
  cockpit_channel_control (channel, "open", open);

  /* Tell the channel we're ready */
  cockpit_channel_ready (channel, NULL);
}
Esempio n. 8
0
static void
cockpit_http_stream_close (CockpitChannel *channel,
                           const gchar *problem)
{
  CockpitHttpStream *self = COCKPIT_HTTP_STREAM (channel);

  if (problem)
    {
      self->failed = TRUE;
      self->state = FINISHED;
      COCKPIT_CHANNEL_CLASS (cockpit_http_stream_parent_class)->close (channel, problem);
    }
  else if (self->state == RELAY_DATA)
    {
      g_debug ("%s: relayed response", self->name);
      self->state = FINISHED;
      cockpit_channel_control (channel, "done", NULL);

      /* Save this for another round? */
      if (self->keep_alive)
        {
          if (self->sig_open)
            g_signal_handler_disconnect (self->stream, self->sig_open);
          g_signal_handler_disconnect (self->stream, self->sig_read);
          g_signal_handler_disconnect (self->stream, self->sig_close);
          g_signal_handler_disconnect (self->stream, self->sig_rejected_cert);
          cockpit_http_client_checkin (self->client, self->stream);
          cockpit_flow_throttle (COCKPIT_FLOW (self->stream), NULL);
          cockpit_flow_throttle (COCKPIT_FLOW (channel), NULL);
          g_object_unref (self->stream);
          self->stream = NULL;
        }

      COCKPIT_CHANNEL_CLASS (cockpit_http_stream_parent_class)->close (channel, NULL);
    }
  else if (self->state != FINISHED)
    {
      g_warn_if_reached ();
      self->failed = TRUE;
      self->state = FINISHED;
      COCKPIT_CHANNEL_CLASS (cockpit_http_stream_parent_class)->close (channel, "internal-error");
    }
}
Esempio n. 9
0
/**
 * cockpit_channel_ready:
 * @self: a pipe
 * @message: an optional control message, or NULL
 *
 * Called by channel implementations to signal when they're
 * ready. Any messages received before the channel was ready
 * will be delivered to the channel's recv() vfunc in the order
 * that they were received.
 *
 * If this is called immediately after or during construction then
 * the closing will happen after the main loop so that handlers
 * can connect appropriately.
 */
void
cockpit_channel_ready (CockpitChannel *self,
                       JsonObject *message)
{
  FrozenMessage *frozen;

  g_return_if_fail (self->priv->frozen != NULL);

  g_object_ref (self);

  while (self->priv->frozen)
    {
      frozen = g_queue_pop_head (self->priv->frozen);
      if (frozen == NULL)
        break;

      /* Is it a control message */
      if (frozen->control)
        {
          process_control (self, json_object_get_string_member (frozen->control, "command"),
                           frozen->control);
        }
      else
        {
          process_recv (self, frozen->payload);
        }

      frozen_message_free (frozen);
    }

  if (self->priv->frozen)
    g_queue_free (self->priv->frozen);
  self->priv->frozen = NULL;

  cockpit_channel_control (self, "ready", message);

  g_object_unref (self);
}
Esempio n. 10
0
static void
on_files_listed (GObject *source_object,
                 GAsyncResult *res,
                 gpointer user_data)
{
  GError *error = NULL;
  JsonObject *options;
  GList *files;

  files = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source_object), res, &error);
  if (error)
    {
      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
        {
          CockpitFslist *self = COCKPIT_FSLIST (user_data);
          g_message ("%s: couldn't process files %s", COCKPIT_FSLIST(user_data)->path, error->message);
          options = cockpit_channel_close_options (COCKPIT_CHANNEL (self));
          json_object_set_string_member (options, "message", error->message);
          cockpit_channel_close (COCKPIT_CHANNEL (self), "internal-error");
        }
      g_clear_error (&error);
      return;
    }

  CockpitFslist *self = COCKPIT_FSLIST (user_data);

  if (files == NULL)
    {
      g_clear_object (&self->cancellable);
      g_object_unref (source_object);

      cockpit_channel_ready (COCKPIT_CHANNEL (self));

      if (self->monitor == NULL)
        {
          cockpit_channel_control (COCKPIT_CHANNEL (self), "done", NULL);
          cockpit_channel_close (COCKPIT_CHANNEL (self), NULL);
        }
      return;
    }

  for (GList *l = files; l; l = l->next)
    {
      GFileInfo *info = G_FILE_INFO (l->data);
      JsonObject *msg;
      GBytes *msg_bytes;

      msg = json_object_new ();
      json_object_set_string_member (msg, "event", "present");
      json_object_set_string_member
        (msg, "path", g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME));
      json_object_set_string_member
        (msg, "type", cockpit_file_type_to_string (g_file_info_get_file_type (info)));
      msg_bytes = cockpit_json_write_bytes (msg);
      json_object_unref (msg);
      cockpit_channel_send (COCKPIT_CHANNEL(self), msg_bytes, FALSE);
      g_bytes_unref (msg_bytes);
    }

  g_list_free_full (files, g_object_unref);

  g_file_enumerator_next_files_async (G_FILE_ENUMERATOR (source_object),
                                      10,
                                      G_PRIORITY_DEFAULT,
                                      self->cancellable,
                                      on_files_listed,
                                      self);
}
Esempio n. 11
0
static gboolean
relay_headers (CockpitHttpStream *self,
               CockpitChannel *channel,
               GByteArray *buffer)
{
  GHashTable *headers = NULL;
  gchar *version = NULL;
  gchar *reason = NULL;
  JsonObject *object;
  const gchar *data;
  JsonObject *heads;
  GHashTableIter iter;
  GBytes *message;
  gpointer key;
  gpointer value;
  guint status;
  gsize length;
  gssize offset;
  gssize offset2;

  data = (const gchar *)buffer->data;
  length = buffer->len;

  offset = web_socket_util_parse_status_line (data, length, &version, &status, &reason);
  if (offset == 0)
    return FALSE; /* want more data */

  if (offset < 0)
    {
      cockpit_channel_fail (channel, "protocol-error",
                            "%s: received response with bad HTTP status line", self->name);
      goto out;
    }

  offset2 = web_socket_util_parse_headers (data + offset, length - offset, &headers);
  if (offset2 == 0)
    return FALSE; /* want more data */

  if (offset2 < 0)
    {
      cockpit_channel_fail (channel, "protocol-error",
                            "%s: received response with bad HTTP headers", self->name);
      goto out;
    }

  g_debug ("%s: response: %u %s", self->name, status, reason);
  g_hash_table_iter_init (&iter, headers);
  while (g_hash_table_iter_next (&iter, &key, &value))
    g_debug ("%s: header: %s %s", self->name, (gchar *)key, (gchar *)value);

  if (!parse_transfer_encoding (self, channel, headers) ||
      !parse_content_length (self, channel, status, headers) ||
      !parse_keep_alive (self, channel, version, headers))
    goto out;

  cockpit_pipe_skip (buffer, offset + offset2);

  if (!self->binary)
    {
      g_hash_table_remove (headers, "Content-Length");
      g_hash_table_remove (headers, "Range");
    }
  g_hash_table_remove (headers, "Connection");
  g_hash_table_remove (headers, "Transfer-Encoding");

  /* Now serialize all the rest of this into JSON */
  object = json_object_new ();
  json_object_set_int_member (object, "status", status);
  json_object_set_string_member (object, "reason", reason);

  heads = json_object_new();
  g_hash_table_iter_init (&iter, headers);
  while (g_hash_table_iter_next (&iter, &key, &value))
    json_object_set_string_member (heads, key, value);

  json_object_set_object_member (object, "headers", heads);

  if (self->headers_inline)
    {
      message = cockpit_json_write_bytes (object);
      cockpit_channel_send (channel, message, TRUE);
      g_bytes_unref (message);
    }
  else
    {
      cockpit_channel_control (channel, "response", object);
    }

  json_object_unref (object);

out:
  if (headers)
    g_hash_table_unref (headers);
  g_free (version);
  g_free (reason);

  return TRUE;
}