Author: tomaz
Date: Thu May 23 19:53:52 2013
New Revision: 1485839
URL: http://svn.apache.org/r1485839
Log:
Fix an issue with double encoding the container name in the CloudFiles
driver upload_object method.
Also properly encode container and object name used in the HTTP request
in the get_container and get_object method. For clarity rename current
_clean* methods to _encode*.
Fixes LIBCLOUD-328.
Modified:
libcloud/trunk/CHANGES
libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
libcloud/trunk/libcloud/test/storage/test_cloudfiles.py
Modified: libcloud/trunk/CHANGES
URL:
http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1485839&r1=1485838&r2=1485839&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Thu May 23 19:53:52 2013
@@ -54,6 +54,14 @@ Changes with Apache Libcloud in deveplom
return value. (LIBCLOUD-326)
[Andre Merzky, Tomaz Muraus]
+ *) Storage
+
+ - Fix an issue with double encoding the container name in the CloudFiles
+ driver upload_object method.
+ Also properly encode container and object name used in the HTTP request
+ in the get_container and get_object method. (LIBCLOUD-328)
+ [Tomaz Muraus]
+
*) Load Balancer
- Add ex_list_current_usage method to the Rackspace driver.
Modified: libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/drivers/cloudfiles.py?rev=1485839&r1=1485838&r2=1485839&view=diff
==============================================================================
--- libcloud/trunk/libcloud/storage/drivers/cloudfiles.py (original)
+++ libcloud/trunk/libcloud/storage/drivers/cloudfiles.py Thu May 23 19:53:52
2013
@@ -237,7 +237,8 @@ class CloudFilesStorageDriver(StorageDri
raise LibcloudError('Unexpected status code: %s' % (response.status))
def get_container(self, container_name):
- response = self.connection.request('/%s' % (container_name),
+ container_name_encoded = self._encode_container_name(container_name)
+ response = self.connection.request('/%s' % (container_name_encoded),
method='HEAD')
if response.status == httplib.NO_CONTENT:
@@ -251,8 +252,11 @@ class CloudFilesStorageDriver(StorageDri
def get_object(self, container_name, object_name):
container = self.get_container(container_name)
- response = self.connection.request('/%s/%s' % (container_name,
- object_name),
+ container_name_encoded = self._encode_container_name(container_name)
+ object_name_encoded = self._encode_container_name(object_name)
+
+ response = self.connection.request('/%s/%s' % (container_name_encoded,
+ object_name_encoded),
method='HEAD')
if response.status in [httplib.OK, httplib.NO_CONTENT]:
obj = self._headers_to_object(
@@ -304,9 +308,9 @@ class CloudFilesStorageDriver(StorageDri
return response.status in [httplib.CREATED, httplib.ACCEPTED]
def create_container(self, container_name):
- container_name = self._clean_container_name(container_name)
+ container_name_encoded = self._encode_container_name(container_name)
response = self.connection.request(
- '/%s' % (container_name), method='PUT')
+ '/%s' % (container_name_encoded), method='PUT')
if response.status == httplib.CREATED:
# Accepted mean that container is not yet created but it will be
@@ -323,7 +327,7 @@ class CloudFilesStorageDriver(StorageDri
raise LibcloudError('Unexpected status code: %s' % (response.status))
def delete_container(self, container):
- name = self._clean_container_name(container.name)
+ name = self._encode_container_name(container.name)
# Only empty container can be deleted
response = self.connection.request('/%s' % (name), method='DELETE')
@@ -398,8 +402,8 @@ class CloudFilesStorageDriver(StorageDri
extra=extra, iterator=iterator)
def delete_object(self, obj):
- container_name = self._clean_container_name(obj.container.name)
- object_name = self._clean_object_name(obj.name)
+ container_name = self._encode_container_name(obj.container.name)
+ object_name = self._encode_object_name(obj.name)
response = self.connection.request(
'/%s/%s' % (container_name, object_name), method='DELETE')
@@ -420,8 +424,8 @@ class CloudFilesStorageDriver(StorageDri
completes. (optional)
@type email: C{str}
"""
- container_name = self._clean_container_name(obj.container.name)
- object_name = self._clean_object_name(obj.name)
+ container_name = self._encode_container_name(obj.container.name)
+ object_name = self._encode_object_name(obj.name)
headers = {'X-Purge-Email': email} if email else {}
response = self.connection.request('/%s/%s' % (container_name,
@@ -607,14 +611,14 @@ class CloudFilesStorageDriver(StorageDri
extra = extra or {}
meta_data = extra.get('meta_data')
- container_name_cleaned = self._clean_container_name(container.name)
- object_name_cleaned = self._clean_object_name(object_name)
- request_path = '/%s/%s' % (container_name_cleaned, object_name_cleaned)
+ container_name_encoded = self._encode_container_name(container.name)
+ object_name_encoded = self._encode_object_name(object_name)
+ request_path = '/%s/%s' % (container_name_encoded, object_name_encoded)
headers = {'X-Auth-Token': self.connection.auth_token,
'X-Object-Manifest': '%s/%s/' %
- (container_name_cleaned,
- object_name_cleaned)}
+ (container_name_encoded,
+ object_name_encoded)}
data = ''
response = self.connection.request(request_path,
@@ -670,8 +674,8 @@ class CloudFilesStorageDriver(StorageDri
upload_func_kwargs, extra=None, file_path=None,
iterator=None, verify_hash=True):
extra = extra or {}
- container_name_cleaned = self._clean_container_name(container.name)
- object_name_cleaned = self._clean_object_name(object_name)
+ container_name_encoded = self._encode_container_name(container.name)
+ object_name_encoded = self._encode_object_name(object_name)
content_type = extra.get('content_type', None)
meta_data = extra.get('meta_data', None)
@@ -681,7 +685,7 @@ class CloudFilesStorageDriver(StorageDri
key = 'X-Object-Meta-%s' % (key)
headers[key] = value
- request_path = '/%s/%s' % (container_name_cleaned, object_name_cleaned)
+ request_path = '/%s/%s' % (container_name_encoded, object_name_encoded)
result_dict = self._upload_object(
object_name=object_name, content_type=content_type,
upload_func=upload_func, upload_func_kwargs=upload_func_kwargs,
@@ -715,9 +719,9 @@ class CloudFilesStorageDriver(StorageDri
raise LibcloudError('status_code=%s' % (response.status),
driver=self)
- def _clean_container_name(self, name):
+ def _encode_container_name(self, name):
"""
- Clean container name.
+ Encode container name so it can be used as part of the HTTP request.
"""
if name.startswith('/'):
name = name[1:]
@@ -735,7 +739,7 @@ class CloudFilesStorageDriver(StorageDri
return name
- def _clean_object_name(self, name):
+ def _encode_object_name(self, name):
name = urlquote(name)
return name
Modified: libcloud/trunk/libcloud/test/storage/test_cloudfiles.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/storage/test_cloudfiles.py?rev=1485839&r1=1485838&r2=1485839&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/storage/test_cloudfiles.py (original)
+++ libcloud/trunk/libcloud/test/storage/test_cloudfiles.py Thu May 23 19:53:52
2013
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
@@ -28,6 +29,7 @@ import libcloud.utils.files
from libcloud.utils.py3 import PY3
from libcloud.utils.py3 import b
from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import urlquote
if PY3:
from io import FileIO as file
@@ -669,6 +671,26 @@ class CloudFilesTests(unittest.TestCase)
finally:
self.driver.connection.request = _request
+ def test_create_container_put_object_name_encoding(self):
+ def upload_file(self, response, file_path, chunked=False,
+ calculate_hash=True):
+ return True, 'hash343hhash89h932439jsaa89', 1000
+
+ old_func = CloudFilesStorageDriver._upload_file
+ CloudFilesStorageDriver._upload_file = upload_file
+
+ container_name = 'speci@l_name'
+ object_name = 'm@objâ¬ct'
+ file_path = os.path.abspath(__file__)
+
+ container = self.driver.create_container(container_name=container_name)
+ self.assertEqual(container.name, container_name)
+
+ obj = self.driver.upload_object(file_path=file_path,
container=container,
+ object_name=object_name)
+ self.assertEqual(obj.name, object_name)
+ CloudFilesStorageDriver._upload_file = old_func
+
def test_ex_enable_static_website(self):
container = Container(name='foo_bar_container', extra={}, driver=self)
result = self.driver.ex_enable_static_website(container=container,
@@ -884,6 +906,22 @@ class CloudFilesMockHttp(StorageMockHttp
status_code = httplib.CREATED
return (status_code, body, headers, httplib.responses[httplib.OK])
+ def _v1_MossoCloudFS_speci_40l_name(self, method, url, body, headers):
+ # test_create_container_put_object_name_encoding
+ # Verify that the name is properly url encoded
+ container_name = 'speci@l_name'
+ encoded_container_name = urlquote(container_name)
+ self.assertTrue(encoded_container_name in url)
+
+ headers = copy.deepcopy(self.base_headers)
+ body = self.fixtures.load('list_container_objects_empty.json')
+ headers = copy.deepcopy(self.base_headers)
+ headers.update({ 'content-length': 18,
+ 'date': 'Mon, 28 Feb 2011 07:52:57 GMT'
+ })
+ status_code = httplib.CREATED
+ return (status_code, body, headers, httplib.responses[httplib.OK])
+
def _v1_MossoCloudFS_test_create_container_ALREADY_EXISTS(
self, method, url, body, headers):
# test_create_container_already_exists
@@ -986,6 +1024,19 @@ class CloudFilesMockRawResponse(MockRawR
headers['etag'] = 'hash343hhash89h932439jsaa89'
return (httplib.CREATED, body, headers, httplib.responses[httplib.OK])
+ def _v1_MossoCloudFS_speci_40l_name_m_40obj_E2_82_ACct(self, method, url,
+ body, headers):
+ # test_create_container_put_object_name_encoding
+ # Verify that the name is properly url encoded
+ object_name = 'm@objâ¬ct'
+ encoded_object_name = urlquote(object_name)
+
+ headers = copy.deepcopy(self.base_headers)
+ body = ''
+ headers['etag'] = 'hash343hhash89h932439jsaa89'
+ return (httplib.CREATED, body, headers, httplib.responses[httplib.OK])
+
+
def _v1_MossoCloudFS_foo_bar_container_empty(self, method, url, body,
headers):
# test_upload_object_zero_size_object