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: support http package #72

Merged
merged 5 commits into from
Apr 25, 2024
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
38 changes: 15 additions & 23 deletions packages/web5/lib/src/dids/did_dht/did_dht.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'dart:typed_data';

import 'package:web5/src/dids.dart';
Expand Down Expand Up @@ -86,16 +85,17 @@ class DidDht {
final message = await Bep44Message.create(dnsPacket.encode(), seq, sign);

final pkarrUrl = Uri.parse('$gatewayUri/$id');
final request = await HttpClient().putUrl(pkarrUrl);

request.headers.contentType = ContentType.binary;
request.add(message);
final response = await http.Client().put(
pkarrUrl,
headers: {
'Content-Type': 'application/octet-stream',
},
body: message,
);

final response = await request.close();
if (response.statusCode != HttpStatus.ok) {
final body = await response.transform(utf8.decoder).join();
if (response.statusCode != 200) {
throw Exception(
'Failed to publish DID document. got: ${response.statusCode} $body',
'Failed to publish DID document. got: ${response.statusCode} ${response.body}',
);
}
}
Expand All @@ -111,7 +111,7 @@ class DidDht {
static Future<DidResolutionResult> resolve(
Did did, {
String relayUrl = _defaultRelay,
HttpClient? client,
http.Client? client,
}) async {
if (did.method != methodName) {
return DidResolutionResult.withError(DidResolutionError.invalidDid);
Expand All @@ -127,19 +127,11 @@ class DidDht {
final parsedRelayUrl = Uri.parse(relayUrl);
final resolutionUrl = parsedRelayUrl.replace(path: did.id);

final httpClient = client ??= HttpClient();
final request = await httpClient.getUrl(resolutionUrl);
final response = await request.close();

final List<int> bytes = [];
await for (var byteList in response) {
bytes.addAll(byteList);
}

httpClient.close(force: false);
final httpClient = client ??= http.Client();
final response = await httpClient.get(resolutionUrl);

final bep44Message = Bep44Message.verify(
Uint8List.fromList(bytes),
response.bodyBytes,
Uint8List.fromList(identityKey),
);

Expand Down Expand Up @@ -167,6 +159,6 @@ class DidDhtResolver extends DidMethodResolver {
String get name => DidDht.methodName;

@override
Future<DidResolutionResult> resolve(Did did, {HttpClient? options}) async =>
Future<DidResolutionResult> resolve(Did did, {http.Client? options}) async =>
DidDht.resolve(did, client: options);
}
16 changes: 7 additions & 9 deletions packages/web5/lib/src/dids/did_web/did_web.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:convert';
import 'dart:io';

import 'package:http/http.dart' as http;
import 'package:web5/src/crypto.dart';
import 'package:web5/src/dids/bearer_did.dart';
import 'package:web5/src/dids/did.dart';
Expand Down Expand Up @@ -85,7 +85,7 @@ class DidWeb {

static Future<DidResolutionResult> resolve(
Did did, {
HttpClient? client,
http.Client? client,
}) async {
if (did.method != methodName) {
return DidResolutionResult.withError(DidResolutionError.invalidDid);
Expand All @@ -106,17 +106,15 @@ class DidWeb {
if (didUri.path.isEmpty) didUri = didUri.replace(path: '/.well-known');
didUri = didUri.replace(pathSegments: [...didUri.pathSegments, 'did.json']);

final HttpClient httpClient = client ??= HttpClient();
final HttpClientRequest request = await httpClient.getUrl(didUri);
final HttpClientResponse response = await request.close();
final httpClient = client ??= http.Client();
final response = await httpClient.get(didUri);

if (response.statusCode != 200) {
return DidResolutionResult.withError(DidResolutionError.notFound);
}

final String str = await response.transform(utf8.decoder).join();
final dynamic jsonParsed = json.decode(str);
final DidDocument doc = DidDocument.fromJson(jsonParsed);
final jsonParsed = json.decode(response.body);
final doc = DidDocument.fromJson(jsonParsed);

return DidResolutionResult(didDocument: doc);
}
Expand All @@ -127,6 +125,6 @@ class DidWebResolver extends DidMethodResolver {
String get name => DidWeb.methodName;

@override
Future<DidResolutionResult> resolve(Did did, {HttpClient? options}) =>
Future<DidResolutionResult> resolve(Did did, {http.Client? options}) =>
DidWeb.resolve(did, client: options);
}
1 change: 1 addition & 0 deletions packages/web5/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies:
convert: ^3.1.1
cryptography: ^2.7.0
pointycastle: ^3.7.3
http: ^1.2.0

dev_dependencies:
lints: ^3.0.0
Expand Down
56 changes: 35 additions & 21 deletions packages/web5/test/dids/did_dht/did_dht_test.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
import 'dart:io';

import 'package:convert/convert.dart';
import 'package:http/http.dart' as http;
import 'package:mocktail/mocktail.dart';

import 'package:test/test.dart';
import 'package:web5/web5.dart';

class MockHttpClient extends Mock implements HttpClient {}

class MockHttpRequest extends Mock implements HttpClientRequest {}

class MockHttpResponse extends Mock implements HttpClientResponse {}
import '../../helpers/mocks.dart';

const validDidDhtDocument =
'''{id: did:dht:74hg1efatndi8enx3e4z6c4u8ieh1xfkyay4ntg4dg1w6risu35y, verificationMethod: [{id: 0, type: JsonWebKey2020, controller: did:dht:74hg1efatndi8enx3e4z6c4u8ieh1xfkyay4ntg4dg1w6risu35y, publicKeyJwk: {kty: OKP, alg: EdDSA, kid: a6tCQvXJQIZQZs_A126CcOT7PuP6R3yADH6DJLr1Zkg, crv: Ed25519, x: 7rhpILiIh1OgT8o1fzNTPVHJPKoGAaFE2hmlTxK2nnY}}], service: [{id: kyc-widget, type: kyc-widget, serviceEndpoint: http://localhost:5173}, {id: pfi, type: PFI, serviceEndpoint: http://localhost:8892/ingress/pfi}], assertionMethod: [0], authentication: [0], capabilityDelegation: [0], capabilityInvocation: [0]}''';
const testVector =
'85ad53bb66db27eba9799d807a1dff1b43823263b72a0824aad94026980048ccbdfc3fdfe9355c243c32f5ed0f40ab3917b925783f6e49b6cd1a73333691e80c000000006625fa11000084000000000300000000035f6b30045f646964343377686674677062646a696878397a653974646e3537357a717a6d347177636365746e66317962696962757a61643772726d7979000010000100001c2000373669643d303b743d303b6b3d7a5468596d6145616138662d365078474c6664336464656e5559784552466b414e61686e66412d6b497341035f7330045f646964343377686674677062646a696878397a653974646e3537357a717a6d347177636365746e66317962696962757a61643772726d7979000010000100001c2000272669643d7066693b743d5046493b73653d68747470733a2f2f6c6f63616c686f73743a39303030045f646964343377686674677062646a696878397a653974646e3537357a717a6d347177636365746e66317962696962757a61643772726d7979000010000100001c20002e2d763d303b766d3d6b303b617574683d6b303b61736d3d6b303b64656c3d6b303b696e763d6b303b7376633d7330';

void main() {
final MockHttpClient mockClient = MockHttpClient();
final MockHttpRequest request = MockHttpRequest();
final MockHttpResponse response = MockHttpResponse();

setUpAll(() {
registerFallbackValue(Uri());
});
late MockHttpClient mockHttpClient;

setUp(() {
reset(mockClient);
reset(request);
reset(response);
mockHttpClient = MockHttpClient();
});

group('DidDht', () {
Expand Down Expand Up @@ -53,13 +42,38 @@ void main() {
});

test('should resolve with didDocument if legit', () async {
when(
() => mockHttpClient.get(
Uri.parse(
'https://diddht.tbddev.org/3whftgpbdjihx9ze9tdn575zqzm4qwccetnf1ybiibuzad7rrmyy',
),
),
).thenAnswer(
(_) async =>
http.Response(String.fromCharCodes(hex.decode(testVector)), 200),
);

final did = Did.parse(
'did:dht:3whftgpbdjihx9ze9tdn575zqzm4qwccetnf1ybiibuzad7rrmyy',
);
final resolutionResult = await DidDht.resolve(did);
final resolutionResult = await DidDht.resolve(
did,
client: mockHttpClient,
);

expect(resolutionResult.didResolutionMetadata.isEmpty(), isTrue);
expect(resolutionResult.didDocument, isNotNull);
expect(
'did:dht:3whftgpbdjihx9ze9tdn575zqzm4qwccetnf1ybiibuzad7rrmyy',
resolutionResult.didDocument?.id,
);

verify(
() => mockHttpClient.get(
Uri.parse(
'https://diddht.tbddev.org/3whftgpbdjihx9ze9tdn575zqzm4qwccetnf1ybiibuzad7rrmyy',
),
),
).called(1);
});
});
}
72 changes: 27 additions & 45 deletions packages/web5/test/dids/did_web_test.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import 'dart:convert';
import 'dart:io';

import 'package:http/http.dart' as http;
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
import 'package:web5/web5.dart';

class MockHttpClient extends Mock implements HttpClient {}

class MockHttpRequest extends Mock implements HttpClientRequest {}

class MockHttpResponse extends Mock implements HttpClientResponse {}
import '../helpers/mocks.dart';

const validDidWebDocument = '''{
"id": "did:web:www.linkedin.com",
Expand Down Expand Up @@ -53,18 +47,10 @@ const validDidWebDocument = '''{
}''';

void main() {
final MockHttpClient mockClient = MockHttpClient();
final MockHttpRequest request = MockHttpRequest();
final MockHttpResponse response = MockHttpResponse();

setUpAll(() {
registerFallbackValue(Uri());
});
late MockHttpClient mockHttpClient;

setUp(() {
reset(mockClient);
reset(request);
reset(response);
mockHttpClient = MockHttpClient();
});

group('DidWeb', () {
Expand All @@ -88,62 +74,58 @@ void main() {
});

test('should return did with failed http request', () async {
when(() => response.statusCode).thenReturn(400);
when(() => request.close()).thenAnswer((_) async => response);
when(() => mockClient.getUrl(any())).thenAnswer((_) async => request);
when(
() => mockHttpClient
.get(Uri.parse('https://www.linkedin.com/.well-known/did.json')),
).thenAnswer((_) async => http.Response('', 400));

final did = Did.parse('did:web:www.linkedin.com');
final result = await DidWeb.resolve(did, client: mockClient);
final result = await DidWeb.resolve(did, client: mockHttpClient);

expect(
result,
DidResolutionResult.withError(DidResolutionError.notFound),
);

verify(
() => mockHttpClient
.get(Uri.parse('https://www.linkedin.com/.well-known/did.json')),
).called(1);
});

test('should resolve successfully', () async {
when(() => response.statusCode).thenReturn(200);
when(() => response.transform(utf8.decoder))
.thenAnswer((_) => Stream.value(validDidWebDocument));
when(() => request.close()).thenAnswer((_) async => response);
when(
() => mockClient.getUrl(
Uri.parse('https://www.linkedin.com/.well-known/did.json'),
),
).thenAnswer((_) async => request);
() => mockHttpClient
.get(Uri.parse('https://www.linkedin.com/.well-known/did.json')),
).thenAnswer((_) async => http.Response(validDidWebDocument, 200));

final did = Did.parse('did:web:www.linkedin.com');
final result = await DidWeb.resolve(did, client: mockClient);
final result = await DidWeb.resolve(did, client: mockHttpClient);

expect(result.didDocument, isNotNull);
expect('did:web:www.linkedin.com', result.didDocument!.id);
expect('did:web:www.linkedin.com', result.didDocument?.id);

verify(
() => mockClient
.getUrl(Uri.parse('https://www.linkedin.com/.well-known/did.json')),
);
() => mockHttpClient
.get(Uri.parse('https://www.linkedin.com/.well-known/did.json')),
).called(1);
});

test('should resolve successfully with paths', () async {
when(() => response.statusCode).thenReturn(200);
when(() => response.transform(utf8.decoder))
.thenAnswer((_) => Stream.value(validDidWebDocument));
when(() => request.close()).thenAnswer((_) async => response);
when(
() => mockClient.getUrl(
Uri.parse('https://www.remotehost.com:8892/ingress/did.json'),
),
).thenAnswer((_) async => request);
() => mockHttpClient
.get(Uri.parse('https://www.remotehost.com:8892/ingress/did.json')),
).thenAnswer((_) async => http.Response(validDidWebDocument, 200));

final did = Did.parse('did:web:www.remotehost.com%3A8892:ingress');
final result = await DidWeb.resolve(
did,
client: mockClient,
client: mockHttpClient,
);
expect(result.didDocument, isNotNull);

verify(
() => mockClient.getUrl(
() => mockHttpClient.get(
Uri.parse('https://www.remotehost.com:8892/ingress/did.json'),
),
);
Expand Down
4 changes: 4 additions & 0 deletions packages/web5/test/helpers/mocks.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import 'package:http/http.dart' as http;
import 'package:mocktail/mocktail.dart';

class MockHttpClient extends Mock implements http.Client {}
Loading