diff --git a/apps/ssb.json b/apps/ssb.json index 0e6055dda..62252daca 100644 --- a/apps/ssb.json +++ b/apps/ssb.json @@ -1,5 +1,5 @@ { "type": "tildefriends-app", "emoji": "🦀", - "previous": "&QuEaXfdWGAl42dkJBoHocZbtKRT3zT25BNgCo88CSfE=.sha256" + "previous": "&zCaET5zt2iWthUzrTd/BWqfmSjeFryuVru6FrlQJOBI=.sha256" } diff --git a/apps/ssb/tf-tab-connections.js b/apps/ssb/tf-tab-connections.js index d03e30018..3c8e71ff8 100644 --- a/apps/ssb/tf-tab-connections.js +++ b/apps/ssb/tf-tab-connections.js @@ -175,6 +175,9 @@ class TfTabConnectionsElement extends LitElement { .map((x) => html`
  • ${this.render_connection(x)}
  • `)} ${this.render_room_peers(connection.id)} +
    +

    ${connection.destroy_reason}

    +
    `; } diff --git a/src/ssb.c b/src/ssb.c index f67e5eaea..3dbe55347 100644 --- a/src/ssb.c +++ b/src/ssb.c @@ -273,6 +273,7 @@ typedef struct _tf_ssb_connection_t uv_async_t async; uv_async_t scheduled_async; uv_timer_t handshake_timer; + uv_timer_t linger_timer; bool closing; tf_ssb_connection_t* tunnel_connection; @@ -1839,10 +1840,21 @@ JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* pr return root; } +static void _tf_ssb_connection_linger_timer(uv_timer_t* timer) +{ + tf_ssb_connection_t* connection = timer->data; + uv_close((uv_handle_t*)&connection->linger_timer, _tf_ssb_connection_on_close); +} + static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* reason) { tf_ssb_t* ssb = connection->ssb; - connection->closing = true; + if (!connection->closing) + { + connection->closing = true; + uv_timer_start(&connection->linger_timer, _tf_ssb_connection_linger_timer, 5000, 0); + _tf_ssb_notify_connections_changed(ssb, k_tf_ssb_change_update, connection); + } if (connection->connect_callback) { PRE_CALLBACK(connection->ssb, connection->connect_callback); @@ -1853,7 +1865,7 @@ static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const ch } if (!connection->destroy_reason) { - connection->destroy_reason = reason; + connection->destroy_reason = tf_strdup(reason); } _tf_ssb_connection_dispatch_scheduled(connection); tf_free(connection->scheduled); @@ -1879,14 +1891,6 @@ static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const ch tf_ssb_connection_remove_request(*it, (*it)->requests[i].request_number); } } - if (*it == connection) - { - *it = connection->next; - connection->next = NULL; - ssb->connections_count--; - _tf_ssb_notify_connections_changed(ssb, k_tf_ssb_change_remove, connection); - break; - } } bool again = true; while (again) @@ -1934,16 +1938,29 @@ static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const ch } if (JS_IsUndefined(connection->object) && !connection->async.data && !connection->scheduled_async.data && !connection->tcp.data && !connection->connect.data && - !connection->handshake_timer.data && connection->ref_count == 0) + !connection->handshake_timer.data && !connection->linger_timer.data && connection->ref_count == 0) { tf_free(connection->message_requests); connection->message_requests = NULL; connection->message_requests_count = 0; + for (tf_ssb_connection_t** it = &connection->ssb->connections; *it; it = &(*it)->next) + { + if (*it == connection) + { + *it = connection->next; + connection->next = NULL; + ssb->connections_count--; + _tf_ssb_notify_connections_changed(ssb, k_tf_ssb_change_remove, connection); + break; + } + } + if (--connection->ssb->connection_ref_count == 0 && connection->ssb->shutting_down_deferred) { tf_ssb_destroy(connection->ssb); } + tf_free((void*)connection->destroy_reason); tf_free(connection); } } @@ -2723,6 +2740,9 @@ static tf_ssb_connection_t* _tf_ssb_connection_create( uv_timer_init(ssb->loop, &connection->handshake_timer); uv_timer_start(&connection->handshake_timer, _tf_ssb_connection_handshake_timer_callback, k_handshake_timeout_ms, 0); + connection->linger_timer.data = connection; + uv_timer_init(ssb->loop, &connection->linger_timer); + connection->object = JS_NewObjectClass(ssb->context, _connection_class_id); JS_SetOpaque(connection->object, connection); char public_key_str[k_id_base64_len] = { 0 }; @@ -2796,6 +2816,9 @@ tf_ssb_connection_t* tf_ssb_connection_tunnel_create(tf_ssb_t* ssb, const char* uv_timer_init(ssb->loop, &tunnel->handshake_timer); uv_timer_start(&tunnel->handshake_timer, _tf_ssb_connection_handshake_timer_callback, k_handshake_timeout_ms, 0); + tunnel->linger_timer.data = tunnel; + uv_timer_init(ssb->loop, &tunnel->linger_timer); + tunnel->object = JS_NewObjectClass(ssb->context, _connection_class_id); JS_SetOpaque(tunnel->object, tunnel); JS_SetPropertyStr(context, tunnel->object, "id", JS_NewString(context, target_id)); @@ -2956,6 +2979,9 @@ static void _tf_ssb_on_connection(uv_stream_t* stream, int status) uv_timer_init(ssb->loop, &connection->handshake_timer); uv_timer_start(&connection->handshake_timer, _tf_ssb_connection_handshake_timer_callback, k_handshake_timeout_ms, 0); + connection->linger_timer.data = connection; + uv_timer_init(ssb->loop, &connection->linger_timer); + struct sockaddr_storage addr = { 0 }; int size = sizeof(addr); if (uv_tcp_getpeername(&connection->tcp, (struct sockaddr*)&addr, &size) == 0) @@ -4265,6 +4291,11 @@ void tf_ssb_connection_adjust_write_count(tf_ssb_connection_t* connection, int d uv_async_send(&connection->scheduled_async); } +const char* tf_ssb_connection_get_destroy_reason(tf_ssb_connection_t* connection) +{ + return connection ? connection->destroy_reason : NULL; +} + void tf_ssb_sync_start(tf_ssb_t* ssb) { tf_ssb_connections_sync_start(ssb->connections_tracker); diff --git a/src/ssb.h b/src/ssb.h index 3ed1652a0..dc65fcdeb 100644 --- a/src/ssb.h +++ b/src/ssb.h @@ -1093,6 +1093,13 @@ void tf_ssb_connection_adjust_read_backpressure(tf_ssb_connection_t* connection, */ void tf_ssb_connection_adjust_write_count(tf_ssb_connection_t* connection, int delta); +/** +** Get the reason why a connection is going away. +** @param connection The connection. +** @return The reason or NULL. +*/ +const char* tf_ssb_connection_get_destroy_reason(tf_ssb_connection_t* connection); + /** ** Initiate a tunnel connection. ** @param ssb The SSB instance. diff --git a/src/ssb.js.c b/src/ssb.js.c index cb00ca5d7..34dea727e 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -1120,6 +1120,11 @@ static JSValue _tf_ssb_connections(JSContext* context, JSValueConst this_val, in int flags = tf_ssb_connection_get_flags(connection); JS_SetPropertyStr(context, flags_object, "one_shot", JS_NewBool(context, (flags & k_tf_ssb_connect_flag_one_shot) != 0)); JS_SetPropertyStr(context, object, "flags", flags_object); + const char* destroy_reason = tf_ssb_connection_get_destroy_reason(connection); + if (destroy_reason) + { + JS_SetPropertyStr(context, object, "destroy_reason", JS_NewString(context, destroy_reason)); + } JS_SetPropertyUint32(context, result, i, object); } }