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); }
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); }
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; } } }