Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.2.0

- Headers emulation

## 0.1.0

- Add benchmark information to README
Expand Down
25 changes: 10 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ This library allows you to connect your Dart or Flutter applications to [Centrif
- **Logging**: Log events, errors, and messages for debugging purposes.
- **Cross-Platform**: Run on Dart VM, Flutter, and Web platforms.
- **Performance**: Achieve high throughput and low latency for real-time messaging.
- **Headers Emulation**: Emulate HTTP headers for WebSocket connections at the Web platform.

## Installation

Expand Down Expand Up @@ -120,28 +121,21 @@ Each message is sent sequentially: the client waits for the server's response be

| | Spinify | Centrifuge-Dart |
| ----- | ------------------- | ------------------- |
| 1 KB | 5763 msg/s (7MB/s) | 5361 msg/s (6MB/s) |
| 5 KB | 4405 msg/s (22MB/s) | 3731 msg/s (18MB/s) |
| 10 KB | 3717 msg/s (37MB/s) | 2857 msg/s (28MB/s) |
| 14 KB | 3305 msg/s (45MB/s) | 2564 msg/s (35MB/s) |
| 16 KB | 3091 msg/s (50MB/s) | 1982 msg/s (32MB/s) |
| 20 KB | 2812 msg/s (56MB/s) | 1811 msg/s (36MB/s) |
| 30 KB | 2463 msg/s (72MB/s) | 1470 msg/s (43MB/s) |
| 40 KB | 1937 msg/s (76MB/s) | 1089 msg/s (42MB/s) |
| 50 KB | 1740 msg/s (85MB/s) | 967 msg/s (47MB/s) |
| 60 KB | 1583 msg/s (92MB/s) | 877 msg/s (51MB/s) |
| 1 KB | 5396 msg/s (6MB/s) | 5433 msg/s (6MB/s) |
| 14 KB | 3216 msg/s (46MB/s) | 3224 msg/s (46MB/s) |
| 30 KB | 2371 msg/s (71MB/s) | 2352 msg/s (70MB/s) |
| 60 KB | 1558 msg/s (92MB/s) | 1547 msg/s (91MB/s) |

_\* Messages larger than 64 KB are not supported._

### Browser (WASM and JS)

| | Spinify WASM | Spinify JS | Centrifuge-Dart JS |
| ----- | ------------------- | ------------------- | ------------------- |
| 1 KB | 3676 msg/s (4MB/s) | 3502 msg/s (4MB/s) | 3067 msg/s (3MB/s) |
| 5 KB | 2659 msg/s (13MB/s) | 3484 msg/s (17MB/s) | 2207 msg/s (11MB/s) |
| 10 KB | 1926 msg/s (19MB/s) | 3189 msg/s (31MB/s) | 1584 msg/s (15MB/s) |
| 14 KB | 1670 msg/s (22MB/s) | 2890 msg/s (39MB/s) | 1287 msg/s (17MB/s) |
| 16 KB | 39 msg/s (662KB/s) | 39 msg/s (662KB/s) | 39 msg/s (662KB/s) |
| 1 KB | 3676 msg/s (4MB/s) | 3590 msg/s (6MB/s) | 3720 msg/s (6MB/s) |
| 5 KB | 2659 msg/s (13MB/s) | 3227 msg/s (18MB/s) | 3223 msg/s (18MB/s) |
| 10 KB | 1926 msg/s (19MB/s) | 3031 msg/s (32MB/s) | 3029 msg/s (32MB/s) |
| 14 KB | 1670 msg/s (22MB/s) | 2750 msg/s (39MB/s) | 2830 msg/s (40MB/s) |

_\* After message sizes exceed 15 KB, there is a noticeable performance drop._

Expand Down Expand Up @@ -180,6 +174,7 @@ _\* After message sizes exceed 15 KB, there is a noticeable performance drop._
- ✅ Benchmarks
- ✅ Performance comparison with other libraries
- ✅ WASM compatibility
- ✅ Headers emulation
- ❌ 95% test coverage
- ❌ JSON codec support for transport
- ❌ DevTools extension
Expand Down
2 changes: 2 additions & 0 deletions example/benchmark/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
Expand Down
30 changes: 15 additions & 15 deletions example/benchmark/.metadata
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.

version:
revision: "2663184aa79047d0a33a14a3b607954f8fdd8730"
revision: "c519ee916eaeb88923e67befb89c0f1dabfa83e6"
channel: "stable"

project_type: app
Expand All @@ -13,26 +13,26 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: android
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: ios
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: linux
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: macos
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: web
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: windows
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6

# User provided section

Expand Down
23 changes: 3 additions & 20 deletions example/benchmark/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.13)
project(runner LANGUAGES CXX)

