Repository: streamlit/streamlit

Issue:
st.file_uploader returns 400 error for files above 100MB
# Summary

When uploading a video file larger than 100MB or so, the `st.file_uploader` widget throws a 400 error. This is unrelated to using `server.maxUploadSize`.

# Steps to reproduce

What are the steps we should take to reproduce the bug:

```python
import streamlit as st

uploaded_files = st.file_uploader('Choose your video', type="MP4")

if uploaded_files is not None:
    video_bytes = uploaded_files.read()
    st.video(video_bytes)
```

When uploading a file of 150MB, a 400 error is thrown:

![image](https://user-images.githubusercontent.com/2762787/83545199-e033db80-a4cc-11ea-8152-e36a6b198d02.png)

## Expected behavior:

I would expect this to work up to the limit of `server.maxUploadSize` or other reasonable, documented max size (if browser limitations are present)

## Actual behavior:

This behavior is unrelated to using `server.maxUploadSize`, as shown by this test matrix:

**Streamlit 0.60, no arguments passed on `streamlit run file.py`**
50MB: ok
100MB: ok
150MB: "Error: Request failed with status code 400"
250MB: "Max allowed in 200MB"

**Streamlit 0.60, ` streamlit run test.py --server.maxUploadSize=500`**

250MB:  "Error: Request failed with status code 400"
750MB: "Max allowed in 500MB"

## Is this a regression?

That is, did this use to work the way you expected in the past?
Yes

# Debug info

- Streamlit version: (get it with `$ streamlit version`)  0.60
- Python version: (get it with `$ python --version`)  3.83
- Using Conda? PipEnv? PyEnv? Pex?  conda
- OS version:  Ubuntu 18.04 LTS
- Browser version: Version 83.0.4103.61 (Official Build) (64-bit)

# Additional information

https://discuss.streamlit.io/t/network-error-when-uploading-files-larger-than-100mb/2646/

Streamlit folks, I can provide the files I tested with, send me a Slack message



Discussion:

Resolving patch:
diff --git a/lib/streamlit/server/Server.py b/lib/streamlit/server/Server.py
--- a/lib/streamlit/server/Server.py
+++ b/lib/streamlit/server/Server.py
@@ -121,13 +121,17 @@ def start_listening(app):
     """
 
     call_count = 0
+    http_server = tornado.httpserver.HTTPServer(
+        app,
+        max_buffer_size=config.get_option("server.maxUploadSize") * 1024 * 1024
+    )
 
     while call_count < MAX_PORT_SEARCH_RETRIES:
         address = config.get_option("server.address")
         port = config.get_option("server.port")
 
         try:
-            app.listen(port, address)
+            http_server.listen(port, address)
             break  # It worked! So let's break out of the loop.
 
         except (OSError, socket.error) as e:


Test patch:
diff --git a/lib/tests/streamlit/Server_test.py b/lib/tests/streamlit/Server_test.py
--- a/lib/tests/streamlit/Server_test.py
+++ b/lib/tests/streamlit/Server_test.py
@@ -450,22 +450,26 @@ def test_healthz(self):
 class PortRotateAHundredTest(unittest.TestCase):
     """Tests port rotation handles a MAX_PORT_SEARCH_RETRIES attempts then sys exits"""
 
-    def get_app(self):
-        app = mock.MagicMock()
+    def get_httpserver(self):
+        httpserver = mock.MagicMock()
 
-        app.listen = mock.Mock()
-        app.listen.side_effect = OSError(errno.EADDRINUSE, "test", "asd")
+        httpserver.listen = mock.Mock()
+        httpserver.listen.side_effect = OSError(errno.EADDRINUSE, "test", "asd")
 
-        return app
+        return httpserver
 
     def test_rotates_a_hundred_ports(self):
-        app = self.get_app()
+        app = mock.MagicMock()
+
         RetriesExceeded = streamlit.server.Server.RetriesExceeded
         with pytest.raises(RetriesExceeded) as pytest_wrapped_e:
-            start_listening(app)
-            self.assertEqual(pytest_wrapped_e.type, SystemExit)
-            self.assertEqual(pytest_wrapped_e.value.code, errno.EADDRINUSE)
-            self.assertEqual(app.listen.call_count, MAX_PORT_SEARCH_RETRIES)
+            with patch.object(
+                tornado.httpserver, "HTTPServer", return_value=self.get_httpserver()
+            ) as mock_server:
+                start_listening(app)
+                self.assertEqual(pytest_wrapped_e.type, SystemExit)
+                self.assertEqual(pytest_wrapped_e.value.code, errno.EADDRINUSE)
+                self.assertEqual(mock_server.listen.call_count, MAX_PORT_SEARCH_RETRIES)
 
 
 class PortRotateOneTest(unittest.TestCase):
@@ -473,30 +477,33 @@ class PortRotateOneTest(unittest.TestCase):
 
     which_port = mock.Mock()
 
-    def get_app(self):
-        app = mock.MagicMock()
+    def get_httpserver(self):
+        httpserver = mock.MagicMock()
 
-        app.listen = mock.Mock()
-        app.listen.side_effect = OSError(errno.EADDRINUSE, "test", "asd")
+        httpserver.listen = mock.Mock()
+        httpserver.listen.side_effect = OSError(errno.EADDRINUSE, "test", "asd")
 
-        return app
+        return httpserver
 
     @mock.patch("streamlit.server.Server.config._set_option")
     @mock.patch("streamlit.server.Server.server_port_is_manually_set")
     def test_rotates_one_port(
         self, patched_server_port_is_manually_set, patched__set_option
     ):
-        app = self.get_app()
+        app = mock.MagicMock()
 
         patched_server_port_is_manually_set.return_value = False
         with pytest.raises(RetriesExceeded) as pytest_wrapped_e:
-            start_listening(app)
+            with patch.object(
+                tornado.httpserver, "HTTPServer", return_value=self.get_httpserver()
+            ) as mock_server:
+                start_listening(app)
 
-            PortRotateOneTest.which_port.assert_called_with(8502)
+                PortRotateOneTest.which_port.assert_called_with(8502)
 
-            patched__set_option.assert_called_with(
-                "server.port", 8501, config.ConfigOption.STREAMLIT_DEFINITION
-            )
+                patched__set_option.assert_called_with(
+                    "server.port", 8501, config.ConfigOption.STREAMLIT_DEFINITION
+                )
 
 
 class MetricsHandlerTest(tornado.testing.AsyncHTTPTestCase):