Feature/middlewares #9
| @ -1,39 +1,182 @@ | |||||||
| <!--  | <!-- | ||||||
| This README describes the package. If you publish this package to pub.dev, |  * Copyright (C) 2022 WYATT GROUP | ||||||
| this README's contents appear on the landing page for your package. |  * Please see the AUTHORS file for details. | ||||||
| 
 | 
 | ||||||
| For information about how to write a good package README, see the guide for |  * This program is free software: you can redistribute it and/or modify | ||||||
| [writing package pages](https://dart.dev/guides/libraries/writing-package-pages).  |  * 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. | ||||||
| 
 | 
 | ||||||
| For general information about developing packages, see the Dart guide for |  * This program is distributed in the hope that it will be useful,  | ||||||
| [creating packages](https://dart.dev/guides/libraries/create-library-packages) |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| and the Flutter guide for |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| [developing packages and plugins](https://flutter.dev/developing-packages).  |  * 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/>. | ||||||
| --> | --> | ||||||
| 
 | 
 | ||||||
| TODO: Put a short description of the package here that helps potential users | # Dart - HTTP Client | ||||||
| know whether this package might be useful for them. |  | ||||||
| 
 | 
 | ||||||
| ## Features | <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> | ||||||
| 
 | 
 | ||||||
| TODO: List what your package can do. Maybe include images, gifs, or videos. | HTTP Client for Dart with Middlewares ! | ||||||
| 
 | 
 | ||||||
| ## Getting started | ## Getting started | ||||||
| 
 | 
 | ||||||
| TODO: List prerequisites and provide or point to information on how to | Simply add wyatt_http_client in pubspec.yaml, then | ||||||
| start using the package. | 
 | ||||||
|  | ```dart | ||||||
|  | import 'package:wyatt_http_client/wyatt_http_client.dart'; | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| ## Usage | ## Usage | ||||||
| 
 | 
 | ||||||
| TODO: Include short and useful examples for package users. Add longer examples | Firstly you have to understand **Middleware** and **Pipeline** concepts. | ||||||
| to `/example` folder.  | 
 | ||||||
|  | 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 | ```dart | ||||||
| const like = 'sample'; | // Create the Pipeline | ||||||
|  | final Pipeline pipeline = Pipeline() | ||||||
|  |     .addMiddleware( | ||||||
|  |         UriPrefixMiddleware( | ||||||
|  |             protocol: Protocols.http, | ||||||
|  |             authority: 'localhost:80', | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     .addMiddleware(SimpleLoggerMiddleware()); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Additional information | Then if you print the pipeline,  | ||||||
| 
 | 
 | ||||||
| TODO: Tell users more about the package: where to find more information, how to  | ``` | ||||||
| contribute to the package, how to file issues, what response they can expect  | [Req] -> UriPrefix -> SimpleLogger | ||||||
| from the package authors, and more. | [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( | ||||||
|  |         UriPrefixMiddleware( | ||||||
|  |             protocol: Protocols.http, | ||||||
|  |             authority: 'localhost:80', | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     .addMiddleware(BodyToJsonMiddleware()) | ||||||
|  |     .addMiddleware( | ||||||
|  |         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( | ||||||
|  |         UriPrefixMiddleware( | ||||||
|  |             protocol: Protocols.http, | ||||||
|  |             authority: 'localhost:80', | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     .addMiddleware(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(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 { | ||||||
|  |    | ||||||
|  |   @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; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | |||||||
| @ -17,14 +17,7 @@ | |||||||
| import 'dart:async'; | import 'dart:async'; | ||||||
| import 'dart:io'; | import 'dart:io'; | ||||||
| 
 | 
 | ||||||
| import 'package:wyatt_http_client/src/authentication/basic_authentication_client.dart'; | import 'package:wyatt_http_client/wyatt_http_client.dart'; | ||||||
| import 'package:wyatt_http_client/src/authentication/bearer_authentication_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/authentication/digest_authentication_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/authentication/refresh_token_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/authentication/unsafe_authentication_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/rest_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/header_keys.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; |  | ||||||
| 
 | 
 | ||||||
| String lastToken = ''; | String lastToken = ''; | ||||||
| int token = 0; | int token = 0; | ||||||
| @ -42,7 +35,7 @@ Future<void> handleBasic(HttpRequest req) async { | |||||||
| 
 | 
 | ||||||
| Future<void> handleBasicNegotiate(HttpRequest req) async { | Future<void> handleBasicNegotiate(HttpRequest req) async { | ||||||
|   if (req.headers.value('Authorization') == null) { |   if (req.headers.value('Authorization') == null) { | ||||||
|     req.response.statusCode = HttpStatus.unauthorized; |     req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|     req.response.headers.set(HeaderKeys.wwwAuthenticate, 'Basic realm="Wyatt"'); |     req.response.headers.set(HeaderKeys.wwwAuthenticate, 'Basic realm="Wyatt"'); | ||||||
|     print(req.response.headers.value('WWW-Authenticate')); |     print(req.response.headers.value('WWW-Authenticate')); | ||||||
|     return req.response.close(); |     return req.response.close(); | ||||||
| @ -56,7 +49,7 @@ Future<void> handleBearer(HttpRequest req) async { | |||||||
| 
 | 
 | ||||||
| Future<void> handleDigest(HttpRequest req) async { | Future<void> handleDigest(HttpRequest req) async { | ||||||
|   if (req.headers.value('Authorization') == null) { |   if (req.headers.value('Authorization') == null) { | ||||||
|     req.response.statusCode = HttpStatus.unauthorized; |     req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|     req.response.headers.set( |     req.response.headers.set( | ||||||
|       'WWW-Authenticate', |       'WWW-Authenticate', | ||||||
|       'Digest realm="Wyatt", ' |       'Digest realm="Wyatt", ' | ||||||
| @ -110,7 +103,7 @@ Future<void> handleOauth2RefreshToken(HttpRequest req) async { | |||||||
|         return req.response.close(); |         return req.response.close(); | ||||||
|       } else { |       } else { | ||||||
|         lastToken = receivedToken; |         lastToken = receivedToken; | ||||||
|         req.response.statusCode = HttpStatus.unauthorized; |         req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|         return req.response.close(); |         return req.response.close(); | ||||||
|       } |       } | ||||||
|     default: |     default: | ||||||
| @ -160,13 +153,13 @@ Future<void> server() async { | |||||||
|           print('Authorized'); |           print('Authorized'); | ||||||
|           error = 0; |           error = 0; | ||||||
|         } else { |         } else { | ||||||
|           request.response.statusCode = HttpStatus.unauthorized; |           request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|       case '/test/oauth2-test-timeout': |       case '/test/oauth2-test-timeout': | ||||||
|         error++; |         error++; | ||||||
|         print('Error $error'); |         print('Error $error'); | ||||||
|         request.response.statusCode = HttpStatus.unauthorized; |         request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|         break; |         break; | ||||||
|       case '/test/oauth2-login': |       case '/test/oauth2-login': | ||||||
|         if (request.method == 'POST') { |         if (request.method == 'POST') { | ||||||
| @ -189,12 +182,12 @@ Future<void> server() async { | |||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|       case '/test/oauth2-refresh-error': |       case '/test/oauth2-refresh-error': | ||||||
|         request.response.statusCode = HttpStatus.unauthorized; |         request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|       default: |       default: | ||||||
|         print(' => Unknown path or method'); |         print(' => Unknown path or method'); | ||||||
|         request.response.statusCode = HttpStatus.notFound; |         request.response.statusCode = HttpStatus.notFound.statusCode; | ||||||
|     } |     } | ||||||
|     request.response.close(); |     request.response.close(); | ||||||
|     print('===================='); |     print('===================='); | ||||||
| @ -204,73 +197,98 @@ Future<void> server() async { | |||||||
| Future<void> main() async { | Future<void> main() async { | ||||||
|   unawaited(server()); |   unawaited(server()); | ||||||
|   final base = 'localhost:8080'; |   final base = 'localhost:8080'; | ||||||
|   final restClient = RestClient(protocol: Protocols.http, authority: base); |   final uriPrefix = UriPrefixMiddleware( | ||||||
|  |     protocol: Protocols.http, | ||||||
|  |     authority: base, | ||||||
|  |   ); | ||||||
|  |   final jsonEncoder = BodyToJsonMiddleware(); | ||||||
|  |   final logger = SimpleLoggerMiddleware(); | ||||||
| 
 | 
 | ||||||
|   // Basic |   // Basic | ||||||
|   final basic = BasicAuthenticationClient( |   final basicAuth = BasicAuthMiddleware( | ||||||
|     username: 'username', |     username: 'username', | ||||||
|     password: 'password', |     password: 'password', | ||||||
|     inner: restClient, |   ); | ||||||
|  |   final basic = MiddlewareClient( | ||||||
|  |     pipeline: Pipeline.fromIterable([ | ||||||
|  |       uriPrefix, | ||||||
|  |       basicAuth, | ||||||
|  |       logger, | ||||||
|  |     ]), | ||||||
|   ); |   ); | ||||||
|   await basic.get(Uri.parse('/test/basic-test')); |   await basic.get(Uri.parse('/test/basic-test')); | ||||||
| 
 | 
 | ||||||
|   // Basic with negotiate |  | ||||||
|   final basicWithNegotiate = BasicAuthenticationClient( |  | ||||||
|     username: 'username', |  | ||||||
|     password: 'password', |  | ||||||
|     preemptive: false, |  | ||||||
|     inner: restClient, |  | ||||||
|   ); |  | ||||||
|   await basicWithNegotiate.get(Uri.parse('/test/basic-test-with-negotiate')); |  | ||||||
| 
 |  | ||||||
|   // Digest |   // Digest | ||||||
|   final digest = DigestAuthenticationClient( |   final digestAuth = DigestAuthMiddleware( | ||||||
|     username: 'Mufasa', |     username: 'Mufasa', | ||||||
|     password: 'Circle Of Life', |     password: 'Circle Of Life', | ||||||
|     inner: restClient, |   ); | ||||||
|  |   final digest = MiddlewareClient( | ||||||
|  |     pipeline: Pipeline.fromIterable([ | ||||||
|  |       uriPrefix, | ||||||
|  |       digestAuth, | ||||||
|  |       logger, | ||||||
|  |     ]), | ||||||
|   ); |   ); | ||||||
|   await digest.get(Uri.parse('/test/digest-test')); |   await digest.get(Uri.parse('/test/digest-test')); | ||||||
| 
 | 
 | ||||||
|   // Bearer |   // // Bearer | ||||||
|   final bearer = BearerAuthenticationClient( |   // final bearer = BearerAuthenticationClient( | ||||||
|     token: 'access-token-test', |   //   token: 'access-token-test', | ||||||
|     inner: restClient, |   //   inner: restClient, | ||||||
|   ); |   // ); | ||||||
|   await bearer.get(Uri.parse('/test/bearer-test')); |   // await bearer.get(Uri.parse('/test/bearer-test')); | ||||||
| 
 | 
 | ||||||
|   // API Key |   // // API Key | ||||||
|   final apiKey = BearerAuthenticationClient( |   // final apiKey = BearerAuthenticationClient( | ||||||
|     token: 'awesome-api-key', |   //   token: 'awesome-api-key', | ||||||
|     authenticationMethod: 'ApiKey', |   //   authenticationMethod: 'ApiKey', | ||||||
|     inner: restClient, |   //   inner: restClient, | ||||||
|   ); |   // ); | ||||||
|   await apiKey.get(Uri.parse('/test/apikey-test')); |   // await apiKey.get(Uri.parse('/test/apikey-test')); | ||||||
| 
 | 
 | ||||||
|   // Unsafe URL |   // Unsafe URL | ||||||
|   final unsafe = UnsafeAuthenticationClient( |   final unsafeAuth = UnsafeAuthMiddleware( | ||||||
|     username: 'Mufasa', |     username: 'Mufasa', | ||||||
|     password: 'Circle Of Life', |     password: 'Circle Of Life', | ||||||
|     inner: restClient, |   ); | ||||||
|  |   final unsafe = MiddlewareClient( | ||||||
|  |     pipeline: Pipeline.fromIterable([ | ||||||
|  |       uriPrefix, | ||||||
|  |       unsafeAuth, | ||||||
|  |       logger, | ||||||
|  |     ]), | ||||||
|   ); |   ); | ||||||
|   await unsafe.get(Uri.parse('/test/unsafe-test')); |   await unsafe.get(Uri.parse('/test/unsafe-test')); | ||||||
| 
 | 
 | ||||||
|   // OAuth2 |   // OAuth2 | ||||||
|   final refreshToken = RefreshTokenClient( |   final refreshTokenAuth = RefreshTokenAuthMiddleware( | ||||||
|     authorizationEndpoint: '/test/oauth2-test?action=login', |     authorizationEndpoint: '/test/oauth2-test?action=login', | ||||||
|     tokenEndpoint: '/test/oauth2-test?action=refresh', |     tokenEndpoint: '/test/oauth2-test?action=refresh', | ||||||
|     accessTokenParser: (body) => body['accessToken']! as String, |     accessTokenParser: (body) => body['accessToken']! as String, | ||||||
|     refreshTokenParser: (body) => body['refreshToken']! as String, |     refreshTokenParser: (body) => body['refreshToken']! as String, | ||||||
|     inner: restClient, |   ); | ||||||
|  |   final refreshToken = MiddlewareClient( | ||||||
|  |     pipeline: Pipeline.fromIterable([ | ||||||
|  |       uriPrefix, | ||||||
|  |       jsonEncoder, | ||||||
|  |       refreshTokenAuth, | ||||||
|  |       logger, | ||||||
|  |     ]), | ||||||
|   ); |   ); | ||||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); |   await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||||
|   await refreshToken.authorize(<String, String>{ |   // Login | ||||||
|     'username': 'username', |   await refreshToken.post( | ||||||
|     'password': 'password', |     Uri.parse('/test/oauth2-test'), | ||||||
|   }); |     body: <String, String>{ | ||||||
|  |       'username': 'username', | ||||||
|  |       'password': 'password', | ||||||
|  |     }, | ||||||
|  |   ); | ||||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); |   await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||||
|   await refreshToken.refresh(); |   // await refreshToken.refresh(); | ||||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); |   // await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test?action=access-denied')); |   // await refreshToken.get(Uri.parse('/test/oauth2-test?action=access-denied')); | ||||||
| 
 | 
 | ||||||
|   exit(0); |   exit(0); | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ import 'dart:convert'; | |||||||
| 
 | 
 | ||||||
| import 'package:wyatt_http_client/src/middleware_client.dart'; | import 'package:wyatt_http_client/src/middleware_client.dart'; | ||||||
| import 'package:wyatt_http_client/src/middlewares/body_to_json_middleware.dart'; | import 'package:wyatt_http_client/src/middlewares/body_to_json_middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/middlewares/refresh_token_middleware.dart'; | import 'package:wyatt_http_client/src/middlewares/refresh_token_auth_middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/middlewares/simple_logger_middleware.dart'; | import 'package:wyatt_http_client/src/middlewares/simple_logger_middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/middlewares/uri_prefix_middleware.dart'; | import 'package:wyatt_http_client/src/middlewares/uri_prefix_middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | import 'package:wyatt_http_client/src/pipeline.dart'; | ||||||
| @ -354,7 +354,6 @@ class FastAPI { | |||||||
| 
 | 
 | ||||||
| void main(List<String> args) async { | void main(List<String> args) async { | ||||||
|   final Pipeline pipeline = Pipeline() |   final Pipeline pipeline = Pipeline() | ||||||
|       .addMiddleware(SimpleLoggerMiddleware()) |  | ||||||
|       .addMiddleware( |       .addMiddleware( | ||||||
|         UriPrefixMiddleware( |         UriPrefixMiddleware( | ||||||
|           protocol: Protocols.http, |           protocol: Protocols.http, | ||||||
| @ -363,39 +362,37 @@ void main(List<String> args) async { | |||||||
|       ) |       ) | ||||||
|       .addMiddleware(BodyToJsonMiddleware()) |       .addMiddleware(BodyToJsonMiddleware()) | ||||||
|       .addMiddleware( |       .addMiddleware( | ||||||
|         RefreshTokenMiddleware( |         RefreshTokenAuthMiddleware( | ||||||
|           authorizationEndpoint: '/api/v1/auth/sign-in-with-password', |           authorizationEndpoint: '/api/v1/auth/sign-in-with-password', | ||||||
|           tokenEndpoint: '/api/v1/auth/refresh', |           tokenEndpoint: '/api/v1/auth/refresh', | ||||||
|           accessTokenParser: (body) => body['access_token']! as String, |           accessTokenParser: (body) => body['access_token']! as String, | ||||||
|           refreshTokenParser: (body) => body['refresh_token']! as String, |           refreshTokenParser: (body) => body['refresh_token']! as String, | ||||||
|           unauthorized: HttpStatus.forbidden, |           unauthorized: HttpStatus.forbidden, | ||||||
|         ), |         ), | ||||||
|       ); |       ) | ||||||
|  |       .addMiddleware(SimpleLoggerMiddleware()); | ||||||
| 
 | 
 | ||||||
|   print(pipeline.getLogic()); |   print(pipeline); | ||||||
|   final client = MiddlewareClient(pipeline: pipeline); |   final client = MiddlewareClient(pipeline: pipeline); | ||||||
| 
 | 
 | ||||||
|   final api = FastAPI( |   final api = FastAPI( | ||||||
|     client: client, |     client: client, | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   // await api.sendSignUpCode('git@pcl.ovh'); |   await api.sendSignUpCode('git@pcl.ovh'); | ||||||
|   // final verifiedAccount = await api.verifyCode( |   final verifiedAccount = await api.verifyCode( | ||||||
|   //   VerifyCode( |     VerifyCode( | ||||||
|   //     email: 'git@pcl.ovh', |       email: 'git@pcl.ovh', | ||||||
|   //     verificationCode: '000000000', |       verificationCode: '000000000', | ||||||
|   //     action: EmailVerificationAction.signUp, |       action: EmailVerificationAction.signUp, | ||||||
|   //   ), |     ), | ||||||
|   // ); |   ); | ||||||
|   // print(verifiedAccount); |   final registeredAccount = await api.signUp( | ||||||
|   // final registeredAccount = await api.signUp( |     SignUp(sessionId: verifiedAccount.sessionId ?? '', password: 'password'), | ||||||
|   //   SignUp(sessionId: verifiedAccount.sessionId ?? '', password: 'password'), |   ); | ||||||
|   // ); |  | ||||||
|   // print(registeredAccount); |  | ||||||
|   final signedInAccount = await api.signInWithPassword( |   final signedInAccount = await api.signInWithPassword( | ||||||
|     Login(email: 'git@pcl.ovh', password: 'password'), |     Login(email: 'git@pcl.ovh', password: 'password'), | ||||||
|   ); |   ); | ||||||
|   // print(signedInAccount); |  | ||||||
|   final accountList = await api.getAccountList(); |   final accountList = await api.getAccountList(); | ||||||
|   print(accountList); |   print(accountList); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,41 +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/>. |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Future<void> main(List<String> args) async { |  | ||||||
|   // final client = Oauth2Client( |  | ||||||
|   //   accessToken: 'test-token', |  | ||||||
|   //   inner: RestClient(protocol: Protocols.http, authority: 'localhost:80'), |  | ||||||
|   // ); |  | ||||||
|   // final client = RestClient( |  | ||||||
|   //   protocol: Protocols.http, |  | ||||||
|   //   authority: 'localhost:80', |  | ||||||
|   //   inner: Oauth2Client( |  | ||||||
|   //     authorizationEndpoint: '/api/v1/account/test', |  | ||||||
|   //     tokenEndpoint: '/api/v1/account/test', |  | ||||||
|   //     accessToken: 'test-token', |  | ||||||
|   //     refreshToken: 'refresh-token', |  | ||||||
|   //   ), |  | ||||||
|   // ); |  | ||||||
|   // final client = RestClient(protocol: Protocols.http, authority: 'localhost:80'); |  | ||||||
|   // final client =AwesomeRestClient(protocol: Protocols.http, authority: 'localhost:80'); |  | ||||||
|   // var r = await client.post( |  | ||||||
|   //   Uri.parse('/api/v1/account/test'), |  | ||||||
|   //   body: <String, String>{ |  | ||||||
|   //     'email': 'test@test.fr', |  | ||||||
|   //   }, |  | ||||||
|   // ); |  | ||||||
| } |  | ||||||
| @ -17,8 +17,8 @@ | |||||||
| 
 | 
 | ||||||
| import 'package:wyatt_http_client/src/middleware_client.dart'; | import 'package:wyatt_http_client/src/middleware_client.dart'; | ||||||
| import 'package:wyatt_http_client/src/middlewares/body_to_json_middleware.dart'; | import 'package:wyatt_http_client/src/middlewares/body_to_json_middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/middlewares/refresh_token_middleware.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/middlewares/simple_logger_middleware.dart'; | import 'package:wyatt_http_client/src/middlewares/simple_logger_middleware.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/middlewares/unsafe_auth_middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/middlewares/uri_prefix_middleware.dart'; | import 'package:wyatt_http_client/src/middlewares/uri_prefix_middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | import 'package:wyatt_http_client/src/pipeline.dart'; | ||||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; | import 'package:wyatt_http_client/src/utils/protocols.dart'; | ||||||
| @ -117,8 +117,8 @@ import 'package:wyatt_http_client/src/utils/protocols.dart'; | |||||||
| // } | // } | ||||||
| 
 | 
 | ||||||
| Future<void> main(List<String> args) async { | Future<void> main(List<String> args) async { | ||||||
|  |   final UnsafeAuthMiddleware auth = UnsafeAuthMiddleware(); | ||||||
|   final Pipeline pipeline = Pipeline() |   final Pipeline pipeline = Pipeline() | ||||||
|       .addMiddleware(SimpleLoggerMiddleware()) |  | ||||||
|       .addMiddleware( |       .addMiddleware( | ||||||
|         UriPrefixMiddleware( |         UriPrefixMiddleware( | ||||||
|           protocol: Protocols.http, |           protocol: Protocols.http, | ||||||
| @ -127,17 +127,33 @@ Future<void> main(List<String> args) async { | |||||||
|       ) |       ) | ||||||
|       .addMiddleware(BodyToJsonMiddleware()) |       .addMiddleware(BodyToJsonMiddleware()) | ||||||
|       .addMiddleware( |       .addMiddleware( | ||||||
|         RefreshTokenMiddleware( |         UnsafeAuthMiddleware( | ||||||
|           authorizationEndpoint: '/api/v1/account/test?action=authorize', |           username: 'wyatt', | ||||||
|           tokenEndpoint: '/api/v1/account/test?action=refresh', |           password: 'motdepasse', | ||||||
|           accessTokenParser: (body) => body['access_token']! as String, |  | ||||||
|           refreshTokenParser: (body) => body['refresh_token']! as String, |  | ||||||
|         ), |         ), | ||||||
|       ); |       ) | ||||||
|  |       .addMiddleware(SimpleLoggerMiddleware()); | ||||||
|  |   // .addMiddleware( | ||||||
|  |   //   RefreshTokenMiddleware( | ||||||
|  |   //     authorizationEndpoint: '/api/v1/account/test?action=authorize', | ||||||
|  |   //     tokenEndpoint: '/api/v1/account/test?action=refresh', | ||||||
|  |   //     accessTokenParser: (body) => body['access_token']! as String, | ||||||
|  |   //     refreshTokenParser: (body) => body['refresh_token']! as String, | ||||||
|  |   //   ), | ||||||
|  |   // ); | ||||||
| 
 | 
 | ||||||
|   print(pipeline.getLogic()); |   print(pipeline); | ||||||
|   final client = MiddlewareClient(pipeline: pipeline); |   final client = MiddlewareClient(pipeline: pipeline); | ||||||
|   final r = await client.post( |   await client.post( | ||||||
|  |     Uri.parse('/api/v1/account/test'), | ||||||
|  |     body: <String, String>{ | ||||||
|  |       'email': 'test@test.fr', | ||||||
|  |     }, | ||||||
|  |   ); | ||||||
|  |   auth | ||||||
|  |     ..username = 'username' | ||||||
|  |     ..password = 'password'; | ||||||
|  |   await client.post( | ||||||
|     Uri.parse('/api/v1/account/test'), |     Uri.parse('/api/v1/account/test'), | ||||||
|     body: <String, String>{ |     body: <String, String>{ | ||||||
|       'email': 'test@test.fr', |       'email': 'test@test.fr', | ||||||
|  | |||||||
| @ -1,67 +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/authentication/interfaces/header_authentication_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/authentication_methods.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/header_keys.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/http_status.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; |  | ||||||
| 
 |  | ||||||
| class BasicAuthenticationClient extends HeaderAuthenticationClient { |  | ||||||
|   final String username; |  | ||||||
|   final String password; |  | ||||||
|   final bool preemptive; |  | ||||||
|   final String authenticationHeader; |  | ||||||
| 
 |  | ||||||
|   BasicAuthenticationClient({ |  | ||||||
|     required this.username, |  | ||||||
|     required this.password, |  | ||||||
|     this.preemptive = true, |  | ||||||
|     this.authenticationHeader = HeaderKeys.authorization, |  | ||||||
|     BaseClient? inner, |  | ||||||
|   }) : super(inner); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Map<String, String> modifyHeader( |  | ||||||
|     Map<String, String> header, [ |  | ||||||
|     BaseRequest? request, |  | ||||||
|   ]) { |  | ||||||
|     header[authenticationHeader] = '${AuthenticationMethods.basic} ' |  | ||||||
|         '${base64Encode(utf8.encode('$username:$password'))}'; |  | ||||||
|     return header; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) async { |  | ||||||
|     if (preemptive) { |  | ||||||
|       // Just send request with modified header. |  | ||||||
|       return super.send(request); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Try to send request without modified header, |  | ||||||
|     // and if it fails, send it with. |  | ||||||
|     final response = await inner.send(request); |  | ||||||
|     if (response.statusCode == HttpStatus.unauthorized) { |  | ||||||
|       // TODO(hpcl): save realm. |  | ||||||
|       final newRequest = Utils.copyRequest(request); |  | ||||||
|       return super.send(newRequest); |  | ||||||
|     } else { |  | ||||||
|       return response; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -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/authentication/interfaces/header_authentication_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/authentication_methods.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/header_keys.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/http_status.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; |  | ||||||
| 
 |  | ||||||
| class BearerAuthenticationClient extends HeaderAuthenticationClient { |  | ||||||
|   final String token; |  | ||||||
|   final bool preemptive; |  | ||||||
|   final String authenticationHeader; |  | ||||||
|   final String authenticationMethod; |  | ||||||
| 
 |  | ||||||
|   BearerAuthenticationClient({ |  | ||||||
|     required this.token, |  | ||||||
|     this.preemptive = true, |  | ||||||
|     this.authenticationHeader = HeaderKeys.authorization, |  | ||||||
|     this.authenticationMethod = AuthenticationMethods.bearer, |  | ||||||
|     BaseClient? inner, |  | ||||||
|   }) : super(inner); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Map<String, String> modifyHeader( |  | ||||||
|     Map<String, String> header, [ |  | ||||||
|     BaseRequest? request, |  | ||||||
|   ]) { |  | ||||||
|     header[authenticationHeader] = '$authenticationMethod $token'; |  | ||||||
|     return header; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) async { |  | ||||||
|     if (preemptive) { |  | ||||||
|       // Just send request with modified header. |  | ||||||
|       return super.send(request); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Try to send request without modified header, |  | ||||||
|     final response = await inner.send(request); |  | ||||||
|     if (response.statusCode == HttpStatus.unauthorized) { |  | ||||||
|       final newRequest = Utils.copyRequest(request); |  | ||||||
|       return super.send(newRequest); |  | ||||||
|     } else { |  | ||||||
|       return response; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,75 +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/authentication/interfaces/header_authentication_client.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'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; |  | ||||||
| 
 |  | ||||||
| class DigestAuthenticationClient extends HeaderAuthenticationClient { |  | ||||||
|   final String username; |  | ||||||
|   final String password; |  | ||||||
|   final DigestAuth _digestAuth; |  | ||||||
|   final String authenticationHeader; |  | ||||||
|   final String wwwAuthenticateHeader; |  | ||||||
| 
 |  | ||||||
|   DigestAuthenticationClient({ |  | ||||||
|     required this.username, |  | ||||||
|     required this.password, |  | ||||||
|     this.authenticationHeader = HeaderKeys.authorization, |  | ||||||
|     this.wwwAuthenticateHeader = HeaderKeys.wwwAuthenticate, |  | ||||||
|     BaseClient? inner, |  | ||||||
|   })  : _digestAuth = DigestAuth(username, password), |  | ||||||
|         super(inner); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Map<String, String> modifyHeader( |  | ||||||
|     Map<String, String> header, [ |  | ||||||
|     BaseRequest? request, |  | ||||||
|   ]) { |  | ||||||
|     if ((_digestAuth.isReady()) && request != null) { |  | ||||||
|       header[authenticationHeader] = _digestAuth.getAuthString( |  | ||||||
|         request.method, |  | ||||||
|         request.url, |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|     return header; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) async { |  | ||||||
|     // Check if our DigestAuth is ready. |  | ||||||
|     if (_digestAuth.isReady()) { |  | ||||||
|       // If it is, try to send the request with the modified header. |  | ||||||
|       return super.send(request); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // If it isn't, try to send the request without the modified header. |  | ||||||
|     final response = await inner.send(request); |  | ||||||
| 
 |  | ||||||
|     if (response.statusCode == HttpStatus.unauthorized) { |  | ||||||
|       final newRequest = Utils.copyRequest(request); |  | ||||||
|       final authInfo = |  | ||||||
|           response.headers[HeaderKeys.wwwAuthenticate.toLowerCase()]; |  | ||||||
|       _digestAuth.initFromAuthenticateHeader(authInfo); |  | ||||||
|       return super.send(newRequest); |  | ||||||
|     } else { |  | ||||||
|       return response; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -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 'dart:convert'; |  | ||||||
| import 'dart:typed_data'; |  | ||||||
| 
 |  | ||||||
| import 'package:http/http.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/implemented_base_client.dart'; |  | ||||||
| 
 |  | ||||||
| abstract class AuthenticatedClient implements ImplementedBaseClient { |  | ||||||
|   final Client _inner; |  | ||||||
| 
 |  | ||||||
|   AuthenticatedClient({ |  | ||||||
|     Client? inner, |  | ||||||
|   }) : _inner = inner ?? Client(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   void close() => _inner.close(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> head(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.head(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> get(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.get(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> post( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.post(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> put( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.put(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> patch( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.patch(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> delete( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.delete(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<String> read(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.read(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.readBytes(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) => _inner.send(request); |  | ||||||
| } |  | ||||||
| @ -1,234 +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 'dart:typed_data'; |  | ||||||
| 
 |  | ||||||
| import 'package:http/http.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/mixins/body_transformer.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/mixins/headers_transformer.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/rest_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; |  | ||||||
| 
 |  | ||||||
| class AwesomeClient extends BaseClient { |  | ||||||
|   final Client _inner; |  | ||||||
| 
 |  | ||||||
|   AwesomeClient({ |  | ||||||
|     Client? inner, |  | ||||||
|   }) : _inner = inner ?? Client(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |  | ||||||
|     return _inner.send(request); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class AwesomeRestClient extends AwesomeClient |  | ||||||
|     with HeadersTransformer, BodyTransformer { |  | ||||||
|   final Protocols protocol; |  | ||||||
|   final String? authority; |  | ||||||
| 
 |  | ||||||
|   AwesomeRestClient({ |  | ||||||
|     this.protocol = Protocols.https, |  | ||||||
|     this.authority = '', |  | ||||||
|     super.inner, |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Object? bodyMutator(Object? body) { |  | ||||||
|     print('bodyMutator: Json encoding'); |  | ||||||
|     return jsonEncode(body); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Map<String, String>? headersMutator( |  | ||||||
|     Object? body, |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   ) { |  | ||||||
|     print('headerMutator: Json encoding'); |  | ||||||
|     final mutation = { |  | ||||||
|       'content-type': 'application/json; charset=utf-8', |  | ||||||
|     }; |  | ||||||
|     if (headers != null) { |  | ||||||
|       headers.addAll(mutation); |  | ||||||
|       return headers; |  | ||||||
|     } else { |  | ||||||
|       return mutation; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   BaseRequest requestMutator(BaseRequest request) { |  | ||||||
|     print('requestMutator: scheme + authority'); |  | ||||||
|     final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}'); |  | ||||||
|     return Utils.copyRequestWith(request, url: uri); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class AwesomeUrlClient extends AuthenticatedClient { |  | ||||||
|   AwesomeUrlClient(super.inner); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class AwesomeOauth2Client extends AuthenticatedClient with HeadersTransformer { |  | ||||||
|   AwesomeOauth2Client(super.inner); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Map<String, String>? headersMutator( |  | ||||||
|     Object? body, |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   ) { |  | ||||||
|     print('headersMutator: Token manager'); |  | ||||||
|     final mutation = { |  | ||||||
|       'authorization': 'Bearer TOKEN', |  | ||||||
|     }; |  | ||||||
|     if (headers != null) { |  | ||||||
|       headers.addAll(mutation); |  | ||||||
|       return headers; |  | ||||||
|     } else { |  | ||||||
|       return mutation; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| abstract class AuthenticatedClient implements Client { |  | ||||||
|   final Client _inner; |  | ||||||
| 
 |  | ||||||
|   Client get inner => _inner; |  | ||||||
| 
 |  | ||||||
|   AuthenticatedClient(BaseClient? inner) : _inner = inner ?? RestClient(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   void close() => _inner.close(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> head(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.head(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> get(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.get(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> post( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.post(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> put( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.put(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> patch( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.patch(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> delete( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.delete(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<String> read(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.read(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.readBytes(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) => _inner.send(request); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| abstract class AuthenticationClient extends BaseClient { |  | ||||||
|   final BaseClient _inner; |  | ||||||
| 
 |  | ||||||
|   BaseClient get inner => _inner; |  | ||||||
| 
 |  | ||||||
|   AuthenticationClient(BaseClient? inner) : _inner = inner ?? RestClient(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> head(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.head(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> get(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.get(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> post( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.post(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> put( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.put(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> patch( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.patch(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> delete( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.delete(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |  | ||||||
|     return _inner.send(request); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   void close() { |  | ||||||
|     _inner.close(); |  | ||||||
|     return super.close(); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,37 +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/authentication/interfaces/authentication_client.dart'; |  | ||||||
| 
 |  | ||||||
| abstract class HeaderAuthenticationClient extends AuthenticationClient { |  | ||||||
|   HeaderAuthenticationClient(super.inner); |  | ||||||
| 
 |  | ||||||
|   Map<String, String> modifyHeader( |  | ||||||
|     Map<String, String> header, [ |  | ||||||
|     BaseRequest? request, |  | ||||||
|   ]) => |  | ||||||
|       header; |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |  | ||||||
|     final newHeader = modifyHeader(Map.from(request.headers), request); |  | ||||||
|     request.headers.clear(); |  | ||||||
|     request.headers.addAll(newHeader); |  | ||||||
|     print(newHeader); |  | ||||||
|     return super.send(request); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,195 +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/authentication/interfaces/oauth2_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/authentication_methods.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/header_keys.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/http_status.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; |  | ||||||
| 
 |  | ||||||
| // class Oauth2Client extends ImplementedBaseClient with RequestTransformer { |  | ||||||
| //   final String authorizationEndpoint; |  | ||||||
| //   final String tokenEndpoint; |  | ||||||
| //   String? accessToken; |  | ||||||
| //   String? refreshToken; |  | ||||||
| //   String? tokenToUse; |  | ||||||
| 
 |  | ||||||
| //   Oauth2Client({ |  | ||||||
| //     required this.authorizationEndpoint, |  | ||||||
| //     required this.tokenEndpoint, |  | ||||||
| //     this.accessToken, |  | ||||||
| //     this.refreshToken, |  | ||||||
| //     super.inner, |  | ||||||
| //   }) : tokenToUse = accessToken; |  | ||||||
| 
 |  | ||||||
| //   @override |  | ||||||
| //   BaseRequest requestMutator(BaseRequest request) { |  | ||||||
| //     print('Oauth2Client::requestMutator -> add authorization: $accessToken'); |  | ||||||
| //     final headers = request.headers; |  | ||||||
| //     final mutation = { |  | ||||||
| //       'Authorization': 'Bearer $tokenToUse', |  | ||||||
| //     }; |  | ||||||
| //     if (tokenToUse?.isNotEmpty ?? false) { |  | ||||||
| //       headers.addAll(mutation); |  | ||||||
| //       return Utils.copyRequestWith(request, headers: headers); |  | ||||||
| //     } |  | ||||||
| //     return request; |  | ||||||
| //   } |  | ||||||
| 
 |  | ||||||
| //   Future<Response?> refresh() async { |  | ||||||
| //     if (refreshToken?.isNotEmpty ?? false) { |  | ||||||
| //       tokenToUse = refreshToken; |  | ||||||
| 
 |  | ||||||
| //       final response = await get( |  | ||||||
| //         Uri.parse(tokenEndpoint), |  | ||||||
| //       ); |  | ||||||
| 
 |  | ||||||
| //       if (response.statusCode == HttpStatus.ok) { |  | ||||||
| //         // final body = json.decode(response.body) as Map<String, dynamic>; |  | ||||||
| //         // accessToken = accessTokenParser(body); |  | ||||||
| //         print('Oauth2Client::refresh -> ok'); |  | ||||||
| //       } |  | ||||||
| //       return response; |  | ||||||
| //     } |  | ||||||
| //     return null; |  | ||||||
| //   } |  | ||||||
| 
 |  | ||||||
| // @override |  | ||||||
| // Map<String, String>? headersMutator( |  | ||||||
| //   Object? body, |  | ||||||
| //   Map<String, String>? headers, |  | ||||||
| // ) { |  | ||||||
| //   print( |  | ||||||
| //     'Oauth2Client::headersMutator -> add authorization: $accessToken', |  | ||||||
| //   ); |  | ||||||
| //   final mutation = { |  | ||||||
| //     'Authorization': 'Bearer $accessToken', |  | ||||||
| //   }; |  | ||||||
| //   if (accessToken.isNotEmpty) { |  | ||||||
| //     if (headers != null) { |  | ||||||
| //       headers.addAll(mutation); |  | ||||||
| //       return headers; |  | ||||||
| //     } else { |  | ||||||
| //       return mutation; |  | ||||||
| //     } |  | ||||||
| //   } else { |  | ||||||
| //     return headers; |  | ||||||
| //   } |  | ||||||
| // } |  | ||||||
| 
 |  | ||||||
| // } |  | ||||||
| 
 |  | ||||||
| class RefreshTokenClient extends Oauth2Client { |  | ||||||
|   final String authorizationEndpoint; |  | ||||||
|   final String tokenEndpoint; |  | ||||||
| 
 |  | ||||||
|   String? accessToken; |  | ||||||
|   final TokenParser accessTokenParser; |  | ||||||
|   String? refreshToken; |  | ||||||
|   final TokenParser refreshTokenParser; |  | ||||||
| 
 |  | ||||||
|   final String authenticationHeader; |  | ||||||
|   final String authenticationMethod; |  | ||||||
| 
 |  | ||||||
|   RefreshTokenClient({ |  | ||||||
|     required this.authorizationEndpoint, |  | ||||||
|     required this.tokenEndpoint, |  | ||||||
|     required this.accessTokenParser, |  | ||||||
|     required this.refreshTokenParser, |  | ||||||
|     this.authenticationHeader = HeaderKeys.authorization, |  | ||||||
|     this.authenticationMethod = AuthenticationMethods.bearer, |  | ||||||
|     BaseClient? inner, |  | ||||||
|   }) : super(inner); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Map<String, String> modifyHeader( |  | ||||||
|     Map<String, String> header, [ |  | ||||||
|     BaseRequest? request, |  | ||||||
|   ]) { |  | ||||||
|     print('accessToken $accessToken'); |  | ||||||
|     print('request $request'); |  | ||||||
|     if (accessToken != null && request != null) { |  | ||||||
|       header[authenticationHeader] = '$authenticationMethod $accessToken'; |  | ||||||
|       return header; |  | ||||||
|     } |  | ||||||
|     return header; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> authorize( |  | ||||||
|     Map<String, dynamic> body, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   }) async { |  | ||||||
|     final response = await inner.post( |  | ||||||
|       Uri.parse(authorizationEndpoint), |  | ||||||
|       body: body, |  | ||||||
|       headers: headers, |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     if (response.statusCode == HttpStatus.ok) { |  | ||||||
|       final body = json.decode(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; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return response; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response?> refresh() async { |  | ||||||
|     if (refreshToken != null) { |  | ||||||
|       final Map<String, String> header = { |  | ||||||
|         authenticationHeader: '$authenticationHeader $refreshToken', |  | ||||||
|       }; |  | ||||||
| 
 |  | ||||||
|       final response = await inner.get( |  | ||||||
|         Uri.parse(tokenEndpoint), |  | ||||||
|         headers: header, |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       if (response.statusCode == HttpStatus.ok) { |  | ||||||
|         final body = json.decode(response.body) as Map<String, dynamic>; |  | ||||||
|         accessToken = accessTokenParser(body); |  | ||||||
|       } |  | ||||||
|       return response; |  | ||||||
|     } |  | ||||||
|     return null; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) async { |  | ||||||
|     final newHeader = modifyHeader(Map.from(request.headers), request); |  | ||||||
|     request.headers.clear(); |  | ||||||
|     request.headers.addAll(newHeader); |  | ||||||
|     final response = await super.send(request); |  | ||||||
| 
 |  | ||||||
|     if (response.statusCode == HttpStatus.unauthorized) { |  | ||||||
|       await refresh(); |  | ||||||
|       return super.send(Utils.copyRequest(request)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return response; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,86 +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 'dart:typed_data'; |  | ||||||
| 
 |  | ||||||
| import 'package:http/http.dart'; |  | ||||||
| 
 |  | ||||||
| abstract class ImplementedClient extends BaseClient { |  | ||||||
|   final Client _inner; |  | ||||||
| 
 |  | ||||||
|   ImplementedClient({ |  | ||||||
|     Client? inner, |  | ||||||
|   }) : _inner = inner ?? Client(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   void close() => _inner.close(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> head(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.head(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> get(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.get(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> post( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.post(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> put( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.put(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> patch( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.patch(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> delete( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) => |  | ||||||
|       _inner.delete(url, headers: headers, body: body, encoding: encoding); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<String> read(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.read(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers}) => |  | ||||||
|       _inner.readBytes(url, headers: headers); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) => _inner.send(request); |  | ||||||
| } |  | ||||||
| @ -14,31 +14,25 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| part of 'pipeline.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'; | ||||||
| 
 | 
 | ||||||
| class Middleware { | abstract class Middleware { | ||||||
|   /// The http [MiddlewareClient] used by this [Middleware] |   Middleware(); | ||||||
|   MiddlewareClient? _client; |   String getName(); | ||||||
| 
 | } | ||||||
|   String getName() => 'MiddlewareNode'; | 
 | ||||||
| 
 | mixin OnRequestMiddleware { | ||||||
|   // ignore: avoid_setters_without_getters |   Future<MiddlewareRequest> onRequest( | ||||||
|   set httpClient(MiddlewareClient? client) => _client = client; |     MiddlewareContext context, | ||||||
| 
 |     MiddlewareRequest request, | ||||||
|   Client? get client => _client?.inner; |   ); | ||||||
| 
 | } | ||||||
|   Future<MiddlewareRequest> onRequest( | 
 | ||||||
|     MiddlewareRequest request, | mixin OnResponseMiddleware { | ||||||
|   ) async { |   Future<MiddlewareResponse> onResponse( | ||||||
|     return request; |     MiddlewareContext context, | ||||||
|   } |     MiddlewareResponse response, | ||||||
| 
 |   ); | ||||||
|   Future<MiddlewareResponse> onResponse(MiddlewareResponse response) async { |  | ||||||
|     return response; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   String toString() { |  | ||||||
|     return getName(); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ class MiddlewareClient extends BaseClient { | |||||||
|     Client? inner, |     Client? inner, | ||||||
|   })  : pipeline = pipeline ?? Pipeline(), |   })  : pipeline = pipeline ?? Pipeline(), | ||||||
|         inner = inner ?? Client() { |         inner = inner ?? Client() { | ||||||
|     this.pipeline.setClient(this); |     print('Using Pipeline:\n$pipeline'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
| @ -100,27 +100,30 @@ class MiddlewareClient extends BaseClient { | |||||||
|         body: body, |         body: body, | ||||||
|         encoding: encoding, |         encoding: encoding, | ||||||
|       ), |       ), | ||||||
|       httpRequest: Request(method, url), |  | ||||||
|       context: MiddlewareContext(pipeline: pipeline), |  | ||||||
|     ); |     ); | ||||||
|  |     final requestContext = MiddlewareContext( | ||||||
|  |       pipeline: pipeline, | ||||||
|  |       client: this, | ||||||
|  |       originalRequest: originalRequest, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|     final modifiedRequest = await pipeline.onRequest( |     final modifiedRequest = await pipeline.onRequest( | ||||||
|  |       requestContext, | ||||||
|       originalRequest.copyWith(), |       originalRequest.copyWith(), | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     final res = await Response.fromStream( |     final originalResponse = MiddlewareResponse( | ||||||
|       await send(modifiedRequest.httpRequest), |       httpResponse: await Response.fromStream( | ||||||
|     ); |         await send(modifiedRequest.request), | ||||||
|     final response = await pipeline.onResponse( |  | ||||||
|       MiddlewareResponse( |  | ||||||
|         httpResponse: res, |  | ||||||
|         middlewareRequest: modifiedRequest, |  | ||||||
|         context: MiddlewareContext( |  | ||||||
|           pipeline: pipeline, |  | ||||||
|           originalRequest: originalRequest, |  | ||||||
|         ), |  | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     return response.httpResponse as Response; |     final responseContext = | ||||||
|  |         requestContext.copyWith(originalResponse: originalResponse); | ||||||
|  | 
 | ||||||
|  |     final modifiedResponse = | ||||||
|  |         await pipeline.onResponse(responseContext, originalResponse.copyWith()); | ||||||
|  | 
 | ||||||
|  |     return modifiedResponse.httpResponse as Response; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,103 +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/>. |  | ||||||
| 
 |  | ||||||
| part of 'pipeline.dart'; |  | ||||||
| 
 |  | ||||||
| class MiddlewareNode { |  | ||||||
|   final Pipeline pipeline; |  | ||||||
| 
 |  | ||||||
|   Middleware? middleware; |  | ||||||
| 
 |  | ||||||
|   late MiddlewareNode _parent; |  | ||||||
|   late MiddlewareNode _child; |  | ||||||
|   final bool _isEnd; |  | ||||||
| 
 |  | ||||||
|   /// Reference to the previous [MiddlewareNode] in the [Pipeline] |  | ||||||
|   MiddlewareNode get parent => _parent; |  | ||||||
| 
 |  | ||||||
|   /// Reference to the next [MiddlewareNode] in the [Pipeline] |  | ||||||
|   MiddlewareNode get child => _child; |  | ||||||
| 
 |  | ||||||
|   /// Whether this is the begin [MiddlewareNode] |  | ||||||
|   bool get isBegin => _parent == this; |  | ||||||
| 
 |  | ||||||
|   /// Whether this is the end [MiddlewareNode] |  | ||||||
|   bool get isEnd => _child == this; |  | ||||||
| 
 |  | ||||||
|   /// Whether this is the first [MiddlewareNode] |  | ||||||
|   bool get isFirst => !isBegin && _parent == pipeline.begin; |  | ||||||
| 
 |  | ||||||
|   /// Whether this is the last [MiddlewareNode] |  | ||||||
|   bool get isLast => !isEnd && _child == pipeline.end; |  | ||||||
| 
 |  | ||||||
|   MiddlewareNode._( |  | ||||||
|     this.pipeline, |  | ||||||
|     this.middleware, { |  | ||||||
|     MiddlewareNode? parent, |  | ||||||
|     MiddlewareNode? child, |  | ||||||
|   }) : _isEnd = false { |  | ||||||
|     _parent = parent ?? this; |  | ||||||
|     _child = child ?? this; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   MiddlewareNode._end(this.pipeline) : _isEnd = true { |  | ||||||
|     _child = this; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   MiddlewareNode._begin(this.pipeline) : _isEnd = true { |  | ||||||
|     _parent = this; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /// Creates a new [MiddlewareNode] right **before** this in [pipeline] |  | ||||||
|   MiddlewareNode insertBefore(Middleware middleware) { |  | ||||||
|     if (isBegin) { |  | ||||||
|       throw StateError( |  | ||||||
|         'A MiddlewareNode cannot be inserted ' |  | ||||||
|         'before begin MiddlewareNode', |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|     final newMiddlewareNode = |  | ||||||
|         MiddlewareNode._(pipeline, middleware, parent: _parent, child: this); |  | ||||||
|     _parent._child = newMiddlewareNode; |  | ||||||
|     _parent = newMiddlewareNode; |  | ||||||
|     pipeline._length++; |  | ||||||
|     return newMiddlewareNode; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /// Creates a new [MiddlewareNode] right **after** this in [pipeline] |  | ||||||
|   MiddlewareNode insertAfter(Middleware middleware) { |  | ||||||
|     if (isEnd) { |  | ||||||
|       throw StateError( |  | ||||||
|         'A MiddlewareNode cannot be inserted ' |  | ||||||
|         'after end MiddlewareNode', |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|     final newMiddlewareNode = |  | ||||||
|         MiddlewareNode._(pipeline, middleware, parent: this, child: _child); |  | ||||||
|     _child._parent = newMiddlewareNode; |  | ||||||
|     _child = newMiddlewareNode; |  | ||||||
|     pipeline._length++; |  | ||||||
|     return newMiddlewareNode; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   MiddlewareNode remove() { |  | ||||||
|     if (_isEnd) throw StateError('Cannot remove end MiddlewareNode'); |  | ||||||
|     _child._parent = _parent; |  | ||||||
|     _parent._child = _child; |  | ||||||
|     pipeline._length--; |  | ||||||
|     return child; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,31 +1,16 @@ | |||||||
| // Copyright (C) 2022 WYATT GROUP | // Copyright (C) 2022 WYATT GROUP | ||||||
| // Please see the AUTHORS file for details. | // Please see the AUTHORS file for details. | ||||||
| // | //  | ||||||
| // This program is free software: you can redistribute it and/or modify | // 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 | // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | // the Free Software Foundation, either version 3 of the License, or | ||||||
| // any later version. | // any later version. | ||||||
| // | //  | ||||||
| // This program is distributed in the hope that it will be useful, | // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | // GNU General Public License for more details. | ||||||
| // | //  | ||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; |  | ||||||
| 
 |  | ||||||
| class ImplementedBaseClient extends BaseClient { |  | ||||||
|   final Client inner; |  | ||||||
| 
 |  | ||||||
|   ImplementedBaseClient({ |  | ||||||
|     Client? inner, |  | ||||||
|   }) : inner = inner ?? Client(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |  | ||||||
|     return inner.send(request); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -0,0 +1,59 @@ | |||||||
|  | // 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'; | ||||||
|  | 
 | ||||||
|  | class BasicAuthMiddleware with OnRequestMiddleware implements Middleware { | ||||||
|  |   String? username; | ||||||
|  |   String? password; | ||||||
|  |   final String authenticationHeader; | ||||||
|  | 
 | ||||||
|  |   BasicAuthMiddleware({ | ||||||
|  |     this.username, | ||||||
|  |     this.password, | ||||||
|  |     this.authenticationHeader = HeaderKeys.authorization, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String getName() => 'BasicAuth'; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<MiddlewareRequest> onRequest( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareRequest request, | ||||||
|  |   ) async { | ||||||
|  |     if (username == null || password == null) { | ||||||
|  |       return request; | ||||||
|  |     } | ||||||
|  |     print( | ||||||
|  |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> Basic: ${base64Encode(utf8.encode('$username:$password'))}', | ||||||
|  |     ); | ||||||
|  |     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; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -16,33 +16,34 @@ | |||||||
| 
 | 
 | ||||||
| import 'dart:convert'; | 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/models/middleware_request.dart'; | ||||||
| import 'package:wyatt_http_client/src/pipeline.dart'; |  | ||||||
| 
 | 
 | ||||||
| class BodyToJsonMiddleware extends Middleware { | class BodyToJsonMiddleware with OnRequestMiddleware implements Middleware { | ||||||
|   @override |   @override | ||||||
|   String getName() => 'BodyToJson'; |   String getName() => 'BodyToJson'; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<MiddlewareRequest> onRequest(MiddlewareRequest request) { |   Future<MiddlewareRequest> onRequest( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareRequest request, | ||||||
|  |   ) async { | ||||||
|     print( |     print( | ||||||
|       '${getName()}::OnRequest: transforms body in json if Map then update ' |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> Transforms body in json if Map then update ' | ||||||
|       'headers with right content-type', |       'headers with right content-type', | ||||||
|     ); |     ); | ||||||
|     var newReq = request.unfreezedRequest; |  | ||||||
|     final mutation = { |     final mutation = { | ||||||
|       'content-type': 'application/json; charset=utf-8', |       'content-type': 'application/json; charset=utf-8', | ||||||
|     }; |     }; | ||||||
|     if (newReq.body is Map) { |     if (request.body is Map) { | ||||||
|       Map<String, String>? headers = newReq.headers; |       final Map<String, String> headers = request.headers..addAll(mutation); | ||||||
|       if (headers != null) { |       request.modifyRequest( | ||||||
|         headers.addAll(mutation); |         request.unfreezedRequest | ||||||
|       } else { |             .copyWith(headers: headers, body: jsonEncode(request.body)), | ||||||
|         headers = mutation; |       ); | ||||||
|       } |  | ||||||
|       newReq = newReq.copyWith(body: jsonEncode(newReq.body), headers: headers); |  | ||||||
|       request.updateUnfreezedRequest(newReq); |  | ||||||
|     } |     } | ||||||
|     return super.onRequest(request); |     return request; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,9 +14,9 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | import 'package:wyatt_http_client/src/middleware.dart'; | ||||||
| 
 | 
 | ||||||
| class DefaultMiddleware extends Middleware { | class DefaultMiddleware implements Middleware { | ||||||
|   @override |   @override | ||||||
|   String getName() => 'DefaultMiddleware'; |   String getName() => 'DefaultMiddleware'; | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,92 @@ | |||||||
|  | // 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'; | ||||||
|  | 
 | ||||||
|  | class DigestAuthMiddleware | ||||||
|  |     with OnRequestMiddleware, OnResponseMiddleware | ||||||
|  |     implements Middleware { | ||||||
|  |   final String username; | ||||||
|  |   final String password; | ||||||
|  |   final DigestAuth _digestAuth; | ||||||
|  |   final String authenticationHeader; | ||||||
|  |   final String wwwAuthenticateHeader; | ||||||
|  |   final HttpStatus unauthorized; | ||||||
|  | 
 | ||||||
|  |   DigestAuthMiddleware({ | ||||||
|  |     required this.username, | ||||||
|  |     required this.password, | ||||||
|  |     this.authenticationHeader = HeaderKeys.authorization, | ||||||
|  |     this.wwwAuthenticateHeader = HeaderKeys.wwwAuthenticate, | ||||||
|  |     this.unauthorized = HttpStatus.unauthorized, | ||||||
|  |   }) : _digestAuth = DigestAuth(username, password); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String getName() => 'DigestAuth'; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<MiddlewareRequest> onRequest( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareRequest request, | ||||||
|  |   ) async { | ||||||
|  |     print( | ||||||
|  |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> Digest ready: ${_digestAuth.isReady()}', | ||||||
|  |     ); | ||||||
|  |     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); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     print( | ||||||
|  |       '${getName()}::OnResponse\n' | ||||||
|  |       '>> Digest ready: ${_digestAuth.isReady()}', | ||||||
|  |     ); | ||||||
|  |     return response; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,30 +1,25 @@ | |||||||
| // Copyright (C) 2022 WYATT GROUP | // Copyright (C) 2022 WYATT GROUP | ||||||
| // Please see the AUTHORS file for details. | // Please see the AUTHORS file for details. | ||||||
| // | //  | ||||||
| // This program is free software: you can redistribute it and/or modify | // 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 | // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | // the Free Software Foundation, either version 3 of the License, or | ||||||
| // any later version. | // any later version. | ||||||
| // | //  | ||||||
| // This program is distributed in the hope that it will be useful, | // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | // GNU General Public License for more details. | ||||||
| // | //  | ||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | export 'access_token_auth_middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/authentication/interfaces/authentication_client.dart'; | export 'basic_auth_middleware.dart'; | ||||||
| 
 | export 'body_to_json_middleware.dart'; | ||||||
| abstract class UrlAuthenticationClient extends AuthenticationClient { | export 'default_middleware.dart'; | ||||||
|   UrlAuthenticationClient(super.inner); | export 'digest_auth_middleware.dart'; | ||||||
| 
 | export 'refresh_token_auth_middleware.dart'; | ||||||
|   BaseRequest modifyRequest(BaseRequest request) => request; | export 'simple_logger_middleware.dart'; | ||||||
| 
 | export 'unsafe_auth_middleware.dart'; | ||||||
|   @override | export 'uri_prefix_middleware.dart'; | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |  | ||||||
|     final newRequest = modifyRequest(request); |  | ||||||
|     return super.send(newRequest); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -0,0 +1,191 @@ | |||||||
|  | // 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>); | ||||||
|  | 
 | ||||||
|  | class RefreshTokenAuthMiddleware | ||||||
|  |     with OnRequestMiddleware, OnResponseMiddleware | ||||||
|  |     implements Middleware { | ||||||
|  |   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; | ||||||
|  | 
 | ||||||
|  |   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, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   @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 { | ||||||
|  |     print( | ||||||
|  |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> accessToken: $accessToken\n' | ||||||
|  |       '>> refreshToken: $refreshToken', | ||||||
|  |     ); | ||||||
|  |     // 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; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     print( | ||||||
|  |       '${getName()}::OnResponse\n' | ||||||
|  |       '>> accessToken: $accessToken\n' | ||||||
|  |       '>> 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; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,213 +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/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'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/authentication_methods.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>); |  | ||||||
| 
 |  | ||||||
| class RefreshTokenMiddleware extends Middleware { |  | ||||||
|   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 maxRetries; |  | ||||||
| 
 |  | ||||||
|   RefreshTokenMiddleware({ |  | ||||||
|     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.maxRetries = 3, |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   String getName() => 'RefreshToken'; |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<MiddlewareRequest> onRequest(MiddlewareRequest request) async { |  | ||||||
|     print( |  | ||||||
|       '${getName()}::OnRequest: accessToken: $accessToken', |  | ||||||
|     ); |  | ||||||
|     if (request.context.originalRequest?.unfreezedRequest.url == |  | ||||||
|         Uri.parse(authorizationEndpoint)) { |  | ||||||
|       return super.onRequest(request); |  | ||||||
|     } |  | ||||||
|     if (accessToken != null) { |  | ||||||
|       // Modify header with accessToken |  | ||||||
|       var newReq = request.unfreezedRequest; |  | ||||||
|       final mutation = { |  | ||||||
|         authenticationHeader: '$authenticationMethod $accessToken', |  | ||||||
|       }; |  | ||||||
|       Map<String, String>? headers = newReq.headers; |  | ||||||
|       if (headers != null) { |  | ||||||
|         headers.addAll(mutation); |  | ||||||
|       } else { |  | ||||||
|         headers = mutation; |  | ||||||
|       } |  | ||||||
|       newReq = newReq.copyWith(headers: headers); |  | ||||||
|       request.updateUnfreezedRequest(newReq); |  | ||||||
|       return super.onRequest(request); |  | ||||||
|     } |  | ||||||
|     if (refreshToken != null) { |  | ||||||
|       // Refresh accessToken with refreshToken before perform request |  | ||||||
|       final subPipeline = request.context.pipeline.fromUntil(this); |  | ||||||
|       final httpClient = MiddlewareClient( |  | ||||||
|         pipeline: subPipeline, |  | ||||||
|         inner: client, |  | ||||||
|       ); |  | ||||||
|       final Map<String, String> headers = { |  | ||||||
|         authenticationHeader: '$authenticationHeader $refreshToken', |  | ||||||
|       }; |  | ||||||
|       final response = |  | ||||||
|           await httpClient.get(Uri.parse(tokenEndpoint), headers: headers); |  | ||||||
|       final status = HttpStatus.from(response.statusCode); |  | ||||||
|       if (status.isSuccess()) { |  | ||||||
|         final body = jsonDecode(response.body) as Map<String, dynamic>; |  | ||||||
|         accessToken = accessTokenParser(body); |  | ||||||
| 
 |  | ||||||
|         // Then modify current request with accessToken |  | ||||||
|         var newReq = request.unfreezedRequest; |  | ||||||
|         final mutation = { |  | ||||||
|           authenticationHeader: '$authenticationMethod $accessToken', |  | ||||||
|         }; |  | ||||||
|         Map<String, String>? headers = newReq.headers; |  | ||||||
|         if (headers != null) { |  | ||||||
|           headers.addAll(mutation); |  | ||||||
|         } else { |  | ||||||
|           headers = mutation; |  | ||||||
|         } |  | ||||||
|         newReq = newReq.copyWith(headers: headers); |  | ||||||
|         request.updateUnfreezedRequest(newReq); |  | ||||||
|       } else { |  | ||||||
|         // Retry |  | ||||||
|         int retries = 0; |  | ||||||
|         while (retries < maxRetries) { |  | ||||||
|           final Map<String, String> headers = { |  | ||||||
|             authenticationHeader: '$authenticationHeader $refreshToken', |  | ||||||
|           }; |  | ||||||
|           final response = |  | ||||||
|               await httpClient.get(Uri.parse(tokenEndpoint), headers: headers); |  | ||||||
|           final status = HttpStatus.from(response.statusCode); |  | ||||||
|           if (status.isSuccess()) { |  | ||||||
|             final body = jsonDecode(response.body) as Map<String, dynamic>; |  | ||||||
|             accessToken = accessTokenParser(body); |  | ||||||
| 
 |  | ||||||
|             // Then modify current request with accessToken |  | ||||||
|             var newReq = request.unfreezedRequest; |  | ||||||
|             final mutation = { |  | ||||||
|               authenticationHeader: '$authenticationMethod $accessToken', |  | ||||||
|             }; |  | ||||||
|             Map<String, String>? headers = newReq.headers; |  | ||||||
|             if (headers != null) { |  | ||||||
|               headers.addAll(mutation); |  | ||||||
|             } else { |  | ||||||
|               headers = mutation; |  | ||||||
|             } |  | ||||||
|             newReq = newReq.copyWith(headers: headers); |  | ||||||
|             request.updateUnfreezedRequest(newReq); |  | ||||||
|             break; |  | ||||||
|           } |  | ||||||
|           retries++; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       return super.onRequest(request); |  | ||||||
|     } |  | ||||||
|     // Pass |  | ||||||
|     return super.onRequest(request); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<MiddlewareResponse> onResponse(MiddlewareResponse response) async { |  | ||||||
|     final res = await super.onResponse(response); |  | ||||||
|     final status = HttpStatus.from(res.httpResponse.statusCode); |  | ||||||
|     if (res.context.originalRequest?.unfreezedRequest.url == |  | ||||||
|         Uri.parse(authorizationEndpoint)) { |  | ||||||
|       if (status.isSuccess()) { |  | ||||||
|         final body = jsonDecode((res.httpResponse as 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; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       return res; |  | ||||||
|     } |  | ||||||
|     if (status == unauthorized) { |  | ||||||
|       print( |  | ||||||
|         '${getName()}::OnResponse: $unauthorized', |  | ||||||
|       ); |  | ||||||
|       // Refresh token then retry |  | ||||||
|       final subPipeline = res.context.pipeline.fromUntil(this); |  | ||||||
|       final httpClient = MiddlewareClient( |  | ||||||
|         pipeline: subPipeline, |  | ||||||
|         inner: client, |  | ||||||
|       ); |  | ||||||
|       final Map<String, String> headers = { |  | ||||||
|         authenticationHeader: '$authenticationHeader $refreshToken', |  | ||||||
|       }; |  | ||||||
|       final response = |  | ||||||
|           await httpClient.get(Uri.parse(tokenEndpoint), headers: headers); |  | ||||||
|       final refreshstatus = HttpStatus.from(response.statusCode); |  | ||||||
|       if (refreshstatus.isSuccess()) { |  | ||||||
|         print( |  | ||||||
|           '${getName()}::OnResponse: refresh successfuly', |  | ||||||
|         ); |  | ||||||
|         final body = jsonDecode(response.body) as Map<String, dynamic>; |  | ||||||
|         accessToken = accessTokenParser(body); |  | ||||||
| 
 |  | ||||||
|         // Then modify current request with accessToken |  | ||||||
|         final midReq = res.middlewareRequest; |  | ||||||
|         final newReq = midReq.httpRequest; |  | ||||||
|         final mutation = { |  | ||||||
|           authenticationHeader: '$authenticationMethod $accessToken', |  | ||||||
|         }; |  | ||||||
|         Map<String, String>? headers = newReq.headers; |  | ||||||
|         if (headers != null) { |  | ||||||
|           headers.addAll(mutation); |  | ||||||
|         } else { |  | ||||||
|           headers = mutation; |  | ||||||
|         } |  | ||||||
|         midReq.updateHttpRequest(headers: headers); |  | ||||||
|         final newRes = await httpClient.send(midReq.httpRequest); |  | ||||||
|         return res.copyWith(httpResponse: res as Response); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -14,31 +14,41 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // 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_request.dart'; | ||||||
| import 'package:wyatt_http_client/src/models/middleware_response.dart'; | import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||||
| import 'package:wyatt_http_client/src/pipeline.dart'; |  | ||||||
| 
 | 
 | ||||||
| class SimpleLoggerMiddleware extends Middleware { | class SimpleLoggerMiddleware | ||||||
|  |     with OnRequestMiddleware, OnResponseMiddleware | ||||||
|  |     implements Middleware { | ||||||
|   @override |   @override | ||||||
|   String getName() => 'SimpleLogger'; |   String getName() => 'SimpleLogger'; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<MiddlewareRequest> onRequest(MiddlewareRequest request) { |   Future<MiddlewareRequest> onRequest( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareRequest request, | ||||||
|  |   ) async { | ||||||
|     print( |     print( | ||||||
|       '${getName()}::OnRequest: ${request.httpRequest.method} ' |       '${getName()}::OnRequest\n' | ||||||
|       '${request.httpRequest.url}\n${request.unfreezedRequest.headers}' |       '>> ${request.method} ${request.url}\n' | ||||||
|       '\n>> ${request.unfreezedRequest.body}', |       '>> Headers: ${request.headers}\n>> Body: ${request.encodedBody}', | ||||||
|     ); |     ); | ||||||
|     return super.onRequest(request); |     return request; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<MiddlewareResponse> onResponse(MiddlewareResponse response) async { |   Future<MiddlewareResponse> onResponse( | ||||||
|     final res = await super.onResponse(response); |     MiddlewareContext context, | ||||||
|  |     MiddlewareResponse response, | ||||||
|  |   ) async { | ||||||
|     print( |     print( | ||||||
|       '${getName()}::OnResponse: ${res.httpResponse.statusCode} -> ' |       '${getName()}::OnResponse\n' | ||||||
|       'received ${res.httpResponse.contentLength} bytes', |       '>> Status: ${response.status.name.toUpperCase()}\n' | ||||||
|  |       '>> Length: ${response.contentLength ?? '0'} bytes', | ||||||
|  |       // '>> Body: ${response.body}', | ||||||
|     ); |     ); | ||||||
|     return res; |     return response; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,30 +14,43 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | import 'package:wyatt_http_client/src/middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/authentication/interfaces/url_authentication_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/utils/convert.dart'; | import 'package:wyatt_http_client/src/utils/convert.dart'; | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; |  | ||||||
| 
 | 
 | ||||||
| class UnsafeAuthenticationClient extends UrlAuthenticationClient { | class UnsafeAuthMiddleware with OnRequestMiddleware implements Middleware { | ||||||
|   final String username; |   String? username; | ||||||
|   final String password; |   String? password; | ||||||
| 
 | 
 | ||||||
|   final String usernameField; |   final String usernameField; | ||||||
|   final String passwordField; |   final String passwordField; | ||||||
| 
 | 
 | ||||||
|   UnsafeAuthenticationClient({ |   UnsafeAuthMiddleware({ | ||||||
|     required this.username, |     this.username, | ||||||
|     required this.password, |     this.password, | ||||||
|     this.usernameField = 'username', |     this.usernameField = 'username', | ||||||
|     this.passwordField = 'password', |     this.passwordField = 'password', | ||||||
|     BaseClient? inner, |   }); | ||||||
|   }) : super(inner); |  | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   BaseRequest modifyRequest(BaseRequest request) { |   String getName() => 'UnsafeAuth'; | ||||||
|     final url = | 
 | ||||||
|  |   @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.url + '?$usernameField=$username&$passwordField=$password'; | ||||||
|     return Utils.copyRequestWith(request, url: url); |     print( | ||||||
|  |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> Append: ?$usernameField=$username&$passwordField=$password', | ||||||
|  |     ); | ||||||
|  |     request.modifyRequest(request.unfreezedRequest.copyWith(url: uri)); | ||||||
|  |     return request; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -14,11 +14,12 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // 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_request.dart'; | ||||||
| import 'package:wyatt_http_client/src/pipeline.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; | import 'package:wyatt_http_client/src/utils/protocols.dart'; | ||||||
| 
 | 
 | ||||||
| class UriPrefixMiddleware extends Middleware { | class UriPrefixMiddleware with OnRequestMiddleware implements Middleware { | ||||||
|   final Protocols protocol; |   final Protocols protocol; | ||||||
|   final String? authority; |   final String? authority; | ||||||
| 
 | 
 | ||||||
| @ -31,11 +32,17 @@ class UriPrefixMiddleware extends Middleware { | |||||||
|   String getName() => 'UriPrefix'; |   String getName() => 'UriPrefix'; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<MiddlewareRequest> onRequest(MiddlewareRequest request) { |   Future<MiddlewareRequest> onRequest( | ||||||
|     final Uri uri = |     MiddlewareContext context, | ||||||
|         Uri.parse('${protocol.scheme}$authority${request.httpRequest.url}'); |     MiddlewareRequest request, | ||||||
|     print('${getName()}::OnRequest: ${request.httpRequest.url} -> $uri'); |   ) async { | ||||||
|     request.updateHttpRequest(url: uri); |     final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}'); | ||||||
|     return super.onRequest(request); |     print( | ||||||
|  |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> From: ${request.url}\n' | ||||||
|  |       '>> To: $uri', | ||||||
|  |     ); | ||||||
|  |     request.modifyRequest(request.unfreezedRequest.copyWith(url: uri)); | ||||||
|  |     return request; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,83 +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'; |  | ||||||
| 
 |  | ||||||
| mixin BodyTransformer on Client { |  | ||||||
|   Object? bodyMutator(Object? body); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> post( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) { |  | ||||||
|     return super.post( |  | ||||||
|       url, |  | ||||||
|       headers: headers, |  | ||||||
|       body: bodyMutator(body), |  | ||||||
|       encoding: encoding, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> put( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) { |  | ||||||
|     return super.put( |  | ||||||
|       url, |  | ||||||
|       headers: headers, |  | ||||||
|       body: bodyMutator(body), |  | ||||||
|       encoding: encoding, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> patch( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) { |  | ||||||
|     return super.patch( |  | ||||||
|       url, |  | ||||||
|       headers: headers, |  | ||||||
|       body: bodyMutator(body), |  | ||||||
|       encoding: encoding, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> delete( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) { |  | ||||||
|     return super.delete( |  | ||||||
|       url, |  | ||||||
|       headers: headers, |  | ||||||
|       body: bodyMutator(body), |  | ||||||
|       encoding: encoding, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,131 +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 'dart:typed_data'; |  | ||||||
| 
 |  | ||||||
| import 'package:http/http.dart'; |  | ||||||
| 
 |  | ||||||
| mixin HeadersTransformer on Client { |  | ||||||
|   Map<String, String>? headersMutator( |  | ||||||
|     Object? body, |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   ); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> head( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   }) { |  | ||||||
|     return super.head( |  | ||||||
|       url, |  | ||||||
|       headers: headersMutator(null, headers), |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> get( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   }) { |  | ||||||
|     return super.get( |  | ||||||
|       url, |  | ||||||
|       headers: headersMutator(null, headers), |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> post( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) { |  | ||||||
|     return super.post( |  | ||||||
|       url, |  | ||||||
|       headers: headersMutator(body, headers), |  | ||||||
|       body: body, |  | ||||||
|       encoding: encoding, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> put( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) { |  | ||||||
|     return super.put( |  | ||||||
|       url, |  | ||||||
|       headers: headersMutator(body, headers), |  | ||||||
|       body: body, |  | ||||||
|       encoding: encoding, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> patch( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) { |  | ||||||
|     return super.patch( |  | ||||||
|       url, |  | ||||||
|       headers: headersMutator(body, headers), |  | ||||||
|       body: body, |  | ||||||
|       encoding: encoding, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Response> delete( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     Object? body, |  | ||||||
|     Encoding? encoding, |  | ||||||
|   }) { |  | ||||||
|     return super.delete( |  | ||||||
|       url, |  | ||||||
|       headers: headersMutator(body, headers), |  | ||||||
|       body: body, |  | ||||||
|       encoding: encoding, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<String> read( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   }) { |  | ||||||
|     return super.read( |  | ||||||
|       url, |  | ||||||
|       headers: headersMutator(null, headers), |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<Uint8List> readBytes( |  | ||||||
|     Uri url, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   }) { |  | ||||||
|     return super.readBytes( |  | ||||||
|       url, |  | ||||||
|       headers: headersMutator(null, headers), |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,33 +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/implemented_base_client.dart'; |  | ||||||
| 
 |  | ||||||
| mixin Oauth2Transformer on ImplementedBaseClient { |  | ||||||
|   late final String authorizationEndpoint; |  | ||||||
|   late final String tokenEndpoint; |  | ||||||
|   String? accessToken; |  | ||||||
|   String? refreshToken; |  | ||||||
| 
 |  | ||||||
|   BaseRequest requestAuthenticator(BaseRequest request); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |  | ||||||
|     final req = requestAuthenticator(request); |  | ||||||
|     return super.send(req); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -15,34 +15,48 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // 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_request.dart'; | ||||||
| import 'package:wyatt_http_client/src/models/middleware_response.dart'; | import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | import 'package:wyatt_http_client/src/pipeline.dart'; | ||||||
| 
 | 
 | ||||||
| class MiddlewareContext { | class MiddlewareContext { | ||||||
|   Pipeline pipeline; |   Pipeline pipeline; | ||||||
|  |   MiddlewareClient client; | ||||||
|   MiddlewareRequest? originalRequest; |   MiddlewareRequest? originalRequest; | ||||||
|  |   MiddlewareRequest? lastRequest; | ||||||
|   MiddlewareResponse? originalResponse; |   MiddlewareResponse? originalResponse; | ||||||
|  |   MiddlewareResponse? lastResponse; | ||||||
| 
 | 
 | ||||||
|   MiddlewareContext({ |   MiddlewareContext({ | ||||||
|     required this.pipeline, |     required this.pipeline, | ||||||
|  |     required this.client, | ||||||
|     this.originalRequest, |     this.originalRequest, | ||||||
|  |     this.lastRequest, | ||||||
|     this.originalResponse, |     this.originalResponse, | ||||||
|  |     this.lastResponse, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   MiddlewareContext copyWith({ |   MiddlewareContext copyWith({ | ||||||
|     Pipeline? pipeline, |     Pipeline? pipeline, | ||||||
|  |     MiddlewareClient? client, | ||||||
|     MiddlewareRequest? originalRequest, |     MiddlewareRequest? originalRequest, | ||||||
|  |     MiddlewareRequest? lastRequest, | ||||||
|     MiddlewareResponse? originalResponse, |     MiddlewareResponse? originalResponse, | ||||||
|  |     MiddlewareResponse? lastResponse, | ||||||
|   }) { |   }) { | ||||||
|     return MiddlewareContext( |     return MiddlewareContext( | ||||||
|       pipeline: pipeline ?? this.pipeline, |       pipeline: pipeline ?? this.pipeline, | ||||||
|  |       client: client ?? this.client, | ||||||
|       originalRequest: originalRequest ?? this.originalRequest, |       originalRequest: originalRequest ?? this.originalRequest, | ||||||
|  |       lastRequest: lastRequest ?? this.lastRequest, | ||||||
|       originalResponse: originalResponse ?? this.originalResponse, |       originalResponse: originalResponse ?? this.originalResponse, | ||||||
|  |       lastResponse: lastResponse ?? this.lastResponse, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'MiddlewareContext(pipeline: $pipeline, ' |   String toString() { | ||||||
|       'originalRequest: $originalRequest, originalResponse: $originalResponse)'; |     return 'MiddlewareContext(pipeline: $pipeline, client: $client, originalRequest: $originalRequest, lastRequest: $lastRequest, originalResponse: $originalResponse, lastResponse: $lastResponse)'; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,83 +15,68 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | import 'dart:convert'; | ||||||
| 
 | 
 | ||||||
| import 'package:wyatt_http_client/src/models/middleware_context.dart'; | import 'package:http/http.dart'; | ||||||
| import 'package:wyatt_http_client/src/models/unfreezed_request.dart'; | import 'package:wyatt_http_client/src/models/unfreezed_request.dart'; | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; | import 'package:wyatt_http_client/src/utils/convert.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/utils/request_utils.dart'; | ||||||
| 
 | 
 | ||||||
| class MiddlewareRequest { | class MiddlewareRequest { | ||||||
|   UnfreezedRequest unfreezedRequest; |   UnfreezedRequest unfreezedRequest; | ||||||
|   Request httpRequest; |   Request _httpRequest; | ||||||
|   MiddlewareContext context; | 
 | ||||||
|  |   Request get request => _httpRequest; | ||||||
|  | 
 | ||||||
|  |   // Proxy | ||||||
|  |   String get method => _httpRequest.method; | ||||||
|  |   Uri get url => _httpRequest.url; | ||||||
|  |   Map<String, String> get headers => _httpRequest.headers; | ||||||
|  |   Encoding get encoding => _httpRequest.encoding; | ||||||
|  |   String get encodedBody => _httpRequest.body; | ||||||
|  |   Object? get body => unfreezedRequest.body; | ||||||
| 
 | 
 | ||||||
|   MiddlewareRequest({ |   MiddlewareRequest({ | ||||||
|     required this.unfreezedRequest, |     required this.unfreezedRequest, | ||||||
|     required this.httpRequest, |   }) : _httpRequest = Request(unfreezedRequest.method, unfreezedRequest.url); | ||||||
|     required this.context, |  | ||||||
|   }) { |  | ||||||
|     context = context.copyWith(originalRequest: this); |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   MiddlewareRequest copyWith({ |   MiddlewareRequest copyWith({ | ||||||
|     UnfreezedRequest? unfreezedRequest, |     UnfreezedRequest? unfreezedRequest, | ||||||
|     Request? httpRequest, |  | ||||||
|     MiddlewareContext? context, |  | ||||||
|   }) { |   }) { | ||||||
|     return MiddlewareRequest( |     return MiddlewareRequest( | ||||||
|       unfreezedRequest: unfreezedRequest ?? this.unfreezedRequest, |       unfreezedRequest: unfreezedRequest ?? this.unfreezedRequest, | ||||||
|       httpRequest: httpRequest ?? this.httpRequest, |  | ||||||
|       context: context ?? this.context, |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void updateUnfreezedRequest(UnfreezedRequest unfreezedRequest) { |   void modifyRequest(UnfreezedRequest unfreezedRequest) { | ||||||
|     final request = httpRequest; |     String? _body; | ||||||
|     if (unfreezedRequest.headers != null) { |  | ||||||
|       request.headers.addAll(unfreezedRequest.headers!); |  | ||||||
|     } |  | ||||||
|     if (unfreezedRequest.encoding != null) { |  | ||||||
|       request.encoding = unfreezedRequest.encoding!; |  | ||||||
|     } |  | ||||||
|     if (unfreezedRequest.body != null) { |     if (unfreezedRequest.body != null) { | ||||||
|       final body = unfreezedRequest.body; |       final body = unfreezedRequest.body; | ||||||
|       if (body is String) { |       if (body is String) { | ||||||
|         request.body = body; |         _body = body; | ||||||
|       } else if (body is List) { |       } else if (body is List) { | ||||||
|         request.bodyBytes = body.cast<int>(); |         _body = String.fromCharCodes(body.cast<int>()); | ||||||
|       } else if (body is Map) { |       } else if (body is Map) { | ||||||
|         request.bodyFields = body.cast<String, String>(); |         _body = Convert.mapToQuery(body.cast<String, String>()); | ||||||
|       } else { |  | ||||||
|         throw ArgumentError('Invalid request body "$body".'); |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     _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; |     this.unfreezedRequest = unfreezedRequest; | ||||||
|     httpRequest = request; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void updateHttpRequest({ |   void apply() { | ||||||
|     String? method, |     modifyRequest(unfreezedRequest); | ||||||
|     Uri? url, |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     int? maxRedirects, |  | ||||||
|     bool? followRedirects, |  | ||||||
|     bool? persistentConnection, |  | ||||||
|     String? body, |  | ||||||
|   }) { |  | ||||||
|     httpRequest = Utils.copyRequestWith( |  | ||||||
|       httpRequest, |  | ||||||
|       method: method, |  | ||||||
|       url: url, |  | ||||||
|       headers: headers, |  | ||||||
|       maxRedirects: maxRedirects, |  | ||||||
|       followRedirects: followRedirects, |  | ||||||
|       persistentConnection: persistentConnection, |  | ||||||
|       body: body, |  | ||||||
|     ) as Request; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'MiddlewareRequest(unfreezedRequest: ' |   String toString() => 'MiddlewareRequest(unfreezedRequest: $unfreezedRequest)'; | ||||||
|       '$unfreezedRequest, httpRequest: $httpRequest, context: $context)'; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,35 +16,37 @@ | |||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | import 'package:http/http.dart'; | ||||||
| import 'package:wyatt_http_client/src/models/middleware_context.dart'; | import 'package:wyatt_http_client/src/utils/http_status.dart'; | ||||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; |  | ||||||
| 
 | 
 | ||||||
| class MiddlewareResponse { | class MiddlewareResponse { | ||||||
|   BaseResponse httpResponse; |   BaseResponse httpResponse; | ||||||
|   MiddlewareRequest middlewareRequest; | 
 | ||||||
|   MiddlewareContext context; |   // Proxy | ||||||
|  |   int get statusCode => httpResponse.statusCode; | ||||||
|  |   HttpStatus get status => HttpStatus.from(statusCode); | ||||||
|  |   String get body { | ||||||
|  |     if (httpResponse is Response) { | ||||||
|  |       return (httpResponse as Response).body; | ||||||
|  |     } else { | ||||||
|  |       return ''; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   int? get contentLength => httpResponse.contentLength; | ||||||
|  |   Map<String, String> get headers => httpResponse.headers; | ||||||
| 
 | 
 | ||||||
|   MiddlewareResponse({ |   MiddlewareResponse({ | ||||||
|     required this.httpResponse, |     required this.httpResponse, | ||||||
|     required this.middlewareRequest, |   }); | ||||||
|     required this.context, |  | ||||||
|   }) { |  | ||||||
|     context = context.copyWith(originalResponse: this); |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   MiddlewareResponse copyWith({ |   MiddlewareResponse copyWith({ | ||||||
|     BaseResponse? httpResponse, |     BaseResponse? httpResponse, | ||||||
|     MiddlewareRequest? middlewareRequest, |  | ||||||
|     MiddlewareContext? context, |  | ||||||
|   }) { |   }) { | ||||||
|     return MiddlewareResponse( |     return MiddlewareResponse( | ||||||
|       httpResponse: httpResponse ?? this.httpResponse, |       httpResponse: httpResponse ?? this.httpResponse, | ||||||
|       middlewareRequest: middlewareRequest ?? this.middlewareRequest, |  | ||||||
|       context: context ?? this.context, |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'MiddlewareResponse(httpResponse: $httpResponse, ' |   String toString() => | ||||||
|       'middlewareRequest: $middlewareRequest, context: $context)'; |       'MiddlewareResponse(httpResponse: $httpResponse)'; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,27 +1,20 @@ | |||||||
| // Copyright (C) 2022 WYATT GROUP | // Copyright (C) 2022 WYATT GROUP | ||||||
| // Please see the AUTHORS file for details. | // Please see the AUTHORS file for details. | ||||||
| // | //  | ||||||
| // This program is free software: you can redistribute it and/or modify | // 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 | // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | // the Free Software Foundation, either version 3 of the License, or | ||||||
| // any later version. | // any later version. | ||||||
| // | //  | ||||||
| // This program is distributed in the hope that it will be useful, | // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | // GNU General Public License for more details. | ||||||
| // | //  | ||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | export 'middleware_context.dart'; | ||||||
| import 'package:wyatt_http_client/src/implemented_base_client.dart'; | export 'middleware_request.dart'; | ||||||
| 
 | export 'middleware_response.dart'; | ||||||
| mixin RequestTransformer on ImplementedBaseClient { | export 'unfreezed_request.dart'; | ||||||
|   BaseRequest requestMutator(BaseRequest request); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |  | ||||||
|     return super.send(requestMutator(request)); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -14,140 +14,94 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | 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_request.dart'; | ||||||
| import 'package:wyatt_http_client/src/models/middleware_response.dart'; | import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||||
| 
 | 
 | ||||||
| part 'middleware.dart'; |  | ||||||
| part 'middleware_node.dart'; |  | ||||||
| 
 |  | ||||||
| class Pipeline { | class Pipeline { | ||||||
|   int _length = 0; |   final List<Middleware> _middlewares; | ||||||
|   late final MiddlewareNode begin; |  | ||||||
|   late final MiddlewareNode end; |  | ||||||
| 
 | 
 | ||||||
|   MiddlewareNode get first => begin.child; |   int get length => _middlewares.length; | ||||||
|   MiddlewareNode get last => end.parent; |  | ||||||
|   bool get isEmpty => _length == 0; |  | ||||||
|   bool get isNotEmpty => !isEmpty; |  | ||||||
|   int get length => _length; |  | ||||||
| 
 | 
 | ||||||
|   Pipeline() { |   Pipeline() : _middlewares = <Middleware>[]; | ||||||
|     _initialize(); |   Pipeline.fromIterable(Iterable<Middleware> middlewares) | ||||||
|  |       : _middlewares = middlewares.toList(); | ||||||
|  | 
 | ||||||
|  |   /// Add a [Middleware] to this [Pipeline] | ||||||
|  |   Pipeline addMiddleware(Middleware middleware) { | ||||||
|  |     _middlewares.add(middleware); | ||||||
|  |     return this; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Pipeline.fromIterable(Iterable<Middleware?> list) { |   /// Create new [Pipeline] from the start or end to a specified [Middleware]. | ||||||
|     _initialize(); |   Pipeline sub( | ||||||
|     MiddlewareNode parent = begin; |     Middleware middleware, { | ||||||
|     for (final element in list) { |     bool include = false, | ||||||
|       if (element != null) { |     bool fromEnd = false, | ||||||
|         parent = parent.insertAfter(element); |   }) { | ||||||
|  |     final nodes = <Middleware>[]; | ||||||
|  |     final list = fromEnd ? _middlewares.reversed : _middlewares; | ||||||
|  |     for (final m in list) { | ||||||
|  |       if (m != middleware) { | ||||||
|  |         nodes.add(m); | ||||||
|       } |       } | ||||||
|     } |       if (m == middleware) { | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   Pipeline.from(Pipeline pipeline) |  | ||||||
|       : this.fromIterable(pipeline.middlewares.map((node) => node.middleware)); |  | ||||||
| 
 |  | ||||||
|   void _initialize() { |  | ||||||
|     _length = 0; |  | ||||||
|     begin = MiddlewareNode._begin(this); |  | ||||||
|     end = MiddlewareNode._end(this); |  | ||||||
|     begin._child = end; |  | ||||||
|     end._parent = begin; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   Iterable<MiddlewareNode> get middlewares sync* { |  | ||||||
|     for (var middleware = first; |  | ||||||
|         middleware != end; |  | ||||||
|         middleware = middleware.child) { |  | ||||||
|       yield middleware; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   int indexOf(MiddlewareNode node) { |  | ||||||
|     int i = -1; |  | ||||||
|     int j = -1; |  | ||||||
|     for (final element in middlewares) { |  | ||||||
|       j++; |  | ||||||
|       if (element == node) { |  | ||||||
|         i = j; |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return i; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   int indexOfChild(Middleware middleware) { |  | ||||||
|     int i = -1; |  | ||||||
|     int j = -1; |  | ||||||
|     for (final element in middlewares) { |  | ||||||
|       j++; |  | ||||||
|       if (element.middleware == middleware) { |  | ||||||
|         i = j; |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return i; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /// Create new [Pipeline] from first [Middleware] to the specified one. |  | ||||||
|   Pipeline fromUntil(Middleware middleware, {bool include = false}) { |  | ||||||
|     final nodes = <Middleware?>[]; |  | ||||||
|     for (final element in middlewares) { |  | ||||||
|       if (element.middleware != middleware) { |  | ||||||
|         nodes.add(element.middleware); |  | ||||||
|       } |  | ||||||
|       if (element.middleware == middleware) { |  | ||||||
|         if (include) { |         if (include) { | ||||||
|           nodes.add(element.middleware); |           nodes.add(m); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     return Pipeline.fromIterable(nodes); |     return Pipeline.fromIterable(fromEnd ? nodes.reversed : nodes); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Pipeline addMiddleware(Middleware middleware) { |   Future<MiddlewareRequest> onRequest( | ||||||
|     last.insertAfter(middleware); |     MiddlewareContext context, | ||||||
|     return this; |     MiddlewareRequest request, | ||||||
|   } |   ) async { | ||||||
| 
 |     print('\n\nNEW REQUEST\n'); | ||||||
|   void setClient(MiddlewareClient? client) { |     MiddlewareRequest req = request..apply(); | ||||||
|     for (var node = first; node != end; node = node.child) { |     MiddlewareContext ctx = context.copyWith(lastRequest: req); | ||||||
|       node.middleware?.httpClient = client; |     for (final middleware in _middlewares) { | ||||||
|     } |       if (middleware is OnRequestMiddleware) { | ||||||
|   } |         req = await (middleware as OnRequestMiddleware) | ||||||
| 
 |             .onRequest(ctx, request); | ||||||
|   Future<MiddlewareRequest> onRequest(MiddlewareRequest request) async { |         ctx = context.copyWith(lastRequest: req); | ||||||
|     MiddlewareRequest req = request; |       } | ||||||
|     for (var node = first; node != end; node = node.child) { |  | ||||||
|       req = await node.middleware?.onRequest(req) ?? req; |  | ||||||
|     } |     } | ||||||
|     return req; |     return req; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<MiddlewareResponse> onResponse(MiddlewareResponse response) async { |   Future<MiddlewareResponse> onResponse( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareResponse response, | ||||||
|  |   ) async { | ||||||
|  |     print('\n\nNEW RESPONSE\n'); | ||||||
|     MiddlewareResponse res = response; |     MiddlewareResponse res = response; | ||||||
|     for (var node = last; node != begin; node = node.parent) { |     MiddlewareContext ctx = context.copyWith(lastResponse: res); | ||||||
|       res = await node.middleware?.onResponse(res) ?? 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; |     return res; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   String getLogic() { |   @override | ||||||
|  |   String toString() { | ||||||
|     final req = <String>[]; |     final req = <String>[]; | ||||||
|     final res = <String>[]; |     final res = <String>[]; | ||||||
|     for (final m in middlewares) { |     for (final middleware in _middlewares) { | ||||||
|       req.add('${m.middleware}'); |       if (middleware is OnRequestMiddleware) { | ||||||
|       res.insert(0, '${m.middleware}'); |         req.add(middleware.getName()); | ||||||
|  |       } | ||||||
|  |       if (middleware is OnResponseMiddleware) { | ||||||
|  |         res.insert(0, middleware.getName()); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|     return '[Req] -> ${req.join(' -> ')}\n[Res] -> ${res.join(' -> ')}'; |     return '[Req] -> ${req.join(' -> ')}\n[Res] -> ${res.join(' -> ')}'; | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   String toString() { |  | ||||||
|     return getLogic(); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,190 +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/implemented_base_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/mixins/body_transformer.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/mixins/headers_transformer.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/mixins/request_transformer.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; |  | ||||||
| 
 |  | ||||||
| class RestClient extends ImplementedBaseClient |  | ||||||
|     with BodyTransformer, HeadersTransformer, RequestTransformer { |  | ||||||
|   final Protocols protocol; |  | ||||||
|   final String? authority; |  | ||||||
| 
 |  | ||||||
|   RestClient({ |  | ||||||
|     this.protocol = Protocols.https, |  | ||||||
|     this.authority = '', |  | ||||||
|     super.inner, |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Object? bodyMutator(Object? body) { |  | ||||||
|     print( |  | ||||||
|       'RestClient::bodyMutator -> encode in json if body is Map: ${body is Map}', |  | ||||||
|     ); |  | ||||||
|     if (body is Map) { |  | ||||||
|       return jsonEncode(body); |  | ||||||
|     } |  | ||||||
|     return body; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Map<String, String>? headersMutator( |  | ||||||
|     Object? body, |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   ) { |  | ||||||
|     print( |  | ||||||
|       'RestClient::headersMutator -> add json content-type if body is Map: ${body is Map}', |  | ||||||
|     ); |  | ||||||
|     final mutation = { |  | ||||||
|       'content-type': 'application/json; charset=utf-8', |  | ||||||
|     }; |  | ||||||
|     if (body is Map) { |  | ||||||
|       if (headers != null) { |  | ||||||
|         headers.addAll(mutation); |  | ||||||
|         return headers; |  | ||||||
|       } else { |  | ||||||
|         return mutation; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return headers; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   BaseRequest requestMutator(BaseRequest request) { |  | ||||||
|     print( |  | ||||||
|       'RestClient::requestMutator -> add prefix path: ${protocol.scheme}$authority', |  | ||||||
|     ); |  | ||||||
|     final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}'); |  | ||||||
|     return Utils.copyRequestWith(request, url: uri); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // class RestClient extends BaseClient { |  | ||||||
| //   final Protocols protocol; |  | ||||||
| //   final String? authority; |  | ||||||
| 
 |  | ||||||
| //   final Client _inner; |  | ||||||
| 
 |  | ||||||
| //   RestClient({ |  | ||||||
| //     this.protocol = Protocols.https, |  | ||||||
| //     this.authority = '', |  | ||||||
| //     Client? inner, |  | ||||||
| //   }) : _inner = inner ?? Client(); |  | ||||||
| 
 |  | ||||||
| //   String? forceJson(Object? body) { |  | ||||||
| //     String? b; |  | ||||||
| //     if (body != null && body is Map) { |  | ||||||
| //       b = jsonEncode(body); |  | ||||||
| //     } |  | ||||||
| //     return b; |  | ||||||
| //   } |  | ||||||
| 
 |  | ||||||
| //   Map<String, String>? forceJsonHeader( |  | ||||||
| //       Object? body, Map<String, String>? headers,) { |  | ||||||
| //     final Map<String, String> h = headers ?? {}; |  | ||||||
| //     if (body != null && body is Map) { |  | ||||||
| //       h['Content-Type'] = 'application/json'; |  | ||||||
| //     } |  | ||||||
| //     return h; |  | ||||||
| //   } |  | ||||||
| 
 |  | ||||||
| //   // @override |  | ||||||
| //   // Future<Response> post( |  | ||||||
| //   //   Uri url, { |  | ||||||
| //   //   Map<String, String>? headers, |  | ||||||
| //   //   Object? body, |  | ||||||
| //   //   Encoding? encoding, |  | ||||||
| //   // }) { |  | ||||||
| //   //   final b = forceJson(body) ?? body; |  | ||||||
| //   //   final h = forceJsonHeader(body, headers) ?? headers; |  | ||||||
| //   //   print(b); |  | ||||||
| //   //   print(h); |  | ||||||
| //   //   return super.post( |  | ||||||
| //   //     url, |  | ||||||
| //   //     headers: h, |  | ||||||
| //   //     body: b, |  | ||||||
| //   //     encoding: encoding, |  | ||||||
| //   //   ); |  | ||||||
| //   // } |  | ||||||
| 
 |  | ||||||
| //   @override |  | ||||||
| //   Future<Response> put( |  | ||||||
| //     Uri url, { |  | ||||||
| //     Map<String, String>? headers, |  | ||||||
| //     Object? body, |  | ||||||
| //     Encoding? encoding, |  | ||||||
| //   }) { |  | ||||||
| //     final b = forceJson(body) ?? body; |  | ||||||
| //     final h = forceJsonHeader(body, headers) ?? headers; |  | ||||||
| //     return super.put( |  | ||||||
| //       url, |  | ||||||
| //       headers: h, |  | ||||||
| //       body: b, |  | ||||||
| //       encoding: encoding, |  | ||||||
| //     ); |  | ||||||
| //   } |  | ||||||
| 
 |  | ||||||
| //   @override |  | ||||||
| //   Future<Response> patch( |  | ||||||
| //     Uri url, { |  | ||||||
| //     Map<String, String>? headers, |  | ||||||
| //     Object? body, |  | ||||||
| //     Encoding? encoding, |  | ||||||
| //   }) { |  | ||||||
| //     final b = forceJson(body) ?? body; |  | ||||||
| //     final h = forceJsonHeader(body, headers) ?? headers; |  | ||||||
| //     return super.patch( |  | ||||||
| //       url, |  | ||||||
| //       headers: h, |  | ||||||
| //       body: b, |  | ||||||
| //       encoding: encoding, |  | ||||||
| //     ); |  | ||||||
| //   } |  | ||||||
| 
 |  | ||||||
| //   @override |  | ||||||
| //   Future<Response> delete( |  | ||||||
| //     Uri url, { |  | ||||||
| //     Map<String, String>? headers, |  | ||||||
| //     Object? body, |  | ||||||
| //     Encoding? encoding, |  | ||||||
| //   }) { |  | ||||||
| //     final b = forceJson(body) ?? body; |  | ||||||
| //     final h = forceJsonHeader(body, headers) ?? headers; |  | ||||||
| //     return super.delete( |  | ||||||
| //       url, |  | ||||||
| //       headers: h, |  | ||||||
| //       body: b, |  | ||||||
| //       encoding: encoding, |  | ||||||
| //     ); |  | ||||||
| //   } |  | ||||||
| 
 |  | ||||||
| //   @override |  | ||||||
| //   Future<StreamedResponse> send(BaseRequest request) { |  | ||||||
| //     final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}'); |  | ||||||
| //     return _inner.send( |  | ||||||
| //       Utils.copyRequestWith( |  | ||||||
| //         request, |  | ||||||
| //         url: uri, |  | ||||||
| //       ), |  | ||||||
| //     ); |  | ||||||
| //   } |  | ||||||
| // } |  | ||||||
| @ -14,6 +14,8 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
| class Convert { | class Convert { | ||||||
|   static String toHex(List<int> bytes, {bool upperCase = false}) { |   static String toHex(List<int> bytes, {bool upperCase = false}) { | ||||||
|     final buffer = StringBuffer(); |     final buffer = StringBuffer(); | ||||||
| @ -29,6 +31,15 @@ class Convert { | |||||||
|       return buffer.toString(); |       return buffer.toString(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   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 { | extension UriX on Uri { | ||||||
|  | |||||||
| @ -14,22 +14,23 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | import 'dart:core'; | ||||||
| import 'package:wyatt_http_client/src/authentication/interfaces/header_authentication_client.dart'; | import 'dart:math'; | ||||||
| 
 | 
 | ||||||
| typedef TokenParser = String Function(Map<String, dynamic>); | abstract class Delay { | ||||||
|  |   static Duration getRetryDelay(int attempt) { | ||||||
|  |     assert(attempt >= 0, 'attempt cannot be negative'); | ||||||
|  |     if (attempt <= 0) { | ||||||
|  |       return Duration.zero; | ||||||
|  |     } | ||||||
|  |     final rand = Random(); | ||||||
|  |     final Duration delayFactor = const Duration(milliseconds: 200); | ||||||
|  |     final double randomizationFactor = 0.25; | ||||||
|  |     final Duration maxDelay = const Duration(seconds: 30); | ||||||
| 
 | 
 | ||||||
| abstract class Oauth2Client extends HeaderAuthenticationClient { |     final rf = randomizationFactor * (rand.nextDouble() * 2 - 1) + 1; | ||||||
|   Oauth2Client(super.inner); |     final exp = min(attempt, 31); // prevent overflows. | ||||||
| 
 |     final delay = delayFactor * pow(2.0, exp) * rf; | ||||||
|   Future<Response?> refresh() { |     return delay < maxDelay ? delay : maxDelay; | ||||||
|     return Future.value(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   Future<Response> authorize( |  | ||||||
|     Map<String, dynamic> body, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   }) { |  | ||||||
|     return Future<Response>.value(); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -18,6 +18,5 @@ enum Protocols { | |||||||
|   http, |   http, | ||||||
|   https; |   https; | ||||||
| 
 | 
 | ||||||
|   String get name => toString().split('.').last; |  | ||||||
|   String get scheme => '$name://'; |   String get scheme => '$name://'; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										89
									
								
								packages/wyatt_http_client/lib/src/utils/request_utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								packages/wyatt_http_client/lib/src/utils/request_utils.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | |||||||
|  | // 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'; | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   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; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static BaseRequest copyRequest(BaseRequest original) { | ||||||
|  |     if (original is Request) { | ||||||
|  |       return _copyNormalRequest(original); | ||||||
|  |     } else { | ||||||
|  |       throw UnimplementedError( | ||||||
|  |         'Cannot handle requests of type ${original.runtimeType}', | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,89 +1,23 @@ | |||||||
| // Copyright (C) 2022 WYATT GROUP | // Copyright (C) 2022 WYATT GROUP | ||||||
| // Please see the AUTHORS file for details. | // Please see the AUTHORS file for details. | ||||||
| // | //  | ||||||
| // This program is free software: you can redistribute it and/or modify | // 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 | // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | // the Free Software Foundation, either version 3 of the License, or | ||||||
| // any later version. | // any later version. | ||||||
| // | //  | ||||||
| // This program is distributed in the hope that it will be useful, | // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | // GNU General Public License for more details. | ||||||
| // | //  | ||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | export 'authentication_methods.dart'; | ||||||
| 
 | export 'digest_auth.dart'; | ||||||
| abstract class Utils { | export 'header_keys.dart'; | ||||||
|   static Request _copyNormalRequest(Request original) { | export 'http_methods.dart'; | ||||||
|     final request = Request(original.method, original.url) | export 'http_status.dart'; | ||||||
|       ..followRedirects = original.followRedirects | export 'protocols.dart'; | ||||||
|       ..headers.addAll(original.headers) | export 'request_utils.dart'; | ||||||
|       ..maxRedirects = original.maxRedirects |  | ||||||
|       ..persistentConnection = original.persistentConnection |  | ||||||
|       ..body = original.body; |  | ||||||
| 
 |  | ||||||
|     return request; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   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; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   static BaseRequest copyRequest(BaseRequest original) { |  | ||||||
|     if (original is Request) { |  | ||||||
|       return _copyNormalRequest(original); |  | ||||||
|     } else { |  | ||||||
|       throw UnimplementedError( |  | ||||||
|         'Cannot handle requests of type ${original.runtimeType}', |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   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}', |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,17 +1,24 @@ | |||||||
| // Copyright (C) 2022 WYATT GROUP | // Copyright (C) 2022 WYATT GROUP | ||||||
| // Please see the AUTHORS file for details. | // Please see the AUTHORS file for details. | ||||||
| //  | // | ||||||
| // This program is free software: you can redistribute it and/or modify | // 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 | // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | // the Free Software Foundation, either version 3 of the License, or | ||||||
| // any later version. | // any later version. | ||||||
| //  | // | ||||||
| // This program is distributed in the hope that it will be useful, | // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | // GNU General Public License for more details. | ||||||
| //  | // | ||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| library wyatt_http_client; | 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'; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user