Faye for Dart
A Dart implementation of the Faye publish-subscribe messaging system, compatible with the Bayeux protocol.
Features
- Bayeux Protocol Support: Full implementation of the Bayeux protocol for real-time messaging
- Multiple Transport Types: Support for HTTP long-polling, WebSocket, and callback-polling
- Channel Management: Comprehensive channel validation and pattern matching
- Error Handling: Robust error handling with detailed error types
- Streaming API: Modern Dart streams for reactive programming
- Cross-Platform: Works on Dart VM, Flutter, and web platforms
- Type Safety: Full type safety with Dart's strong typing system
- Extension Support: Support for Faye extensions for authentication and message transformation
- Comprehensive Logging: Built-in logging system for debugging and monitoring
- Automatic Reconnection: WebSocket transport with automatic reconnection and heartbeat
Installation
Add the following to your pubspec.yaml:
dependencies:
dart_faye: ^1.0.0
Then run:
dart pub get
Quick Start
Basic Usage
import 'package:dart_faye/dart_faye.dart';
void main() async {
// Create a client
final client = Client('http://localhost:8000/bayeux');
// Connect to the server
await client.connect();
// Subscribe to a channel
final subscription = await client.subscribe('/chat/room1', (data) {
print('Received message: $data');
});
// Publish a message
await client.publish('/chat/room1', {
'user': 'Alice',
'message': 'Hello, world!'
});
// Unsubscribe when done
await client.unsubscribe('/chat/room1');
// Disconnect
await client.disconnect();
}
Advanced Usage with Extensions
import 'package:dart_faye/dart_faye.dart';
void main() async {
// Create client with options
final client = Client('http://localhost:8000/bayeux', {
'timeout': 30,
'interval': 1000,
});
// Set up extension for authentication
final extension = FayeExtension(
api: 'your-api-key',
token: 'your-token',
onLog: (level, data) {
print('[$level] $data');
},
);
// Set the extension on the client
client.setExtension(extension);
// Listen to connection state changes
client.stateStream.listen((state) {
switch (state) {
case 1: // unconnected
print('Disconnected');
break;
case 2: // connecting
print('Connecting...');
break;
case 3: // connected
print('Connected');
break;
case 4: // disconnected
print('Disconnected');
break;
}
});
// Listen to errors
client.errorStream.listen((error) {
print('Error: ${error.message}');
});
// Connect
await client.connect();
// Subscribe to multiple channels
final chatSubscription = await client.subscribe('/chat/**', (data) {
print('Chat message: $data');
});
final notificationSubscription = await client.subscribe('/notifications', (data) {
print('Notification: $data');
});
// Publish messages (extension will automatically add authentication)
await client.publish('/chat/room1', {'message': 'Hello from room 1'});
await client.publish('/chat/room2', {'message': 'Hello from room 2'});
await client.publish('/notifications', {'type': 'info', 'text': 'System update'});
// Get client statistics
final stats = client.statistics;
print('Client stats: $stats');
// Clean up
await client.close();
}
API Reference
Client
The main client class for connecting to a Bayeux server.
Constructor
Client(String endpoint, [Map<String, dynamic>? options])
endpoint: The Bayeux server endpoint URLoptions: Optional configuration options
Methods
connect({Map<String, String>? headers}): Connect to the serverdisconnect(): Disconnect from the serversubscribe(String channel, SubscriptionCallback callback): Subscribe to a channelunsubscribe(String channel): Unsubscribe from a channelpublish(String channel, dynamic data): Publish a message to a channelclose(): Close the client and clean up resourcessetExtension(dynamic extension): Set a Faye extension for authentication and message transformationsetTransport(String transport): Set the transport type ('websocket', 'http', 'callback-polling')
Properties
state: Current connection state (1=unconnected, 2=connecting, 3=connected, 4=disconnected)clientId: Server-assigned client IDtransport: Current transport typesubscriptions: List of active subscriptionsmessageStream: Stream of incoming messagesstateStream: Stream of state changeserrorStream: Stream of errors
FayeExtension
A class for implementing Faye extensions to add authentication and transform messages.
class FayeExtension {
FayeExtension({
required String api,
required String token,
required Function(String level, dynamic data) onLog,
});
Map<String, dynamic> outgoing(Map<String, dynamic> message);
Map<String, dynamic> incoming(Map<String, dynamic> message);
}
Channel
Represents a Bayeux channel with validation and pattern matching.
final channel = Channel('/chat/room1');
print(channel.isMeta); // false
print(channel.isService); // false
print(channel.isPattern); // false
print(channel.matches('/chat/*')); // true
Subscription
Represents a subscription to a channel.
final subscription = await client.subscribe('/chat/room1', (data) {
print('Received: $data');
});
print(subscription.active); // true
print(subscription.messageCount); // 0
subscription.cancel(); // Cancel the subscription
Error Handling
The library provides comprehensive error handling with specific error types:
client.errorStream.listen((error) {
if (error.isNetworkError) {
print('Network error: ${error.message}');
} else if (error.isAuthenticationError) {
print('Authentication error: ${error.message}');
} else if (error.isSubscriptionError) {
print('Subscription error: ${error.message}');
}
});
Transport Types
HTTP Long-Polling
Default transport that works with any HTTP server supporting the Bayeux protocol.
client.setTransport('http');
WebSocket
For better performance when the server supports WebSocket transport.
client.setTransport('websocket');
Callback-Polling
For environments where WebSocket is not available and JSONP is required.
client.setTransport('callback-polling');
Channel Patterns
The library supports channel patterns for subscribing to multiple channels:
/chat/*: Matches/chat/room1,/chat/room2, etc./chat/**: Matches/chat/room1,/chat/room1/messages, etc./users/*/status: Matches/users/alice/status,/users/bob/status, etc.
Error Types
FayeError.network(): Network-related errorsFayeError.timeout(): Timeout errorsFayeError.protocol(): Protocol errorsFayeError.authentication(): Authentication errorsFayeError.subscription(): Subscription errorsFayeError.publication(): Publication errorsFayeError.channel(): Channel validation errors
Logging
The package includes comprehensive logging for debugging:
import 'package:logging/logging.dart';
void main() {
// Configure logging
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: '
'${record.loggerName}: ${record.message}');
});
// Create client and use normally
final client = Client('http://localhost:8000/bayeux');
// ... rest of your code
}
Examples
See the example/ directory for complete working examples:
basic_client.dart: Basic client usagechat_app.dart: Simple chat applicationmulti_transport.dart: Using multiple transport typeserror_handling.dart: Comprehensive error handling
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
This implementation is based on the original Faye library by James Coglan and follows the Bayeux protocol specification.