void client_dispatch_response(Client *client, PendingRequest *pending_request, Packet *response, bool force, bool ignore_authentication) { Node *pending_request_client_node = NULL; int enqueued = 0; #ifdef BRICKD_WITH_PROFILING uint64_t elapsed; #endif if (!ignore_authentication && client->authentication_state != CLIENT_AUTHENTICATION_STATE_DISABLED && client->authentication_state != CLIENT_AUTHENTICATION_STATE_DONE) { log_packet_debug("Ignoring non-authenticated client ("CLIENT_SIGNATURE_FORMAT")", client_expand_signature(client)); goto cleanup; } // find matching pending request if not forced and no pending request is // already given. do this before the disconnect check to ensure that even // for a disconnected client the pending request list is updated correctly if (!force && pending_request == NULL) { pending_request_client_node = client->pending_request_sentinel.next; while (pending_request_client_node != &client->pending_request_sentinel) { pending_request = containerof(pending_request_client_node, PendingRequest, client_node); if (packet_is_matching_response(response, &pending_request->header)) { break; } pending_request_client_node = pending_request_client_node->next; } if (pending_request_client_node == &client->pending_request_sentinel) { pending_request = NULL; goto cleanup; } } if (client->disconnected) { log_debug("Ignoring disconnected client ("CLIENT_SIGNATURE_FORMAT")", client_expand_signature(client)); goto cleanup; } if (force || pending_request != NULL) { enqueued = writer_write(&client->response_writer, response); if (enqueued < 0) { goto cleanup; } if (force) { log_packet_debug("Forced to %s response to client ("CLIENT_SIGNATURE_FORMAT")", enqueued ? "enqueue" : "send", client_expand_signature(client)); } else { #ifdef BRICKD_WITH_PROFILING elapsed = microseconds() - pending_request->arrival_time; log_packet_debug("%s response to client ("CLIENT_SIGNATURE_FORMAT"), was requested %u.%03u msec ago, %d request(s) still pending", enqueued ? "Enqueued" : "Sent", client_expand_signature(client), (unsigned int)(elapsed / 1000), (unsigned int)(elapsed % 1000), client->pending_request_count - 1); #else log_packet_debug("%s response to client ("CLIENT_SIGNATURE_FORMAT"), %d request(s) still pending", enqueued ? "Enqueued" : "Sent", client_expand_signature(client), client->pending_request_count - 1); #endif } } cleanup: if (pending_request != NULL) { pending_request_remove_and_free(pending_request); } }
void network_dispatch_response(Packet *response) { EnumerateCallback *enumerate_callback; char packet_signature[PACKET_MAX_SIGNATURE_LENGTH]; int i; Client *client; Node *pending_request_global_node; PendingRequest *pending_request; if (packet_header_get_sequence_number(&response->header) == 0) { if (response->header.function_id == CALLBACK_ENUMERATE) { enumerate_callback = (EnumerateCallback *)response; if (enumerate_callback->enumeration_type == ENUMERATION_TYPE_CONNECTED || enumerate_callback->enumeration_type == ENUMERATION_TYPE_DISCONNECTED) { network_drop_pending_requests(response->header.uid); } } if (_clients.count == 0) { log_packet_debug("No clients connected, dropping %s (%s)", packet_get_response_type(response), packet_get_response_signature(packet_signature, response)); return; } log_packet_debug("Broadcasting %s (%s) to %d client(s)", packet_get_response_type(response), packet_get_response_signature(packet_signature, response), _clients.count); for (i = 0; i < _clients.count; ++i) { client = array_get(&_clients, i); client_dispatch_response(client, NULL, response, true, false); } } else if (_clients.count + _zombies.count > 0) { log_packet_debug("Dispatching response (%s) to %d client(s) and %d zombies(s)", packet_get_response_signature(packet_signature, response), _clients.count, _zombies.count); pending_request_global_node = _pending_request_sentinel.next; while (pending_request_global_node != &_pending_request_sentinel) { pending_request = containerof(pending_request_global_node, PendingRequest, global_node); if (packet_is_matching_response(response, &pending_request->header)) { if (pending_request->client != NULL) { client_dispatch_response(pending_request->client, pending_request, response, false, false); } else { zombie_dispatch_response(pending_request->zombie, pending_request); } return; } pending_request_global_node = pending_request_global_node->next; } log_warn("Broadcasting response (%s) because no client/zombie has a matching pending request", packet_get_response_signature(packet_signature, response)); for (i = 0; i < _clients.count; ++i) { client = array_get(&_clients, i); client_dispatch_response(client, NULL, response, true, false); } } else { log_packet_debug("No clients/zombies connected, dropping response (%s)", packet_get_response_signature(packet_signature, response)); } }