Repository: jazzband/django-debug-toolbar

Issue:
I can no longer use iptools.IpRangeList for INTERNAL_IPS
Hello!

Long time happy user of DDT here.

With https://github.com/jazzband/django-debug-toolbar/commit/782bdd9b0f24d3d71f6d68a20b12d5c51c6d370e I can no longer use [iptools.IpRangeList](https://python-iptools.readthedocs.io/en/latest/) with `INTERNAL_IPS`, it blows up when I want to whitelist a larger IP range such as IPv6. With smaller ranges such as `10.0.0.0/8` it won't blow up but it will delay every request significantly while DDT waits for `IpRangeList` to write out all the IP addresses.

```python
INTERNAL_IPS = iptools.IpRangeList('127.0.0.1', '10/8', '::1', '1a:1a:1a:1a::/64')
```

```python
Traceback (most recent call last):
  File "/mnt/drive/skeleton/virtual/lib/python3.10/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/mnt/drive/skeleton/virtual/lib/python3.10/site-packages/debug_toolbar/middleware.py", line 64, in __call__
    if not show_toolbar(request) or DebugToolbar.is_toolbar_request(request):
  File "/mnt/drive/skeleton/virtual/lib/python3.10/site-packages/debug_toolbar/middleware.py", line 23, in show_toolbar
    internal_ips = list(settings.INTERNAL_IPS)
OverflowError: cannot fit 'int' into an index-sized integer
```

For now I simplified my configuration to work around it. My requirements have changed a bit over the years and I can live with this simplified setup, but I just wanted to let you know in case this fallout was undesirable.


Discussion:
Thanks for letting us know! Would you be interested in creating a PR to resolve this? 
I'm happy to.

The commit doesn't state _why_ a list was preferable. Looking around I assume the cast in https://github.com/jazzband/django-debug-toolbar/commit/782bdd9b0f24d3d71f6d68a20b12d5c51c6d370e was done because in https://github.com/jazzband/django-debug-toolbar/commit/7d77a34dcd00bcbc1561541877be132bef1789a5 the `.copy()+append()` was added, which didn't work on certain not-a-list objects. 

For the biggest compatibility and efficiency I'd say the `copy()+append()` should not be done either. The code should go back to `request.META.get("REMOTE_ADDR") in settings.INTERNAL_IPS`. An additional clause can be added to deal with the Docker IP check separately.

I'll make a PR later today.
The reason was that the suggested docker solution was to add the docker internal host IP address to [`INTERNAL_IPS`](https://docs.djangoproject.com/en/5.0/ref/settings/#internal-ips) which is defined as a list. @matthiask does the docker host need to be added to `INTERNAL_IPS`? If yes, @quinox you may be headed down the wrong direction.

Resolving patch:
diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py
--- a/debug_toolbar/middleware.py
+++ b/debug_toolbar/middleware.py
@@ -20,8 +20,14 @@ def show_toolbar(request):
     """
     Default function to determine whether to show the toolbar on a given page.
     """
-    internal_ips = list(settings.INTERNAL_IPS)
+    if not settings.DEBUG:
+        return False
 
+    # Test: settings
+    if request.META.get("REMOTE_ADDR") in settings.INTERNAL_IPS:
+        return True
+
+    # Test: Docker
     try:
         # This is a hack for docker installations. It attempts to look
         # up the IP address of the docker host.
@@ -31,11 +37,14 @@ def show_toolbar(request):
             ".".join(socket.gethostbyname("host.docker.internal").rsplit(".")[:-1])
             + ".1"
         )
-        internal_ips.append(docker_ip)
+        if request.META.get("REMOTE_ADDR") == docker_ip:
+            return True
     except socket.gaierror:
         # It's fine if the lookup errored since they may not be using docker
         pass
-    return settings.DEBUG and request.META.get("REMOTE_ADDR") in internal_ips
+
+    # No test passed
+    return False
 
 
 @lru_cache(maxsize=None)


Test patch:
diff --git a/tests/test_integration.py b/tests/test_integration.py
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -75,6 +75,28 @@ def test_show_toolbar_docker(self, mocked_gethostbyname):
             self.assertTrue(show_toolbar(self.request))
         mocked_gethostbyname.assert_called_once_with("host.docker.internal")
 
+    def test_not_iterating_over_INTERNAL_IPS(self):
+        """Verify that the middleware does not iterate over INTERNAL_IPS in some way.
+
+        Some people use iptools.IpRangeList for their INTERNAL_IPS. This is a class
+        that can quickly answer the question if the setting contain a certain IP address,
+        but iterating over this object will drain all performance / blow up.
+        """
+
+        class FailOnIteration:
+            def __iter__(self):
+                raise RuntimeError(
+                    "The testcase failed: the code should not have iterated over INTERNAL_IPS"
+                )
+
+            def __contains__(self, x):
+                return True
+
+        with self.settings(INTERNAL_IPS=FailOnIteration()):
+            response = self.client.get("/regular/basic/")
+            self.assertEqual(response.status_code, 200)
+            self.assertContains(response, "djDebug")  # toolbar
+
     def test_should_render_panels_RENDER_PANELS(self):
         """
         The toolbar should force rendering panels on each request