2019-01-09 22:59:03 +00:00
|
|
|
import pickle
|
2020-05-20 21:36:02 +00:00
|
|
|
import requests
|
|
|
|
import threading
|
2021-01-13 21:48:58 +00:00
|
|
|
import typing
|
2020-05-20 21:36:02 +00:00
|
|
|
|
2020-04-22 21:00:35 +00:00
|
|
|
from hydrus.core import HydrusData
|
|
|
|
from hydrus.core import HydrusSerialisable
|
|
|
|
from hydrus.core import HydrusGlobals as HG
|
2023-04-19 20:38:13 +00:00
|
|
|
from hydrus.core import HydrusTime
|
2018-04-18 22:10:15 +00:00
|
|
|
|
2020-07-29 20:52:44 +00:00
|
|
|
from hydrus.client import ClientConstants as CC
|
2024-02-14 21:20:24 +00:00
|
|
|
from hydrus.client import ClientGlobals as CG
|
2020-07-29 20:52:44 +00:00
|
|
|
from hydrus.client.networking import ClientNetworkingContexts
|
2022-01-05 22:15:56 +00:00
|
|
|
from hydrus.client.networking import ClientNetworkingFunctions
|
2020-07-29 20:52:44 +00:00
|
|
|
|
2018-11-21 22:22:36 +00:00
|
|
|
try:
|
|
|
|
|
|
|
|
import socket
|
|
|
|
import socks
|
|
|
|
|
|
|
|
SOCKS_PROXY_OK = True
|
|
|
|
|
|
|
|
except:
|
|
|
|
|
|
|
|
SOCKS_PROXY_OK = False
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
class NetworkSessionManagerSessionContainer( HydrusSerialisable.SerialisableBaseNamed ):
|
|
|
|
|
|
|
|
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER_SESSION_CONTAINER
|
|
|
|
SERIALISABLE_NAME = 'Session Manager Session Container'
|
2021-05-12 20:49:20 +00:00
|
|
|
SERIALISABLE_VERSION = 2
|
2021-01-13 21:48:58 +00:00
|
|
|
|
2023-11-01 21:38:03 +00:00
|
|
|
POOL_CONNECTION_TIMEOUT = 5 * 60
|
|
|
|
SESSION_TIMEOUT = 45 * 60
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
def __init__( self, name, network_context = None, session = None ):
|
|
|
|
|
|
|
|
if network_context is None:
|
|
|
|
|
|
|
|
network_context = ClientNetworkingContexts.GLOBAL_NETWORK_CONTEXT
|
|
|
|
|
|
|
|
|
|
|
|
HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
|
|
|
|
|
|
|
|
self.network_context = network_context
|
|
|
|
self.session = session
|
2023-11-01 21:38:03 +00:00
|
|
|
self.last_touched_time = HydrusTime.GetNow()
|
|
|
|
|
|
|
|
self.pool_is_cleared = True
|
|
|
|
self.printed_connection_pool_error = False
|
2021-01-13 21:48:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _InitialiseEmptySession( self ):
|
|
|
|
|
|
|
|
self.session = requests.Session()
|
|
|
|
|
|
|
|
if self.network_context.context_type == CC.NETWORK_CONTEXT_HYDRUS:
|
|
|
|
|
|
|
|
self.session.verify = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _GetSerialisableInfo( self ):
|
|
|
|
|
|
|
|
serialisable_network_context = self.network_context.GetSerialisableTuple()
|
|
|
|
|
2021-05-12 20:49:20 +00:00
|
|
|
self.session.cookies.clear_session_cookies()
|
|
|
|
|
|
|
|
pickled_cookies_hex = pickle.dumps( self.session.cookies ).hex()
|
2021-01-13 21:48:58 +00:00
|
|
|
|
2021-05-12 20:49:20 +00:00
|
|
|
return ( serialisable_network_context, pickled_cookies_hex )
|
2021-01-13 21:48:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
|
|
|
|
2021-05-12 20:49:20 +00:00
|
|
|
( serialisable_network_context, pickled_cookies_hex ) = serialisable_info
|
2021-01-13 21:48:58 +00:00
|
|
|
|
|
|
|
self.network_context = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_network_context )
|
|
|
|
|
2021-05-12 20:49:20 +00:00
|
|
|
self._InitialiseEmptySession()
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
try:
|
|
|
|
|
2021-05-12 20:49:20 +00:00
|
|
|
cookies = pickle.loads( bytes.fromhex( pickled_cookies_hex ) )
|
2021-01-13 21:48:58 +00:00
|
|
|
|
2021-05-12 20:49:20 +00:00
|
|
|
self.session.cookies = cookies
|
2021-01-13 21:48:58 +00:00
|
|
|
|
2021-05-12 20:49:20 +00:00
|
|
|
except:
|
2021-01-13 21:48:58 +00:00
|
|
|
|
2021-05-12 20:49:20 +00:00
|
|
|
HydrusData.Print( "Could not load and set cookies for session {}".format( self.network_context ) )
|
2021-01-13 21:48:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
self.session.cookies.clear_session_cookies()
|
|
|
|
|
|
|
|
|
2021-05-12 20:49:20 +00:00
|
|
|
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
|
|
|
|
|
|
|
if version == 1:
|
|
|
|
|
|
|
|
( serialisable_network_context, pickled_session_hex ) = old_serialisable_info
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
session = pickle.loads( bytes.fromhex( pickled_session_hex ) )
|
|
|
|
|
|
|
|
except:
|
|
|
|
|
|
|
|
session = requests.Session()
|
|
|
|
|
|
|
|
|
|
|
|
pickled_cookies_hex = pickle.dumps( session.cookies ).hex()
|
|
|
|
|
|
|
|
new_serialisable_info = ( serialisable_network_context, pickled_cookies_hex )
|
|
|
|
|
|
|
|
return ( 2, new_serialisable_info )
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-01 21:38:03 +00:00
|
|
|
def MaintainConnectionPool( self ):
|
|
|
|
|
|
|
|
if not self.pool_is_cleared and HydrusTime.TimeHasPassed( self.last_touched_time + self.POOL_CONNECTION_TIMEOUT ):
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
my_session_adapters = list( self.session.adapters.values() )
|
|
|
|
|
|
|
|
for adapter in my_session_adapters:
|
|
|
|
|
|
|
|
poolmanager = getattr( adapter, 'poolmanager', None )
|
|
|
|
|
|
|
|
if poolmanager is not None:
|
|
|
|
|
|
|
|
poolmanager.clear()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.pool_is_cleared = True
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
if not self.printed_connection_pool_error:
|
|
|
|
|
|
|
|
self.printed_connection_pool_error = True
|
|
|
|
|
|
|
|
HydrusData.Print( 'There was a problem clearing the connection pool, this message will not be printed again this boot:' )
|
|
|
|
HydrusData.PrintException( e, do_wait = False )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def PrepareForNewWork( self ):
|
|
|
|
|
|
|
|
self.last_touched_time = HydrusTime.GetNow()
|
|
|
|
self.pool_is_cleared = False
|
|
|
|
|
|
|
|
my_session_cookies = self.session.cookies
|
|
|
|
|
|
|
|
if HydrusTime.TimeHasPassed( self.last_touched_time + self.SESSION_TIMEOUT ):
|
|
|
|
|
|
|
|
my_session_cookies.clear_session_cookies()
|
|
|
|
|
|
|
|
|
|
|
|
my_session_cookies.clear_expired_cookies()
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER_SESSION_CONTAINER ] = NetworkSessionManagerSessionContainer
|
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
class NetworkSessionManager( HydrusSerialisable.SerialisableBase ):
|
|
|
|
|
|
|
|
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER
|
|
|
|
SERIALISABLE_NAME = 'Session Manager'
|
|
|
|
SERIALISABLE_VERSION = 1
|
|
|
|
|
|
|
|
def __init__( self ):
|
|
|
|
|
|
|
|
HydrusSerialisable.SerialisableBase.__init__( self )
|
|
|
|
|
|
|
|
self._dirty = False
|
2021-01-13 21:48:58 +00:00
|
|
|
self._dirty_session_container_names = set()
|
|
|
|
self._deletee_session_container_names = set()
|
2018-04-18 22:10:15 +00:00
|
|
|
|
|
|
|
self._lock = threading.Lock()
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
self._session_container_names = set()
|
|
|
|
|
|
|
|
self._session_container_names_to_session_containers = {}
|
|
|
|
self._network_contexts_to_session_containers = {}
|
2018-04-18 22:10:15 +00:00
|
|
|
|
2018-11-21 22:22:36 +00:00
|
|
|
self._proxies_dict = {}
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
self._ReinitialiseProxies()
|
2018-11-21 22:22:36 +00:00
|
|
|
|
2024-02-14 21:20:24 +00:00
|
|
|
CG.client_controller.sub( self, 'ReinitialiseProxies', 'notify_new_options' )
|
|
|
|
CG.client_controller.sub( self, 'MaintainConnectionPools', 'memory_maintenance_pulse' )
|
2018-04-18 22:10:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _GetSerialisableInfo( self ):
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
return sorted( self._session_container_names )
|
2018-04-18 22:10:15 +00:00
|
|
|
|
|
|
|
|
2018-10-24 21:34:02 +00:00
|
|
|
def _GetSessionNetworkContext( self, network_context ):
|
|
|
|
|
|
|
|
# just in case one of these slips through somehow
|
|
|
|
if network_context.context_type == CC.NETWORK_CONTEXT_DOMAIN:
|
|
|
|
|
2022-01-05 22:15:56 +00:00
|
|
|
second_level_domain = ClientNetworkingFunctions.ConvertDomainIntoSecondLevelDomain( network_context.context_data )
|
2018-10-24 21:34:02 +00:00
|
|
|
|
|
|
|
network_context = ClientNetworkingContexts.NetworkContext( CC.NETWORK_CONTEXT_DOMAIN, second_level_domain )
|
|
|
|
|
|
|
|
|
|
|
|
return network_context
|
|
|
|
|
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
self._session_container_names = set( serialisable_info )
|
2018-04-18 22:10:15 +00:00
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
|
|
|
|
def _InitialiseSessionContainer( self, network_context ):
|
|
|
|
|
|
|
|
session = requests.Session()
|
|
|
|
|
|
|
|
if network_context.context_type == CC.NETWORK_CONTEXT_HYDRUS:
|
2018-04-18 22:10:15 +00:00
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
session.verify = False
|
2018-04-18 22:10:15 +00:00
|
|
|
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
session_container_name = HydrusData.GenerateKey().hex()
|
|
|
|
|
|
|
|
session_container = NetworkSessionManagerSessionContainer( session_container_name, network_context = network_context, session = session )
|
|
|
|
|
|
|
|
self._session_container_names_to_session_containers[ session_container_name ] = session_container
|
|
|
|
self._network_contexts_to_session_containers[ network_context ] = session_container
|
|
|
|
|
|
|
|
self._session_container_names.add( session_container_name )
|
|
|
|
self._dirty_session_container_names.add( session_container_name )
|
|
|
|
|
|
|
|
self._SetDirty()
|
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
def _ReinitialiseProxies( self ):
|
2018-11-21 22:22:36 +00:00
|
|
|
|
|
|
|
self._proxies_dict = {}
|
|
|
|
|
2024-02-14 21:20:24 +00:00
|
|
|
http_proxy = CG.client_controller.new_options.GetNoneableString( 'http_proxy' )
|
|
|
|
https_proxy = CG.client_controller.new_options.GetNoneableString( 'https_proxy' )
|
|
|
|
no_proxy = CG.client_controller.new_options.GetNoneableString( 'no_proxy' )
|
2018-11-21 22:22:36 +00:00
|
|
|
|
|
|
|
if http_proxy is not None:
|
|
|
|
|
|
|
|
self._proxies_dict[ 'http' ] = http_proxy
|
|
|
|
|
|
|
|
|
|
|
|
if https_proxy is not None:
|
|
|
|
|
|
|
|
self._proxies_dict[ 'https' ] = https_proxy
|
|
|
|
|
|
|
|
|
2020-09-23 21:02:02 +00:00
|
|
|
if ( http_proxy is not None or https_proxy is not None ) and no_proxy is not None:
|
|
|
|
|
|
|
|
self._proxies_dict[ 'no_proxy' ] = no_proxy
|
|
|
|
|
|
|
|
|
2018-11-21 22:22:36 +00:00
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
def _SetDirty( self ):
|
|
|
|
|
|
|
|
self._dirty = True
|
|
|
|
|
|
|
|
|
|
|
|
def ClearSession( self, network_context ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
2018-10-24 21:34:02 +00:00
|
|
|
network_context = self._GetSessionNetworkContext( network_context )
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
if network_context in self._network_contexts_to_session_containers:
|
|
|
|
|
|
|
|
session_container = self._network_contexts_to_session_containers[ network_context ]
|
|
|
|
|
|
|
|
del self._network_contexts_to_session_containers[ network_context ]
|
|
|
|
|
|
|
|
session_container_name = session_container.GetName()
|
|
|
|
|
|
|
|
if session_container_name in self._session_container_names_to_session_containers:
|
|
|
|
|
|
|
|
del self._session_container_names_to_session_containers[ session_container_name ]
|
|
|
|
|
|
|
|
|
|
|
|
self._session_container_names.discard( session_container_name )
|
2021-01-20 22:22:03 +00:00
|
|
|
self._dirty_session_container_names.discard( session_container_name )
|
2021-01-13 21:48:58 +00:00
|
|
|
self._deletee_session_container_names.add( session_container_name )
|
2018-04-18 22:10:15 +00:00
|
|
|
|
|
|
|
self._SetDirty()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
def GetDeleteeSessionNames( self ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
|
|
return set( self._deletee_session_container_names )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def GetDirtySessionContainers( self ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
|
|
return [ self._session_container_names_to_session_containers[ session_container_name ] for session_container_name in self._dirty_session_container_names ]
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
def GetNetworkContexts( self ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
return list( self._network_contexts_to_session_containers.keys() )
|
2018-04-18 22:10:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def GetSession( self, network_context ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
2018-10-24 21:34:02 +00:00
|
|
|
network_context = self._GetSessionNetworkContext( network_context )
|
2018-04-18 22:10:15 +00:00
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
if network_context not in self._network_contexts_to_session_containers:
|
2018-04-18 22:10:15 +00:00
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
self._InitialiseSessionContainer( network_context )
|
2018-04-18 22:10:15 +00:00
|
|
|
|
|
|
|
|
2023-11-01 21:38:03 +00:00
|
|
|
session_container = self._network_contexts_to_session_containers[ network_context ]
|
|
|
|
|
|
|
|
session_container.PrepareForNewWork()
|
|
|
|
|
|
|
|
session = session_container.session
|
2018-04-18 22:10:15 +00:00
|
|
|
|
2018-11-21 22:22:36 +00:00
|
|
|
if session.proxies != self._proxies_dict:
|
|
|
|
|
|
|
|
session.proxies = dict( self._proxies_dict )
|
|
|
|
|
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
#
|
|
|
|
|
|
|
|
# tumblr can't into ssl for some reason, and the data subdomain they use has weird cert properties, looking like amazon S3
|
|
|
|
# perhaps it is inward-facing somehow? whatever the case, let's just say fuck it for tumblr
|
|
|
|
|
|
|
|
if network_context.context_type == CC.NETWORK_CONTEXT_DOMAIN and network_context.context_data == 'tumblr.com':
|
|
|
|
|
|
|
|
session.verify = False
|
|
|
|
|
|
|
|
|
2024-02-14 21:20:24 +00:00
|
|
|
if not CG.client_controller.new_options.GetBoolean( 'verify_regular_https' ):
|
2021-04-14 21:54:17 +00:00
|
|
|
|
|
|
|
session.verify = False
|
|
|
|
|
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
return session
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-10-31 21:41:14 +00:00
|
|
|
def GetSessionForDomain( self, domain ):
|
|
|
|
|
|
|
|
network_context = ClientNetworkingContexts.NetworkContext( context_type = CC.NETWORK_CONTEXT_DOMAIN, context_data = domain )
|
|
|
|
|
|
|
|
return self.GetSession( network_context )
|
|
|
|
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
def HasDirtySessionContainers( self ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
|
|
return len( self._dirty_session_container_names ) > 0 or len( self._deletee_session_container_names ) > 0
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
def IsDirty( self ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
|
|
return self._dirty
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-01 21:38:03 +00:00
|
|
|
def MaintainConnectionPools( self ):
|
|
|
|
|
|
|
|
for session_container in self._network_contexts_to_session_containers.values():
|
|
|
|
|
|
|
|
session_container.MaintainConnectionPool()
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
def ReinitialiseProxies( self ):
|
2018-11-21 22:22:36 +00:00
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
self._ReinitialiseProxies()
|
2018-11-21 22:22:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
def SetClean( self ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
|
|
self._dirty = False
|
2021-01-13 21:48:58 +00:00
|
|
|
self._dirty_session_container_names = set()
|
|
|
|
self._deletee_session_container_names = set()
|
2018-04-18 22:10:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2019-08-07 22:59:53 +00:00
|
|
|
def SetDirty( self ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
2021-01-13 21:48:58 +00:00
|
|
|
self._SetDirty()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def SetSessionContainers( self, session_containers: typing.Collection[ NetworkSessionManagerSessionContainer ], set_all_sessions_dirty = False ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
|
|
self._session_container_names_to_session_containers = {}
|
|
|
|
self._network_contexts_to_session_containers = {}
|
|
|
|
|
|
|
|
self._session_container_names = set()
|
|
|
|
self._dirty_session_container_names = set()
|
|
|
|
self._deletee_session_container_names = set()
|
|
|
|
|
|
|
|
for session_container in session_containers:
|
|
|
|
|
|
|
|
session_container_name = session_container.GetName()
|
|
|
|
|
|
|
|
self._session_container_names_to_session_containers[ session_container_name ] = session_container
|
|
|
|
self._network_contexts_to_session_containers[ session_container.network_context ] = session_container
|
|
|
|
|
|
|
|
self._session_container_names.add( session_container_name )
|
|
|
|
|
|
|
|
if set_all_sessions_dirty:
|
|
|
|
|
|
|
|
self._dirty_session_container_names.add( session_container_name )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def SetSessionDirty( self, network_context: ClientNetworkingContexts.NetworkContext ):
|
|
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
|
|
network_context = self._GetSessionNetworkContext( network_context )
|
|
|
|
|
|
|
|
if network_context in self._network_contexts_to_session_containers:
|
|
|
|
|
|
|
|
self._dirty_session_container_names.add( self._network_contexts_to_session_containers[ network_context ].GetName() )
|
|
|
|
|
2019-08-07 22:59:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-04-18 22:10:15 +00:00
|
|
|
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER ] = NetworkSessionManager
|