Repository: pallets/werkzeug

Issue:
cannot read data due to `max_content_length`
<!--
This issue tracker is a tool to address bugs in Werkzeug itself. Please
use Pallets Discord or Stack Overflow for questions about your own code.

Replace this comment with a clear outline of what the bug is.
-->
I have a Flask api that sets MAX_CONTENT_LENGTH to 2MB. With Werkzeug 2.3.0/2.3.1 this causes the reading of `request.data` (or `request.get_data()` to freeze. This happens in my Windows development environment using `flask run`. Not when running in Gunicorn under Linux.

This might be a result of https://github.com/pallets/werkzeug/commit/4f7048e7a31752142f18eefeccd49acc42a89e31

<!--
Describe how to replicate the bug.

Include a minimal reproducible example that demonstrates the bug.
Include the full traceback if there was an exception.
-->
## To replicate

Install Flask:

```
pip install flask
```

For me this installs:

* blinker==1.6.2
* click==8.1.3
* colorama==0.4.6
* Flask==2.3.1
* importlib-metadata==6.6.0
* itsdangerous==2.1.2
* Jinja2==3.1.2
* MarkupSafe==2.1.2
* Werkzeug==2.3.1
* zipp==3.15.0

Create a simple Flask-app:
```
from flask import Flask, jsonify, request

app = Flask("server", static_url_path="")
app.config["MAX_CONTENT_LENGTH"] = 2 * 1000 * 1000


@app.get("/api/test")
def get_roles():
    r = request
    print(r.get_data())
    return jsonify(True)
```

Run it:

```
 flask run
```

Then browse to http://127.0.0.1:5000/api/test. This will not result in a response. 

<!--
Describe the expected behavior that should have happened but didn't.
-->
It should response with "true" as a result. 

If  `app.config["MAX_CONTENT_LENGTH"] = 2 * 1000 * 1000` is removed. It all works as expected. 

## Environment

- OS: Windows 10/11 (Verified on 2 different systems)
- Python version:  Python 3.8 / 3.9
- Werkzeug version: 2.3.0/2.3.1



Discussion:
Why are you reading `request.data` in a GET request? That's incorrect.
> Why are you reading `request.data` in a GET request? That's incorrect.

Even the `request.get_data()` fails
Same question, why are you reading data in a GET request? That's incorrect.

Also, please stop editing the title.
> Same question, why are you reading data in a GET request? That's incorrect.
> 
> Also, please stop editing the title.

Sorry about the edits. 

I myself am not reading the data for gets, but some other components (such as Authlib) do. And thus it causes the same issue. I merely constructed a MVCE for it.
> I myself am not reading the data for gets, 

_You_ opened the issue _here_, it's your responsibility to justify it as written. If you think it's an issue with other libraries, report it there instead.
You are (or a library is) asking for data when none was sent. There's no way to distinguish between "no data, no content length" and "streaming data, no content length". If `max_content_length` is set, it means "read up to this limit if there is no content length". The browser isn't sending anything, so the read keeps waiting until it times out.
I read the `max_content_length` as "if reading data, don't read more than the configured value". This used to work just fine, but with 2.3.0 its behaviour changed.
And it's not reading more than the configured length. It's not reading anything, because nothing is sent. The problem is the ambiguity of requesting data when there is no data, compared to requesting streaming data. It's not possible to tell the two cases apart.
I am just pointing out the change in behaviour between the two versions, but also between the different hosting/running options. Just to help out.

I already replace the component that was the `get_data()` on every request and cause my blockage. 

Resolving patch:
diff --git a/src/werkzeug/wsgi.py b/src/werkzeug/wsgi.py
--- a/src/werkzeug/wsgi.py
+++ b/src/werkzeug/wsgi.py
@@ -147,9 +147,9 @@ def get_input_stream(
     server handles terminating the stream, so it is safe to read directly. For example,
     a server that knows how to handle chunked requests safely would set this.
 
-    If ``max_content_length`` is set, that limit is used even if ``Content-Length`` or
-    ``wsgi.input_terminated`` are set. If none of these are set, then an empty stream is
-    returned unless the user explicitly disables this safe fallback.
+    If ``max_content_length`` is set, it can be enforced on streams if
+    ``wsgi.input_terminated`` is set. Otherwise, an empty stream is returned unless the
+    user explicitly disables this safe fallback.
 
     If the limit is reached before the underlying stream is exhausted (such as a file
     that is too large, or an infinite stream), the remaining contents of the stream
@@ -173,14 +173,18 @@ def get_input_stream(
     if content_length is not None and max_content_length is not None:
         if content_length > max_content_length:
             raise RequestEntityTooLarge()
-    elif max_content_length is not None:
-        return t.cast(
-            t.IO[bytes], LimitedStream(stream, max_content_length, is_max=True)
-        )
 
     # A WSGI server can set this to indicate that it terminates the input stream. In
-    # that case the stream is safe without wrapping.
+    # that case the stream is safe without wrapping, or can enforce a max length.
     if "wsgi.input_terminated" in environ:
+        if max_content_length is not None:
+            # If this is moved above, it can cause the stream to hang if a read attempt
+            # is made when the client sends no data. For example, the development server
+            # does not handle buffering except for chunked encoding.
+            return t.cast(
+                t.IO[bytes], LimitedStream(stream, max_content_length, is_max=True)
+            )
+
         return stream
 
     # No limit given, return an empty stream unless the user explicitly allows the


Test patch: