Git Product home page Git Product logo

microdot's Introduction

microdot

Build status codecov

“The impossibly small web framework for Python and MicroPython”

Microdot is a minimalistic Python web framework inspired by Flask. Given its small size, it can run on systems with limited resources such as microcontrollers. Both standard Python (CPython) and MicroPython are supported.

from microdot import Microdot

app = Microdot()

@app.route('/')
async def index(request):
    return 'Hello, world!'

app.run()

Migrating to Microdot 2

Version 2 of Microdot incorporates feedback received from users of earlier releases, and attempts to improve and correct some design decisions that have proven to be problematic.

For this reason most applications built for earlier versions will need to be updated to work correctly with Microdot 2. The Migration Guide describes the backwards incompatible changes that were made.

Resources

Roadmap

The following features are planned for future releases of Microdot, both for MicroPython and CPython:

  • Support for forms encoded in multipart/form-data format
  • Authentication support, similar to Flask-Login for Flask
  • OpenAPI integration, similar to APIFairy for Flask

In addition to the above, the following extensions are also under consideration, but only for CPython:

Do you have other ideas to propose? Let's discuss them!

microdot's People

Contributors

altoretrato avatar andypiper avatar bulletmark avatar carlo-colombo avatar dependabot[bot] avatar diegopomares avatar dpgeorge avatar hamsanger avatar miguelgrinberg avatar sgbaird avatar steveli777 avatar taktran avatar teslavolt avatar wheelercc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

microdot's Issues

Support streaming contents

Hello

I wanted to use SSE, However, that requires leaving the connection open and being able to push/flush multiple responses for the same request.

The way Flask has implemented this feature is to allow a generator as the response body. (see: Streaming Content). Which can be used to implement SSE.

I can see that you already have a similar setup in body_iter, is there any chance you consider exposing that (i.e. allow the application to overwrite body_iter to any generator if a function is returned as response), so the application wouldn't be limited to only streaming file-like objects?

Thanks.

upip install fails

Hi Miguel,

Even though Microdot seems to be in the Pypi repo, I cannot install it.
I'm using "MicroPython v1.12 on 2019-12-20; ESP32 module with ESP32"

>>> upip.install('microdot')
Installing to: /lib/
Warning: micropython.org SSL certificate is not validated
Error installing 'microdot': , packages may be partially installed
>>> import microdot
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: no module named 'microdot'
>>> 

Any ideas?

add func to return

2EE19C19-C447-4d62-940A-67A460A27C4C
@classmethod
def send(cls, file_date, content_type=None, status_code=200):
if not content_type:
content_type = 'application/octet-stream'
return cls(body=file_date, status_code=status_code,
headers={'Content-Type': content_type})

Request fails when uploading large files

I believe this issue is somewhat analogous to issue #3 and #5. I am trying to upload a large file from my browser using a simple XMLHttpRequest() to an ESP32 running async Microdot. However, the code dies immediately within Request.create() at the line body = await client_stream.read(content_length) because it is trying to immediately allocate memory for the full file size. I was expecting that for this case the request body would be a stream which I could read in block chunks. Am I missing something?

I guess ideally there would be a request.files attribute like you describe for flask here. Any plans for that?

Async websockets - Unicode Error when connecting from Dart/Flutter

Hi,

I have a micropython app running microdot async websockets. It works ok from a browser (using the example) however when I try to connect to the websocket from Dart/Flutter I get a unicode error.

Traceback (most recent call last):
  File "/lib/microdot_asyncio.py", line 328, in handle_request
  File "/lib/microdot_asyncio.py", line 70, in create
UnicodeError: 

Any ideas on how I can debug this further?

I am using the latest microdot code from github.

Thanks

Handling special characters/utf-8 from request

According to the docs body string in HTTP response should be encoded in UTF-8 but the response cant handle special characters like "ÅÄÖ". This is the code im using:

app = Microdot()

htmldoc = '''<!DOCTYPE html>
<html>
    <head>
        <title>Microdot Example Page</title>
         <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    </head>
    <body>
        <div>
            <h1>Microdot Example Page</h1>
            <p>Test åöä</p>
            <p><a href="/shutdown">Click to shutdown the server</a></p>
            <form action="/form" method="POST" accept-charset="utf-8">
            <label for="ssid">SSID:</label><br>
            <input type="text" id="ssid" name="ssid"><br>
            <label for="password">Password:</label><br>
            <input type="password" id="password" name="password"><br>
            <input type="submit" value="Submit">
            </form>
        </div>
    </body>
</html>
'''

