Browse code

Tolerate non-XML errors, e.g. proxy-generated 403/502 HTML errors

Code used to raise an unhandled exception if the resulting error is not an XML
(ElementTree ParseError when creating S3Error object). In certain cases (e.g.
5xx errors), this also disrupted the retry flow.

Easily reproducible when running a simple 's3cmd ls' while s3cfg has an http
proxy set up, and the proxy denies all requests - returns a non-XML 403 error.

* Also extracted a method for the XML Error parsing to clean the code a little.

Oren Held authored on 2014/05/10 20:52:10
Showing 1 changed files
... ...
@@ -46,14 +46,13 @@ class S3Error (S3Exception):
46 46
             for header in response["headers"]:
47 47
                 debug("HttpHeader: %s: %s" % (header, response["headers"][header]))
48 48
         if response.has_key("data") and response["data"]:
49
-            tree = getTreeFromXml(response["data"])
50
-            error_node = tree
51
-            if not error_node.tag == "Error":
52
-                error_node = tree.find(".//Error")
53
-            for child in error_node.getchildren():
54
-                if child.text != "":
55
-                    debug("ErrorXML: " + child.tag + ": " + repr(child.text))
56
-                    self.info[child.tag] = child.text
49
+            try:
50
+                tree = getTreeFromXml(response["data"])
51
+            except ET.ParseError:
52
+                debug("Not an XML response")
53
+            else:
54
+                self.info.update(self.parse_error_xml(tree))
55
+
57 56
         self.code = self.info["Code"]
58 57
         self.message = self.info["Message"]
59 58
         self.resource = self.info["Resource"]
... ...
@@ -65,6 +64,20 @@ class S3Error (S3Exception):
65 65
             retval += (u": %s" % self.info["Message"])
66 66
         return retval
67 67
 
68
+    @staticmethod
69
+    def parse_error_xml(tree):
70
+        info = {}
71
+        error_node = tree
72
+        if not error_node.tag == "Error":
73
+            error_node = tree.find(".//Error")
74
+        for child in error_node.getchildren():
75
+            if child.text != "":
76
+                debug("ErrorXML: " + child.tag + ": " + repr(child.text))
77
+                info[child.tag] = child.text
78
+
79
+        return info
80
+
81
+
68 82
 class CloudFrontError(S3Error):
69 83
     pass
70 84