CockpitConnectable * cockpit_channel_parse_stream (CockpitChannel *self) { CockpitConnectable *connectable; GSocketConnectable *address; gboolean local; gchar *name; address = parse_address (self, &name, &local); if (!address) return NULL; connectable = g_new0 (CockpitConnectable, 1); connectable->address = address; connectable->name = name; connectable->refs = 1; connectable->local = local; if (!parse_stream_options (self, connectable)) { cockpit_connectable_unref (connectable); connectable = NULL; } return connectable; }
static void close_immediately (CockpitStream *self, const gchar *problem) { GError *error = NULL; GIOStream *io; if (self->priv->closed) return; if (problem) { g_free (self->priv->problem); self->priv->problem = g_strdup (problem); } if (self->priv->connecting) { cockpit_connectable_unref (self->priv->connecting); self->priv->connecting = NULL; } self->priv->closed = TRUE; g_debug ("%s: closing stream%s%s", self->priv->name, self->priv->problem ? ": " : "", self->priv->problem ? self->priv->problem : ""); if (self->priv->in_source) stop_input (self); if (self->priv->out_source) stop_output (self); if (self->priv->io) { io = self->priv->io; self->priv->io = NULL; if (self->priv->sig_accept_cert) { g_signal_handler_disconnect (io, self->priv->sig_accept_cert); self->priv->sig_accept_cert = 0; } g_io_stream_close (io, NULL, &error); if (error) { g_message ("%s: close failed: %s", self->priv->name, error->message); g_clear_error (&error); } g_object_unref (io); } g_debug ("%s: closed", self->priv->name); g_signal_emit (self, cockpit_stream_sig_close, 0, self->priv->problem); }
static void cockpit_http_client_unref (gpointer data) { CockpitHttpClient *client = data; if (--client->refs == 0) { cockpit_http_client_reset (client); if (client->connectable) cockpit_connectable_unref (client->connectable); g_free (client->name); g_slice_free (CockpitHttpClient, client); } }
static void initialize_io (CockpitStream *self) { GInputStream *is; GOutputStream *os; g_return_if_fail (self->priv->in_source == NULL); is = g_io_stream_get_input_stream (self->priv->io); os = g_io_stream_get_output_stream (self->priv->io); if (!G_IS_POLLABLE_INPUT_STREAM (is) || !g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (is)) || !G_IS_POLLABLE_OUTPUT_STREAM (os) || !g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (os))) { g_warning ("%s: stream is not pollable", self->priv->name); close_immediately (self, "internal-error"); return; } if (self->priv->connecting) { cockpit_connectable_unref (self->priv->connecting); self->priv->connecting = NULL; } self->priv->in_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (is), NULL); g_source_set_name (self->priv->in_source, "stream-input"); g_source_set_callback (self->priv->in_source, (GSourceFunc)dispatch_input, self, NULL); g_source_attach (self->priv->in_source, self->priv->context); if (G_IS_TLS_CONNECTION (self->priv->io)) { self->priv->sig_accept_cert = g_signal_connect (G_TLS_CONNECTION (self->priv->io), "accept-certificate", G_CALLBACK (on_rejected_certificate), self); } else { self->priv->sig_accept_cert = 0; } start_output (self); g_signal_emit (self, cockpit_stream_sig_open, 0); }
static void cockpit_web_socket_stream_prepare (CockpitChannel *channel) { CockpitWebSocketStream *self = COCKPIT_WEB_SOCKET_STREAM (channel); CockpitConnectable *connectable = NULL; JsonObject *options; const gchar *path; gboolean started = FALSE; COCKPIT_CHANNEL_CLASS (cockpit_web_socket_stream_parent_class)->prepare (channel); if (self->closed) goto out; connectable = cockpit_channel_parse_stream (channel); if (!connectable) goto out; options = cockpit_channel_get_options (channel); if (!cockpit_json_get_string (options, "path", NULL, &path)) { g_warning ("%s: bad \"path\" field in WebSocket stream request", self->origin); goto out; } else if (path == NULL || path[0] != '/') { g_warning ("%s: invalid or missing \"path\" field in WebSocket stream request", self->origin); goto out; } self->url = g_strdup_printf ("%s://%s%s", connectable->tls ? "wss" : "ws", connectable->name, path); self->origin = g_strdup_printf ("%s://%s", connectable->tls ? "https" : "http", connectable->name); /* Parsed elsewhere */ self->binary = json_object_has_member (options, "binary"); cockpit_connect_stream_full (connectable, NULL, on_socket_connect, g_object_ref (self)); started = TRUE; out: if (connectable) { cockpit_connectable_unref (connectable); if (!started) cockpit_channel_close (channel, "protocol-error"); } }
static void cockpit_http_stream_prepare (CockpitChannel *channel) { CockpitHttpStream *self = COCKPIT_HTTP_STREAM (channel); CockpitConnectable *connectable = NULL; const gchar *payload; const gchar *connection; JsonObject *options; const gchar *path; COCKPIT_CHANNEL_CLASS (cockpit_http_stream_parent_class)->prepare (channel); if (self->failed) goto out; options = cockpit_channel_get_options (channel); if (!cockpit_json_get_string (options, "connection", NULL, &connection)) { cockpit_channel_fail (channel, "protocol-error", "bad \"connection\" field in HTTP stream request"); goto out; } if (!cockpit_json_get_string (options, "path", "/", &path)) { cockpit_channel_fail (channel, "protocol-error", "bad \"path\" field in HTTP stream request"); goto out; } /* * In http-stream1 the headers are sent as first message. * In http-stream2 the headers are in a control message. */ if (cockpit_json_get_string (options, "payload", NULL, &payload) && payload && g_str_equal (payload, "http-stream1")) { self->headers_inline = TRUE; } self->client = cockpit_http_client_ensure (connection); if (!self->client->connectable || json_object_has_member (options, "unix") || json_object_has_member (options, "port") || json_object_has_member (options, "internal") || json_object_has_member (options, "tls") || json_object_has_member (options, "address")) { connectable = cockpit_connect_parse_stream (channel); if (!connectable) goto out; if (self->client->connectable) cockpit_connectable_unref (self->client->connectable); self->client->connectable = cockpit_connectable_ref (connectable); } self->name = g_strdup_printf ("%s://%s%s", self->client->connectable->tls ? "https" : "http", self->client->connectable->name, path); self->stream = cockpit_http_client_checkout (self->client); if (!self->stream) { self->stream = cockpit_stream_connect (self->name, self->client->connectable); self->sig_open = g_signal_connect (self->stream, "open", G_CALLBACK (on_stream_open), self); } /* Parsed elsewhere */ self->binary = json_object_has_member (options, "binary"); self->sig_read = g_signal_connect (self->stream, "read", G_CALLBACK (on_stream_read), self); self->sig_close = g_signal_connect (self->stream, "close", G_CALLBACK (on_stream_close), self); self->sig_rejected_cert = g_signal_connect (self->stream, "rejected-cert", G_CALLBACK (on_rejected_cert), self); /* Let the channel throttle the stream's input flow*/ cockpit_flow_throttle (COCKPIT_FLOW (self->stream), COCKPIT_FLOW (self)); /* Let the stream throtlte the channel peer's output flow */ cockpit_flow_throttle (COCKPIT_FLOW (channel), COCKPIT_FLOW (self->stream)); /* If not waiting for open */ if (!self->sig_open) cockpit_channel_ready (channel, NULL); out: if (connectable) cockpit_connectable_unref (connectable); }