Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create initial NDN binding implementation #113

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
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
52 changes: 52 additions & 0 deletions example/ndn/client_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2024 Contributors to the Eclipse Foundation. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// SPDX-License-Identifier: BSD-3-Clause

// ignore_for_file: avoid_print

import "package:dart_wot/binding_ndn.dart";
import "package:dart_wot/core.dart";
import "package:dart_wot/src/binding_ndn/ndn_config.dart";

Future<void> main() async {
final faceUri = Uri.parse("tcp4://localhost:6363");
final ndnConfig = NdnConfig(
faceUri: faceUri,
);

final servient = Servient(
clientFactories: [NdnClientFactory(ndnConfig: ndnConfig)],
);

final wot = await servient.start();

final thingDescription = {
"@context": "https://www.w3.org/2022/wot/td/v1.1",
"title": "NDN Thing",
"id": "urn:test",
"securityDefinitions": {
"nosec_sc": {"scheme": "nosec"},
},
"security": "nosec_sc",
"properties": {
"ping": {
"forms": [
{
"contentType": "text/plain",
"href": "ndn:///ndn/ping/9001",
},
],
},
},
}.toThingDescription();

final consumedThing = await wot.consume(thingDescription);

final result = await consumedThing.readProperty("ping");

print(await result.value());

await servient.shutdown();
}
13 changes: 13 additions & 0 deletions lib/binding_ndn.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2024 Contributors to the Eclipse Foundation. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// SPDX-License-Identifier: BSD-3-Clause

/// Protocol binding for Named Data Networking (NDN). Follows
/// an experimental [NDN Protocol Binding specification].
///
/// [NDN Protocol Binding specification]: https://github.com/JKRhb/wot-binding-templates/tree/ndn-binding
library binding_ndn;

export "src/binding_ndn/ndn_consumer_factory.dart";
5 changes: 0 additions & 5 deletions lib/src/binding_coap/coap_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -438,11 +438,6 @@ final class CoapClient implements ProtocolClient {
return CoapSubscription(coapClient, null, complete);
}

@override
Future<void> start() async {
// Do nothing
}

@override
Future<void> stop() async {}

Expand Down
2 changes: 1 addition & 1 deletion lib/src/binding_coap/coap_client_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ final class CoapClientFactory implements ProtocolClientFactory {
}

