Repository: mitmproxy/mitmproxy

Issue:
strutils.bytes_to_escaped_str: Add option to not escape quotes at all
As the title says. This is useful e.g. in the raw view, where these don't need to be replaced. I believe this should just be 

``` python
ret = re.sub(r"(?<!\\)(\\\\)*\\'", lambda m: (m.group(1) or "") + "'", ret)
```



Discussion:

Resolving patch:
diff --git a/netlib/strutils.py b/netlib/strutils.py
--- a/netlib/strutils.py
+++ b/netlib/strutils.py
@@ -69,7 +69,7 @@ def escape_control_characters(text, keep_spacing=True):
     return text.translate(trans)
 
 
-def bytes_to_escaped_str(data, keep_spacing=False):
+def bytes_to_escaped_str(data, keep_spacing=False, escape_single_quotes=False):
     """
     Take bytes and return a safe string that can be displayed to the user.
 
@@ -86,6 +86,8 @@ def bytes_to_escaped_str(data, keep_spacing=False):
     # We always insert a double-quote here so that we get a single-quoted string back
     # https://stackoverflow.com/questions/29019340/why-does-python-use-different-quotes-for-representing-strings-depending-on-their
     ret = repr(b'"' + data).lstrip("b")[2:-1]
+    if not escape_single_quotes:
+        ret = re.sub(r"(?<!\\)(\\\\)*\\'", lambda m: (m.group(1) or "") + "'", ret)
     if keep_spacing:
         ret = re.sub(
             r"(?<!\\)(\\\\)*\\([nrt])",
diff --git a/pathod/language/base.py b/pathod/language/base.py
--- a/pathod/language/base.py
+++ b/pathod/language/base.py
@@ -136,7 +136,7 @@ def parseAction(cls, x):
 
     def spec(self):
         inner = strutils.bytes_to_escaped_str(self.val)
-        inner = inner.replace(r"\'", r"\x27")
+        inner = inner.replace(r"'", r"\x27")
         return "'" + inner + "'"
 
 
@@ -148,7 +148,7 @@ def expr(cls):
         return e.setParseAction(lambda x: cls(*x))
 
     def spec(self):
-        return strutils.bytes_to_escaped_str(self.val)
+        return strutils.bytes_to_escaped_str(self.val, escape_single_quotes=True)
 
 
 class TokValueGenerate(Token):
@@ -166,7 +166,7 @@ def get_generator(self, settings_):
 
     def freeze(self, settings):
         g = self.get_generator(settings)
-        return TokValueLiteral(strutils.bytes_to_escaped_str(g[:]))
+        return TokValueLiteral(strutils.bytes_to_escaped_str(g[:], escape_single_quotes=True))
 
     @classmethod
     def expr(cls):
@@ -578,4 +578,4 @@ def spec(self):
 
     def freeze(self, settings):
         f = self.parsed.freeze(settings).spec()
-        return self.__class__(TokValueLiteral(strutils.bytes_to_escaped_str(f.encode())))
+        return self.__class__(TokValueLiteral(strutils.bytes_to_escaped_str(f.encode(), escape_single_quotes=True)))
diff --git a/pathod/pathoc.py b/pathod/pathoc.py
--- a/pathod/pathoc.py
+++ b/pathod/pathoc.py
@@ -444,7 +444,7 @@ def http(self, r):
             finally:
                 if resp:
                     lg("<< %s %s: %s bytes" % (
-                        resp.status_code, strutils.bytes_to_escaped_str(resp.reason.encode()), len(resp.content)
+                        resp.status_code, strutils.escape_control_characters(resp.reason) if resp.reason else "", len(resp.content)
                     ))
                     if resp.status_code in self.ignorecodes:
                         lg.suppress()


Test patch:
diff --git a/test/netlib/test_strutils.py b/test/netlib/test_strutils.py
--- a/test/netlib/test_strutils.py
+++ b/test/netlib/test_strutils.py
@@ -48,9 +48,12 @@ def test_bytes_to_escaped_str():
     assert strutils.bytes_to_escaped_str(b"\b") == r"\x08"
     assert strutils.bytes_to_escaped_str(br"&!?=\)") == r"&!?=\\)"
     assert strutils.bytes_to_escaped_str(b'\xc3\xbc') == r"\xc3\xbc"
-    assert strutils.bytes_to_escaped_str(b"'") == r"\'"
+    assert strutils.bytes_to_escaped_str(b"'") == r"'"
     assert strutils.bytes_to_escaped_str(b'"') == r'"'
 
+    assert strutils.bytes_to_escaped_str(b"'", escape_single_quotes=True) == r"\'"
+    assert strutils.bytes_to_escaped_str(b'"', escape_single_quotes=True) == r'"'
+
     assert strutils.bytes_to_escaped_str(b"\r\n\t") == "\\r\\n\\t"
     assert strutils.bytes_to_escaped_str(b"\r\n\t", True) == "\r\n\t"