add more documentation, work in progress

Commit 9aed376 · patx · 2025-08-15T01:54:08-04:00

Changeset
9aed376216aac608e97509b8108a5a3301406462
Parents
b2ce11c1806d95a01561e53d556fc8d551fe959d

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/docs/docs/explanation/architecture.rst b/docs/docs/explanation/architecture.rst
new file mode 100644
index 0000000..a55c720
--- /dev/null
+++ b/docs/docs/explanation/architecture.rst
@@ -0,0 +1,95 @@
+Architecture and design
+=======================
+
+MicroPie is intentionally small.  Its goal is to provide just enough
+functionality to build asynchronous web applications without locking
+you into a rigid structure.  This section discusses some of the key
+design choices and how they influence the API.
+
+ASGI application
+----------------
+
+MicroPie is built on top of the `ASGI`_ specification, which defines
+how Python applications communicate with asynchronous servers.  Your
+application subclass inherits from :class:`~micropie.App` and
+implements the ASGI call interface via the ``__call__`` method.  The
+framework inspects the ``scope['type']`` field to dispatch to HTTP,
+WebSocket or lifespan handling code.
+
+Routing by naming
+-----------------
+
+Unlike frameworks that require you to declare routes in advance,
+MicroPie derives routing information from the names of your methods.
+The first segment of the path becomes the handler name.  This allows
+you to add new endpoints simply by defining a new method on your
+application subclass.  The remaining path segments, query string
+parameters and form data are mapped to arguments via function
+signature inspection.  This approach reduces boilerplate but may
+surprise users expecting explicit routing.  You can always regain
+control by writing a custom middleware to map paths to handlers or by
+checking ``self.request.method`` and ``self.request.path_params`` inside
+your handlers.
+
+Context‑local request
+---------------------
+
+MicroPie stores the current request in a :mod:`contextvars`
+ContextVar.  This allows you to access ``self.request`` within your
+handler as well as from deeper helper functions without passing the
+request around explicitly.  The context variable is reset at the end
+of each request to avoid leaking state.
+
+Session management
+------------------
+
+Sessions are stored in a pluggable back‑end and identified by a
+random ``session_id`` cookie.  The default in‑memory back‑end keeps
+session data in dictionaries keyed by session ID and updates a last
+access timestamp to implement expiration.  You can customise the
+back‑end by providing your own implementation of
+:class:`~micropie.SessionBackend`.  MicroPie saves sessions after a
+handler returns, only if ``self.request.session`` is non‑empty, to
+avoid creating unnecessary cookies.
+
+Middleware pipeline
+-------------------
+
+The middleware hooks allow you to intercept requests before and after
+handlers.  Middleware can modify the request object, provide early
+responses (useful for authentication), and alter the final response.
+For WebSockets, separate middleware hooks run before the connection
+handler starts and after it finishes.
+
+Streaming and SSE support
+-------------------------
+
+Handlers may return an iterator or asynchronous generator to stream
+data to the client.  MicroPie detects such responses and iterates
+over them, sending each chunk to the client.  When using server‑sent
+events (SSE) there is an additional challenge: if the client
+disconnects, the iterator must be cancelled.  MicroPie wraps SSE
+responses in a small loop that listens for disconnect events and
+cancels the generator accordingly.  Remember to include a
+``Content‑Type: text/event-stream`` header when sending SSE.
+
+Error handling
+--------------
+
+MicroPie automatically handles common error cases.  Requests for
+unknown routes result in a ``404 Not Found``.  If a required
+parameter is missing, MicroPie responds with ``400 Bad Request``.
+Unhandled exceptions inside handlers produce a ``500 Internal Server
+Error`` and are printed to standard error.  You can override these
+behaviours via middleware.
+
+Extensibility
+-------------
+
+The minimalist core is designed to be extended.  You can mount your
+application behind other ASGI middleware, integrate additional
+protocols like Socket.IO, or implement your own session storage.  The
+framework imposes few constraints so that you remain in control of
+your stack.
+
+.. _ASGI: https://asgi.readthedocs.io/
\ No newline at end of file
diff --git a/docs/docs/explanation/index.rst b/docs/docs/explanation/index.rst
new file mode 100644
index 0000000..0d3ac07
--- /dev/null
+++ b/docs/docs/explanation/index.rst
@@ -0,0 +1,13 @@
+Explanations
+============
+
+The explanation section explores concepts and design decisions behind
+MicroPie.  It answers “why” questions and provides deeper context.
+For hands‑on instructions see the tutorials and how‑to guides; for
+precise definitions consult the reference.
+
+.. toctree::
+   :maxdepth: 1
+   :caption: Explanations
+
+   architecture
\ No newline at end of file
diff --git a/docs/docs/glossary.rst b/docs/docs/glossary.rst
new file mode 100644
index 0000000..c02a776
--- /dev/null
+++ b/docs/docs/glossary.rst
@@ -0,0 +1,44 @@
+Glossary
+========
+
+This glossary defines terms used throughout the MicroPie documentation.
+
+.. glossary::
+
+   ASGI
+     The Asynchronous Server Gateway Interface.  A standard for
+     asynchronous communication between Python applications and
+     servers.  MicroPie applications implement the ASGI call
+     interface.  See the `ASGI specification <https://asgi.readthedocs.io>`_.
+
+   route handler
+     A method on your :class:`~micropie.App` subclass that handles an
+     incoming HTTP request.  The first segment of the URL path maps to
+     the handler name, and remaining segments or query parameters map
+     to arguments.  Handlers may be synchronous or asynchronous.
+
+   session
+     A dictionary of data associated with a client across multiple
+     requests.  MicroPie stores the session in a pluggable back‑end
+     and identifies it using a ``session_id`` cookie.  See
+     :doc:`howto/sessions`.
+
+   middleware
+     Code that runs before and/or after a request handler.  Middleware
+     can short‑circuit requests, modify responses, enforce security
+     policies or implement custom routing.  MicroPie defines
+     :class:`~micropie.HttpMiddleware` and
+     :class:`~micropie.WebSocketMiddleware` base classes.
+
+   WebSocket
+     A persistent, bidirectional communication channel over TCP.  In
+     MicroPie, WebSocket handlers are methods prefixed with ``ws_``.
+     They receive an instance of :class:`~micropie.WebSocket` and
+     optionally additional parameters.  See :doc:`tutorial/websockets`.
+
+   context variable
+     An object from the :mod:`contextvars` module that stores data
+     local to the current asynchronous context.  MicroPie stores the
+     current request in a context variable so it is accessible via
+     :meth:`~micropie.App.request` without passing it through your
+     function calls.
\ No newline at end of file
diff --git a/docs/docs/howto/index.rst b/docs/docs/howto/index.rst
new file mode 100644
index 0000000..6d3e1a2
--- /dev/null
+++ b/docs/docs/howto/index.rst
@@ -0,0 +1,19 @@
+How‑to guides
+=============
+
+How‑to guides provide short recipes for solving a specific problem with
+MicroPie.  They assume that you already know the basics covered in
+the tutorials.  Each guide focuses on performing a single task.  See
+the :doc:`tutorials <../tutorial/index>` for a more gentle
+introduction.
+
+.. toctree::
+   :maxdepth: 1
+   :caption: Guides
+
+   sessions
+   middleware
+   templates
+   streaming
+   static_files
+   socketio
\ No newline at end of file
diff --git a/docs/docs/howto/middleware.rst b/docs/docs/howto/middleware.rst
new file mode 100644
index 0000000..a815318
--- /dev/null
+++ b/docs/docs/howto/middleware.rst
@@ -0,0 +1,93 @@
+Writing middleware
+==================
+
+Middleware allows you to insert code that runs before and after your
+handlers.  It is useful for cross‑cutting concerns such as logging,
+authentication, rate limiting or explicit routing.  MicroPie defines
+separate middleware classes for HTTP and WebSocket connections.
+
+HTTP middleware
+---------------
+
+To create HTTP middleware, subclass :class:`~micropie.HttpMiddleware`
+and implement two asynchronous methods:
+
+* :meth:`~micropie.HttpMiddleware.before_request` – called before the
+  handler is executed.  If this method returns a dictionary with
+  ``status_code`` and ``body`` keys, MicroPie immediately sends that
+  response and skips calling the handler.  You can also provide
+  additional headers via a ``headers`` key.
+
+* :meth:`~micropie.HttpMiddleware.after_request` – called after the
+  handler has returned a response but before it is sent to the client.
+  You can modify the status code, body or headers by returning a
+  dictionary with updated values.
+
+Register your middleware by appending an instance to
+:attr:`~micropie.App.middlewares` on your application:
+
+.. code-block:: python
+
+   from micropie import App, HttpMiddleware
+
+   class LoggingMiddleware(HttpMiddleware):
+       async def before_request(self, request):
+           print(f"Incoming request: {request.method} {request.scope['path']}")
+           # returning None continues processing
+
+       async def after_request(self, request, status_code, body, headers):
+           print(f"Response status: {status_code}")
+           # returning None uses the original response
+
+   class MyApp(App):
+       async def index(self):
+           return "Hello, world!"
+
+   app = MyApp()
+   app.middlewares.append(LoggingMiddleware())
+
+WebSocket middleware
+--------------------
+
+WebSocket middleware must subclass :class:`~micropie.WebSocketMiddleware`
+and implement two methods:
+
+* :meth:`~micropie.WebSocketMiddleware.before_websocket` – called
+  before a WebSocket handler runs.  If this method returns a
+  dictionary with ``code`` and ``reason``, MicroPie closes the
+  connection with the given code and reason.
+
+* :meth:`~micropie.WebSocketMiddleware.after_websocket` – called after
+  the WebSocket handler completes.  Use this to perform cleanup.
+
+Example:
+
+.. code-block:: python
+
+   from micropie import App, WebSocketMiddleware
+
+   class RejectAnonymous(WebSocketMiddleware):
+       async def before_websocket(self, request):
+           # Reject connections without a "user" query parameter
+           if "user" not in request.query_params:
+               return {"code": 1008, "reason": "User name required"}
+
+       async def after_websocket(self, request):
+           print("WebSocket closed")
+
+   class MyApp(App):
+       async def ws_chat(self, ws, user):
+           await ws.accept()
+           await ws.send_text(f"Welcome, {user}!")
+           await ws.close()
+
+   app = MyApp()
+   app.ws_middlewares.append(RejectAnonymous())
+
+Explicit routing and other patterns
+----------------------------------
+
+You can implement custom routing schemes by writing middleware that
+parses the incoming path and sets ``request._route_handler`` or
+``request._ws_route_handler`` accordingly.  See the examples in the
+``examples/middleware`` directory for a complete implementation.
\ No newline at end of file
diff --git a/docs/docs/howto/sessions.rst b/docs/docs/howto/sessions.rst
new file mode 100644
index 0000000..b29f6de
--- /dev/null
+++ b/docs/docs/howto/sessions.rst
@@ -0,0 +1,84 @@
+Managing sessions
+=================
+
+MicroPie includes simple session management built on top of cookies.  A
+session stores data associated with a client across multiple requests.
+Sessions are useful for keeping track of login state, counters and
+temporary information.
+
+Enabling sessions
+-----------------
+
+By default, MicroPie uses an in‑memory session back‑end implemented by
+:class:`~micropie.InMemorySessionBackend`.  Each session is identified
+by a ``session_id`` cookie.  When the client makes a request, the
+cookie is read and the session data is loaded from the back‑end.  When
+you modify the session dictionary, MicroPie writes the updated
+session back at the end of the request and issues a ``Set‑Cookie``
+header if needed.
+
+In order to use sessions, simply read and write the
+:attr:`~micropie.Request.session` dictionary in your handler.  The
+following example counts the number of visits for each client:
+
+.. code-block:: python
+
+   from micropie import App
+
+   class MyApp(App):
+       async def index(self):
+           if "visits" not in self.request.session:
+               self.request.session["visits"] = 1
+           else:
+               self.request.session["visits"] += 1
+           return f"You have visited {self.request.session['visits']} times."
+
+   app = MyApp()
+
+Custom session back‑ends
+------------------------
+
+The in‑memory back‑end stores all sessions in a Python dictionary.  It
+is suitable for development but will lose data when the process
+terminates and cannot be shared across worker processes.  To persist
+sessions in a database or cache, implement the abstract
+:class:`~micropie.SessionBackend` interface:
+
+.. code-block:: python
+
+   from micropie import SessionBackend
+
+   class DatabaseSessionBackend(SessionBackend):
+       async def load(self, session_id: str) -> dict:
+           # fetch the session from your database or cache
+           data = await get_session_from_db(session_id)
+           return data or {}
+
+       async def save(self, session_id: str, data: dict, timeout: int) -> None:
+           # store the session with an expiration timeout
+           await save_session_to_db(session_id, data, timeout)
+
+Assign your back‑end when constructing your application:
+
+.. code-block:: python
+
+   backend = DatabaseSessionBackend()
+   app = MyApp(session_backend=backend)
+
+Expiring sessions
+-----------------
+
+The global constant :data:`micropie.SESSION_TIMEOUT` controls how long
+a session remains valid (in seconds).  The default is eight hours.
+Back‑ends may choose to enforce this timeout in whatever manner makes
+sense for their storage layer.
+
+Security considerations
+-----------------------
+
+Sessions are sent to the client as cookies and should be treated as
+untrusted input.  Avoid storing sensitive information directly in the
+session.  If you implement your own back‑end, ensure that session
+identifiers are random, unique and that the cookie includes the
+``HttpOnly``, ``Secure`` and ``SameSite=Lax`` directives.  The
+in‑memory back‑end provided by MicroPie already issues these flags.
\ No newline at end of file
diff --git a/docs/docs/howto/socketio.rst b/docs/docs/howto/socketio.rst
new file mode 100644
index 0000000..02b5bbe
--- /dev/null
+++ b/docs/docs/howto/socketio.rst
@@ -0,0 +1,54 @@
+Integrating Socket.IO
+=====================
+
+MicroPie’s built‑in WebSocket support provides a simple API for
+bidirectional communication.  If you require advanced features such
+as automatic reconnection, broadcasting to rooms, namespaces, or
+fallback transports for browsers that do not support WebSockets, you
+can integrate `python‑socketio <https://python-socketio.readthedocs.io>`_.
+
+Socket.IO runs as an ASGI application, so you can mount it
+alongside your MicroPie app and serve both under the same Uvicorn
+server.  The following example shows how to set up a basic chat
+application using Socket.IO with MicroPie.
+
+.. code-block:: python
+
+   import socketio
+   from micropie import App
+
+   # Create a Socket.IO server instance (ASGI compatible)
+   sio = socketio.AsyncServer(async_mode="asgi")
+   socket_app = socketio.ASGIApp(sio)
+
+   # Create your MicroPie application
+   class MyApp(App):
+       async def index(self):
+           # Serve an HTML page with Socket.IO client code (omitted)
+           return await self._render_template("chat.html")
+
+   app = MyApp()
+
+   # Uvicorn accepts a single callable; wrap both apps in a simple
+   # dispatcher that routes to Socket.IO on the /socket.io path
+   async def application(scope, receive, send):
+       if scope["path"].startswith("/socket.io"):
+           await socket_app(scope, receive, send)
+       else:
+           await app(scope, receive, send)
+
+Run the dispatcher with Uvicorn:
+
+.. code-block:: console
+
+   $ uvicorn app:application
+
+On the client side, connect to ``/socket.io`` as usual using the
+Socket.IO JavaScript client.  All other paths are handled by your
+MicroPie routes.  See the ``examples/socketio`` directory in the
+MicroPie source distribution for a more complete implementation that
+includes HTML and JavaScript code.
+
+Note that python‑socketio may install its own dependencies.  Consult
+the Socket.IO documentation for configuration options such as
+message queue back‑ends and authentication.
\ No newline at end of file
diff --git a/docs/docs/howto/static_files.rst b/docs/docs/howto/static_files.rst
new file mode 100644
index 0000000..38933cd
--- /dev/null
+++ b/docs/docs/howto/static_files.rst
@@ -0,0 +1,45 @@
+Serving static files
+====================
+
+MicroPie deliberately avoids bundling a static file server.  Serving
+static content such as images, CSS and JavaScript can be delegated to
+a specialised middleware or a separate web server.  This approach
+keeps the core framework small and flexible.
+
+Using a static file middleware
+------------------------------
+
+To serve static files from within your MicroPie application, you can
+use third‑party libraries such as
+`ServeStatic <https://archmonger.github.io/ServeStatic/latest/>`_ or
+Starlette’s `StaticFiles` class.  The example below uses
+ServeStatic to serve files from a ``static`` directory:
+
+.. code-block:: python
+
+   from micropie import App
+   from servestatic import ServeStatic
+
+   class MyApp(App):
+       async def index(self):
+           return "Hello, world!"
+
+   app = MyApp()
+
+   # Mount the static file handler at the /static path
+   app_with_static = ServeStatic(app, directory="static", path_prefix="/static")
+
+When running your application, pass ``app_with_static`` to your ASGI
+server instead of ``app``.  Requests to paths under ``/static`` will
+be served from your ``static`` directory, while all other requests
+fall through to your MicroPie routes.
+
+Using a reverse proxy
+---------------------
+
+Alternatively, you can serve static files via a dedicated HTTP server
+such as Nginx or Caddy.  In production deployments this is often the
+preferred option, as a dedicated server can handle caching and
+compression.  Configure your web server to serve the ``/static``
+prefix directly from the filesystem and forward all other requests to
+your ASGI server running MicroPie.
\ No newline at end of file
diff --git a/docs/docs/howto/streaming.rst b/docs/docs/howto/streaming.rst
new file mode 100644
index 0000000..b231ce3
--- /dev/null
+++ b/docs/docs/howto/streaming.rst
@@ -0,0 +1,66 @@
+Streaming responses
+===================
+
+For long‑running tasks or large responses it is often desirable to
+stream data to the client in chunks rather than return a single
+buffer.  MicroPie supports streaming by allowing your handler to
+return an iterator or asynchronous generator.  Each chunk yielded by
+the generator is sent as part of the response body.  When the
+generator finishes, MicroPie automatically sends an empty chunk to
+signal completion.
+
+Example: number stream
+----------------------
+
+The following handler streams a sequence of numbers to the client one
+per second:
+
+.. code-block:: python
+
+   from micropie import App
+   import asyncio
+
+   class MyApp(App):
+       async def numbers(self):
+           async def generator():
+               for i in range(5):
+                   yield f"Number: {i}\n"
+                   await asyncio.sleep(1)
+           return generator()
+
+   app = MyApp()
+
+Visiting ``/numbers`` will produce output like:
+
+.. code-block:: text
+
+   Number: 0
+   Number: 1
+   Number: 2
+   Number: 3
+   Number: 4
+
+MicroPie automatically sets ``Content‑Type`` to ``text/html`` if you
+do not specify a content type.  You can override or add headers by
+returning a tuple ``(status_code, body, headers)`` and making the
+generator the body.
+
+Handling client disconnects
+---------------------------
+
+If the client disconnects while a generator is running, MicroPie
+cancels the generator and closes the underlying response.  This
+behaviour is important for long‑lived streams such as server‑sent
+events (SSE).  When writing a generator, use ``try``/``finally`` or
+``async with`` to perform cleanup when the stream is cancelled.
+
+Server‑sent events
+------------------
+
+MicroPie treats any iterable response body as a general stream.
+Implementing true server‑sent events requires you to set the
+``Content‑Type`` header to ``text/event-stream`` and send
+properly‑formatted SSE messages (``data: ...\n\n``).  Additionally,
+MicroPie includes a patch that handles early disconnection of SSE
+clients in its HTTP handling code.  For a full example, see the
+``examples/sse`` directory in the MicroPie source distribution.
\ No newline at end of file
diff --git a/docs/docs/howto/templates.rst b/docs/docs/howto/templates.rst
new file mode 100644
index 0000000..57b5176
--- /dev/null
+++ b/docs/docs/howto/templates.rst
@@ -0,0 +1,71 @@
+Rendering templates
+==================
+
+MicroPie can render HTML templates using the Jinja2 template engine.
+Templates allow you to separate presentation from code by placing
+HTML in separate files.  Template rendering is optional; you can use
+MicroPie without Jinja2 installed, but the
+:meth:`~micropie.App._render_template` method will not be available.
+
+Installing Jinja2
+-----------------
+
+If you installed MicroPie with the ``[standard]`` extra, Jinja2 is
+already available.  Otherwise install it with pip:
+
+.. code-block:: console
+
+   $ pip install jinja2
+
+Creating templates
+------------------
+
+MicroPie looks for templates in a directory called ``templates`` in
+your current working directory.  Use normal Jinja2 syntax in your
+templates.  Here is a simple ``templates/index.html`` file:
+
+.. code-block:: html
+
+   <!DOCTYPE html>
+   <html lang="en">
+   <head>
+       <meta charset="UTF-8">
+       <title>{{ title }}</title>
+   </head>
+   <body>
+       <h1>{{ message }}</h1>
+   </body>
+   </html>
+
+Rendering a template
+--------------------
+
+To render a template, call :meth:`~micropie.App._render_template` from
+within an asynchronous handler.  The method returns a string containing
+the rendered HTML.  Because template loading and rendering may block,
+MicroPie runs it in a background thread using ``asyncio.to_thread``.
+
+.. code-block:: python
+
+   from micropie import App
+
+   class MyApp(App):
+       async def index(self):
+           return await self._render_template(
+               "index.html",
+               title="Welcome",
+               message="Hello from MicroPie!",
+           )
+
+   app = MyApp()
+
+When you visit ``/`` in your browser, MicroPie returns the rendered
+HTML with a ``Content‑Type`` of ``text/html; charset=utf‑8``.
+
+Template variables
+------------------
+
+You can pass arbitrary keyword arguments to `_render_template`.  These
+become variables in the Jinja2 template.  For more information on
+creating complex templates, including inheritance and control flow,
+refer to the `Jinja2 documentation <https://jinja.palletsprojects.com>`_.
\ No newline at end of file
diff --git a/docs/docs/index.rst b/docs/docs/index.rst
new file mode 100644
index 0000000..3ab55be
--- /dev/null
+++ b/docs/docs/index.rst
@@ -0,0 +1,56 @@
+.. MicroPie documentation master file, created for the MicroPie ASGI framework.
+
+MicroPie ASGI Web Framework
+===========================
+
+MicroPie is a small, asynchronous web framework built on top of the
+ASGI specification.  It is designed to be simple to learn and easy to
+extend while still providing the features necessary to build modern web
+applications.  MicroPie offers automatic URL routing, pluggable
+session back‑ends, middleware hooks, WebSocket support and optional
+template rendering.  Its focus on minimalism makes it a good choice for
+lightweight services, APIs and educational projects.
+
+This documentation is organized according to the
+Diátaxis documentation framework. That framework separates documentation into
+four distinct types:
+
+* **Tutorials** – step‑by‑step introductions that teach you how to
+  accomplish a task.  Tutorials should avoid deep explanations and help
+  you get started quickly.
+* **How‑to guides** – recipes focused on solving a particular problem.
+  Guides assume you already know the basics and want to apply MicroPie
+  to a concrete task.
+* **Reference** – authoritative descriptions of classes, functions and
+  modules.  These documents are factual and concise.
+* **Explanation** – discussions that explore the rationale behind
+  design choices and deeper concepts in MicroPie.
+
+In addition to these four types, a small glossary collects
+terminology used throughout the framework.  If you are new to
+MicroPie, start with the tutorial.  If you want to accomplish a
+specific task, head to the how‑to guides.  If you need to look up a
+particular method or class, consult the reference manual.  For
+background and design considerations, see the explanation section.
+
+.. _Diátaxis documentation framework: https://diataxis.fr/
+
+Contents
+========
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Documentation
+
+   tutorial/index
+   howto/index
+   reference/index
+   explanation/index
+   glossary
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/docs/reference/app.rst b/docs/docs/reference/app.rst
new file mode 100644
index 0000000..7fbad67
--- /dev/null
+++ b/docs/docs/reference/app.rst
@@ -0,0 +1,78 @@
+App class
+=========
+
+.. _app-class:
+
+The :class:`~micropie.App` class is the core of a MicroPie application.
+It implements the ASGI call interface and dispatches HTTP, WebSocket
+and lifespan events to your handlers.  Subclass :class:`~micropie.App`
+and add public methods to create route handlers.  Instantiate your
+subclass and pass the resulting object to an ASGI server such as
+Uvicorn.
+
+Constructor
+-----------
+
+.. class:: App(session_backend=None)
+
+   Create a new application.  If *session_backend* is provided it must
+   be an instance of :class:`~micropie.SessionBackend`.  When omitted,
+   MicroPie uses an in‑memory back‑end.
+
+Attributes
+----------
+
+.. attribute:: middlewares
+
+   A list of :class:`~micropie.HttpMiddleware` instances.  Middlewares
+   run before and after every HTTP request.  Append a middleware
+   instance to enable it.  See :doc:`../howto/middleware` for examples.
+
+.. attribute:: ws_middlewares
+
+   A list of :class:`~micropie.WebSocketMiddleware` instances used for
+   WebSocket connections.
+
+.. attribute:: startup_handlers
+
+   A list of asynchronous callables that run during the ASGI
+   ``lifespan.startup`` event.  Use this to set up resources such as
+   database connections.  See :doc:`../tutorial/quickstart` for an
+   example.
+
+.. attribute:: shutdown_handlers
+
+   A list of asynchronous callables that run during the ASGI
+   ``lifespan.shutdown`` event.  Use this to clean up resources.
+
+Methods
+-------
+
+.. method:: __call__(scope, receive, send)
+
+   The ASGI entry point.  Dispatches to HTTP, WebSocket or lifespan
+   handlers based on ``scope['type']``.  You normally do not call this
+   directly; the ASGI server calls it for you.
+
+.. method:: request
+
+   Return the current :class:`~micropie.Request` object.  This is
+   stored in a context variable so that it is available throughout
+   asynchronous callbacks.
+
+.. method:: _redirect(location, extra_headers=None)
+
+   Return a tuple ``(302, '', headers)`` representing an HTTP redirect
+   to *location*.  Use this helper in your handlers to redirect the
+   client.
+
+.. method:: _render_template(name, **kwargs)
+
+   Render a Jinja2 template asynchronously.  Returns a string
+   containing the rendered output.  Requires Jinja2 to be installed.
+   See :doc:`../howto/templates` for details.
+
+Additionally, MicroPie defines several private helper methods such as
+``_parse_cookies`` and ``_send_response``.  These are considered
+internal and not part of the public API.  They may change without
+notice.
\ No newline at end of file
diff --git a/docs/docs/reference/exceptions.rst b/docs/docs/reference/exceptions.rst
new file mode 100644
index 0000000..fd1454c
--- /dev/null
+++ b/docs/docs/reference/exceptions.rst
@@ -0,0 +1,18 @@
+Exceptions
+==========
+
+MicroPie defines a small number of exceptions.  The most commonly
+encountered is :class:`~micropie.ConnectionClosed`, used for WebSocket
+disconnections.
+
+ConnectionClosed
+----------------
+
+.. class:: ConnectionClosed
+
+   Raised by :meth:`~micropie.WebSocket.receive_text` and
+   :meth:`~micropie.WebSocket.receive_bytes` when the client closes
+   the WebSocket connection.  Catch this exception to detect
+   disconnection and exit your handler gracefully.  The exception does
+   not carry any attributes; the WebSocket connection has already been
+   closed when it is raised.
\ No newline at end of file
diff --git a/docs/docs/reference/index.rst b/docs/docs/reference/index.rst
new file mode 100644
index 0000000..40095d3
--- /dev/null
+++ b/docs/docs/reference/index.rst
@@ -0,0 +1,18 @@
+API reference
+=============
+
+The reference manual provides detailed descriptions of the classes and
+functions exposed by MicroPie.  These documents are factual and
+succinct【730263613588085†L336-L339】.  For tutorials and recipes, see the
+:doc:`../tutorial/index` and :doc:`../howto/index` sections.
+
+.. toctree::
+   :maxdepth: 1
+   :caption: Reference
+
+   app
+   request
+   websocket
+   middleware
+   session
+   exceptions
\ No newline at end of file
diff --git a/docs/docs/reference/middleware.rst b/docs/docs/reference/middleware.rst
new file mode 100644
index 0000000..4754f9e
--- /dev/null
+++ b/docs/docs/reference/middleware.rst
@@ -0,0 +1,51 @@
+Middleware classes
+==================
+
+MicroPie defines abstract base classes for writing middleware.  Use
+these classes to intercept requests, implement custom routing,
+authentication, logging and other cross‑cutting concerns.  See the
+:doc:`../howto/middleware` guide for practical examples.
+
+HttpMiddleware
+--------------
+
+.. class:: HttpMiddleware
+
+   Base class for HTTP middleware.  Subclasses must implement two
+   asynchronous methods:
+
+   .. method:: before_request(request)
+
+      Called before the HTTP handler runs.  *request* is the
+      :class:`~micropie.Request` object.  Return a dictionary
+      ``{"status_code": status, "body": body, "headers": headers}``
+      to short‑circuit the request and send a response immediately.  To
+      continue processing, return ``None``.
+
+   .. method:: after_request(request, status_code, response_body, extra_headers)
+
+      Called after the handler has completed but before the response
+      is sent.  You may return a dictionary with updated
+      ``status_code``, ``body`` and ``headers``.  Return ``None`` to
+      leave the response unchanged.
+
+WebSocketMiddleware
+-------------------
+
+.. class:: WebSocketMiddleware
+
+   Base class for WebSocket middleware.  Subclasses must implement
+   these methods:
+
+   .. method:: before_websocket(request)
+
+      Called before a WebSocket handler runs.  *request* is a
+      :class:`~micropie.WebSocketRequest` object.  Return a dictionary
+      ``{"code": code, "reason": reason}`` to reject the connection
+      with the given close code and reason.  Return ``None`` to allow
+      the connection.
+
+   .. method:: after_websocket(request)
+
+      Called after the WebSocket handler completes.  Use this to
+      release resources or log activity.  Return value is ignored.
\ No newline at end of file
diff --git a/docs/docs/reference/request.rst b/docs/docs/reference/request.rst
new file mode 100644
index 0000000..332b8e7
--- /dev/null
+++ b/docs/docs/reference/request.rst
@@ -0,0 +1,80 @@
+Request objects
+===============
+
+.. _request-object:
+
+MicroPie provides two request classes: :class:`~micropie.Request` for
+HTTP requests and :class:`~micropie.WebSocketRequest` for WebSocket
+connections.  You access the current request via
+:meth:`~micropie.App.request` on your application instance or by using
+the context variable in lower‑level code.
+
+Request class
+-------------
+
+.. class:: Request(scope)
+
+   Represents an incoming HTTP request.  The *scope* argument is the
+   ASGI scope dictionary provided by the server.  You should not
+   instantiate this class yourself.  MicroPie creates one per
+   request and stores it in a context variable.
+
+   .. attribute:: scope
+
+      The original ASGI scope for the request.
+
+   .. attribute:: method
+
+      The HTTP method, such as ``GET`` or ``POST``.
+
+   .. attribute:: path_params
+
+      A list of positional path parameters.  See
+      :doc:`../tutorial/routing` for details on parameter mapping.
+
+   .. attribute:: query_params
+
+      A ``dict`` mapping each query parameter to a list of values.
+      For convenience, use ``self.request.query_params['name'][0]`` to
+      obtain the first value.
+
+   .. attribute:: body_params
+
+      A ``dict`` mapping form field names to lists of values.  For
+      JSON requests, body parameters are derived from the top‑level
+      object; for ``application/x-www-form-urlencoded`` forms they are
+      parsed using :func:`urllib.parse.parse_qs`.
+
+   .. attribute:: get_json
+
+      The JSON body parsed into a Python object.  Only populated when
+      the request contains valid JSON with content type
+      ``application/json``.
+
+   .. attribute:: session
+
+      A ``dict`` for storing per‑client data across requests.  See
+      :doc:`../howto/sessions`.
+
+   .. attribute:: files
+
+      A ``dict`` of uploaded files for multipart/form‑data requests.
+      Each entry is a mapping containing keys ``filename``,
+      ``content_type`` and ``content``, where ``content`` is an
+      ``asyncio.Queue`` yielding file chunks as bytes.  See
+      :doc:`../tutorial/routing` for information on awaiting file fields.
+
+   .. attribute:: headers
+
+      A case‑insensitive mapping of header names to values, decoded as
+      UTF‑8 with invalid bytes replaced.  Header names are lowercased.
+
+WebSocketRequest class
+----------------------
+
+.. class:: WebSocketRequest(scope)
+
+   Inherits from :class:`~micropie.Request` and represents a WebSocket
+   connection request.  All attributes of :class:`~micropie.Request`
+   apply.  For WebSocket handlers the request is accessible via
+   ``self.request`` inside the handler or via the context variable.
\ No newline at end of file
diff --git a/docs/docs/reference/session.rst b/docs/docs/reference/session.rst
new file mode 100644
index 0000000..1804ae2
--- /dev/null
+++ b/docs/docs/reference/session.rst
@@ -0,0 +1,63 @@
+Session back‑ends
+=================
+
+MicroPie abstracts session storage behind the :class:`~micropie.SessionBackend`
+interface.  Different back‑ends may store session data in memory,
+databases, caches or external services.  A session is a dictionary of
+key/value pairs associated with a ``session_id`` cookie.
+
+SessionBackend
+--------------
+
+.. class:: SessionBackend
+
+   Abstract base class for session storage.  Implementations must
+   provide the following asynchronous methods:
+
+   .. method:: load(session_id)
+
+      Load session data for the given *session_id*.  Return a
+      dictionary of session data or an empty dictionary if the session
+      does not exist or has expired.
+
+   .. method:: save(session_id, data, timeout)
+
+      Persist the *data* dictionary for the given *session_id* with
+      an expiry time of *timeout* seconds.
+
+InMemorySessionBackend
+----------------------
+
+.. class:: InMemorySessionBackend
+
+   Concrete implementation of :class:`SessionBackend` that stores
+   sessions in memory.  This back‑end is appropriate for development
+   and testing but does not persist data across process restarts and
+   cannot be shared among worker processes.
+
+   .. method:: __init__()
+
+      Create an empty in‑memory session store.
+
+   .. method:: load(session_id)
+
+      Return the session dictionary if it exists and has not expired
+      according to :data:`micropie.SESSION_TIMEOUT`, otherwise return
+      an empty dictionary.
+
+   .. method:: save(session_id, data, timeout)
+
+      Store *data* under *session_id* and update the last access time.
+
+SESSION_TIMEOUT
+---------------
+
+.. data:: SESSION_TIMEOUT
+
+   The default session expiration time in seconds (eight hours).  You
+   can override this constant in your own code before importing
+   MicroPie or by passing a different timeout when saving sessions in
+   a custom back‑end.
+
+See also the :doc:`../howto/sessions` guide for examples of using and
+implementing session back‑ends.
\ No newline at end of file
diff --git a/docs/docs/reference/websocket.rst b/docs/docs/reference/websocket.rst
new file mode 100644
index 0000000..78b84a2
--- /dev/null
+++ b/docs/docs/reference/websocket.rst
@@ -0,0 +1,74 @@
+WebSocket class
+===============
+
+.. _websocket-reference:
+
+The :class:`~micropie.WebSocket` class encapsulates a WebSocket
+connection.  MicroPie constructs an instance for each WebSocket
+request and passes it as the first argument to your WebSocket handler.
+
+Constructor
+-----------
+
+.. class:: WebSocket(receive, send)
+
+   Create a new WebSocket wrapper around the ASGI ``receive`` and
+   ``send`` callables.  You do not instantiate this class yourself;
+   MicroPie does so internally.
+
+Methods
+-------
+
+.. method:: accept(subprotocol=None, session_id=None)
+
+   Accept the WebSocket connection.  You must call this method before
+   sending or receiving messages.  If you provide a *session_id*,
+   MicroPie sets a ``session_id`` cookie during the handshake.  The
+   optional *subprotocol* argument specifies a negotiated subprotocol.
+
+.. method:: receive_text()
+
+   Await a text message from the client.  Returns a string.  Raises
+   :class:`~micropie.ConnectionClosed` if the client has disconnected or
+   ``ValueError`` if a binary message is received.
+
+.. method:: receive_bytes()
+
+   Await a binary message from the client.  Returns bytes.  Raises
+   :class:`~micropie.ConnectionClosed` if the client has disconnected or
+   ``ValueError`` if a text message is received.
+
+.. method:: send_text(data)
+
+   Send a text message to the client.  Raises ``RuntimeError`` if you
+   have not called :meth:`accept`.
+
+.. method:: send_bytes(data)
+
+   Send a binary message to the client.  Raises ``RuntimeError`` if
+   the connection has not been accepted.
+
+.. method:: close(code=1000, reason=None)
+
+   Close the WebSocket connection.  By default uses code 1000
+   (normal closure).  The optional *reason* is sent to the client.
+
+Attributes
+----------
+
+.. attribute:: accepted
+
+   ``True`` if the WebSocket has been accepted.
+
+.. attribute:: session_id
+
+   The session ID set during the handshake, or ``None`` if not set.
+
+Exceptions
+----------
+
+.. class:: ConnectionClosed
+
+   Raised by :meth:`receive_text` and :meth:`receive_bytes` when the
+   client has closed the connection.  Catch this exception in your
+   handlers to perform cleanup after a disconnect.
\ No newline at end of file
diff --git a/docs/docs/tutorial/index.rst b/docs/docs/tutorial/index.rst
new file mode 100644
index 0000000..93f2c06
--- /dev/null
+++ b/docs/docs/tutorial/index.rst
@@ -0,0 +1,16 @@
+Tutorials
+=========
+
+The tutorials walk you through the basics of writing a MicroPie
+application.  They are intended for readers who have never used
+MicroPie before.  Each tutorial builds upon the previous one and
+demonstrates a specific aspect of the framework.  Tutorials should
+present clear, logical steps and avoid long theoretical discussions.
+
+.. toctree::
+   :maxdepth: 1
+   :caption: Tutorials
+
+   quickstart
+   routing
+   websockets
diff --git a/docs/docs/tutorial/quickstart.rst b/docs/docs/tutorial/quickstart.rst
new file mode 100644
index 0000000..9531525
--- /dev/null
+++ b/docs/docs/tutorial/quickstart.rst
@@ -0,0 +1,80 @@
+Quick start
+===========
+
+This tutorial shows you how to install MicroPie and run a very simple
+application.  By the end you will be able to serve an HTTP
+endpoint that returns plain text.  This section assumes that you
+already have Python 3.8 or later installed.
+
+Installation
+------------
+
+MicroPie is distributed on the Python Package Index (PyPI).  The
+recommended way to install it is via ``pip`` with the standard
+optional dependencies:
+
+.. code-block:: console
+
+   $ pip install micropie[standard]
+
+This command installs MicroPie along with the Jinja2 template engine
+and the ``multipart`` parser for handling file uploads.  If you want
+the fastest JSON parsing and a preconfigured ASGI server, you can
+install all optional extras:
+
+.. code-block:: console
+
+   $ pip install micropie[all]
+
+See :ref:`reference/session` for details about session back‑ends and
+the :ref:`howto/templates` guide for information on templates.
+
+Writing your first application
+------------------------------
+
+Create a file called ``app.py`` with the following contents:
+
+.. code-block:: python
+
+   from micropie import App
+
+   class MyApp(App):
+       async def index(self):
+           return "Welcome to MicroPie ASGI."
+
+   app = MyApp()
+
+MicroPie applications are defined by subclassing :class:`~micropie.App`.
+Each public method of your subclass becomes a potential route handler
+automatically mapped to an HTTP path.  The special method ``index``
+handles requests to the root path ``/``.  Handlers may be asynchronous
+(``async def``) or synchronous functions and can return strings,
+bytes, JSON‑serialisable objects, tuples of status and body, or an
+async generator for streaming responses.
+
+Running the application
+-----------------------
+
+MicroPie itself is not a web server; it is an ASGI application.  To
+run it you will need an ASGI server such as Uvicorn, Hypercorn or
+Daphne.  Install Uvicorn if you have not already:
+
+.. code-block:: console
+
+   $ pip install uvicorn
+
+Start your application with Uvicorn:
+
+.. code-block:: console
+
+   $ uvicorn app:app
+
+By default Uvicorn listens on ``http://127.0.0.1:8000``.  Open that URL
+in your browser and you should see the text “Welcome to MicroPie ASGI.”
+
+What’s next?
+------------
+
+Now that you have a running MicroPie application, proceed to the
+:doc:`routing <routing>` tutorial to learn how URL paths map to
+methods and how to pass parameters to your handlers.
\ No newline at end of file
diff --git a/docs/docs/tutorial/routing.rst b/docs/docs/tutorial/routing.rst
new file mode 100644
index 0000000..d4a3be5
--- /dev/null
+++ b/docs/docs/tutorial/routing.rst
@@ -0,0 +1,126 @@
+Routing and handlers
+====================
+
+MicroPie maps incoming HTTP requests to methods on your :class:`~micropie.App`
+subclass.  This section explains how the mapping works and how your
+handlers receive input from the URL path, query strings, form data and
+sessions.
+
+URL to function mapping
+-----------------------
+
+When an HTTP request arrives, MicroPie extracts the path portion of the
+URL and uses the first path segment to determine which method should
+handle the request.  For example, a GET request to ``/greet`` calls
+``greet`` on your :class:`~micropie.App` subclass.  A request to the
+root URL ``/`` calls ``index``.  Only methods that do not start with
+an underscore are exposed as route handlers.  Prefacing a method name
+with an underscore makes it private and hides it from external
+requests.
+
+Handlers may be synchronous or asynchronous functions.  They return
+either a string, bytes, a JSON‑serialisable object, a tuple of
+``(status_code, body)`` or ``(status_code, body, headers)``, or an
+iterator/generator for streaming responses.  See
+:doc:`howto/streaming` for details on streaming.
+
+Parameters and arguments
+------------------------
+
+MicroPie automatically populates handler arguments from several
+sources in the following order:
+
+1. **Path parameters** – Additional path segments after the first map to
+   positional parameters.  For example:
+
+   .. code-block:: python
+
+      class AppExample(App):
+          async def greet(self, name):
+              return f"Hello, {name}!"
+
+   Visiting ``/greet/Alice`` passes ``"Alice"`` as the ``name`` argument.
+   You can also use ``*args`` to capture an arbitrary number of path
+   segments.
+
+2. **Query parameters** – Named parameters in the query string fill
+   keyword arguments when path parameters are exhausted.  For example,
+   ``/greet?name=Alice`` also passes ``"Alice"`` to the ``name``
+   parameter.
+
+3. **Body/form data** – For POST, PUT and PATCH requests, form fields
+   populate handler arguments.  JSON bodies are decoded into a
+   dictionary of key/value pairs.
+
+4. **Files** – If the request is multipart/form‑data, uploaded files
+   appear in :attr:`~micropie.Request.files`.  You can declare a file
+   argument in your handler signature to receive a file object.
+
+5. **Session data** – Values stored in :attr:`~micropie.Request.session`
+   fill remaining parameters.  See :doc:`howto/sessions` for details.
+
+6. **Default values** – If no other source provides a value, default
+   values in your function signature are used.
+
+If MicroPie cannot determine a value for a required parameter, it
+returns a ``400 Bad Request``.
+
+Examples
+--------
+
+The following examples illustrate common patterns:
+
+.. code-block:: python
+
+   class MyApp(App):
+       async def greet(self, name="Guest"):
+           """Return a greeting for a named visitor.
+
+           If the ``name`` argument is not provided via the path or
+           query parameters, ``"Guest"`` is used as a default.
+           """
+           return f"Hello, {name}!"
+
+       async def add(self, x, y):
+           """Add two numbers provided via path segments.
+
+           Example: ``/add/2/3`` returns ``5``.
+           """
+           return str(int(x) + int(y))
+
+       async def submit(self, username="Anonymous"):
+           """Handle a form submission.
+
+           This handler accepts POST requests and uses the
+           ``username`` field from the request body.
+           """
+           return f"Form submitted by: {username}"
+
+See :ref:`request-object` for the attributes available on the current
+request and :ref:`app-class` for details about the ``App`` class.
+
+HTTP methods and responses
+--------------------------
+
+Handlers run for any HTTP method unless you explicitly check
+``self.request.method`` inside the handler.  It is your
+responsibility to dispatch based on the method if your endpoint should
+behave differently for GET and POST.  Handlers may return:
+
+* A string or bytes – sent as the body of the response with a
+  ``200 OK`` status.
+* A JSON‑serialisable object – automatically encoded into JSON and
+  returned with a ``Content‑Type`` of ``application/json``.
+* A tuple ``(status_code, body)`` – sets the HTTP status and body.
+* A tuple ``(status_code, body, headers)`` – sets status, body and
+  additional headers.  Headers should be a list of ``(name, value)`` pairs.
+* A generator or async generator – streams chunks to the client.  Use
+  this for large responses or server‑sent events.
+
+Advanced routing
+----------------
+
+You can implement explicit routing, path prefixing or complex
+dispatching by writing a custom :class:`~micropie.HttpMiddleware`.  See
+the :doc:`howto/middleware` guide and the examples in the
+``examples/explicit_routing`` directory of the source distribution.
\ No newline at end of file
diff --git a/docs/docs/tutorial/websockets.rst b/docs/docs/tutorial/websockets.rst
new file mode 100644
index 0000000..e4197a2
--- /dev/null
+++ b/docs/docs/tutorial/websockets.rst
@@ -0,0 +1,88 @@
+WebSocket support
+=================
+
+MicroPie includes first‑class support for WebSockets.  WebSockets are a
+persistent, bidirectional communication channel over a single TCP
+connection.  They enable real‑time applications such as chat, live
+notifications and collaborative editing.
+
+Mapping WebSocket routes
+------------------------
+
+WebSocket handlers live on the same :class:`~micropie.App` subclass
+that serves your HTTP routes.  The naming convention is similar to
+HTTP handlers, but with a ``ws_`` prefix.  For example, a method
+called ``ws_chat`` handles WebSocket connections at ``/chat``.  If the
+path is empty, ``ws_index`` handles the root WebSocket route.
+
+When a WebSocket connection is initiated, MicroPie constructs a
+:class:`~micropie.WebSocket` object and passes it as the first
+argument to your handler, along with any path or query parameters.  A
+typical handler must call :meth:`~micropie.WebSocket.accept` to accept
+the connection and then use :meth:`~micropie.WebSocket.receive_text`
+and :meth:`~micropie.WebSocket.send_text` (or their ``bytes``
+counterparts) to communicate.
+
+Example: echo server
+--------------------
+
+The example below implements a simple echo server.  The server
+accepts the WebSocket connection and then echoes back whatever text it
+receives.  When the client disconnects, the handler finishes.
+
+.. code-block:: python
+
+   from micropie import App
+
+   class MyApp(App):
+       async def ws_echo(self, ws):
+           # Accept the connection; without this call, MicroPie will
+           # automatically reject the WebSocket.
+           await ws.accept()
+           while True:
+               try:
+                   message = await ws.receive_text()
+               except ConnectionClosed:
+                   # Client closed the connection
+                   break
+               # Send the same message back to the client
+               await ws.send_text(f"Echo: {message}")
+
+   app = MyApp()
+
+To test this endpoint, run your application and connect to
+``ws://127.0.0.1:8000/echo`` using a WebSocket client.  Most modern
+browsers support WebSockets via JavaScript.
+
+Accessing query and path parameters
+-----------------------------------
+
+Just like HTTP handlers, WebSocket handlers receive path and query
+parameters.  Additional positional arguments come from the path, and
+keyword arguments are filled from the query string or session.  For
+example:
+
+.. code-block:: python
+
+   class MyApp(App):
+       async def ws_greet(self, ws, name="Guest"):
+           await ws.accept()
+           await ws.send_text(f"Hello {name}!")
+           await ws.close()
+
+Connecting to ``ws://localhost:8000/greet/Alice`` sends back
+``"Hello Alice!"``.  Connecting to ``ws://localhost:8000/greet?name=Bob``
+sends ``"Hello Bob!"``.  See :ref:`websocket-reference` for the
+complete API of the :class:`~micropie.WebSocket` class.
+
+Integrating Socket.IO
+---------------------
+
+MicroPie focuses on the core WebSocket protocol.  If you need more
+advanced real‑time features—automatic reconnection, broadcasting, or
+fallback transports such as polling—you can integrate
+`python‑socketio <https://python-socketio.readthedocs.io>`_.  See the
+``examples/socketio`` directory in the MicroPie source distribution for
+sample code and instructions.  When integrating Socket.IO, you still
+run your MicroPie application behind an ASGI server and attach the
+Socket.IO server as additional middleware or route.
\ No newline at end of file
diff --git a/micropie.py b/micropie.py
index 5f53d18..dded65d 100644
--- a/micropie.py
+++ b/micropie.py
@@ -467,13 +467,12 @@ class App:
                         await self._send_response(send, 400, "400 Bad Request: Missing boundary")
                         return
                     # Start TRUE streaming parser in background, populate request.* live
-                    # Tune file_queue_maxsize as desired (e.g., 512 ≈ ~32MB if ~64KB chunks)
                     parse_task = asyncio.create_task(
                         self._parse_multipart_into_request(
                             receive,
                             boundary_match.group(1).encode("utf-8"),
                             request,
-                            file_queue_maxsize=512,
+                            file_queue_maxsize=2048,
                         )
                     )
                 else:
@@ -818,7 +817,7 @@ class App:
         boundary: bytes,
         request: "Request",
         *,
-        file_queue_maxsize: int = 100000
+        file_queue_maxsize: int = 2048
     ) -> None:
         """
         Parse multipart directly from ASGI receive() and populate