1
0
Fork 0
mirror of https://github.com/wallabag/wallabag.git synced 2025-06-27 16:36:00 +00:00

Migrate from Guzzle to Symfony HttpClient

This commit is contained in:
Yassine Guedidi 2025-01-09 01:14:27 +01:00
parent 335054c9a3
commit 63dc176302
12 changed files with 322 additions and 964 deletions

View file

@ -32,7 +32,6 @@ class AppKernel extends Kernel
new BabDev\PagerfantaBundle\BabDevPagerfantaBundle(), new BabDev\PagerfantaBundle\BabDevPagerfantaBundle(),
new FOS\JsRoutingBundle\FOSJsRoutingBundle(), new FOS\JsRoutingBundle\FOSJsRoutingBundle(),
new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(), new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(),
new Http\HttplugBundle\HttplugBundle(),
new Sentry\SentryBundle\SentryBundle(), new Sentry\SentryBundle\SentryBundle(),
new Twig\Extra\TwigExtraBundle\TwigExtraBundle(), new Twig\Extra\TwigExtraBundle\TwigExtraBundle(),
new Symfony\WebpackEncoreBundle\WebpackEncoreBundle(), new Symfony\WebpackEncoreBundle\WebpackEncoreBundle(),

View file

@ -48,6 +48,10 @@ framework:
X-Accept: 'application/json' X-Accept: 'application/json'
request_html_function.client: request_html_function.client:
scope: '.*' scope: '.*'
browser.client:
scope: '.*'
verify_host: false
verify_peer: false
# Twig Configuration # Twig Configuration
twig: twig:
@ -452,17 +456,6 @@ sensio_framework_extra:
router: router:
annotations: false annotations: false
httplug:
clients:
wallabag:
factory: Wallabag\Helper\HttpClientFactory
config:
defaults:
timeout: 10
plugins: ['httplug.plugin.logger']
discovery:
client: false
# define custom entity so we can override length attribute to fix utf8mb4 issue # define custom entity so we can override length attribute to fix utf8mb4 issue
craue_config: craue_config:
entity_name: Wallabag\Entity\InternalSetting entity_name: Wallabag\Entity\InternalSetting

View file

@ -191,11 +191,17 @@ services:
tags: tags:
- { name: doctrine.event_subscriber } - { name: doctrine.event_subscriber }
psr18.wallabag.client:
class: Symfony\Component\HttpClient\Psr18Client
arguments:
$client: '@Wallabag\HttpClient\WallabagClient'
Graby\Graby: Graby\Graby:
arguments: arguments:
$config: $config:
error_message: '%wallabag.fetching_error_message%' error_message: '%wallabag.fetching_error_message%'
error_message_title: '%wallabag.fetching_error_message_title%' error_message_title: '%wallabag.fetching_error_message_title%'
$client: '@psr18.wallabag.client'
calls: calls:
- [ setLogger, [ "@logger" ] ] - [ setLogger, [ "@logger" ] ]
tags: tags:
@ -205,9 +211,6 @@ services:
arguments: arguments:
$config: {} $config: {}
wallabag.http_client:
alias: 'httplug.client.wallabag'
Wallabag\SiteConfig\GrabySiteConfigBuilder: Wallabag\SiteConfig\GrabySiteConfigBuilder:
tags: tags:
- { name: monolog.logger, channel: graby } - { name: monolog.logger, channel: graby }
@ -216,11 +219,9 @@ services:
Wallabag\SiteConfig\SiteConfigBuilder: Wallabag\SiteConfig\SiteConfigBuilder:
alias: Wallabag\SiteConfig\GrabySiteConfigBuilder alias: Wallabag\SiteConfig\GrabySiteConfigBuilder
GuzzleHttp\Cookie\CookieJar: ~ Symfony\Component\BrowserKit\HttpBrowser:
arguments:
Wallabag\Helper\HttpClientFactory: $client: '@browser.client'
calls:
- ['addSubscriber', ['@Wallabag\HttpClient\Authenticator']]
RulerZ\RulerZ: RulerZ\RulerZ:
alias: rulerz alias: rulerz

View file

