void
cockpit_channel_socket_open (CockpitWebService *service,
                             JsonObject *open,
                             const gchar *original_path,
                             const gchar *path,
                             GIOStream *io_stream,
                             GHashTable *headers,
                             GByteArray *input_buffer,
                             gboolean for_tls_proxy)
{
  CockpitChannelSocket *self = NULL;
  WebSocketDataType data_type;
  CockpitTransport *transport;
  gchar **protocols = NULL;
  gchar *id = NULL;

  if (!cockpit_web_service_parse_external (open, NULL, NULL, NULL, &protocols) ||
      !cockpit_web_service_parse_binary (open, &data_type))
    {
      respond_with_error (original_path, path, io_stream, for_tls_proxy, headers, 400, "Bad channel request");
      goto out;
    }

  transport = cockpit_web_service_get_transport (service);
  if (!transport)
    {
      respond_with_error (original_path, path, io_stream, for_tls_proxy, headers, 502, "Failed to open channel transport");
      goto out;
    }

  json_object_set_boolean_member (open, "flow-control", TRUE);

  id = cockpit_web_service_unique_channel (service);
  self = g_object_new (COCKPIT_TYPE_CHANNEL_SOCKET,
                       "transport", transport,
                       "options", open,
                       "id", id,
                       NULL);

  self->data_type = data_type;

  self->socket = cockpit_web_service_create_socket ((const gchar **)protocols, original_path,
                                                     io_stream, headers, input_buffer, for_tls_proxy);
  self->socket_open = g_signal_connect (self->socket, "open", G_CALLBACK (on_socket_open), self);
  self->socket_message = g_signal_connect (self->socket, "message", G_CALLBACK (on_socket_message), self);
  self->socket_close = g_signal_connect (self->socket, "close", G_CALLBACK (on_socket_close), self);

  /* Unref when the channel closes */
  g_signal_connect_after (self, "closed", G_CALLBACK (g_object_unref), NULL);

  /* Tell the channel to throttle based on back pressure from socket */
  cockpit_flow_throttle (COCKPIT_FLOW (self), COCKPIT_FLOW (self->socket));

  /* Tell the socket peer's output to throttle based on back pressure */
  cockpit_flow_throttle (COCKPIT_FLOW (self->socket), COCKPIT_FLOW (self));

out:
  g_free (id);
  g_free (protocols);
}
Esempio n. 2
0
void
cockpit_channel_socket_open (CockpitWebService *service,
                             JsonObject *open,
                             const gchar *original_path,
                             const gchar *path,
                             GIOStream *io_stream,
                             GHashTable *headers,
                             GByteArray *input_buffer)
{
  CockpitChannelSocket *chock = NULL;
  WebSocketDataType data_type;
  CockpitTransport *transport;
  gchar **protocols = NULL;

  if (!cockpit_web_service_parse_external (open, NULL, NULL, &protocols) ||
      !cockpit_web_service_parse_binary (open, &data_type))
    {
      respond_with_error (original_path, path, io_stream, headers, 400, "Bad channel request");
      goto out;
    }

  transport = cockpit_web_service_ensure_transport (service, open);
  if (!transport)
    {
      respond_with_error (original_path, path, io_stream, headers, 502, "Failed to open channel transport");
      goto out;
    }

  chock = g_new0 (CockpitChannelSocket, 1);
  chock->channel = cockpit_web_service_unique_channel (service);
  chock->open = json_object_ref (open);
  chock->data_type = data_type;

  json_object_set_string_member (open, "command", "open");
  json_object_set_string_member (open, "channel", chock->channel);

  chock->socket = cockpit_web_service_create_socket ((const gchar **)protocols, original_path,
                                                     io_stream, headers, input_buffer);
  chock->socket_open = g_signal_connect (chock->socket, "open", G_CALLBACK (on_socket_open), chock);
  chock->socket_message = g_signal_connect (chock->socket, "message", G_CALLBACK (on_socket_message), chock);
  chock->socket_close = g_signal_connect (chock->socket, "close", G_CALLBACK (on_socket_close), chock);

  chock->transport = g_object_ref (transport);
  chock->transport_recv = g_signal_connect (chock->transport, "recv", G_CALLBACK (on_transport_recv), chock);
  chock->transport_control = g_signal_connect (chock->transport, "control", G_CALLBACK (on_transport_control), chock);
  chock->transport_closed = g_signal_connect (chock->transport, "closed", G_CALLBACK (on_transport_closed), chock);

out:
  g_free (protocols);
}
Esempio n. 3
0
static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) {
    struct conn_data *conn = (struct conn_data *) nc->user_data;
    const time_t now = time(NULL);
#ifdef DEBUG
    write_log("%d conn=%p nc=%p ev=%d ev_data=%p bec=%p bec_nc=%p\n", now, conn,
              nc, ev, ev_data, conn != NULL ? conn->be_conn : NULL,
              conn != NULL && conn->be_conn != NULL ? conn->be_conn->nc : NULL);
#endif

    if (conn == NULL) {
        if (ev == NS_ACCEPT) {
            conn = calloc(1, sizeof(*conn));
            if (conn == NULL) {
                send_http_err(nc, s_error_500);
            } else {
                memset(conn, 0, sizeof(*conn));
                nc->user_data = conn;
                conn->client.nc = nc;
                conn->client.body_len = -1;
                conn->backend.body_len = -1;
                conn->last_activity = now;
            }
            return;
        } else {
            nc->flags |= NSF_CLOSE_IMMEDIATELY;
            return;
        }
    }

    if (ev != NS_POLL) conn->last_activity = now;

    switch (ev) {
    case NS_HTTP_REQUEST: { /* From client */
        assert(conn != NULL);
        assert(conn->be_conn == NULL);
        struct http_message *hm = (struct http_message *) ev_data;
        conn->client.flags.keep_alive = is_keep_alive(hm);

        if (!connect_backend(conn, hm)) {
            respond_with_error(conn, s_error_500);
            break;
        }

        if (conn->backend.nc == NULL) {
            /* This is a redirect, we're done. */
            conn->client.nc->flags |= NSF_SEND_AND_CLOSE;
            break;
        }

        forward(conn, hm, &conn->client, &conn->backend);
        break;
    }

    case NS_CONNECT: { /* To backend */
        assert(conn != NULL);
        assert(conn->be_conn != NULL);
        int status = *(int *) ev_data;
        if (status != 0) {
            write_log("Error connecting to %s: %d (%s)\n",
                      conn->be_conn->be->host_port, status, strerror(status));
            /* TODO(lsm): mark backend as defunct, try it later on */
            respond_with_error(conn, s_error_500);
            conn->be_conn->nc = NULL;
            release_backend(conn);
            break;
        }
        break;
    }

    case NS_HTTP_REPLY: { /* From backend */
        assert(conn != NULL);
        struct http_message *hm = (struct http_message *) ev_data;
        conn->backend.flags.keep_alive = s_backend_keepalive && is_keep_alive(hm);
        forward(conn, hm, &conn->backend, &conn->client);
        release_backend(conn);
        if (!conn->client.flags.keep_alive) {
            conn->client.nc->flags |= NSF_SEND_AND_CLOSE;
        } else {
#ifdef DEBUG
            write_log("conn=%p remains open\n", conn);
#endif
        }
        break;
    }

    case NS_POLL: {
        assert(conn != NULL);
        if (now - conn->last_activity > CONN_IDLE_TIMEOUT &&
                conn->backend.nc == NULL /* not waiting for backend */) {
#ifdef DEBUG
            write_log("conn=%p has been idle for too long\n", conn);
            conn->client.nc->flags |= NSF_SEND_AND_CLOSE;
#endif
        }
        break;
    }

    case NS_CLOSE: {
        assert(conn != NULL);
        if (nc == conn->client.nc) {
#ifdef DEBUG
            write_log("conn=%p nc=%p client closed, body_sent=%d\n", conn, nc,
                      conn->backend.body_sent);
#endif
            conn->client.nc = NULL;
            if (conn->backend.nc != NULL) {
                conn->backend.nc->flags |= NSF_CLOSE_IMMEDIATELY;
            }
        } else if (nc == conn->backend.nc) {
#ifdef DEBUG
            write_log("conn=%p nc=%p backend closed\n", conn, nc);
#endif
            conn->backend.nc = NULL;
            if (conn->client.nc != NULL &&
                    (conn->backend.body_len < 0 ||
                     conn->backend.body_sent < conn->backend.body_len)) {
                write_log("Backend %s disconnected.\n", conn->be_conn->be->host_port);
                respond_with_error(conn, s_error_500);
            }
        }
        if (conn->client.nc == NULL && conn->backend.nc == NULL) {
            free(conn);
        }
        break;
    }
    }
}