Compare commits
	
		
			5 Commits
		
	
	
		
			9e9ec9ba9b
			...
			a7204f588f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a7204f588f | |||
| 81367e5f3a | |||
| 14cb3485e4 | |||
| 58d3b70bee | |||
| ea4a30ca00 | 
| @ -1,39 +1,182 @@ | |||||||
| <!--  | <!-- | ||||||
| This README describes the package. If you publish this package to pub.dev, |  * Copyright (C) 2022 WYATT GROUP | ||||||
| this README's contents appear on the landing page for your package. |  * Please see the AUTHORS file for details. | ||||||
| 
 | 
 | ||||||
| For information about how to write a good package README, see the guide for |  * This program is free software: you can redistribute it and/or modify | ||||||
| [writing package pages](https://dart.dev/guides/libraries/writing-package-pages).  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * any later version. | ||||||
| 
 | 
 | ||||||
| For general information about developing packages, see the Dart guide for |  * This program is distributed in the hope that it will be useful,  | ||||||
| [creating packages](https://dart.dev/guides/libraries/create-library-packages) |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| and the Flutter guide for |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| [developing packages and plugins](https://flutter.dev/developing-packages).  |  * GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| --> | --> | ||||||
| 
 | 
 | ||||||
| TODO: Put a short description of the package here that helps potential users | # Dart - HTTP Client | ||||||
| know whether this package might be useful for them. |  | ||||||
| 
 | 
 | ||||||
| ## Features | <p align="left"> | ||||||
|  |   <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"> | ||||||
|  |     <img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /> | ||||||
|  |   </a> | ||||||
|  |   <img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" /> | ||||||
|  | </p> | ||||||
| 
 | 
 | ||||||
| TODO: List what your package can do. Maybe include images, gifs, or videos. | HTTP Client for Dart with Middlewares ! | ||||||
| 
 | 
 | ||||||
| ## Getting started | ## Getting started | ||||||
| 
 | 
 | ||||||
| TODO: List prerequisites and provide or point to information on how to | Simply add wyatt_http_client in pubspec.yaml, then | ||||||
| start using the package. | 
 | ||||||
|  | ```dart | ||||||
|  | import 'package:wyatt_http_client/wyatt_http_client.dart'; | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| ## Usage | ## Usage | ||||||
| 
 | 
 | ||||||
| TODO: Include short and useful examples for package users. Add longer examples | Firstly you have to understand **Middleware** and **Pipeline** concepts. | ||||||
| to `/example` folder.  | 
 | ||||||
|  | In `wyatt_http_client` a middleware is an object where requests and responses  | ||||||
|  | pass through. And a pipeline is basicaly a list of middlewares. | ||||||
|  | 
 | ||||||
|  | In a pipeline with middlewares A and B, if request pass through A, then B,  | ||||||
|  | the response will pass through B then A. | ||||||
|  | 
 | ||||||
|  | > You can `print(pipeline)` to get full process order of a pipeline. | ||||||
|  | 
 | ||||||
|  | For example, if you want to log every request, and simplify an url you can use provided `SimpleLogger` and `UriPrefix` . | ||||||
| 
 | 
 | ||||||
| ```dart | ```dart | ||||||
| const like = 'sample'; | // Create the Pipeline | ||||||
|  | final Pipeline pipeline = Pipeline() | ||||||
|  |     .addMiddleware( | ||||||
|  |         UriPrefixMiddleware( | ||||||
|  |             protocol: Protocols.http, | ||||||
|  |             authority: 'localhost:80', | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     .addMiddleware(SimpleLoggerMiddleware()); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Additional information | Then if you print the pipeline,  | ||||||
| 
 | 
 | ||||||
| TODO: Tell users more about the package: where to find more information, how to  | ``` | ||||||
| contribute to the package, how to file issues, what response they can expect  | [Req] -> UriPrefix -> SimpleLogger | ||||||
| from the package authors, and more. | [Res] -> SimpleLogger | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | > The `response` doesn't pass through `UriPrefix` because it's an `OnRequestMiddleware` only. | ||||||
|  | 
 | ||||||
|  | And you can create a client. | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | final client = MiddlewareClient(pipeline: pipeline); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | At this point you can use `client` like every Client from `package:http/http.dart` . | ||||||
|  | 
 | ||||||
|  | ## Recipes | ||||||
|  | 
 | ||||||
|  | ### Rest API with URL Authentication | ||||||
|  | 
 | ||||||
|  | Let's build a client for a REST API where the (bad) authentication is through the URL. | ||||||
|  | We need some middlewares: | ||||||
|  | 
 | ||||||
|  | * SimpleLogger, to log every request and response (useful for debug). | ||||||
|  | * BodyToJson, to automaticaly transform Map object to JSON. | ||||||
|  | * UriPrefix, to simplify the build of an API Object (save protocol and API prefix). | ||||||
|  | * UnsafeAuth, to use url based authentication. | ||||||
|  | 
 | ||||||
|  | Let's start by creating the Pipeline: | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | final Pipeline pipeline = Pipeline() | ||||||
|  |     .addMiddleware( | ||||||
|  |         UriPrefixMiddleware( | ||||||
|  |             protocol: Protocols.http, | ||||||
|  |             authority: 'localhost:80', | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     .addMiddleware(BodyToJsonMiddleware()) | ||||||
|  |     .addMiddleware( | ||||||
|  |         UnsafeAuthMiddleware( | ||||||
|  |             username: 'wyatt', | ||||||
|  |             password: 'motdepasse', | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     .addMiddleware(SimpleLoggerMiddleware()); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Then simply create a client and make a call. | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | final client = MiddlewareClient(pipeline: pipeline); | ||||||
|  | 
 | ||||||
|  | await client.get(Uri.parse('/protected')); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | > Here it make a `GET` call on `http://localhost:80/protected?username=wyatt&password=motdepasse` | ||||||
|  | 
 | ||||||
|  | And voilà. | ||||||
|  | 
 | ||||||
|  | ### Rest API with Oauth2 | ||||||
|  | 
 | ||||||
|  | So now we want a real authentication. | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | final Pipeline pipeline = Pipeline() | ||||||
|  |     .addMiddleware( | ||||||
|  |         UriPrefixMiddleware( | ||||||
|  |             protocol: Protocols.http, | ||||||
|  |             authority: 'localhost:80', | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     .addMiddleware(BodyToJsonMiddleware()) | ||||||
|  |     .addMiddleware( | ||||||
|  |         RefreshTokenAuthMiddleware( | ||||||
|  |             authorizationEndpoint: '/auth/sign-in', | ||||||
|  |             tokenEndpoint: '/auth/refresh', | ||||||
|  |             accessTokenParser: (body) => body['access_token']! as String, | ||||||
|  |             refreshTokenParser: (body) => body['refresh_token']! as String, | ||||||
|  |             unauthorized: HttpStatus.forbidden, | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     .addMiddleware(SimpleLoggerMiddleware()); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | > Here we just change `UnsafeAuthMiddleware` by `RefreshTokenAuthMiddleware` and the whole app while adapt to a new authentication system. | ||||||
|  | 
 | ||||||
|  | ### Create a new Middleware | ||||||
|  | 
 | ||||||
|  | You can create your own middleware by implementing `Middleware` class, and use mixins to add `OnRequest` and/or `OnResponse` methods. | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | class SimpleLoggerMiddleware  | ||||||
|  |     with OnRequestMiddleware, OnResponseMiddleware  | ||||||
|  |     implements Middleware { | ||||||
|  |    | ||||||
|  |   @override | ||||||
|  |   String getName() => 'SimpleLogger'; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<MiddlewareRequest> onRequest( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareRequest request, | ||||||
|  |   ) async { | ||||||
|  |     print('${getName()}::OnRequest'); | ||||||
|  |     return request; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<MiddlewareResponse> onResponse( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareResponse response, | ||||||
|  |   ) async { | ||||||
|  |     print('${getName()}::OnResponse'); | ||||||
|  |     return response; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | |||||||
| @ -17,14 +17,7 @@ | |||||||
| import 'dart:async'; | import 'dart:async'; | ||||||
| import 'dart:io'; | import 'dart:io'; | ||||||
| 
 | 
 | ||||||
| import 'package:wyatt_http_client/src/authentication/basic_authentication_client.dart'; | import 'package:wyatt_http_client/wyatt_http_client.dart'; | ||||||
| import 'package:wyatt_http_client/src/authentication/bearer_authentication_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/authentication/digest_authentication_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/authentication/refresh_token_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/authentication/unsafe_authentication_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/rest_client.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/header_keys.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; |  | ||||||
| 
 | 
 | ||||||
| String lastToken = ''; | String lastToken = ''; | ||||||
| int token = 0; | int token = 0; | ||||||
| @ -42,7 +35,7 @@ Future<void> handleBasic(HttpRequest req) async { | |||||||
| 
 | 
 | ||||||
| Future<void> handleBasicNegotiate(HttpRequest req) async { | Future<void> handleBasicNegotiate(HttpRequest req) async { | ||||||
|   if (req.headers.value('Authorization') == null) { |   if (req.headers.value('Authorization') == null) { | ||||||
|     req.response.statusCode = HttpStatus.unauthorized; |     req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|     req.response.headers.set(HeaderKeys.wwwAuthenticate, 'Basic realm="Wyatt"'); |     req.response.headers.set(HeaderKeys.wwwAuthenticate, 'Basic realm="Wyatt"'); | ||||||
|     print(req.response.headers.value('WWW-Authenticate')); |     print(req.response.headers.value('WWW-Authenticate')); | ||||||
|     return req.response.close(); |     return req.response.close(); | ||||||
| @ -56,7 +49,7 @@ Future<void> handleBearer(HttpRequest req) async { | |||||||
| 
 | 
 | ||||||
| Future<void> handleDigest(HttpRequest req) async { | Future<void> handleDigest(HttpRequest req) async { | ||||||
|   if (req.headers.value('Authorization') == null) { |   if (req.headers.value('Authorization') == null) { | ||||||
|     req.response.statusCode = HttpStatus.unauthorized; |     req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|     req.response.headers.set( |     req.response.headers.set( | ||||||
|       'WWW-Authenticate', |       'WWW-Authenticate', | ||||||
|       'Digest realm="Wyatt", ' |       'Digest realm="Wyatt", ' | ||||||
| @ -110,7 +103,7 @@ Future<void> handleOauth2RefreshToken(HttpRequest req) async { | |||||||
|         return req.response.close(); |         return req.response.close(); | ||||||
|       } else { |       } else { | ||||||
|         lastToken = receivedToken; |         lastToken = receivedToken; | ||||||
|         req.response.statusCode = HttpStatus.unauthorized; |         req.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|         return req.response.close(); |         return req.response.close(); | ||||||
|       } |       } | ||||||
|     default: |     default: | ||||||
| @ -160,13 +153,13 @@ Future<void> server() async { | |||||||
|           print('Authorized'); |           print('Authorized'); | ||||||
|           error = 0; |           error = 0; | ||||||
|         } else { |         } else { | ||||||
|           request.response.statusCode = HttpStatus.unauthorized; |           request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|       case '/test/oauth2-test-timeout': |       case '/test/oauth2-test-timeout': | ||||||
|         error++; |         error++; | ||||||
|         print('Error $error'); |         print('Error $error'); | ||||||
|         request.response.statusCode = HttpStatus.unauthorized; |         request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|         break; |         break; | ||||||
|       case '/test/oauth2-login': |       case '/test/oauth2-login': | ||||||
|         if (request.method == 'POST') { |         if (request.method == 'POST') { | ||||||
| @ -189,12 +182,12 @@ Future<void> server() async { | |||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|       case '/test/oauth2-refresh-error': |       case '/test/oauth2-refresh-error': | ||||||
|         request.response.statusCode = HttpStatus.unauthorized; |         request.response.statusCode = HttpStatus.unauthorized.statusCode; | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|       default: |       default: | ||||||
|         print(' => Unknown path or method'); |         print(' => Unknown path or method'); | ||||||
|         request.response.statusCode = HttpStatus.notFound; |         request.response.statusCode = HttpStatus.notFound.statusCode; | ||||||
|     } |     } | ||||||
|     request.response.close(); |     request.response.close(); | ||||||
|     print('===================='); |     print('===================='); | ||||||
| @ -204,73 +197,98 @@ Future<void> server() async { | |||||||
| Future<void> main() async { | Future<void> main() async { | ||||||
|   unawaited(server()); |   unawaited(server()); | ||||||
|   final base = 'localhost:8080'; |   final base = 'localhost:8080'; | ||||||
|   final restClient = RestClient(protocol: Protocols.http, authority: base); |   final uriPrefix = UriPrefixMiddleware( | ||||||
|  |     protocol: Protocols.http, | ||||||
|  |     authority: base, | ||||||
|  |   ); | ||||||
|  |   final jsonEncoder = BodyToJsonMiddleware(); | ||||||
|  |   final logger = SimpleLoggerMiddleware(); | ||||||
| 
 | 
 | ||||||
|   // Basic |   // Basic | ||||||
|   final basic = BasicAuthenticationClient( |   final basicAuth = BasicAuthMiddleware( | ||||||
|     username: 'username', |     username: 'username', | ||||||
|     password: 'password', |     password: 'password', | ||||||
|     inner: restClient, |   ); | ||||||
|  |   final basic = MiddlewareClient( | ||||||
|  |     pipeline: Pipeline.fromIterable([ | ||||||
|  |       uriPrefix, | ||||||
|  |       basicAuth, | ||||||
|  |       logger, | ||||||
|  |     ]), | ||||||
|   ); |   ); | ||||||
|   await basic.get(Uri.parse('/test/basic-test')); |   await basic.get(Uri.parse('/test/basic-test')); | ||||||
| 
 | 
 | ||||||
|   // Basic with negotiate |  | ||||||
|   final basicWithNegotiate = BasicAuthenticationClient( |  | ||||||
|     username: 'username', |  | ||||||
|     password: 'password', |  | ||||||
|     preemptive: false, |  | ||||||
|     inner: restClient, |  | ||||||
|   ); |  | ||||||
|   await basicWithNegotiate.get(Uri.parse('/test/basic-test-with-negotiate')); |  | ||||||
| 
 |  | ||||||
|   // Digest |   // Digest | ||||||
|   final digest = DigestAuthenticationClient( |   final digestAuth = DigestAuthMiddleware( | ||||||
|     username: 'Mufasa', |     username: 'Mufasa', | ||||||
|     password: 'Circle Of Life', |     password: 'Circle Of Life', | ||||||
|     inner: restClient, |   ); | ||||||
|  |   final digest = MiddlewareClient( | ||||||
|  |     pipeline: Pipeline.fromIterable([ | ||||||
|  |       uriPrefix, | ||||||
|  |       digestAuth, | ||||||
|  |       logger, | ||||||
|  |     ]), | ||||||
|   ); |   ); | ||||||
|   await digest.get(Uri.parse('/test/digest-test')); |   await digest.get(Uri.parse('/test/digest-test')); | ||||||
| 
 | 
 | ||||||
|   // Bearer |   // // Bearer | ||||||
|   final bearer = BearerAuthenticationClient( |   // final bearer = BearerAuthenticationClient( | ||||||
|     token: 'access-token-test', |   //   token: 'access-token-test', | ||||||
|     inner: restClient, |   //   inner: restClient, | ||||||
|   ); |   // ); | ||||||
|   await bearer.get(Uri.parse('/test/bearer-test')); |   // await bearer.get(Uri.parse('/test/bearer-test')); | ||||||
| 
 | 
 | ||||||
|   // API Key |   // // API Key | ||||||
|   final apiKey = BearerAuthenticationClient( |   // final apiKey = BearerAuthenticationClient( | ||||||
|     token: 'awesome-api-key', |   //   token: 'awesome-api-key', | ||||||
|     authenticationMethod: 'ApiKey', |   //   authenticationMethod: 'ApiKey', | ||||||
|     inner: restClient, |   //   inner: restClient, | ||||||
|   ); |   // ); | ||||||
|   await apiKey.get(Uri.parse('/test/apikey-test')); |   // await apiKey.get(Uri.parse('/test/apikey-test')); | ||||||
| 
 | 
 | ||||||
|   // Unsafe URL |   // Unsafe URL | ||||||
|   final unsafe = UnsafeAuthenticationClient( |   final unsafeAuth = UnsafeAuthMiddleware( | ||||||
|     username: 'Mufasa', |     username: 'Mufasa', | ||||||
|     password: 'Circle Of Life', |     password: 'Circle Of Life', | ||||||
|     inner: restClient, |   ); | ||||||
|  |   final unsafe = MiddlewareClient( | ||||||
|  |     pipeline: Pipeline.fromIterable([ | ||||||
|  |       uriPrefix, | ||||||
|  |       unsafeAuth, | ||||||
|  |       logger, | ||||||
|  |     ]), | ||||||
|   ); |   ); | ||||||
|   await unsafe.get(Uri.parse('/test/unsafe-test')); |   await unsafe.get(Uri.parse('/test/unsafe-test')); | ||||||
| 
 | 
 | ||||||
|   // OAuth2 |   // OAuth2 | ||||||
|   final refreshToken = RefreshTokenClient( |   final refreshTokenAuth = RefreshTokenAuthMiddleware( | ||||||
|     authorizationEndpoint: '/test/oauth2-test?action=login', |     authorizationEndpoint: '/test/oauth2-test?action=login', | ||||||
|     tokenEndpoint: '/test/oauth2-test?action=refresh', |     tokenEndpoint: '/test/oauth2-test?action=refresh', | ||||||
|     accessTokenParser: (body) => body['accessToken']! as String, |     accessTokenParser: (body) => body['accessToken']! as String, | ||||||
|     refreshTokenParser: (body) => body['refreshToken']! as String, |     refreshTokenParser: (body) => body['refreshToken']! as String, | ||||||
|     inner: restClient, |   ); | ||||||
|  |   final refreshToken = MiddlewareClient( | ||||||
|  |     pipeline: Pipeline.fromIterable([ | ||||||
|  |       uriPrefix, | ||||||
|  |       jsonEncoder, | ||||||
|  |       refreshTokenAuth, | ||||||
|  |       logger, | ||||||
|  |     ]), | ||||||
|   ); |   ); | ||||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); |   await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||||
|   await refreshToken.authorize(<String, String>{ |   // Login | ||||||
|     'username': 'username', |   await refreshToken.post( | ||||||
|     'password': 'password', |     Uri.parse('/test/oauth2-test'), | ||||||
|   }); |     body: <String, String>{ | ||||||
|  |       'username': 'username', | ||||||
|  |       'password': 'password', | ||||||
|  |     }, | ||||||
|  |   ); | ||||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); |   await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||||
|   await refreshToken.refresh(); |   // await refreshToken.refresh(); | ||||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test')); |   // await refreshToken.get(Uri.parse('/test/oauth2-test')); | ||||||
|   await refreshToken.get(Uri.parse('/test/oauth2-test?action=access-denied')); |   // await refreshToken.get(Uri.parse('/test/oauth2-test?action=access-denied')); | ||||||
| 
 | 
 | ||||||
|   exit(0); |   exit(0); | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,398 @@ | |||||||
|  | // 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/>. | ||||||
|  | 
 | ||||||
|  | // ignore_for_file: public_member_api_docs, sort_constructors_first | ||||||
|  | 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_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'; | ||||||
|  | import 'package:wyatt_http_client/src/utils/http_status.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/utils/protocols.dart'; | ||||||
|  | 
 | ||||||
|  | enum EmailVerificationAction { | ||||||
|  |   signUp, | ||||||
|  |   resetPassword, | ||||||
|  |   changeEmail; | ||||||
|  | 
 | ||||||
|  |   String toSnakeCase() { | ||||||
|  |     return name.splitMapJoin( | ||||||
|  |       RegExp('[A-Z]'), | ||||||
|  |       onMatch: (m) => '_${m[0]?.toLowerCase()}', | ||||||
|  |       onNonMatch: (n) => n, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   factory EmailVerificationAction.fromString(String str) { | ||||||
|  |     return EmailVerificationAction.values.firstWhere( | ||||||
|  |       (EmailVerificationAction element) => element.toSnakeCase() == str, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class VerifyCode { | ||||||
|  |   final String email; | ||||||
|  |   final String verificationCode; | ||||||
|  |   final EmailVerificationAction action; | ||||||
|  |   VerifyCode({ | ||||||
|  |     required this.email, | ||||||
|  |     required this.verificationCode, | ||||||
|  |     required this.action, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   VerifyCode copyWith({ | ||||||
|  |     String? email, | ||||||
|  |     String? verificationCode, | ||||||
|  |     EmailVerificationAction? action, | ||||||
|  |   }) { | ||||||
|  |     return VerifyCode( | ||||||
|  |       email: email ?? this.email, | ||||||
|  |       verificationCode: verificationCode ?? this.verificationCode, | ||||||
|  |       action: action ?? this.action, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toMap() { | ||||||
|  |     return <String, dynamic>{ | ||||||
|  |       'email': email, | ||||||
|  |       'verification_code': verificationCode, | ||||||
|  |       'action': action.toSnakeCase(), | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   factory VerifyCode.fromMap(Map<String, dynamic> map) { | ||||||
|  |     return VerifyCode( | ||||||
|  |       email: map['email'] as String, | ||||||
|  |       verificationCode: map['verification_code'] as String, | ||||||
|  |       action: EmailVerificationAction.fromString(map['action'] as String), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String toJson() => json.encode(toMap()); | ||||||
|  | 
 | ||||||
|  |   factory VerifyCode.fromJson(String source) => | ||||||
|  |       VerifyCode.fromMap(json.decode(source) as Map<String, dynamic>); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => | ||||||
|  |       'VerifyCode(email: $email, verificationCode: $verificationCode, action: $action)'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class Account { | ||||||
|  |   final String email; | ||||||
|  |   final String? sessionId; | ||||||
|  |   Account({ | ||||||
|  |     required this.email, | ||||||
|  |     this.sessionId, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   Account copyWith({ | ||||||
|  |     String? email, | ||||||
|  |     String? sessionId, | ||||||
|  |   }) { | ||||||
|  |     return Account( | ||||||
|  |       email: email ?? this.email, | ||||||
|  |       sessionId: sessionId ?? this.sessionId, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toMap() { | ||||||
|  |     return <String, dynamic>{ | ||||||
|  |       'email': email, | ||||||
|  |       'session_id': sessionId, | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   factory Account.fromMap(Map<String, dynamic> map) { | ||||||
|  |     return Account( | ||||||
|  |       email: map['email'] as String, | ||||||
|  |       sessionId: map['session_id'] != null ? map['session_id'] as String : null, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String toJson() => json.encode(toMap()); | ||||||
|  | 
 | ||||||
|  |   factory Account.fromJson(String source) => | ||||||
|  |       Account.fromMap(json.decode(source) as Map<String, dynamic>); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'Account(email: $email, sessionId: $sessionId)'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class SignUp { | ||||||
|  |   final String sessionId; | ||||||
|  |   final String password; | ||||||
|  |   SignUp({ | ||||||
|  |     required this.sessionId, | ||||||
|  |     required this.password, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   SignUp copyWith({ | ||||||
|  |     String? sessionId, | ||||||
|  |     String? password, | ||||||
|  |   }) { | ||||||
|  |     return SignUp( | ||||||
|  |       sessionId: sessionId ?? this.sessionId, | ||||||
|  |       password: password ?? this.password, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toMap() { | ||||||
|  |     return <String, dynamic>{ | ||||||
|  |       'session_id': sessionId, | ||||||
|  |       'password': password, | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   factory SignUp.fromMap(Map<String, dynamic> map) { | ||||||
|  |     return SignUp( | ||||||
|  |       sessionId: map['session_id'] as String, | ||||||
|  |       password: map['password'] as String, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String toJson() => json.encode(toMap()); | ||||||
|  | 
 | ||||||
|  |   factory SignUp.fromJson(String source) => | ||||||
|  |       SignUp.fromMap(json.decode(source) as Map<String, dynamic>); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'SignUp(sessionId: $sessionId, password: $password)'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class TokenSuccess { | ||||||
|  |   final String accessToken; | ||||||
|  |   final String refreshToken; | ||||||
|  |   final Account account; | ||||||
|  |   TokenSuccess({ | ||||||
|  |     required this.accessToken, | ||||||
|  |     required this.refreshToken, | ||||||
|  |     required this.account, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   TokenSuccess copyWith({ | ||||||
|  |     String? accessToken, | ||||||
|  |     String? refreshToken, | ||||||
|  |     Account? account, | ||||||
|  |   }) { | ||||||
|  |     return TokenSuccess( | ||||||
|  |       accessToken: accessToken ?? this.accessToken, | ||||||
|  |       refreshToken: refreshToken ?? this.refreshToken, | ||||||
|  |       account: account ?? this.account, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toMap() { | ||||||
|  |     return <String, dynamic>{ | ||||||
|  |       'access_token': accessToken, | ||||||
|  |       'refresh_token': refreshToken, | ||||||
|  |       'account': account.toMap(), | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   factory TokenSuccess.fromMap(Map<String, dynamic> map) { | ||||||
|  |     return TokenSuccess( | ||||||
|  |       accessToken: map['access_token'] as String, | ||||||
|  |       refreshToken: map['refresh_token'] as String, | ||||||
|  |       account: Account.fromMap(map['account'] as Map<String, dynamic>), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String toJson() => json.encode(toMap()); | ||||||
|  | 
 | ||||||
|  |   factory TokenSuccess.fromJson(String source) => | ||||||
|  |       TokenSuccess.fromMap(json.decode(source) as Map<String, dynamic>); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => | ||||||
|  |       'TokenSuccess(accessToken: $accessToken, refreshToken: $refreshToken, account: $account)'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class Login { | ||||||
|  |   final String email; | ||||||
|  |   final String password; | ||||||
|  |   Login({ | ||||||
|  |     required this.email, | ||||||
|  |     required this.password, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   Login copyWith({ | ||||||
|  |     String? email, | ||||||
|  |     String? password, | ||||||
|  |   }) { | ||||||
|  |     return Login( | ||||||
|  |       email: email ?? this.email, | ||||||
|  |       password: password ?? this.password, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toMap() { | ||||||
|  |     return <String, dynamic>{ | ||||||
|  |       'email': email, | ||||||
|  |       'password': password, | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   factory Login.fromMap(Map<String, dynamic> map) { | ||||||
|  |     return Login( | ||||||
|  |       email: map['email'] as String, | ||||||
|  |       password: map['password'] as String, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String toJson() => json.encode(toMap()); | ||||||
|  | 
 | ||||||
|  |   factory Login.fromJson(String source) => | ||||||
|  |       Login.fromMap(json.decode(source) as Map<String, dynamic>); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'Login(email: $email, password: $password)'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class FastAPI { | ||||||
|  |   final String baseUrl; | ||||||
|  |   final MiddlewareClient client; | ||||||
|  |   final int apiVersion; | ||||||
|  | 
 | ||||||
|  |   FastAPI({ | ||||||
|  |     this.baseUrl = 'localhost:80', | ||||||
|  |     MiddlewareClient? client, | ||||||
|  |     this.apiVersion = 1, | ||||||
|  |   }) : client = client ?? MiddlewareClient(); | ||||||
|  | 
 | ||||||
|  |   String get apiPath => '/api/v$apiVersion'; | ||||||
|  | 
 | ||||||
|  |   Future<void> sendSignUpCode(String email) async { | ||||||
|  |     final r = await client.post( | ||||||
|  |       Uri.parse('$apiPath/auth/send-sign-up-code'), | ||||||
|  |       body: <String, String>{ | ||||||
|  |         'email': email, | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |     if (r.statusCode != 201) { | ||||||
|  |       throw Exception('Invalid reponse: ${r.statusCode}'); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<Account> verifyCode(VerifyCode verifyCode) async { | ||||||
|  |     final r = await client.post( | ||||||
|  |       Uri.parse('$apiPath/auth/verify-code'), | ||||||
|  |       body: verifyCode.toMap(), | ||||||
|  |     ); | ||||||
|  |     if (r.statusCode != 202) { | ||||||
|  |       throw Exception('Invalid reponse: ${r.statusCode}'); | ||||||
|  |     } else { | ||||||
|  |       return Account.fromMap( | ||||||
|  |         (jsonDecode(r.body) as Map<String, dynamic>)['account'] | ||||||
|  |             as Map<String, dynamic>, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<Account> signUp(SignUp signUp) async { | ||||||
|  |     final r = await client.post( | ||||||
|  |       Uri.parse('$apiPath/auth/sign-up'), | ||||||
|  |       body: signUp.toMap(), | ||||||
|  |     ); | ||||||
|  |     if (r.statusCode != 201) { | ||||||
|  |       throw Exception('Invalid reponse: ${r.statusCode}'); | ||||||
|  |     } else { | ||||||
|  |       return Account.fromJson(r.body); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<TokenSuccess> signInWithPassword(Login login) async { | ||||||
|  |     final r = await client.post( | ||||||
|  |       Uri.parse('$apiPath/auth/sign-in-with-password'), | ||||||
|  |       body: login.toMap(), | ||||||
|  |     ); | ||||||
|  |     if (r.statusCode != 200) { | ||||||
|  |       throw Exception('Invalid reponse: ${r.statusCode}'); | ||||||
|  |     } else { | ||||||
|  |       return TokenSuccess.fromJson(r.body); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Future<TokenSuccess> refresh() async { | ||||||
|  |   //   final r = await client.refresh(); | ||||||
|  |   //   return TokenSuccess.fromJson(r?.body ?? ''); | ||||||
|  |   // } | ||||||
|  | 
 | ||||||
|  |   Future<List<Account>> getAccountList() async { | ||||||
|  |     final r = await client.get( | ||||||
|  |       Uri.parse('$apiPath/account'), | ||||||
|  |     ); | ||||||
|  |     if (r.statusCode != 200) { | ||||||
|  |       throw Exception('Invalid reponse: ${r.statusCode}'); | ||||||
|  |     } else { | ||||||
|  |       final list = (jsonDecode(r.body) as Map<String, dynamic>)['founds'] | ||||||
|  |           as List<Map<String, dynamic>>; | ||||||
|  |       final result = <Account>[]; | ||||||
|  |       for (final element in list) { | ||||||
|  |         result.add(Account.fromMap(element)); | ||||||
|  |       } | ||||||
|  |       return result; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void main(List<String> args) async { | ||||||
|  |   final Pipeline pipeline = Pipeline() | ||||||
|  |       .addMiddleware( | ||||||
|  |         UriPrefixMiddleware( | ||||||
|  |           protocol: Protocols.http, | ||||||
|  |           authority: 'localhost:80', | ||||||
|  |         ), | ||||||
|  |       ) | ||||||
|  |       .addMiddleware(BodyToJsonMiddleware()) | ||||||
|  |       .addMiddleware( | ||||||
|  |         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); | ||||||
|  |   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, | ||||||
|  |     ), | ||||||
|  |   ); | ||||||
|  |   final registeredAccount = await api.signUp( | ||||||
|  |     SignUp(sessionId: verifiedAccount.sessionId ?? '', password: 'password'), | ||||||
|  |   ); | ||||||
|  |   final signedInAccount = await api.signInWithPassword( | ||||||
|  |     Login(email: 'git@pcl.ovh', password: 'password'), | ||||||
|  |   ); | ||||||
|  |   final accountList = await api.getAccountList(); | ||||||
|  |   print(accountList); | ||||||
|  | } | ||||||
							
								
								
									
										162
									
								
								packages/wyatt_http_client/example/pipeline.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								packages/wyatt_http_client/example/pipeline.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,162 @@ | |||||||
|  | // ignore_for_file: public_member_api_docs, sort_constructors_first | ||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'package:wyatt_http_client/src/middleware_client.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/middlewares/body_to_json_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'; | ||||||
|  | 
 | ||||||
|  | // class RequestMutatorMiddleware implements Middleware { | ||||||
|  | //   @override | ||||||
|  | //   Middleware? parent; | ||||||
|  | 
 | ||||||
|  | //   @override | ||||||
|  | //   Middleware? child; | ||||||
|  | 
 | ||||||
|  | //   RequestMutatorMiddleware({ | ||||||
|  | //     this.parent, | ||||||
|  | //     this.child, | ||||||
|  | //   }); | ||||||
|  | 
 | ||||||
|  | //   @override | ||||||
|  | //   BaseRequest onRequest(BaseRequest request) { | ||||||
|  | //     print('RequestMutator::OnRequest: ${request.method} -> ${request.url}'); | ||||||
|  | //     return child?.onRequest(request) ?? request; | ||||||
|  | //   } | ||||||
|  | 
 | ||||||
|  | //   @override | ||||||
|  | //   BaseResponse onResponse(BaseResponse response) { | ||||||
|  | //     final res = child?.onResponse(response) ?? response; | ||||||
|  | //     print( | ||||||
|  | //       'RequestMutator::OnResponse: ${res.statusCode} -> ${res.contentLength} bytes', | ||||||
|  | //     ); | ||||||
|  | //     return res; | ||||||
|  | //   } | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | // typedef Middleware = Handler Function(Handler innerHandler); | ||||||
|  | 
 | ||||||
|  | // Middleware createMiddleware({ | ||||||
|  | //   FutureOr<Response?> Function(Request)? requestHandler, | ||||||
|  | //   FutureOr<Response> Function(Response)? responseHandler, | ||||||
|  | //   FutureOr<Response> Function(Object error, StackTrace)? errorHandler, | ||||||
|  | // }) { | ||||||
|  | //   requestHandler ??= (request) => null; | ||||||
|  | //   responseHandler ??= (response) => response; | ||||||
|  | 
 | ||||||
|  | //   FutureOr<Response> Function(Object, StackTrace)? onError; | ||||||
|  | //   if (errorHandler != null) { | ||||||
|  | //     onError = (error, stackTrace) { | ||||||
|  | //       if (error is Exception) throw error; | ||||||
|  | //       return errorHandler(error, stackTrace); | ||||||
|  | //     }; | ||||||
|  | //   } | ||||||
|  | 
 | ||||||
|  | //   return (Handler innerHandler) { | ||||||
|  | //     return (request) { | ||||||
|  | //       return Future.sync(() => requestHandler!(request)).then((response) { | ||||||
|  | //         if (response != null) return response; | ||||||
|  | 
 | ||||||
|  | //         return Future.sync(() => innerHandler(request)) | ||||||
|  | //             .then((response) => responseHandler!(response), onError: onError); | ||||||
|  | //       }); | ||||||
|  | //     }; | ||||||
|  | //   }; | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | // extension MiddlewareX on Middleware { | ||||||
|  | //   Middleware addMiddleware(Middleware other) => | ||||||
|  | //       (Handler handler) => this(other(handler)); | ||||||
|  | //   Handler addHandler(Handler handler) => this(handler); | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | // typedef Handler = FutureOr<Response> Function(Request request); | ||||||
|  | 
 | ||||||
|  | // final headerMutator = createMiddleware( | ||||||
|  | //   responseHandler: (response) { | ||||||
|  | //     print(response.headers); | ||||||
|  | //     return response; | ||||||
|  | //   },); | ||||||
|  | 
 | ||||||
|  | // class Pipeline { | ||||||
|  | //   const Pipeline(); | ||||||
|  | 
 | ||||||
|  | //   Pipeline addMiddleware(Middleware middleware) => | ||||||
|  | //       _Pipeline(middleware, addHandler); | ||||||
|  | 
 | ||||||
|  | //   Handler addHandler(Handler handler) => handler; | ||||||
|  | 
 | ||||||
|  | //   Middleware get middleware => addHandler; | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | // class _Pipeline extends Pipeline { | ||||||
|  | //   final Middleware _middleware; | ||||||
|  | //   final Middleware _parent; | ||||||
|  | 
 | ||||||
|  | //   _Pipeline(this._middleware, this._parent); | ||||||
|  | 
 | ||||||
|  | //   @override | ||||||
|  | //   Handler addHandler(Handler handler) => _parent(_middleware(handler)); | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | Future<void> main(List<String> args) async { | ||||||
|  |   final UnsafeAuthMiddleware auth = UnsafeAuthMiddleware(); | ||||||
|  |   final Pipeline pipeline = Pipeline() | ||||||
|  |       .addMiddleware( | ||||||
|  |         UriPrefixMiddleware( | ||||||
|  |           protocol: Protocols.http, | ||||||
|  |           authority: 'localhost:80', | ||||||
|  |         ), | ||||||
|  |       ) | ||||||
|  |       .addMiddleware(BodyToJsonMiddleware()) | ||||||
|  |       .addMiddleware( | ||||||
|  |         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); | ||||||
|  |   final client = MiddlewareClient(pipeline: pipeline); | ||||||
|  |   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,115 +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 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, |  | ||||||
|   ]) { |  | ||||||
|     if (accessToken != null && request != null) { |  | ||||||
|       header[authenticationHeader] = '$authenticationMethod $accessToken'; |  | ||||||
|       return header; |  | ||||||
|     } |  | ||||||
|     return header; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<void> authorize( |  | ||||||
|     Map<String, dynamic> body, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   }) async { |  | ||||||
|     final response = await inner.post( |  | ||||||
|       Uri.parse(authorizationEndpoint), |  | ||||||
|       body: jsonEncode(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; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<void> 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); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) async { |  | ||||||
|     final response = await super.send(request); |  | ||||||
| 
 |  | ||||||
|     if (response.statusCode == HttpStatus.unauthorized) { |  | ||||||
|       await refresh(); |  | ||||||
|       return super.send(Utils.copyRequest(request)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return response; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -14,23 +14,25 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||||
| import 'package:wyatt_http_client/src/authentication/interfaces/authentication_client.dart'; | import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||||
| 
 | 
 | ||||||
| abstract class HeaderAuthenticationClient extends AuthenticationClient { | abstract class Middleware { | ||||||
|   HeaderAuthenticationClient(super.inner); |   Middleware(); | ||||||
| 
 |   String getName(); | ||||||
|   Map<String, String> modifyHeader( | } | ||||||
|     Map<String, String> header, [ | 
 | ||||||
|     BaseRequest? request, | mixin OnRequestMiddleware { | ||||||
|   ]) => |   Future<MiddlewareRequest> onRequest( | ||||||
|       header; |     MiddlewareContext context, | ||||||
| 
 |     MiddlewareRequest request, | ||||||
|   @override |   ); | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { | } | ||||||
|     final newHeader = modifyHeader(Map.from(request.headers), request); | 
 | ||||||
|     request.headers.clear(); | mixin OnResponseMiddleware { | ||||||
|     request.headers.addAll(newHeader); |   Future<MiddlewareResponse> onResponse( | ||||||
|     return super.send(request); |     MiddlewareContext context, | ||||||
|   } |     MiddlewareResponse response, | ||||||
|  |   ); | ||||||
| } | } | ||||||
							
								
								
									
										129
									
								
								packages/wyatt_http_client/lib/src/middleware_client.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								packages/wyatt_http_client/lib/src/middleware_client.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,129 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
|  | import 'package:http/http.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/unfreezed_request.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/pipeline.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/utils/http_methods.dart'; | ||||||
|  | 
 | ||||||
|  | class MiddlewareClient extends BaseClient { | ||||||
|  |   final Client inner; | ||||||
|  |   final Pipeline pipeline; | ||||||
|  | 
 | ||||||
|  |   MiddlewareClient({ | ||||||
|  |     Pipeline? pipeline, | ||||||
|  |     Client? inner, | ||||||
|  |   })  : pipeline = pipeline ?? Pipeline(), | ||||||
|  |         inner = inner ?? Client() { | ||||||
|  |     print('Using Pipeline:\n$pipeline'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Response> head(Uri url, {Map<String, String>? headers}) => | ||||||
|  |       _sendUnstreamed(HttpMethods.head.method, url, headers); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Response> get(Uri url, {Map<String, String>? headers}) => | ||||||
|  |       _sendUnstreamed(HttpMethods.get.method, url, headers); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Response> post( | ||||||
|  |     Uri url, { | ||||||
|  |     Map<String, String>? headers, | ||||||
|  |     Object? body, | ||||||
|  |     Encoding? encoding, | ||||||
|  |   }) => | ||||||
|  |       _sendUnstreamed(HttpMethods.post.method, url, headers, body, encoding); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Response> put( | ||||||
|  |     Uri url, { | ||||||
|  |     Map<String, String>? headers, | ||||||
|  |     Object? body, | ||||||
|  |     Encoding? encoding, | ||||||
|  |   }) => | ||||||
|  |       _sendUnstreamed(HttpMethods.put.method, url, headers, body, encoding); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Response> patch( | ||||||
|  |     Uri url, { | ||||||
|  |     Map<String, String>? headers, | ||||||
|  |     Object? body, | ||||||
|  |     Encoding? encoding, | ||||||
|  |   }) => | ||||||
|  |       _sendUnstreamed(HttpMethods.patch.method, url, headers, body, encoding); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Response> delete( | ||||||
|  |     Uri url, { | ||||||
|  |     Map<String, String>? headers, | ||||||
|  |     Object? body, | ||||||
|  |     Encoding? encoding, | ||||||
|  |   }) => | ||||||
|  |       _sendUnstreamed(HttpMethods.delete.method, url, headers, body, encoding); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<StreamedResponse> send(BaseRequest request) { | ||||||
|  |     return inner.send(request); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<Response> _sendUnstreamed( | ||||||
|  |     String method, | ||||||
|  |     Uri url, | ||||||
|  |     Map<String, String>? headers, [ | ||||||
|  |     Object? body, | ||||||
|  |     Encoding? encoding, | ||||||
|  |   ]) async { | ||||||
|  |     final originalRequest = MiddlewareRequest( | ||||||
|  |       unfreezedRequest: UnfreezedRequest( | ||||||
|  |         method: method, | ||||||
|  |         url: url, | ||||||
|  |         headers: headers, | ||||||
|  |         body: body, | ||||||
|  |         encoding: encoding, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |     final requestContext = MiddlewareContext( | ||||||
|  |       pipeline: pipeline, | ||||||
|  |       client: this, | ||||||
|  |       originalRequest: originalRequest, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     final modifiedRequest = await pipeline.onRequest( | ||||||
|  |       requestContext, | ||||||
|  |       originalRequest.copyWith(), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     final originalResponse = MiddlewareResponse( | ||||||
|  |       httpResponse: await Response.fromStream( | ||||||
|  |         await send(modifiedRequest.request), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     final responseContext = | ||||||
|  |         requestContext.copyWith(originalResponse: originalResponse); | ||||||
|  | 
 | ||||||
|  |     final modifiedResponse = | ||||||
|  |         await pipeline.onResponse(responseContext, originalResponse.copyWith()); | ||||||
|  | 
 | ||||||
|  |     return modifiedResponse.httpResponse as Response; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,37 +1,16 @@ | |||||||
| // Copyright (C) 2022 WYATT GROUP | // Copyright (C) 2022 WYATT GROUP | ||||||
| // Please see the AUTHORS file for details. | // Please see the AUTHORS file for details. | ||||||
| // | //  | ||||||
| // This program is free software: you can redistribute it and/or modify | // This program is free software: you can redistribute it and/or modify | ||||||
| // it under the terms of the GNU General Public License as published by | // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | // the Free Software Foundation, either version 3 of the License, or | ||||||
| // any later version. | // any later version. | ||||||
| // | //  | ||||||
| // This program is distributed in the hope that it will be useful, | // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | // GNU General Public License for more details. | ||||||
| // | //  | ||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; |  | ||||||
| import 'package:wyatt_http_client/src/rest_client.dart'; |  | ||||||
| 
 |  | ||||||
| abstract class AuthenticationClient extends BaseClient { |  | ||||||
|   final BaseClient _inner; |  | ||||||
| 
 |  | ||||||
|   BaseClient get inner => _inner; |  | ||||||
| 
 |  | ||||||
|   AuthenticationClient(BaseClient? inner) : _inner = inner ?? RestClient(); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |  | ||||||
|     return _inner.send(request); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   void close() { |  | ||||||
|     _inner.close(); |  | ||||||
|     return super.close(); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -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; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,49 @@ | |||||||
|  | // 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'; | ||||||
|  | 
 | ||||||
|  | class BodyToJsonMiddleware with OnRequestMiddleware implements Middleware { | ||||||
|  |   @override | ||||||
|  |   String getName() => 'BodyToJson'; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<MiddlewareRequest> onRequest( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareRequest request, | ||||||
|  |   ) async { | ||||||
|  |     print( | ||||||
|  |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> Transforms body in json if Map then update ' | ||||||
|  |       'headers with right content-type', | ||||||
|  |     ); | ||||||
|  |     final mutation = { | ||||||
|  |       'content-type': 'application/json; charset=utf-8', | ||||||
|  |     }; | ||||||
|  |     if (request.body is Map) { | ||||||
|  |       final Map<String, String> headers = request.headers..addAll(mutation); | ||||||
|  |       request.modifyRequest( | ||||||
|  |         request.unfreezedRequest | ||||||
|  |             .copyWith(headers: headers, body: jsonEncode(request.body)), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return request; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -14,17 +14,9 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | import 'package:wyatt_http_client/src/middleware.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; |  | ||||||
| 
 | 
 | ||||||
|  | class DefaultMiddleware implements Middleware { | ||||||
|   @override |   @override | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |   String getName() => 'DefaultMiddleware'; | ||||||
|     final newRequest = modifyRequest(request); |  | ||||||
|     return super.send(newRequest); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| @ -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; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +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/>. | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,54 @@ | |||||||
|  | // 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'; | ||||||
|  | 
 | ||||||
|  | class SimpleLoggerMiddleware | ||||||
|  |     with OnRequestMiddleware, OnResponseMiddleware | ||||||
|  |     implements Middleware { | ||||||
|  |   @override | ||||||
|  |   String getName() => 'SimpleLogger'; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<MiddlewareRequest> onRequest( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareRequest request, | ||||||
|  |   ) async { | ||||||
|  |     print( | ||||||
|  |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> ${request.method} ${request.url}\n' | ||||||
|  |       '>> Headers: ${request.headers}\n>> Body: ${request.encodedBody}', | ||||||
|  |     ); | ||||||
|  |     return request; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<MiddlewareResponse> onResponse( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareResponse response, | ||||||
|  |   ) async { | ||||||
|  |     print( | ||||||
|  |       '${getName()}::OnResponse\n' | ||||||
|  |       '>> Status: ${response.status.name.toUpperCase()}\n' | ||||||
|  |       '>> Length: ${response.contentLength ?? '0'} bytes', | ||||||
|  |       // '>> Body: ${response.body}', | ||||||
|  |     ); | ||||||
|  |     return response; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -14,30 +14,43 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | import 'package:wyatt_http_client/src/middleware.dart'; | ||||||
| import 'package:wyatt_http_client/src/authentication/interfaces/url_authentication_client.dart'; | import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||||
| import 'package:wyatt_http_client/src/utils/convert.dart'; | import 'package:wyatt_http_client/src/utils/convert.dart'; | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; |  | ||||||
| 
 | 
 | ||||||
| class UnsafeAuthenticationClient extends UrlAuthenticationClient { | class UnsafeAuthMiddleware with OnRequestMiddleware implements Middleware { | ||||||
|   final String username; |   String? username; | ||||||
|   final String password; |   String? password; | ||||||
| 
 | 
 | ||||||
|   final String usernameField; |   final String usernameField; | ||||||
|   final String passwordField; |   final String passwordField; | ||||||
| 
 | 
 | ||||||
|   UnsafeAuthenticationClient({ |   UnsafeAuthMiddleware({ | ||||||
|     required this.username, |     this.username, | ||||||
|     required this.password, |     this.password, | ||||||
|     this.usernameField = 'username', |     this.usernameField = 'username', | ||||||
|     this.passwordField = 'password', |     this.passwordField = 'password', | ||||||
|     BaseClient? inner, |   }); | ||||||
|   }) : super(inner); |  | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   BaseRequest modifyRequest(BaseRequest request) { |   String getName() => 'UnsafeAuth'; | ||||||
|     final url = | 
 | ||||||
|  |   @override | ||||||
|  |   Future<MiddlewareRequest> onRequest( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareRequest request, | ||||||
|  |   ) async { | ||||||
|  |     if (username == null || password == null) { | ||||||
|  |       return request; | ||||||
|  |     } | ||||||
|  |     final Uri uri = | ||||||
|         request.url + '?$usernameField=$username&$passwordField=$password'; |         request.url + '?$usernameField=$username&$passwordField=$password'; | ||||||
|     return Utils.copyRequestWith(request, url: url); |     print( | ||||||
|  |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> Append: ?$usernameField=$username&$passwordField=$password', | ||||||
|  |     ); | ||||||
|  |     request.modifyRequest(request.unfreezedRequest.copyWith(url: uri)); | ||||||
|  |     return request; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -14,25 +14,35 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | import 'package:wyatt_http_client/src/middleware.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/middleware_context.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||||
| import 'package:wyatt_http_client/src/utils/protocols.dart'; | import 'package:wyatt_http_client/src/utils/protocols.dart'; | ||||||
| import 'package:wyatt_http_client/src/utils/utils.dart'; |  | ||||||
| 
 | 
 | ||||||
| class RestClient extends BaseClient { | class UriPrefixMiddleware with OnRequestMiddleware implements Middleware { | ||||||
|   final Protocols protocol; |   final Protocols protocol; | ||||||
|   final String? authority; |   final String? authority; | ||||||
| 
 | 
 | ||||||
|   final Client _inner; |   UriPrefixMiddleware({ | ||||||
| 
 |     required this.protocol, | ||||||
|   RestClient({ |     required this.authority, | ||||||
|     this.protocol = Protocols.https, |   }); | ||||||
|     this.authority = '', |  | ||||||
|     Client? inner, |  | ||||||
|   }) : _inner = inner ?? Client(); |  | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<StreamedResponse> send(BaseRequest request) { |   String getName() => 'UriPrefix'; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<MiddlewareRequest> onRequest( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareRequest request, | ||||||
|  |   ) async { | ||||||
|     final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}'); |     final Uri uri = Uri.parse('${protocol.scheme}$authority${request.url}'); | ||||||
|     return _inner.send(Utils.copyRequestWith(request, url: uri)); |     print( | ||||||
|  |       '${getName()}::OnRequest\n' | ||||||
|  |       '>> From: ${request.url}\n' | ||||||
|  |       '>> To: $uri', | ||||||
|  |     ); | ||||||
|  |     request.modifyRequest(request.unfreezedRequest.copyWith(url: uri)); | ||||||
|  |     return request; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -0,0 +1,62 @@ | |||||||
|  | // ignore_for_file: public_member_api_docs, sort_constructors_first | ||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'package:wyatt_http_client/src/middleware_client.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/middleware_request.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/middleware_response.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/pipeline.dart'; | ||||||
|  | 
 | ||||||
|  | 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() { | ||||||
|  |     return 'MiddlewareContext(pipeline: $pipeline, client: $client, originalRequest: $originalRequest, lastRequest: $lastRequest, originalResponse: $originalResponse, lastResponse: $lastResponse)'; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,82 @@ | |||||||
|  | // ignore_for_file: public_member_api_docs, sort_constructors_first | ||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
|  | import 'package:http/http.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/models/unfreezed_request.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/utils/convert.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/utils/request_utils.dart'; | ||||||
|  | 
 | ||||||
|  | class MiddlewareRequest { | ||||||
|  |   UnfreezedRequest unfreezedRequest; | ||||||
|  |   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, | ||||||
|  |   }) : _httpRequest = Request(unfreezedRequest.method, unfreezedRequest.url); | ||||||
|  | 
 | ||||||
|  |   MiddlewareRequest copyWith({ | ||||||
|  |     UnfreezedRequest? unfreezedRequest, | ||||||
|  |   }) { | ||||||
|  |     return MiddlewareRequest( | ||||||
|  |       unfreezedRequest: unfreezedRequest ?? this.unfreezedRequest, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void modifyRequest(UnfreezedRequest unfreezedRequest) { | ||||||
|  |     String? _body; | ||||||
|  |     if (unfreezedRequest.body != null) { | ||||||
|  |       final body = unfreezedRequest.body; | ||||||
|  |       if (body is String) { | ||||||
|  |         _body = body; | ||||||
|  |       } else if (body is List) { | ||||||
|  |         _body = String.fromCharCodes(body.cast<int>()); | ||||||
|  |       } else if (body is Map) { | ||||||
|  |         _body = Convert.mapToQuery(body.cast<String, String>()); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     _httpRequest = RequestUtils.copyRequestWith( | ||||||
|  |       _httpRequest, | ||||||
|  |       method: unfreezedRequest.method, | ||||||
|  |       url: unfreezedRequest.url, | ||||||
|  |       headers: unfreezedRequest.headers, | ||||||
|  |       body: _body, | ||||||
|  |     ) as Request; | ||||||
|  |     if (unfreezedRequest.encoding != null) { | ||||||
|  |       _httpRequest.encoding = unfreezedRequest.encoding!; | ||||||
|  |     } | ||||||
|  |     this.unfreezedRequest = unfreezedRequest; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void apply() { | ||||||
|  |     modifyRequest(unfreezedRequest); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'MiddlewareRequest(unfreezedRequest: $unfreezedRequest)'; | ||||||
|  | } | ||||||
| @ -0,0 +1,52 @@ | |||||||
|  | // ignore_for_file: public_member_api_docs, sort_constructors_first | ||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'package:http/http.dart'; | ||||||
|  | import 'package:wyatt_http_client/src/utils/http_status.dart'; | ||||||
|  | 
 | ||||||
|  | class MiddlewareResponse { | ||||||
|  |   BaseResponse httpResponse; | ||||||
|  | 
 | ||||||
|  |   // 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, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   MiddlewareResponse copyWith({ | ||||||
|  |     BaseResponse? httpResponse, | ||||||
|  |   }) { | ||||||
|  |     return MiddlewareResponse( | ||||||
|  |       httpResponse: httpResponse ?? this.httpResponse, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => | ||||||
|  |       'MiddlewareResponse(httpResponse: $httpResponse)'; | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								packages/wyatt_http_client/lib/src/models/models.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								packages/wyatt_http_client/lib/src/models/models.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +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/>. | ||||||
|  | 
 | ||||||
|  | export 'middleware_context.dart'; | ||||||
|  | export 'middleware_request.dart'; | ||||||
|  | export 'middleware_response.dart'; | ||||||
|  | export 'unfreezed_request.dart'; | ||||||
| @ -0,0 +1,55 @@ | |||||||
|  | // 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'; | ||||||
|  | 
 | ||||||
|  | class UnfreezedRequest { | ||||||
|  |   final String method; | ||||||
|  |   final Uri url; | ||||||
|  |   final Map<String, String>? headers; | ||||||
|  |   final Object? body; | ||||||
|  |   final Encoding? encoding; | ||||||
|  | 
 | ||||||
|  |   UnfreezedRequest({ | ||||||
|  |     required this.method, | ||||||
|  |     required this.url, | ||||||
|  |     this.headers, | ||||||
|  |     this.body, | ||||||
|  |     this.encoding, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   UnfreezedRequest copyWith({ | ||||||
|  |     String? method, | ||||||
|  |     Uri? url, | ||||||
|  |     Map<String, String>? headers, | ||||||
|  |     Object? body, | ||||||
|  |     Encoding? encoding, | ||||||
|  |   }) { | ||||||
|  |     return UnfreezedRequest( | ||||||
|  |       method: method ?? this.method, | ||||||
|  |       url: url ?? this.url, | ||||||
|  |       headers: headers ?? this.headers, | ||||||
|  |       body: body ?? this.body, | ||||||
|  |       encoding: encoding ?? this.encoding, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return 'UnfreezedRequest(method: $method, url: $url, headers: ' | ||||||
|  |         '$headers, body: $body, encoding: $encoding)'; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										107
									
								
								packages/wyatt_http_client/lib/src/pipeline.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								packages/wyatt_http_client/lib/src/pipeline.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | |||||||
|  | // 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'; | ||||||
|  | 
 | ||||||
|  | class Pipeline { | ||||||
|  |   final List<Middleware> _middlewares; | ||||||
|  | 
 | ||||||
|  |   int get length => _middlewares.length; | ||||||
|  | 
 | ||||||
|  |   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; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Create new [Pipeline] from the start or end to a specified [Middleware]. | ||||||
|  |   Pipeline sub( | ||||||
|  |     Middleware middleware, { | ||||||
|  |     bool include = false, | ||||||
|  |     bool fromEnd = false, | ||||||
|  |   }) { | ||||||
|  |     final nodes = <Middleware>[]; | ||||||
|  |     final list = fromEnd ? _middlewares.reversed : _middlewares; | ||||||
|  |     for (final m in list) { | ||||||
|  |       if (m != middleware) { | ||||||
|  |         nodes.add(m); | ||||||
|  |       } | ||||||
|  |       if (m == middleware) { | ||||||
|  |         if (include) { | ||||||
|  |           nodes.add(m); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return Pipeline.fromIterable(fromEnd ? nodes.reversed : nodes); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   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( | ||||||
|  |     MiddlewareContext context, | ||||||
|  |     MiddlewareResponse response, | ||||||
|  |   ) async { | ||||||
|  |     print('\n\nNEW RESPONSE\n'); | ||||||
|  |     MiddlewareResponse res = response; | ||||||
|  |     MiddlewareContext ctx = context.copyWith(lastResponse: res); | ||||||
|  |     for (final middleware in _middlewares.reversed) { | ||||||
|  |       if (middleware is OnResponseMiddleware) { | ||||||
|  |         res = await (middleware as OnResponseMiddleware) | ||||||
|  |             .onResponse(ctx, response); | ||||||
|  |         ctx = context.copyWith(lastResponse: res); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     final req = <String>[]; | ||||||
|  |     final res = <String>[]; | ||||||
|  |     for (final middleware in _middlewares) { | ||||||
|  |       if (middleware is OnRequestMiddleware) { | ||||||
|  |         req.add(middleware.getName()); | ||||||
|  |       } | ||||||
|  |       if (middleware is OnResponseMiddleware) { | ||||||
|  |         res.insert(0, middleware.getName()); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return '[Req] -> ${req.join(' -> ')}\n[Res] -> ${res.join(' -> ')}'; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -14,6 +14,8 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
| class Convert { | class Convert { | ||||||
|   static String toHex(List<int> bytes, {bool upperCase = false}) { |   static String toHex(List<int> bytes, {bool upperCase = false}) { | ||||||
|     final buffer = StringBuffer(); |     final buffer = StringBuffer(); | ||||||
| @ -29,6 +31,15 @@ class Convert { | |||||||
|       return buffer.toString(); |       return buffer.toString(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   static String mapToQuery(Map<String, String> map, {Encoding? encoding}) { | ||||||
|  |     final pairs = <List<String>>[]; | ||||||
|  |     map.forEach((key, value) => pairs.add([ | ||||||
|  |           Uri.encodeQueryComponent(key, encoding: encoding ?? utf8), | ||||||
|  |           Uri.encodeQueryComponent(value, encoding: encoding ?? utf8) | ||||||
|  |         ]),); | ||||||
|  |     return pairs.map((pair) => '${pair[0]}=${pair[1]}').join('&'); | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extension UriX on Uri { | extension UriX on Uri { | ||||||
|  | |||||||
							
								
								
									
										36
									
								
								packages/wyatt_http_client/lib/src/utils/delay.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								packages/wyatt_http_client/lib/src/utils/delay.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'dart:core'; | ||||||
|  | import 'dart:math'; | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -14,21 +14,15 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:wyatt_http_client/src/authentication/interfaces/header_authentication_client.dart'; | enum HttpMethods { | ||||||
|  |   head('HEAD'), | ||||||
|  |   get('GET'), | ||||||
|  |   post('POST'), | ||||||
|  |   put('PUT'), | ||||||
|  |   patch('PATCH'), | ||||||
|  |   delete('DELETE'); | ||||||
| 
 | 
 | ||||||
| typedef TokenParser = String Function(Map<String, dynamic>); |   final String method; | ||||||
| 
 | 
 | ||||||
| abstract class Oauth2Client extends HeaderAuthenticationClient { |   const HttpMethods(this.method); | ||||||
|   Oauth2Client(super.inner); |  | ||||||
| 
 |  | ||||||
|   Future<void> refresh() { |  | ||||||
|     return Future.value(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   Future<void> authorize( |  | ||||||
|     Map<String, dynamic> body, { |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|   }) { |  | ||||||
|     return Future.value(); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| @ -1,83 +1,123 @@ | |||||||
| // Copyright (C) 2022 WYATT GROUP | // Copyright (C) 2022 WYATT GROUP | ||||||
| // Please see the AUTHORS file for details. | // Please see the AUTHORS file for details. | ||||||
| //  | // | ||||||
| // This program is free software: you can redistribute it and/or modify | // This program is free software: you can redistribute it and/or modify | ||||||
| // it under the terms of the GNU General Public License as published by | // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | // the Free Software Foundation, either version 3 of the License, or | ||||||
| // any later version. | // any later version. | ||||||
| //  | // | ||||||
| // This program is distributed in the hope that it will be useful, | // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | // GNU General Public License for more details. | ||||||
| //  | // | ||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| /// Status codes for HTTP responses. Extracted from dart:io | /// Status codes for HTTP responses. Extracted from dart:io | ||||||
| abstract class HttpStatus { | enum HttpStatus { | ||||||
|   static const int continue_ = 100; |   continue_(100), | ||||||
|   static const int switchingProtocols = 101; |   switchingProtocols(101), | ||||||
|   static const int processing = 102; |   processing(102), | ||||||
|   static const int ok = 200; |   ok(200), | ||||||
|   static const int created = 201; |   created(201), | ||||||
|   static const int accepted = 202; |   accepted(202), | ||||||
|   static const int nonAuthoritativeInformation = 203; |   nonAuthoritativeInformation(203), | ||||||
|   static const int noContent = 204; |   noContent(204), | ||||||
|   static const int resetContent = 205; |   resetContent(205), | ||||||
|   static const int partialContent = 206; |   partialContent(206), | ||||||
|   static const int multiStatus = 207; |   multiStatus(207), | ||||||
|   static const int alreadyReported = 208; |   alreadyReported(208), | ||||||
|   static const int imUsed = 226; |   imUsed(226), | ||||||
|   static const int multipleChoices = 300; |   multipleChoices(300), | ||||||
|   static const int movedPermanently = 301; |   movedPermanently(301), | ||||||
|   static const int found = 302; |   found(302), | ||||||
|   static const int movedTemporarily = 302; // Common alias for found. |   movedTemporarily(302), // Common alias for found. | ||||||
|   static const int seeOther = 303; |   seeOther(303), | ||||||
|   static const int notModified = 304; |   notModified(304), | ||||||
|   static const int useProxy = 305; |   useProxy(305), | ||||||
|   static const int temporaryRedirect = 307; |   temporaryRedirect(307), | ||||||
|   static const int permanentRedirect = 308; |   permanentRedirect(308), | ||||||
|   static const int badRequest = 400; |   badRequest(400), | ||||||
|   static const int unauthorized = 401; |   unauthorized(401), | ||||||
|   static const int paymentRequired = 402; |   paymentRequired(402), | ||||||
|   static const int forbidden = 403; |   forbidden(403), | ||||||
|   static const int notFound = 404; |   notFound(404), | ||||||
|   static const int methodNotAllowed = 405; |   methodNotAllowed(405), | ||||||
|   static const int notAcceptable = 406; |   notAcceptable(406), | ||||||
|   static const int proxyAuthenticationRequired = 407; |   proxyAuthenticationRequired(407), | ||||||
|   static const int requestTimeout = 408; |   requestTimeout(408), | ||||||
|   static const int conflict = 409; |   conflict(409), | ||||||
|   static const int gone = 410; |   gone(410), | ||||||
|   static const int lengthRequired = 411; |   lengthRequired(411), | ||||||
|   static const int preconditionFailed = 412; |   preconditionFailed(412), | ||||||
|   static const int requestEntityTooLarge = 413; |   requestEntityTooLarge(413), | ||||||
|   static const int requestUriTooLong = 414; |   requestUriTooLong(414), | ||||||
|   static const int unsupportedMediaType = 415; |   unsupportedMediaType(415), | ||||||
|   static const int requestedRangeNotSatisfiable = 416; |   requestedRangeNotSatisfiable(416), | ||||||
|   static const int expectationFailed = 417; |   expectationFailed(417), | ||||||
|   static const int misdirectedRequest = 421; |   misdirectedRequest(421), | ||||||
|   static const int unprocessableEntity = 422; |   unprocessableEntity(422), | ||||||
|   static const int locked = 423; |   locked(423), | ||||||
|   static const int failedDependency = 424; |   failedDependency(424), | ||||||
|   static const int upgradeRequired = 426; |   upgradeRequired(426), | ||||||
|   static const int preconditionRequired = 428; |   preconditionRequired(428), | ||||||
|   static const int tooManyRequests = 429; |   tooManyRequests(429), | ||||||
|   static const int requestHeaderFieldsTooLarge = 431; |   requestHeaderFieldsTooLarge(431), | ||||||
|   static const int connectionClosedWithoutResponse = 444; |   connectionClosedWithoutResponse(444), | ||||||
|   static const int unavailableForLegalReasons = 451; |   unavailableForLegalReasons(451), | ||||||
|   static const int clientClosedRequest = 499; |   clientClosedRequest(499), | ||||||
|   static const int internalServerError = 500; |   internalServerError(500), | ||||||
|   static const int notImplemented = 501; |   notImplemented(501), | ||||||
|   static const int badGateway = 502; |   badGateway(502), | ||||||
|   static const int serviceUnavailable = 503; |   serviceUnavailable(503), | ||||||
|   static const int gatewayTimeout = 504; |   gatewayTimeout(504), | ||||||
|   static const int httpVersionNotSupported = 505; |   httpVersionNotSupported(505), | ||||||
|   static const int variantAlsoNegotiates = 506; |   variantAlsoNegotiates(506), | ||||||
|   static const int insufficientStorage = 507; |   insufficientStorage(507), | ||||||
|   static const int loopDetected = 508; |   loopDetected(508), | ||||||
|   static const int notExtended = 510; |   notExtended(510), | ||||||
|   static const int networkAuthenticationRequired = 511; |   networkAuthenticationRequired(511), | ||||||
|   // Client generated status code. |   // Client generated status code. | ||||||
|   static const int networkConnectTimeoutError = 599; |   networkConnectTimeoutError(599); | ||||||
|  | 
 | ||||||
|  |   final int statusCode; | ||||||
|  | 
 | ||||||
|  |   const HttpStatus(this.statusCode); | ||||||
|  | 
 | ||||||
|  |   bool equals(Object other) { | ||||||
|  |     if (other is HttpStatus) { | ||||||
|  |       return statusCode == other.statusCode; | ||||||
|  |     } | ||||||
|  |     if (other is int) { | ||||||
|  |       return statusCode == other; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool isInfo() { | ||||||
|  |     return statusCode >= 100 && statusCode < 200;  | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool isSuccess() { | ||||||
|  |     return statusCode >= 200 && statusCode < 300;  | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool isRedirection() { | ||||||
|  |     return statusCode >= 300 && statusCode < 400;  | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool isClientError() { | ||||||
|  |     return statusCode >= 400 && statusCode < 500;  | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool isServerError() { | ||||||
|  |     return statusCode >= 500 && statusCode < 600;  | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   factory HttpStatus.from(int status) { | ||||||
|  |     return HttpStatus.values | ||||||
|  |         .firstWhere((element) => element.statusCode == status); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,6 +18,5 @@ enum Protocols { | |||||||
|   http, |   http, | ||||||
|   https; |   https; | ||||||
| 
 | 
 | ||||||
|   String get name => toString().split('.').last; |  | ||||||
|   String get scheme => '$name://'; |   String get scheme => '$name://'; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										89
									
								
								packages/wyatt_http_client/lib/src/utils/request_utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								packages/wyatt_http_client/lib/src/utils/request_utils.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'package:http/http.dart'; | ||||||
|  | 
 | ||||||
|  | abstract class RequestUtils { | ||||||
|  |   static Request _copyNormalRequestWith( | ||||||
|  |     Request original, { | ||||||
|  |     String? method, | ||||||
|  |     Uri? url, | ||||||
|  |     Map<String, String>? headers, | ||||||
|  |     int? maxRedirects, | ||||||
|  |     bool? followRedirects, | ||||||
|  |     bool? persistentConnection, | ||||||
|  |     String? body, | ||||||
|  |   }) { | ||||||
|  |     final request = Request(method ?? original.method, url ?? original.url) | ||||||
|  |       ..followRedirects = followRedirects ?? original.followRedirects | ||||||
|  |       ..headers.addAll(headers ?? original.headers) | ||||||
|  |       ..maxRedirects = maxRedirects ?? original.maxRedirects | ||||||
|  |       ..persistentConnection = | ||||||
|  |           persistentConnection ?? original.persistentConnection | ||||||
|  |       ..body = body ?? original.body; | ||||||
|  | 
 | ||||||
|  |     return request; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static BaseRequest copyRequestWith( | ||||||
|  |     BaseRequest original, { | ||||||
|  |     String? method, | ||||||
|  |     Uri? url, | ||||||
|  |     Map<String, String>? headers, | ||||||
|  |     int? maxRedirects, | ||||||
|  |     bool? followRedirects, | ||||||
|  |     bool? persistentConnection, | ||||||
|  |     String? body, | ||||||
|  |   }) { | ||||||
|  |     if (original is Request) { | ||||||
|  |       return _copyNormalRequestWith( | ||||||
|  |         original, | ||||||
|  |         method: method, | ||||||
|  |         url: url, | ||||||
|  |         headers: headers, | ||||||
|  |         maxRedirects: maxRedirects, | ||||||
|  |         followRedirects: followRedirects, | ||||||
|  |         persistentConnection: persistentConnection, | ||||||
|  |         body: body, | ||||||
|  |       ); | ||||||
|  |     } else { | ||||||
|  |       throw UnimplementedError( | ||||||
|  |         'Cannot handle requests of type ${original.runtimeType}', | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Request _copyNormalRequest(Request original) { | ||||||
|  |     final request = Request(original.method, original.url) | ||||||
|  |       ..followRedirects = original.followRedirects | ||||||
|  |       ..headers.addAll(original.headers) | ||||||
|  |       ..maxRedirects = original.maxRedirects | ||||||
|  |       ..persistentConnection = original.persistentConnection | ||||||
|  |       ..body = original.body; | ||||||
|  | 
 | ||||||
|  |     return request; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static BaseRequest copyRequest(BaseRequest original) { | ||||||
|  |     if (original is Request) { | ||||||
|  |       return _copyNormalRequest(original); | ||||||
|  |     } else { | ||||||
|  |       throw UnimplementedError( | ||||||
|  |         'Cannot handle requests of type ${original.runtimeType}', | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,89 +1,23 @@ | |||||||
| // Copyright (C) 2022 WYATT GROUP | // Copyright (C) 2022 WYATT GROUP | ||||||
| // Please see the AUTHORS file for details. | // Please see the AUTHORS file for details. | ||||||
| // | //  | ||||||
| // This program is free software: you can redistribute it and/or modify | // This program is free software: you can redistribute it and/or modify | ||||||
| // it under the terms of the GNU General Public License as published by | // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | // the Free Software Foundation, either version 3 of the License, or | ||||||
| // any later version. | // any later version. | ||||||
| // | //  | ||||||
| // This program is distributed in the hope that it will be useful, | // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | // GNU General Public License for more details. | ||||||
| // | //  | ||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| import 'package:http/http.dart'; | export 'authentication_methods.dart'; | ||||||
| 
 | export 'digest_auth.dart'; | ||||||
| abstract class Utils { | export 'header_keys.dart'; | ||||||
|   static Request _copyNormalRequest(Request original) { | export 'http_methods.dart'; | ||||||
|     final request = Request(original.method, original.url) | export 'http_status.dart'; | ||||||
|       ..followRedirects = original.followRedirects | export 'protocols.dart'; | ||||||
|       ..headers.addAll(original.headers) | export 'request_utils.dart'; | ||||||
|       ..maxRedirects = original.maxRedirects |  | ||||||
|       ..persistentConnection = original.persistentConnection |  | ||||||
|       ..body = original.body; |  | ||||||
| 
 |  | ||||||
|     return request; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   static Request _copyNormalRequestWith( |  | ||||||
|     Request original, { |  | ||||||
|     String? method, |  | ||||||
|     Uri? url, |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     int? maxRedirects, |  | ||||||
|     bool? followRedirects, |  | ||||||
|     bool? persistentConnection, |  | ||||||
|     String? body, |  | ||||||
|   }) { |  | ||||||
|     final request = Request(method ?? original.method, url ?? original.url) |  | ||||||
|       ..followRedirects = followRedirects ?? original.followRedirects |  | ||||||
|       ..headers.addAll(headers ?? original.headers) |  | ||||||
|       ..maxRedirects = maxRedirects ?? original.maxRedirects |  | ||||||
|       ..persistentConnection = |  | ||||||
|           persistentConnection ?? original.persistentConnection |  | ||||||
|       ..body = body ?? original.body; |  | ||||||
| 
 |  | ||||||
|     return request; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   static BaseRequest copyRequest(BaseRequest original) { |  | ||||||
|     if (original is Request) { |  | ||||||
|       return _copyNormalRequest(original); |  | ||||||
|     } else { |  | ||||||
|       throw UnimplementedError( |  | ||||||
|         'Cannot handle requests of type ${original.runtimeType}', |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   static BaseRequest copyRequestWith( |  | ||||||
|     BaseRequest original, { |  | ||||||
|     String? method, |  | ||||||
|     Uri? url, |  | ||||||
|     Map<String, String>? headers, |  | ||||||
|     int? maxRedirects, |  | ||||||
|     bool? followRedirects, |  | ||||||
|     bool? persistentConnection, |  | ||||||
|     String? body, |  | ||||||
|   }) { |  | ||||||
|     if (original is Request) { |  | ||||||
|       return _copyNormalRequestWith( |  | ||||||
|         original, |  | ||||||
|         method: method, |  | ||||||
|         url: url, |  | ||||||
|         headers: headers, |  | ||||||
|         maxRedirects: maxRedirects, |  | ||||||
|         followRedirects: followRedirects, |  | ||||||
|         persistentConnection: persistentConnection, |  | ||||||
|         body: body, |  | ||||||
|       ); |  | ||||||
|     } else { |  | ||||||
|       throw UnimplementedError( |  | ||||||
|         'Cannot handle requests of type ${original.runtimeType}', |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,17 +1,24 @@ | |||||||
| // Copyright (C) 2022 WYATT GROUP | // Copyright (C) 2022 WYATT GROUP | ||||||
| // Please see the AUTHORS file for details. | // Please see the AUTHORS file for details. | ||||||
| //  | // | ||||||
| // This program is free software: you can redistribute it and/or modify | // This program is free software: you can redistribute it and/or modify | ||||||
| // it under the terms of the GNU General Public License as published by | // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | // the Free Software Foundation, either version 3 of the License, or | ||||||
| // any later version. | // any later version. | ||||||
| //  | // | ||||||
| // This program is distributed in the hope that it will be useful, | // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | // GNU General Public License for more details. | ||||||
| //  | // | ||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| library wyatt_http_client; | library wyatt_http_client; | ||||||
|  | 
 | ||||||
|  | export 'src/middleware.dart'; | ||||||
|  | export 'src/middleware_client.dart'; | ||||||
|  | export 'src/middlewares/middlewares.dart'; | ||||||
|  | export 'src/models/models.dart'; | ||||||
|  | export 'src/pipeline.dart'; | ||||||
|  | export 'src/utils/utils.dart'; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user