@ -29,8 +29,6 @@ $config
'mnapoli/piwik-twig-extension', 'mnapoli/piwik-twig-extension',
'ocramius/proxy-manager', 'ocramius/proxy-manager',
'pagerfanta/twig', 'pagerfanta/twig',
'php-http/client-common',
'php-http/httplug',
'php-http/mock-client', 'php-http/mock-client',
'phpstan/extension-installer', 'phpstan/extension-installer',
'phpstan/phpstan', 'phpstan/phpstan',
@ -39,13 +37,11 @@ $config
'phpstan/phpstan-symfony', 'phpstan/phpstan-symfony',
'psr/http-client', 'psr/http-client',
'psr/http-factory', 'psr/http-factory',
'psr/http-message',
'rulerz-php/doctrine-orm', 'rulerz-php/doctrine-orm',
'scheb/2fa-qr-code', 'scheb/2fa-qr-code',
'scheb/2fa-trusted-device', 'scheb/2fa-trusted-device',
'shipmonk/composer-dependency-analyser', 'shipmonk/composer-dependency-analyser',
'symfony/asset', 'symfony/asset',
'symfony/browser-kit',
'symfony/css-selector', 'symfony/css-selector',
'symfony/doctrine-bridge', 'symfony/doctrine-bridge',
'symfony/google-mailer', 'symfony/google-mailer',
@ -57,10 +53,8 @@ $config
'twig/string-extra', 'twig/string-extra',
], [ErrorType::UNUSED_DEPENDENCY]) ], [ErrorType::UNUSED_DEPENDENCY])
->ignoreErrorsOnPackages([ ->ignoreErrorsOnPackages([
'guzzlehttp/streams',
'monolog/monolog', 'monolog/monolog',
'symfony/filesystem', 'symfony/filesystem',
'symfony/http-client',
], [ErrorType::PROD_DEPENDENCY_ONLY_IN_DEV]) ], [ErrorType::PROD_DEPENDENCY_ONLY_IN_DEV])
->ignoreErrorsOnPackages([ ->ignoreErrorsOnPackages([
'dama/doctrine-test-bundle', 'dama/doctrine-test-bundle',

View file

@ -75,9 +75,7 @@
"friendsofsymfony/oauth-server-bundle": "dev-master#dc8ff343363cf794d30eb1a123610d186a43f162", "friendsofsymfony/oauth-server-bundle": "dev-master#dc8ff343363cf794d30eb1a123610d186a43f162",
"friendsofsymfony/rest-bundle": "^3.6", "friendsofsymfony/rest-bundle": "^3.6",
"friendsofsymfony/user-bundle": "^3.2.1", "friendsofsymfony/user-bundle": "^3.2.1",
"guzzlehttp/guzzle": "^5.3.4",
"guzzlehttp/psr7": "^2.6.2", "guzzlehttp/psr7": "^2.6.2",
"guzzlehttp/streams": "^3.0",
"html2text/html2text": "^4.3.1", "html2text/html2text": "^4.3.1",
"incenteev/composer-parameter-handler": "^2.2", "incenteev/composer-parameter-handler": "^2.2",
"j0k3r/graby": "^2.4.5", "j0k3r/graby": "^2.4.5",
@ -99,10 +97,6 @@
"pagerfanta/twig": "^3.8", "pagerfanta/twig": "^3.8",
"php-amqplib/php-amqplib": "^3.6.1", "php-amqplib/php-amqplib": "^3.6.1",
"php-amqplib/rabbitmq-bundle": "^2.14.0", "php-amqplib/rabbitmq-bundle": "^2.14.0",
"php-http/client-common": "^2.7.1",
"php-http/guzzle5-adapter": "^2.0",
"php-http/httplug": "^2.4",
"php-http/httplug-bundle": "^1.32",
"pragmarx/recovery": "^0.2.1", "pragmarx/recovery": "^0.2.1",
"predis/predis": "^2.2.2", "predis/predis": "^2.2.2",
"psr/http-client": "^1.0.3", "psr/http-client": "^1.0.3",

603
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "efc4ae447cf6faadf84c1551b73682dc", "content-hash": "a85056bec7fa90b9be4aa16c34464c0e",
"packages": [ "packages": [
{ {
"name": "babdev/pagerfanta-bundle", "name": "babdev/pagerfanta-bundle",
@ -2980,63 +2980,6 @@
}, },
"time": "2015-05-14T08:18:23+00:00" "time": "2015-05-14T08:18:23+00:00"
}, },
{
"name": "guzzlehttp/guzzle",
"version": "5.3.4",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "b87eda7a7162f95574032da17e9323c9899cb6b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/b87eda7a7162f95574032da17e9323c9899cb6b2",
"reference": "b87eda7a7162f95574032da17e9323c9899cb6b2",
"shasum": ""
},
"require": {
"guzzlehttp/ringphp": "^1.1",
"php": ">=5.4.0",
"react/promise": "^2.2"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.0"
},
"type": "library",
"autoload": {
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/5.3"
},
"time": "2019-10-30T09:32:00+00:00"
},
{ {
"name": "guzzlehttp/psr7", "name": "guzzlehttp/psr7",
"version": "2.7.0", "version": "2.7.0",
@ -3153,117 +3096,6 @@
], ],
"time": "2024-07-18T11:15:46+00:00" "time": "2024-07-18T11:15:46+00:00"
}, },
{
"name": "guzzlehttp/ringphp",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/RingPHP.git",
"reference": "5e2a174052995663dd68e6b5ad838afd47dd615b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/RingPHP/zipball/5e2a174052995663dd68e6b5ad838afd47dd615b",
"reference": "5e2a174052995663dd68e6b5ad838afd47dd615b",
"shasum": ""
},
"require": {
"guzzlehttp/streams": "~3.0",
"php": ">=5.4.0",
"react/promise": "~2.0"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "~4.0"
},
"suggest": {
"ext-curl": "Guzzle will use specific adapters if cURL is present"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Ring\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.",
"support": {
"issues": "https://github.com/guzzle/RingPHP/issues",
"source": "https://github.com/guzzle/RingPHP/tree/1.1.1"
},
"abandoned": true,
"time": "2018-07-31T13:22:33+00:00"
},
{
"name": "guzzlehttp/streams",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/streams.git",
"reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5",
"reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Stream\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Provides a simple abstraction over streams of data",
"homepage": "http://guzzlephp.org/",
"keywords": [
"Guzzle",
"stream"
],
"support": {
"issues": "https://github.com/guzzle/streams/issues",
"source": "https://github.com/guzzle/streams/tree/master"
},
"abandoned": true,
"time": "2014-10-12T19:18:40+00:00"
},
{ {
"name": "hoa/compiler", "name": "hoa/compiler",
"version": "3.17.08.08", "version": "3.17.08.08",
@ -6511,74 +6343,6 @@
}, },
"time": "2024-10-02T11:20:13+00:00" "time": "2024-10-02T11:20:13+00:00"
}, },
{
"name": "php-http/guzzle5-adapter",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-http/guzzle5-adapter.git",
"reference": "cce48360b1f8a3467bd94e853e6107aa4532008e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/guzzle5-adapter/zipball/cce48360b1f8a3467bd94e853e6107aa4532008e",
"reference": "cce48360b1f8a3467bd94e853e6107aa4532008e",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^5.1",
"php": "^7.0",
"php-http/discovery": "^1.0",
"php-http/httplug": "^2.0"
},
"provide": {
"php-http/client-implementation": "1.0",
"psr/http-client-implementation": "1.0"
},
"require-dev": {
"ext-curl": "*",
"guzzlehttp/ringphp": "^1.1",
"php-http/client-integration-tests": "^2.0",
"phpunit/phpunit": "^6.0 || ^7.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"psr-4": {
"Http\\Adapter\\Guzzle5\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Eric GELOEN",
"email": "geloen.eric@gmail.com"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
}
],
"description": "Guzzle 5 HTTP Adapter",
"homepage": "http://httplug.io",
"keywords": [
"Guzzle",
"http"
],
"support": {
"issues": "https://github.com/php-http/guzzle5-adapter/issues",
"source": "https://github.com/php-http/guzzle5-adapter/tree/2.0.0"
},
"abandoned": "php-http/guzzle7-adapter",
"time": "2019-02-05T12:28:45+00:00"
},
{ {
"name": "php-http/httplug", "name": "php-http/httplug",
"version": "2.4.1", "version": "2.4.1",
@ -6636,169 +6400,6 @@
}, },
"time": "2024-09-23T11:39:58+00:00" "time": "2024-09-23T11:39:58+00:00"
}, },
{
"name": "php-http/httplug-bundle",
"version": "1.34.3",
"source": {
"type": "git",
"url": "https://github.com/php-http/HttplugBundle.git",
"reference": "87c61d27c025dd9d699a2208a6d06be58061e433"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/HttplugBundle/zipball/87c61d27c025dd9d699a2208a6d06be58061e433",
"reference": "87c61d27c025dd9d699a2208a6d06be58061e433",
"shasum": ""
},
"require": {
"php": "^7.3 || ^8.0",
"php-http/client-common": "^1.9 || ^2.0",
"php-http/client-implementation": "^1.0",
"php-http/discovery": "^1.14",
"php-http/httplug": "^2.0",
"php-http/logger-plugin": "^1.1",
"php-http/message": "^1.13",
"php-http/message-factory": "^1.0.2",
"php-http/stopwatch-plugin": "^1.2",
"psr/http-message": "^1.0 || ^2.0",
"symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/http-kernel": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/options-resolver": "^4.4 || ^5.0 || ^6.0 || ^7.0"
},
"conflict": {
"php-http/cache-plugin": "<1.7.0",
"php-http/curl-client": "<2.0",
"php-http/guzzle6-adapter": "<1.1",
"php-http/socket-client": "<2.0",
"php-http/throttle-plugin": "<1.1"
},
"require-dev": {
"guzzlehttp/psr7": "^1.7 || ^2.0",
"matthiasnoback/symfony-config-test": "^4.3 || ^5.0",
"matthiasnoback/symfony-dependency-injection-test": "^4.3.1 || ^5.0",
"nyholm/nsa": "^1.1",
"nyholm/psr7": "^1.2.1",
"php-http/cache-plugin": "^1.7",
"php-http/mock-client": "^1.2",
"php-http/promise": "^1.0",
"phpunit/phpunit": "^9.6",
"symfony/browser-kit": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/cache": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/dom-crawler": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/http-foundation": "^4.4.19 || ^5.0 || ^6.0 || ^7.0",
"symfony/stopwatch": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/twig-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/web-profiler-bundle": "^4.4.19 || ^5.0 || ^6.0 || ^7.0",
"twig/twig": "^1.41 || ^2.10 || ^3.0"
},
"suggest": {
"php-http/cache-plugin": "To configure clients that cache responses",
"php-http/mock-client": "Add this to your require-dev section to mock HTTP responses easily",
"twig/twig": "Add this to your require-dev section when using the WebProfilerBundle"
},
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Http\\HttplugBundle\\": "src/"
},
"exclude-from-classmap": [
"/Tests/Resources/MyPsr18TestClient.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "David Buchmann",
"email": "mail@davidbu.ch"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com"
}
],
"description": "Symfony integration for HTTPlug",
"homepage": "http://httplug.io",
"keywords": [
"adapter",
"bundle",
"discovery",
"factory",
"http",
"httplug",
"message",
"php-http"
],
"support": {
"issues": "https://github.com/php-http/HttplugBundle/issues",
"source": "https://github.com/php-http/HttplugBundle/tree/1.34.3"
},
"time": "2024-09-01T08:25:40+00:00"
},
{
"name": "php-http/logger-plugin",
"version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/php-http/logger-plugin.git",
"reference": "bf47eb5cb379962d276c94da14861669c2313563"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/logger-plugin/zipball/bf47eb5cb379962d276c94da14861669c2313563",
"reference": "bf47eb5cb379962d276c94da14861669c2313563",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0",
"php-http/client-common": "^1.9 || ^2.0",
"php-http/message": "^1.0",
"psr/log": "^1.0 || ^2 || ^3",
"symfony/polyfill-php73": "^1.17"
},
"require-dev": {
"phpspec/phpspec": "^5.1 || ^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
},
"autoload": {
"psr-4": {
"Http\\Client\\Common\\Plugin\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
}
],
"description": "PSR-3 Logger plugin for HTTPlug",
"homepage": "http://httplug.io",
"keywords": [
"http",
"httplug",
"logger",
"plugin"
],
"support": {
"issues": "https://github.com/php-http/logger-plugin/issues",
"source": "https://github.com/php-http/logger-plugin/tree/1.3.1"
},
"time": "2024-09-01T06:51:51+00:00"
},
{ {
"name": "php-http/message", "name": "php-http/message",
"version": "1.16.2", "version": "1.16.2",
@ -6975,64 +6576,6 @@
}, },
"time": "2024-03-15T13:55:21+00:00" "time": "2024-03-15T13:55:21+00:00"
}, },
{
"name": "php-http/stopwatch-plugin",
"version": "1.4.2",
"source": {
"type": "git",
"url": "https://github.com/php-http/stopwatch-plugin.git",
"reference": "11862cfbc719afade4ff407964ab3fbfe9ec2baa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/stopwatch-plugin/zipball/11862cfbc719afade4ff407964ab3fbfe9ec2baa",
"reference": "11862cfbc719afade4ff407964ab3fbfe9ec2baa",
"shasum": ""
},
"require": {
"php": "^7.3 || ^8.0",
"php-http/client-common": "^1.9 || ^2.0",
"symfony/stopwatch": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"require-dev": {
"guzzlehttp/psr7": "^2.1",
"symfony/phpunit-bridge": "^6.4.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
}
},
"autoload": {
"psr-4": {
"Http\\Client\\Common\\Plugin\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
}
],
"description": "Symfony Stopwatch plugin for HTTPlug",
"homepage": "http://httplug.io",
"keywords": [
"http",
"httplug",
"plugin",
"stopwatch"
],
"support": {
"issues": "https://github.com/php-http/stopwatch-plugin/issues",
"source": "https://github.com/php-http/stopwatch-plugin/tree/1.4.2"
},
"time": "2023-12-05T14:36:57+00:00"
},
{ {
"name": "phpdocumentor/reflection-common", "name": "phpdocumentor/reflection-common",
"version": "2.2.0", "version": "2.2.0",
@ -8126,78 +7669,6 @@
}, },
"time": "2019-03-08T08:55:37+00:00" "time": "2019-03-08T08:55:37+00:00"
}, },
{
"name": "react/promise",
"version": "v2.11.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/promise.git",
"reference": "1a8460931ea36dc5c76838fec5734d55c88c6831"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/promise/zipball/1a8460931ea36dc5c76838fec5734d55c88c6831",
"reference": "1a8460931ea36dc5c76838fec5734d55c88c6831",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
},
"type": "library",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"React\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "A lightweight implementation of CommonJS Promises/A for PHP",
"keywords": [
"promise",
"promises"
],
"support": {
"issues": "https://github.com/reactphp/promise/issues",
"source": "https://github.com/reactphp/promise/tree/v2.11.0"
},
"funding": [
{
"url": "https://opencollective.com/reactphp",
"type": "open_collective"
}
],
"time": "2023-11-16T16:16:50+00:00"
},
{ {
"name": "rulerz-php/doctrine-orm", "name": "rulerz-php/doctrine-orm",
"version": "dev-master", "version": "dev-master",
@ -18605,6 +18076,78 @@
], ],
"time": "2023-11-13T13:48:05+00:00" "time": "2023-11-13T13:48:05+00:00"
}, },
{
"name": "react/promise",
"version": "v2.11.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/promise.git",
"reference": "1a8460931ea36dc5c76838fec5734d55c88c6831"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/promise/zipball/1a8460931ea36dc5c76838fec5734d55c88c6831",
"reference": "1a8460931ea36dc5c76838fec5734d55c88c6831",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
},
"type": "library",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"React\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "A lightweight implementation of CommonJS Promises/A for PHP",
"keywords": [
"promise",
"promises"
],
"support": {
"issues": "https://github.com/reactphp/promise/issues",
"source": "https://github.com/reactphp/promise/tree/v2.11.0"
},
"funding": [
{
"url": "https://opencollective.com/reactphp",
"type": "open_collective"
}
],
"time": "2023-11-16T16:16:50+00:00"
},
{ {
"name": "react/socket", "name": "react/socket",
"version": "v1.16.0", "version": "v1.16.0",

View file

@ -1,74 +0,0 @@
<?php
namespace Wallabag\Helper;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Event\SubscriberInterface;
use Http\Adapter\Guzzle5\Client as GuzzleAdapter;
use Http\Client\HttpClient;
use Http\HttplugBundle\ClientFactory\ClientFactory;
use Psr\Log\LoggerInterface;
/**
* Builds and configures the HTTP client.
*/
class HttpClientFactory implements ClientFactory
{
/** @var SubscriberInterface[] */
private $subscribers = [];
/** @var CookieJar */
private $cookieJar;
private $restrictedAccess;
private $logger;
/**
* HttpClientFactory constructor.
*
* @param string $restrictedAccess This param is a kind of boolean. Values: 0 or 1
*/
public function __construct(CookieJar $cookieJar, $restrictedAccess, LoggerInterface $logger)
{
$this->cookieJar = $cookieJar;
$this->restrictedAccess = $restrictedAccess;
$this->logger = $logger;
}
/**
* Adds a subscriber to the HTTP client.
*/
public function addSubscriber(SubscriberInterface $subscriber)
{
$this->subscribers[] = $subscriber;
}
/**
* Input an array of configuration to be able to create a HttpClient.
*
* @return HttpClient
*/
public function createClient(array $config = [])
{
$this->logger->log('debug', 'Restricted access config enabled?', ['enabled' => (int) $this->restrictedAccess]);
if (0 === (int) $this->restrictedAccess) {
return new GuzzleAdapter(new GuzzleClient($config));
}
// we clear the cookie to avoid websites who use cookies for analytics
$this->cookieJar->clear();
if (!isset($config['defaults']['cookies'])) {
// need to set the (shared) cookie jar
$config['defaults']['cookies'] = $this->cookieJar;
}
$guzzle = new GuzzleClient($config);
foreach ($this->subscribers as $subscriber) {
$guzzle->getEmitter()->attach($subscriber);
}
return new GuzzleAdapter($guzzle);
}
}

View file

@ -2,24 +2,18 @@
namespace Wallabag\HttpClient; namespace Wallabag\HttpClient;
use GuzzleHttp\Event\BeforeEvent; use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Event\CompleteEvent; use Psr\Http\Message\UriInterface;
use GuzzleHttp\Event\SubscriberInterface;
use GuzzleHttp\Message\RequestInterface;
use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger; use Psr\Log\NullLogger;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Wallabag\SiteConfig\LoginFormAuthenticator; use Wallabag\SiteConfig\LoginFormAuthenticator;
use Wallabag\SiteConfig\SiteConfig; use Wallabag\SiteConfig\SiteConfig;
use Wallabag\SiteConfig\SiteConfigBuilder; use Wallabag\SiteConfig\SiteConfigBuilder;
class Authenticator implements SubscriberInterface, LoggerAwareInterface class Authenticator implements LoggerAwareInterface
{ {
// avoid loop when login failed which can just be a bad login/password
// after 2 attempts, we skip the login
public const MAX_RETRIES = 2;
private int $retries = 0;
/** @var SiteConfigBuilder */ /** @var SiteConfigBuilder */
private $configBuilder; private $configBuilder;
@ -29,9 +23,6 @@ class Authenticator implements SubscriberInterface, LoggerAwareInterface
/** @var LoggerInterface */ /** @var LoggerInterface */
private $logger; private $logger;
/**
* AuthenticatorSubscriber constructor.
*/
public function __construct(SiteConfigBuilder $configBuilder, LoginFormAuthenticator $authenticator) public function __construct(SiteConfigBuilder $configBuilder, LoginFormAuthenticator $authenticator)
{ {
$this->configBuilder = $configBuilder; $this->configBuilder = $configBuilder;
@ -44,78 +35,61 @@ class Authenticator implements SubscriberInterface, LoggerAwareInterface
$this->logger = $logger; $this->logger = $logger;
} }
public function getEvents(): array public function loginIfRequired(string $url): bool
{ {
return [ $config = $this->buildSiteConfig(new Uri($url));
'before' => ['loginIfRequired'],
'complete' => ['loginIfRequested'],
];
}
public function loginIfRequired(BeforeEvent $event)
{
$config = $this->buildSiteConfig($event->getRequest());
if (false === $config || !$config->requiresLogin()) { if (false === $config || !$config->requiresLogin()) {
$this->logger->debug('loginIfRequired> will not require login'); $this->logger->debug('loginIfRequired> will not require login');
return; return false;
} }
$client = $event->getClient(); if ($this->authenticator->isLoggedIn($config)) {
return false;
if (!$this->authenticator->isLoggedIn($config, $client)) {
$this->logger->debug('loginIfRequired> user is not logged in, attach authenticator');
$emitter = $client->getEmitter();
$emitter->detach($this);
$this->authenticator->login($config, $client);
$emitter->attach($this);
} }
$this->logger->debug('loginIfRequired> user is not logged in, attach authenticator');
$this->authenticator->login($config);
return true;
} }
public function loginIfRequested(CompleteEvent $event) public function loginIfRequested(ResponseInterface $response): bool
{ {
$config = $this->buildSiteConfig($event->getRequest()); $config = $this->buildSiteConfig(new Uri($response->getInfo('url')));
if (false === $config || !$config->requiresLogin()) { if (false === $config || !$config->requiresLogin()) {
$this->logger->debug('loginIfRequested> will not require login'); $this->logger->debug('loginIfRequested> will not require login');
return; return false;
} }
$body = $event->getResponse()->getBody(); $body = $response->getContent();
if ( if ('' === $body) {
null === $body
|| '' === $body->getContents()
) {
$this->logger->debug('loginIfRequested> empty body, ignoring'); $this->logger->debug('loginIfRequested> empty body, ignoring');
return; return false;
} }
$isLoginRequired = $this->authenticator->isLoginRequired($config, $body); $isLoginRequired = $this->authenticator->isLoginRequired($config, $body);
$this->logger->debug('loginIfRequested> retry #' . $this->retries . ' with login ' . ($isLoginRequired ? '' : 'not ') . 'required'); $this->logger->debug('loginIfRequested> retry with login ' . ($isLoginRequired ? '' : 'not ') . 'required');
if ($isLoginRequired && $this->retries < self::MAX_RETRIES) { if (!$isLoginRequired) {
$client = $event->getClient(); return false;
$emitter = $client->getEmitter();
$emitter->detach($this);
$this->authenticator->login($config, $client);
$emitter->attach($this);
$event->retry();
++$this->retries;
} }
$this->authenticator->login($config);
return true;
} }
/** /**
* @return SiteConfig|false * @return SiteConfig|false
*/ */
private function buildSiteConfig(RequestInterface $request) private function buildSiteConfig(UriInterface $uri)
{ {
return $this->configBuilder->buildForHost($request->getHost()); return $this->configBuilder->buildForHost($uri->getHost());
} }
} }

View file

@ -0,0 +1,84 @@
<?php
namespace Wallabag\HttpClient;
use Psr\Log\LoggerInterface;
use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
class WallabagClient implements HttpClientInterface
{
private $restrictedAccess;
private HttpClientInterface $httpClient;
private HttpBrowser $browser;
private Authenticator $authenticator;
private LoggerInterface $logger;
public function __construct($restrictedAccess, HttpBrowser $browser, Authenticator $authenticator, LoggerInterface $logger)
{
$this->restrictedAccess = $restrictedAccess;
$this->browser = $browser;
$this->authenticator = $authenticator;
$this->logger = $logger;
$this->httpClient = HttpClient::create([
'timeout' => 10,
]);
}
public function request(string $method, string $url, array $options = []): ResponseInterface
{
$this->logger->log('debug', 'Restricted access config enabled?', ['enabled' => (int) $this->restrictedAccess]);
if (0 === (int) $this->restrictedAccess) {
return $this->httpClient->request($method, $url, $options);
}
$login = $this->authenticator->loginIfRequired($url);
if (!$login) {
return $this->httpClient->request($method, $url, $options);
}
if (null !== $cookieHeader = $this->getCookieHeader($url)) {
$options['headers']['cookie'] = $cookieHeader;
}
$response = $this->httpClient->request($method, $url, $options);
$login = $this->authenticator->loginIfRequested($response);
if (!$login) {
return $response;
}
if (null !== $cookieHeader = $this->getCookieHeader($url)) {
$options['headers']['cookie'] = $cookieHeader;
}
return $this->httpClient->request($method, $url, $options);
}
public function stream($responses, ?float $timeout = null): ResponseStreamInterface
{
return $this->httpClient->stream($responses, $timeout);
}
private function getCookieHeader(string $url): ?string
{
$cookies = [];
foreach ($this->browser->getCookieJar()->allRawValues($url) as $name => $value) {
$cookies[] = $name . '=' . $value;
}
if ([] === $cookies) {
return null;
}
return implode('; ', $cookies);
}
}

View file

@ -2,18 +2,19 @@
namespace Wallabag\SiteConfig; namespace Wallabag\SiteConfig;
use GuzzleHttp\ClientInterface; use Symfony\Component\BrowserKit\HttpBrowser;
use GuzzleHttp\Cookie\CookieJar;
use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Wallabag\ExpressionLanguage\AuthenticatorProvider; use Wallabag\ExpressionLanguage\AuthenticatorProvider;
class LoginFormAuthenticator class LoginFormAuthenticator
{ {
private HttpBrowser $browser;
private ExpressionLanguage $expressionLanguage; private ExpressionLanguage $expressionLanguage;
public function __construct(AuthenticatorProvider $authenticatorProvider) public function __construct(HttpBrowser $browser, AuthenticatorProvider $authenticatorProvider)
{ {
$this->browser = $browser;
$this->expressionLanguage = new ExpressionLanguage(null, [$authenticatorProvider]); $this->expressionLanguage = new ExpressionLanguage(null, [$authenticatorProvider]);
} }
@ -22,17 +23,14 @@ class LoginFormAuthenticator
* *
* @return self * @return self
*/ */
public function login(SiteConfig $siteConfig, ClientInterface $guzzle) public function login(SiteConfig $siteConfig)
{ {
$postFields = [ $postFields = [
$siteConfig->getUsernameField() => $siteConfig->getUsername(), $siteConfig->getUsernameField() => $siteConfig->getUsername(),
$siteConfig->getPasswordField() => $siteConfig->getPassword(), $siteConfig->getPasswordField() => $siteConfig->getPassword(),
] + $this->getExtraFields($siteConfig); ] + $this->getExtraFields($siteConfig);
$guzzle->post( $this->browser->request('POST', $siteConfig->getLoginUri(), $postFields);
$siteConfig->getLoginUri(),
['body' => $postFields, 'allow_redirects' => true, 'verify' => false]
);
return $this; return $this;
} }
@ -42,15 +40,12 @@ class LoginFormAuthenticator
* *
* @return bool * @return bool
*/ */
public function isLoggedIn(SiteConfig $siteConfig, ClientInterface $guzzle) public function isLoggedIn(SiteConfig $siteConfig)
{ {
if (($cookieJar = $guzzle->getDefaultOption('cookies')) instanceof CookieJar) { foreach ($this->browser->getCookieJar()->all() as $cookie) {
/** @var \GuzzleHttp\Cookie\SetCookie $cookie */ // check required cookies
foreach ($cookieJar as $cookie) { if ($cookie->getDomain() === $siteConfig->getHost()) {
// check required cookies return true;
if ($cookie->getDomain() === $siteConfig->getHost()) {
return true;
}
} }
} }

View file

@ -2,13 +2,12 @@
namespace Tests\Wallabag\SiteConfig; namespace Tests\Wallabag\SiteConfig;
use GuzzleHttp\Client;
use GuzzleHttp\Message\Response;
use GuzzleHttp\Stream\Stream;
use GuzzleHttp\Subscriber\Mock;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Wallabag\ExpressionLanguage\AuthenticatorProvider; use Wallabag\ExpressionLanguage\AuthenticatorProvider;
use Wallabag\SiteConfig\LoginFormAuthenticator; use Wallabag\SiteConfig\LoginFormAuthenticator;
use Wallabag\SiteConfig\SiteConfig; use Wallabag\SiteConfig\SiteConfig;
@ -30,19 +29,17 @@ class LoginFormAuthenticatorTest extends TestCase
'password' => 'unkn0wn', 'password' => 'unkn0wn',
]); ]);
$response = new Response( $browserResponse = new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]);
200, $browserClient = new MockHttpClient([$browserResponse]);
['content-type' => 'text/html'], $browser = new HttpBrowser($browserClient);
Stream::factory('')
);
$guzzle = new Client();
$guzzle->getEmitter()->attach(new Mock([$response]));
$mockHttpClient = new MockHttpClient([new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]); $requestHtmlFunctionResponse = new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]);
$requestHtmlFunctionClient = new MockHttpClient([$requestHtmlFunctionResponse]);
$authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient);
$authenticatorProvider = new AuthenticatorProvider($mockHttpClient); $auth = new LoginFormAuthenticator($browser, $authenticatorProvider);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$res = $auth->login($siteConfig, $guzzle); $res = $auth->login($siteConfig);
$this->assertInstanceOf(LoginFormAuthenticator::class, $res); $this->assertInstanceOf(LoginFormAuthenticator::class, $res);
} }
@ -63,19 +60,17 @@ class LoginFormAuthenticatorTest extends TestCase
'password' => 'unkn0wn', 'password' => 'unkn0wn',
]); ]);
$response = new Response( $browserResponse = new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]);
200, $browserClient = new MockHttpClient([$browserResponse]);
['content-type' => 'text/html'], $browser = new HttpBrowser($browserClient);
Stream::factory('<html></html>')
);
$guzzle = new Client();
$guzzle->getEmitter()->attach(new Mock([$response, $response]));
$mockHttpClient = new MockHttpClient([new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]); $requestHtmlFunctionResponse = new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]);
$requestHtmlFunctionClient = new MockHttpClient([$requestHtmlFunctionResponse]);
$authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient);
$authenticatorProvider = new AuthenticatorProvider($mockHttpClient); $auth = new LoginFormAuthenticator($browser, $authenticatorProvider);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$res = $auth->login($siteConfig, $guzzle); $res = $auth->login($siteConfig);
$this->assertInstanceOf(LoginFormAuthenticator::class, $res); $this->assertInstanceOf(LoginFormAuthenticator::class, $res);
} }
@ -96,121 +91,44 @@ class LoginFormAuthenticatorTest extends TestCase
'password' => 'unkn0wn', 'password' => 'unkn0wn',
]); ]);
$response = $this->getMockBuilder(Response::class) $browserResponse = new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]);
->disableOriginalConstructor() $browserClient = new MockHttpClient([$browserResponse]);
$browser = $this->getMockBuilder(HttpBrowser::class)
->setConstructorArgs([$browserClient])
->getMock(); ->getMock();
$browser->expects($this->any())
$response->expects($this->any()) ->method('request')
->method('getBody')
->willReturn(file_get_contents(__DIR__ . '/../fixtures/aoc.media.html'));
$response->expects($this->any())
->method('getStatusCode')
->willReturn(200);
$mockHttpClient = new MockHttpClient([new MockResponse(file_get_contents(__DIR__ . '/../fixtures/aoc.media.html'), ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]);
$client = $this->getMockBuilder(Client::class)
->disableOriginalConstructor()
->getMock();
$client->expects($this->any())
->method('post')
->with( ->with(
$this->equalTo('POST'),
$this->equalTo('https://aoc.media/wp-admin/admin-ajax.php'), $this->equalTo('https://aoc.media/wp-admin/admin-ajax.php'),
$this->equalTo([ $this->equalTo([
'body' => [ 'nom' => 'johndoe',
'nom' => 'johndoe', 'password' => 'unkn0wn',
'password' => 'unkn0wn', 'security' => 'c506c1b8bc',
'security' => 'c506c1b8bc', 'action' => 'login_user',
'action' => 'login_user',
],
'allow_redirects' => true,
'verify' => false,
]) ])
) )
->willReturn($response); ;
$client->expects($this->any()) $requestHtmlFunctionResponse = $this->getMockBuilder(ResponseInterface::class)->getMock();
->method('get') $requestHtmlFunctionResponse->expects($this->any())
->method('getContent')
->willReturn(file_get_contents(__DIR__ . '/../fixtures/aoc.media.html'))
;
$requestHtmlFunctionClient = $this->getMockBuilder(HttpClientInterface::class)->getMock();
$requestHtmlFunctionClient->expects($this->any())
->method('request')
->with( ->with(
$this->equalTo('GET'),
$this->equalTo('https://aoc.media/'), $this->equalTo('https://aoc.media/'),
$this->equalTo([])
) )
->willReturn($response); ->willReturn($requestHtmlFunctionResponse)
;
$authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient);
$authenticatorProvider = new AuthenticatorProvider($mockHttpClient); $auth = new LoginFormAuthenticator($browser, $authenticatorProvider);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$res = $auth->login($siteConfig, $client);
$this->assertInstanceOf(LoginFormAuthenticator::class, $res); $res = $auth->login($siteConfig);
}
public function testLoginPostWithExtraFieldsWithData()
{
$siteConfig = new SiteConfig([
'host' => 'nextinpact.com',
'loginUri' => 'https://compte.nextinpact.com/Account/Login',
'usernameField' => 'UserName',
'passwordField' => 'Password',
'extraFields' => [
'__RequestVerificationToken' => '@=xpath(\'//form[@action="/Account/Login"]/input[@name="__RequestVerificationToken"]\', request_html(\'https://compte.nextinpact.com/Account/Login?http://www.nextinpact.com/\', {\'headers\': {\'X-Requested-With\':\'XMLHttpRequest\'}}))',
'returnUrl' => 'https://www.nextinpact.com/news/102835-pour-cour-comptes-fonctionnement-actuel-vote-par-internet-nest-pas-satisfaisant.htm',
],
'username' => 'johndoe',
'password' => 'unkn0wn',
]);
$response = $this->getMockBuilder(Response::class)
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getBody')
->willReturn(file_get_contents(__DIR__ . '/../fixtures/nextinpact-login.html'));
$response->expects($this->any())
->method('getStatusCode')
->willReturn(200);
$mockHttpClient = new MockHttpClient([new MockResponse(file_get_contents(__DIR__ . '/../fixtures/nextinpact-login.html'), ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]);
$client = $this->getMockBuilder(Client::class)
->disableOriginalConstructor()
->getMock();
$client->expects($this->any())
->method('post')
->with(
$this->equalTo('https://compte.nextinpact.com/Account/Login'),
$this->equalTo([
'body' => [
'UserName' => 'johndoe',
'Password' => 'unkn0wn',
'__RequestVerificationToken' => 's6x2QcnQDUL92mkKSi_JuUBXcgUYx_Plf-KyQ2eJypKAjQZIeTvaFHOsfEdTrcSXt3dt2CW39V7r9V16LUtvjszodAU1',
'returnUrl' => 'https://www.nextinpact.com/news/102835-pour-cour-comptes-fonctionnement-actuel-vote-par-internet-nest-pas-satisfaisant.htm',
],
'allow_redirects' => true,
'verify' => false,
])
)
->willReturn($response);
$client->expects($this->any())
->method('get')
->with(
$this->equalTo('https://compte.nextinpact.com/Account/Login?http://www.nextinpact.com/'),
$this->equalTo([
'headers' => [
'X-Requested-With' => 'XMLHttpRequest',
],
])
)
->willReturn($response);
$authenticatorProvider = new AuthenticatorProvider($mockHttpClient);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$res = $auth->login($siteConfig, $client);
$this->assertInstanceOf(LoginFormAuthenticator::class, $res); $this->assertInstanceOf(LoginFormAuthenticator::class, $res);
} }
@ -225,10 +143,16 @@ class LoginFormAuthenticatorTest extends TestCase
'password' => 'unkn0wn', 'password' => 'unkn0wn',
]); ]);
$mockHttpClient = new MockHttpClient(); $browserResponse = new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]);
$browserClient = new MockHttpClient([$browserResponse]);
$browser = new HttpBrowser($browserClient);
$requestHtmlFunctionResponse = new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]);
$requestHtmlFunctionClient = new MockHttpClient([$requestHtmlFunctionResponse]);
$authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient);
$auth = new LoginFormAuthenticator($browser, $authenticatorProvider);
$authenticatorProvider = new AuthenticatorProvider($mockHttpClient);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-login.html')); $loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-login.html'));
$this->assertFalse($loginRequired); $this->assertFalse($loginRequired);
@ -245,10 +169,16 @@ class LoginFormAuthenticatorTest extends TestCase
'notLoggedInXpath' => '//h2[@class="title_reserve_article"]', 'notLoggedInXpath' => '//h2[@class="title_reserve_article"]',
]); ]);
$mockHttpClient = new MockHttpClient(); $browserResponse = new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]);
$browserClient = new MockHttpClient([$browserResponse]);
$browser = new HttpBrowser($browserClient);
$requestHtmlFunctionResponse = new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]);
$requestHtmlFunctionClient = new MockHttpClient([$requestHtmlFunctionResponse]);
$authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient);
$auth = new LoginFormAuthenticator($browser, $authenticatorProvider);
$authenticatorProvider = new AuthenticatorProvider($mockHttpClient);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-article.html')); $loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-article.html'));
$this->assertTrue($loginRequired); $this->assertTrue($loginRequired);

