miguelgrinberg/microdot

Shutdown not completing request

Closed this issue · 11 comments

Describe the bug
When using the example in docs for shutting down the server, it does not complete the request and therefor not shutting down the server.

To Reproduce
use function like in docs

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

Getting no response, and therefore expecting the server not shutting down. So i can't access the ESP because it is in an infinite loop

Expected behavior
Response should be returned

What do you have in your main script after app.run()? Also, you talk about an infinite loop. Where is that? Or are you just guessing?

Currently my main.py looks like this

from microdot import Microdot

# Create webserver
app = Microdot()

@app.before_request
async def authenticate(request):
    headers = request.headers
    # Format `Authorization: Bearer <api_token>`
    if "Authorization" not in headers:
        return "Unauthorized", 401
    if "Bearer" not in headers["Authorization"]:
        return "Unauthorized", 401
    api_token = headers["Authorization"].split(" ")[1]
    if api_token != API_TOKEN:
        return "Unauthorized", 401
    
@app.get("/")
async def index(request):
    return "Hello, world!"

@app.post("/open")
async def open(request):
    # open the connected door/barrier
    opener.open(1)
    return "OK"

@app.get("/shutdown")
async def shutdown(request):
    if DEBUG:
        request.app.shutdown()
        return "The server is shutting down..."
    return "Shutting down the server is prohibited during live mode!", 403

if not DEBUG:
    print("Starting webserver...")
    app.run(port=8000)

yes i think i'm only guessing. Using MPY-Jama IDE i'm connecting directly to the ESP32 but have problems to use WebREPL after rebooting device
I'm always getting `A process is already in execution"

Because of this problem i'm using this DEBUG Flag

Okay, try this. Start your device in DEBUG mode and connect to a REPL. Start the server with:

from main import app
app.run(port=8000)

At this point your REPL is going to block. Now connect to your web server in the usual manner, and trigger the shutdown route. With a working shutdown, you should be getting the REPL prompt back. If you don't, then hit Ctrl-C to interrupt the server and share any output that you get, such as a stack trace.

I got the REPL back, but when i'm trying to execute a command it says A process is already in execution. This also happens when i hard reset the ESP32.

Over night without power i was able to reconnect.

Should the shutdown route send an response before shutting down the server?

I forgot to mention, that i'm using MicroPython v1.22.1

I mentioned this before. When you get any kind of output you need to show it to me. Show me what you are doing and what error you get please. Every detail matters, show me everything.

I don't get any outputs thats the problem.

My boot.py looks like this:

# This file is executed on every boot (including wake-boot from deepsleep)
import esp
esp.osdebug(0)
# garbage collection
import gc
gc.collect()

from nic import Nic
from opener import Opener

# Global Constants
API_TOKEN = "api_key"
# NOTE: set this to True only for developing purposes to stop webserver
DEBUG = True

# connect to network
# NOTE: comment this if you don't connect to a network
nic = Nic()

# initialise modules
opener = Opener()

main.py

from microdot import Microdot

# Create webserver
app = Microdot()

@app.before_request
async def authenticate(request):
    headers = request.headers
    # Format `Authorization: Bearer <api_token>`
    if "Authorization" not in headers:
        return "Unauthorized", 401
    if "Bearer" not in headers["Authorization"]:
        return "Unauthorized", 401
    api_token = headers["Authorization"].split(" ")[1]
    if api_token != API_TOKEN:
        return "Unauthorized", 401
    
@app.get("/")
async def index(request):
    return "Hello, world!"

@app.post("/open")
async def open(request):
    # open the connected door/barrier
    opener.open(1)
    return "OK"

@app.get("/shutdown")
async def shutdown(request):
    if DEBUG:
        request.app.shutdown()
        return "The server is shutting down..."
    return "Shutting down the server is prohibited during live mode!", 403


def run():
    print("Starting webserver...")
    app.run(port=8000)

After Starting the Webserver with run(), hitting CTRL-C and trying to use run() again - i get the message above. Also Hard Resetting is not working.

MicroPython >>> run()

Starting webserver...

* The program has been interrupted!
ets Jun  8 2016 00:22:57

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:4728
load:0x40078000,len:14888
load:0x40080400,len:3368
entry 0x400805cc
�[0;32mI (778) system_api: Base MAC address is not set�[0m
�[0;32mI (788) system_api: read default base MAC address from EFUSE�[0m
�[0;32mI (808) esp_eth.netif.netif_glue: 80:64:6f:e9:c2:d3�[0m
�[0;32mI (808) esp_eth.netif.netif_glue: ethernet attached to netif�[0m
�[0;32mI (2508) ethernet: Ethernet Started�[0m
�[0;32mConnIectin g to (netwo2rk...508) ethernet: Ethernet Link Up�[0m

�[0;32mI (3508) ethernet: Ethernet Got IP�[0m
�[0;32mI (3508) esp_netif_handlers: eth ip: 192.168.178.44, mask: 255.255.255.0, gw: 192.168.178.1n�[0m
etwork config:  ('192.168.178.44', '255.255.255.0', '192.168.178.1', '192.168.178.1')
MicroPython v1.22.1 on 2024-01-05; Generic ESP32 module with ESP32
Type "help()" for more information.

* ESP32 has been reset!

MicroPython >>> run()

I'm not really sure what to make of this. The only reference I have found for the error is an issue for the IDE that you are using: jczic/ESP32-MPY-Jama#46. It's still open, and nobody who commented there seems to know what this error is about. Maybe try a different IDE?

As far as I can see, the problem that you have is not related to the Microdot shutdown, which appears to work well.

Ok.. it seems like a problem with MPY-Jama - VS Code with PyMakr extension works fine.. Sorry to bother you.

One last question: shouldn't the shutdown endpoint send an response?

I would expect a response to be returned, yes. But it's hard to tell what happens when immediately after the request the whole server is closed. Maybe the response is still sitting in a buffer when the server goes away. You have to consider that this is a debugging option, servers are not designed to ever go away.

Just because I'm curious, what happens from the client's point of view when the shutdown is invoked? Does the client block forever? Does it get an error response?

Postman tries forever to complete the request :) so i think i will simply remove the shutdown option - using PyMakr works fine, so i can simply stop and reset the device