From df1f4f3e3932a9653b7b1731e121c51c7fdf31e1 Mon Sep 17 00:00:00 2001
From: "Lisak, Peter" <peter.lisak@firma.seznam.cz>
Date: Tue, 6 Oct 2015 10:08:02 +0200
Subject: [PATCH] swiftclient content-type header

According to help `swift upload -h` you can add a customized request header 'Content-Type'.
But actually it is ignored (cleared and default is used) if subcommand is upload.

Subcommand post works as expected in help.

Bug fix: Use 'Content-Type' from the customized request headers also if
uploading.

Change-Id: If0d1354b6214b909527341078fe1769aa6587457
---
 swiftclient/client.py                | 10 +++++---
 tests/functional/test_swiftclient.py | 34 ++++++++++++++++++++++++++++
 tests/unit/test_swiftclient.py       | 18 +++++++++++++++
 3 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/swiftclient/client.py b/swiftclient/client.py
index f2d3098c..cad115f6 100644
--- a/swiftclient/client.py
+++ b/swiftclient/client.py
@@ -1028,8 +1028,11 @@ def put_object(url, token=None, container=None, name=None, contents=None,
     :param chunk_size: chunk size of data to write; it defaults to 65536;
                        used only if the contents object has a 'read'
                        method, e.g. file-like objects, ignored otherwise
-    :param content_type: value to send as content-type header; if None, an
-                         empty string value will be sent
+
+    :param content_type: value to send as content-type header, overriding any
+                       value included in the headers param; if None and no
+                       value is found in the headers param, an empty string
+                       value will be sent
     :param headers: additional headers to include in the request, if any
     :param http_conn: HTTP connection object (If None, it will create the
                       conn object)
@@ -1071,7 +1074,8 @@ def put_object(url, token=None, container=None, name=None, contents=None,
                 content_length = int(v)
     if content_type is not None:
         headers['Content-Type'] = content_type
-    else:  # python-requests sets application/x-www-form-urlencoded otherwise
+    elif 'Content-Type' not in headers:
+        # python-requests sets application/x-www-form-urlencoded otherwise
         headers['Content-Type'] = ''
     if not contents:
         headers['Content-Length'] = '0'
diff --git a/tests/functional/test_swiftclient.py b/tests/functional/test_swiftclient.py
index 2be280da..35a5ea7e 100644
--- a/tests/functional/test_swiftclient.py
+++ b/tests/functional/test_swiftclient.py
@@ -217,6 +217,40 @@ class TestFunctional(testtools.TestCase):
         self.assertEqual('application/octet-stream',
                          hdrs.get('content-type'))
 
+        # Same but with content_type
+        self.conn.put_object(
+            self.containername, self.objectname,
+            content_type='text/plain', contents=self.test_data)
+        hdrs = self.conn.head_object(self.containername, self.objectname)
+        self.assertEqual(str(len(self.test_data)),
+                         hdrs.get('content-length'))
+        self.assertEqual(self.etag, hdrs.get('etag'))
+        self.assertEqual('text/plain',
+                         hdrs.get('content-type'))
+
+        # Same but with content-type in headers
+        self.conn.put_object(
+            self.containername, self.objectname,
+            headers={'Content-Type': 'text/plain'}, contents=self.test_data)
+        hdrs = self.conn.head_object(self.containername, self.objectname)
+        self.assertEqual(str(len(self.test_data)),
+                         hdrs.get('content-length'))
+        self.assertEqual(self.etag, hdrs.get('etag'))
+        self.assertEqual('text/plain',
+                         hdrs.get('content-type'))
+
+        # content_type rewrites content-type in headers
+        self.conn.put_object(
+            self.containername, self.objectname,
+            content_type='image/jpeg',
+            headers={'Content-Type': 'text/plain'}, contents=self.test_data)
+        hdrs = self.conn.head_object(self.containername, self.objectname)
+        self.assertEqual(str(len(self.test_data)),
+                         hdrs.get('content-length'))
+        self.assertEqual(self.etag, hdrs.get('etag'))
+        self.assertEqual('image/jpeg',
+                         hdrs.get('content-type'))
+
         # Same but with content-length
         self.conn.put_object(
             self.containername, self.objectname,
diff --git a/tests/unit/test_swiftclient.py b/tests/unit/test_swiftclient.py
index 53fcccb7..68af46ab 100644
--- a/tests/unit/test_swiftclient.py
+++ b/tests/unit/test_swiftclient.py
@@ -1013,6 +1013,24 @@ class TestPutObject(MockHttpTest):
         request_header = resp.requests_params['headers']
         self.assertEqual(request_header['content-type'], b'')
 
+    def test_content_type_in_headers(self):
+        conn = c.http_connection(u'http://www.test.com/')
+        resp = MockHttpResponse(status=200)
+        conn[1].getresponse = resp.fake_response
+        conn[1]._request = resp._fake_request
+
+        # title-case header
+        hdrs = {'Content-Type': 'text/Plain'}
+        c.put_object(url='http://www.test.com', http_conn=conn, headers=hdrs)
+        request_header = resp.requests_params['headers']
+        self.assertEqual(request_header['content-type'], b'text/Plain')
+
+        # method param overrides headers
+        c.put_object(url='http://www.test.com', http_conn=conn, headers=hdrs,
+                     content_type='image/jpeg')
+        request_header = resp.requests_params['headers']
+        self.assertEqual(request_header['content-type'], b'image/jpeg')
+
 
 class TestPostObject(MockHttpTest):