Compare commits

...

2 Commits

Author SHA1 Message Date
f2a6c446eb
chore: remove deprecated http client package in favor of http/dio packages
Some checks failed
continuous-integration/drone/pr Build is failing
2024-03-30 17:48:11 +01:00
fda5a0ae62
chore(deferred): remove unimplemented deferred packages 2024-03-30 17:47:30 +01:00
53 changed files with 34 additions and 2383 deletions

View File

@ -177,13 +177,13 @@ Then implements your data sources:
```dart
class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
final MiddlewareClient _client;
final Client _client;
PhotoApiDataSourceImpl(this._client);
@override
Future<Photo> getPhoto(int id) async {
final response = await _client.get(Uri.parse('/photos/$id'));
final response = await _client.get(Uri.parse('$kDefaultApiUrl/photos/$id'));
final photo =
PhotoModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
return photo;
@ -197,7 +197,7 @@ class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
final delimiter2 =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/photos$delimiter1$startQuery$delimiter2$limitQuery';
final url = '$kDefaultApiUrl/photos$delimiter1$startQuery$delimiter2$limitQuery';
final response = await _client.get(Uri.parse(url));
final photos =
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});
@ -206,9 +206,7 @@ class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
}
```
> 1: Note that here we use `MiddlewareClient` from our http package.
> 2: You can create multiple implementations (one real and one mock for example).
> 1: You can create multiple implementations (one real and one mock for example).
And implement the repositories:

View File

@ -1,4 +1,4 @@
// Copyright (C) 2022 WYATT GROUP
// Copyright (C) 2024 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
@ -14,7 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'middleware_context.dart';
export 'middleware_request.dart';
export 'middleware_response.dart';
export 'unfreezed_request.dart';
const String kDefaultApiUrl = 'https://jsonplaceholder.typicode.com/';

View File

@ -26,7 +26,6 @@ import 'package:architecture_example/domain/data_sources/local/favorite_local_da
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:get_it/get_it.dart';
import 'package:wyatt_http_client/wyatt_http_client.dart';
final getIt = GetIt.I;
@ -50,22 +49,11 @@ abstract class GetItInitializer {
..registerLazySingleton<FavoriteLocalDataSource>(
FavoriteHiveDataSource.new,
)
..registerLazySingleton<MiddlewareClient>(() {
final Pipeline pipeline = Pipeline()
..addMiddleware(
const UriPrefixMiddleware(
protocol: Protocols.https,
authority: 'jsonplaceholder.typicode.com',
),
)
..addMiddleware(const BodyToJsonMiddleware());
return MiddlewareClient(pipeline: pipeline);
})
..registerLazySingleton<PhotoRemoteDataSource>(
() => PhotoApiDataSourceImpl(getIt()),
PhotoApiDataSourceImpl.new,
)
..registerLazySingleton<AlbumRemoteDataSource>(
() => AlbumApiDataSourceImpl(getIt()),
AlbumApiDataSourceImpl.new,
);
}

View File

@ -16,20 +16,25 @@
import 'dart:convert';
import 'package:architecture_example/core/constants/constants.dart';
import 'package:architecture_example/data/models/album_model.dart';
import 'package:architecture_example/data/models/list_album_model.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:wyatt_http_client/wyatt_http_client.dart';
import 'package:http/http.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class AlbumApiDataSourceImpl extends AlbumRemoteDataSource {
AlbumApiDataSourceImpl(this._client);
final MiddlewareClient _client;
AlbumApiDataSourceImpl({
Client? client,
}) : _client = client ?? Client();
final Client _client;
@override
Future<Album> getAlbum(int id) async {
final response = await _client.get(Uri.parse('/albums/$id'));
final response = await _client
.get(Uri.parse('$kDefaultApiUrl/albums/$id'));
final album =
AlbumModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
return album;
@ -43,7 +48,7 @@ class AlbumApiDataSourceImpl extends AlbumRemoteDataSource {
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
final delimiter2 =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/albums$delimiter1$startQuery$delimiter2$limitQuery';
final url = '$kDefaultApiUrl/albums$delimiter1$startQuery$delimiter2$limitQuery';
final response = await _client.get(Uri.parse(url));
final albums =
ListAlbumModel.fromJson({'albums': jsonDecode(response.body)});

View File

@ -16,20 +16,24 @@
import 'dart:convert';
import 'package:architecture_example/core/constants/constants.dart';
import 'package:architecture_example/data/models/list_photo_model.dart';
import 'package:architecture_example/data/models/photo_model.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_http_client/wyatt_http_client.dart';
import 'package:http/http.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
PhotoApiDataSourceImpl(this._client);
final MiddlewareClient _client;
PhotoApiDataSourceImpl({
Client? client,
}) : _client = client ?? Client();
final Client _client;
@override
Future<Photo> getPhoto(int id) async {
final response = await _client.get(Uri.parse('/photos/$id'));
final response = await _client.get(Uri.parse('$kDefaultApiUrl/photos/$id'));
final photo =
PhotoModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
return photo;
@ -43,7 +47,8 @@ class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
final delimiter2 =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/photos$delimiter1$startQuery$delimiter2$limitQuery';
final url =
'$kDefaultApiUrl/photos$delimiter1$startQuery$delimiter2$limitQuery';
final response = await _client.get(Uri.parse(url));
final photos =
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});
@ -60,7 +65,8 @@ class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
final delimiter =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/photos?albumId=$albumId&$startQuery$delimiter$limitQuery';
final url =
'$kDefaultApiUrl/photos?albumId=$albumId&$startQuery$delimiter$limitQuery';
final response = await _client.get(Uri.parse(url));
final photos =
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});

View File

@ -45,12 +45,10 @@ dependencies:
wyatt_bloc_helper:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: ^2.0.2
wyatt_http_client:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: ^2.0.1
wyatt_type_utils:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
version: ^0.0.5
http: ^1.2.1
dev_dependencies:
build_runner: ^2.3.2

View File

@ -1 +0,0 @@
../../.gitignore

View File

@ -1 +0,0 @@
../../.pubignore

View File

@ -1 +0,0 @@
../../AUTHORS

View File

@ -1 +0,0 @@
../../LICENSE

View File

@ -1 +0,0 @@
../../.gitignore

View File

@ -1 +0,0 @@
../../.pubignore

View File

@ -1 +0,0 @@
../../AUTHORS

View File

@ -1 +0,0 @@
../../LICENSE

View File

@ -1 +0,0 @@
../../.gitignore

View File

@ -1 +0,0 @@
../../.pubignore

View File