# The name of the executable created for the application. Change this to change
Expand Down Expand Up @@ -54,25 +54,8 @@ add_subdirectory(${FLUTTER_MANAGED_DIR})
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)

add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")

# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)

# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})

# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")

# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
Expand Down
26 changes: 26 additions & 0 deletions example/benchmark/linux/runner/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.13)
project(runner LANGUAGES CXX)

# Define the application target. To change its name, change BINARY_NAME in the
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
# work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)

# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})

# Add preprocessor definitions for the application ID.
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")

# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)

target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
6 changes: 6 additions & 0 deletions example/benchmark/linux/runner/main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "my_application.h"

int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}
130 changes: 130 additions & 0 deletions example/benchmark/linux/runner/my_application.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#include "my_application.h"

#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif

#include "flutter/generated_plugin_registrant.h"

struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
};

G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)

// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));

// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).
// If running on X and not using GNOME then just use a traditional title bar
// in case the window manager does more exotic layout, e.g. tiling.
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "spinifybenchmark");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "spinifybenchmark");
}

gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));

g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);

FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));

fl_register_plugins(FL_PLUGIN_REGISTRY(view));

gtk_widget_grab_focus(GTK_WIDGET(view));
}

// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);

g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
}

g_application_activate(application);
*exit_status = 0;

return TRUE;
}

// Implements GApplication::startup.
static void my_application_startup(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);

// Perform any actions required at application startup.

G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
}

// Implements GApplication::shutdown.
static void my_application_shutdown(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);

// Perform any actions required at application shutdown.

G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
}

// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}

static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_APPLICATION_CLASS(klass)->startup = my_application_startup;
G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}

static void my_application_init(MyApplication* self) {}

MyApplication* my_application_new() {
// Set the program name to the application ID, which helps various systems
// like GTK and desktop environments map this running application to its
// corresponding .desktop file. This ensures better integration by allowing
// the application to be recognized beyond its binary name.
g_set_prgname(APPLICATION_ID);

return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE,
nullptr));
}
18 changes: 18 additions & 0 deletions example/benchmark/linux/runner/my_application.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_

#include <gtk/gtk.h>

G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)

/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();

#endif // FLUTTER_MY_APPLICATION_H_
4 changes: 4 additions & 0 deletions example/benchmark/macos/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}

override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}
2 changes: 1 addition & 1 deletion example/benchmark/macos/Runner/Configs/AppInfo.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ PRODUCT_NAME = spinifybenchmark
PRODUCT_BUNDLE_IDENTIFIER = dev.plugfox.spinifybenchmark

// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2024 dev.plugfox. All rights reserved.
PRODUCT_COPYRIGHT = Copyright © 2025 dev.plugfox. All rights reserved.
2 changes: 1 addition & 1 deletion example/benchmark/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ dependencies:
cupertino_icons: ^1.0.8

# Centrifuge clients
centrifuge: ^0.14.0
centrifuge: ^0.15.0
spinify:
path: ../../

Expand Down
2 changes: 1 addition & 1 deletion example/benchmark/windows/runner/Runner.rc
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ BEGIN
VALUE "FileDescription", "spinifybenchmark" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "spinifybenchmark" "\0"
VALUE "LegalCopyright", "Copyright (C) 2024 dev.plugfox. All rights reserved." "\0"
VALUE "LegalCopyright", "Copyright (C) 2025 dev.plugfox. All rights reserved." "\0"
VALUE "OriginalFilename", "spinifybenchmark.exe" "\0"
VALUE "ProductName", "spinifybenchmark" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
Expand Down
5 changes: 5 additions & 0 deletions lib/src/model/command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ final class SpinifyConnectRequest extends SpinifyCommand {
required this.subs,
required this.name,
required this.version,
required this.headers,
});

@override
Expand All @@ -86,6 +87,10 @@ final class SpinifyConnectRequest extends SpinifyCommand {

/// Version of client.
final String version;

/// Headers to send.
/// Used for headers emulation at the web platform.
final Map<String, String>? headers;
}

/// {@macro command}
Expand Down
Loading
Loading