Something got lost during pull request #189. Fixes #211, updates documentation. (#213)

* 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:
Alexandr Zarubkin 2017-08-31 19:44:23 +03:00 committed by Me No Dev
parent 2540507664
commit e6c432e563
2 changed files with 155 additions and 5 deletions

158
README.md
View File

@ -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)
- [Handlers and how do they work](#handlers-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)
- [Request Variables](#request-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)
- [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 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)
- [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 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 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 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 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)
- [Print to response](#print-to-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
- URL Rewrite plugin for conditional and permanent url rewrites
- ServeStatic plugin that supports cache, Last-Modified, default index and more
- Simple template processing engine to handle templates
## Important things to remember
- 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
- 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
- [WebSocketToSerial](https://github.com/hallard/WebSocketToSerial) - Debug serial devices through the web browser
- [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);
```
### 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
```cpp
@ -386,6 +435,38 @@ response->addHeader("Server","ESP Async Web Server");
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
```cpp
//Send index.htm with default content type
@ -464,6 +545,52 @@ response->addHeader("Server","ESP Async Web Server");
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
Used when content length is unknown. Works best if the client supports HTTP/1.1
```cpp
@ -478,6 +605,29 @@ response->addHeader("Server","ESP Async Web Server");
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
```cpp
AsyncResponseStream *response = request->beginResponseStream("text/html");
@ -590,7 +740,7 @@ server.serveStatic("/", SPIFFS, "/www/");
server.serveStatic("/", SPIFFS, "/www/").setDefaultFile("default.html");
```
### Serving static files with authentication
### Serving static files with authentication
```cpp
server
@ -809,8 +959,8 @@ ws.binary((uint32_t)client_id, flash_binary, 4);
//send binary to all clients
ws.binaryAll((char*)binary);
ws.binaryAll((uint8_t*)binary, (size_t)len);
//HTTP Authenticate before switch to Websocket protocol
ws.setAuthentication("user", "pass");
//HTTP Authenticate before switch to Websocket protocol
ws.setAuthentication("user", "pass");
//client methods
AsyncWebSocketClient * client;
@ -1046,7 +1196,7 @@ void setup(){
return request->requestAuthentication();
request->send(200, "text/plain", "Login Success!");
});
// Simple Firmware Update Form
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>");

View File

@ -308,7 +308,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
if(_chunked){
// HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
// 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;
while(outLen < headLen + 4) buf[outLen++] = ' ';
buf[outLen++] = '\r';