View file

@ -2,40 +2,16 @@
namespace Tests\Wallabag\HttpClient; namespace Tests\Wallabag\HttpClient;
use GuzzleHttp\Client;
use GuzzleHttp\Event\BeforeEvent;
use GuzzleHttp\Event\CompleteEvent;
use GuzzleHttp\Message\Request;
use GuzzleHttp\Message\Response;
use GuzzleHttp\Stream\Stream;
use GuzzleHttp\Subscriber\Mock;
use Monolog\Handler\TestHandler; use Monolog\Handler\TestHandler;
use Monolog\Logger; use Monolog\Logger;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Wallabag\HttpClient\Authenticator; use Wallabag\HttpClient\Authenticator;
use Wallabag\SiteConfig\ArraySiteConfigBuilder; use Wallabag\SiteConfig\ArraySiteConfigBuilder;
use Wallabag\SiteConfig\LoginFormAuthenticator; use Wallabag\SiteConfig\LoginFormAuthenticator;
class AuthenticatorTest extends TestCase class AuthenticatorTest extends TestCase
{ {
public function testGetEvents()
{
$authenticator = $this->getMockBuilder(LoginFormAuthenticator::class)
->disableOriginalConstructor()
->getMock();
$subscriber = new Authenticator(
new ArraySiteConfigBuilder(),
$authenticator
);
$events = $subscriber->getEvents();
$this->assertArrayHasKey('before', $events);
$this->assertArrayHasKey('complete', $events);
$this->assertSame('loginIfRequired', $events['before'][0]);
$this->assertSame('loginIfRequested', $events['complete'][0]);
}
public function testLoginIfRequiredNotRequired() public function testLoginIfRequiredNotRequired()
{ {
$authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class)
@ -51,17 +27,9 @@ class AuthenticatorTest extends TestCase
$subscriber->setLogger($logger); $subscriber->setLogger($logger);
$request = new Request('GET', 'http://www.example.com'); $login = $subscriber->loginIfRequired('http://www.example.com');
$event = $this->getMockBuilder(BeforeEvent::class) $this->assertFalse($login);
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getRequest')
->willReturn($request);
$subscriber->loginIfRequired($event);
$records = $handler->getRecords(); $records = $handler->getRecords();
@ -91,29 +59,9 @@ class AuthenticatorTest extends TestCase
$subscriber->setLogger($logger); $subscriber->setLogger($logger);
$response = new Response( $login = $subscriber->loginIfRequired('http://www.example.com');
200,
['content-type' => 'text/html'],
Stream::factory('')
);
$guzzle = new Client();
$guzzle->getEmitter()->attach(new Mock([$response]));
$request = new Request('GET', 'http://www.example.com'); $this->assertTrue($login);
$event = $this->getMockBuilder(BeforeEvent::class)
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getRequest')
->willReturn($request);
$event->expects($this->once())
->method('getClient')
->willReturn($guzzle);
$subscriber->loginIfRequired($event);
$records = $handler->getRecords(); $records = $handler->getRecords();
@ -136,17 +84,18 @@ class AuthenticatorTest extends TestCase
$subscriber->setLogger($logger); $subscriber->setLogger($logger);
$request = new Request('GET', 'http://www.example.com'); $response = $this->getMockBuilder(ResponseInterface::class)
$event = $this->getMockBuilder(CompleteEvent::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$event->expects($this->once()) $response->expects($this->once())
->method('getRequest') ->method('getInfo')
->willReturn($request); ->with($this->equalTo('url'))
->willReturn('http://www.example.com');
$subscriber->loginIfRequested($event); $login = $subscriber->loginIfRequested($response);
$this->assertFalse($login);
$records = $handler->getRecords(); $records = $handler->getRecords();
@ -176,31 +125,27 @@ class AuthenticatorTest extends TestCase
$subscriber->setLogger($logger); $subscriber->setLogger($logger);
$response = new Response( $response = $this->getMockBuilder(ResponseInterface::class)
200,
['content-type' => 'text/html'],
Stream::factory('<html><body/></html>')
);
$request = new Request('GET', 'http://www.example.com');
$event = $this->getMockBuilder(CompleteEvent::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$event->expects($this->once()) $response->expects($this->once())
->method('getResponse') ->method('getInfo')
->willReturn($response); ->with($this->equalTo('url'))
->willReturn('http://www.example.com');
$event->expects($this->once()) $response->expects($this->once())
->method('getRequest') ->method('getContent')
->willReturn($request); ->willReturn('<html><body/></html>');
$subscriber->loginIfRequested($event); $login = $subscriber->loginIfRequested($response);
$this->assertFalse($login);
$records = $handler->getRecords(); $records = $handler->getRecords();
$this->assertCount(1, $records); $this->assertCount(1, $records);
$this->assertSame('loginIfRequested> retry #0 with login not required', $records[0]['message']); $this->assertSame('loginIfRequested> retry with login not required', $records[0]['message']);
} }
public function testLoginIfRequestedRequested() public function testLoginIfRequestedRequested()
@ -228,37 +173,27 @@ class AuthenticatorTest extends TestCase
$subscriber->setLogger($logger); $subscriber->setLogger($logger);
$response = new Response( $response = $this->getMockBuilder(ResponseInterface::class)
200,
['content-type' => 'text/html'],
Stream::factory('<html><body/></html>')
);
$guzzle = new Client();
$guzzle->getEmitter()->attach(new Mock([$response]));
$request = new Request('GET', 'http://www.example.com');
$event = $this->getMockBuilder(CompleteEvent::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$event->expects($this->once()) $response->expects($this->once())
->method('getResponse') ->method('getInfo')
->willReturn($response); ->with($this->equalTo('url'))
->willReturn('http://www.example.com');
$event->expects($this->once()) $response->expects($this->once())
->method('getRequest') ->method('getContent')
->willReturn($request); ->willReturn('<html><body/></html>');
$event->expects($this->any()) $login = $subscriber->loginIfRequested($response);
->method('getClient')
->willReturn($guzzle);
$subscriber->loginIfRequested($event); $this->assertTrue($login);
$records = $handler->getRecords(); $records = $handler->getRecords();
$this->assertCount(1, $records); $this->assertCount(1, $records);
$this->assertSame('loginIfRequested> retry #0 with login required', $records[0]['message']); $this->assertSame('loginIfRequested> retry with login required', $records[0]['message']);
} }
public function testLoginIfRequestedRedirect() public function testLoginIfRequestedRedirect()
@ -279,32 +214,22 @@ class AuthenticatorTest extends TestCase
$subscriber->setLogger($logger); $subscriber->setLogger($logger);
$response = new Response( $response = $this->getMockBuilder(ResponseInterface::class)
301,
[],
Stream::factory('')
);
$guzzle = new Client();
$guzzle->getEmitter()->attach(new Mock([$response]));
$request = new Request('GET', 'http://www.example.com');
$event = $this->getMockBuilder(CompleteEvent::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$event->expects($this->once()) $response->expects($this->once())
->method('getResponse') ->method('getInfo')
->willReturn($response); ->with($this->equalTo('url'))
->willReturn('http://www.example.com');
$event->expects($this->once()) $response->expects($this->once())
->method('getRequest') ->method('getContent')
->willReturn($request); ->willReturn('');
$event->expects($this->any()) $login = $subscriber->loginIfRequested($response);
->method('getClient')
->willReturn($guzzle);
$subscriber->loginIfRequested($event); $this->assertFalse($login);
$records = $handler->getRecords(); $records = $handler->getRecords();