mirror of
https://github.com/FlareSolverr/FlareSolverr.git
synced 2025-12-06 01:28:37 +01:00
Sessions with auto-creation on fetch request and TTL (#736)
* Add support for sessions * Add tests for sessions * Missing return type * Don't re-create an existing session * Return success in case of session doesn't exists on destroy * Create session if necessary on get request * Add session TTL to the request.get method When fetching some webpage with a predefined session id, FlareSorverr is using existing instance of WebDriver. That allows user to not manage cookies explicitly and rely on WebDriver to maintain the session. However, if session has been created long time ago, CloudFlare might stop accepting the requests, so we want to recreate the session time to time. From the user perspective the easiest way of doing it is to define their expectation on the session duration. These changes add an option to define Time-to-live (TTL) for the session and FlareSorverr takes care about rotating the sessions. * Update message for session destroy in tests --------- Co-authored-by: Michel Roux <xefir@crystalyx.net>
This commit is contained in:
committed by
GitHub
parent
f6879c70de
commit
49fd1aacfc
@@ -2,19 +2,25 @@ import logging
|
||||
import platform
|
||||
import sys
|
||||
import time
|
||||
from datetime import timedelta
|
||||
from urllib.parse import unquote
|
||||
from uuid import uuid1
|
||||
|
||||
from func_timeout import func_timeout, FunctionTimedOut
|
||||
from func_timeout import FunctionTimedOut, func_timeout
|
||||
from selenium.common import TimeoutException
|
||||
from selenium.webdriver.chrome.webdriver import WebDriver
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.expected_conditions import (
|
||||
presence_of_element_located, staleness_of, title_is)
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
from selenium.webdriver.support.expected_conditions import presence_of_element_located, staleness_of, title_is
|
||||
|
||||
from dtos import V1RequestBase, V1ResponseBase, ChallengeResolutionT, ChallengeResolutionResultT, IndexResponse, \
|
||||
HealthResponse, STATUS_OK, STATUS_ERROR
|
||||
import utils
|
||||
from dtos import (STATUS_ERROR, STATUS_OK, ChallengeResolutionResultT,
|
||||
ChallengeResolutionT, HealthResponse, IndexResponse,
|
||||
V1RequestBase, V1ResponseBase)
|
||||
from sessions import SessionsStorage
|
||||
|
||||
ACCESS_DENIED_TITLES = [
|
||||
# Cloudflare
|
||||
@@ -44,6 +50,7 @@ CHALLENGE_SELECTORS = [
|
||||
]
|
||||
SHORT_TIMEOUT = 10
|
||||
|
||||
SESSIONS_STORAGE = SessionsStorage()
|
||||
|
||||
def test_browser_installation():
|
||||
logging.info("Testing web browser installation...")
|
||||
@@ -120,11 +127,11 @@ def _controller_v1_handler(req: V1RequestBase) -> V1ResponseBase:
|
||||
# execute the command
|
||||
res: V1ResponseBase
|
||||
if req.cmd == 'sessions.create':
|
||||
raise Exception("Not implemented yet.")
|
||||
res = _cmd_sessions_create(req)
|
||||
elif req.cmd == 'sessions.list':
|
||||
raise Exception("Not implemented yet.")
|
||||
res = _cmd_sessions_list(req)
|
||||
elif req.cmd == 'sessions.destroy':
|
||||
raise Exception("Not implemented yet.")
|
||||
res = _cmd_sessions_destroy(req)
|
||||
elif req.cmd == 'request.get':
|
||||
res = _cmd_request_get(req)
|
||||
elif req.cmd == 'request.post':
|
||||
@@ -171,19 +178,79 @@ def _cmd_request_post(req: V1RequestBase) -> V1ResponseBase:
|
||||
return res
|
||||
|
||||
|
||||
def _cmd_sessions_create(req: V1RequestBase) -> V1ResponseBase:
|
||||
logging.debug("Creating new session...")
|
||||
|
||||
session, fresh = SESSIONS_STORAGE.create()
|
||||
session_id = session.session_id
|
||||
|
||||
if not fresh:
|
||||
return V1ResponseBase({
|
||||
"status": STATUS_OK,
|
||||
"message": "Session already exists.",
|
||||
"session": session_id
|
||||
})
|
||||
|
||||
return V1ResponseBase({
|
||||
"status": STATUS_OK,
|
||||
"message": "Session created successfully.",
|
||||
"session": session_id
|
||||
})
|
||||
|
||||
|
||||
def _cmd_sessions_list(req: V1RequestBase) -> V1ResponseBase:
|
||||
session_ids = SESSIONS_STORAGE.session_ids()
|
||||
|
||||
return V1ResponseBase({
|
||||
"status": STATUS_OK,
|
||||
"message": "",
|
||||
"sessions": session_ids
|
||||
})
|
||||
|
||||
|
||||
def _cmd_sessions_destroy(req: V1RequestBase) -> V1ResponseBase:
|
||||
session_id = req.session
|
||||
existed = SESSIONS_STORAGE.destroy(session_id)
|
||||
|
||||
if not existed:
|
||||
return V1ResponseBase({
|
||||
"status": STATUS_OK,
|
||||
"message": "The session doesn't exists."
|
||||
})
|
||||
|
||||
return V1ResponseBase({
|
||||
"status": STATUS_OK,
|
||||
"message": "The session has been removed."
|
||||
})
|
||||
|
||||
|
||||
def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT:
|
||||
timeout = req.maxTimeout / 1000
|
||||
driver = None
|
||||
try:
|
||||
driver = utils.get_webdriver()
|
||||
if req.session:
|
||||
session_id = req.session
|
||||
ttl = timedelta(minutes=req.session_ttl_minutes) if req.session_ttl_minutes else None
|
||||
session, fresh = SESSIONS_STORAGE.get(session_id, ttl)
|
||||
|
||||
if fresh:
|
||||
logging.debug(f"new session created to perform the request (session_id={session_id})")
|
||||
else:
|
||||
logging.debug(f"existing session is used to perform the request (session_id={session_id}, lifetime={str(session.lifetime())}, ttl={str(ttl)})")
|
||||
|
||||
driver = session.driver
|
||||
else:
|
||||
driver = utils.get_webdriver()
|
||||
logging.debug('New instance of webdriver has been created to perform the request')
|
||||
return func_timeout(timeout, _evil_logic, (req, driver, method))
|
||||
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))
|
||||
finally:
|
||||
if driver is not None:
|
||||
if not req.session and driver is not None:
|
||||
driver.quit()
|
||||
logging.debug('A used instance of webdriver has been destroyed')
|
||||
|
||||
|
||||
def click_verify(driver: WebDriver):
|
||||
|
||||
Reference in New Issue
Block a user