@ -7,6 +7,7 @@ from collections import defaultdict
from io import BytesIO
from io import BytesIO
import socket
import gevent
import gevent
from random import shuffle
from random import shuffle
@ -99,7 +100,7 @@ class CMClient(EventEmitter):
: param retry : number of retries before returning . Unlimited when set to ` ` None ` `
: param retry : number of retries before returning . Unlimited when set to ` ` None ` `
: type retry : : class : ` int `
: type retry : : class : ` int `
: param delay : delay in secnds before connection attempt
: param delay : delay in seco nds before connection attempt
: type delay : : class : ` int `
: type delay : : class : ` int `
: return : successful connection
: return : successful connection
: rtype : : class : ` bool `
: rtype : : class : ` bool `
@ -119,19 +120,27 @@ class CMClient(EventEmitter):
self . _LOG . debug ( " Connect initiated. " )
self . _LOG . debug ( " Connect initiated. " )
if len ( self . cm_servers ) == 0 and not self . cm_servers . auto_discovery :
self . _LOG . error ( " CM server list is empty. " )
self . _connecting = False
return False
for i , server_addr in enumerate ( self . cm_servers ) :
for i , server_addr in enumerate ( self . cm_servers ) :
if retry and i > retry :
if retry and i > retry :
self . _connecting = False
return False
return False
start = time ( )
start = time ( )
if server_addr :
if self . connection . connect ( server_addr ) :
if self . connection . connect ( server_addr ) :
break
break
self . _LOG . debug ( " Failed to connect. Retrying... " )
else :
self . _LOG . debug ( " No servers available. Retrying... " )
diff = time ( ) - start
diff = time ( ) - start
self . _LOG . debug ( " Failed to connect. Retrying... " )
if diff < 5 :
if diff < 5 :
self . sleep ( 5 - diff )
self . sleep ( 5 - diff )
@ -400,6 +409,7 @@ class CMServerList(object):
Good = 1
Good = 1
Bad = 2
Bad = 2
auto_discovery = True #: whether to automatically try to bootstrap CM server list
def __init__ ( self , bad_timespan = 300 ) :
def __init__ ( self , bad_timespan = 300 ) :
self . _LOG = logging . getLogger ( " CMServerList " )
self . _LOG = logging . getLogger ( " CMServerList " )
@ -407,7 +417,11 @@ class CMServerList(object):
self . bad_timespan = bad_timespan
self . bad_timespan = bad_timespan
self . list = defaultdict ( dict )
self . list = defaultdict ( dict )
self . bootstrap_from_builtin_list ( )
def __len__ ( self ) :
return len ( self . list )
def __repr__ ( self ) :
return " < %s : %d servers> " % ( self . __class__ . __name__ , len ( self ) )
def clear ( self ) :
def clear ( self ) :
""" Clears the server list """
""" Clears the server list """
@ -415,38 +429,30 @@ class CMServerList(object):
self . _LOG . debug ( " List cleared. " )
self . _LOG . debug ( " List cleared. " )
self . list . clear ( )
self . list . clear ( )
def bootstrap_from_builtin_list ( self ) :
def bootstrap_from_dns ( self ) :
"""
"""
Resets the server list to the built in one .
Fetches CM server list from WebAPI and replaces the current one
This method is called during initialization .
"""
"""
self . _LOG . debug ( " Bootstraping from builtin list " )
self . _LOG . debug ( " Attempting bootstrap via DNS " )
self . clear ( )
# build-in list
try :
self . merge_list ( [
answer = socket . getaddrinfo ( " cm0.steampowered.com " ,
( ' 162.254.193.7 ' , 27018 ) ,
27017 ,
( ' 208.78.164.9 ' , 27018 ) ,
socket . AF_INET ,
( ' 208.78.164.11 ' , 27017 ) ,
proto = socket . IPPROTO_TCP )
( ' 162.254.193.7 ' , 27019 ) ,
except Exception as exp :
( ' 162.254.193.47 ' , 27017 ) ,
self . _LOG . error ( " DNS boostrap failed: %s " % str ( exp ) )
( ' 155.133.242.9 ' , 27019 ) ,
return False
( ' 208.78.164.14 ' , 27018 ) ,
( ' 155.133.242.8 ' , 27018 ) ,
servers = list ( map ( lambda addr : addr [ 4 ] , answer ) )
( ' 162.254.195.45 ' , 27017 ) ,
( ' 208.78.164.10 ' , 27018 ) ,
if servers :
( ' 208.78.164.12 ' , 27017 ) ,
self . clear ( )
( ' 208.64.201.176 ' , 27018 ) ,
self . merge_list ( servers )
( ' 146.66.152.10 ' , 27017 ) ,
return True
( ' 162.254.193.46 ' , 27019 ) ,
else :
( ' 185.25.180.14 ' , 27017 ) ,
self . _LOG . error ( " DNS boostrap: cm0.steampowered.com resolved no A records " )
( ' 162.254.193.46 ' , 27018 ) ,
return False
( ' 155.133.242.9 ' , 27017 ) ,
( ' 162.254.195.44 ' , 27018 ) ,
( ' 162.254.195.45 ' , 27018 ) ,
( ' 208.78.164.9 ' , 27017 ) ,
( ' 208.78.164.11 ' , 27019 )
] )
def bootstrap_from_webapi ( self , cellid = 0 ) :
def bootstrap_from_webapi ( self , cellid = 0 ) :
"""
"""
@ -461,7 +467,8 @@ class CMServerList(object):
from steam import webapi
from steam import webapi
try :
try :
resp = webapi . get ( ' ISteamDirectory ' , ' GetCMList ' , 1 , params = { ' cellid ' : cellid } )
resp = webapi . get ( ' ISteamDirectory ' , ' GetCMList ' , 1 , params = { ' cellid ' : cellid ,
' http_timeout ' : 3 } )
except Exception as exp :
except Exception as exp :
self . _LOG . error ( " WebAPI boostrap failed: %s " % str ( exp ) )
self . _LOG . error ( " WebAPI boostrap failed: %s " % str ( exp ) )
return False
return False
@ -485,9 +492,22 @@ class CMServerList(object):
return True
return True
def __iter__ ( self ) :
def __iter__ ( self ) :
def genfunc ( ) :
def cm_server_iter ( ) :
while True :
while True :
good_servers = list ( filter ( lambda x : x [ 1 ] [ ' quality ' ] == CMServerList . Good , self . list . items ( ) ) )
if self . auto_discovery :
if not self . list :
self . bootstrap_from_webapi ( )
if not self . list :
self . bootstrap_from_dns ( )
if not self . list :
yield None
elif not self . list :
self . _LOG . error ( " Server list is empty. " )
return
good_servers = list ( filter ( lambda x : x [ 1 ] [ ' quality ' ] == CMServerList . Good ,
self . list . items ( )
) )
if len ( good_servers ) == 0 :
if len ( good_servers ) == 0 :
self . reset_all ( )
self . reset_all ( )
@ -498,7 +518,7 @@ class CMServerList(object):
for server_addr , meta in good_servers :
for server_addr , meta in good_servers :
yield server_addr
yield server_addr
return genfunc ( )
return cm_server_iter ( )
def reset_all ( self ) :
def reset_all ( self ) :
""" Reset status for all servers in the list """
""" Reset status for all servers in the list """