adafruit / adafruit/Adafruit_CircuitPython_HTTPServer

Pico W need too much time for receiving request data in random cases

Open
#46 13 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
52
Forks
30
PR merge metrics
No merged PRs in 30d

Description

I have a for example a small web page with repeated ajax requests. Page is sending one request per second and server on Pico W needs different times for processing same request. In my case it varies between 40 ms and 250 ms.

I expected the time for processing the request to be approximately in the range of 40 to 80ms.

Connection to Pico is fast and when i added some debug prints to poll() function in server.py, then i found, that the bottleneck is function - ```self._receive_header_bytes(conn)```

```
def poll(self):
try:
conn, client_address = self._sock.accept()

with conn:
conn.settimeout(self._timeout)

start_msecs = supervisor.ticks_ms()

# Receiving data until empty line
header_bytes = self._receive_header_bytes(conn)

print(f"time to receive header bytes: {supervisor.ticks_ms() - start_msecs} ms")

# Return if no data received
```

example of printed output (it's not the same sample as shown in image)
```
time to receive header bytes: 157 ms
time to receive header bytes: 1 ms
time to receive header bytes: 152 ms
time to receive header bytes: 1 ms
time to receive header bytes: 129 ms
time to receive header bytes: 1 ms
time to receive header bytes: 12 ms
time to receive header bytes: 95 ms
time to receive header bytes: 210 ms
time to receive header bytes: 1 ms
time to receive header bytes: 12 ms
time to receive header bytes: 1 ms
time to receive header bytes: 46 ms
time to receive header bytes: 1 ms
time to receive header bytes: 167 ms
time to receive header bytes: 188 ms
time to receive header bytes: 199 ms
time to receive header bytes: 208 ms
```
![image1](https://user-images.githubusercontent.com/11430650/230475235-93d0b29a-24a9-4ec2-9c82-f3b1acf84c8e.png)
![image2](https://user-images.githubusercontent.com/11430650/230475253-345f1835-8eeb-4694-abe7-3c6f376fdf9c.png)

example code:
```
import os
import time
import ipaddress
import wifi
import socketpool

import microcontroller

from adafruit_httpserver.server import HTTPServer
from adafruit_httpserver.request import HTTPRequest
from adafruit_httpserver.response import HTTPResponse
from adafruit_httpserver.methods import HTTPMethod
from adafruit_httpserver.mime_type import MIMEType
from adafruit_httpserver.status import CommonHTTPStatus

# set static IP address
ipv4 = ipaddress.IPv4Address("192.168.88.99")
netmask = ipaddress.IPv4Address("255.255.255.0")
gateway = ipaddress.IPv4Address("192.168.88.1")

wifi.radio.set_ipv4_address(ipv4=ipv4, netmask=netmask, gateway=gateway)
# connect to your SSID
wifi.radio.connect(
os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")
)

print("Connected to WiFi")
pool = socketpool.SocketPool(wifi.radio)
server = HTTPServer(pool)

@server.route("/")
def base(request: HTTPRequest): # pylint: disable=unused-argument
with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:
response.send("""



const ajax = function (opt, cb) {
const headers = opt.headers || {},
body = opt.body || "",
xhr = new XMLHttpRequest();

xhr.open(opt.method || (body ? "POST" : "GET"), opt.url || "/", true);
xhr.ontimeout = xhr.onabort = xhr.onerror = function (e) {
console.error("XHR error: ", e, "opt: ", opt);
};
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
cb(xhr.status, xhr.response || xhr.responseText);
}
};
headers["Content-Type"] = headers["Content-Type"] || "application/x-www-form-urlencoded";
for (const f in headers) {
xhr.setRequestHeader(f, headers[f]);
}
xhr.send(body);
};

function ajaxRequest(request, interval) {
const ax = function () {
ajax(request, function (code, response) {
if (code == 200 && response) {
console.log("response", response);
}
if (interval) {
setTimeout(ax, interval);
}
});
};
ax();
}

data = {"id":"sw1","value":true}
intervalMs = 1000

ajaxRequest({ url: "/api", method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }, intervalMs);




sapmle page with ajax request every 1 second

""")

@server.route("/api", HTTPMethod.POST)
def base(request: HTTPRequest):
print("api request", request.body)
with HTTPResponse(request, content_type=MIMEType.TYPE_JSON) as response:
response.send('{"status": "ok"}')

print("starting server..")
# startup the server
try:
server.start(str(wifi.radio.ipv4_address))
print("Listening on http://%s:80" % wifi.radio.ipv4_address)

# if the server fails to begin, restart the pico w
except OSError:
time.sleep(5)
print("restarting..")
microcontroller.reset()

while True:
try:
server.poll()

except Exception as e:
print(e)
continue
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in server.py at HTTPServer.poll() and the self._receive_header_bytes(conn) call, then reproduce the example's repeated POST requests on a Pico W while measuring header-receive times. Trace why identical requests vary from roughly 40 to 250 ms; done means the cause is documented and the request latency is consistently near the expected range.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.