@app.route('/')
def hello(request):
    return htmldoc, 200, {'Content-Type': 'text/html'}

@app.route('/form', methods=['GET', 'POST'])
def index(req):
    if req.method == 'POST':
        print(req.form.get('ssid')) #  <-- This outputs Lös if the given value is Lös
        ssid = req.form.get('ssid')
        password = req.form.get('password')
        print(ssid) #  <-- This outputs Lös if the given value is Lös
        print(password) #  <-- This outputs Lös if the given value is Lös
    return f'SSID: {ssid}, Password: {password}' #  <-- This outputs Lös if the given value is Lös

[FEATURE REQUEST] example of controlling an LED and reading temperature from Pico W

problème update V1 tout V1.1

Miguel
Many thanks with that fantastic project
There is a problem on microdotwebsocket.py with
self.request.sock.write line 20 and after you are not writing self.request.sock.send
Same thing with self.request.sock.read line 113 and after it is self.request.sock.recv

Regards

Making the Response reason available to the application

I recommend a simple enhancement to the Response class to allow for specification of the reason. Currently it hard-codes this to "OK" for a 200 response and N/A for everything else. For example, if returning a 400 response, it would be more useful for the application to specify why, where there is provision for it, instead of having to put this in the response body. It is apparently quite valid to customise this, per https://www.ibm.com/docs/en/cics-ts/5.1?topic=concepts-status-codes-reason-phrases - "The HTTP/1.1 specification says that the reason phrases for each status code may be replaced by local equivalents."

A suggested fix is to replace, in microdot.py...

164:     def __init__(self, body='', status_code=200, headers=None):
165:         self.status_code = status_code

with

164:     def __init__(self, body='', status_code=200, reason="N/A", headers=None):
165:         self.status_code = status_code
166:         self.reason = reason

and

210:             reason='OK' if self.status_code == 200 else 'N/A').encode())

with

211:             reason='OK' if self.status_code == 200 else self.reason).encode())

TODO for new features!

Hello!

Do you think to implement new features? If yes, what do you thing about to create a TODO and/or a roadmap for the new features?

Thank you very much!

Pass parameter/context to html

Hello,

I am looking for a way to adjust my HTML dynamically.

My use case is, that I want to develop a web UI for ESP32 MCU, where the users can configure the WLAN. Therefore, the HTML should be but not static but dynamic (based on the scanned WLAN list). Here I found an example, but not using microdot.

HTML multiple dropdown list returns only the last selected value

Hi again,
I've noticed that HTML select/options list with multiple selection returns only the last selected value in the request.form:

<form method="POST" action="">
<p>
<i class="bi bi-calendar-day"></i>
<label for="weekdays">days:</label>&nbsp;&nbsp;
<select class="form-select" name="weekdays" id="weekdays" multiple>
<option value="" selected hidden disabled>change</option>
<option value="0">Monday</option>
<option value="1">Tuesday</option>
<option value="2">Wednesday</option>
<option value="3">Thursday</option>
<option value="4">Friday</option>
<option value="5">Saturday</option>
<option value="6">Sunday</option>
</select></p>
<button type="submit" name="settings" value="save" class="btn btn-primary">
</form>

Is this an expected/known behavior? Any hints on how to fix it? Thanks!

Possible to redirect from a alias (app name) to the host as a IP?

Hi,

Thanks a lot for the library, first of all. This is actually a question and not a issue per se, (sorry for that).

