SF.net SVN: fclient: [82] trunk/sandbox/fcp/fcp2_0_client.py
Status: Pre-Alpha
Brought to you by:
jurner
|
From: <ju...@us...> - 2008-01-28 11:22:23
|
Revision: 82
http://fclient.svn.sourceforge.net/fclient/?rev=82&view=rev
Author: jurner
Date: 2008-01-28 03:22:27 -0800 (Mon, 28 Jan 2008)
Log Message:
-----------
continued working on the client
Modified Paths:
--------------
trunk/sandbox/fcp/fcp2_0_client.py
Modified: trunk/sandbox/fcp/fcp2_0_client.py
===================================================================
--- trunk/sandbox/fcp/fcp2_0_client.py 2008-01-28 11:21:45 UTC (rev 81)
+++ trunk/sandbox/fcp/fcp2_0_client.py 2008-01-28 11:22:27 UTC (rev 82)
@@ -11,6 +11,40 @@
"""
+
+#Bug reports filed and open:
+#--------------------------------------------------------------------------------------------------------------------------------------------
+# [0001931: Send EndListPersistentRequests following client connect]
+#
+# PendingRequests currently get lost if a.) the node goes down b.) if the client goes down unexpectedly.
+# This affects IdentifierCollision + FilenameCollision + ClientPut when a SSK needs to be created first
+#
+# we can handle this case none short of maintaining a file keeping messages. But still there is the
+# problem of knowing when the node has actually registered a request. The node does not send
+# an EndListPersistentRequests on connect so it is impossible to tell when or if to restore one of
+# the pending requests we stored.
+#---------------------------------------------------------------------------------------------------------------------------------------------
+# [0001893: CloseConnectionDuplicateClientName bug or feature?]
+#
+# CloseConnectionDuplicateClientName
+# currently fcp takes down a our connection if another client (...) uses the same connection name.
+#----------------------------------------------------------------------------------------------------------------------------------------------
+# [0001888: explicite connection locale]
+#
+# Many strings are already translated by freenet, but there is no way to tell the node wich language
+# to use to talk to a client. Maybe a good idea, maybe not.
+#-----------------------------------------------------------------------------------------------------------------------------------------------
+# [0001781: unregister directories registered via TestDDARequest]
+#
+# With current state of the art DDA handling it is not possiblr to unregister directories (may pile
+# up in memory over time).
+#------------------------------------------------------------------------------------------------------------------------------------------------
+# [0002019: Socket dies if first message is not ClientHello]
+#
+# minor one
+#-------------------------------------------------------------------------------------------------------------------------------------------------
+
+
import atexit
import base64
import cPickle
@@ -135,6 +169,7 @@
Priority,
ProtocolError,
ReturnType,
+ UploadFrom,
Verbosity,
)
from fcp2_0_message import Message
@@ -154,6 +189,7 @@
'RequestCompleted', # the request is not removed neither from node nor client
'RequestFailed', # the request is already removed from node and client
+ 'RequestFetchable',
'RequestModified',
'RequestProgress',
'RequestRestored',
@@ -174,13 +210,14 @@
'PeerNote',
+ # others
+ 'SSKKeypair',
+
###############################
'ProtocolError',
- 'PersistentGet',
+
- # others
- 'SSKKeypair',
)
@@ -191,15 +228,15 @@
debugVerbosity=None,
):
"""
- @param conectionName: name of the connection
+ @param conectionName: name of the connection or None to use an arbitrary connection name
@param debugVerbosity: verbosity level for debugging. Default is L{Verbosity.Warning}
@ivar events: events the client supports
"""
- self._connectionName = connectionName
- self._ddaTests = []
- self._identifierMapping = {} # mapping from identifiers to FcRequestIdentifiers
- self._requests = {}
+ self._connectionName = self.setConnectionName(connectionName)
+ self._ddaTests = [] # currently running DDA tests (request0, ... requestN)
+ self._sskRequests = {} # currently pending ssk requests (sskIdentifier --> request)
+ self._requests = {} # currently running requests (requestIdentifier --> request)
self._log = logging.getLogger(self.__class__.__name__)
self._socket = None
@@ -208,7 +245,7 @@
self.setDebugVerbosity(self.DebugVerbosity.Warning if debugVerbosity is None else debugVerbosity)
atexit.register(self.close)
-
+
###############################################################
##
@@ -253,6 +290,8 @@
# params from PersistentRequestModified
'FcModified': {},
+ # params for DDA test
+ 'FcTestDDA': {},
# params for SimpleProgress
'FcProgressTotal': '0',
@@ -284,8 +323,6 @@
@return: (str) uuid
@note: the identifier returned is unique to the client but may not be unique to the node
"""
-
- # we store FcRequestIdentifier and additional params in ClientToken
identifier = self.FcParams.newUuid()
# add additional params to msg
@@ -384,12 +421,16 @@
self._socket.close()
self._socket = None
- # clean left over tmp files
+ # clean left over DDA test tmp files
for initialRequest in self._ddaTests:
- if initialRequest['FcTestDDA']['TmpFile'] is not None:
+ if initialRequest['FcTestDDA'].get('TmpFile', None) is not None:
saveRemoveFile(initialRequest['FcTestDDA']['TmpFile'])
-
-
+
+ self._ddaTests = []
+ self._requests = {}
+ self._sskRequests = {}
+
+
def connect(self, host=DefaultFcpHost, port=DefaultFcpPort, duration=20, timeout=0.5):
"""Iterator to stablish a connection to a freenet node
@param host: (str) host of th node
@@ -402,6 +443,10 @@
"""
self._log.info(self.LogMessages.Connecting)
+ # try to Connect socket
+ if self._socket is not None:
+ self.close()
+
# poll untill freenet responds
timeElapsed = 0
while timeElapsed <= duration:
@@ -425,7 +470,7 @@
# So take it over here.
self.sendMessage(
self.Message.MessageClientHello,
- Name=self._connectionName if self._connectionName is not None else uuid.uuid_time(),
+ Name=self._connectionName,
ExpectedVersion=self.Version,
)
while timeElapsed <= duration:
@@ -458,6 +503,21 @@
raise StopIteration
+ def getConnectionName(self):
+ """Returns the connection name used by the client
+ @return: (str) connection name
+ """
+ return self._connectionName
+
+ def setConnectionName(self, connectionName=None):
+ """Sets the connection name to be used by the client
+ @param connectionName: (str) connection name or None to use an arbitrary connection name
+ @return: (str) connection name
+ """
+ self._connectionName = uuid.uuid_time() if connectionName is None else connectionName
+ return self._connectionName
+
+
def setDebugVerbosity(self, debugVerbosity):
"""Sets the verbosity level of the client
@note: see L{Verbosity}
@@ -490,14 +550,13 @@
@param msg: (Message) to handle
@return: True if the message was handled, False otherwise
"""
+
+ CancelPersistentRequests = 0 # for testing... if True, cancels all PersistentRequests
+
if msg.name == self.Message.MessageClientSocketTimeout:
return True
self._log.debug(self.LogMessages.MessageReceived + msg.pprint())
- # check if we have a corrosponding initial message
- fcRequestIdentifier = None
- initialRequest = None
-
# check if we have an initial request corrosponding to msg
requestIdentifier = msg.get('Identifier', None)
initialRequest = None if requestIdentifier is None else self._requests.get(requestIdentifier, None)
@@ -666,8 +725,6 @@
if initialRequest['FcTestDDA']['TmpFile'] is not None:
saveRemoveFile(initialRequest['FcTestDDA']['TmpFile'])
wantWrite = initialRequest.params['FcTestDDA']['WantWrite']
- errorMsg = initialRequest['FcTestDDA']['ErrorMsg']
- del initialRequest.params['FcTestDDA']
# check if test was sucessful
testFailed = False
@@ -678,12 +735,11 @@
if testFailed:
- #TODO: check if Fcp removed the request
#TODO: check if errorMsg gives reasonable feedback
- del self._request[fcRequestIdentifier]
+ del self._request[initialRequest['Identifier']]
initialRequest['FcStatus'] = self.Message.StatusError | self.Message.StatusRemoved
- initialRequest['FcErrorMessage'] = errorMsg
+ initialRequest['FcErrorMessage'] = initialRequest['FcTestDDA']['ErrorMsg']
self.events.ProtocolError(initialRequest)
return True
@@ -767,8 +823,6 @@
# Fcp does no good job in handling persistent requests and identifiers. Already dropped some
# notes and reports regarding this. See freenet-tech mailing list [Fcp notes and issues]
- CancelPersistentRequests = 0 # for testing... if True, cancels all persistent requests
-
# unknown request... try to restore it
if initialRequest is None:
restoredRequest = self._restorePersistentRequestFromNode(msg)
@@ -852,6 +906,37 @@
self.events.RequestProgress(initialRequest)
return True
+ ## put related
+
+ elif msg.name == self.Message.MessageURIGenerated:
+ if initialRequest is None: # something went wrong
+ return False
+ initialRequest['URI'] = msg['URI']
+ return True
+
+
+ elif msg.name == self.Message.MessagePutFetchable:
+ if initialRequest is None:
+ # something went wrong
+ return False
+
+ self.events.RequestFetchable(initialRequest)
+ return True
+
+
+ elif msg.name == self.Message.MessagePutSuccessful:
+ if initialRequest is None:
+ # something went wrong
+ return False
+
+ # TODO: StartupTime and CompletionTime are passed, but
+ # as long as no corrosponding params are passed in DataFound
+ # we ignore them
+ initialRequest['URI'] = msg['URI']
+ self.events.RequestCompleted(initialRequest)
+ return True
+
+
####################################################
##
## Peer related messages
@@ -892,18 +977,26 @@
####################################################
elif msg.name == self.Message.MessageSSKKeypair:
if initialRequest is None:
- return False
+ self.events.SSKKeypair(msg)
+ return True
- #TODO: maybe we need a mapping from SSKKeypair to pending request
-
initialRequest['Uri'] = msg['InsertUri']
initialRequest['FcRequestUri'] = msg['RequestUri']
-
self.sendMessageEx(initialRequest)
return True
+
+ elif msg.name == self.Message.MessageCloseConnectionDuplicateClientName:
+ msg = self.Message(
+ self.Message.MessageClientDisconnected,
+ DisconnectReason=DisconnectReason.DuplicateClientName,
+ )
+ self.events.ClientDisconnect(msg)
+ return True
+
+
# default
return False
@@ -1018,6 +1111,46 @@
## ClientGet related methods
##
########################################################
+ def clientGet(self,
+ uri,
+ messageSubType,
+ userData,
+ persistentUserData,
+ filenameCollision,
+ **messageParams
+ ):
+ """Requests a key from the node
+ @param uri: uri of the file to request (may contain prefixes like 'freenet:' or 'http://')
+ @param messageSubType: one of the Message.SubType* consts to the desired request
+ @param userData: any non persistent data to associate to the request or None
+ @param persistentUserData: any string to associate to the request as persistent data or None
+ @param filenameCollision: what to do if the disk target alreaady exists. One of the FilenameCollision.* consts
+ @param messageParams: keyword arguments to pass along with the ClientGet message (uppercase first letter!!).
+ If the value of a keyword is None, it is ignored.
+
+ @return: (str) identifier of the request
+
+
+ """
+ uri = self.Uri(uri).uri
+ msg = self.Message(self.Message.MessageClientGet, URI=uri)
+ for paramName, value in messageParams.items():
+ if value is not None:
+ msg[paramName] = value
+
+ self._registerRequest(
+ msg,
+ userData,
+ messageSubType,
+ time.time(),
+ persistentUserData,
+ filenameCollision=filenameCollision,
+ )
+ self.sendMessageEx(msg)
+ return msg['Identifier']
+
+
+
def getData(self,
uri,
@@ -1052,45 +1185,30 @@
@return: (str) request identifier
@note: if a filename collision is handled a RequestFilenameChanged event is triggered
"""
+ return self.clientGet(
+ uri,
+ self.Message.SubTypeGetData,
+ userData,
+ persistentUserData,
+ consts.FilenameCollision.HandleNever,
- msg = self.Message(
- self.Message.MessageClientGet,
-
- BinaryBlob=binaryBlob,
- Global=self.FcpFalse,
- DSOnly=dsOnly,
-
- Identifier=None,
- IgnoreDS=ignoreDS,
- Persistence=persistence,
- PriorityClass=priorityClass,
-
- ReturnType=self.ReturnType.Direct,
- URI=self.Uri(uri).uri,
- Verbosity=self.Verbosity.ReportProgress,
-
- #MaxTempSize=whatever,
- #TempFilename=whatever
- )
- if allowedMimeTypes is not None:
- msg['AllowedMimeTypes'] = allowedMimeTypes
- if maxRetries is not None:
- msg['MaxRetries'] = maxRetries
- if maxSize is not None:
- msg['MaxSize'] = maxSize
+ # Fcp params
+ AllowedMimeTypes = allowedMimeTypes,
+ BinaryBlob=binaryBlob,
+ Global=self.FcpFalse,
+ DSOnly=dsOnly,
+ Identifier=None,
+ IgnoreDS=ignoreDS,
+ MaxRetries = maxRetries,
+ MaxSize = maxSize,
+ Persistence=persistence,
+ PriorityClass=priorityClass,
+ ReturnType=self.ReturnType.Direct,
+ URI=self.Uri(uri).uri,
+ Verbosity=self.Verbosity.ReportProgress,
+ )
- self._registerRequest(
- msg,
- userData,
- self.Message.SubTypeGetData,
- time.time(),
- persistentUserData,
- )
- self.sendMessageEx(msg)
- return msg['Identifier']
-
-
def getFile(self,
uri,
filename,
@@ -1129,52 +1247,37 @@
@return: (str) request identifier
@note: if a filename collision is handled a RequestFilenameChanged event is triggered
"""
+ return self.clientGet(
+ uri,
+ self.Message.SubTypeGetFile,
+ userData,
+ persistentUserData,
+ filenameCollision,
- msg = self.Message(
- self.Message.MessageClientGet,
-
- BinaryBlob=binaryBlob,
- Filename=filename,
- Global=self.FcpFalse,
- DSOnly=dsOnly,
-
- Identifier=None,
- IgnoreDS=ignoreDS,
- Persistence=persistence,
- PriorityClass=priorityClass,
-
- ReturnType=self.ReturnType.Disk,
- URI=self.Uri(uri).uri,
- Verbosity=self.Verbosity.ReportProgress,
-
- #MaxTempSize=whatever,
- #TempFilename=whatever
- )
- if allowedMimeTypes is not None:
- msg['AllowedMimeTypes'] = allowedMimeTypes
- if maxRetries is not None:
- msg['MaxRetries'] = maxRetries
- if maxSize is not None:
- msg['MaxSize'] = maxSize
+ # Fcp params
+ AllowedMimeTypes = allowedMimeTypes,
+ BinaryBlob=binaryBlob,
+ Filename=filename,
+ Global=self.FcpFalse,
+ DSOnly=dsOnly,
+ Identifier=None,
+ IgnoreDS=ignoreDS,
+ MaxRetries = maxRetries,
+ MaxSize = maxSize,
+ Persistence=persistence,
+ PriorityClass=priorityClass,
+ ReturnType=self.ReturnType.Disk,
+ URI=self.Uri(uri).uri,
+ Verbosity=self.Verbosity.ReportProgress,
+ )
- self._registerRequest(
- msg,
- userData,
- self.Message.SubTypeGetFile,
- time.time(),
- persistentUserData,
- filenameCollision,
- )
- self.sendMessageEx(msg)
- return msg['Identifier']
-
-
def getKeyInfo(self,
uri,
dsOnly=consts.FcpFalse,
ignoreDS=consts.FcpFalse,
+ maxRetries=None,
persistence=consts.Persistence.Connection,
priorityClass=consts.Priority.Medium,
@@ -1187,6 +1290,7 @@
@param dsOnly: if FcpTrue, retrieves the file from the local data store only
@param ignoreDs: If FcpTrue, ignores the local data store
+ @param maxRetries: (int) maximum number of retries or -1 to retry forver or None to leave it to the node to decide
@param persistence: persistence of the request as one of the L{consts.Persistence} constants
@param priorityClass: priority of the request as one of the L{consts.Priority} consts
@param userData: any non persistent data to associate to the request
@@ -1194,49 +1298,123 @@
@return: (str) request identifier
"""
-
- # how to retrieve meta info about a key?
- # ...idea is to provoke a GetFailed (TooBig)
-
- msg = self.Message(
- self.Message.MessageClientGet,
+ # how to retrieve meta info about a key? ...idea is to provoke a GetFailed (TooBig)
+ return self.clientGet(
+ uri,
+ self.Message.SubTypeGetKeyInfo,
+ userData,
+ persistentUserData,
+ consts.FilenameCollision.HandleNever,
+ # Fcp params
+ Global=self.FcpFalse,
DSOnly=dsOnly,
- Global=self.FcpFalse,
Identifier=None,
IgnoreDS=ignoreDS,
-
+ MaxRetries = maxRetries,
MaxSize=self.MaxSizeKeyInfo,
-
Persistence=persistence,
PriorityClass=priorityClass,
ReturnType=self.ReturnType.Nothing,
-
URI=self.Uri(uri).uri,
Verbosity=self.Verbosity.ReportProgress,
)
+
+
+ ########################################################
+ ##
+ ## ClientPut related methods
+ ##
+ ########################################################
+ def clientPut(self,
+ uri,
+ messageSubType,
+ userData,
+ persistentUserData,
+ data,
+ **messageParams):
+
+ msg = self.Message(self.Message.MessageClientPut, URI=uri)
+ for paramName, value in messageParams.items():
+ if value is not None:
+ msg[paramName] = value
+ if data is not None:
+ msg.data = data
self._registerRequest(
msg,
userData,
- self.Message.SubTypeGetKeyInfo,
+ messageSubType,
time.time(),
persistentUserData,
+ #filenameCollision=filenameCollision,
)
self.sendMessageEx(msg)
return msg['Identifier']
-
- ########################################################
- ##
- ## ClientPut related methods
- ##
- ########################################################
- def put(self):
+
+
+ #TODO: method names
+
+ #CHK
+ def putData(self,
+ data,
+
+ contentType=None,
+ dontCompress=None,
+ maxRetries=None,
+ persistence=consts.Persistence.Connection,
+ priorityClass=consts.Priority.Medium,
+ targetFilename=None,
+
+ userData=None,
+ persistentUserData='',
+ ):
+
+ return self.clientPut(
+ self.Uri.KeyCHK,
+ self.Message.SubTypePutData,
+ userData,
+ persistentUserData,
+ data,
+
+ # fcp params
+ ContentType=contentType,
+ DataLength=len(data),
+ #EarlyEncode='false',
+ #GetCHKOnly='false',
+ Global=self.FcpFalse,
+ Identifier=None,
+ MaxRetries=maxRetries,
+ DontCompress=dontCompress,
+ Persistence=persistence,
+ TergetFilename=targetFilename,
+ UploadFrom=self.UploadFrom.Direct,
+ Verbosity=self.Verbosity.ReportProgressAndCompression,
+ )
+
+ def putFile(self):
pass
+
+ #KSK
+ def putNamedData(self):
+ pass
+ def putReference(self):
+ pass
+ #SSK
+ def putUpdatableData(self):
+ pass
+
+
+ #USK
+ def putVersionedData(self):
+ pass
+
+
+
########################################################
##
## request related methods
@@ -1379,7 +1557,7 @@
"""
while True:
identifier = uuid.uuid_time()
- if identifier not in self._requests:
+ if identifier not in self._sskRequests:
break
self.sendMessage(
self.Message.MessageGenerateSSK,
@@ -1427,7 +1605,7 @@
def testGetFile():
filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test.jpg')
-
+ print filename
identifier = c.getFile(
'CHK@q4~2soHTd9SOINIoXmg~dn7LNUAOYzN1tHNHT3j4c9E,gcVRtoglEhgqN-DJolXPqJ4yX1f~1gBGh89HNWlFMWQ,AAIC--8/snow_002%20%2810%29.jpg',
filename,
@@ -1438,11 +1616,11 @@
for i in xrange(50):
c.next()
- #c.removeRequest(identifier)
- #for i in xrange(5):
- # c.next()
+ c.removeRequest(identifier)
+ for i in xrange(5):
+ c.next()
- testGetFile()
+ #testGetFile()
@@ -1457,10 +1635,25 @@
for i in xrange(5):
c.next()
- #testgetKeyInfo()
+ #testGetKeyInfo()
+ def testPutData():
+ identifier = c.putData(
+ 'test123',
+ )
+
+ for i in xrange(1000):
+ c.next()
+ c.removeRequest(identifier)
+ for i in xrange(5):
+ c.next()
+
+ #testPutData()
+
+
+
def testConfigData():
from fcp2_0_config import Config
@@ -1478,7 +1671,7 @@
#testConfigData()
-
+
def testNodeData():
def cb(event, msg):
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|