diff --git a/src/flaresolverr_service.py b/src/flaresolverr_service.py index 890d8ad..ad21504 100644 --- a/src/flaresolverr_service.py +++ b/src/flaresolverr_service.py @@ -242,7 +242,7 @@ def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT: except FunctionTimedOut: raise Exception(f'Error solving the challenge. Timeout after {timeout} seconds.') except Exception as e: - raise Exception('Error solving the challenge. ' + str(e)) + raise Exception('Error solving the challenge. ' + str(e).replace('\n', '\\n')) finally: if not req.session and driver is not None: driver.quit() diff --git a/src/sessions.py b/src/sessions.py index d80607a..1a635e0 100644 --- a/src/sessions.py +++ b/src/sessions.py @@ -25,7 +25,8 @@ class SessionsStorage: def __init__(self): self.sessions = {} - def create(self, session_id: Optional[str] = None, proxyconf: Optional[dict] = None, force_new: Optional[bool] = False) -> Tuple[Session, bool]: + def create(self, session_id: Optional[str] = None, proxy: Optional[dict] = None, + force_new: Optional[bool] = False) -> Tuple[Session, bool]: """create creates new instance of WebDriver if necessary, assign defined (or newly generated) session_id to the instance and returns the session object. If a new session has been created @@ -44,7 +45,7 @@ class SessionsStorage: if self.exists(session_id): return self.sessions[session_id], False - driver = utils.get_webdriver(proxyconf=proxyconf) + driver = utils.get_webdriver(proxy) created_at = datetime.now() session = Session(session_id, driver, created_at) diff --git a/src/tests.py b/src/tests.py index 0f6d974..6e31435 100644 --- a/src/tests.py +++ b/src/tests.py @@ -1,4 +1,5 @@ import unittest +from typing import Optional from webtest import TestApp @@ -7,7 +8,7 @@ import flaresolverr import utils -def _find_obj_by_key(key: str, value: str, _list: list) -> dict | None: +def _find_obj_by_key(key: str, value: str, _list: list) -> Optional[dict]: for obj in _list: if obj[key] == value: return obj @@ -28,6 +29,8 @@ class TestFlareSolverr(unittest.TestCase): cloudflare_blocked_url = "https://cpasbiens3.fr/index.php?do=search&subaction=search" app = TestApp(flaresolverr.app) + # wait until the server is ready + app.get('/') def test_wrong_endpoint(self): res = self.app.get('/wrong', status=404) @@ -261,10 +264,88 @@ class TestFlareSolverr(unittest.TestCase): self.assertGreater(len(solution.cookies), 0) self.assertIn("Chrome/", solution.userAgent) - # todo: test Cmd 'request.get' should return OK with HTTP 'proxy' param - # todo: test Cmd 'request.get' should return OK with HTTP 'proxy' param with credentials - # todo: test Cmd 'request.get' should return OK with SOCKSv5 'proxy' param - # todo: test Cmd 'request.get' should fail with wrong 'proxy' param + def test_v1_endpoint_request_get_proxy_http_param(self): + """ + To configure TinyProxy in local: + * sudo vim /etc/tinyproxy/tinyproxy.conf + * edit => LogFile "/tmp/tinyproxy.log" + * edit => Syslog Off + * sudo tinyproxy -d + * sudo tail -f /tmp/tinyproxy.log + """ + res = self.app.post_json('/v1', { + "cmd": "request.get", + "url": self.google_url, + "proxy": { + "url": self.proxy_url + } + }) + self.assertEqual(res.status_code, 200) + + body = V1ResponseBase(res.json) + self.assertEqual(STATUS_OK, body.status) + self.assertEqual("Challenge not detected!", body.message) + self.assertGreater(body.startTimestamp, 10000) + self.assertGreaterEqual(body.endTimestamp, body.startTimestamp) + self.assertEqual(utils.get_flaresolverr_version(), body.version) + + solution = body.solution + self.assertIn(self.google_url, solution.url) + self.assertEqual(solution.status, 200) + self.assertIs(len(solution.headers), 0) + self.assertIn("Google", solution.response) + self.assertGreater(len(solution.cookies), 0) + self.assertIn("Chrome/", solution.userAgent) + + def test_v1_endpoint_request_get_proxy_socks_param(self): + """ + To configure Dante in local: + * https://linuxhint.com/set-up-a-socks5-proxy-on-ubuntu-with-dante/ + * sudo vim /etc/sockd.conf + * sudo systemctl restart sockd.service + * curl --socks5 socks5://127.0.0.1:1080 https://www.google.com + """ + res = self.app.post_json('/v1', { + "cmd": "request.get", + "url": self.google_url, + "proxy": { + "url": self.proxy_socks_url + } + }) + self.assertEqual(res.status_code, 200) + + body = V1ResponseBase(res.json) + self.assertEqual(STATUS_OK, body.status) + self.assertEqual("Challenge not detected!", body.message) + self.assertGreater(body.startTimestamp, 10000) + self.assertGreaterEqual(body.endTimestamp, body.startTimestamp) + self.assertEqual(utils.get_flaresolverr_version(), body.version) + + solution = body.solution + self.assertIn(self.google_url, solution.url) + self.assertEqual(solution.status, 200) + self.assertIs(len(solution.headers), 0) + self.assertIn("Google", solution.response) + self.assertGreater(len(solution.cookies), 0) + self.assertIn("Chrome/", solution.userAgent) + + def test_v1_endpoint_request_get_proxy_wrong_param(self): + res = self.app.post_json('/v1', { + "cmd": "request.get", + "url": self.google_url, + "proxy": { + "url": "http://127.0.0.1:43210" + } + }, status=500) + self.assertEqual(res.status_code, 500) + + body = V1ResponseBase(res.json) + self.assertEqual(STATUS_ERROR, body.status) + self.assertIn("Error: Error solving the challenge. Message: unknown error: net::ERR_PROXY_CONNECTION_FAILED", + body.message) + self.assertGreater(body.startTimestamp, 10000) + self.assertGreaterEqual(body.endTimestamp, body.startTimestamp) + self.assertEqual(utils.get_flaresolverr_version(), body.version) def test_v1_endpoint_request_get_fail_timeout(self): res = self.app.post_json('/v1', { @@ -401,6 +482,20 @@ class TestFlareSolverr(unittest.TestCase): self.assertEqual("Session created successfully.", body.message) self.assertEqual(body.session, "test_create_session") + def test_v1_endpoint_sessions_create_with_proxy(self): + res = self.app.post_json('/v1', { + "cmd": "sessions.create", + "proxy": { + "url": self.proxy_url + } + }) + self.assertEqual(res.status_code, 200) + + body = V1ResponseBase(res.json) + self.assertEqual(STATUS_OK, body.status) + self.assertEqual("Session created successfully.", body.message) + self.assertIsNotNone(body.session) + def test_v1_endpoint_sessions_list(self): self.app.post_json('/v1', { "cmd": "sessions.create", diff --git a/src/tests_sites.py b/src/tests_sites.py index a29369c..aa7dcbc 100644 --- a/src/tests_sites.py +++ b/src/tests_sites.py @@ -39,6 +39,8 @@ def asset_cloudflare_solution(self, res, site_url, site_text): class TestFlareSolverr(unittest.TestCase): app = TestApp(flaresolverr.app) + # wait until the server is ready + app.get('/') def test_v1_endpoint_request_get_cloudflare(self): sites_get = [ diff --git a/src/utils.py b/src/utils.py index 31c1fdd..b2a01df 100644 --- a/src/utils.py +++ b/src/utils.py @@ -36,7 +36,7 @@ def get_flaresolverr_version() -> str: return FLARESOLVERR_VERSION -def get_webdriver(proxyconf: dict=None) -> WebDriver: +def get_webdriver(proxy: dict = None) -> WebDriver: global PATCHED_DRIVER_PATH logging.debug('Launching web browser...') @@ -55,8 +55,8 @@ def get_webdriver(proxyconf: dict=None) -> WebDriver: options.add_argument('--ignore-certificate-errors') options.add_argument('--ignore-ssl-errors') - if proxyconf: - proxy_url = proxyconf['url'] + if proxy and 'url' in proxy: + proxy_url = proxy['url'] logging.debug("Using webdriver proxy: %s", proxy_url) options.add_argument('--proxy-server=%s' % proxy_url)