Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PYROBUSTA_VERSION := v0.8.0
PYROBUSTA_VERSION := v0.9.0
DEVICE ?= u0

SRC_DIR := src
Expand Down
6 changes: 6 additions & 0 deletions dist/pyrobusta/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Global server attributes"""

from os import getcwd

PYROBUSTA_VERSION = "v0.9.0"
WORKING_DIR = getcwd().rstrip("/")
Binary file added dist/pyrobusta/application.mpy
Binary file not shown.
12 changes: 7 additions & 5 deletions dist/pyrobusta/assets/www/concepts.html

Large diffs are not rendered by default.

131 changes: 87 additions & 44 deletions dist/pyrobusta/assets/www/configuration.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h2 id="table-of-contents">Table of Contents</h2>
<li><a href="#configuration">Configuration</a></li>
<li><a href="#configuration-format-deployment">Configuration Format &amp; Deployment</a></li>
<li><a href="#parameter-description">Parameter Description</a></li>
<li><a href="#configuration-api">Configuration API</a></li>
<li><a href="#configuration-loading">Configuration Loading</a></li>
</ul>
<hr />
<h2 id="configuration-format-deployment">Configuration Format &amp; Deployment</h2>
Expand All @@ -32,8 +32,8 @@ <h2 id="configuration-format-deployment">Configuration Format &amp; Deployment</
tls=False # turn off TLS
</code></pre>
<p>Perform a soft reset and upload <code>pyrobusta.env</code> using mpremote.</p>
<pre><code>$ mpremote a0 soft-reset
$ mpremote a0 cp pyrobusta.env :/pyrobusta.env
<pre><code>$ mpremote connect /dev/ttyACM1 soft-reset
$ mpremote connect /dev/ttyACM1 cp pyrobusta.env :/pyrobusta.env
</code></pre>
<h2 id="parameter-description">Parameter Description</h2>
<table>
Expand All @@ -52,10 +52,55 @@ <h2 id="parameter-description">Parameter Description</h2>
</tr>
<tr>
<td><code>wifi_password</code></td>
<td>Password of the Wi-Fi network. When empty, Wi-Fi is not initialized by the built-in <code>wifi.py</code> module.</td>
<td>Password of the Wi-Fi network.</td>
<td>None</td>
</tr>
<tr>
<td><code>tls</code></td>
<td>Enables or disables TLS. When enabled, <code>cert.der</code> and <code>key.der</code> must be installed at the server root.</td>
<td>False</td>
</tr>
<tr>
<td><code>tls_cert_file</code></td>
<td>Alternative path to the TLS certificate.</td>
<td><code>/cert.der</code></td>
</tr>
<tr>
<td><code>tls_key_file</code></td>
<td>Alternative path to the TLS private key.</td>
<td><code>/key.der</code></td>
</tr>
<tr>
<td><code>passwd_file</code></td>
<td>Path to the file containing user credentials used for authentication.</td>
<td><code>/pyrobusta.passwd</code></td>
</tr>
<tr>
<td><code>roles_file</code></td>
<td>Path to the file containing RBAC role definitions used for authorization.</td>
<td><code>/pyrobusta.roles</code></td>
</tr>
<tr>
<td><code>log_level</code></td>
<td>Logging level. Can be one of: <code>error</code>, <code>warning</code>, <code>info</code>, <code>debug</code>.</td>
<td><code>info</code></td>
</tr>
<tr>
<td><code>socket_max_con</code></td>
<td>Maximum number of simultaneous socket connections.</td>
<td>2</td>
</tr>
<tr>
<td><code>http_served_paths</code></td>
<td>Space-separated list of filesystem paths that may be served over HTTP.</td>
<td><code>/www</code></td>
</tr>
<tr>
<td><code>http_mem_cap</code></td>
<td>Fraction of available heap memory reserved for stream buffers. Valid range: (0, 1].</td>
<td>0.1</td>
</tr>
<tr>
<td><code>http_port</code></td>
<td>Port number for HTTP.</td>
<td>80</td>
Expand All @@ -67,65 +112,63 @@ <h2 id="parameter-description">Parameter Description</h2>
</tr>
<tr>
<td><code>http_multipart</code></td>
<td>Enables or disables multipart request and response processing. Enabling multipart support increases memory usage.</td>
<td>Enables or disables multipart request and response processing (<code>Content-Type: multipart/*</code>).</td>
<td>False</td>
</tr>
<tr>
<td><code>http_mem_cap</code></td>
<td>Fraction of available heap memory reserved for stream buffers. Valid range: (0, 1].</td>
<td>0.1</td>
</tr>
<tr>
<td><code>http_served_paths</code></td>
<td>Space-separated list of filesystem paths that may be served over HTTP.</td>
<td><code>/www /lib/pyrobusta</code></td>
</tr>
<tr>
<td><code>http_files_api</code></td>
<td>Enables or disables the file management API endpoint (<code>/files</code>), allowing upload, download, and listing of files.</td>
<td>False</td>
</tr>
<tr>
<td><code>socket_max_con</code></td>
<td>Maximum number of simultaneous socket connections.</td>
<td>2</td>
<td><code>http_auth</code></td>
<td>Selects the type of authentication method enforced by the server. Currently, basic authentication (<code>basic</code>) is supported.</td>
<td>None</td>
</tr>
<tr>
<td><code>tls</code></td>
<td>Enables or disables TLS. When enabled, <code>cert.der</code> and <code>key.der</code> must be installed at the server root.</td>
<td><code>http_browser_security</code></td>
<td>Enables or disables browser security features including browser security headers (Content Security Policy, referrer policy) and CSRF protection (if authentication is enabled). Disabling browser security is only recommended when using non-browser clients or during local development and testing.</td>
<td>True</td>
</tr>
<tr>
<td><code>http_insecure_auth</code></td>
<td>Allows clients to authenticate over unsecured HTTP (without TLS). This may expose credentials or authentication tokens in transit.</td>
<td>False</td>
</tr>
<tr>
<td><code>log_level</code></td>
<td>Logging level. Can be one of: <code>warning</code>, <code>info</code>, <code>debug</code>.</td>
<td>info</td>
<td><code>http_sessions</code></td>
<td>Enables browser session cookies for authenticated clients. When enabled, successful authentication establishes a session that can be reused without resending authentication credentials.</td>
<td>True</td>
</tr>
<tr>
<td><code>http_session_ttl_sec</code></td>
<td>Duration of validity of session cookies in seconds.</td>
<td>900</td>
</tr>
</tbody>
</table>
<h2 id="configuration-api">Configuration API</h2>
<p>Configuration values can be accessed through the
<code>pyrobusta.utils.config</code> module.
Values are loaded from <code>pyrobusta.env</code> during server initialization.
Configuration values can be retrieved using
<code>get_config()</code> together with one of the
predefined <code>CONF_*</code> constants.</p>
<p>After initialization, configuration values are retrieved from an internal cache.
The cached values are normalized to their expected runtime types to avoid repeated
parsing of environment strings.</p>
<p>Configuration values are treated as immutable during runtime.
Changes are applied only when the configuration cache is reloaded.
The configuration cache can be reloaded by calling
<code>read_config()</code>, which re-reads <code>pyrobusta.env</code>
and rebuilds the internal normalized cache.</p>
<pre><code>from pyrobusta.utils.config import get_config, CONF_TLS
<h2 id="configuration-loading">Configuration Loading</h2>
<p>Configuration is represented by the <code>Config</code> class in
<code>pyrobusta.utils.config</code>. During application initialization,
<code>pyrobusta.application</code> creates and loads a <code>Config</code> instance from
<code>pyrobusta.env</code>, converting values to their expected runtime types.</p>
<p>Configuration values are exposed as attributes on the <code>Config</code> instance:</p>
<pre><code>config = Config(&quot;/pyrobusta.env&quot;)

@HttpEngine.route(&quot;/tls&quot;, &quot;GET&quot;)
def tls_status(http_ctx, _):
enabled = get_config(CONF_TLS)
return &quot;text/plain&quot;, f&quot;TLS enabled: {enabled}&quot;
if config.tls:
...
</code></pre>
<p>The application uses the configuration to initialize and specialize the
relevant subsystems during startup. The <code>Config</code> instance is then discarded;
runtime components do not access configuration directly.</p>
<p>Configuration is <strong>immutable for the lifetime of an application</strong>. Changes
to <code>pyrobusta.env</code> therefore require a new application instance and, under
normal operation, an application restart.</p>
<p>The <code>pyrobusta.utils.config</code> module does not maintain a global configuration
object or runtime configuration cache. It provides the <code>Config</code> definition
and configuration-loading functionality.</p>
<hr />
<p>PyRobusta v0.8.0 Web Server</p>
<p>PyRobusta v0.9.0 Web Server</p>

</body>
</html>
152 changes: 152 additions & 0 deletions dist/pyrobusta/assets/www/error_handling.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>

<h1 id="logging-error-handling">Logging &amp; Error Handling</h1>
<p><a href="index.html">← Back</a></p>
<p>This page describes how the server provides logging with different levels of verbosity
and handles HTTP status codes for client and server errors, including exceptions raised by applications.</p>
<hr />
<h2 id="table-of-contents">Table of Contents</h2>
<ul>
<li><a href="#logging-error-handling">Logging &amp; Error Handling</a></li>
<li><a href="#logging">Logging</a></li>
<li><a href="#client-errors-4xx">Client Errors (4xx)</a></li>
<li><a href="#server-errors-5xx">Server Errors (5xx)</a></li>
<li><a href="#exception-handling">Exception Handling</a></li>
</ul>
<hr />
<h2 id="logging">Logging</h2>
<p>The server provides built-in logging constructs implemented by the <code>pyrobusta.utils.logging</code>
module. The logging module supports several log levels, controlled by the <code>log_level</code> server
configuration. <code>log_level</code> can be set to one of <code>error</code>, <code>warning</code>, <code>info</code>, or <code>debug</code>, listed in increasing
order of verbosity. Selecting a log level enables logging for that level and all less verbose levels.
For example, setting <code>log_level</code> to <code>info</code> enables <code>info</code>, <code>warning</code>, and <code>error</code> messages.</p>
<p>Logging methods expect a mandatory format string as the first positional argument,
followed by optional positional arguments substituted in the format string.</p>
<pre><code class="language-python">import asyncio

from pyrobusta import application
from pyrobusta.utils import logging


async def main():
await application.run()
logging.info(&quot;%s: %s&quot;, __name__, &quot;the application started&quot;)
while True:
await asyncio.sleep(1)

asyncio.run(main())
</code></pre>
<pre><code class="language-log">29972 INFO pyrobusta.application: connected, ip=[192.168.1.101]
29983 INFO pyrobusta.server.http_server: 1 connection(s) allowed
30029 INFO pyrobusta.server.http_server: started
30041 INFO __main__: the application started
</code></pre>
<h2 id="client-errors-4xx">Client Errors (4xx)</h2>
<p>As described in the <a href="response.html#status-codes">Response Handling</a> guide, route
handlers can set the status codes directly with the <code>terminate</code> method of the HTTP
context. Invalid requests should be rejected by the application with a 4xx status
code. The specific status code is application-specific.</p>
<pre><code class="language-python">@HttpEngine.route(&quot;/app/action&quot;, &quot;POST&quot;)
def handler(http_ctx, _):
content_type = http_ctx.headers.get(&quot;content-type&quot;)
if content_type != &quot;application/json&quot;:
http_ctx.terminate(415)
return &quot;text/plain&quot;, &quot;Unsupported media&quot;
# &lt;rest of the application route&gt;
</code></pre>
<p>The application must use one of the following status codes supported by PyRobusta:</p>
<table>
<thead>
<tr>
<th>Status Code</th>
<th>HTTP Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>400</td>
<td>Bad Request</td>
</tr>
<tr>
<td>401</td>
<td>Unauthorized</td>
</tr>
<tr>
<td>403</td>
<td>Forbidden</td>
</tr>
<tr>
<td>404</td>
<td>Not Found</td>
</tr>
<tr>
<td>405</td>
<td>Method Not Allowed</td>
</tr>
<tr>
<td>408</td>
<td>Request Timeout</td>
</tr>
<tr>
<td>413</td>
<td>Content Too Large</td>
</tr>
<tr>
<td>415</td>
<td>Unsupported Media Type</td>
</tr>
</tbody>
</table>
<h2 id="server-errors-5xx">Server Errors (5xx)</h2>
<p>5xx status codes indicate errors preventing the server from processing a valid request.
The following list of 5xx status codes are supported by PyRobusta:</p>
<table>
<thead>
<tr>
<th>Status Code</th>
<th>HTTP Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>500</td>
<td>Internal Server Error</td>
</tr>
<tr>
<td>503</td>
<td>Service Unavailable</td>
</tr>
<tr>
<td>505</td>
<td>Version Not Supported</td>
</tr>
</tbody>
</table>
<h2 id="exception-handling">Exception Handling</h2>
<p>Applications may raise exceptions to signal a failure. Uncaught exceptions are
automatically mapped to the 500 status code sent in the response.</p>
<p>Additionally, when the server catches an exception raised by the application,
it logs an error message.</p>
<pre><code class="language-python">@HttpEngine.route(&quot;/app/action&quot;, &quot;POST&quot;)
def handler(http_ctx, _):
# [...]
raise RuntimeError(&quot;failed to execute action&quot;)
# [...]
</code></pre>
<pre><code class="language-log">2287169 INFO pyrobusta.application: already connected ip=[192.168.1.101]
2287179 INFO pyrobusta.server.http_server: 1 connection(s) allowed
2287222 INFO pyrobusta.server.http_server: started
[...]
2292844 ERROR pyrobusta.protocol.http.run: error=[failed to execute action]
</code></pre>
<hr />
<p>PyRobusta v0.9.0 Web Server</p>

</body>
</html>
2 changes: 1 addition & 1 deletion dist/pyrobusta/assets/www/file_server.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ <h3 id="example-request_3">Example Request</h3>
192.168.1.100/files/www/user_data/test.txt
</code></pre>
<hr />
<p>PyRobusta v0.8.0 Web Server</p>
<p>PyRobusta v0.9.0 Web Server</p>

</body>
</html>
4 changes: 3 additions & 1 deletion dist/pyrobusta/assets/www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ <h2 id="available-resources">Available Resources</h2>
<li><a href="response.html">Response Processing</a></li>
<li><a href="static_content.html">Static Content</a></li>
<li><a href="file_server.html">File Server API</a></li>
<li><a href="security.html">Authentication &amp; Security</a></li>
<li><a href="error_handling.html">Logging &amp; Error Handling</a></li>
<li><a href="https://github.com/szeka9/PyRobusta">Source Code</a></li>
</ul>
<hr />
<p>PyRobusta v0.8.0 Web Server</p>
<p>PyRobusta v0.9.0 Web Server</p>

</body>
</html>
Loading
Loading