@override
ProtocolClient createClient() => CoapClient(
Future<ProtocolClient> createClient() async => CoapClient(
coapConfig: coapConfig,
pskCredentialsCallback: _pskCredentialsCallback,
aceSecurityCallback: _aceSecurityCallback,
Expand Down
5 changes: 0 additions & 5 deletions lib/src/binding_http/http_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,6 @@ final class HttpClient implements ProtocolClient {
return _contentFromResponse(form, response);
}

@override
Future<void> start() async {
// Do nothing
}

@override
Future<void> stop() async {
_client.close();
Expand Down
2 changes: 1 addition & 1 deletion lib/src/binding_http/http_client_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class HttpClientFactory implements ProtocolClientFactory {
}

@override
ProtocolClient createClient() => HttpClient(
Future<ProtocolClient> createClient() async => HttpClient(
basicCredentialsCallback: _basicCredentialsCallback,
bearerCredentialsCallback: _bearerCredentialsCallback,
);
Expand Down
5 changes: 0 additions & 5 deletions lib/src/binding_mqtt/mqtt_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,6 @@ final class MqttClient implements ProtocolClient {
..disconnect();
}

@override
Future<void> start() async {
// Do nothing
}

@override
Future<void> stop() async {
// Do nothing
Expand Down
2 changes: 1 addition & 1 deletion lib/src/binding_mqtt/mqtt_client_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ final class MqttClientFactory implements ProtocolClientFactory {
_basicCredentialsCallback;

@override
ProtocolClient createClient() => MqttClient(
Future<ProtocolClient> createClient() async => MqttClient(
mqttConfig: _mqttConfig,
basicCredentialsCallback: _basicCredentialsCallback,
);
Expand Down
25 changes: 25 additions & 0 deletions lib/src/binding_ndn/ndn_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2024 Contributors to the Eclipse Foundation. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// SPDX-License-Identifier: BSD-3-Clause

import "package:meta/meta.dart";

import "ndn_consumer.dart";

@immutable

/// Configuration class used by [NdnClient]s.
class NdnConfig {
///
const NdnConfig({
this.faceUri,
});

/// [Uri] of the local NDN Forwarding Daemon (NFD).
///
/// If `null`, then the default [Uri] will be used, connecting the
/// [NdnClient] to the NFD via a Unix Socket.
final Uri? faceUri;
}
92 changes: 92 additions & 0 deletions lib/src/binding_ndn/ndn_consumer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2024 Contributors to the Eclipse Foundation. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// SPDX-License-Identifier: BSD-3-Clause

import "package:dart_ndn/dart_ndn.dart";
import "package:meta/meta.dart";

import "../../core.dart";
import "ndn_config.dart";

/// A WoT [ProtocolClient] acting as an NDN consumer.
@immutable
class NdnClient implements ProtocolClient {
/// Creates a new [NdnClient] from an NDN [_consumer].
const NdnClient(this._consumer);

/// Asynchronously creates a new [NdnClient] from an [NdnConfig].
static Future<NdnClient> create(NdnConfig config) async {
final consumer = await Consumer.create(config.faceUri);
return NdnClient(consumer);
}

final Consumer _consumer;

@override
Stream<DiscoveryContent> discoverDirectly(
Uri uri, {
bool disableMulticast = false,
}) {
// TODO: implement discoverDirectly
throw UnimplementedError();
}

@override
Stream<DiscoveryContent> discoverWithCoreLinkFormat(Uri uri) {
// TODO: implement discoverWithCoreLinkFormat
throw UnimplementedError();
}

@override
Future<Content> invokeResource(AugmentedForm form, Content content) {
// TODO: implement invokeResource
throw UnimplementedError();
}

@override
Future<Content> readResource(AugmentedForm form) async {
// TODO: Add Name.fromUri
final name = Name.fromString(form.href.path);

final result = await _consumer.expressInterest(name);

switch (result) {
case DataReceived(:final data):
final iterable = [data.content ?? <int>[]];

return Content(form.contentType, Stream.fromIterable(iterable));
default:
throw Exception("TODO");
}
}

@override
Future<Content> requestThingDescription(Uri url) {
// TODO: implement requestThingDescription
throw UnimplementedError();
}

@override
Future<void> stop() async {
await _consumer.shutdown();
}

@override
Future<Subscription> subscribeResource(
AugmentedForm form, {
required void Function(Content content) next,
void Function(Exception error)? error,
required void Function() complete,
}) {
// TODO: implement subscribeResource
throw UnimplementedError();
}

@override
Future<void> writeResource(AugmentedForm form, Content content) {
// TODO: implement writeResource
throw UnimplementedError();
}
}
46 changes: 46 additions & 0 deletions lib/src/binding_ndn/ndn_consumer_factory.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2024 Contributors to the Eclipse Foundation. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// SPDX-License-Identifier: BSD-3-Clause

import "package:meta/meta.dart";

import "../../core.dart";
import "ndn_config.dart";
import "ndn_consumer.dart";

/// A [ProtocolClientFactory] that produces
@immutable
class NdnClientFactory implements ProtocolClientFactory {
/// Creates a new [ProtocolClientFactory] from an [ndnConfig].
const NdnClientFactory({
this.ndnConfig = const NdnConfig(),
});

/// The [NdnConfig] acting as the blueprint for creating
final NdnConfig ndnConfig;

@override
Future<ProtocolClient> createClient() async {
return NdnClient.create(ndnConfig);
}

@override
bool destroy() {
return true;
}

@override
bool init() {
return true;
}

@override
Set<String> get schemes => {"ndn"};

@override
bool supportsOperation(OperationType operationType, String? subprotocol) {
return operationType == OperationType.readproperty && subprotocol == null;
}
}
18 changes: 10 additions & 8 deletions lib/src/core/implementation/consumed_thing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ class ConsumedThing implements scripting_api.ConsumedThing {
/// Determines the id of this [ConsumedThing].
String get identifier => thingDescription.identifier;

(ProtocolClient client, AugmentedForm form) _getClientFor(
Future<(ProtocolClient client, AugmentedForm form)> _getClientFor(
List<Form> forms,
OperationType operationType,
InteractionAffordance interactionAffordance, {
required int? formIndex,
required Map<String, Object>? uriVariables,
}) {
}) async {
final augmentedForms = forms
.map(
(form) => AugmentedForm.new(
Expand All @@ -59,7 +59,7 @@ class ConsumedThing implements scripting_api.ConsumedThing {
if (formIndex >= 0 && formIndex < forms.length) {
foundForm = augmentedForms[formIndex];
final scheme = foundForm.href.scheme;
client = servient.clientFor(scheme);
client = await servient.createClient(scheme);
} else {
throw ArgumentError(
'ConsumedThing "$title" missing formIndex for '
Expand All @@ -86,7 +86,7 @@ class ConsumedThing implements scripting_api.ConsumedThing {
orElse: () => throw Exception("No matching form found!"),
);
final scheme = foundForm.href.scheme;
client = servient.clientFor(scheme);
client = await servient.createClient(scheme);
}

return (client, foundForm);
Expand All @@ -108,7 +108,7 @@ class ConsumedThing implements scripting_api.ConsumedThing {
);
}

final (ProtocolClient client, AugmentedForm form) = _getClientFor(
final (ProtocolClient client, AugmentedForm form) = await _getClientFor(
property.forms,
OperationType.readproperty,
property,
Expand All @@ -117,6 +117,8 @@ class ConsumedThing implements scripting_api.ConsumedThing {
);

final content = await client.readResource(form);
// FIXME: Clients should be reusable
await client.stop();
return InteractionOutput(content, servient.contentSerdes, form, property);
}

Expand All @@ -137,7 +139,7 @@ class ConsumedThing implements scripting_api.ConsumedThing {
);
}

final (client, form) = _getClientFor(
final (client, form) = await _getClientFor(
property.forms,
OperationType.writeproperty,
property,
Expand Down Expand Up @@ -173,7 +175,7 @@ class ConsumedThing implements scripting_api.ConsumedThing {
);
}

final (client, form) = _getClientFor(
final (client, form) = await _getClientFor(
action.forms,
OperationType.invokeaction,
action,
Expand Down Expand Up @@ -269,7 +271,7 @@ class ConsumedThing implements scripting_api.ConsumedThing {
subscriptions = _subscribedEvents;
}

final (client, form) = _getClientFor(
final (client, form) = await _getClientFor(
affordance.forms,
operationType,
affordance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import "../content.dart";

/// Base class for a Protocol Client.
abstract interface class ProtocolClient {
/// Starts this [ProtocolClient].
Future<void> start();

/// Stops this [ProtocolClient].
Future<void> stop();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ abstract interface class ProtocolClientFactory {

/// Creates a new [ProtocolClient] with that supports one or more of the given
/// [schemes].
ProtocolClient createClient();
Future<ProtocolClient> createClient();

/// Indicates whether this [ProtocolClientFactory] supports a given
/// [operationType] and subprotocol.
Expand Down
Loading
Loading