summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrebs <drebs@leap.se>2014-04-15 14:30:31 -0300
committerdrebs <drebs@leap.se>2014-04-15 14:48:26 -0300
commit2c3e88a83a9edf2758691f163e9dec84d30ea81c (patch)
treed0b78f2bfefc66aacd4ee1149ce95fe36ff448d4
parent8ef9038178ca0bfab8045be92bd20e6407e4fe2a (diff)
Close connection after syncing (#5507).
-rw-r--r--client/changes/bug_5507_close-connection-after-sync2
-rw-r--r--client/src/leap/soledad/client/sqlcipher.py53
2 files changed, 30 insertions, 25 deletions
diff --git a/client/changes/bug_5507_close-connection-after-sync b/client/changes/bug_5507_close-connection-after-sync
new file mode 100644
index 00000000..0e77ab25
--- /dev/null
+++ b/client/changes/bug_5507_close-connection-after-sync
@@ -0,0 +1,2 @@
+ o Close connection with server after syncing to avoid client hanging on exit
+ (#5507).
diff --git a/client/src/leap/soledad/client/sqlcipher.py b/client/src/leap/soledad/client/sqlcipher.py
index 3aea340d..04f8ebf9 100644
--- a/client/src/leap/soledad/client/sqlcipher.py
+++ b/client/src/leap/soledad/client/sqlcipher.py
@@ -43,12 +43,15 @@ So, as the statements above were introduced for backwards compatibility with
SLCipher 1.1 databases, we do not implement them as all SQLCipher databases
handled by Soledad should be created by SQLCipher >= 2.0.
"""
-import httplib
import logging
import os
import string
import threading
import time
+import json
+
+from hashlib import sha256
+from contextlib import contextmanager
from pysqlcipher import dbapi2
from u1db.backends import sqlite_backend
@@ -149,7 +152,6 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase):
k_lock = threading.Lock()
create_doc_lock = threading.Lock()
update_indexes_lock = threading.Lock()
- _syncer = None
def __init__(self, sqlcipher_file, password, document_factory=None,
crypto=None, raw_key=False, cipher='aes-256-cbc',
@@ -211,6 +213,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase):
has_conflicts=has_conflicts,
syncable=syncable)
self.set_document_factory(factory)
+ self._syncers = {}
@classmethod
def _open_database(cls, sqlcipher_file, password, document_factory=None,
@@ -351,46 +354,46 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase):
:return: The local generation before the synchronisation was performed.
:rtype: int
"""
- if not self.syncer:
- self._create_syncer(url, creds=creds)
-
- try:
- res = self.syncer.sync(autocreate=autocreate)
- except httplib.CannotSendRequest:
- # raised when you reuse httplib.HTTP object for new request
- # while you havn't called its getresponse()
- # this catch works for the current connclass used
- # by our HTTPClientBase, since it uses httplib.
- # we will have to replace it if it changes.
- logger.info("Replacing connection and trying again...")
- self._syncer = None
- self._create_syncer(url, creds=creds)
- res = self.syncer.sync(autocreate=autocreate)
+ res = None
+ with self.syncer(url, creds=creds) as syncer:
+ res = syncer.sync(autocreate=autocreate)
return res
- @property
- def syncer(self):
+ @contextmanager
+ def syncer(self, url, creds=None):
"""
Accesor for synchronizer.
"""
- return self._syncer
+ syncer = self._get_syncer(url, creds=creds)
+ yield syncer
+ syncer.sync_target.close()
- def _create_syncer(self, url, creds=None):
+ def _get_syncer(self, url, creds=None):
"""
- Creates a synchronizer
+ Get a synchronizer for C{url} using C{creds}.
:param url: The url of the target replica to sync with.
:type url: str
:param creds: optional dictionary giving credentials.
- to authorize the operation with the server.
+ to authorize the operation with the server.
:type creds: dict
+
+ :return: A synchronizer.
+ :rtype: u1db.sync.Synchronizer
"""
- if self._syncer is None:
- self._syncer = Synchronizer(
+ # we want to store at most one syncer for each url, so we also store a
+ # hash of the connection credentials and replace the stored syncer for
+ # a certain url if credentials have changed.
+ h = sha256(json.dumps([url, creds])).hexdigest()
+ cur_h, syncer = self._syncers.get(url, (None, None))
+ if syncer is None or h != cur_h:
+ syncer = Synchronizer(
self,
SoledadSyncTarget(url,
creds=creds,
crypto=self._crypto))
+ self._syncers[url] = (h, syncer)
+ return syncer
def _extra_schema_init(self, c):
"""