Instead of browsing for the ip adress of the host (like 192.168.x.y:5000), can I give a app name and get it redirected to the webserver (like http://appname.local)?

I looked up in some forums which are telling to include the "appname" to to "etc/hosts" file, which off course does not exist in a ESP8266...

Thanks!

unable to install microdot on esp8266

import upip
upip.install('microdot')
Installing to: /lib/
Warning: micropython.org SSL certificate is not validated
Error installing 'microdot': syntax error in JSON, packages may be partially installed

problem with more than one connection on pyboard + w5500

I can run a simple web server (the getting started example) and access it on a single device, but when I try to access it on a second device it stops working with the following error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "microdot.py", line 926, in run
  File "microdot.py", line 35, in create_thread
  File "microdot.py", line 968, in handle_request
  File "microdot.py", line 482, in write
OSError: 7

and the error isn't thrown right away, usually after I try to access it again from the first device that I tried it on.

This happens specially on chrome which seemingly sends a couple more packets than chromium.

I was guessing maybe it's a firmware-related problem (cause of the error code 7: 'Argument list too long') but I've got the latest micropython firmware with MICROPY_PY_NETWORK_WIZNET5K option enabled. I'm running this on a pyboard v1.1 + W5500 if that matters.

thank you in advance.

Question: upload file example.

Hello.

I would like to send, via upload page, to the ESP32 flash a file.tar.gz file (~6MB). Are there any example or tip how to do that?

The idea is just simple: user click on upload button on the page, and select on local computer the file, and send it.

Thank you.

Variable Rules in Microdot?

Hi Miguel,

I'm trying to set up variable rules for app routes, and I'm not succeeding in my efforts to get them to work. Would you expect the following to work, as an example, for any files stored in a sub directory ('/img/')?:

@app.route("/img/<path:subpath>", methods=["GET", "POST"])
def content(subpath):
    filepath = 'www/img' + str(subpath)
    response = send_file(filepath)
    return response

I'd be very grateful for your assistance.

Many thanks,

Colin

send_file fails with larger files in asyncio version

Was the issue in #3 also fixed in the asyncio version?

When trying to send a 30 kB file this happens:

Traceback (most recent call last):
  File "main.py", line 57, in <module>
  File "microdot_asyncio.py", line 100, in run
  File "/lib/uasyncio/core.py", line 180, in run_until_complete
  File "/lib/uasyncio/core.py", line 109, in run_forever
  File "microdot_asyncio.py", line 94, in serve
  File "microdot_asyncio.py", line 145, in dispatch_request
  File "microdot_asyncio.py", line 72, in write
  File "/lib/uasyncio/__init__.py", line 169, in awrite
OSError: [Errno 104] ECONNRESET

The picture was only partially loaded and the web server dead after this (back to Python prompt).

[Pico W] CircuitPython support vs. switching to MicroPython

I built up a basic Pico W - compatible example for an autonomous research lab demo using CircuitPython libraries like neopixel and adafruit_as7341. If I want to use microdot, seems I'll need to switch over to running MicroPython and then use the Blinka compatibility layer to access the CircuitPython libs (and hope that what I want is supported). Does that seem like the best option? Any feedback/suggestions?

The alternative I've been trying to avoid is asking users to parse through web server setup code that they'll likely have very little familiarity with. Flask came to mind, but it was quickly obvious that it's not suited for Pico. Then came across a YouTube comment about microdot.

Code called in parallel to the main http server

Hi.
Is it possible to have an http server running and in parallel some kind of cron that every so often will call my code completely unrelated to the HTTP microdot server?

Example : every few hours I would like to download small data from the Internet, which I will want to serve as someone calls my server.

Trivial example: every few hours, in parallel to the running http server, I would like to turn on and off the diode

Example for using microdot_asyncio

Hi,

thanks for the library! I am looking for a nonblocking webserver and found your project. I am pretty new to micropython though.

Would it be possible to get a small example of how to use microdot_asyncio so I can create a nonblocking web server?

Thanks in advance!

Question: run asgi and wsgi app

Are the two examples supposed to be run on a computer os terminal? To run on ESP32, do I need to upip install uvicorn and gunicorn, and run the app in Python code?

WiFi connection function?

I'm using microdot on the Raspberry Pi Pico W and it's great to see some Pico web server code at the right level of abstraction, rather than the ugly socket stuff.
For me, it would be really nice to have a utility function in microdot.py that does the WiFi connection (see below). I know its perhaps not generic enough, but it makes the main web server code very much cleaner. What do you think?
Sorry not to do this as a pull-request, but (to be honest) I don't really know what I'm doing with git.

Proposed new function:

def connect_to_network(wlan, ssid, password):
    wlan.active(True)
    wlan.config(pm = 0xa11140)  # Disable power-save mode
    wlan.connect(ssid, password)

    max_wait = 10
    while max_wait > 0:
        if wlan.status() < 0 or wlan.status() >= 3:
            break
        max_wait -= 1
        print('waiting for connection...')
        time.sleep(1)

    if wlan.status() != 3:
        raise RuntimeError('network connection failed')
    else:
        print('connected')
        status = wlan.ifconfig()
        print('ip = ' + status[0])

This the simplifies my server code to:

from microdot import Microdot, connect_to_network
import network
import time

ssid = 'Monk'
password = 'd4daaa7eda'

wlan = network.WLAN(network.STA_IF)
app = Microdot()
        
print('Connecting to Network...')
connect_to_network(wlan, ssid, password)

@app.route('/')
def index(request):
    return 'Hello, from Pico'

app.run(port=80)

Feature request: secure WebSocket (wss)

Hello!

Congratulations for the great project.

I would like to know if you have intention to support secure WebSocket (use SSL over WebSocket) on the Microdot.

Thank you.

Feature Request: Shutdown server

I have a use case that requires I start and stop the app based on certain criteria. For instance, if wifi credentials are invalid, I start the app and provide an API for updating the credentials. Once the credentials are updated, I stop the app and connect to the access point. I also allow the user to enter setup mode manually, which then restarts the app. Currently, the only way to accomplish this is to restart the entire process.

It would also be nice to leverage the existing event loop instead of running forever. Creating a task on the existing event loop is more inline with other MicroPython libs and is much more flexible in general.

Found a possible security concern

Hey there!

I belong to an open source security research community, and a member (@ktg9) has found an issue, but doesn’t know the best way to disclose it.

If not a hassle, might you kindly add a SECURITY.md file with an email, or another contact method? GitHub recommends this best practice to ensure security issues are responsibly disclosed, and it would serve as a simple instruction for security researchers in the future.

Thank you for your consideration, and I look forward to hearing from you!

(cc @huntr-helper)

Affinity for any particular templating library?

Thanks for creating Microdot! I teach an introductory engineering class with MicroPython and the ESP32, and Microdot's minimalism is perfect for me.

Do you have a suggested or preferred templating system for microdot? (My experience is with Flask/Jinja, and I see that picoweb is designed to work with utemplate). Or is part of the point that it'll work with whatever template system I want?

How to find the IP address of the server?

Hi, this is my first test of Microdot, using a T-Pico C3, equipped with an ESP32 C3.
I test the simple example below (using Thonny, then Run):

from microdot import Microdot
app = Microdot()

@app.route('/')
def index(request):
    return 'Hello, world!'

app.run(port=80)

But I don't know how to access to the server. using my smartphone and Httper, because I don't know the IP address of the ESP32.

I tried to do this:

import network
ap = network.WLAN(network.AP_IF)
print(ap.ifconfig())

which answers ('192.168.4.1', '255.255.255.0', '192.168.4.1', '0.0.0.0') but I don't get any reply from the server when I send a request to 192.168.4.1 using Httper, both for GET and POST requests.
The same with
app.run(host='192.168.4.1', port=80)
How can I know the correct IP address?

send_file fails with larger files

send_file is great but it seems to have an issue with larger files.

When trying to send an 18k JPG, I get this:

Unhandled exception in thread started by <closure>
Traceback (most recent call last):
  File "/lib/microdot.py", line 24, in run
  File "/lib/microdot.py", line 390, in dispatch_request
  File "/lib/microdot.py", line 210, in write
OSError: [Errno 104] ECONNRESET

Looking at the code, I think a likely problem is that it reads the whole file into memory before it sends it. Is there a way to do this differently, more like a stream architecture, so it doesn't need so much memory?

microdot serving over Ethernet rather than WiFi on ESP32

I've been enjoying microdot for some time.
Today I've tried implementing an existing project on a generic ESP32 with an Ethernet interface.
As soon as I get to:

async def main():
    await app.start_server(host='0.0.0.0', port=80, debug=True)

The Ethernet interface stops responding to ICMP ping.

As a quick test I configured the wifi interface and microdot happily serves over that:

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:4344
load:0x40078000,len:13816
load:0x40080400,len:3340
entry 0x40080618
Wifi Config ('10.4.0.78', '255.192.0.0', '10.0.1.254', '10.0.0.1')
RGBW order selected
Config: ('10.4.0.170', '255.192.0.0', '10.0.1.254', '10.0.0.1')  <--- ETHERNET
CREATE TASK main - this starts the WebServer
CREATE TASK monitor - this is the watchdog
CREATE TASK roll - this drives the NeoPixels
CREATE TASK buttons - this listens to the Buttons
Starting async server on 0.0.0.0:80...
GET / 200
GET /style.css 200
GET /favicon.ico 200

Is there anywhere I should be looking?

Regards

Andy

upip install fails

Hi!
I flashed Micropython to my esp8266 a couple of days ago (pulled from master) and I couldn't install it with upip.install('microdot').
It says:

Installing to: /lib/                                                                                                                                  
Warning: micropython.org SSL certificate is not validated                                                                                             
Installing microdot 0.3.0 from https://files.pythonhosted.org/packages/2b/7b/d0f35947d698b7d1c660d9eee08181b1ef4f5a2d7b1e30c7d649e382e745/microdot-0.3
.0.tar.gz                                                                                                                                             
Error installing 'microdot': [Errno 22] EINVAL, packages may be partially installed

How can I install manually?
I mean, how a folder structure should look like?

Microdot, Micropython and Raspberry Pi Pico W

I am attempting to use Microdot with Micropython v1.19.1-88-g74e33e714 on a Raspberry Pi Pico W.

When I attempt to import a class from the microdot.py file (from microdot import Microdot) I receive the following error:

Traceback (most recent call last):
File "", line 1, in
ImportError: no module named 'microdot.Microdot'

When I import as a whole (import microdot) it imports, but none of the classes are available (app = Microdot() )

Is this an issue with Microdot not being supported by this version of Micropython?

Thanks in advance

-- Mike

Unhandled EINTR exception thrown from Microdot.run() accept() call

Microdot 0.3.0 (running on MicroPython v1.12-1 on 2020-02-09; linux version on Ubuntu 20.04.2 LTS) does not handle OS Error 4 (EINTR) that can be thrown from the s.accept() call in Microdot.run().
[Symptom: Hit Reload repeatedly in web-browser to load one HTML file and one PNG image file; EINTR gets thrown (seemingly at random) approximately 1 time in 10].
Arguably this exception should be handled by Micropython.

This page ( https://stackoverflow.com/questions/14136195/what-is-the-proper-way-to-handle-in-python-ioerror-errno-4-interrupted-syst ) suggests the EINTR can be ignored and the operation retried.

I am working around this issue by a change to Microdot.run() to ignore the exception as per attached file
run.py.txt
ws:

HTML Templates

Hello Miguel, I've come across with your project and I was wondering if you are considering adding template system (jinja2) to it.
Thank you

Feature request: Websocket support

Hello!

As you wrote here that are opened to suggestions for new features, I have just one that I consider very important: the Websocket support.

My goal is to create a simple web interface for the ESP32, STM32 to configure some sensors (like as temperature, humity, etc) and monitoring (events, alarms) it real time. Well, with Websocket I can just wait a event/alarms coming without todo a polling on the HTTP Server.

In my opinion is a very important feature!

Thank you in advance!

Have a little trouble to understand source code

Hi miguel!
In example/gpio.py, I notice parameters response. I think in gpio.py, I don't import response class from microdot.py, but when I get attributes I get this.
I add two lines code in gpio.py.

print(type(response))
print(dir(response))

I get this.

<class 'Response'>
['__class__', '__dict__', '__init__', '__module__', '__qualname__', 'write', 'status_code', 'headers', 'redirect', 'send_file', 'body', 'set_cookie', 'types_map', 'complete']

So, the response class from where?

upip install of microdot-asyncio fails

Similar to #2, I get the same error for microdot-asyncio installing with upip.

>>> upip.install('microdot-asyncio')
Installing to: /lib/
Error installing 'microdot-asyncio': , packages may be partially installed

Again no error message between : and ,
Same fix hopefully?

Error while import microdot

I am using node mcu esp8266.
When I import microdot lib there is error:

ImportError: can't import name Microdot

Is there a way to do a "catchall" for captive portals?

Hey @miguelgrinberg - I'm loving microdot, thanks heaps for making it.

Is there a way to do a catchall route? captive portals have so many different endpoints on different OS versions, and when I was using tinyweb, it had a catchall that enforced any url that didn't have a route specified to go to a specific route of my choosing.

Thanks.

Local use of CSS on the gpio.html example

Hello!

Maybe this is a very basic question, but I have no idea how is possible to do that.

I was running the gpio.html example. It is very good, but I would like that all CSS bootstrap.min.css stay inside microcontroller flash, how is possible?

I downloaded the bootstrap.min.css and copied into microcontroller flash, in the same directory where there are the gpio.py and gpio.html, and changed this line of gpio.html:

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

and substituted by this:

<link rel="stylesheet" type="text/css" href="bootstrap.min.css">.

But that do not works.

I tried to change the path on the href too, with these options, but nothing works:

<link rel="stylesheet" type="text/css" href="/bootstrap.min.css">.
<link rel="stylesheet" type="text/css" href="./bootstrap.min.css">.

Could you please tell me what I need to do to that works?

I readed the docs https://microdot.readthedocs.io/en/latest/api.html# to find something, but no success. I would also like to inform you that I found a simple error in that documentation. In the https://microdot.readthedocs.io/en/latest/api.html#microdot.Microdot.after_request the example was wrote as @app.before_request

Thank you very much.

ESP32 "Connection Refused" by server

Wrote a simple "hello world" server as shown in the example, but getting "Connection refused" error when trying to connect. I tried from multiple web browsers as well as using curl on the command line.

I am running on the Adafruit ESP32 'Huzzah32' board

Here is the code:

from neopixel import NeoPixel
from machine import Pin
import time
from microdot import Microdot

time.sleep(3)  # give 3 seconds to connect to WiFi

# web server
app = Microdot()

@app.route('/')
def hello(request):
    return 'Hello world'

print('Starting microdot app')
app.run()

Output of curl:

❯ curl http://192.168.1.198/
curl: (7) Failed to connect to 192.168.1.198 port 80: Connection refused

Check for http headers must not be case-sensitive

In the init and create functions of Request the header keys are compared with case-sensitive strings (Content-Length, Content-Type and Cookie) but the HTTP specification says that these header keys are case-insensitive.

elif header == 'Content-Type':

The check should be case-insensitive like so:
if header.lower() == 'content-type':

I came across this issue because a HTTP client was using the HTTP header 'Content-type'.

Arduino RP2040 nano connect - SOL_SOCKET

Running microdot on the official Micropython 1.18 firmware results in a error on line:
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Unfortunately, commenting out this line does not help.

Reading body of request is not guaranteed to read entire body

The code used to read the body of the request is:

        # body
        body = await client_stream.read(content_length) if content_length and \
            content_length <= Request.max_content_length else b''

But asyncio.Stream.read(n) is not guaranteed to read exactly n bytes, it may return a shorter read than that. In such a case only part of the body is read.

Is this expected behaviour of microdot (to not always read the whole body), or is it a bug, which can be fixed by using readexactly?

Microdot serving over Ethernet (Same Switch)

Hi - I wonder if you can shed some light on an issue I've found.

I have Microdot running on a W5100S-EVB-Pico which is a RP2040 with built-in Ethernet.

Here's the stripped down start-up:

try:
    import uasyncio as asyncio
except ImportError:
    import asyncio
import network
from microdot_asyncio import Microdot, Response
from web_gen import *
#from watchd import *

"""
async def main():
    await app.start_server(host='0.0.0.0', port=80, debug=True)
    print ("This is after the Web Server has started")


event_loop = asyncio.get_event_loop()

asyncio.create_task(main())
print ("This is after the asyncio create_task of main")

#asyncio.create_task(monitor())
#print ("This is after the asyncio create_task of monitor - the watchdog thread")

#asyncio.create_task(roll(np))
#print ("This is after the asyncio create_task of roll")

event_loop.run_forever()
"""

app.run(host='0.0.0.0', port=80, debug=True)

This is avoid getting caught up with the rest of my code. If I test from a system that is on another switch to Microdot all is well. However, if I test from a device that is on the SAME switch I get these issues:
Screenshot 2022-08-11 at 17 20 31
Screenshot 2022-08-11 at 17 26 37

You'll see that the style.css file is pending - and is finally served after 6 mins.

I've noticed this sort of issue on a older technology (NetMedia Siteplayer), so I wonder if the request is arriving so fast it is not registered, and the extra switch adds a tiny delay as therefore all is good.

Regards

Andy

For reference here's the web_gen.py:

try:
    import uasyncio as asyncio
except ImportError:
    import asyncio
from microdot_asyncio import Microdot, Response
from machine import Pin
from neo_patterns import *

LED = Pin(25, Pin.OUT)

app = Microdot()

@app.route('/')
@app.route('/command')
async def hello(request):
    return Response.send_file('index.html', status_code=200)

@app.route('/docs')
async def show_docs(request):
    return Response.send_file('docs.html', status_code=200)

@app.route('/toggle')
async def toggle(request):
    LED.value(not(LED.value()))
    return Response.send_file('index.html', status_code=200)

@app.route('/json_config')
async def get_config(request):
    return Response.send_file('config.json', status_code=200)

@app.route('/button_config')
async def get_button_config(request):
    return Response.send_file('buttons.json', status_code=200)

@app.route('/config_send')
async def show_config_req(request):
    print("CONFIG DATA - REQ QUERY: {}".format(request.query_string))
    save_config(request.query_string)
    return Response.send_file('config.html', status_code=200)



@app.route('/queue/<job>')
async def get_queue(request,job):
    messages.append(job)
    return Response.redirect('/', status_code=302)

@app.route('/cmd/<job>')
async def get_cmd(request,job):
    messages.append(job)
    return Response(body="['Queued']", status_code=200)

@app.route('/scene/<job>')
async def get_scene(request,job):
    LED.value(not(LED.value()))
    scene_def = "{}:{}".format(job,request.query_string)
    messages.append(scene_def)
    return Response.redirect('/', status_code=302)


@app.route('/style.css')
async def serve_path(request):
    print('Direct style.css')
    try:
        return Response.send_file('style.css', status_code=200)
    except:
        print ("Request for {} NOT FOUND".format(path))
        return 'Request for {} NOT FOUND'.format(path)


@app.route('/<path:path>')
async def serve_path(request,path):
    print('Asked for {}'.format(path))
    try:
        return Response.send_file(path, status_code=200)
    except:
        print ("Request for {} NOT FOUND".format(path))
        return 'Request for {} NOT FOUND'.format(path)

@app.route('/shutdown')
async def shutdown(request):
    request.app.shutdown()
    return 'The server is shutting down...'

The networking is all 1Gb/s switches. The local switch is a NetGear.

Regards

Andy

Access log of all requests and responses

It would be helpful (especially during development) to be able to log requests and responses, similar to apache/nginx/uwsgi logs. Probably using Common Log Format would be handy.

Stops working when idling a bit?

Hello, i've the simple hello world example running atm. using uasyncio on an ESP32-WROOM from AZ-Delivery.

My problem is that it works fine first, but when i let it idle a bit and try to open the page again, it is just stuck in loading the page and nothing happens anymore.

Used micropyserver first without this issue, so i might miss something related to this library or maybe "u"async is the problem?

Would really like to use this, but i've no clue how to solve that issue, i run the server in debug mode & it won't spit out any errors or so, just hangs somewhere. The page loading never ends like when unreachable or so. Network inspector says "Pending". Sometimes it happens after ~10 minutes, sometimes after 30 minutes :S

Any help would be appreciated

import network
import socket
import time

import uasyncio
from microdot_asyncio import Microdot

wlan_sta = network.WLAN(network.STA_IF)


def do_connect():
    import network
    #sta_if = network.WLAN(network.STA_IF)
    if not wlan_sta.isconnected():
        print('connecting to network...')
        wlan_sta.active(True)
        wlan_sta.connect('myssid', 'mypass')
        while not wlan_sta.isconnected():
            pass
    time.sleep(1)
    print('network config:', wlan_sta.ifconfig())

app = Microdot()

do_connect()

status = wlan_sta.ifconfig()
addr = socket.getaddrinfo(status[0], 80)[0][-1]

@app.route('/')
async def index(req):
    return 'Hello, world!'

async def main():
    await app.start_server(host=addr[0], port=80, debug=True)



uasyncio.run(main())

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.