Re: [lwip-users] Slow answer from web server with asynchronous read func
From:
Info
Subject:
Re: [lwip-users] Slow answer from web server with asynchronous read functionality
Date:
Tue, 8 Oct 2024 17:00:25 +0200
User-agent:
Mozilla Thunderbird
I believe I found the reason of the problem. The following happens.
If a HTTP request is delayed and the answer is prepared in a file
and it's content size is only few bytes (that is the scenario, which
I use mostly), then the http_send() in the httpd.c
return a wrong value.
The callback function which used from the httpd webserver is http_continue().
This function contains the following code (I removed the debug code
for better readability)
struct http_state *hs = (struct http_state *)connection;
LWIP_ASSERT_CORE_LOCKED();
if (hs && (hs->pcb) && (hs->handle)) {
if (http_send(hs->pcb, hs)) {
/* If we wrote anything to be sent, go ahead and send it now.
*/
altcp_output(hs->pcb);
}
}
If http_send() is called and the file is read and could be read
within this call then somewhere in this function the following
decision will be processed
if ((hs->left == 0) && (fs_bytes_left(hs->handle)
<= 0)) {
/* We reached the end of the file so this request is done.
* This adds the FIN flag right into the last data segment. */
LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
http_eof(pcb, hs);
return 0;
}
And if this decision is true, because all data from file could be
read. Now http_send() returns 0, which means back in http_continue()
the call of altcp_output() (which is in fact tcp_output()
) is not done. That means any pending data to send will happen
if the TCP timer elapsed (that's why I see so long answer delays). I
now changed the return value of the above code from "return 0"
to "return data_to_send" (which holds the correct return
value in this case). And now I see the response times I expected.
Is my
analysis correct and this is a bug in the web server code ?
Kind regards
Roland
Am 03.10.24 um 15:39 schrieb Trampas
Stern:
First off it is recommended that the command line
parser run in a different context for each application. For
example when I run multiple command line interfaces each one
gets its own parser. This way things like history and such are
local to each interface and commands run independently. This
should solve your dead lock and be more reasonable for users.
It may require some mutexes elsewhere in your code.
I have seen latency issues with TCP and LWIP as it will
often cache responses and process them after some time period.
I mitigate this by calling the tcp_output() function to flush
the tcp_write() and decrease latency.
Does nobody have the time to give me some advise where
the problem could be ?
Any help would really be very welcome.
Kind regards
Roland
Am 17.09.24 um 09:32 schrieb Info via lwip-users:
Hi
I got a step further with my investigation. The "delayed"
answer is related to the TCP_TMR_INTERVAL, it is set to
250 ms. If I set this value to 100ms for example, the
answer from the web server is sent faster (means after
about 100 ms the answer is received).
I have attached three Wireshark capture showing the
communication of different settings:
HTTPD_answer _in_CGI_handler :
The answer file is completly created in the CGI handler
(which is more or less our answer timing reference)
HTTPD_async_read_250ms_tcp_timer :
The answer file is available after the helper task has
been running (calling tcpip_callback() at the end) and
TCP_TMR_INTERVAL is set to 250
HTTPD_async_read_100ms_tcp_timer :
The answer file is available after the helper task has
been running (calling tcpip_callback() at the end) and
TCP_TMR_INTERVAL is set to 100
Lowering TCP_TMR_INTERVAL could help, but I'm not sure if
this is the correct way ?
Maybe I missed something before calling tcpip_callback(),
which calls http_continue() (the callback function and
arguments passed by fs_read_async() ).
Any help or tips are welcome.
Kind regards,
Roland
Am 13.09.24 um 15:35 schrieb Info via lwip-users:
Hi
I'm using the web server (httpd) from the lwIP (V2.13)
with FreeRTOS. The web server is running fine so far. A
socket server is also running (in it's own task) to
implement a small command line. The web server also has
the ability to access the command line over HTTP
requests. Both the web and the socket server using the
same command line parser functionality. The parser is
protected with a semaphore, because not all commands on
the command line are fully reentrant.
Problem: If both the socket and the web server using the
parser at the same time a deadlock could occurs, that is
because the web server is running in the TCP task
context and the socket server is trying to use the TCP
task from inside the command line parser.
I enabled the LWIP_HTTPD_FS_ASYNC_READ and it's
functionality and added the required code. The CGI
handler now creates an empty response file and sends a
message to the helper task. The helper task receives the
message, calls the command line parser and initiate to
send back the answer via a tcpip_callback() call.
With this implementation, it is possible to call the
command line parser outside of the TCP task context and
not blocking the TCP task.
That works so far, but the answer time of this solution
is 10 times slower, than to call the command line parser
directly in the CGI handler. I measured the time between
the receive of the HTTP request and the call of the
callback function passed by tcpip_callback() and that is
not the problem. It is something in the lwIP that slow
down the answer.
Question:
- What could be the reason of the longer response time
(about 250ms instead of 20ms) ?
- Is it possible to let the web server (httpd) in it's
own task ? Has anyone done it already ?