Skip to content

Commit

Permalink
feat: support http package (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
ethan-tbd committed Apr 25, 2024
1 parent 130cbcb commit d5d380d
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 98 deletions.
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 {}

0 comments on commit d5d380d

Please sign in to comment.