Repository: getsentry/sentry-python

Issue:
Send events without proxy  when http_proxy is configured as env variable
Hi,
I have problem with configuration of sentry_sdk. My code is behind corporate proxy and `http_proxy` is configured on machine as env variable (required when I want to install something via pip for example). Also I have env variable `no_proxy` where I added ip address for host where sentry is working (this ip is inside corporate network). When I try send event to sentry I got return code 403 (proxy refuse request). Probably sentry_sdk automacally fetch proxy from http_proxy env but ignore no_proxy. When I remove http_proxy from env everything is working. How can I configure python_sdk to ignore my http_proxy env variable? My code: 

```
import logging
import sentry_sdk
from sentry_sdk.integrations.logging import LoggingIntegration


sentry_logging = LoggingIntegration(
    level=logging.INFO,       
    event_level=logging.ERROR 
)
sentry_sdk.init(
    dsn="<dsn>",
    integrations=[sentry_logging],
    debug=True,
)
```


Discussion:
Hi, yes there's currently no way to disable `http_proxy` if you have an envvar. I am not sure if `no_proxy` is easy to implement.
The same problem behid proxy. In this situation sentry_sdk is difficult to use. Maybe better solution will be use requests instead urllib3? Requests can handle no_proxy out of the box.
I want to add that switching to requests library can have other benefits like supporting SOCKS proxies. Currently, providing SOCKS proxy to `http_proxy` parameter results in `ProxySchemeUnknown` exception. To fix that with urllib3, you should explicitly check for `socks://` schemes and use `SOCKSProxyManager` instead of `ProxyManager`, but requests handles it seamlessly.
To keep this issue updated: We discussed this internally and decided on a hard rule not to use requests. The reason for this is that we would like to explicitly control which behavior we support (since all of the new SDKs share a single piece of documentation describing that behavior, and the behavior across SDKs and across languages should be the same), and requests ideologically is the opposite of that: I for one was unaware that requests reads environment variables like that, even though I've been using it for quite a while.

I would propose that we keep the current, simple, stupid proxying behavior, but add a new flag that disables any access to the process environment. Is this an acceptable fix?
> I would propose that we keep the current, simple, stupid proxying behavior, but add a new flag that disables any access to the process environment. Is this an acceptable fix?

The `no_proxy` env var is widely used. I strongly feel that if an application reads from `http_proxy` it should also respect the `no_proxy` variable. Please consider supporting it, thanks.

Resolving patch:
diff --git a/sentry_sdk/transport.py b/sentry_sdk/transport.py
--- a/sentry_sdk/transport.py
+++ b/sentry_sdk/transport.py
@@ -156,10 +156,15 @@ def _make_pool(
         ca_certs,  # type: Optional[Any]
     ):
         # type: (...) -> Union[PoolManager, ProxyManager]
-        # Use http_proxy if scheme is https and https_proxy is not set
-        proxy = parsed_dsn.scheme == "https" and https_proxy or http_proxy
-        if not proxy:
-            proxy = getproxies().get(parsed_dsn.scheme)
+        proxy = None
+
+        # try HTTPS first
+        if parsed_dsn.scheme == "https" and (https_proxy != ""):
+            proxy = https_proxy or getproxies().get("https")
+
+        # maybe fallback to HTTP proxy
+        if not proxy and (http_proxy != ""):
+            proxy = http_proxy or getproxies().get("http")
 
         opts = self._get_pool_options(ca_certs)
 


Test patch:
diff --git a/tests/test_client.py b/tests/test_client.py
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -1,6 +1,7 @@
 # coding: utf-8
 import json
 import logging
+import os
 import pytest
 import subprocess
 import sys
@@ -52,25 +53,106 @@ def test_transport_option(monkeypatch):
     assert str(Client(transport=transport).dsn) == dsn
 
 
-def test_http_proxy(monkeypatch):
-    client = Client("https://foo@sentry.io/123", http_proxy="http://localhost/123")
+def test_proxy_http_use(monkeypatch):
+    client = Client("http://foo@sentry.io/123", http_proxy="http://localhost/123")
     assert client.transport._pool.proxy.scheme == "http"
 
+
+def test_proxy_https_use(monkeypatch):
+    client = Client("https://foo@sentry.io/123", http_proxy="https://localhost/123")
+    assert client.transport._pool.proxy.scheme == "https"
+
+
+def test_proxy_both_select_http(monkeypatch):
     client = Client(
-        "https://foo@sentry.io/123",
+        "http://foo@sentry.io/123",
         https_proxy="https://localhost/123",
         http_proxy="http://localhost/123",
     )
