Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2013-10-31 23:21:20 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2013-10-31 23:21:20 (GMT)
commit9b2dbf2a01afbcaa1b23b2d38515255ffa580cc4 (patch)
tree4d2315af4df0483b589605f92315055ca8841e67
parent8223b2b538540aa227a30cc18434571455af5bc3 (diff)
Do not reconnect on getting relocation errors
-rw-r--r--sugar_network/client/__init__.py40
-rw-r--r--sugar_network/client/routes.py21
-rw-r--r--sugar_network/toolkit/http.py26
-rw-r--r--sugar_network/toolkit/router.py1
-rwxr-xr-xtests/units/client/online_routes.py52
5 files changed, 96 insertions, 44 deletions
diff --git a/sugar_network/client/__init__.py b/sugar_network/client/__init__.py
index d8eefcb..c863e98 100644
--- a/sugar_network/client/__init__.py
+++ b/sugar_network/client/__init__.py
@@ -21,6 +21,25 @@ from os.path import join, expanduser, exists
from sugar_network.toolkit import http, Option
+def profile_path(*args):
+ """Path within sugar profile directory.
+
+ Missed directories will be created.
+
+ :param args:
+ path parts that will be added to the resulting path
+ :returns:
+ full path with directory part existed
+
+ """
+ if os.geteuid():
+ root_dir = join(os.environ['HOME'], '.sugar',
+ os.environ.get('SUGAR_PROFILE', 'default'))
+ else:
+ root_dir = '/var/sugar-network'
+ return join(root_dir, *args)
+
+
api_url = Option(
'url to connect to Sugar Network server API',
default='http://node-devel.sugarlabs.org', short_option='-a',
@@ -36,7 +55,7 @@ no_check_certificate = Option(
local_root = Option(
'path to the directory to keep all local data',
- default=lambda: profile_path('network'), name='local_root')
+ default=profile_path('network'), name='local_root')
server_mode = Option(
'start server to share local documents',
@@ -113,25 +132,6 @@ keyfile = Option(
_logger = logging.getLogger('client')
-def profile_path(*args):
- """Path within sugar profile directory.
-
- Missed directories will be created.
-
- :param args:
- path parts that will be added to the resulting path
- :returns:
- full path with directory part existed
-
- """
- if os.geteuid():
- root_dir = join(os.environ['HOME'], '.sugar',
- os.environ.get('SUGAR_PROFILE', 'default'))
- else:
- root_dir = '/var/sugar-network'
- return join(root_dir, *args)
-
-
def path(*args):
"""Calculate a path from the root.
diff --git a/sugar_network/client/routes.py b/sugar_network/client/routes.py
index 1feedec..bcc6a60 100644
--- a/sugar_network/client/routes.py
+++ b/sugar_network/client/routes.py
@@ -15,8 +15,8 @@
import os
import logging
-import httplib
from base64 import b64encode
+from httplib import IncompleteRead
from zipfile import ZipFile, ZIP_DEFLATED
from os.path import join, basename
@@ -207,10 +207,15 @@ class ClientRoutes(model.FrontRoutes, implementations.Routes, journal.Routes):
try:
reply = self._node.call(request, response)
if hasattr(reply, 'read'):
- return _ResponseStream(reply, self._restart_online)
+ if response.relocations:
+ return reply
+ else:
+ return _ResponseStream(reply, self._restart_online)
else:
return reply
- except (http.ConnectionError, httplib.IncompleteRead):
+ except (http.ConnectionError, IncompleteRead):
+ if response.relocations:
+ raise
self._restart_online()
return self._local.call(request, response)
else:
@@ -518,16 +523,16 @@ class _ResponseStream(object):
self._stream = stream
self._on_fail_cb = on_fail_cb
- def __hasattr__(self, key):
- return hasattr(self._stream, key)
+ def __hasattr__(self, name):
+ return hasattr(self._stream, name)
- def __getattr__(self, key):
- return getattr(self._stream, key)
+ def __getattr__(self, name):
+ return getattr(self._stream, name)
def read(self, size=None):
try:
return self._stream.read(size)
- except (http.ConnectionError, httplib.IncompleteRead):
+ except (http.ConnectionError, IncompleteRead):
self._on_fail_cb()
raise
diff --git a/sugar_network/toolkit/http.py b/sugar_network/toolkit/http.py
index 0a25c57..2506873 100644
--- a/sugar_network/toolkit/http.py
+++ b/sugar_network/toolkit/http.py
@@ -102,10 +102,15 @@ class GatewayTimeout(Status):
status_code = 504
+class _ConnectionError(Status):
+
+ status = '999 For testing purpose only'
+ status_code = 999
+
+
class Connection(object):
_Session = None
- _ConnectionError = None
def __init__(self, api_url='', auth=None, max_retries=0, **session_args):
self.api_url = api_url
@@ -203,12 +208,8 @@ class Connection(object):
try_ = 0
while True:
try_ += 1
- try:
- reply = self._session.request(method, path, data=data,
- headers=headers, params=params, **kwargs)
- except Connection._ConnectionError, error:
- raise ConnectionError, error, sys.exc_info()[2]
-
+ reply = self._session.request(method, path, data=data,
+ headers=headers, params=params, **kwargs)
if reply.status_code == Unauthorized.status_code:
enforce(self.auth is not None, Unauthorized, 'No credentials')
self._authenticate(reply.headers.get('www-authenticate'))
@@ -229,7 +230,8 @@ class Connection(object):
continue
error = content or reply.headers.get('x-sn-error') or \
'No error message provided'
- cls = _FORWARD_STATUSES.get(reply.status_code, RuntimeError)
+ cls = _FORWARD_STATUSES.get(reply.status_code, RuntimeError) \
+ or ConnectionError
raise cls(error)
return reply
@@ -277,6 +279,8 @@ class Connection(object):
response.meta[key[5:]] = json.loads(value)
elif not resend:
response[key] = value
+ if resend:
+ response.relocations += 1
if not resend:
break
path = reply.headers['location']
@@ -306,7 +310,8 @@ class Connection(object):
sys.path.insert(0, sys_path)
from requests import Session, exceptions
Connection._Session = Session
- Connection._ConnectionError = exceptions.ConnectionError
+ global ConnectionError
+ ConnectionError = exceptions.ConnectionError
self._session = Connection._Session()
self._session.headers['accept-language'] = \
@@ -384,7 +389,7 @@ class SugarAuth(object):
os.chmod(key_dir, 0700)
_logger.info('Generate RSA private key at %r', self._key_path)
- self._key = RSA.gen_key(2048, 65537, lambda *args: None)
+ self._key = RSA.gen_key(1024, 65537, lambda *args: None)
self._key.save_key(self._key_path, cipher=None)
os.chmod(self._key_path, 0600)
@@ -466,4 +471,5 @@ _FORWARD_STATUSES = {
BadGateway.status_code: BadGateway,
ServiceUnavailable.status_code: ServiceUnavailable,
GatewayTimeout.status_code: GatewayTimeout,
+ _ConnectionError.status_code: None,
}
diff --git a/sugar_network/toolkit/router.py b/sugar_network/toolkit/router.py
index b6d4466..9dd11cd 100644
--- a/sugar_network/toolkit/router.py
+++ b/sugar_network/toolkit/router.py
@@ -349,6 +349,7 @@ class Request(dict):
class Response(dict):
status = '200 OK'
+ relocations = 0
def __init__(self, **kwargs):
dict.__init__(self, kwargs)
diff --git a/tests/units/client/online_routes.py b/tests/units/client/online_routes.py
index 20bc2a1..46c18c1 100755
--- a/tests/units/client/online_routes.py
+++ b/tests/units/client/online_routes.py
@@ -1252,21 +1252,61 @@ Can't find all required implementations:
ipc.get(['context', context], cmd='feed', layer='public'))
def test_Redirects(self):
- URL = 'http://sugarlabs.org'
class Document(Resource):
@db.blob_property()
- def blob(self, value):
- raise http.Redirect(URL)
+ def blob1(self, value):
+ raise http.Redirect(prefix + 'blob2')
- self.start_online_client([User, Context, Implementation, Document])
+ @db.blob_property()
+ def blob3(self, value):
+ raise http.Redirect(client.api_url.value + prefix + 'blob4')
+
+ self.start_online_client([User, Document])
ipc = IPCConnection()
guid = ipc.post(['document'], {})
+ prefix = '/document/' + guid + '/'
+
+ response = requests.request('GET', client.api_url.value + prefix + 'blob1', allow_redirects=False)
+ self.assertEqual(303, response.status_code)
+ self.assertEqual(prefix + 'blob2', response.headers['Location'])
- response = requests.request('GET', client.api_url.value + '/document/' + guid + '/blob', allow_redirects=False)
+ response = requests.request('GET', client.api_url.value + prefix + 'blob3', allow_redirects=False)
self.assertEqual(303, response.status_code)
- self.assertEqual(URL, response.headers['Location'])
+ self.assertEqual(client.api_url.value + prefix + 'blob4', response.headers['Location'])
+
+ def test_DoNotSwitchToOfflineOnRedirectFails(self):
+
+ class Document(Resource):
+
+ @db.blob_property()
+ def blob1(self, value):
+ raise http.Redirect(prefix + '/blob2')
+
+ @db.blob_property()
+ def blob2(self, value):
+ raise http._ConnectionError()
+
+ local_volume = self.start_online_client([User, Document])
+ ipc = IPCConnection()
+ guid = ipc.post(['document'], {})
+ prefix = client.api_url.value + '/document/' + guid + '/'
+ local_volume['document'].create({'guid': guid})
+
+ trigger = self.wait_for_events(ipc, event='inline', state='connecting')
+ try:
+ ipc.get(['document', guid, 'blob1'])
+ except Exception:
+ pass
+ assert trigger.wait(.1) is None
+
+ trigger = self.wait_for_events(ipc, event='inline', state='connecting')
+ try:
+ ipc.get(['document', guid, 'blob2'])
+ except Exception:
+ pass
+ assert trigger.wait(.1) is not None
def test_ContentDisposition(self):
self.start_online_client([User, Context, Implementation, Artifact])