Feature/middlewares #9
| @ -1,39 +1,182 @@ | ||||
| <!--  | ||||
| This README describes the package. If you publish this package to pub.dev, | ||||
| this README's contents appear on the landing page for your package. | ||||
| <!-- | ||||
|  * Copyright (C) 2022 WYATT GROUP | ||||
|  * Please see the AUTHORS file for details. | ||||
| 
 | ||||
| For information about how to write a good package README, see the guide for | ||||
| [writing package pages](https://dart.dev/guides/libraries/writing-package-pages).  | ||||
|  * 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. | ||||
| 
 | ||||
| For general information about developing packages, see the Dart guide for | ||||
| [creating packages](https://dart.dev/guides/libraries/create-library-packages) | ||||
| and the Flutter guide for | ||||
| [developing packages and plugins](https://flutter.dev/developing-packages).  | ||||
|  * 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/>. | ||||
| --> | ||||
| 
 | ||||
| TODO: Put a short description of the package here that helps potential users | ||||
| know whether this package might be useful for them. | ||||
| # Dart - HTTP Client | ||||
| 
 | ||||
| ## 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 | ||||
| 
 | ||||
| TODO: List prerequisites and provide or point to information on how to | ||||
| start using the package. | ||||
| Simply add wyatt_http_client in pubspec.yaml, then | ||||
| 
 | ||||
| ```dart | ||||
| import 'package:wyatt_http_client/wyatt_http_client.dart'; | ||||
| ``` | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| TODO: Include short and useful examples for package users. Add longer examples | ||||
| to `/example` folder.  | ||||
| Firstly you have to understand **Middleware** and **Pipeline** concepts. | ||||
| 
 | ||||
| In `wyatt_http_client` a middleware is an object where requests and responses  | ||||
| pass through. And a pipeline is basicaly a list of middlewares. | ||||
| 
 | ||||
| In a pipeline with middlewares A and B, if request pass through A, then B,  | ||||
| the response will pass through B then A. | ||||
| 
 | ||||
| > You can `print(pipeline)` to get full process order of a pipeline. | ||||
| 
 | ||||
| For example, if you want to log every request, and simplify an url you can use provided `SimpleLogger` and `UriPrefix` . | ||||
| 
 | ||||
| ```dart | ||||
| 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  | ||||
| from the package authors, and more. | ||||
| ``` | ||||
| [Req] -> UriPrefix -> SimpleLogger | ||||
| [Res] -> SimpleLogger | ||||
| ``` | ||||
| 
 | ||||
| > The `response` doesn't pass through `UriPrefix` because it's an `OnRequestMiddleware` only. | ||||
| 
 | ||||
| And you can create a client. | ||||
| 
 | ||||
| ```dart | ||||
| final client = MiddlewareClient(pipeline: pipeline); | ||||
| ``` | ||||
| 
 | ||||
| At this point you can use `client` like every Client from `package:http/http.dart` . | ||||
| 
 | ||||
| ## Recipes | ||||
| 
 | ||||
| ### Rest API with URL Authentication | ||||
| 
 | ||||
| Let's build a client for a REST API where the (bad) authentication is through the URL. | ||||
| We need some middlewares: | ||||
| 
 | ||||
| * SimpleLogger, to log every request and response (useful for debug). | ||||
| * BodyToJson, to automaticaly transform Map object to JSON. | ||||
| * UriPrefix, to simplify the build of an API Object (save protocol and API prefix). | ||||
| * UnsafeAuth, to use url based authentication. | ||||
| 
 | ||||
| Let's start by creating the Pipeline: | ||||
| 
 | ||||
| ```dart | ||||
| final Pipeline pipeline = Pipeline() | ||||
|     .addMiddleware( | ||||
|         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:io'; | ||||
| 
 | ||||
| import 'package:wyatt_http_client/src/authentication/basic_authentication_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'; | ||||
| import 'package:wyatt_http_client/wyatt_http_client.dart'; | ||||
| 
 | ||||
| String lastToken = ''; | ||||
| int token = 0; | ||||
| @ -42,7 +35,7 @@ Future<void> handleBasic(HttpRequest req) async { | ||||
| 
 | ||||
| Future<void> handleBasicNegotiate(HttpRequest req) async { | ||||
|   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"'); | ||||
|     print(req.response.headers.value('WWW-Authenticate')); | ||||
|     return req.response.close(); | ||||
| @ -56,7 +49,7 @@ Future<void> handleBearer(HttpRequest req) async { | ||||
| 
 | ||||
| Future<void> handleDigest(HttpRequest req) async { | ||||
|   if (req.headers.value('Authorization') == null) { | ||||
|     req.response.statusCode = HttpStatus.unauthorized; | ||||
|     req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|     req.response.headers.set( | ||||
|       'WWW-Authenticate', | ||||
|       'Digest realm="Wyatt", ' | ||||
| @ -110,7 +103,7 @@ Future<void> handleOauth2RefreshToken(HttpRequest req) async { | ||||
|         return req.response.close(); | ||||
|       } else { | ||||
|         lastToken = receivedToken; | ||||
|         req.response.statusCode = HttpStatus.unauthorized; | ||||
|         req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|         return req.response.close(); | ||||
|       } | ||||
|     default: | ||||
| @ -160,13 +153,13 @@ Future<void> server() async { | ||||
|           print('Authorized'); | ||||
|           error = 0; | ||||
|         } else { | ||||
|           request.response.statusCode = HttpStatus.unauthorized; | ||||
|           request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|         } | ||||
|         break; | ||||
|       case '/test/oauth2-test-timeout': | ||||
|         error++; | ||||
|         print('Error $error'); | ||||
|         request.response.statusCode = HttpStatus.unauthorized; | ||||
|         request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|         break; | ||||
|       case '/test/oauth2-login': | ||||
|         if (request.method == 'POST') { | ||||
| @ -189,12 +182,12 @@ Future<void> server() async { | ||||
|         } | ||||
|         break; | ||||
|       case '/test/oauth2-refresh-error': | ||||
|         request.response.statusCode = HttpStatus.unauthorized; | ||||
|         request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||
|         break; | ||||
| 
 | ||||
|       default: | ||||
|         print(' => Unknown path or method'); | ||||
|         request.response.statusCode = HttpStatus.notFound; | ||||
|         request.response.statusCode = HttpStatus.notFound.statusCode; | ||||
|     } | ||||
|     request.response.close(); | ||||
|     print('===================='); | ||||
| @ -204,73 +197,98 @@ Future<void> server() async { | ||||
| Future<void> main() async { | ||||
|   unawaited(server()); | ||||
|   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 | ||||
|   final basic = BasicAuthenticationClient( | ||||
|   final basicAuth = BasicAuthMiddleware( | ||||
|     username: 'username', | ||||
|     password: 'password', | ||||
|     inner: restClient, | ||||
|   ); | ||||
|   final basic = MiddlewareClient( | ||||
|     pipeline: Pipeline.fromIterable([ | ||||
|       uriPrefix, | ||||
|       basicAuth, | ||||
|       logger, | ||||
|     ]), | ||||
|   ); | ||||
|   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 | ||||
|   final digest = DigestAuthenticationClient( | ||||
|   final digestAuth = DigestAuthMiddleware( | ||||
|     username: 'Mufasa', | ||||
|     password: 'Circle Of Life', | ||||
|     inner: restClient, | ||||
|   ); | ||||
|   final digest = MiddlewareClient( | ||||
|     pipeline: Pipeline.fromIterable([ | ||||
|       uriPrefix, | ||||
|       digestAuth, | ||||
|       logger, | ||||
|     ]), | ||||
|   ); | ||||
|   await digest.get(Uri.parse('/test/digest-test')); | ||||
| 
 | ||||
|   // Bearer | ||||
|   final bearer = BearerAuthenticationClient( | ||||
|     token: 'access-token-test', | ||||
|     inner: restClient, | ||||
|   ); | ||||
|   await bearer.get(Uri.parse('/test/bearer-test')); | ||||
|   // // Bearer | ||||
|   // final bearer = BearerAuthenticationClient( | ||||
|   //   token: 'access-token-test', | ||||
|   //   inner: restClient, | ||||
|   // ); | ||||
|   // await bearer.get(Uri.parse('/test/bearer-test')); | ||||
| 
 | ||||
|   // API Key | ||||
|   final apiKey = BearerAuthenticationClient( | ||||
|     token: 'awesome-api-key', | ||||
|     authenticationMethod: 'ApiKey', | ||||
|     inner: restClient, | ||||
|   ); | ||||
|   await apiKey.get(Uri.parse('/test/apikey-test')); | ||||
|   // // API Key | ||||
|   // final apiKey = BearerAuthenticationClient( | ||||
|   //   token: 'awesome-api-key', | ||||
|   //   authenticationMethod: 'ApiKey', | ||||
|   //   inner: restClient, | ||||
|   // ); | ||||
|   // await apiKey.get(Uri.parse('/test/apikey-test')); | ||||
| 
 | ||||
|   // Unsafe URL | ||||
|   final unsafe = UnsafeAuthenticationClient( | ||||
|   final unsafeAuth = UnsafeAuthMiddleware( | ||||
|     username: 'Mufasa', | ||||
|     password: 'Circle Of Life', | ||||
|     inner: restClient, | ||||
|   ); | ||||
|   final unsafe = MiddlewareClient( | ||||
|     pipeline: Pipeline.fromIterable([ | ||||
|       uriPrefix, | ||||
|       unsafeAuth, | ||||
|       logger, | ||||
|     ]), | ||||
|   ); | ||||
|   await unsafe.get(Uri.parse('/test/unsafe-test')); | ||||
| 
 | ||||
|   // OAuth2 | ||||
|   final refreshToken = RefreshTokenClient( | ||||
|   final refreshTokenAuth = RefreshTokenAuthMiddleware( | ||||
|     authorizationEndpoint: '/test/oauth2-test?action=login', | ||||
|     tokenEndpoint: '/test/oauth2-test?action=refresh', | ||||
|     accessTokenParser: (body) => body['accessToken']! 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.authorize(<String, String>{ | ||||
|     'username': 'username', | ||||
|     'password': 'password', | ||||
|   }); | ||||
|   // Login | ||||
|   await refreshToken.post( | ||||
|     Uri.parse('/test/oauth2-test'), | ||||
|     body: <String, String>{ | ||||
|       'username': 'username', | ||||
|       'password': 'password', | ||||
|     }, | ||||
|   ); | ||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||
|   await refreshToken.refresh(); | ||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test?action=access-denied')); | ||||
|   // await refreshToken.refresh(); | ||||
|   // await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||
|   // await refreshToken.get(Uri.parse('/test/oauth2-test?action=access-denied')); | ||||
| 
 | ||||
|   exit(0); | ||||
| } | ||||
|  | ||||
| @ -19,7 +19,7 @@ import 'dart:convert'; | ||||
| 
 | ||||
| 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/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/uri_prefix_middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | ||||
| @ -354,7 +354,6 @@ class FastAPI { | ||||
| 
 | ||||
| void main(List<String> args) async { | ||||
|   final Pipeline pipeline = Pipeline() | ||||
|       .addMiddleware(SimpleLoggerMiddleware()) | ||||
|       .addMiddleware( | ||||
|         UriPrefixMiddleware( | ||||
|           protocol: Protocols.http, | ||||
| @ -363,39 +362,37 @@ void main(List<String> args) async { | ||||
|       ) | ||||
|       .addMiddleware(BodyToJsonMiddleware()) | ||||
|       .addMiddleware( | ||||
|         RefreshTokenMiddleware( | ||||
|         RefreshTokenAuthMiddleware( | ||||
|           authorizationEndpoint: '/api/v1/auth/sign-in-with-password', | ||||
|           tokenEndpoint: '/api/v1/auth/refresh', | ||||
|           accessTokenParser: (body) => body['access_token']! as String, | ||||
|           refreshTokenParser: (body) => body['refresh_token']! as String, | ||||
|           unauthorized: HttpStatus.forbidden, | ||||
|         ), | ||||
|       ); | ||||
|       ) | ||||
|       .addMiddleware(SimpleLoggerMiddleware()); | ||||
| 
 | ||||
|   print(pipeline.getLogic()); | ||||
|   print(pipeline); | ||||
|   final client = MiddlewareClient(pipeline: pipeline); | ||||
| 
 | ||||
|   final api = FastAPI( | ||||
|     client: client, | ||||
|   ); | ||||
| 
 | ||||
|   // await api.sendSignUpCode('git@pcl.ovh'); | ||||
|   // final verifiedAccount = await api.verifyCode( | ||||
|   //   VerifyCode( | ||||
|   //     email: 'git@pcl.ovh', | ||||
|   //     verificationCode: '000000000', | ||||
|   //     action: EmailVerificationAction.signUp, | ||||
|   //   ), | ||||
|   // ); | ||||
|   // print(verifiedAccount); | ||||
|   // final registeredAccount = await api.signUp( | ||||
|   //   SignUp(sessionId: verifiedAccount.sessionId ?? '', password: 'password'), | ||||
|   // ); | ||||
|   // print(registeredAccount); | ||||
|   await api.sendSignUpCode('git@pcl.ovh'); | ||||
|   final verifiedAccount = await api.verifyCode( | ||||
|     VerifyCode( | ||||
|       email: 'git@pcl.ovh', | ||||
|       verificationCode: '000000000', | ||||
|       action: EmailVerificationAction.signUp, | ||||
|     ), | ||||
|   ); | ||||
|   final registeredAccount = await api.signUp( | ||||
|     SignUp(sessionId: verifiedAccount.sessionId ?? '', password: 'password'), | ||||
|   ); | ||||
|   final signedInAccount = await api.signInWithPassword( | ||||
|     Login(email: 'git@pcl.ovh', password: 'password'), | ||||
|   ); | ||||
|   // print(signedInAccount); | ||||
|   final accountList = await api.getAccountList(); | ||||
|   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/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/unsafe_auth_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/utils/protocols.dart'; | ||||
| @ -117,8 +117,8 @@ import 'package:wyatt_http_client/src/utils/protocols.dart'; | ||||
| // } | ||||
| 
 | ||||
| Future<void> main(List<String> args) async { | ||||
|   final UnsafeAuthMiddleware auth = UnsafeAuthMiddleware(); | ||||
|   final Pipeline pipeline = Pipeline() | ||||
|       .addMiddleware(SimpleLoggerMiddleware()) | ||||
|       .addMiddleware( | ||||
|         UriPrefixMiddleware( | ||||
|           protocol: Protocols.http, | ||||
| @ -127,17 +127,33 @@ Future<void> main(List<String> args) async { | ||||
|       ) | ||||
|       .addMiddleware(BodyToJsonMiddleware()) | ||||
|       .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, | ||||
|         UnsafeAuthMiddleware( | ||||
|           username: 'wyatt', | ||||
|           password: 'motdepasse', | ||||
|         ), | ||||
|       ); | ||||
|       ) | ||||
|       .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 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'), | ||||
|     body: <String, String>{ | ||||
|       '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 | ||||
| // 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 { | ||||
|   /// The http [MiddlewareClient] used by this [Middleware] | ||||
|   MiddlewareClient? _client; | ||||
| 
 | ||||
|   String getName() => 'MiddlewareNode'; | ||||
| 
 | ||||
|   // ignore: avoid_setters_without_getters | ||||
|   set httpClient(MiddlewareClient? client) => _client = client; | ||||
| 
 | ||||
|   Client? get client => _client?.inner; | ||||
| 
 | ||||
|   Future<MiddlewareRequest> onRequest( | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     return request; | ||||
|   } | ||||
| 
 | ||||
|   Future<MiddlewareResponse> onResponse(MiddlewareResponse response) async { | ||||
|     return response; | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   String toString() { | ||||
|     return getName(); | ||||
|   } | ||||
| abstract class Middleware { | ||||
|   Middleware(); | ||||
|   String getName(); | ||||
| } | ||||
| 
 | ||||
| mixin OnRequestMiddleware { | ||||
|   Future<MiddlewareRequest> onRequest( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| mixin OnResponseMiddleware { | ||||
|   Future<MiddlewareResponse> onResponse( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareResponse response, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @ -33,7 +33,7 @@ class MiddlewareClient extends BaseClient { | ||||
|     Client? inner, | ||||
|   })  : pipeline = pipeline ?? Pipeline(), | ||||
|         inner = inner ?? Client() { | ||||
|     this.pipeline.setClient(this); | ||||
|     print('Using Pipeline:\n$pipeline'); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
| @ -100,27 +100,30 @@ class MiddlewareClient extends BaseClient { | ||||
|         body: body, | ||||
|         encoding: encoding, | ||||
|       ), | ||||
|       httpRequest: Request(method, url), | ||||
|       context: MiddlewareContext(pipeline: pipeline), | ||||
|     ); | ||||
|     final requestContext = MiddlewareContext( | ||||
|       pipeline: pipeline, | ||||
|       client: this, | ||||
|       originalRequest: originalRequest, | ||||
|     ); | ||||
| 
 | ||||
|     final modifiedRequest = await pipeline.onRequest( | ||||
|       requestContext, | ||||
|       originalRequest.copyWith(), | ||||
|     ); | ||||
| 
 | ||||
|     final res = await Response.fromStream( | ||||
|       await send(modifiedRequest.httpRequest), | ||||
|     ); | ||||
|     final response = await pipeline.onResponse( | ||||
|       MiddlewareResponse( | ||||
|         httpResponse: res, | ||||
|         middlewareRequest: modifiedRequest, | ||||
|         context: MiddlewareContext( | ||||
|           pipeline: pipeline, | ||||
|           originalRequest: originalRequest, | ||||
|         ), | ||||
|     final originalResponse = MiddlewareResponse( | ||||
|       httpResponse: await Response.fromStream( | ||||
|         await send(modifiedRequest.request), | ||||
|       ), | ||||
|     ); | ||||
| 
 | ||||
|     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 | ||||
| // 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'; | ||||
| 
 | ||||
| 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 '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/pipeline.dart'; | ||||
| 
 | ||||
| class BodyToJsonMiddleware extends Middleware { | ||||
| class BodyToJsonMiddleware with OnRequestMiddleware implements Middleware { | ||||
|   @override | ||||
|   String getName() => 'BodyToJson'; | ||||
| 
 | ||||
|   @override | ||||
|   Future<MiddlewareRequest> onRequest(MiddlewareRequest request) { | ||||
|   Future<MiddlewareRequest> onRequest( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     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', | ||||
|     ); | ||||
|     var newReq = request.unfreezedRequest; | ||||
|     final mutation = { | ||||
|       'content-type': 'application/json; charset=utf-8', | ||||
|     }; | ||||
|     if (newReq.body is Map) { | ||||
|       Map<String, String>? headers = newReq.headers; | ||||
|       if (headers != null) { | ||||
|         headers.addAll(mutation); | ||||
|       } else { | ||||
|         headers = mutation; | ||||
|       } | ||||
|       newReq = newReq.copyWith(body: jsonEncode(newReq.body), headers: headers); | ||||
|       request.updateUnfreezedRequest(newReq); | ||||
|     if (request.body is Map) { | ||||
|       final Map<String, String> headers = request.headers..addAll(mutation); | ||||
|       request.modifyRequest( | ||||
|         request.unfreezedRequest | ||||
|             .copyWith(headers: headers, body: jsonEncode(request.body)), | ||||
|       ); | ||||
|     } | ||||
|     return super.onRequest(request); | ||||
|     return request; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -14,9 +14,9 @@ | ||||
| // 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/pipeline.dart'; | ||||
| import 'package:wyatt_http_client/src/middleware.dart'; | ||||
| 
 | ||||
| class DefaultMiddleware extends Middleware { | ||||
| class DefaultMiddleware implements Middleware { | ||||
|   @override | ||||
|   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 | ||||
| // 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 UrlAuthenticationClient extends AuthenticationClient { | ||||
|   UrlAuthenticationClient(super.inner); | ||||
| 
 | ||||
|   BaseRequest modifyRequest(BaseRequest request) => request; | ||||
| 
 | ||||
|   @override | ||||
|   Future<StreamedResponse> send(BaseRequest request) { | ||||
|     final newRequest = modifyRequest(request); | ||||
|     return super.send(newRequest); | ||||
|   } | ||||
| } | ||||
| export 'access_token_auth_middleware.dart'; | ||||
| export 'basic_auth_middleware.dart'; | ||||
| export 'body_to_json_middleware.dart'; | ||||
| export 'default_middleware.dart'; | ||||
| export 'digest_auth_middleware.dart'; | ||||
| export 'refresh_token_auth_middleware.dart'; | ||||
| export 'simple_logger_middleware.dart'; | ||||
| export 'unsafe_auth_middleware.dart'; | ||||
| export 'uri_prefix_middleware.dart'; | ||||
| @ -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 | ||||
| // 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/pipeline.dart'; | ||||
| 
 | ||||
| class SimpleLoggerMiddleware extends Middleware { | ||||
| class SimpleLoggerMiddleware | ||||
|     with OnRequestMiddleware, OnResponseMiddleware | ||||
|     implements Middleware { | ||||
|   @override | ||||
|   String getName() => 'SimpleLogger'; | ||||
| 
 | ||||
|   @override | ||||
|   Future<MiddlewareRequest> onRequest(MiddlewareRequest request) { | ||||
|   Future<MiddlewareRequest> onRequest( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     print( | ||||
|       '${getName()}::OnRequest: ${request.httpRequest.method} ' | ||||
|       '${request.httpRequest.url}\n${request.unfreezedRequest.headers}' | ||||
|       '\n>> ${request.unfreezedRequest.body}', | ||||
|       '${getName()}::OnRequest\n' | ||||
|       '>> ${request.method} ${request.url}\n' | ||||
|       '>> Headers: ${request.headers}\n>> Body: ${request.encodedBody}', | ||||
|     ); | ||||
|     return super.onRequest(request); | ||||
|     return request; | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<MiddlewareResponse> onResponse(MiddlewareResponse response) async { | ||||
|     final res = await super.onResponse(response); | ||||
|   Future<MiddlewareResponse> onResponse( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareResponse response, | ||||
|   ) async { | ||||
|     print( | ||||
|       '${getName()}::OnResponse: ${res.httpResponse.statusCode} -> ' | ||||
|       'received ${res.httpResponse.contentLength} bytes', | ||||
|       '${getName()}::OnResponse\n' | ||||
|       '>> 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 | ||||
| // 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/url_authentication_client.dart'; | ||||
| import 'package:wyatt_http_client/src/middleware.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/convert.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; | ||||
| 
 | ||||
| class UnsafeAuthenticationClient extends UrlAuthenticationClient { | ||||
|   final String username; | ||||
|   final String password; | ||||
| class UnsafeAuthMiddleware with OnRequestMiddleware implements Middleware { | ||||
|   String? username; | ||||
|   String? password; | ||||
| 
 | ||||
|   final String usernameField; | ||||
|   final String passwordField; | ||||
| 
 | ||||
|   UnsafeAuthenticationClient({ | ||||
|     required this.username, | ||||
|     required this.password, | ||||
|   UnsafeAuthMiddleware({ | ||||
|     this.username, | ||||
|     this.password, | ||||
|     this.usernameField = 'username', | ||||
|     this.passwordField = 'password', | ||||
|     BaseClient? inner, | ||||
|   }) : super(inner); | ||||
|   }); | ||||
| 
 | ||||
|   @override | ||||
|   BaseRequest modifyRequest(BaseRequest request) { | ||||
|     final url = | ||||
|   String getName() => 'UnsafeAuth'; | ||||
| 
 | ||||
|   @override | ||||
|   Future<MiddlewareRequest> onRequest( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     if (username == null || password == null) { | ||||
|       return request; | ||||
|     } | ||||
|     final Uri uri = | ||||
|         request.url + '?$usernameField=$username&$passwordField=$password'; | ||||
|     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 | ||||
| // 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/pipeline.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; | ||||
| 
 | ||||
| class UriPrefixMiddleware extends Middleware { | ||||
| class UriPrefixMiddleware with OnRequestMiddleware implements Middleware { | ||||
|   final Protocols protocol; | ||||
|   final String? authority; | ||||
| 
 | ||||
| @ -31,11 +32,17 @@ class UriPrefixMiddleware extends Middleware { | ||||
|   String getName() => 'UriPrefix'; | ||||
| 
 | ||||
|   @override | ||||
|   Future<MiddlewareRequest> onRequest(MiddlewareRequest request) { | ||||
|     final Uri uri = | ||||
|         Uri.parse('${protocol.scheme}$authority${request.httpRequest.url}'); | ||||
|     print('${getName()}::OnRequest: ${request.httpRequest.url} -> $uri'); | ||||
|     request.updateHttpRequest(url: uri); | ||||
|     return super.onRequest(request); | ||||
|   Future<MiddlewareRequest> onRequest( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}'); | ||||
|     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 | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_http_client/src/middleware_client.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||
| import 'package:wyatt_http_client/src/pipeline.dart'; | ||||
| 
 | ||||
| class MiddlewareContext { | ||||
|   Pipeline pipeline; | ||||
|   MiddlewareClient client; | ||||
|   MiddlewareRequest? originalRequest; | ||||
|   MiddlewareRequest? lastRequest; | ||||
|   MiddlewareResponse? originalResponse; | ||||
|   MiddlewareResponse? lastResponse; | ||||
| 
 | ||||
|   MiddlewareContext({ | ||||
|     required this.pipeline, | ||||
|     required this.client, | ||||
|     this.originalRequest, | ||||
|     this.lastRequest, | ||||
|     this.originalResponse, | ||||
|     this.lastResponse, | ||||
|   }); | ||||
| 
 | ||||
|   MiddlewareContext copyWith({ | ||||
|     Pipeline? pipeline, | ||||
|     MiddlewareClient? client, | ||||
|     MiddlewareRequest? originalRequest, | ||||
|     MiddlewareRequest? lastRequest, | ||||
|     MiddlewareResponse? originalResponse, | ||||
|     MiddlewareResponse? lastResponse, | ||||
|   }) { | ||||
|     return MiddlewareContext( | ||||
|       pipeline: pipeline ?? this.pipeline, | ||||
|       client: client ?? this.client, | ||||
|       originalRequest: originalRequest ?? this.originalRequest, | ||||
|       lastRequest: lastRequest ?? this.lastRequest, | ||||
|       originalResponse: originalResponse ?? this.originalResponse, | ||||
|       lastResponse: lastResponse ?? this.lastResponse, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'MiddlewareContext(pipeline: $pipeline, ' | ||||
|       'originalRequest: $originalRequest, originalResponse: $originalResponse)'; | ||||
|   String toString() { | ||||
|     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 | ||||
| // 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/utils/utils.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/convert.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/request_utils.dart'; | ||||
| 
 | ||||
| class MiddlewareRequest { | ||||
|   UnfreezedRequest unfreezedRequest; | ||||
|   Request httpRequest; | ||||
|   MiddlewareContext context; | ||||
|   Request _httpRequest; | ||||
| 
 | ||||
|   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({ | ||||
|     required this.unfreezedRequest, | ||||
|     required this.httpRequest, | ||||
|     required this.context, | ||||
|   }) { | ||||
|     context = context.copyWith(originalRequest: this); | ||||
|   } | ||||
|   }) : _httpRequest = Request(unfreezedRequest.method, unfreezedRequest.url); | ||||
| 
 | ||||
|   MiddlewareRequest copyWith({ | ||||
|     UnfreezedRequest? unfreezedRequest, | ||||
|     Request? httpRequest, | ||||
|     MiddlewareContext? context, | ||||
|   }) { | ||||
|     return MiddlewareRequest( | ||||
|       unfreezedRequest: unfreezedRequest ?? this.unfreezedRequest, | ||||
|       httpRequest: httpRequest ?? this.httpRequest, | ||||
|       context: context ?? this.context, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   void updateUnfreezedRequest(UnfreezedRequest unfreezedRequest) { | ||||
|     final request = httpRequest; | ||||
|     if (unfreezedRequest.headers != null) { | ||||
|       request.headers.addAll(unfreezedRequest.headers!); | ||||
|     } | ||||
|     if (unfreezedRequest.encoding != null) { | ||||
|       request.encoding = unfreezedRequest.encoding!; | ||||
|     } | ||||
|   void modifyRequest(UnfreezedRequest unfreezedRequest) { | ||||
|     String? _body; | ||||
|     if (unfreezedRequest.body != null) { | ||||
|       final body = unfreezedRequest.body; | ||||
|       if (body is String) { | ||||
|         request.body = body; | ||||
|         _body = body; | ||||
|       } else if (body is List) { | ||||
|         request.bodyBytes = body.cast<int>(); | ||||
|         _body = String.fromCharCodes(body.cast<int>()); | ||||
|       } else if (body is Map) { | ||||
|         request.bodyFields = body.cast<String, String>(); | ||||
|       } else { | ||||
|         throw ArgumentError('Invalid request body "$body".'); | ||||
|         _body = Convert.mapToQuery(body.cast<String, String>()); | ||||
|       } | ||||
|     } | ||||
|     _httpRequest = RequestUtils.copyRequestWith( | ||||
|       _httpRequest, | ||||
|       method: unfreezedRequest.method, | ||||
|       url: unfreezedRequest.url, | ||||
|       headers: unfreezedRequest.headers, | ||||
|       body: _body, | ||||
|     ) as Request; | ||||
|     if (unfreezedRequest.encoding != null) { | ||||
|       _httpRequest.encoding = unfreezedRequest.encoding!; | ||||
|     } | ||||
|     this.unfreezedRequest = unfreezedRequest; | ||||
|     httpRequest = request; | ||||
|   } | ||||
| 
 | ||||
|   void updateHttpRequest({ | ||||
|     String? method, | ||||
|     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; | ||||
|   void apply() { | ||||
|     modifyRequest(unfreezedRequest); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'MiddlewareRequest(unfreezedRequest: ' | ||||
|       '$unfreezedRequest, httpRequest: $httpRequest, context: $context)'; | ||||
|   String toString() => 'MiddlewareRequest(unfreezedRequest: $unfreezedRequest)'; | ||||
| } | ||||
|  | ||||
| @ -16,35 +16,37 @@ | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||
| import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||
| import 'package:wyatt_http_client/src/utils/http_status.dart'; | ||||
| 
 | ||||
| class MiddlewareResponse { | ||||
|   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({ | ||||
|     required this.httpResponse, | ||||
|     required this.middlewareRequest, | ||||
|     required this.context, | ||||
|   }) { | ||||
|     context = context.copyWith(originalResponse: this); | ||||
|   } | ||||
|   }); | ||||
| 
 | ||||
|   MiddlewareResponse copyWith({ | ||||
|     BaseResponse? httpResponse, | ||||
|     MiddlewareRequest? middlewareRequest, | ||||
|     MiddlewareContext? context, | ||||
|   }) { | ||||
|     return MiddlewareResponse( | ||||
|       httpResponse: httpResponse ?? this.httpResponse, | ||||
|       middlewareRequest: middlewareRequest ?? this.middlewareRequest, | ||||
|       context: context ?? this.context, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'MiddlewareResponse(httpResponse: $httpResponse, ' | ||||
|       'middlewareRequest: $middlewareRequest, context: $context)'; | ||||
|   String toString() => | ||||
|       'MiddlewareResponse(httpResponse: $httpResponse)'; | ||||
| } | ||||
|  | ||||
| @ -1,27 +1,20 @@ | ||||
| // 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 RequestTransformer on ImplementedBaseClient { | ||||
|   BaseRequest requestMutator(BaseRequest request); | ||||
| 
 | ||||
|   @override | ||||
|   Future<StreamedResponse> send(BaseRequest request) { | ||||
|     return super.send(requestMutator(request)); | ||||
|   } | ||||
| } | ||||
| export 'middleware_context.dart'; | ||||
| export 'middleware_request.dart'; | ||||
| export 'middleware_response.dart'; | ||||
| export 'unfreezed_request.dart'; | ||||
| @ -14,140 +14,94 @@ | ||||
| // 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/middleware_client.dart'; | ||||
| 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'; | ||||
| 
 | ||||
| part 'middleware.dart'; | ||||
| part 'middleware_node.dart'; | ||||
| 
 | ||||
| class Pipeline { | ||||
|   int _length = 0; | ||||
|   late final MiddlewareNode begin; | ||||
|   late final MiddlewareNode end; | ||||
|   final List<Middleware> _middlewares; | ||||
| 
 | ||||
|   MiddlewareNode get first => begin.child; | ||||
|   MiddlewareNode get last => end.parent; | ||||
|   bool get isEmpty => _length == 0; | ||||
|   bool get isNotEmpty => !isEmpty; | ||||
|   int get length => _length; | ||||
|   int get length => _middlewares.length; | ||||
| 
 | ||||
|   Pipeline() { | ||||
|     _initialize(); | ||||
|   Pipeline() : _middlewares = <Middleware>[]; | ||||
|   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) { | ||||
|     _initialize(); | ||||
|     MiddlewareNode parent = begin; | ||||
|     for (final element in list) { | ||||
|       if (element != null) { | ||||
|         parent = parent.insertAfter(element); | ||||
|   /// Create new [Pipeline] from the start or end to a specified [Middleware]. | ||||
|   Pipeline sub( | ||||
|     Middleware middleware, { | ||||
|     bool include = false, | ||||
|     bool fromEnd = false, | ||||
|   }) { | ||||
|     final nodes = <Middleware>[]; | ||||
|     final list = fromEnd ? _middlewares.reversed : _middlewares; | ||||
|     for (final m in list) { | ||||
|       if (m != middleware) { | ||||
|         nodes.add(m); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   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 (m == middleware) { | ||||
|         if (include) { | ||||
|           nodes.add(element.middleware); | ||||
|           nodes.add(m); | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     return Pipeline.fromIterable(nodes); | ||||
|     return Pipeline.fromIterable(fromEnd ? nodes.reversed : nodes); | ||||
|   } | ||||
| 
 | ||||
|   Pipeline addMiddleware(Middleware middleware) { | ||||
|     last.insertAfter(middleware); | ||||
|     return this; | ||||
|   } | ||||
| 
 | ||||
|   void setClient(MiddlewareClient? client) { | ||||
|     for (var node = first; node != end; node = node.child) { | ||||
|       node.middleware?.httpClient = client; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   Future<MiddlewareRequest> onRequest(MiddlewareRequest request) async { | ||||
|     MiddlewareRequest req = request; | ||||
|     for (var node = first; node != end; node = node.child) { | ||||
|       req = await node.middleware?.onRequest(req) ?? req; | ||||
|   Future<MiddlewareRequest> onRequest( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareRequest request, | ||||
|   ) async { | ||||
|     print('\n\nNEW REQUEST\n'); | ||||
|     MiddlewareRequest req = request..apply(); | ||||
|     MiddlewareContext ctx = context.copyWith(lastRequest: req); | ||||
|     for (final middleware in _middlewares) { | ||||
|       if (middleware is OnRequestMiddleware) { | ||||
|         req = await (middleware as OnRequestMiddleware) | ||||
|             .onRequest(ctx, request); | ||||
|         ctx = context.copyWith(lastRequest: req); | ||||
|       } | ||||
|     } | ||||
|     return req; | ||||
|   } | ||||
| 
 | ||||
|   Future<MiddlewareResponse> onResponse(MiddlewareResponse response) async { | ||||
|   Future<MiddlewareResponse> onResponse( | ||||
|     MiddlewareContext context, | ||||
|     MiddlewareResponse response, | ||||
|   ) async { | ||||
|     print('\n\nNEW RESPONSE\n'); | ||||
|     MiddlewareResponse res = response; | ||||
|     for (var node = last; node != begin; node = node.parent) { | ||||
|       res = await node.middleware?.onResponse(res) ?? res; | ||||
|     MiddlewareContext ctx = context.copyWith(lastResponse: res); | ||||
|     for (final middleware in _middlewares.reversed) { | ||||
|       if (middleware is OnResponseMiddleware) { | ||||
|         res = await (middleware as OnResponseMiddleware) | ||||
|             .onResponse(ctx, response); | ||||
|         ctx = context.copyWith(lastResponse: res); | ||||
|       } | ||||
|     } | ||||
|     return res; | ||||
|   } | ||||
| 
 | ||||
|   String getLogic() { | ||||
|   @override | ||||
|   String toString() { | ||||
|     final req = <String>[]; | ||||
|     final res = <String>[]; | ||||
|     for (final m in middlewares) { | ||||
|       req.add('${m.middleware}'); | ||||
|       res.insert(0, '${m.middleware}'); | ||||
|     for (final middleware in _middlewares) { | ||||
|       if (middleware is OnRequestMiddleware) { | ||||
|         req.add(middleware.getName()); | ||||
|       } | ||||
|       if (middleware is OnResponseMiddleware) { | ||||
|         res.insert(0, middleware.getName()); | ||||
|       } | ||||
|     } | ||||
|     return '[Req] -> ${req.join(' -> ')}\n[Res] -> ${res.join(' -> ')}'; | ||||
|   } | ||||
| 
 | ||||
|   @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 | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| class Convert { | ||||
|   static String toHex(List<int> bytes, {bool upperCase = false}) { | ||||
|     final buffer = StringBuffer(); | ||||
| @ -29,6 +31,15 @@ class Convert { | ||||
|       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 { | ||||
|  | ||||
| @ -14,22 +14,23 @@ | ||||
| // 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 'dart:core'; | ||||
| 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 { | ||||
|   Oauth2Client(super.inner); | ||||
| 
 | ||||
|   Future<Response?> refresh() { | ||||
|     return Future.value(); | ||||
|   } | ||||
| 
 | ||||
|   Future<Response> authorize( | ||||
|     Map<String, dynamic> body, { | ||||
|     Map<String, String>? headers, | ||||
|   }) { | ||||
|     return Future<Response>.value(); | ||||
|     final rf = randomizationFactor * (rand.nextDouble() * 2 - 1) + 1; | ||||
|     final exp = min(attempt, 31); // prevent overflows. | ||||
|     final delay = delayFactor * pow(2.0, exp) * rf; | ||||
|     return delay < maxDelay ? delay : maxDelay; | ||||
|   } | ||||
| } | ||||
| @ -18,6 +18,5 @@ enum Protocols { | ||||
|   http, | ||||
|   https; | ||||
| 
 | ||||
|   String get name => toString().split('.').last; | ||||
|   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 | ||||
| // 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 Utils { | ||||
|   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 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}', | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| export 'authentication_methods.dart'; | ||||
| export 'digest_auth.dart'; | ||||
| export 'header_keys.dart'; | ||||
| export 'http_methods.dart'; | ||||
| export 'http_status.dart'; | ||||
| export 'protocols.dart'; | ||||
| export 'request_utils.dart'; | ||||
|  | ||||
| @ -1,17 +1,24 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| //  | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| //  | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| //  | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| library wyatt_http_client; | ||||
| 
 | ||||
| export 'src/middleware.dart'; | ||||
| export 'src/middleware_client.dart'; | ||||
| export 'src/middlewares/middlewares.dart'; | ||||
| export 'src/models/models.dart'; | ||||
| export 'src/pipeline.dart'; | ||||
| export 'src/utils/utils.dart'; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user