@ -1,24 +0,0 @@
/*
* Copyright (C) 2022 WYATT GROUP
* Please see the AUTHORS file for details.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
{
"recommendations": [
"psioniq.psi-header",
"blaugold.melos-code"
]
}

View File

@ -1,71 +0,0 @@
{
"psi-header.changes-tracking": {
"isActive": true
},
"psi-header.config": {
"blankLinesAfter": 1,
"forceToTop": true
},
"psi-header.lang-config": [
{
"beforeHeader": [
"# -*- coding:utf-8 -*-",
"#!/usr/bin/env python3"
],
"begin": "###",
"end": "###",
"language": "python",
"prefix": "# "
},
{
"beforeHeader": [
"#!/usr/bin/env sh",
""
],
"language": "shellscript",
"begin": "",
"end": "",
"prefix": "# "
},
{
"begin": "",
"end": "",
"language": "dart",
"prefix": "// "
},
{
"begin": "",
"end": "",
"language": "yaml",
"prefix": "# "
},
{
"begin": "<!--",
"end": "-->",
"language": "markdown",
},
],
"psi-header.templates": [
{
"language": "*",
"template": [
"Copyright (C) <<year>> WYATT GROUP",
"Please see the AUTHORS file for details.",
"",
"This program is free software: you can redistribute it and/or modify",
"it under the terms of the GNU General Public License as published by",
"the Free Software Foundation, either version 3 of the License, or",
"any later version.",
"",
"This program is distributed in the hope that it will be useful,",
"but WITHOUT ANY WARRANTY; without even the implied warranty of",
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the",
"GNU General Public License for more details.",
"",
"You should have received a copy of the GNU General Public License",
"along with this program. If not, see <https://www.gnu.org/licenses/>."
],
}
],
"dart.runPubGetOnPubspecChanges": false,
}

View File

@ -1 +0,0 @@
../../AUTHORS

View File

@ -1,25 +0,0 @@
## 2.0.1
- **FIX**: apply dart fix --apply.
## 2.0.0
> Note: This release has breaking changes.
- **DOCS**: add simple example.
- **BREAKING** **REFACTOR**: fix cascade dart good practices + docs.
## 1.2.0
- **FEAT**: add new middleware feature.
- **FEAT**: implements doublelinked list for middlewares.
- **FEAT**: [WIP] implements middleware system.
- **FEAT**: [WIP] work on middleware feature.
## 1.1.0
- **FEAT**: add oauth2 refresh token client.
## 1.0.0
- Initial version.

View File

@ -1 +0,0 @@
../../LICENSE

View File

@ -1,182 +0,0 @@
<!--
* Copyright (C) 2022 WYATT GROUP
* Please see the AUTHORS file for details.
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
# HTTP Client
<p align="left">
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a>
<img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" />
</p>
HTTP Client for Dart with Middlewares !
## Getting started
Simply add wyatt_http_client in pubspec.yaml, then
```dart
import 'package:wyatt_http_client/wyatt_http_client.dart';
```
## Usage
Firstly you have to understand **Middleware** and **Pipeline** concepts.
In `wyatt_http_client` a middleware is an object where requests and responses
pass through. And a pipeline is basicaly a list of middlewares.
In a pipeline with middlewares A and B, if request pass through A, then B,
the response will pass through B then A.
> You can `print(pipeline)` to get full process order of a pipeline.
For example, if you want to log every request, and simplify an url you can use provided `SimpleLogger` and `UriPrefix` .
```dart
// Create the Pipeline
final Pipeline pipeline = Pipeline()
..addMiddleware(
const UriPrefixMiddleware(
protocol: Protocols.http,
authority: 'localhost:80',
),
)
..addMiddleware(const SimpleLoggerMiddleware());
```
Then if you print the pipeline,
```
[Req] -> UriPrefix -> SimpleLogger
[Res] -> SimpleLogger
```
> The `response` doesn't pass through `UriPrefix` because it's an `OnRequestMiddleware` only.
And you can create a client.
```dart
final client = MiddlewareClient(pipeline: pipeline);
```
At this point you can use `client` like every Client from `package:http/http.dart` .
## Recipes
### Rest API with URL Authentication
Let's build a client for a REST API where the (bad) authentication is through the URL.
We need some middlewares:
* SimpleLogger, to log every request and response (useful for debug).
* BodyToJson, to automaticaly transform Map object to JSON.
* UriPrefix, to simplify the build of an API Object (save protocol and API prefix).
* UnsafeAuth, to use url based authentication.
Let's start by creating the Pipeline:
```dart
final Pipeline pipeline = Pipeline()
..addMiddleware(
const UriPrefixMiddleware(
protocol: Protocols.http,
authority: 'localhost:80',
),
)
..addMiddleware(const BodyToJsonMiddleware())
..addMiddleware(
const UnsafeAuthMiddleware(
username: 'wyatt',
password: 'motdepasse',
),
)
..addMiddleware(SimpleLoggerMiddleware());
```
Then simply create a client and make a call.
```dart
final client = MiddlewareClient(pipeline: pipeline);
await client.get(Uri.parse('/protected'));
```
> Here it make a `GET` call on `http://localhost:80/protected?username=wyatt&password=motdepasse`
And voilà.
### Rest API with Oauth2
So now we want a real authentication.
```dart
final Pipeline pipeline = Pipeline()
..addMiddleware(
const UriPrefixMiddleware(
protocol: Protocols.http,
authority: 'localhost:80',
),
)
..addMiddleware(const BodyToJsonMiddleware())
..addMiddleware(
RefreshTokenAuthMiddleware(
authorizationEndpoint: '/auth/sign-in',
tokenEndpoint: '/auth/refresh',
accessTokenParser: (body) => body['access_token']! as String,
refreshTokenParser: (body) => body['refresh_token']! as String,
unauthorized: HttpStatus.forbidden,
),
)
..addMiddleware(const SimpleLoggerMiddleware());
```
> Here we just change `UnsafeAuthMiddleware` by `RefreshTokenAuthMiddleware` and the whole app while adapt to a new authentication system.
### Create a new Middleware
You can create your own middleware by implementing `Middleware` class, and use mixins to add `OnRequest` and/or `OnResponse` methods.
```dart
class SimpleLoggerMiddleware
with OnRequestMiddleware, OnResponseMiddleware
implements Middleware {
const SimpleLoggerMiddleware();
@override
String getName() => 'SimpleLogger';
@override
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
) async {
print('${getName()}::OnRequest');
return request;
}
@override
Future<MiddlewareResponse> onResponse(
MiddlewareContext context,
MiddlewareResponse response,
) async {
print('${getName()}::OnResponse');
return response;
}
}
```

View File

@ -1 +0,0 @@
include: package:wyatt_analysis/analysis_options.yaml

View File

@ -1,78 +0,0 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_http_client/wyatt_http_client.dart';
Future<void> testSimpleGet() async {
print('testSimpleGet');
final pipeline = Pipeline()
..addMiddleware(const BodyToJsonMiddleware())
..addMiddleware(const SimpleLoggerMiddleware());
final client = MiddlewareClient(pipeline: pipeline);
final response = await client
.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
print(response.body);
}
Future<void> testUriPrefix() async {
print('testUriPrefix');
final pipeline = Pipeline()
..addMiddleware(
const UriPrefixMiddleware(
protocol: Protocols.https,
authority: 'jsonplaceholder.typicode.com',
),
)
..addMiddleware(const BodyToJsonMiddleware())
..addMiddleware(const SimpleLoggerMiddleware());
final client = MiddlewareClient(pipeline: pipeline);
final response = await client.get(Uri.parse('/todos/1'));
print(response.body);
}
Future<void> testBasicAuth() async {
print('testBasicAuth');
final pipeline = Pipeline()
..addMiddleware(
const BasicAuthMiddleware(
username: 'guest',
password: 'guest',
),
)
..addMiddleware(const BodyToJsonMiddleware())
..addMiddleware(const SimpleLoggerMiddleware());
final client = MiddlewareClient(pipeline: pipeline);
final response =
await client.get(Uri.parse('https://jigsaw.w3.org/HTTP/Basic/'));
if (HttpStatus.from(response.statusCode).isSuccess()) {
print("🎉 You're in!");
} else {
print("⭕️ Nope, you're not in.");
}
}
void main(List<String> args) {
testSimpleGet();
testUriPrefix();
testBasicAuth();
}

View File

@ -1,48 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart';
/// {@template middleware}
/// A middleware is a class that can intercept requests and responses
/// and modify them before they are sent to the server or before they
/// are returned to the client.
/// {@endtemplate}
abstract class Middleware {
/// {@macro middleware}
const Middleware();
/// The name of the middleware.
String getName();
}
mixin OnRequestMiddleware {
/// Performs an action before the request is sent to the server.
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
);
}
mixin OnResponseMiddleware {
/// Performs an action before the response is returned to the client.
Future<MiddlewareResponse> onResponse(
MiddlewareContext context,
MiddlewareResponse response,
);
}

View File

@ -1,134 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:http/http.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart';
import 'package:wyatt_http_client/src/models/unfreezed_request.dart';
import 'package:wyatt_http_client/src/pipeline.dart';
import 'package:wyatt_http_client/src/utils/http_methods.dart';
/// {@template middleware_client}
/// A custom [Client] implementation that allows you to intercept requests
/// and responses and modify them before they are sent to the server or
/// before they are returned to the client.
/// {@endtemplate}
class MiddlewareClient extends BaseClient {
/// {@macro middleware_client}
MiddlewareClient({
Pipeline? pipeline,
Client? inner,
}) : pipeline = pipeline ?? Pipeline(),
inner = inner ?? Client();
/// The [Client] that will be used to send requests.
final Client inner;
/// The [Pipeline] that will be used to intercept requests and responses.
final Pipeline pipeline;
@override
Future<Response> head(Uri url, {Map<String, String>? headers}) =>
_sendUnstreamed(HttpMethods.head.method, url, headers);
@override
Future<Response> get(Uri url, {Map<String, String>? headers}) =>
_sendUnstreamed(HttpMethods.get.method, url, headers);
@override
Future<Response> post(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_sendUnstreamed(HttpMethods.post.method, url, headers, body, encoding);
@override
Future<Response> put(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_sendUnstreamed(HttpMethods.put.method, url, headers, body, encoding);
@override
Future<Response> patch(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_sendUnstreamed(HttpMethods.patch.method, url, headers, body, encoding);
@override
Future<Response> delete(
Uri url, {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
_sendUnstreamed(HttpMethods.delete.method, url, headers, body, encoding);
@override
Future<StreamedResponse> send(BaseRequest request) => inner.send(request);
Future<Response> _sendUnstreamed(
String method,
Uri url,
Map<String, String>? headers, [
Object? body,
Encoding? encoding,
]) async {
final originalRequest = MiddlewareRequest(
unfreezedRequest: UnfreezedRequest(
method: method,
url: url,
headers: headers,
body: body,
encoding: encoding,
),
);
final requestContext = MiddlewareContext(
pipeline: pipeline,
client: this,
originalRequest: originalRequest,
);
final modifiedRequest = await pipeline.onRequest(
requestContext,
originalRequest.copyWith(),
);
final originalResponse = MiddlewareResponse(
httpResponse: await Response.fromStream(
await send(modifiedRequest.request),
),
);
final responseContext =
requestContext.copyWith(originalResponse: originalResponse);
final modifiedResponse =
await pipeline.onResponse(responseContext, originalResponse.copyWith());
return modifiedResponse.httpResponse as Response;
}
}

View File

@ -1,65 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:wyatt_http_client/src/middleware.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/utils/authentication_methods.dart';
import 'package:wyatt_http_client/src/utils/header_keys.dart';
/// {@template basic_auth_middleware}
/// A middleware that adds basic authentication to the request.
/// {@endtemplate}
class BasicAuthMiddleware with OnRequestMiddleware implements Middleware {
/// {@macro basic_auth_middleware}
const BasicAuthMiddleware({
this.username,
this.password,
this.authenticationHeader = HeaderKeys.authorization,
});
/// The username to use for authentication.
final String? username;
/// The password to use for authentication.
final String? password;
/// The header to use for authentication.
final String authenticationHeader;
@override
String getName() => 'BasicAuth';
@override
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
) async {
if (username == null || password == null) {
return request;
}
final mutation = {
authenticationHeader: '${AuthenticationMethods.basic} '
'${base64Encode(utf8.encode('$username:$password'))}',
};
final Map<String, String> headers = request.headers..addAll(mutation);
request.modifyRequest(request.unfreezedRequest.copyWith(headers: headers));
return request;
}
}

View File

@ -1,50 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:wyatt_http_client/src/middleware.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
/// {@template body_to_json_middleware}
/// A middleware that transforms the body in json if it's a [Map].
/// {@endtemplate}
class BodyToJsonMiddleware with OnRequestMiddleware implements Middleware {
/// {@macro body_to_json_middleware}
const BodyToJsonMiddleware();
@override
String getName() => 'BodyToJson';
@override
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
) async {
final mutation = {
'content-type': 'application/json; charset=utf-8',
};
if (request.body is Map) {
final Map<String, String> headers = request.headers..addAll(mutation);
request.modifyRequest(
request.unfreezedRequest
.copyWith(headers: headers, body: jsonEncode(request.body)),
);
}
return request;
}
}

View File

@ -1,28 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_http_client/src/middleware.dart';
/// {@template default_middleware}
/// A default middleware that does nothing.
/// {@endtemplate}
class DefaultMiddleware implements Middleware {
/// {@macro default_middleware}
const DefaultMiddleware();
@override
String getName() => 'DefaultMiddleware';
}

View File

@ -1,87 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_http_client/src/middleware.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart';
import 'package:wyatt_http_client/src/utils/digest_auth.dart';
import 'package:wyatt_http_client/src/utils/header_keys.dart';
import 'package:wyatt_http_client/src/utils/http_status.dart';
/// {@template digest_auth_middleware}
/// A middleware that handles digest authentication.
/// {@endtemplate}
class DigestAuthMiddleware
with OnRequestMiddleware, OnResponseMiddleware
implements Middleware {
/// {@macro digest_auth_middleware}
DigestAuthMiddleware({
required this.username,
required this.password,
this.authenticationHeader = HeaderKeys.authorization,
this.wwwAuthenticateHeader = HeaderKeys.wwwAuthenticate,
this.unauthorized = HttpStatus.unauthorized,
}) : _digestAuth = DigestAuth(username, password);
final String username;
final String password;
final DigestAuth _digestAuth;
final String authenticationHeader;
final String wwwAuthenticateHeader;
final HttpStatus unauthorized;
@override
String getName() => 'DigestAuth';
@override
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
) async {
if (_digestAuth.isReady()) {
final mutation = {
authenticationHeader: _digestAuth.getAuthString(
request.method,
request.url,
),
};
final Map<String, String> headers = request.headers..addAll(mutation);
request
.modifyRequest(request.unfreezedRequest.copyWith(headers: headers));
}
return request;
}
@override
Future<MiddlewareResponse> onResponse(
MiddlewareContext context,
MiddlewareResponse response,
) async {
if (response.status == unauthorized) {
final authInfo =
response.headers[HeaderKeys.wwwAuthenticate.toLowerCase()];
_digestAuth.initFromAuthenticateHeader(authInfo);
final MiddlewareRequest? newRequest = context.lastRequest?.copyWith();
if (newRequest != null) {
final newResponse = await context.client.send(newRequest.request);
return MiddlewareResponse(httpResponse: newResponse);
}
}
return response;
}
}

View File

@ -1,26 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// All built-in middlewares
export 'basic_auth_middleware.dart';
export 'body_to_json_middleware.dart';
export 'default_middleware.dart';
export 'digest_auth_middleware.dart';
export 'refresh_token_auth_middleware.dart';
export 'simple_logger_middleware.dart';
export 'unsafe_auth_middleware.dart';
export 'uri_prefix_middleware.dart';

View File

@ -1,184 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:wyatt_http_client/src/middleware.dart';
import 'package:wyatt_http_client/src/middleware_client.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart';
import 'package:wyatt_http_client/src/utils/authentication_methods.dart';
import 'package:wyatt_http_client/src/utils/delay.dart';
import 'package:wyatt_http_client/src/utils/header_keys.dart';
import 'package:wyatt_http_client/src/utils/http_status.dart';
typedef TokenParser = String Function(Map<String, dynamic>);
/// {@template refresh_token_auth_middleware}
/// A middleware that refreshes the access token when it expires.
/// This middleware is useful for OAuth2.
/// {@endtemplate}
class RefreshTokenAuthMiddleware
with OnRequestMiddleware, OnResponseMiddleware
implements Middleware {
/// {@macro refresh_token_auth_middleware}
RefreshTokenAuthMiddleware({
required this.authorizationEndpoint,
required this.tokenEndpoint,
required this.accessTokenParser,
required this.refreshTokenParser,
this.authenticationHeader = HeaderKeys.authorization,
this.authenticationMethod = AuthenticationMethods.bearer,
this.unauthorized = HttpStatus.unauthorized,
this.maxAttempts = 8,
});
final String authorizationEndpoint;
final String tokenEndpoint;
String? accessToken;
final TokenParser accessTokenParser;
String? refreshToken;
final TokenParser refreshTokenParser;
final String authenticationHeader;
final String authenticationMethod;
final HttpStatus unauthorized;
final int maxAttempts;
@override
String getName() => 'RefreshToken';
Future<MiddlewareRequest?> refresh(MiddlewareContext context) async {
final subPipeline = context.pipeline.sub(this);
final httpClient = MiddlewareClient(
pipeline: subPipeline,
inner: context.client.inner,
);
final headers = {
authenticationHeader: '$authenticationMethod $refreshToken',
};
final response = MiddlewareResponse(
httpResponse: await httpClient.get(
Uri.parse(tokenEndpoint),
headers: headers,
),
);
if (response.status.isSuccess()) {
final body = jsonDecode(response.body) as Map<String, dynamic>;
accessToken = accessTokenParser(body);
// Then modify current request with accessToken
final mutation = {
authenticationHeader: '$authenticationMethod $accessToken',
};
final Map<String, String>? headers = context.lastRequest?.headers
?..addAll(mutation);
final newRequest = context.lastRequest?.copyWith(
unfreezedRequest:
context.lastRequest?.unfreezedRequest.copyWith(headers: headers),
);
return newRequest;
}
return null;
}
Future<MiddlewareRequest?> retry(MiddlewareContext context) async {
// Retry
int attempt = 1;
while (attempt <= maxAttempts) {
// Delayed before retry
await Future<void>.delayed(Delay.getRetryDelay(attempt));
final newRequest = await refresh(context);
if (newRequest != null) {
return newRequest;
}
attempt++;
}
return null;
}
@override
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
) async {
// Check if it is authorization
if (context.originalRequest?.url == Uri.parse(authorizationEndpoint)) {
return request;
}
// Check if it is refresh
if (context.originalRequest?.url == Uri.parse(tokenEndpoint)) {
return request;
}
// If AccessToken not null then return request with authorization header
if (accessToken != null) {
final mutation = {
authenticationHeader: '$authenticationMethod $accessToken',
};
final Map<String, String> headers = request.headers..addAll(mutation);
request
.modifyRequest(request.unfreezedRequest.copyWith(headers: headers));
return request;
}
// If AccessToken is null BUT there is a refreshToken, then try refreshing
if (refreshToken != null) {
MiddlewareRequest? newRequest = await refresh(context);
newRequest ??= await retry(context);
return newRequest ?? request;
}
// Pass
return request;
}
@override
Future<MiddlewareResponse> onResponse(
MiddlewareContext context,
MiddlewareResponse response,
) async {
// Check if it is authorization
if (context.originalRequest?.url == Uri.parse(authorizationEndpoint)) {
// If success, then update tokens
if (response.status.isSuccess()) {
final body = jsonDecode(response.body) as Map<String, dynamic>;
final accessToken = accessTokenParser(body);
final refreshToken = refreshTokenParser(body);
if (accessToken.isNotEmpty) {
this.accessToken = accessToken;
}
if (refreshToken.isNotEmpty) {
this.refreshToken = refreshToken;
}
}
}
if (response.status == unauthorized) {
// Refresh
MiddlewareRequest? newRequest = await refresh(context);
newRequest ??= await retry(context);
if (newRequest != null) {
return response.copyWith(
httpResponse: await context.client.send(newRequest.request),
);
}
}
return response;
}
}

View File

@ -1,72 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_http_client/src/middleware.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart';
/// {@template simple_logger_middleware}
/// A simple logger middleware that logs the request and response.
/// {@endtemplate}
class SimpleLoggerMiddleware
with OnRequestMiddleware, OnResponseMiddleware
implements Middleware {
/// {@macro simple_logger_middleware}
const SimpleLoggerMiddleware();
@override
String getName() => 'SimpleLogger';
@override
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
) async {
final log = StringBuffer()
..writeln('${getName()}::OnRequest')
..writeln('>> ${request.method} ${request.url}');
if (request.headers.isNotEmpty) {
log.writeln('>> Headers:');
request.headers.forEach((key, value) {
log.writeln('>> $key: $value');
});
}
if (request.encodedBody.isNotEmpty) {
log
..writeln('>> Body:')
..writeln(request.encodedBody);
}
print(log);
return request;
}
@override
Future<MiddlewareResponse> onResponse(
MiddlewareContext context,
MiddlewareResponse response,
) async {
final log = StringBuffer()
..writeln('${getName()}::OnResponse')
..writeln('>> Status: ${response.status.name.toUpperCase()}')
..writeln('>> Length: ${response.contentLength ?? '0'} bytes');
print(log);
return response;
}
}

View File

@ -1,56 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_http_client/src/middleware.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/utils/convert.dart';
/// {@template unsafe_auth_middleware}
/// A middleware that appends the username and password to the URL.
///
/// This is not recommended to use in production.
/// {@endtemplate}
class UnsafeAuthMiddleware with OnRequestMiddleware implements Middleware {
const UnsafeAuthMiddleware({
this.username,
this.password,
this.usernameField = 'username',
this.passwordField = 'password',
});
final String? username;
final String? password;
final String usernameField;
final String passwordField;
@override
String getName() => 'UnsafeAuth';
@override
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
) async {
if (username == null || password == null) {
return request;
}
final Uri uri =
request.url + '?$usernameField=$username&$passwordField=$password';
request.modifyRequest(request.unfreezedRequest.copyWith(url: uri));
return request;
}
}

View File

@ -1,50 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_http_client/src/middleware.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/utils/protocols.dart';
/// {@template uri_prefix_middleware}
/// A middleware that adds a prefix to the request's URI.
/// {@endtemplate}
class UriPrefixMiddleware with OnRequestMiddleware implements Middleware {
/// {@macro uri_prefix_middleware}
const UriPrefixMiddleware({
required this.protocol,
required this.authority,
});
/// The protocol of the prefix.
final Protocols protocol;
/// The authority of the prefix.
final String? authority;
@override
String getName() => 'UriPrefix';
@override
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
) async {
final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}');
request.modifyRequest(request.unfreezedRequest.copyWith(url: uri));
return request;
}
}

View File

@ -1,78 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_http_client/src/middleware_client.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart';
import 'package:wyatt_http_client/src/pipeline.dart';
/// {@template middleware_context}
/// A class that contains the context of the middleware.
/// {@endtemplate}
class MiddlewareContext {
/// {@macro middleware_context}
const MiddlewareContext({
required this.pipeline,
required this.client,
this.originalRequest,
this.lastRequest,
this.originalResponse,
this.lastResponse,
});
/// The pipeline that the middleware is in.
final Pipeline pipeline;
/// The client that the middleware is in.
final MiddlewareClient client;
/// The original request that the middleware is in.
final MiddlewareRequest? originalRequest;
/// The last request that the middleware is in.
final MiddlewareRequest? lastRequest;
/// The original response that the middleware is in.
final MiddlewareResponse? originalResponse;
/// The last response that the middleware is in.
final MiddlewareResponse? lastResponse;
/// Create a copy of this [MiddlewareContext] with the given fields replaced
/// with the new values.
MiddlewareContext copyWith({
Pipeline? pipeline,
MiddlewareClient? client,
MiddlewareRequest? originalRequest,
MiddlewareRequest? lastRequest,
MiddlewareResponse? originalResponse,
MiddlewareResponse? lastResponse,
}) =>
MiddlewareContext(
pipeline: pipeline ?? this.pipeline,
client: client ?? this.client,
originalRequest: originalRequest ?? this.originalRequest,
lastRequest: lastRequest ?? this.lastRequest,
originalResponse: originalResponse ?? this.originalResponse,
lastResponse: lastResponse ?? this.lastResponse,
);
@override
String toString() =>
'MiddlewareContext(pipeline: $pipeline, client: $client, '
'originalRequest: $originalRequest, lastRequest: $lastRequest, '
'originalResponse: $originalResponse, lastResponse: $lastResponse)';
}

View File

@ -1,102 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:http/http.dart';
import 'package:wyatt_http_client/src/models/unfreezed_request.dart';
import 'package:wyatt_http_client/src/utils/convert.dart';
import 'package:wyatt_http_client/src/utils/request_utils.dart';
/// {@template middleware_request}
/// A class that represents a middleware request.
/// {@endtemplate}
class MiddlewareRequest {
/// {@macro middleware_request}
MiddlewareRequest({
required this.unfreezedRequest,
}) : _httpRequest = Request(unfreezedRequest.method, unfreezedRequest.url);
/// The unfreezed request.
UnfreezedRequest unfreezedRequest;
Request _httpRequest;
/// The http request. (Read-only)
Request get request => _httpRequest;
/// The request method (proxy, read-only).
String get method => _httpRequest.method;
/// The request url (proxy, read-only).
Uri get url => _httpRequest.url;
/// The request headers (proxy, read-only).
Map<String, String> get headers => _httpRequest.headers;
/// The request body (proxy, read-only).
Encoding get encoding => _httpRequest.encoding;
/// The request body (proxy, read-only).
String get encodedBody => _httpRequest.body;
/// The request body (proxy, read-only).
Object? get body => unfreezedRequest.body;
/// Copies this request and returns a new request with the given
/// [unfreezedRequest].
MiddlewareRequest copyWith({
UnfreezedRequest? unfreezedRequest,
}) =>
MiddlewareRequest(
unfreezedRequest: unfreezedRequest ?? this.unfreezedRequest,
);
/// Modifies the request with the given [unfreezedRequest].
void modifyRequest(UnfreezedRequest unfreezedRequest) {
String? body;
if (unfreezedRequest.body != null) {
var body = unfreezedRequest.body;
if (body is String) {
body = body;
} else if (body is List) {
body = String.fromCharCodes(body.cast<int>());
} else if (body is Map) {
body = Convert.mapToQuery(body.cast<String, String>());
}
}
_httpRequest = RequestUtils.copyRequestWith(
_httpRequest,
method: unfreezedRequest.method,
url: unfreezedRequest.url,
headers: unfreezedRequest.headers,
body: body,
) as Request;
if (unfreezedRequest.encoding != null) {
_httpRequest.encoding = unfreezedRequest.encoding!;
}
this.unfreezedRequest = unfreezedRequest;
}
/// Applies the changes made to the request by modifying it with the
/// [unfreezedRequest].
void apply() {
modifyRequest(unfreezedRequest);
}
@override
String toString() => 'MiddlewareRequest(unfreezedRequest: $unfreezedRequest)';
}

View File

@ -1,63 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:http/http.dart';
import 'package:wyatt_http_client/src/utils/http_status.dart';
/// {@template middleware_response}
/// A class that represents a middleware response.
/// {@endtemplate}
class MiddlewareResponse {
/// {@macro middleware_response}
const MiddlewareResponse({
required this.httpResponse,
});
/// {@macro middleware_response}
final BaseResponse httpResponse;
/// The status code of the response. (proxy)
int get statusCode => httpResponse.statusCode;
/// The status of the response. (proxy)
HttpStatus get status => HttpStatus.from(statusCode);
/// The body of the response. (proxy or empty string)
String get body {
if (httpResponse is Response) {
return (httpResponse as Response).body;
} else {
return '';
}
}
/// The content length of the response. (proxy)
int? get contentLength => httpResponse.contentLength;
/// The headers of the response. (proxy)
Map<String, String> get headers => httpResponse.headers;
/// Returns a copy of this response with the given [httpResponse].
MiddlewareResponse copyWith({
BaseResponse? httpResponse,
}) =>
MiddlewareResponse(
httpResponse: httpResponse ?? this.httpResponse,
);
@override
String toString() => 'MiddlewareResponse(httpResponse: $httpResponse)';
}

View File

@ -1,69 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
/// {@template unfreezed_request}
/// A class that represents an unfreezed request.
/// It is used to unfreeze a Request object, and allows you to
/// modify the request before sending it.
/// {@endtemplate}
class UnfreezedRequest {
/// {@macro unfreezed_request}
const UnfreezedRequest({
required this.method,
required this.url,
this.headers,
this.body,
this.encoding,
});
/// The request method.
final String method;
/// The request url.
final Uri url;
/// The request headers.
final Map<String, String>? headers;
/// The request body.
final Object? body;
/// The request encoding.
final Encoding? encoding;
/// Copies this request and returns a new request with the given [method],
/// [url], [headers], [body] and [encoding].
UnfreezedRequest copyWith({
String? method,
Uri? url,
Map<String, String>? headers,
Object? body,
Encoding? encoding,
}) =>
UnfreezedRequest(
method: method ?? this.method,
url: url ?? this.url,
headers: headers ?? this.headers,
body: body ?? this.body,
encoding: encoding ?? this.encoding,
);
@override
String toString() => 'UnfreezedRequest(method: $method, url: $url, headers: '
'$headers, body: $body, encoding: $encoding)';
}

View File

@ -1,122 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_http_client/src/middleware.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart';
/// {@template pipeline}
/// A [Pipeline] is a list of [Middleware]s that are executed in order.
/// {@endtemplate}
class Pipeline {
/// {@macro pipeline}
Pipeline() : _middlewares = <Middleware>[];
/// {@macro pipeline}
Pipeline.fromIterable(Iterable<Middleware> middlewares)
: _middlewares = middlewares.toList();
final List<Middleware> _middlewares;
/// The length of the [Pipeline].
///
/// This is the number of [Middleware]s in the [Pipeline].
int get length => _middlewares.length;
/// Add a [Middleware] to this [Pipeline]
void addMiddleware(Middleware middleware) {
_middlewares.add(middleware);
}
/// Create new [Pipeline] from the start or end to a specified [Middleware].
Pipeline sub(
Middleware middleware, {
bool include = false,
bool fromEnd = false,
}) {
final nodes = <Middleware>[];
final list = fromEnd ? _middlewares.reversed : _middlewares;
for (final m in list) {
if (m != middleware) {
nodes.add(m);
}
if (m == middleware) {
if (include) {
nodes.add(m);
}
break;
}
}
return Pipeline.fromIterable(fromEnd ? nodes.reversed : nodes);
}
/// Call the [onRequest] method of all [OnRequestMiddleware]s in the
/// [Pipeline].
///
/// The [MiddlewareRequest] returned by the last [OnRequestMiddleware] is
/// returned.
Future<MiddlewareRequest> onRequest(
MiddlewareContext context,
MiddlewareRequest request,
) async {
MiddlewareRequest req = request..apply();
MiddlewareContext ctx = context.copyWith(lastRequest: req);
for (final middleware in _middlewares) {
if (middleware is OnRequestMiddleware) {
req = await (middleware as OnRequestMiddleware).onRequest(ctx, request);
ctx = context.copyWith(lastRequest: req);
}
}
return req;
}
/// Call the [onResponse] method of all [OnResponseMiddleware]s in the
/// [Pipeline].
///
/// The [MiddlewareResponse] returned by the last [OnResponseMiddleware] is
/// returned.
Future<MiddlewareResponse> onResponse(
MiddlewareContext context,
MiddlewareResponse response,
) async {
MiddlewareResponse res = response;
MiddlewareContext ctx = context.copyWith(lastResponse: res);
for (final middleware in _middlewares.reversed) {
if (middleware is OnResponseMiddleware) {
res = await (middleware as OnResponseMiddleware)
.onResponse(ctx, response);
ctx = context.copyWith(lastResponse: res);
}
}
return res;
}
@override
String toString() {
final req = <String>[];
final res = <String>[];
for (final middleware in _middlewares) {
if (middleware is OnRequestMiddleware) {
req.add(middleware.getName());
}
if (middleware is OnResponseMiddleware) {
res.insert(0, middleware.getName());
}
}
return '[Req] -> ${req.join(' -> ')}\n[Res] -> ${res.join(' -> ')}';
}
}

View File

@ -1,27 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Defines some authentication methods
abstract class AuthenticationMethods {
/// The `Basic` authentication method.
static const String basic = 'Basic';
/// The `Bearer` authentication method.
static const String bearer = 'Bearer';
/// The `Digest` authentication method.
static const String digest = 'Digest';
}

View File

@ -1,62 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
/// Defines some convert functions.
abstract class Convert {
/// Converts a list of bytes to a hex string.
///
/// If [upperCase] is `true`, the hex string will be in uppercase.
static String toHex(List<int> bytes, {bool upperCase = false}) {
final buffer = StringBuffer();
for (final int part in bytes) {
if (part & 0xff != part) {
throw const FormatException('Non-byte integer detected');
}
buffer.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}');
}
if (upperCase) {
return buffer.toString().toUpperCase();
} else {
return buffer.toString();
}
}
/// Converts a map to a query string.
///
/// If [encoding] is `null`, the default encoding is `utf8`.
///
/// For example, the map `{a: 1, b: 2}` will be converted to `a=1&b=2`.
static String mapToQuery(Map<String, String> map, {Encoding? encoding}) {
final pairs = <List<String>>[];
map.forEach(
(key, value) => pairs.add([
Uri.encodeQueryComponent(key, encoding: encoding ?? utf8),
Uri.encodeQueryComponent(value, encoding: encoding ?? utf8),
]),
);
return pairs.map((pair) => '${pair[0]}=${pair[1]}').join('&');
}
}
extension UriX on Uri {
/// Returns a new [Uri] by appending the given [path] to this [Uri].
Uri operator +(String path) {
final thisPath = toString();
return Uri.parse(thisPath + path);
}
}

View File

@ -1,30 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:crypto/crypto.dart';
/// Defines some crypto functions.
abstract class Crypto {
/// Hash a string using MD5
static String md5Hash(String data) {
final content = const Utf8Encoder().convert(data);
const md5Crypto = md5;
final digest = md5Crypto.convert(content).toString();
return digest;
}
}

View File

@ -1,38 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:core';
import 'dart:math';
/// Defines some delay functions.
abstract class Delay {
/// Returns a delay based on the [attempt].
static Duration getRetryDelay(int attempt) {
assert(attempt >= 0, 'attempt cannot be negative');
if (attempt <= 0) {
return Duration.zero;
}
final rand = Random();
const Duration delayFactor = Duration(milliseconds: 200);
const double randomizationFactor = 0.25;
const Duration maxDelay = Duration(seconds: 30);
final rf = randomizationFactor * (rand.nextDouble() * 2 - 1) + 1;
final exp = min(attempt, 31); // prevent overflows.
final delay = delayFactor * pow(2.0, exp) * rf;
return delay < maxDelay ? delay : maxDelay;
}
}

View File

@ -1,196 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:math';
import 'package:wyatt_http_client/src/utils/convert.dart';
import 'package:wyatt_http_client/src/utils/crypto.dart';
/// A class for digest authentication.
class DigestAuth {
// request counter
DigestAuth(this.username, this.password);
final String username;
final String password;
// must get from first response
String? _algorithm;
String? _qop;
String? _realm;
String? _nonce;
String? _opaque;
int _nc = 0;
/// Splits WWW-Authenticate header into a map.
Map<String, String>? splitWWWAuthenticateHeader(String header) {
if (!header.startsWith('Digest ')) {
throw ArgumentError.value(
header,
'header',
'Header must start with "Digest "',
);
}
final h = header.substring(7); // remove 'Digest '
final ret = <String, String>{};
final components = h.split(',').map((token) => token.trim());
for (final component in components) {
final kv = component.split('=');
ret[kv[0]] = kv.getRange(1, kv.length).join('=').replaceAll('"', '');
}
return ret;
}
String _computeNonce() {
final rnd = Random.secure();
final values = List<int>.generate(16, (i) => rnd.nextInt(256));
return Convert.toHex(values);
}
String _formatNonceCount(int nc) => nc.toRadixString(16).padLeft(8, '0');
String _computeHA1(
String realm,
String? algorithm,
String username,
String password,
String? nonce,
String? cnonce,
) {
if (algorithm == null || algorithm == 'MD5') {
final token1 = '$username:$realm:$password';
return Crypto.md5Hash(token1);
} else if (algorithm == 'MD5-sess') {
final token1 = '$username:$realm:$password';
final md51 = Crypto.md5Hash(token1);
final token2 = '$md51:$nonce:$cnonce';
return Crypto.md5Hash(token2);
} else {
throw ArgumentError.value(
algorithm,
'algorithm',
'Unsupported algorithm',
);
}
}
Map<String, String?> _computeResponse(
String method,
String path,
String body,
String? algorithm,
String? qop,
String? opaque,
String realm,
String? cnonce,
String? nonce,
int nc,
String username,
String password,
) {
final ret = <String, String?>{};
final ha1 =
_computeHA1(realm, algorithm, username, password, nonce, cnonce);
String ha2;
if (qop == 'auth-int') {
final bodyHash = Crypto.md5Hash(body);
final token2 = '$method:$path:$bodyHash';
ha2 = Crypto.md5Hash(token2);
} else {
// qop in [null, auth]
final token2 = '$method:$path';
ha2 = Crypto.md5Hash(token2);
}
final nonceCount = _formatNonceCount(nc);
ret['username'] = username;
ret['realm'] = realm;
ret['nonce'] = nonce;
ret['uri'] = path;
if (qop != null) {
ret['qop'] = qop;
}
ret['nc'] = nonceCount;
ret['cnonce'] = cnonce;
if (opaque != null) {
ret['opaque'] = opaque;
}
ret['algorithm'] = algorithm;
if (qop == null) {
final token3 = '$ha1:$nonce:$ha2';
ret['response'] = Crypto.md5Hash(token3);
} else if (qop == 'auth' || qop == 'auth-int') {
final token3 = '$ha1:$nonce:$nonceCount:$cnonce:$qop:$ha2';
ret['response'] = Crypto.md5Hash(token3);
}
return ret;
}
String getAuthString(String method, Uri url) {
final cnonce = _computeNonce();
_nc += 1;
// if url has query parameters, append query to path
final path = url.hasQuery ? '${url.path}?${url.query}' : url.path;
// after the first request we have the nonce, so we can provide credentials
final authValues = _computeResponse(
method,
path,
'',
_algorithm,
_qop,
_opaque,
_realm!,
cnonce,
_nonce,
_nc,
username,
password,
);
final authValuesString = authValues.entries
.where((e) => e.value != null)
.map((e) => [e.key, '="', e.value, '"'].join())
.toList()
.join(', ');
final authString = 'Digest $authValuesString';
return authString;
}
void initFromAuthenticateHeader(String? authInfo) {
if (authInfo == null) {
throw ArgumentError.notNull('authInfo');
}
final values = splitWWWAuthenticateHeader(authInfo);
if (values != null) {
_algorithm = values['algorithm'] ?? _algorithm;
_qop = values['qop'] ?? _qop;
_realm = values['realm'] ?? _realm;
_nonce = values['nonce'] ?? _nonce;
_opaque = values['opaque'] ?? _opaque;
_nc = 0;
}
}
bool isReady() => _nonce != null && (_nc == 0 || _qop != null);
}

View File

@ -1,27 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Defines some header keys.
abstract class HeaderKeys {
/// The `Authorization` header key.
static const String authorization = 'Authorization';
/// The `WWW-Authenticate` header key.
static const String wwwAuthenticate = 'WWW-Authenticate';
/// The `Content-Type` header key.
static const String contentType = 'Content-Type';
}

View File

@ -1,32 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Defines http verb methods.
enum HttpMethods {
head('HEAD'),
get('GET'),
post('POST'),
put('PUT'),
patch('PATCH'),
delete('DELETE');
const HttpMethods(this.method);
/// Returns the method of the http verb.
///
/// For example, the method of [HttpMethods.get] is `GET`.
final String method;
}

View File

@ -1,116 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Status codes for HTTP responses. Extracted from dart:io
enum HttpStatus {
continue_(100),
switchingProtocols(101),
processing(102),
ok(200),
created(201),
accepted(202),
nonAuthoritativeInformation(203),
noContent(204),
resetContent(205),
partialContent(206),
multiStatus(207),
alreadyReported(208),
imUsed(226),
multipleChoices(300),
movedPermanently(301),
found(302),
movedTemporarily(302), // Common alias for found.
seeOther(303),
notModified(304),
useProxy(305),
temporaryRedirect(307),
permanentRedirect(308),
badRequest(400),
unauthorized(401),
paymentRequired(402),
forbidden(403),
notFound(404),
methodNotAllowed(405),
notAcceptable(406),
proxyAuthenticationRequired(407),
requestTimeout(408),
conflict(409),
gone(410),
lengthRequired(411),
preconditionFailed(412),
requestEntityTooLarge(413),
requestUriTooLong(414),
unsupportedMediaType(415),
requestedRangeNotSatisfiable(416),
expectationFailed(417),
misdirectedRequest(421),
unprocessableEntity(422),
locked(423),
failedDependency(424),
upgradeRequired(426),
preconditionRequired(428),
tooManyRequests(429),
requestHeaderFieldsTooLarge(431),
connectionClosedWithoutResponse(444),
unavailableForLegalReasons(451),
clientClosedRequest(499),
internalServerError(500),
notImplemented(501),
badGateway(502),
serviceUnavailable(503),
gatewayTimeout(504),
httpVersionNotSupported(505),
variantAlsoNegotiates(506),
insufficientStorage(507),
loopDetected(508),
notExtended(510),
networkAuthenticationRequired(511),
// Client generated status code.
networkConnectTimeoutError(599);
const HttpStatus(this.statusCode);
/// Returns the [HttpStatus] with the given [statusCode].
factory HttpStatus.from(int status) =>
HttpStatus.values.firstWhere((element) => element.statusCode == status);
final int statusCode;
bool equals(Object other) {
if (other is HttpStatus) {
return statusCode == other.statusCode;
}
if (other is int) {
return statusCode == other;
}
return false;
}
/// Checks if the status code is in the range of 100-199.
bool isInfo() => statusCode >= 100 && statusCode < 200;
/// Checks if the status code is in the range of 200-299.
bool isSuccess() => statusCode >= 200 && statusCode < 300;
/// Checks if the status code is in the range of 300-399.
bool isRedirection() => statusCode >= 300 && statusCode < 400;
/// Checks if the status code is in the range of 400-499.
bool isClientError() => statusCode >= 400 && statusCode < 500;
/// Checks if the status code is in the range of 500-599.
bool isServerError() => statusCode >= 500 && statusCode < 600;
}

View File

@ -1,26 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Defines few protocols
enum Protocols {
http,
https;
/// Returns the scheme of the protocol.
///
/// For example, the scheme of [Protocols.http] is `http://`.
String get scheme => '$name://';
}

View File

@ -1,95 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:http/http.dart';
/// Defines some request utils.
abstract class RequestUtils {
static Request _copyNormalRequestWith(
Request original, {
String? method,
Uri? url,
Map<String, String>? headers,
int? maxRedirects,
bool? followRedirects,
bool? persistentConnection,
String? body,
}) {
final request = Request(method ?? original.method, url ?? original.url)
..followRedirects = followRedirects ?? original.followRedirects
..headers.addAll(headers ?? original.headers)
..maxRedirects = maxRedirects ?? original.maxRedirects
..persistentConnection =
persistentConnection ?? original.persistentConnection
..body = body ?? original.body;
return request;
}
/// Copies the given [original] request and returns a new request with the
/// given [method], [url], [headers], [maxRedirects], [followRedirects],
/// [persistentConnection] and [body].
static BaseRequest copyRequestWith(
BaseRequest original, {
String? method,
Uri? url,
Map<String, String>? headers,
int? maxRedirects,
bool? followRedirects,
bool? persistentConnection,
String? body,
}) {
if (original is Request) {
return _copyNormalRequestWith(
original,
method: method,
url: url,
headers: headers,
maxRedirects: maxRedirects,
followRedirects: followRedirects,
persistentConnection: persistentConnection,
body: body,
);
} else {
throw UnimplementedError(
'Cannot handle requests of type ${original.runtimeType}',
);
}
}
static Request _copyNormalRequest(Request original) {
final request = Request(original.method, original.url)
..followRedirects = original.followRedirects
..headers.addAll(original.headers)
..maxRedirects = original.maxRedirects
..persistentConnection = original.persistentConnection
..body = original.body;
return request;
}
/// Copies the given [original] request and returns a new request.
/// This method is useful when you want to modify the request
static BaseRequest copyRequest(BaseRequest original) {
if (original is Request) {
return _copyNormalRequest(original);
} else {
throw UnimplementedError(
'Cannot handle requests of type ${original.runtimeType}',
);
}
}
}

View File

@ -1,23 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'authentication_methods.dart';
export 'digest_auth.dart';
export 'header_keys.dart';
export 'http_methods.dart';
export 'http_status.dart';
export 'protocols.dart';
export 'request_utils.dart';

View File

@ -1,24 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
library wyatt_http_client;
export 'src/middleware.dart';
export 'src/middleware_client.dart';
export 'src/middlewares/middlewares.dart';
export 'src/models/models.dart';
export 'src/pipeline.dart';
export 'src/utils/utils.dart';

View File

@ -1,18 +0,0 @@
name: wyatt_http_client
description: A Dart client for RESTful APIs with authentication.
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_http_client
version: 2.0.1
publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
environment:
sdk: '>=2.17.0 <3.0.0'
dependencies:
crypto: ^3.0.2
http: ^1.1.0
dev_dependencies:
wyatt_analysis:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
version: ^2.5.0