Repository: websocket-client/websocket-client

Issue:
Authenticated proxy fails if username/password contains urlencoded characters
When I try to use an authenticated proxy like this:

```sh
export http_proxy=http://USERNAME:PASSWORD@example.com:1234
export https_proxy=http://USERNAME:PASSWORD@example.com:1234
```

I get `407 Proxy Authentication Required` with `websocket-client`, while it works fine with `requests` etc.

Discussion:
engn33r: First, because this project is undergoing a license change, can you consent to the Apache 2.0 license change in issue #697? Instructions are at the top of that issue. To avoid complications, I want to avoid accepting any PRs during this license transition unless the contributor first consents to the license change.
codecov[bot]: # [Codecov](https://codecov.io/gh/websocket-client/websocket-client/pull/723?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client) Report
> Merging [#723](https://codecov.io/gh/websocket-client/websocket-client/pull/723?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client) (4ee6958) into [master](https://codecov.io/gh/websocket-client/websocket-client/commit/426ed15429e103e78cb76982d46c41a52b7947ff?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client) (426ed15) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/websocket-client/websocket-client/pull/723/graphs/tree.svg?width=650&height=150&src=pr&token=pcXhUQwiL3&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client)](https://codecov.io/gh/websocket-client/websocket-client/pull/723?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client)

```diff
@@           Coverage Diff           @@
##           master     #723   +/-   ##
=======================================
  Coverage   82.51%   82.51%           
=======================================
  Files          13       13           
  Lines        1264     1264           
  Branches      245      245           
=======================================
  Hits         1043     1043           
  Misses        141      141           
  Partials       80       80           
```


| [Impacted Files](https://codecov.io/gh/websocket-client/websocket-client/pull/723?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client) | Coverage Δ | |
|---|---|---|
| [websocket/\_url.py](https://codecov.io/gh/websocket-client/websocket-client/pull/723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client#diff-d2Vic29ja2V0L191cmwucHk=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/websocket-client/websocket-client/pull/723?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/websocket-client/websocket-client/pull/723?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client). Last update [426ed15...4ee6958](https://codecov.io/gh/websocket-client/websocket-client/pull/723?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=websocket-client).

nyuszika7h: > First, because this project is undergoing a license change, can you consent to the Apache 2.0 license change in issue #697? Instructions are at the top of that issue. To avoid complications, I want to avoid accepting any PRs during this license transition unless the contributor first consents to the license change.

Sure, done.

Patch:
diff --git a/websocket/_url.py b/websocket/_url.py
index f681ebf..0867911 100644
--- a/websocket/_url.py
+++ b/websocket/_url.py
@@ -26,7 +26,7 @@
 import socket
 import struct
 
-from urllib.parse import urlparse
+from urllib.parse import unquote, urlparse
 
 
 __all__ = ["parse_url", "get_proxy_info"]
@@ -171,7 +171,7 @@ def get_proxy_info(
         value = os.environ.get(key, os.environ.get(key.upper(), "")).replace(" ", "")
         if value:
             proxy = urlparse(value)
-            auth = (proxy.username, proxy.password) if proxy.username else None
+            auth = (unquote(proxy.username), unquote(proxy.password)) if proxy.username else None
             return proxy.hostname, proxy.port, auth
 
     return None, 0, None


Test patch:
diff --git a/websocket/tests/test_url.py b/websocket/tests/test_url.py
index a33e934..34a3594 100644
--- a/websocket/tests/test_url.py
+++ b/websocket/tests/test_url.py
@@ -277,6 +277,10 @@ def testProxyFromEnv(self):
         os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
         self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("a", "b")))
 
+        os.environ["http_proxy"] = "http://john%40example.com:P%40SSWORD@localhost:3128/"
+        os.environ["https_proxy"] = "http://john%40example.com:P%40SSWORD@localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("john@example.com", "P@SSWORD")))
+
         os.environ["http_proxy"] = "http://a:b@localhost/"
         os.environ["https_proxy"] = "http://a:b@localhost2/"
         os.environ["no_proxy"] = "example1.com,example2.com"