* Removed unnecessary memmove from chunked response generation. * Added simple template processor to AsyncFileResponse. Unzipped files in SPIFFS, Streams, PROGMEM strings, callback/chunked responses may have template placeholders like %TEMPLATE_VAR% inside. If callback is specified in Async...Response constructor call, it will be used to replace these with actual strings. The prototype of callback is String(const String&), i.e. it gets variable name and returns its value. Template variables' delimiter is currently percent sign ('%'). Maximal placeholder length is 32 chars (chosen somewhat arbitrarily, it may be stored on stack during processing). It is not guaranteed that placeholders longer than that will be processed. Signed-off-by: Alexandr Zarubkin <me21@yandex.ru>
This commit is contained in:
parent
2540507664
commit
e6c432e563
150
README.md
150
README.md
|
@ -17,6 +17,7 @@ To use this library you might need to have the latest git versions of [ESP8266](
|
||||||
- [Rewrites and how do they work](#rewrites-and-how-do-they-work)
|
- [Rewrites and how do they work](#rewrites-and-how-do-they-work)
|
||||||
- [Handlers and how do they work](#handlers-and-how-do-they-work)
|
- [Handlers and how do they work](#handlers-and-how-do-they-work)
|
||||||
- [Responses and how do they work](#responses-and-how-do-they-work)
|
- [Responses and how do they work](#responses-and-how-do-they-work)
|
||||||
|
- [Template processing](#template-processing)
|
||||||
- [Libraries and projects that use AsyncWebServer](#libraries-and-projects-that-use-asyncwebserver)
|
- [Libraries and projects that use AsyncWebServer](#libraries-and-projects-that-use-asyncwebserver)
|
||||||
- [Request Variables](#request-variables)
|
- [Request Variables](#request-variables)
|
||||||
- [Common Variables](#common-variables)
|
- [Common Variables](#common-variables)
|
||||||
|
@ -32,14 +33,20 @@ To use this library you might need to have the latest git versions of [ESP8266](
|
||||||
- [Basic response with string content and extra headers](#basic-response-with-string-content-and-extra-headers)
|
- [Basic response with string content and extra headers](#basic-response-with-string-content-and-extra-headers)
|
||||||
- [Send large webpage from PROGMEM](#send-large-webpage-from-progmem)
|
- [Send large webpage from PROGMEM](#send-large-webpage-from-progmem)
|
||||||
- [Send large webpage from PROGMEM and extra headers](#send-large-webpage-from-progmem-and-extra-headers)
|
- [Send large webpage from PROGMEM and extra headers](#send-large-webpage-from-progmem-and-extra-headers)
|
||||||
|
- [Send large webpage from PROGMEM containing templates](#send-large-webpage-from-progmem-containing-templates)
|
||||||
|
- [Send large webpage from PROGMEM containing templates and extra headers](#send-large-webpage-from-progmem-containing-templates-and-extra-headers)
|
||||||
- [Send binary content from PROGMEM](#send-binary-content-from-progmem)
|
- [Send binary content from PROGMEM](#send-binary-content-from-progmem)
|
||||||
- [Respond with content coming from a Stream](#respond-with-content-coming-from-a-stream)
|
- [Respond with content coming from a Stream](#respond-with-content-coming-from-a-stream)
|
||||||
- [Respond with content coming from a Stream and extra headers](#respond-with-content-coming-from-a-stream-and-extra-headers)
|
- [Respond with content coming from a Stream and extra headers](#respond-with-content-coming-from-a-stream-and-extra-headers)
|
||||||
|
- [Respond with content coming from a Stream containing templates](#respond-with-content-coming-from-a-stream-containing-templates)
|
||||||
|
- [Respond with content coming from a Stream containing templates and extra headers](#respond-with-content-coming-from-a-stream-containing-templates-and-extra-headers)
|
||||||
- [Respond with content coming from a File](#respond-with-content-coming-from-a-file)
|
- [Respond with content coming from a File](#respond-with-content-coming-from-a-file)
|
||||||
- [Respond with content coming from a File and extra headers](#respond-with-content-coming-from-a-file-and-extra-headers)
|
- [Respond with content coming from a File and extra headers](#respond-with-content-coming-from-a-file-and-extra-headers)
|
||||||
- [Respond with content coming from a File containing templates](#respond-with-content-coming-from-a-file-containing-templates)
|
- [Respond with content coming from a File containing templates](#respond-with-content-coming-from-a-file-containing-templates)
|
||||||
- [Respond with content using a callback](#respond-with-content-using-a-callback)
|
- [Respond with content using a callback](#respond-with-content-using-a-callback)
|
||||||
- [Respond with content using a callback and extra headers](#respond-with-content-using-a-callback-and-extra-headers)
|
- [Respond with content using a callback and extra headers](#respond-with-content-using-a-callback-and-extra-headers)
|
||||||
|
- [Respond with content using a callback containing templates](#respond-with-content-using-a-callback-containing-templates)
|
||||||
|
- [Respond with content using a callback containing templates and extra headers](#respond-with-content-using-a-callback-containing-templates-and-extra-headers)
|
||||||
- [Chunked Response](#chunked-response)
|
- [Chunked Response](#chunked-response)
|
||||||
- [Print to response](#print-to-response)
|
- [Print to response](#print-to-response)
|
||||||
- [ArduinoJson Basic Response](#arduinojson-basic-response)
|
- [ArduinoJson Basic Response](#arduinojson-basic-response)
|
||||||
|
@ -81,6 +88,7 @@ To use this library you might need to have the latest git versions of [ESP8266](
|
||||||
- Async EventSource (Server-Sent Events) plugin to send events to the browser
|
- Async EventSource (Server-Sent Events) plugin to send events to the browser
|
||||||
- URL Rewrite plugin for conditional and permanent url rewrites
|
- URL Rewrite plugin for conditional and permanent url rewrites
|
||||||
- ServeStatic plugin that supports cache, Last-Modified, default index and more
|
- ServeStatic plugin that supports cache, Last-Modified, default index and more
|
||||||
|
- Simple template processing engine to handle templates
|
||||||
|
|
||||||
## Important things to remember
|
## Important things to remember
|
||||||
- This is fully asynchronous server and as such does not run on the loop thread.
|
- This is fully asynchronous server and as such does not run on the loop thread.
|
||||||
|
@ -146,6 +154,15 @@ To use this library you might need to have the latest git versions of [ESP8266](
|
||||||
- Responding asynchronously is probably the most difficult thing for most to understand
|
- Responding asynchronously is probably the most difficult thing for most to understand
|
||||||
- Many different options exist for the user to make responding a background task
|
- Many different options exist for the user to make responding a background task
|
||||||
|
|
||||||
|
### Template processing
|
||||||
|
- ESPAsyncWebserver contains simple template processing engine.
|
||||||
|
- Template processing can be added to most response types.
|
||||||
|
- Currently it supports only replacing template placeholders with actual values. No conditional processing, cycles, etc.
|
||||||
|
- Placeholders are delimited with ```%``` symbols. Like this: ```%TEMPLATE_PLACEHOLDER%```.
|
||||||
|
- It works by extracting placeholder name from response text and passing it to user provided function which should return actual value to be used instead of placeholder.
|
||||||
|
- Since it's user provided function, it is possible for library users to implement conditional processing and cycles themselves.
|
||||||
|
- Since it's impossible to know the actual response size after template processing step in advance (and, therefore, to include it in response headers), the response becomes [chunked](#chunked-response).
|
||||||
|
|
||||||
## Libraries and projects that use AsyncWebServer
|
## Libraries and projects that use AsyncWebServer
|
||||||
- [WebSocketToSerial](https://github.com/hallard/WebSocketToSerial) - Debug serial devices through the web browser
|
- [WebSocketToSerial](https://github.com/hallard/WebSocketToSerial) - Debug serial devices through the web browser
|
||||||
- [Sattrack](https://github.com/Hopperpop/Sattrack) - Track the ISS with ESP8266
|
- [Sattrack](https://github.com/Hopperpop/Sattrack) - Track the ISS with ESP8266
|
||||||
|
@ -313,6 +330,38 @@ response->addHeader("Server","ESP Async Web Server");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Send large webpage from PROGMEM containing templates
|
||||||
|
```cpp
|
||||||
|
String processor(const String& var)
|
||||||
|
{
|
||||||
|
if(var == "HELLO_FROM_TEMPLATE")
|
||||||
|
return F("Hello world!");
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
const char index_html[] PROGMEM = "..."; // large char array, tested with 14k
|
||||||
|
request->send_P(200, "text/html", index_html, processor);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Send large webpage from PROGMEM containing templates and extra headers
|
||||||
|
```cpp
|
||||||
|
String processor(const String& var)
|
||||||
|
{
|
||||||
|
if(var == "HELLO_FROM_TEMPLATE")
|
||||||
|
return F("Hello world!");
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
const char index_html[] PROGMEM = "..."; // large char array, tested with 14k
|
||||||
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html, processor);
|
||||||
|
response->addHeader("Server","ESP Async Web Server");
|
||||||
|
request->send(response);
|
||||||
|
```
|
||||||
|
|
||||||
### Send binary content from PROGMEM
|
### Send binary content from PROGMEM
|
||||||
```cpp
|
```cpp
|
||||||
|
|
||||||
|
@ -386,6 +435,38 @@ response->addHeader("Server","ESP Async Web Server");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Respond with content coming from a Stream containing templates
|
||||||
|
```cpp
|
||||||
|
String processor(const String& var)
|
||||||
|
{
|
||||||
|
if(var == "HELLO_FROM_TEMPLATE")
|
||||||
|
return F("Hello world!");
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
//read 12 bytes from Serial and send them as Content Type text/plain
|
||||||
|
request->send(Serial, "text/plain", 12, processor);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Respond with content coming from a Stream containing templates and extra headers
|
||||||
|
```cpp
|
||||||
|
String processor(const String& var)
|
||||||
|
{
|
||||||
|
if(var == "HELLO_FROM_TEMPLATE")
|
||||||
|
return F("Hello world!");
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
//read 12 bytes from Serial and send them as Content Type text/plain
|
||||||
|
AsyncWebServerResponse *response = request->beginResponse(Serial, "text/plain", 12, processor);
|
||||||
|
response->addHeader("Server","ESP Async Web Server");
|
||||||
|
request->send(response);
|
||||||
|
```
|
||||||
|
|
||||||
### Respond with content coming from a File
|
### Respond with content coming from a File
|
||||||
```cpp
|
```cpp
|
||||||
//Send index.htm with default content type
|
//Send index.htm with default content type
|
||||||
|
@ -464,6 +545,52 @@ response->addHeader("Server","ESP Async Web Server");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Respond with content using a callback containing templates
|
||||||
|
```cpp
|
||||||
|
String processor(const String& var)
|
||||||
|
{
|
||||||
|
if(var == "HELLO_FROM_TEMPLATE")
|
||||||
|
return F("Hello world!");
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
//send 128 bytes as plain text
|
||||||
|
request->send("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
||||||
|
//Write up to "maxLen" bytes into "buffer" and return the amount written.
|
||||||
|
//index equals the amount of bytes that have been already sent
|
||||||
|
//You will not be asked for more bytes once the content length has been reached.
|
||||||
|
//Keep in mind that you can not delay or yield waiting for more data!
|
||||||
|
//Send what you currently have and you will be asked for more again
|
||||||
|
return mySource.read(buffer, maxLen);
|
||||||
|
}, processor);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Respond with content using a callback containing templates and extra headers
|
||||||
|
```cpp
|
||||||
|
String processor(const String& var)
|
||||||
|
{
|
||||||
|
if(var == "HELLO_FROM_TEMPLATE")
|
||||||
|
return F("Hello world!");
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
//send 128 bytes as plain text
|
||||||
|
AsyncWebServerResponse *response = request->beginResponse("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
||||||
|
//Write up to "maxLen" bytes into "buffer" and return the amount written.
|
||||||
|
//index equals the amount of bytes that have been already sent
|
||||||
|
//You will not be asked for more bytes once the content length has been reached.
|
||||||
|
//Keep in mind that you can not delay or yield waiting for more data!
|
||||||
|
//Send what you currently have and you will be asked for more again
|
||||||
|
return mySource.read(buffer, maxLen);
|
||||||
|
}, processor);
|
||||||
|
response->addHeader("Server","ESP Async Web Server");
|
||||||
|
request->send(response);
|
||||||
|
```
|
||||||
|
|
||||||
### Chunked Response
|
### Chunked Response
|
||||||
Used when content length is unknown. Works best if the client supports HTTP/1.1
|
Used when content length is unknown. Works best if the client supports HTTP/1.1
|
||||||
```cpp
|
```cpp
|
||||||
|
@ -478,6 +605,29 @@ response->addHeader("Server","ESP Async Web Server");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Chunked Response containing templates
|
||||||
|
Used when content length is unknown. Works best if the client supports HTTP/1.1
|
||||||
|
```cpp
|
||||||
|
String processor(const String& var)
|
||||||
|
{
|
||||||
|
if(var == "HELLO_FROM_TEMPLATE")
|
||||||
|
return F("Hello world!");
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
||||||
|
//Write up to "maxLen" bytes into "buffer" and return the amount written.
|
||||||
|
//index equals the amount of bytes that have been already sent
|
||||||
|
//You will be asked for more data until 0 is returned
|
||||||
|
//Keep in mind that you can not delay or yield waiting for more data!
|
||||||
|
return mySource.read(buffer, maxLen);
|
||||||
|
}, processor);
|
||||||
|
response->addHeader("Server","ESP Async Web Server");
|
||||||
|
request->send(response);
|
||||||
|
```
|
||||||
|
|
||||||
### Print to response
|
### Print to response
|
||||||
```cpp
|
```cpp
|
||||||
AsyncResponseStream *response = request->beginResponseStream("text/html");
|
AsyncResponseStream *response = request->beginResponseStream("text/html");
|
||||||
|
|
|
@ -308,7 +308,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||||
if(_chunked){
|
if(_chunked){
|
||||||
// HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
|
// HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
|
||||||
// See RFC2616 sections 2, 3.6.1.
|
// See RFC2616 sections 2, 3.6.1.
|
||||||
readLen = _fillBuffer(buf+headLen+6, outLen - 8);
|
readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8);
|
||||||
outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
|
outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
|
||||||
while(outLen < headLen + 4) buf[outLen++] = ' ';
|
while(outLen < headLen + 4) buf[outLen++] = ' ';
|
||||||
buf[outLen++] = '\r';
|
buf[outLen++] = '\r';
|
||||||
|
|
Loading…
Reference in New Issue