-    assert client.transport._pool.proxy.scheme == "https"
-
-    client = Client("http://foo@sentry.io/123", http_proxy="http://localhost/123")
     assert client.transport._pool.proxy.scheme == "http"
 
+
+def test_proxy_both_select_https(monkeypatch):
     client = Client(
-        "http://foo@sentry.io/123",
+        "https://foo@sentry.io/123",
         https_proxy="https://localhost/123",
         http_proxy="http://localhost/123",
     )
+    assert client.transport._pool.proxy.scheme == "https"
+
+
+def test_proxy_http_fallback_http(monkeypatch):
+    client = Client("https://foo@sentry.io/123", http_proxy="http://localhost/123")
+    assert client.transport._pool.proxy.scheme == "http"
+
+
+def test_proxy_none_noenv(monkeypatch):
+    client = Client("http://foo@sentry.io/123")
+    assert client.transport._pool.proxy is None
+
+
+def test_proxy_none_httpenv_select(monkeypatch):
+    os.environ["HTTP_PROXY"] = "http://localhost/123"
+    client = Client("http://foo@sentry.io/123")
+    assert client.transport._pool.proxy.scheme == "http"
+
+
+def test_proxy_none_httpsenv_select(monkeypatch):
+    os.environ["HTTPS_PROXY"] = "https://localhost/123"
+    client = Client("https://foo@sentry.io/123")
+    assert client.transport._pool.proxy.scheme == "https"
+
+
+def test_proxy_none_httpenv_fallback(monkeypatch):
+    os.environ["HTTP_PROXY"] = "http://localhost/123"
+    client = Client("https://foo@sentry.io/123")
+    assert client.transport._pool.proxy.scheme == "http"
+
+
+def test_proxy_bothselect_bothen(monkeypatch):
+    os.environ["HTTP_PROXY"] = "http://localhost/123"
+    os.environ["HTTPS_PROXY"] = "https://localhost/123"
+    client = Client("https://foo@sentry.io/123", http_proxy="", https_proxy="")
+    assert client.transport._pool.proxy is None
+
+
+def test_proxy_bothavoid_bothenv(monkeypatch):
+    os.environ["HTTP_PROXY"] = "http://localhost/123"
+    os.environ["HTTPS_PROXY"] = "https://localhost/123"
+    client = Client("https://foo@sentry.io/123", http_proxy=None, https_proxy=None)
+    assert client.transport._pool.proxy.scheme == "https"
+
+
+def test_proxy_bothselect_httpenv(monkeypatch):
+    os.environ["HTTP_PROXY"] = "http://localhost/123"
+    client = Client("https://foo@sentry.io/123", http_proxy=None, https_proxy=None)
+    assert client.transport._pool.proxy.scheme == "http"
+
+
+def test_proxy_httpselect_bothenv(monkeypatch):
+    os.environ["HTTP_PROXY"] = "http://localhost/123"
+    os.environ["HTTPS_PROXY"] = "https://localhost/123"
+    client = Client("https://foo@sentry.io/123", http_proxy=None, https_proxy="")
+    assert client.transport._pool.proxy.scheme == "http"
+
+
+def test_proxy_httpsselect_bothenv(monkeypatch):
+    os.environ["HTTP_PROXY"] = "http://localhost/123"
+    os.environ["HTTPS_PROXY"] = "https://localhost/123"
+    client = Client("https://foo@sentry.io/123", http_proxy="", https_proxy=None)
+    assert client.transport._pool.proxy.scheme == "https"
+
+
+def test_proxy_httpselect_httpsenv(monkeypatch):
+    os.environ["HTTPS_PROXY"] = "https://localhost/123"
+    client = Client("https://foo@sentry.io/123", http_proxy=None, https_proxy="")
+    assert client.transport._pool.proxy is None
+
+
+def test_proxy_httpsselect_bothenv_http(monkeypatch):
+    os.environ["HTTP_PROXY"] = "http://localhost/123"
+    os.environ["HTTPS_PROXY"] = "https://localhost/123"
+    client = Client("http://foo@sentry.io/123", http_proxy=None, https_proxy=None)
     assert client.transport._pool.proxy.scheme == "http"