A framework for building convergent cross-platform Nextcloud clients using Flutter.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
3.4 KiB

import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:universal_io/io.dart';
export 'package:cookie_jar/cookie_jar.dart';
extension DynamiteHttpClientResponseBody on HttpClientResponse {
Future<Uint8List> get bodyBytes async {
final buffer = BytesBuilder();
await forEach(buffer.add);
return buffer.toBytes();
}
Future<String> get body => transform(utf8.decoder).join();
Future<dynamic> get jsonBody => transform(utf8.decoder).transform(json.decoder).first;
Map<String, String> get responseHeaders {
final responseHeaders = <String, String>{};
headers.forEach((final name, final values) {
responseHeaders[name] = values.last;
});
return responseHeaders;
}
}
class DynamiteResponse<T, U> {
DynamiteResponse(
this.data,
this.headers,
);
final T data;
final U headers;
@override
String toString() => 'DynamiteResponse(data: $data, headers: $headers)';
}
class DynamiteApiException implements Exception {
DynamiteApiException(
this.statusCode,
this.headers,
this.body,
);
final int statusCode;
final Map<String, String> headers;
final String body;
@override
String toString() => 'DynamiteApiException(statusCode: $statusCode, headers: $headers, body: $body)';
}
abstract class DynamiteAuthentication {
String get type;
String get scheme;
Map<String, String> get headers;
}
class DynamiteHttpBasicAuthentication extends DynamiteAuthentication {
DynamiteHttpBasicAuthentication({
required this.username,
required this.password,
});
final String username;
final String password;
@override
String type = 'http';
@override
String scheme = 'basic';
@override
Map<String, String> get headers => {
'Authorization': 'Basic ${base64.encode(utf8.encode('$username:$password'))}',
};
}
class DynamiteHttpBearerAuthentication extends DynamiteAuthentication {
DynamiteHttpBearerAuthentication({
required this.token,
});
final String token;
@override
String type = 'http';
@override
String scheme = 'bearer';
@override
Map<String, String> get headers => {
'Authorization': 'Bearer $token',
};
}
class DynamiteClient {
DynamiteClient(
this.baseURL, {
this.baseHeaders,
final String? userAgent,
final HttpClient? httpClient,
this.cookieJar,
this.authentications = const [],
}) : httpClient = (httpClient ?? HttpClient())..userAgent = userAgent;
final String baseURL;
final Map<String, String>? baseHeaders;
final HttpClient httpClient;
final CookieJar? cookieJar;
final List<DynamiteAuthentication> authentications;
Future<HttpClientResponse> doRequest(
final String method,
final String path,
final Map<String, String> headers,
final Uint8List? body,
) async {
final uri = Uri.parse('$baseURL$path');
final request = await httpClient.openUrl(method, uri);
for (final header in {...?baseHeaders, ...headers}.entries) {
request.headers.add(header.key, header.value);
}
if (body != null) {
request.add(body.toList());
}
if (cookieJar != null) {
request.cookies.addAll(await cookieJar!.loadForRequest(uri));
}
final response = await request.close();
if (cookieJar != null) {
await cookieJar!.saveFromResponse(uri, response.cookies);
}
return response;
}
}