Giter Site home page Giter Site logo

espasyncwebserver's Introduction

ESPAsyncWebServer

Build Status Codacy Badge

For help and support Join the chat at https://gitter.im/me-no-dev/ESPAsyncWebServer

Async HTTP and WebSocket Server for ESP8266 Arduino

For ESP8266 it requires ESPAsyncTCP To use this library you might need to have the latest git versions of ESP8266 Arduino Core

For ESP32 it requires AsyncTCP to work To use this library you might need to have the latest git versions of ESP32 Arduino Core

Table of contents

Installation

Using PlatformIO

PlatformIO is an open source ecosystem for IoT development with cross platform build system, library manager and full support for Espressif ESP8266/ESP32 development. It works on the popular host OS: Mac OS X, Windows, Linux 32/64, Linux ARM (like Raspberry Pi, BeagleBone, CubieBoard).

  1. Install PlatformIO IDE
  2. Create new project using "PlatformIO Home > New Project"
  3. Update dev/platform to staging version:
  4. Add "ESP Async WebServer" to project using Project Configuration File platformio.ini and lib_deps option:
[env:myboard]
platform = espressif...
board = ...
framework = arduino

# using the latest stable version
lib_deps = ESP Async WebServer

# or using GIT Url (the latest development version)
lib_deps = https://github.com/me-no-dev/ESPAsyncWebServer.git
  1. Happy coding with PlatformIO!

Why should you care

  • Using asynchronous network means that you can handle more than one connection at the same time
  • You are called once the request is ready and parsed
  • When you send the response, you are immediately ready to handle other connections while the server is taking care of sending the response in the background
  • Speed is OMG
  • Easy to use API, HTTP Basic and Digest MD5 Authentication (default), ChunkedResponse
  • Easily extendible to handle any type of content
  • Supports Continue 100
  • Async WebSocket plugin offering different locations without extra servers or ports
  • 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.
  • You can not use yield or delay or any function that uses them inside the callbacks
  • The server is smart enough to know when to close the connection and free resources
  • You can not send more than one response to a single request

Principles of operation

The Async Web server

  • Listens for connections
  • Wraps the new clients into Request
  • Keeps track of clients and cleans memory
  • Manages Rewrites and apply them on the request url
  • Manages Handlers and attaches them to Requests

Request Life Cycle

  • TCP connection is received by the server
  • The connection is wrapped inside Request object
  • When the request head is received (type, url, get params, http version and host), the server goes through all Rewrites (in the order they were added) to rewrite the url and inject query parameters, next, it goes through all attached Handlers(in the order they were added) trying to find one that canHandle the given request. If none are found, the default(catch-all) handler is attached.
  • The rest of the request is received, calling the handleUpload or handleBody methods of the Handler if they are needed (POST+File/Body)
  • When the whole request is parsed, the result is given to the handleRequest method of the Handler and is ready to be responded to
  • In the handleRequest method, to the Request is attached a Response object (see below) that will serve the response data back to the client
  • When the Response is sent, the client is closed and freed from the memory

Rewrites and how do they work

  • The Rewrites are used to rewrite the request url and/or inject get parameters for a specific request url path.
  • All Rewrites are evaluated on the request in the order they have been added to the server.
  • The Rewrite will change the request url only if the request url (excluding get parameters) is fully match the rewrite url, and when the optional Filter callback return true.
  • Setting a Filter to the Rewrite enables to control when to apply the rewrite, decision can be based on request url, http version, request host/port/target host, get parameters or the request client's localIP or remoteIP.
  • Two filter callbacks are provided: ON_AP_FILTER to execute the rewrite when request is made to the AP interface, ON_STA_FILTER to execute the rewrite when request is made to the STA interface.
  • The Rewrite can specify a target url with optional get parameters, e.g. /to-url?with=params

Handlers and how do they work

  • The Handlers are used for executing specific actions to particular requests
  • One Handler instance can be attached to any request and lives together with the server
  • Setting a Filter to the Handler enables to control when to apply the handler, decision can be based on request url, http version, request host/port/target host, get parameters or the request client's localIP or remoteIP.
  • Two filter callbacks are provided: ON_AP_FILTER to execute the rewrite when request is made to the AP interface, ON_STA_FILTER to execute the rewrite when request is made to the STA interface.
  • The canHandle method is used for handler specific control on whether the requests can be handled and for declaring any interesting headers that the Request should parse. Decision can be based on request method, request url, http version, request host/port/target host and get parameters
  • Once a Handler is attached to given Request (canHandle returned true) that Handler takes care to receive any file/data upload and attach a Response once the Request has been fully parsed
  • Handlers are evaluated in the order they are attached to the server. The canHandle is called only if the Filter that was set to the Handler return true.
  • The first Handler that can handle the request is selected, not further Filter and canHandle are called.

Responses and how do they work

  • The Response objects are used to send the response data back to the client
  • The Response object lives with the Request and is freed on end or disconnect
  • Different techniques are used depending on the response type to send the data in packets returning back almost immediately and sending the next packet when this one is received. Any time in between is spent to run the user loop and handle other network packets
  • 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.

Libraries and projects that use AsyncWebServer

  • WebSocketToSerial - Debug serial devices through the web browser
  • Sattrack - Track the ISS with ESP8266
  • ESP Radio - Icecast radio based on ESP8266 and VS1053
  • VZero - the Wireless zero-config controller for volkszaehler.org
  • ESPurna - ESPurna ("spark" in Catalan) is a custom C firmware for ESP8266 based smart switches. It was originally developed with the ITead Sonoff in mind.
  • fauxmoESP - Belkin WeMo emulator library for ESP8266.
  • ESP-RFID - MFRC522 RFID Access Control Management project for ESP8266.

Request Variables

Common Variables

request->version();       // uint8_t: 0 = HTTP/1.0, 1 = HTTP/1.1
request->method();        // enum:    HTTP_GET, HTTP_POST, HTTP_DELETE, HTTP_PUT, HTTP_PATCH, HTTP_HEAD, HTTP_OPTIONS
request->url();           // String:  URL of the request (not including host, port or GET parameters)
request->host();          // String:  The requested host (can be used for virtual hosting)
request->contentType();   // String:  ContentType of the request (not avaiable in Handler::canHandle)
request->contentLength(); // size_t:  ContentLength of the request (not avaiable in Handler::canHandle)
request->multipart();     // bool:    True if the request has content type "multipart"

Headers

//List all collected headers
int headers = request->headers();
int i;
for(i=0;i<headers;i++){
  AsyncWebHeader* h = request->getHeader(i);
  Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
}

//get specific header by name
if(request->hasHeader("MyHeader")){
  AsyncWebHeader* h = request->getHeader("MyHeader");
  Serial.printf("MyHeader: %s\n", h->value().c_str());
}

//List all collected headers (Compatibility)
int headers = request->headers();
int i;
for(i=0;i<headers;i++){
  Serial.printf("HEADER[%s]: %s\n", request->headerName(i).c_str(), request->header(i).c_str());
}

//get specific header by name (Compatibility)
if(request->hasHeader("MyHeader")){
  Serial.printf("MyHeader: %s\n", request->header("MyHeader").c_str());
}

GET, POST and FILE parameters

//List all parameters
int params = request->params();
for(int i=0;i<params;i++){
  AsyncWebParameter* p = request->getParam(i);
  if(p->isFile()){ //p->isPost() is also true
    Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
  } else if(p->isPost()){
    Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
  } else {
    Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
  }
}

//Check if GET parameter exists
if(request->hasParam("download"))
  AsyncWebParameter* p = request->getParam("download");

//Check if POST (but not File) parameter exists
if(request->hasParam("download", true))
  AsyncWebParameter* p = request->getParam("download", true);

//Check if FILE was uploaded
if(request->hasParam("download", true, true))
  AsyncWebParameter* p = request->getParam("download", true, true);

//List all parameters (Compatibility)
int args = request->args();
for(int i=0;i<args;i++){
  Serial.printf("ARG[%s]: %s\n", request->argName(i).c_str(), request->arg(i).c_str());
}

//Check if parameter exists (Compatibility)
if(request->hasArg("download"))
  String arg = request->arg("download");

FILE Upload handling

void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
  if(!index){
    Serial.printf("UploadStart: %s\n", filename.c_str());
  }
  for(size_t i=0; i<len; i++){
    Serial.write(data[i]);
  }
  if(final){
    Serial.printf("UploadEnd: %s, %u B\n", filename.c_str(), index+len);
  }
}

Body data handling

void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
  if(!index){
    Serial.printf("BodyStart: %u B\n", total);
  }
  for(size_t i=0; i<len; i++){
    Serial.write(data[i]);
  }
  if(index + len == total){
    Serial.printf("BodyEnd: %u B\n", total);
  }
}

If needed, the _tempObject field on the request can be used to store a pointer to temporary data (e.g. from the body) associated with the request. If assigned, the pointer will automatically be freed along with the request.

JSON body handling with ArduinoJson

Endpoints which consume JSON can use a special handler to get ready to use JSON data in the request callback:

#include "AsyncJson.h"
#include "ArduinoJson.h"

AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint", [](AsyncWebServerRequest *request, JsonVariant &json) {
  JsonObject& jsonObj = json.as<JsonObject>();
  // ...
});
server.addHandler(handler);

Responses

Redirect to another URL

//to local url
request->redirect("/login");

//to external url
request->redirect("http://esp8266.com");

Basic response with HTTP Code

request->send(404); //Sends 404 File Not Found

Basic response with HTTP Code and extra headers

AsyncWebServerResponse *response = request->beginResponse(404); //Sends 404 File Not Found
response->addHeader("Server","ESP Async Web Server");
request->send(response);

Basic response with string content

request->send(200, "text/plain", "Hello World!");

Basic response with string content and extra headers

AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Hello World!");
response->addHeader("Server","ESP Async Web Server");
request->send(response);

Send large webpage from PROGMEM

const char index_html[] PROGMEM = "..."; // large char array, tested with 14k
request->send_P(200, "text/html", index_html);

Send large webpage from PROGMEM and extra headers

const char index_html[] PROGMEM = "..."; // large char array, tested with 14k
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html);
response->addHeader("Server","ESP Async Web Server");
request->send(response);

Send large webpage from PROGMEM containing templates

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

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

//File: favicon.ico.gz, Size: 726
#define favicon_ico_gz_len 726
const uint8_t favicon_ico_gz[] PROGMEM = {
 0x1F, 0x8B, 0x08, 0x08, 0x0B, 0x87, 0x90, 0x57, 0x00, 0x03, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6F,
 0x6E, 0x2E, 0x69, 0x63, 0x6F, 0x00, 0xCD, 0x53, 0x5F, 0x48, 0x9A, 0x51, 0x14, 0xBF, 0x62, 0x6D,
 0x86, 0x96, 0xA9, 0x64, 0xD3, 0xFE, 0xA8, 0x99, 0x65, 0x1A, 0xB4, 0x8A, 0xA8, 0x51, 0x54, 0x23,
 0xA8, 0x11, 0x49, 0x51, 0x8A, 0x34, 0x62, 0x93, 0x85, 0x31, 0x58, 0x44, 0x12, 0x45, 0x2D, 0x58,
 0xF5, 0x52, 0x41, 0x10, 0x23, 0x82, 0xA0, 0x20, 0x98, 0x2F, 0xC1, 0x26, 0xED, 0xA1, 0x20, 0x89,
 0x04, 0xD7, 0x83, 0x58, 0x20, 0x28, 0x04, 0xAB, 0xD1, 0x9B, 0x8C, 0xE5, 0xC3, 0x60, 0x32, 0x64,
 0x0E, 0x56, 0xBF, 0x9D, 0xEF, 0xF6, 0x30, 0x82, 0xED, 0xAD, 0x87, 0xDD, 0x8F, 0xF3, 0xDD, 0x8F,
 0x73, 0xCF, 0xEF, 0x9C, 0xDF, 0x39, 0xBF, 0xFB, 0x31, 0x26, 0xA2, 0x27, 0x37, 0x97, 0xD1, 0x5B,
 0xCF, 0x9E, 0x67, 0x30, 0xA6, 0x66, 0x8C, 0x99, 0xC9, 0xC8, 0x45, 0x9E, 0x6B, 0x3F, 0x5F, 0x74,
 0xA6, 0x94, 0x5E, 0xDB, 0xFF, 0xB2, 0xE6, 0xE7, 0xE7, 0xF9, 0xDE, 0xD6, 0xD6, 0x96, 0xDB, 0xD8,
 0xD8, 0x78, 0xBF, 0xA1, 0xA1, 0xC1, 0xDA, 0xDC, 0xDC, 0x2C, 0xEB, 0xED, 0xED, 0x15, 0x9B, 0xCD,
 0xE6, 0x4A, 0x83, 0xC1, 0xE0, 0x2E, 0x29, 0x29, 0x99, 0xD6, 0x6A, 0xB5, 0x4F, 0x75, 0x3A, 0x9D,
 0x61, 0x75, 0x75, 0x95, 0xB5, 0xB7, 0xB7, 0xDF, 0xC8, 0xD1, 0xD4, 0xD4, 0xF4, 0xB0, 0xBA, 0xBA,
 0xFA, 0x83, 0xD5, 0x6A, 0xFD, 0x5A, 0x5E, 0x5E, 0x9E, 0x28, 0x2D, 0x2D, 0x0D, 0x10, 0xC6, 0x4B,
 0x98, 0x78, 0x5E, 0x5E, 0xDE, 0x95, 0x42, 0xA1, 0x40, 0x4E, 0x4E, 0xCE, 0x65, 0x76, 0x76, 0xF6,
 0x47, 0xB5, 0x5A, 0x6D, 0x4F, 0x26, 0x93, 0xA2, 0xD6, 0xD6, 0x56, 0x8E, 0x6D, 0x69, 0x69, 0xD1,
 0x11, 0x36, 0x62, 0xB1, 0x58, 0x60, 0x32, 0x99, 0xA0, 0xD7, 0xEB, 0x51, 0x58, 0x58, 0x88, 0xFC,
 0xFC, 0x7C, 0x10, 0x16, 0x02, 0x56, 0x2E, 0x97, 0x43, 0x2A, 0x95, 0x42, 0x2C, 0x16, 0x23, 0x33,
 0x33, 0x33, 0xAE, 0x52, 0xA9, 0x1E, 0x64, 0x65, 0x65, 0x71, 0x7C, 0x7D, 0x7D, 0xBD, 0x93, 0xEA,
 0xFE, 0x30, 0x1A, 0x8D, 0xE8, 0xEC, 0xEC, 0xC4, 0xE2, 0xE2, 0x22, 0x6A, 0x6A, 0x6A, 0x40, 0x39,
 0x41, 0xB5, 0x38, 0x4E, 0xC8, 0x33, 0x3C, 0x3C, 0x0C, 0x87, 0xC3, 0xC1, 0x6B, 0x54, 0x54, 0x54,
 0xBC, 0xE9, 0xEB, 0xEB, 0x93, 0x5F, 0x5C, 0x5C, 0x30, 0x8A, 0x9D, 0x2E, 0x2B, 0x2B, 0xBB, 0xA2,
 0x3E, 0x41, 0xBD, 0x21, 0x1E, 0x8F, 0x63, 0x6A, 0x6A, 0x0A, 0x81, 0x40, 0x00, 0x94, 0x1B, 0x3D,
 0x3D, 0x3D, 0x42, 0x3C, 0x96, 0x96, 0x96, 0x70, 0x7E, 0x7E, 0x8E, 0xE3, 0xE3, 0x63, 0xF8, 0xFD,
 0xFE, 0xB4, 0xD7, 0xEB, 0xF5, 0x8F, 0x8F, 0x8F, 0x5B, 0x68, 0x5E, 0x6F, 0x05, 0xCE, 0xB4, 0xE3,
 0xE8, 0xE8, 0x08, 0x27, 0x27, 0x27, 0xD8, 0xDF, 0xDF, 0xC7, 0xD9, 0xD9, 0x19, 0x6C, 0x36, 0x1B,
 0x36, 0x36, 0x36, 0x38, 0x9F, 0x85, 0x85, 0x05, 0xAC, 0xAF, 0xAF, 0x23, 0x1A, 0x8D, 0x22, 0x91,
 0x48, 0x20, 0x16, 0x8B, 0xFD, 0xDA, 0xDA, 0xDA, 0x7A, 0x41, 0x33, 0x7E, 0x57, 0x50, 0x50, 0x80,
 0x89, 0x89, 0x09, 0x84, 0xC3, 0x61, 0x6C, 0x6F, 0x6F, 0x23, 0x12, 0x89, 0xE0, 0xE0, 0xE0, 0x00,
 0x43, 0x43, 0x43, 0x58, 0x5E, 0x5E, 0xE6, 0x9C, 0x7D, 0x3E, 0x1F, 0x46, 0x47, 0x47, 0x79, 0xBE,
 0xBD, 0xBD, 0x3D, 0xE1, 0x3C, 0x1D, 0x0C, 0x06, 0x9F, 0x10, 0xB7, 0xC7, 0x84, 0x4F, 0xF6, 0xF7,
 0xF7, 0x63, 0x60, 0x60, 0x00, 0x83, 0x83, 0x83, 0x18, 0x19, 0x19, 0xC1, 0xDC, 0xDC, 0x1C, 0x8F,
 0x17, 0x7C, 0xA4, 0x27, 0xE7, 0x34, 0x39, 0x39, 0x89, 0x9D, 0x9D, 0x1D, 0x6E, 0x54, 0xE3, 0x13,
 0xE5, 0x34, 0x11, 0x37, 0x49, 0x51, 0x51, 0xD1, 0x4B, 0xA5, 0x52, 0xF9, 0x45, 0x26, 0x93, 0x5D,
 0x0A, 0xF3, 0x92, 0x48, 0x24, 0xA0, 0x6F, 0x14, 0x17, 0x17, 0xA3, 0xB6, 0xB6, 0x16, 0x5D, 0x5D,
 0x5D, 0x7C, 0x1E, 0xBB, 0xBB, 0xBB, 0x9C, 0xD7, 0xE1, 0xE1, 0x21, 0x42, 0xA1, 0xD0, 0x6B, 0xD2,
 0x45, 0x4C, 0x33, 0x12, 0x34, 0xCC, 0xA0, 0x19, 0x54, 0x92, 0x56, 0x0E, 0xD2, 0xD9, 0x43, 0xF8,
 0xCF, 0x82, 0x56, 0xC2, 0xDC, 0xEB, 0xEA, 0xEA, 0x38, 0x7E, 0x6C, 0x6C, 0x4C, 0xE0, 0xFE, 0x9D,
 0xB8, 0xBF, 0xA7, 0xFA, 0xAF, 0x56, 0x56, 0x56, 0xEE, 0x6D, 0x6E, 0x6E, 0xDE, 0xB8, 0x47, 0x55,
 0x55, 0x55, 0x6C, 0x66, 0x66, 0x46, 0x44, 0xDA, 0x3B, 0x34, 0x1A, 0x4D, 0x94, 0xB0, 0x3F, 0x09,
 0x7B, 0x45, 0xBD, 0xA5, 0x5D, 0x2E, 0x57, 0x8C, 0x7A, 0x73, 0xD9, 0xED, 0xF6, 0x3B, 0x84, 0xFF,
 0xE7, 0x7D, 0xA6, 0x3A, 0x2C, 0x95, 0x4A, 0xB1, 0x8E, 0x8E, 0x0E, 0x6D, 0x77, 0x77, 0xB7, 0xCD,
 0xE9, 0x74, 0x3E, 0x73, 0xBB, 0xDD, 0x8F, 0x3C, 0x1E, 0x8F, 0xE6, 0xF4, 0xF4, 0x94, 0xAD, 0xAD,
 0xAD, 0xDD, 0xDE, 0xCF, 0x73, 0x0B, 0x0B, 0xB8, 0xB6, 0xE0, 0x5D, 0xC6, 0x66, 0xC5, 0xE4, 0x10,
 0x4C, 0xF4, 0xF7, 0xD8, 0x59, 0xF2, 0x7F, 0xA3, 0xB8, 0xB4, 0xFC, 0x0F, 0xEE, 0x37, 0x70, 0xEC,
 0x16, 0x4A, 0x7E, 0x04, 0x00, 0x00
};

AsyncWebServerResponse *response = request->beginResponse_P(200, "image/x-icon", favicon_ico_gz, favicon_ico_gz_len);
response->addHeader("Content-Encoding", "gzip");
request->send(response);

Respond with content coming from a Stream

//read 12 bytes from Serial and send them as Content Type text/plain
request->send(Serial, "text/plain", 12);

Respond with content coming from a Stream and extra headers

//read 12 bytes from Serial and send them as Content Type text/plain
AsyncWebServerResponse *response = request->beginResponse(Serial, "text/plain", 12);
response->addHeader("Server","ESP Async Web Server");
request->send(response);

Respond with content coming from a Stream containing templates

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

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

//Send index.htm with default content type
request->send(SPIFFS, "/index.htm");

//Send index.htm as text
request->send(SPIFFS, "/index.htm", "text/plain");

//Download index.htm
request->send(SPIFFS, "/index.htm", String(), true);

Respond with content coming from a File and extra headers

//Send index.htm with default content type
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm");

//Send index.htm as text
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm", "text/plain");

//Download index.htm
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm", String(), true);

response->addHeader("Server","ESP Async Web Server");
request->send(response);

Respond with content coming from a File containing templates

Internally uses Chunked Response.

Index.htm contents:

%HELLO_FROM_TEMPLATE%

Somewhere in source files:

String processor(const String& var)
{
  if(var == "HELLO_FROM_TEMPLATE")
    return F("Hello world!");
  return String();
}

// ...

//Send index.htm with template processor function
request->send(SPIFFS, "/index.htm", String(), false, processor);

Respond with content using a callback

//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);
});

Respond with content using a callback and extra headers

//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);
});
response->addHeader("Server","ESP Async Web Server");
request->send(response);

Respond with content using a callback containing templates

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

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

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);
});
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

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

AsyncResponseStream *response = request->beginResponseStream("text/html");
response->addHeader("Server","ESP Async Web Server");
response->printf("<!DOCTYPE html><html><head><title>Webpage at %s</title></head><body>", request->url().c_str());

response->print("<h2>Hello ");
response->print(request->client()->remoteIP());
response->print("</h2>");

response->print("<h3>General</h3>");
response->print("<ul>");
response->printf("<li>Version: HTTP/1.%u</li>", request->version());
response->printf("<li>Method: %s</li>", request->methodToString());
response->printf("<li>URL: %s</li>", request->url().c_str());
response->printf("<li>Host: %s</li>", request->host().c_str());
response->printf("<li>ContentType: %s</li>", request->contentType().c_str());
response->printf("<li>ContentLength: %u</li>", request->contentLength());
response->printf("<li>Multipart: %s</li>", request->multipart()?"true":"false");
response->print("</ul>");

response->print("<h3>Headers</h3>");
response->print("<ul>");
int headers = request->headers();
for(int i=0;i<headers;i++){
  AsyncWebHeader* h = request->getHeader(i);
  response->printf("<li>%s: %s</li>", h->name().c_str(), h->value().c_str());
}
response->print("</ul>");

response->print("<h3>Parameters</h3>");
response->print("<ul>");
int params = request->params();
for(int i=0;i<params;i++){
  AsyncWebParameter* p = request->getParam(i);
  if(p->isFile()){
    response->printf("<li>FILE[%s]: %s, size: %u</li>", p->name().c_str(), p->value().c_str(), p->size());
  } else if(p->isPost()){
    response->printf("<li>POST[%s]: %s</li>", p->name().c_str(), p->value().c_str());
  } else {
    response->printf("<li>GET[%s]: %s</li>", p->name().c_str(), p->value().c_str());
  }
}
response->print("</ul>");

response->print("</body></html>");
//send the response last
request->send(response);

ArduinoJson Basic Response

This way of sending Json is great for when the result is below 4KB

#include "AsyncJson.h"
#include "ArduinoJson.h"


AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonBuffer jsonBuffer;
JsonObject &root = jsonBuffer.createObject();
root["heap"] = ESP.getFreeHeap();
root["ssid"] = WiFi.SSID();
root.printTo(*response);
request->send(response);

ArduinoJson Advanced Response

This response can handle really large Json objects (tested to 40KB) There isn't any noticeable speed decrease for small results with the method above Since ArduinoJson does not allow reading parts of the string, the whole Json has to be passed every time a chunks needs to be sent, which shows speed decrease proportional to the resulting json packets

#include "AsyncJson.h"
#include "ArduinoJson.h"


AsyncJsonResponse * response = new AsyncJsonResponse();
response->addHeader("Server","ESP Async Web Server");
JsonObject& root = response->getRoot();
root["heap"] = ESP.getFreeHeap();
root["ssid"] = WiFi.SSID();
response->setLength();
request->send(response);

Serving static files

In addition to serving files from SPIFFS as described above, the server provide a dedicated handler that optimize the performance of serving files from SPIFFS - AsyncStaticWebHandler. Use server.serveStatic() function to initialize and add a new instance of AsyncStaticWebHandler to the server. The Handler will not handle the request if the file does not exists, e.g. the server will continue to look for another handler that can handle the request. Notice that you can chain setter functions to setup the handler, or keep a pointer to change it at a later time.

Serving specific file by name

// Serve the file "/www/page.htm" when request url is "/page.htm"
server.serveStatic("/page.htm", SPIFFS, "/www/page.htm");

Serving files in directory

To serve files in a directory, the path to the files should specify a directory in SPIFFS and ends with "/".

// Serve files in directory "/www/" when request url starts with "/"
// Request to the root or none existing files will try to server the defualt
// file name "index.htm" if exists
server.serveStatic("/", SPIFFS, "/www/");

// Server with different default file
server.serveStatic("/", SPIFFS, "/www/").setDefaultFile("default.html");

Serving static files with authentication

server
    .serveStatic("/", SPIFFS, "/www/")
    .setDefaultFile("default.html")
    .setAuthentication("user", "pass");

Specifying Cache-Control header

It is possible to specify Cache-Control header value to reduce the number of calls to the server once the client loaded the files. For more information on Cache-Control values see Cache-Control

// Cache responses for 10 minutes (600 seconds)
server.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=600");

//*** Change Cache-Control after server setup ***

// During setup - keep a pointer to the handler
AsyncStaticWebHandler* handler = &server.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=600");

// At a later event - change Cache-Control
handler->setCacheControl("max-age=30");

Specifying Date-Modified header

It is possible to specify Date-Modified header to enable the server to return Not-Modified (304) response for requests with "If-Modified-Since" header with the same value, instead of responding with the actual file content.

// Update the date modified string every time files are updated
server.serveStatic("/", SPIFFS, "/www/").setLastModified("Mon, 20 Jun 2016 14:00:00 GMT");

//*** Chage last modified value at a later stage ***

// During setup - read last modified value from config or EEPROM
String date_modified = loadDateModified();
AsyncStaticWebHandler* handler = &server.serveStatic("/", SPIFFS, "/www/");
handler->setLastModified(date_modified);

// At a later event when files are updated
String date_modified = getNewDateModfied();
saveDateModified(date_modified); // Save for next reset
handler->setLastModified(date_modified);

Specifying Template Processor callback

It is possible to specify template processor for static files. For information on template processor see Respond with content coming from a File containing templates.

String processor(const String& var)
{
  if(var == "HELLO_FROM_TEMPLATE")
    return F("Hello world!");
  return String();
}

// ...

server.serveStatic("/", SPIFFS, "/www/").setTemplateProcessor(processor);

Param Rewrite With Matching

It is possible to rewrite the request url with parameter matchg. Here is an example with one parameter: Rewrite for example "/radio/{frequence}" -> "/radio?f={frequence}"

class OneParamRewrite : public AsyncWebRewrite
{
  protected:
    String _urlPrefix;
    int _paramIndex;
    String _paramsBackup;

  public:
  OneParamRewrite(const char* from, const char* to)
    : AsyncWebRewrite(from, to) {

      _paramIndex = _from.indexOf('{');

      if( _paramIndex >=0 && _from.endsWith("}")) {
        _urlPrefix = _from.substring(0, _paramIndex);
        int index = _params.indexOf('{');
        if(index >= 0) {
          _params = _params.substring(0, index);
        }
      } else {
        _urlPrefix = _from;
      }
      _paramsBackup = _params;
  }

  bool match(AsyncWebServerRequest *request) override {
    if(request->url().startsWith(_urlPrefix)) {
      if(_paramIndex >= 0) {
        _params = _paramsBackup + request->url().substring(_paramIndex);
      } else {
        _params = _paramsBackup;
      }
    return true;

    } else {
      return false;
    }
  }
};

Usage:

  server.addRewrite( new OneParamRewrite("/radio/{frequence}", "/radio?f={frequence}") );

Using filters

Filters can be set to Rewrite or Handler in order to control when to apply the rewrite and consider the handler. A filter is a callback function that evaluates the request and return a boolean true to include the item or false to exclude it. Two filter callback are provided for convince:

  • ON_STA_FILTER - return true when requests are made to the STA (station mode) interface.
  • ON_AP_FILTER - return true when requests are made to the AP (access point) interface.

Serve different site files in AP mode

server.serveStatic("/", SPIFFS, "/www/").setFilter(ON_STA_FILTER);
server.serveStatic("/", SPIFFS, "/ap/").setFilter(ON_AP_FILTER);

Rewrite to different index on AP

// Serve the file "/www/index-ap.htm" in AP, and the file "/www/index.htm" on STA
server.rewrite("/", "index.htm");
server.rewrite("/index.htm", "index-ap.htm").setFilter(ON_AP_FILTER);
server.serveStatic("/", SPIFFS, "/www/");

Serving different hosts

// Filter callback using request host
bool filterOnHost1(AsyncWebServerRequest *request) { return request->host() == "host1"; }

// Server setup: server files in "/host1/" to requests for "host1", and files in "/www/" otherwise.
server.serveStatic("/", SPIFFS, "/host1/").setFilter(filterOnHost1);
server.serveStatic("/", SPIFFS, "/www/");

Determine interface inside callbacks

  String RedirectUrl = "http://";
  if (ON_STA_FILTER(request)) {
    RedirectUrl += WiFi.localIP().toString();
  } else {
    RedirectUrl += WiFi.softAPIP().toString();
  }
  RedirectUrl += "/index.htm";
  request->redirect(RedirectUrl);

Bad Responses

Some responses are implemented, but you should not use them, because they do not conform to HTTP. The following example will lead to unclean close of the connection and more time wasted than providing the length of the content

Respond with content using a callback without content length to HTTP/1.0 clients

//This is used as fallback for chunked responses to HTTP/1.0 Clients
request->send("text/plain", 0, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
  //Write up to "maxLen" bytes into "buffer" and return the amount written.
  //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);
});

Async WebSocket Plugin

The server includes a web socket plugin which lets you define different WebSocket locations to connect to without starting another listening service or using different port

Async WebSocket Event

void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
  if(type == WS_EVT_CONNECT){
    //client connected
    os_printf("ws[%s][%u] connect\n", server->url(), client->id());
    client->printf("Hello Client %u :)", client->id());
    client->ping();
  } else if(type == WS_EVT_DISCONNECT){
    //client disconnected
    os_printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id());
  } else if(type == WS_EVT_ERROR){
    //error was received from the other end
    os_printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data);
  } else if(type == WS_EVT_PONG){
    //pong message was received (in response to a ping request maybe)
    os_printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:"");
  } else if(type == WS_EVT_DATA){
    //data packet
    AwsFrameInfo * info = (AwsFrameInfo*)arg;
    if(info->final && info->index == 0 && info->len == len){
      //the whole message is in a single frame and we got all of it's data
      os_printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len);
      if(info->opcode == WS_TEXT){
        data[len] = 0;
        os_printf("%s\n", (char*)data);
      } else {
        for(size_t i=0; i < info->len; i++){
          os_printf("%02x ", data[i]);
        }
        os_printf("\n");
      }
      if(info->opcode == WS_TEXT)
        client->text("I got your text message");
      else
        client->binary("I got your binary message");
    } else {
      //message is comprised of multiple frames or the frame is split into multiple packets
      if(info->index == 0){
        if(info->num == 0)
          os_printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
        os_printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len);
      }

      os_printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len);
      if(info->message_opcode == WS_TEXT){
        data[len] = 0;
        os_printf("%s\n", (char*)data);
      } else {
        for(size_t i=0; i < len; i++){
          os_printf("%02x ", data[i]);
        }
        os_printf("\n");
      }

      if((info->index + len) == info->len){
        os_printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len);
        if(info->final){
          os_printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
          if(info->message_opcode == WS_TEXT)
            client->text("I got your text message");
          else
            client->binary("I got your binary message");
        }
      }
    }
  }
}

Methods for sending data to a socket client

//Server methods
AsyncWebSocket ws("/ws");
//printf to a client
ws.printf((uint32_t)client_id, arguments...);
//printf to all clients
ws.printfAll(arguments...);
//printf_P to a client
ws.printf_P((uint32_t)client_id, PSTR(format), arguments...);
//printfAll_P to all clients
ws.printfAll_P(PSTR(format), arguments...);
//send text to a client
ws.text((uint32_t)client_id, (char*)text);
ws.text((uint32_t)client_id, (uint8_t*)text, (size_t)len);
//send text from PROGMEM to a client
ws.text((uint32_t)client_id, PSTR("text"));
const char flash_text[] PROGMEM = "Text to send"
ws.text((uint32_t)client_id, FPSTR(flash_text));
//send text to all clients
ws.textAll((char*)text);
ws.textAll((uint8_t*)text, (size_t)len);
//send binary to a client
ws.binary((uint32_t)client_id, (char*)binary);
ws.binary((uint32_t)client_id, (uint8_t*)binary, (size_t)len);
//send binary from PROGMEM to a client
const uint8_t flash_binary[] PROGMEM = { 0x01, 0x02, 0x03, 0x04 };
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");

//client methods
AsyncWebSocketClient * client;
//printf
client->printf(arguments...);
//printf_P
client->printf_P(PSTR(format), arguments...);
//send text
client->text((char*)text);
client->text((uint8_t*)text, (size_t)len);
//send text from PROGMEM
client->text(PSTR("text"));
const char flash_text[] PROGMEM = "Text to send";
client->text(FPSTR(flash_text));
//send binary
client->binary((char*)binary);
client->binary((uint8_t*)binary, (size_t)len);
//send binary from PROGMEM
const uint8_t flash_binary[] PROGMEM = { 0x01, 0x02, 0x03, 0x04 };
client->binary(flash_binary, 4);

Direct access to web socket message buffer

When sending a web socket message using the above methods a buffer is created. Under certain circumstances you might want to manipulate or populate this buffer directly from your application, for example to prevent unnecessary duplications of the data. This example below shows how to create a buffer and print data to it from an ArduinoJson object then send it.

void sendDataWs(AsyncWebSocketClient * client)
{
    DynamicJsonBuffer jsonBuffer;
    JsonObject& root = jsonBuffer.createObject();
    root["a"] = "abc";
    root["b"] = "abcd";
    root["c"] = "abcde";
    root["d"] = "abcdef";
    root["e"] = "abcdefg";
    size_t len = root.measureLength();
    AsyncWebSocketMessageBuffer * buffer = ws.makeBuffer(len); //  creates a buffer (len + 1) for you.
    if (buffer) {
        root.printTo((char *)buffer->get(), len + 1);
        if (client) {
            client->text(buffer);
        } else {
            ws.textAll(buffer);
        }
    }
}

Limiting the number of web socket clients

Browsers sometimes do not correctly close the websocket connection, even when the close() function is called in javascript. This will eventually exhaust the web server's resources and will cause the server to crash. Periodically calling the cleanClients() function from the main loop() function limits the number of clients by closing the oldest client when the maximum number of clients has been exceeded. This can called be every cycle, however, if you wish to use less power, then calling as infrequently as once per second is sufficient.

void loop(){
  ws.cleanupClients();
}

Async Event Source Plugin

The server includes EventSource (Server-Sent Events) plugin which can be used to send short text events to the browser. Difference between EventSource and WebSockets is that EventSource is single direction, text-only protocol.

Setup Event Source on the server

AsyncWebServer server(80);
AsyncEventSource events("/events");

void setup(){
  // setup ......
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it gat is: %u\n", client->lastId());
    }
    //send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!",NULL,millis(),1000);
  });
  //HTTP Basic authentication
  events.setAuthentication("user", "pass");
  server.addHandler(&events);
  // setup ......
}

void loop(){
  if(eventTriggered){ // your logic here
    //send event "myevent"
    events.send("my event content","myevent",millis());
  }
}

Setup Event Source in the browser

if (!!window.EventSource) {
  var source = new EventSource('/events');

  source.addEventListener('open', function(e) {
    console.log("Events Connected");
  }, false);

  source.addEventListener('error', function(e) {
    if (e.target.readyState != EventSource.OPEN) {
      console.log("Events Disconnected");
    }
  }, false);

  source.addEventListener('message', function(e) {
    console.log("message", e.data);
  }, false);

  source.addEventListener('myevent', function(e) {
    console.log("myevent", e.data);
  }, false);
}

Scanning for available WiFi Networks

//First request will return 0 results unless you start scan from somewhere else (loop/setup)
//Do not request more often than 3-5 seconds
server.on("/scan", HTTP_GET, [](AsyncWebServerRequest *request){
  String json = "[";
  int n = WiFi.scanComplete();
  if(n == -2){
    WiFi.scanNetworks(true);
  } else if(n){
    for (int i = 0; i < n; ++i){
      if(i) json += ",";
      json += "{";
      json += "\"rssi\":"+String(WiFi.RSSI(i));
      json += ",\"ssid\":\""+WiFi.SSID(i)+"\"";
      json += ",\"bssid\":\""+WiFi.BSSIDstr(i)+"\"";
      json += ",\"channel\":"+String(WiFi.channel(i));
      json += ",\"secure\":"+String(WiFi.encryptionType(i));
      json += ",\"hidden\":"+String(WiFi.isHidden(i)?"true":"false");
      json += "}";
    }
    WiFi.scanDelete();
    if(WiFi.scanComplete() == -2){
      WiFi.scanNetworks(true);
    }
  }
  json += "]";
  request->send(200, "application/json", json);
  json = String();
});

Remove handlers and rewrites

Server goes through handlers in same order as they were added. You can't simple add handler with same path to override them. To remove handler:

// save callback for particular URL path
auto handler = server.on("/some/path", [](AsyncWebServerRequest *request){
  //do something useful
});
// when you don't need handler anymore remove it
server.removeHandler(&handler);

// same with rewrites
server.removeRewrite(&someRewrite);

server.onNotFound([](AsyncWebServerRequest *request){
  request->send(404);
});

// remove server.onNotFound handler
server.onNotFound(NULL);

// remove all rewrites, handlers and onNotFound/onFileUpload/onRequestBody callbacks
server.reset();

Setting up the server

#include "ESPAsyncTCP.h"
#include "ESPAsyncWebServer.h"

AsyncWebServer server(80);
AsyncWebSocket ws("/ws"); // access at ws://[esp ip]/ws
AsyncEventSource events("/events"); // event source (Server-Sent events)

const char* ssid = "your-ssid";
const char* password = "your-pass";
const char* http_username = "admin";
const char* http_password = "admin";

//flag to use from web update to reboot the ESP
bool shouldReboot = false;

void onRequest(AsyncWebServerRequest *request){
  //Handle Unknown Request
  request->send(404);
}

void onBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
  //Handle body
}

void onUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
  //Handle upload
}

void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
  //Handle WebSocket event
}

void setup(){
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.printf("WiFi Failed!\n");
    return;
  }

  // attach AsyncWebSocket
  ws.onEvent(onEvent);
  server.addHandler(&ws);

  // attach AsyncEventSource
  server.addHandler(&events);

  // respond to GET requests on URL /heap
  server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", String(ESP.getFreeHeap()));
  });

  // upload a file to /upload
  server.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request){
    request->send(200);
  }, onUpload);

  // send a file when /index is requested
  server.on("/index", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.htm");
  });

  // HTTP basic authentication
  server.on("/login", HTTP_GET, [](AsyncWebServerRequest *request){
    if(!request->authenticate(http_username, http_password))
        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>");
  });
  server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){
    shouldReboot = !Update.hasError();
    AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", shouldReboot?"OK":"FAIL");
    response->addHeader("Connection", "close");
    request->send(response);
  },[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
    if(!index){
      Serial.printf("Update Start: %s\n", filename.c_str());
      Update.runAsync(true);
      if(!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)){
        Update.printError(Serial);
      }
    }
    if(!Update.hasError()){
      if(Update.write(data, len) != len){
        Update.printError(Serial);
      }
    }
    if(final){
      if(Update.end(true)){
        Serial.printf("Update Success: %uB\n", index+len);
      } else {
        Update.printError(Serial);
      }
    }
  });

  // attach filesystem root at URL /fs
  server.serveStatic("/fs", SPIFFS, "/");

  // Catch-All Handlers
  // Any request that can not find a Handler that canHandle it
  // ends in the callbacks below.
  server.onNotFound(onRequest);
  server.onFileUpload(onUpload);
  server.onRequestBody(onBody);

  server.begin();
}

void loop(){
  if(shouldReboot){
    Serial.println("Rebooting...");
    delay(100);
    ESP.restart();
  }
  static char temp[128];
  sprintf(temp, "Seconds since boot: %u", millis()/1000);
  events.send(temp, "time"); //send event "time"
}

Setup global and class functions as request handlers

#include <Arduino.h>
#include <ESPAsyncWebserver.h>
#include <Hash.h>
#include <functional>

void handleRequest(AsyncWebServerRequest *request){}

class WebClass {
public :
  AsyncWebServer classWebServer = AsyncWebServer(81);

  WebClass(){};

  void classRequest (AsyncWebServerRequest *request){}

  void begin(){
    // attach global request handler
    classWebServer.on("/example", HTTP_ANY, handleRequest);

    // attach class request handler
    classWebServer.on("/example", HTTP_ANY, std::bind(&WebClass::classRequest, this, std::placeholders::_1));
  }
};

AsyncWebServer globalWebServer(80);
WebClass webClassInstance;

void setup() {
  // attach global request handler
  globalWebServer.on("/example", HTTP_ANY, handleRequest);

  // attach class request handler
  globalWebServer.on("/example", HTTP_ANY, std::bind(&WebClass::classRequest, webClassInstance, std::placeholders::_1));
}

void loop() {

}

Methods for controlling websocket connections

  // Disable client connections if it was activated
  if ( ws.enabled() )
    ws.enable(false);

  // enable client connections if it was disabled
  if ( !ws.enabled() )
    ws.enable(true);

Example of OTA code

  // OTA callbacks
  ArduinoOTA.onStart([]() {
    // Clean SPIFFS
    SPIFFS.end();

    // Disable client connections    
    ws.enable(false);

    // Advertise connected clients what's going on
    ws.textAll("OTA Update Started");

    // Close them
    ws.closeAll();

  });

Adding Default Headers

In some cases, such as when working with CORS, or with some sort of custom authentication system, you might need to define a header that should get added to all responses (including static, websocket and EventSource). The DefaultHeaders singleton allows you to do this.

Example:

DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
webServer.begin();

NOTE: You will still need to respond to the OPTIONS method for CORS pre-flight in most cases. (unless you are only using GET)

This is one option:

webServer.onNotFound([](AsyncWebServerRequest *request) {
  if (request->method() == HTTP_OPTIONS) {
    request->send(200);
  } else {
    request->send(404);
  }
});

Path variable

With path variable you can create a custom regex rule for a specific parameter in a route. For example we want a sensorId parameter in a route rule to match only a integer.

  server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
      String sensorId = request->pathArg(0);
  });

NOTE: All regex patterns starts with ^ and ends with $

To enable the Path variable support, you have to define the buildflag -DASYNCWEBSERVER_REGEX.

For Arduino IDE create/update platform.local.txt:

Windows: C:\Users(username)\AppData\Local\Arduino15\packages\{espxxxx}\hardware\espxxxx\{version}\platform.local.txt

Linux: ~/.arduino15/packages/{espxxxx}/hardware/{espxxxx}/{version}/platform.local.txt

Add/Update the following line:

  compiler.cpp.extra_flags=-DDASYNCWEBSERVER_REGEX

For platformio modify platformio.ini:

[env:myboard]
build_flags = 
  -DASYNCWEBSERVER_REGEX

NOTE: By enabling ASYNCWEBSERVER_REGEX, <regex> will be included. This will add an 100k to your binary.

espasyncwebserver's People

Contributors

adam5wu avatar adapt0 avatar andig avatar anisimovsergey avatar atanisoft avatar avonwyss avatar baggior avatar bmooij avatar buddydvd avatar carlosrdomin avatar coderus avatar devyte avatar everslick avatar gerald-guiony avatar gmag11 avatar hagai-shatz avatar hallard avatar herm avatar iafilius avatar ivankravets avatar matt123p avatar me-no-dev avatar me21 avatar merlinschumacher avatar nut-code-monkey avatar omersiar avatar probonopd avatar sticilface avatar tetious avatar trygvis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

espasyncwebserver's Issues

example needed for TLS

Do you have an example TLS with cer.cert and k.key are uploaded memory (with/without password) ?

Serial return to WebSocket client.

Hello me-no-dev.

I'm developing an html app that sends short control commands over websocket to serial>arduino. The esp8266 is obvious choice for this. I'm not a seasoned developer with much knowledge of c++ (3 months). My first web app is almost complete and at over 300KB with it's dependencies is quite large and still growing. I have played with the idea of storing this on SPIFFS using libraries and examples from Markus. I could not achieve reliable results.
However using a modified version of your ESP_AsyncFSBrowser example and libraries the results are amazing. Well done and thank you.
Now to my problem, my app requires full duplex serial <> websocket communication for the best user experience. Preferably as transparent as possible.
I modified some code by tzapu https://github.com/tzapu/WebSocketSerialMonitor and this has served me well up to now.

String inputString = "";
boolean stringComplete = false;

void setup() {

Serial.begin(115200);
delay(100);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(100);
}
Serial.println(WiFi.localIP());
webSocket.begin();
webSocket.onEvent(webSocketEvent);

inputString.reserve(128);
}

void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
if (inChar == '>') {
inputString += inChar;
stringComplete = true;
return;
} else {
inputString += inChar;
}
}
}

void loop() {

serialEvent();
if (stringComplete) {
String reply = inputString;
inputString = "";
stringComplete = false;
webSocket.broadcastTXT(reply);
}
webSocket.loop();
}

The above goes below the socket handlers in my sketch.
I'm trying to include the serialEvent or something similar into your example ESP_AsyncFSBrowser but I'm encountering many problems. I don't yet fully understand the Async protocol and I know that my problem is not really an issue. I thought I would try to get your help anyway as there is next to no information available elsewhere and this could benefit others. Could you please suggest a coding example that would work in this case.
Many thanks for now.
indev2

How to use instructions missing

Hi,
I am seeing that ESPAsyncWebServer library is not there even in the latest version of ESP8266. Theres very little information about how to use this library.

Could you please help me out here? I will write improve the documentation once I get to know how to use this.

Cheers,
Akash A

Enhancement: addHeader()

Please allow addHeader similar and generic to setLastModified and setCache- for static handlers and EventSource. This will e.g. allow to specify the CORS headers for Ajax.

sync connectin with GPRS

hi @me-no-dev ,sorry for trigger issue here but actually , I'm trying to establish websocketclient from GPRS connection to update remote website data, unfortunately Markus not available today's , and that's why I put my problem here, I find only this code in this github , which is no suitable for my module (SIM800L), you may know we only have AT command to control the GSM module whereas I found only one command to change HTTP parameters (AT+HTTPPARA="CONTENT",), is it possible to run websocket connection with just this command?
can you guide me to write the simple websocket client ?

thanks

Latest Commit: 501 error...

Hmm, pulled to latest c95ac73, it compiles, but on request GET / I get a 501 Not Implemented error. I start the server like so:

AsyncWebServer webserver(80);
webserver.on("/", HTTP_GET, webserver_handleRoot);
webserver.onNotFound(webserver_handleNotFound);
webserver.begin();

void webserver_handleRoot(AsyncWebServerRequest *request) { /* ... */ }
void webserver_handleNotFound(AsyncWebServerRequest *request) { /* ... */ }

Web Server crashed

Hi,
I started Http calls continuously from an android and I see that when simultaneous calls are made esp8266 crashes. Here is the crash log

Exception (29):
epc1=0x402057fc epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000

ctx: sys 
sp: 3ffff8a0 end: 3fffffb0 offset: 01a0

>>>stack>>>
3ffffa40:  00000010 3fffbd88 3fffbd88 3ffe8548  
3ffffa50:  00000020 000000c8 00000000 40206b0c  
3ffffa60:  3fffbfac 00000010 3ffffabc 40206b5b  
3ffffa70:  000005b4 00000000 3ffffabc 3ffe8548  
3ffffa80:  3ffffab0 000000c8 3ffffae0 401004d8  
3ffffa90:  3fffbf24 00000076 3ffffae0 3ffe8548  
3ffffaa0:  3ffffbd8 000000c8 00000000 40204162  
3ffffab0:  00000000 00000000 00000000 3fffbfd4  
3ffffac0:  0000001f 00000010 3fffb964 40206c78  
3ffffad0:  3fffb964 000000c8 3fffb964 402041d5  
3ffffae0:  00000000 00000000 00000000 3fffbfac  
3ffffaf0:  0000001f 00000010 3ffffbfc 402023f6  
3ffffb00:  3fffb964 3ffffbd8 3fffbdf8 402026c7  
3ffffb10:  7473227b 73757461 73223a22 65636375  
3ffffb20:  2c227373 6d616e22 223a2265 6d6f7441  
3ffffb30:  33312d38 33313338 2c223732 69777322  
3ffffb40:  5f686374 74617473 3a227375 7773227b  
3ffffb50:  68637469 223a2230 222c2230 74697773  
3ffffb60:  22316863 2230223a 7773222c 68637469  
3ffffb70:  223a2232 222c2230 74697773 22336863  
3ffffb80:  2230223a 00007d7d 00000002 40101c72  
3ffffb90:  40104f34 3fff1414 3ffed930 3ffef0ec  
3ffffba0:  00000010 3ffffc88 00000000 4010053d  
3ffffbb0:  00000010 3ffffc94 00000000 3ffef0ec  
3ffffbc0:  00000010 3ffffc50 00000000 4010053d  
3ffffbd0:  00000010 3ffffc88 3fffbf24 0000007f  
3ffffbe0:  00000076 000000c7 00000076 3ffe8548  
3ffffbf0:  00000010 3fffbdec 00000200 3fffbefc  
3ffffc00:  0000001f 00000010 3ffffc50 0000001f  
3ffffc10:  000000c8 3ffffc70 3ffffc70 40206b0c  
3ffffc20:  3ffe8cd8 00000000 3ffffc70 00000002  
3ffffc30:  00000002 3fffb964 3fffb964 40207554  
3ffffc40:  3fffb978 3fffbcb4 3ffffc70 40205b49  
3ffffc50:  3ffe8cd8 3fffbcb4 3fffb978 40206aac  
3ffffc60:  3fffb978 3fff923c 3fffb978 40206d2d  
3ffffc70:  00000000 3ffea7e7 3fffb964 40204580  
3ffffc80:  00000002 3ffea7e7 3fffb978 40206d90  
3ffffc90:  3ffea7e7 00000000 00000000 00000002  
3ffffca0:  00000002 3ffea7e7 3fffb964 40204974  
3ffffcb0:  3fffb978 3fff923c 3fffb978 0000001f  
3ffffcc0:  00000021 3ffea7c8 3fffb964 402045b6  
3ffffcd0:  3fffb978 3ffea7c8 3fffb978 40206d90  
3ffffce0:  3ffea7c8 00000000 00000000 0000001f  
3ffffcf0:  00000021 3ffea7c8 3fffb964 402047a8  
3ffffd00:  3fffb978 3fff923c 3fffb978 00000036  
3ffffd10:  00000057 3ffea792 3fffb964 402045b6  
3ffffd20:  3fffb978 3ffea792 3fffb978 40206d90  
3ffffd30:  3ffea792 00000000 00000000 00000036  
3ffffd40:  00000057 3ffea792 3fffb964 402047a8  
3ffffd50:  3fffb978 00000001 3fffb978 00000018  
3ffffd60:  0000006f 3ffea77a 3fffb964 402045b6  
3ffffd70:  3fffb978 3ffea77a 3fffb978 40206d90  
3ffffd80:  3ffea77a 00000000 00000000 00000018  
3ffffd90:  0000006f 3ffea77a 3fffb964 402047a8  
3ffffda0:  3ffe8cd8 3fffb978 3fffb978 00000013  
3ffffdb0:  00000082 3ffea767 3fffb964 402045b6  
3ffffdc0:  3fffb978 3ffea767 3fffb978 40206d90  
3ffffdd0:  3ffea767 00000000 00000000 00000013  
3ffffde0:  00000082 3ffea767 3fffb964 402047a8  
3ffffdf0:  3ffe8cd8 3fffb964 3fffb978 00000015  
3ffffe00:  00000097 3ffea752 3fffb964 4020452d  
3ffffe10:  3fffb978 3ffea752 3fffb978 40206d90  
3ffffe20:  3ffea752 3ffed270 3fffb928 00000015  
3ffffe30:  00000097 3ffea752 3fffb964 402047a8  
3ffffe40:  3ffea06c 00000000 00000000 4010068c  
3ffffe50:  00000000 09ce2c9c 40102f52 00000100  
3ffffe60:  3fffb978 3ffea06c 3ffea06c 00000001  
3ffffe70:  00000001 00007fff 000004f8 3fffb900  
3ffffe80:  00000001 3fff31d4 3fffb8ac 402049a1  
3ffffe90:  3fffc278 40102cec 3fffb8ac 40202e10  
3ffffea0:  3fff9e9c 3ffeefbc 3fff9e9c 40225cf9  
3ffffeb0:  3fff09fc 3fff0a08 00000006 3fff0810  
3ffffec0:  3fff9e9c 3fff0818 3fff0814 40202e63  
3ffffed0:  3fff9e9c 3fff0818 3fff0814 4022b460  
3ffffee0:  00000097 0501a8c0 00000018 00000097  
3ffffef0:  00000018 00000000 3ffef1cc 40107304  
3fffff00:  40100000 00000000 00000064 3fff0a00  
3fffff10:  3ffea72a 3fff0a08 3fff31d4 40229969  
3fffff20:  3fff0724 3fff127c 3fff127c 3ffee390  
3fffff30:  00000000 3fff31d4 0000001c 3fff127c  
3fffff40:  3ffea71c 00000000 3fff31d4 40228d65  
3fffff50:  0201a8c0 000000f5 00000000 00000019  
3fffff60:  00000002 0000001a 4020977f 3ffed270  
3fffff70:  3ffea6f4 3fffdcc0 3ffe9908 3ffe9908  
3fffff80:  402096f2 3ffed270 00000000 3fff12ec  
3fffff90:  3fffdc80 00000000 3fff31d4 4021d7e7  
3fffffa0:  40000f49 3fffdab0 3fffdab0 40000f49  
<<<stack<<<

 ets Jan  8 2013,rst cause:2, boot mode:(3,7)

load 0x4010f000, len 1384, room 16 
tail 8
chksum 0x2d
csum 0x2d
v3de0c112
~ld

Here is my handler method

void handleDiscovery(AsyncWebServerRequest *request) {
  AsyncResponseStream *response = request->beginResponseStream("text/json");
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["status"] = "success";
  root["name"] = wifiManager.getStationSSID();
  JsonObject& switchStatus = jsonBuffer.createObject();
  switchStatus["switch0"] = "0";
  switchStatus["switch1"] = "0";
  switchStatus["switch2"] = "0";
  switchStatus["switch3"] = "0";
  root["switch_status"] = switchStatus;

  char buffer[200];
  root.printTo(buffer, sizeof(buffer));
  request->send(200, "application/json", buffer);
}

Can you please help me as to how to debug/fix the issue?

Only the first ~2.5KByte is served for static files

Hi me-no-dev,

I have uploaded the ESP_AsyncFSBrowser to my nodemcu but it looks like it struggles sending large files.

image

image

I have tried it with many different client (IE, firefox, chrome) but it's the same. The same SPIFFS content seems to work the default arduino webserver, but it's terribly slow. I have tried both gzipped and non gzipped version of the file but it's the same.

I am using the master/head for both the ESPAsyncWebserver and ESPAsyncTCP.

Web Server stops responding if client dies in the middle of sending a request

My ESP async web server application (based on ESP_AsyncFSBrowser) handles thousands of requests per hour from other ESP clients day after day. However, if a client gets powered down in the middle of sending a request, the web server stops accepting requests - WifiClient.connect will fail on the clients. The web server never recovers and needs to be reset.

The web server still responds to ping, and it can connect to another web server -- I tried using using that as a self-check but it keeps working. It even accepts OTA updates. It just won't accept client connections.

I tried connecting to itself as a self-check, but strangely, I find that this always fails even if the web server is working and accepts requests from other clients.

Is there a way to recover from this state or detect that it is not accepting connections so I can initiate a self-reset?

AsyncWebSocket memory leak?

Each time I connected then disconnected to the Websocket server, ESP.getFreeHeap() decreases by 184.

Example code: ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino with one modification to demonstrate this issue:

Line 121:
Change
client->printf("Hello Client %u :)", client->id());

To
client->printf("Hello Client %u :), %u", client->id(), ESP.getFreeHeap());

Websocket client log:

CONNECTED

RESPONSE: Hello Client 1 :), 38696

DISCONNECTED

CONNECTED

RESPONSE: Hello Client 2 :), 38512

DISCONNECTED

CONNECTED

RESPONSE: Hello Client 3 :), 38328

DISCONNECTED

...

Is it possible to send a response to client with HTTP request from another source?

I need to send a response to the client using the "answer" from another site. Is it possible with AsyncWebServer? Thanks.

setup()
{
...
server.on("/ping", pingSource);
server.begin();
}
void pingSource(AsyncWebServerRequest *request)
{
HTTPClient http;
String send = "{\"status\": \"ping\"}";
http.begin("http://some-site.net/api");
http.addHeader("Content-Type", "application/json");
request->send(200, "text/plain", String(http.POST(send));
http.end();
}

esp8266 web server ( reading the content of web server )

I make web server on my esp8266 all i want is to read the content of this web server
i make my web server with this line of code and send to it some html and javascript
this line of HTML code i send with wifi module <div id = "dir">stop
now I need to read the content of my web page and see what in this div I am noob with this module :D

Error when build

Hello,
I got following error when build the ESP_AsyncFSBrowser example

Arduino: 1.6.7 (Windows 7), Board: "Generic ESP8266 Module, Serial, 80 MHz, 40MHz, DIO, 115200, 512K (64K SPIFFS), ck"

Build options changed, rebuilding all
In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:73:17: error: redeclaration of 'CLOSED'

CLOSED = 0,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:128:3: note: previous declaration 'tcp_state CLOSED'

CLOSED = 0,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:74:17: error: redeclaration of 'LISTEN'

LISTEN = 1,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:129:3: note: previous declaration 'tcp_state LISTEN'

LISTEN = 1,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:75:17: error: redeclaration of 'SYN_SENT'

SYN_SENT = 2,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:130:3: note: previous declaration 'tcp_state SYN_SENT'

SYN_SENT = 2,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:76:17: error: redeclaration of 'SYN_RCVD'

SYN_RCVD = 3,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:131:3: note: previous declaration 'tcp_state SYN_RCVD'

SYN_RCVD = 3,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:77:17: error: redeclaration of 'ESTABLISHED'

ESTABLISHED = 4,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:132:3: note: previous declaration 'tcp_state ESTABLISHED'

ESTABLISHED = 4,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:78:17: error: redeclaration of 'FIN_WAIT_1'

FIN_WAIT_1 = 5,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:133:3: note: previous declaration 'tcp_state FIN_WAIT_1'

FIN_WAIT_1 = 5,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:79:17: error: redeclaration of 'FIN_WAIT_2'

FIN_WAIT_2 = 6,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:134:3: note: previous declaration 'tcp_state FIN_WAIT_2'

FIN_WAIT_2 = 6,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:80:17: error: redeclaration of 'CLOSE_WAIT'

CLOSE_WAIT = 7,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:135:3: note: previous declaration 'tcp_state CLOSE_WAIT'

CLOSE_WAIT = 7,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:81:17: error: redeclaration of 'CLOSING'

CLOSING = 8,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:136:3: note: previous declaration 'tcp_state CLOSING'

CLOSING = 8,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:82:17: error: redeclaration of 'LAST_ACK'

LAST_ACK = 9,

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:137:3: note: previous declaration 'tcp_state LAST_ACK'

LAST_ACK = 9,

^

In file included from C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/ESP8266WiFi.h:28:0,

             from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:32:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/include/wl_definitions.h:83:17: error: redeclaration of 'TIME_WAIT'

TIME_WAIT = 10

             ^

In file included from C:\Program Files (x86)\Arduino\libraries\ESPAsyncTCP\src\ESPAsyncTCP.cpp:27:0:

C:\Users\John\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.0.0\libraries\ESP8266WiFi\src/lwip/tcp.h:138:3: note: previous declaration 'tcp_state TIME_WAIT'

TIME_WAIT = 10

^

exit status 1
Error compiling.

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.

Update firmware over the FILE Upload handling.

I have a problem with updating firmware from AsyncWebServer.

void setup(){
    server.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request){
        request->send(200);
    }, handleUpload);   
    server.begin();     
}
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
    //Handle upload 
        Serial1.println("----UPLOAD-----"); 
        Serial1.print("FILENAME: ");
        Serial1.println(filename);
            Serial1.print("INDEX: ");
            Serial1.println(index);
        Serial1.print("LENGTH: ");
        Serial1.println(len);   
        AsyncWebHeader *header = request->getHeader("X-File-Size");
        Serial1.print("File size: ");
        Serial1.println((size_t)header->value().toFloat());
        if (!Update.isRunning())
        {
            Serial1.print("Status Update.begin(): ");
            Serial1.println(Update.begin((size_t)header->value().toFloat()));
            Serial1.print("Update remaining: ");
            Serial1.println(Update.remaining());
        }
        else
        {
            Serial1.println("Status Update.begin(): RUNNING");
        }       

    Serial1.print("FLASH BYTES: ");
    ESP.wdtDisable();
    Serial1.println(Update.write(data, len));       
        Serial1.print("Update remaining: ");
    Serial1.println(Update.remaining());
    ESP.wdtEnable(10);  
    Serial1.print("HEAP: ");
    Serial1.println(ESP.getFreeHeap());
    if (final)
    {
        Update.end();
        Serial1.print("----FINAL-----");
    }   
}

In Serial I received:

----UPLOAD-----
FILENAME: AsyncWebModbusRTU.ino.bin
INDEX: 0
LENGTH: 1291
File size: 313696
Status Update.begin(): 1
Update remaining: 313696
FLASH BYTES: 1291
Update remaining: 313696
HEAP: 23816
----UPLOAD-----
FILENAME: AsyncWebModbusRTU.ino.bin
INDEX: 1291
LENGTH: 1460
File size: 313696
Status Update.begin(): RUNNING
FLASH BYTES: 1460
Update remaining: 313696
HEAP: 23816
----UPLOAD-----
FILENAME: AsyncWebModbusRTU.ino.bin
INDEX: 2751
LENGTH: 1460
File size: 313696
Status Update.begin(): RUNNING
FLASH BYTES: 
 ets Jan  8 2013,rst cause:4, boot mode:(1,6)

wdt reset

Is there some kind of solution to this problem?

Missing file "Hash.h"

At line 40 on file AsyncWebSocket.cpp, there's an include of file "Hash.h" that isn't present on the library, and isn't on it's pre-requisites. Compiling the example "as-is" generates error.

  • Using IDE PlataformIO with framework Arduino and plataform Expressif, on board NodeMCU V1.0

WebSocket Buffer Len for WS_TEXT message

When receiving WS_TEXT from websocket handler, if my text is for example foo the associated len received is 3. Does the buffer as 1 more size not counted by len ? because setting data[len] = 0; may have unpredictable effects ?

Feature Request: make full use of lwip's buffer size

Just for completeness, I'd like to request a feature to make AsyncServer better integrate with lwIP's stack functionality. Web browsers will immediately close a connection as soon as Content-Length bytes have been received.

Can we have AsyncServer send as much data as lwip tcp_snd_buf has currently room? Currently, the HTTP header is sent as separate packet, delaying Windows' ACK for this:
image
Also in the image above, the last two packets should have been sent in one go without waiting for the ACK... It would be very nice to have this for performance reasons since now a transfer takes 0.5s (with my setting and 5k HTML data) to Windows clients, which we could easily get down to under 100ms.

Can't build the example

Hi,
I got following error when build the ESP_AsyncFSBrowser example.

I'm using PlatformIO 2.9.1, checked that Platformio ESP core == https://github.com/esp8266/Arduino master branch.

(penv) r:\test>pio run
[05/13/16 20:26:00] Processing d1_mini (platform: espressif, board: d1_mini, framework: arduino)
---------------------------------------------------------------------------------------------------------------------------------------------------------------
xtensa-lx106-elf-ar rcs .pioenvs\d1_mini\libFrameworkArduinoVariant.a
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\src\tmp_ino_to.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-functions=4 -f
function-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -DARDUINO=20200 -DP
LATFORMIO=020901 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\fram
ework-arduinoespressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant -I.pioenvs\d1_mini\ESP8266WiFi -I.p
ioenvs\d1_mini\ESP8266mDNS -I.pioenvs\d1_mini\ArduinoOTA -I.pioenvs\d1_mini\Hash -I.pioenvs\d1_mini\ESPAsyncTCP_ID305 -I.pioenvs\d1_mini\ESPAsyncWebServer_ID306
 src\tmp_ino_to.cpp
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\FrameworkArduino\Esp.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-function
s=4 -ffunction-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -DARDUINO=202
00 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\framework-arduinoe
spressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant .pioenvs\d1_mini\FrameworkArduino\Esp.cpp
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\FrameworkArduino\FS.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-functions
=4 -ffunction-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -DARDUINO=2020
0 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\framework-arduinoes
pressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant .pioenvs\d1_mini\FrameworkArduino\FS.cpp
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\FrameworkArduino\HardwareSerial.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -fali
gn-functions=4 -ffunction-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -D
ARDUINO=20200 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\framewo
rk-arduinoespressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant .pioenvs\d1_mini\FrameworkArduino\Hard
wareSerial.cpp
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\FrameworkArduino\IPAddress.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-fu
nctions=4 -ffunction-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -DARDUI
NO=20200 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\framework-ar
duinoespressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant .pioenvs\d1_mini\FrameworkArduino\IPAddress
.cpp
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\FrameworkArduino\MD5Builder.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-f
unctions=4 -ffunction-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -DARDU
INO=20200 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\framework-a
rduinoespressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant .pioenvs\d1_mini\FrameworkArduino\MD5Build
er.cpp
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\FrameworkArduino\Print.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-functi
ons=4 -ffunction-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -DARDUINO=2
0200 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\framework-arduin
oespressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant .pioenvs\d1_mini\FrameworkArduino\Print.cpp
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\FrameworkArduino\Stream.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-funct
ions=4 -ffunction-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -DARDUINO=
20200 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\framework-ardui
noespressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant .pioenvs\d1_mini\FrameworkArduino\Stream.cpp
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\FrameworkArduino\StreamString.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign
-functions=4 -ffunction-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -DAR
DUINO=20200 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\framework
-arduinoespressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant .pioenvs\d1_mini\FrameworkArduino\Stream
String.cpp
R:/test/src/ESP_AsyncFSBrowser.ino:18:6: error: 'bool SPIFFSEditor::canHandle(AsyncWebServerRequest*)' cannot be overloaded
if(request->method() == HTTP_GET && request->url() == "/edit" && (SPIFFS.exists("/edit.htm") || SPIFFS.exists("/edit.htm.gz")))
^
R:/test/src/ESP_AsyncFSBrowser.ino:17:10: error: with 'bool SPIFFSEditor::canHandle(AsyncWebServerRequest*)'
bool canHandle(AsyncWebServerRequest *request){
^
R:/test/src/ESP_AsyncFSBrowser.ino:34:10: error: 'void SPIFFSEditor::handleRequest(AsyncWebServerRequest*)' cannot be overloaded
if(_username.length() && (request->method() != HTTP_GET || request->url() == "/edit" || request->url() == "/list") && !request->authenticate(_username.c_str(),_
password.c_str()))
^
R:/test/src/ESP_AsyncFSBrowser.ino:19:10: error: with 'void SPIFFSEditor::handleRequest(AsyncWebServerRequest*)'
return true;
^
R:/test/src/ESP_AsyncFSBrowser.ino:99:10: error: 'void SPIFFSEditor::handleUpload(AsyncWebServerRequest*, String, size_t, uint8_t*, size_t, bool)' cannot be ove
rloaded
if(!index){
^
R:/test/src/ESP_AsyncFSBrowser.ino:21:10: error: with 'void SPIFFSEditor::handleUpload(AsyncWebServerRequest*, String, size_t, uint8_t*, size_t, bool)'
return true;
^
scons: *** [.pioenvs\d1_mini\src\tmp_ino_to.o] Error 1
xtensa-lx106-elf-g++ -o .pioenvs\d1_mini\FrameworkArduino\Tone.o -c -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-functio
ns=4 -ffunction-sections -fdata-sections -MMD -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_NODEMCU -DARDUINO=20
200 -U__STRICT_ANSI__ -IC:\users\hazar\.platformio\packages\framework-arduinoespressif\tools\sdk\include -IC:\users\hazar\.platformio\packages\framework-arduino
espressif\tools\sdk\lwip\include -I.pioenvs\d1_mini\FrameworkArduino -I.pioenvs\d1_mini\FrameworkArduinoVariant .pioenvs\d1_mini\FrameworkArduino\Tone.cpp
================================================================= [ ERROR ] Took 1.71 seconds =================================================================

Case-insensitive header field names

The RFC 7230 3.2 and also the old RFC 2616 4.2 define the field name as case-insensitive. node-fetch sends the content-length header field in lower case. That means the web server doesn't go into that if branch. I tested it with a quick 'n dirty or condition. I would like to make a PR. I think a generic solution, calling .toLowerCase() after this line would be the best solution, but that would also cause changes in many other places in the code and maybe even applications that use the web server library. What do you prefer?

serveStatic("/" ...) results in HTTP 500

readme.md says:

  // attach filesystem root at URL /fs 
  server.serveStatic("/fs", SPIFFS, "/");

I wanted to serve the root uri from /html/ on SPIFFS, so I used
server.serveStatic("/", SPIFFS, "/html/");
but this results in HTTP 500. The following syntax works though:
server.serveStatic("", SPIFFS, "/html/");

strftime strftime strftime

Arduino/libraries/ESPAsyncWebServer-master/src/WebHandlers.cpp:67:64: error: 'strftime' was not declared in this scope
strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified);
^

Arduino/libraries/ESPAsyncWebServer-master/src/WebHandlers.cpp:72:60: error: 'strftime' was not declared in this scope
return setLastModified((struct tm *)gmtime(&last_modified));

Arduino/libraries/ESPAsyncWebServer-master/src/WebHandlers.cpp:77:25: error: 'strftime' was not declared in this scope
if(time(&last_modified) == 0) //time is not yet set

How to solve?

Web Server Stops responding after a while

Hi All,
ESP async webserver becomes unavailable after sometime intermittently where it says
Connection to http://192.168.1.4:80 refused
org.apache.http.conn.HttpHostConnectException: Connection to http://192.168.1.4:80 refused

The same happens from the web browser and then it starts working randomly. Checking the logs of the ESP device itself there is no crash.

Am i missing something in the setup of the server which can keep it alive all the time.

Cheers,
Akash A

Cheat sheet for converting from standard esp web server library.

I am trying to convert a rather large project from the standard esp web server library to this one.

I was kind of hoping there might be a list of equivalent functions one to the other to make porting easier.

Things like server->arg("name") in the esp web server library and what there equivalent is in this one.

Tiny request for documentation: how to return a large PROGMEM char*?

Hi, I'm fiddeling with your work again, and while all the dynamic content works very well, my index.html is a 5k html inside a PROGMEM char[] ="...";. How do I send this in the response?

I don't want to put this into SPIFFS, since user may reformat FS and everything would be gone. Therefore this content goes into flash...

I've thought of a stream, but have not found a way to create a stream from a progmem variable. Then I thought of a callback which reads chunks of the data, but I find myself unable to save the last index I read from (bear in mind: several clients can request index at the same time. How do i elegeantly keep track of each request?)

Could you add an example of how to stream a large progmem content? Thanks.

Does Async TCP break HTTP Client?

Hi,
seems to me that Async TCP breaks HTTP Client.

Is there a way to submit requests to a url? Since on the basic SyncClient this does not work in my case...

Apart from google-url, only IP based connections work with syncClient

Thanks in advance

Problem uploading multiple files

Hi,

First, thanks for all your work on an awesome set of libraries and other contribution to ESP arduino dev!!!

I've experienced a small issue trying to upload multiple files.

Single file upload based on your examples works great (for instance, upload "one.txt"). Chrome client on windows, simple html form as follows:

<html>
<body>
<form action="/upload" method="post"
enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" ><br>
<input type="submit" name="submit" value="Submit">
</form>
</body>
</html>

Supported by this code:

void handleFileUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
  static bool _authenticated;
  static String _username = "", _password="";

  dbg_printf("File: %s, size:%u bytes, index: %u, final: %s\n", filename.c_str(), len, index, final?"true":"false");
  if(!index){
    _authenticated = false;
    if(!_username.length() || request->authenticate(_username.c_str(),_password.c_str())){
      _authenticated = true;
      filename= "/" + filename;
      request->_tempFile = SPIFFS.open(filename, "w");
    }
  }
  if(_authenticated && request->_tempFile){
    if(len){
      request->_tempFile.write(data,len);
    }
    if(final){
      request->_tempFile.close();
    }
  }
}

However, if I try to modify the HTML to support selecting multiple files (so I can upload "one.txt" followed by "two.txt" in one go, eg:

<html>
<body>

<form action="/upload" method="post"
enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" **multiple**><br>
<input type="submit" name="submit" value="Submit">
</form>
</body>
</html>

Then I get a fatal exception:

Fatal exception 3(LoadStoreErrorCause):
epc1=0x4010011d, epc2=0x00000000, epc3=0x40000f68, excvaddr=0x40036c00, depc=0x00000000

Exception (3):
epc1=0x4010011d epc2=0x00000000 epc3=0x40000f68 excvaddr=0x40036c00 depc=0x00000000

ctx: sys
sp: 3ffffbf0 end: 3fffffb0 offset: 01a0

The debug output from the above sprintf is:
File: two.txt, size:3 bytes, index: 0, final: true
... which is identical to what I see if I upload just one file.

Any suggestions as to what I'm doing wrong (apart from trying to upload two files :P )?

Thanks again!!

use ESPAsyncWebServer and websocket to stream video

hi ,can I have your comments and opinion about if ESP module can handle the video stream from browser to the module . I think Arducam made good library to stream the video to client browser but I don't know whether reverse action is possible or not , if it is what would be optimum fps and frame size

Platformio compile fails

I've installed through the platformio library manager and build for esp12. This fails during this include:

#include <Hash.h>

Update: funny enough its fine if I compile again. So more a platformio issue? I'm confused there :O

AsyncWebSocket printf bug

I found a bug in AsyncWebSocket printf function: wrong argument printed, perhaps invalid pointer.
The problem seems to happen with .printf(client_id, arguments...) but not with client->printf(arguments...).
Example code to reproduce the problem (code of the setup function omitted):

const char WSErr[] PROGMEM = "{\"type\":\"STATUS\",\"response\":\"Error at %d\"}";
int clientID;
bool readyToTest = false;

AsyncWebSocket ws("/.ws"); // access at ws://[esp ip]/.ws

void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)
{
  //Handle WebSocket event
  if(type == WS_EVT_CONNECT) {
    Serial.printf("ws[%s][%u] connect\n", server->url(), client->id());
    client->printf("{\"type\":\"ACK\",\"response\":\"HELO\",\"client-id\":%u}", client->id());
    client->ping();
    clientID = client->id();
  } else if(type == WS_EVT_DISCONNECT) {
    Serial.printf("ws[%s][%u] disconnect: u\n", server->url(), client->id());
  } else if(type == WS_EVT_ERROR) {
    Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data);
  } else if(type == WS_EVT_PONG) {
    Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:"");
    readyToTest = true;
  } else if(type == WS_EVT_DATA) {
  }
}

void testWS() {
   char tmp[100];
   unsigned char testvalue = 2;

   strcpy_P(tmp, WSErr);
   Serial.printf(tmp, testvalue);
   ws.printf(clientID, tmp, testvalue);
}

void loop {
   if(readyToTest) {
     readyToTest = false;
     testWS();
   }
}

Output at serial monitor:

{"type":"STATUS","response":"Error at 2"}

Output at browser console (see script below)

{"type":"STATUS","response":"Error at 1073698976"}

This is the javascript that handles websocket connections and report to console:

        ws = new WebSocket("ws://10.0.22.101/.ws", WSPROTO);
    ws.onopen = function(event) {  
        connected = true;
    };
    ws.onclose = function(event) {
        connected = false;
    };
    ws.onmessage = function (event) {
           console.log(event.data);
        }

Resetting authentication

Is there a way to revoke authentication?
After authenticating with a login I need to 'sign out' and revoke the authentication but I can't see any way of doing it.

Thanks.

Change webserver port

Is it possible to dynamically change the server port?
I want to allow configuration of the server and part of that is allowing the user to determine the port that is used.
I see that in the constructor for the server (which is a global variable) the port is hardcoded:
AsyncWebServer server(80);

How can I then change the port number in my Setup routine?

I tried adding the following in my startup (port = 8081 by the way):
server = AsyncWebServer(port);
but as soon as I try to use the server (by sending an http request) my ESP crashes:
Exception (9):
epc1=0x402129ce epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000002 depc=0x00000000

Setting the port to 8081 in the initial constructor doesn't cause me any problem.

Thanks

code structure

During debugging I kept being confused by AsyncWebServerHandlerImpl ws WebHandlers, the sooner only containing static and callback handlers. Wouldn't it make sense to move those to the same file?

AsyncServer and OTA

When OTA starts it blocks the synchronous web server. The async server however seems to keep running according to my logs. As both async server and ota may e.g. update spiffs that seems to call for trouble.
Should async server be stopped on OTA start? How?

Problem with requestAuthentication() in Internet Explorer

Request Authentication doesn't work in Internet Explorer.
After input login and password request repeated.

server.on("/index.html", [](AsyncWebServerRequest *request)
    {       
        if (!request->authenticate(_login, _pass))          
            return request->requestAuthentication();
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.html", "text/html");
        request->send(response);
    }); 

async server shows crash with heap4c

Trying to track down some crashes I've implemented heap4c and a simple sketch:

#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>

AsyncWebServer g_server(80);

void setup() {
  Serial.begin(115200);
  WiFi.begin();
  while (WiFi.status() != WL_CONNECTED)
    delay(100);
  g_server.begin();
}

void loop() {
  delay(100);
}

When putting a little load, this will crash pretty immediately if heap4c present and run fine for ages (as does a more complex sketch) as long as ESPAsyncWebServer is not started. As you can see there are no handlers attached, start of ESPAsyncWebServer alone is enough to cause the crash.

Use this for load:

while (true); do curl -m 1 http://192.168.0.30; done

Crash (update- deleted everything before wifi is up):

ip:192.168.0.30,mask:255.255.255.0,gw:192.168.0.1f 112 153 <
m 5 64 154 <

f 64 154 <
m 16 64 155 <
f 64 149 <
m 120 168 156 <
m 16 64 157 <
f 96 152 <
f 152 151 <
f 168 156 <
f 64 157 <
m 16 64 158 <
f 64 158 <
m 16 64 159 <
f 64 159 <
m 16 64 160 <
f 64 160 <
m 16 64 161 <
f 64 155 <
m 16 64 162 <
f 64 161 <
m 16 64 163 <
m 20 72 164 <
f 72 164 <
f 64 163 <
m 16 64 165 <
f 64 165 <
m 16 64 166 <
f 64 166 <
m 16 64 167 <
f 64 167 <
m 16 64 168 <
f 64 150 <
m 16 64 169 <
f 64 162 <
m 16 64 170 <
f 64 168 <
m 16 64 171 <
m 20 72 172 <
f 72 172 <
m 20 72 173 <
f 72 173 <
f 64 171 <
m 16 64 174 <
f 64 174 <
m 16 64 175 <
f 64 175 <
m 16 64 176 <
f 64 176 <
m 16 64 177 <
m 20 72 178 <
f 72 178 <
f 64 170 <
m 16 64 179 <
f 64 177 <
m 16 64 180 <
f 64 180 <
m 16 64 181 <
f 64 181 <
m 16 64 182 <
f 64 182 <
m 16 64 183 <
f 64 183 <
m 16 64 184 <
f 64 1 <
m 16 64 185 <
f 64 169 <
m 16 64 186 <
f 64 179 <
m 16 64 187 <
f 64 184 <
m 16 64 188 <
m 20 72 189 <
m 176 224 190 <
m 16 64 191 <
m 116 168 192 <
m 20 72 193 <
m 100 152 194 <
m 116 168 195 <
m 8 64 196 <
f 72 189 <
f 152 194 <
f 64 188 <
m 16 64 197 <
m 20 72 198 <
f 64 196 <
f 72 198 <
f 168 195 <
f 64 191 <
m 16 64 199 <
m 20 72 200 <
m 176 224 201 <
m 220 272 202 <
m 4 64 203 <
m 16 64 204 <
m 16 64 205 <
m 16 64 206 <
m 16 64 207 <
m 16 64 208 <
m 16 64 209 <
m 16 64 210 <
m 16 64 211 <
m 16 64 212 <
m 16 64 213 <
m 4 56 214 <
m 4 56 215 <
f 56 214 <
m 4 56 216 <
m 4 56 217 <
f 56 216 <
m 4 56 218 <
m 4 56 219 <
f 56 218 <
m 4 56 220 <
m 4 56 221 <
f 56 220 <
m 4 56 222 <
m 4 56 223 <
f 56 222 <
m 4 56 224 <
m 4 56 225 <
f 56 224 <
f 168 192 <
f 72 193 <
f 72 200 <
m 20 72 226 <
m 32 80 227 <
f 64 204 <
m 16 88 228 <
m 16 72 229 <
m 32 80 230 <
f 72 229 <
f 80 230 <
m 16 72 231 <
m 16 64 232 <
m 16 64 233 <
f 64 233 <
f 64 232 <
f 72 231 <
f 88 228 <
m 32 88 234 <
m 28 80 235 <
m 16 72 236 <
m 16 64 237 <
m 16 64 238 <
f 64 238 <
m 16 64 239 <
f 64 239 <
f 88 234 <
m 16 88 240 <
f 88 240 <
m 16 88 241 <
f 88 241 <
f 64 237 <
f 72 236 <
f 80 235 <
m 16 88 242 <
f 88 242 <
m 48 96 243 <
f 80 227 <
m 64 112 244 <
f 96 243 <
m 80 128 245 <
f 112 244 <
m 96 168 246 <
f 128 245 <
m 96 144 247 <
m 28 80 248 <
m 16 72 249 <
m 16 64 250 <
m 16 64 251 <
f 64 251 <
m 16 64 252 <
m 80 128 253 <
f 64 252 <
f 64 250 <
f 144 247 <
m 16 64 254 <
f 64 254 <
m 16 64 255 <
f 64 255 <
m 16 64 256 <
f 64 256 <
m 16 64 257 <
f 64 257 <
m 16 64 258 <
f 64 258 <
m 16 64 259 <
m 16 64 260 <
f 64 260 <
f 64 259 <
f 128 253 <
f 72 249 <
f 80 248 <
m 16 72 261 <
f 72 261 <
m 16 72 262 <
m 28 80 263 <
m 16 64 264 <
m 16 64 265 <
m 16 64 266 <
f 64 266 <
m 16 64 267 <
f 64 267 <
f 72 262 <
m 16 72 268 <
f 72 268 <
m 16 72 269 <
f 72 269 <
m 16 72 270 <
f 72 270 <
m 16 72 271 <
f 72 271 <
m 16 72 272 <
f 72 272 <
m 16 72 273 <
m 16 64 274 <
f 64 274 <
f 72 273 <
f 64 265 <
f 64 264 <
f 80 263 <
m 16 72 275 <
f 72 275 <
m 48 96 276 <
m 28 80 277 <
m 16 72 278 <
m 16 64 279 <
m 16 64 280 <
f 64 280 <
m 16 64 281 <
m 32 80 282 <
f 64 281 <
f 64 279 <
f 96 276 <
m 16 64 283 <
f 64 283 <
m 16 64 284 <
f 64 284 <
m 16 64 285 <
f 64 285 <
m 16 64 286 <
f 64 286 <
m 16 64 287 <
f 64 287 <
m 16 64 288 <
m 16 96 289 <
f 96 289 <
f 64 288 <
f 80 282 <
f 72 278 <
f 80 277 <
m 16 72 290 <
f 72 290 <
m 32 80 291 <
m 28 80 292 <
m 16 72 293 <
m 16 64 294 <
m 16 64 295 <
f 64 295 <
m 16 64 296 <
f 64 296 <
f 80 291 <
m 16 80 297 <
f 80 297 <
m 16 80 298 <
f 80 298 <
m 16 80 299 <
f 80 299 <
m 16 80 300 <
f 80 300 <
m 16 80 301 <
f 80 301 <
m 16 80 302 <
m 16 64 303 <
f 64 303 <
f 80 302 <
f 64 294 <
f 72 293 <
f 80 292 <
m 16 72 304 <
f 72 304 <
m 48 96 305 <
m 28 80 306 <
m 16 72 307 <
m 16 64 308 <
m 16 64 309 <
f 64 309 <
m 16 64 310 <
m 48 96 311 <
f 64 310 <
f 64 308 <
f 96 305 <
m 16 64 312 <
f 64 312 <
m 16 64 313 <
f 64 313 <
m 16 64 314 <
f 64 314 <
m 16 64 315 <
f 64 315 <
m 16 64 316 <
f 64 316 <
m 16 64 317 <
m 16 96 318 <
f 96 318 <
f 64 317 <
f 96 311 <
f 72 307 <
f 80 306 <
m 16 72 319 <
f 72 319 <
m 32 80 320 <
m 28 80 321 <
m 16 72 322 <
m 16 64 323 <
m 16 64 324 <
f 64 324 <
m 16 64 325 <
m 32 80 326 <
f 64 325 <
f 64 323 <
f 80 320 <
m 16 64 327 <
f 64 327 <
m 16 64 328 <
f 64 328 <
m 16 64 329 <
f 64 329 <
m 16 64 330 <
f 64 330 <
m 16 64 331 <
f 64 331 <
m 16 64 332 <
m 16 80 333 <
f 80 333 <
f 64 332 <
f 80 326 <
f 72 322 <
f 80 321 <
m 16 72 334 <
f 72 334 <
m 32 80 335 <
m 28 80 336 <
m 16 72 337 <
m 16 64 338 <
m 16 64 339 <
f 64 339 <
m 16 64 340 <
f 64 340 <
f 80 335 <
m 16 80 341 <
f 80 341 <
m 16 80 342 <
f 80 342 <
m 16 80 343 <
f 80 343 <
m 16 80 344 <
f 80 344 <
m 16 80 345 <
f 80 345 <
m 16 80 346 <
m 16 64 347 <
f 64 347 <
f 80 346 <
f 64 338 <
f 72 337 <
f 80 336 <
m 16 72 348 <
f 72 348 <
m 16 72 349 <
m 16 64 350 <
m 16 64 351 <
m 16 64 352 <
m 56 104 353 <
m 16 64 354 <
m 16 64 355 <
m 16 64 356 <
m 16 64 357 <
m 16 64 358 <
m 28 80 359 <
m 16 64 360 <
m 16 64 361 <
f 64 358 <
f 64 357 <
f 64 356 <
f 64 355 <
m 32 80 362 <
m 16 64 363 <
m 32 112 364 <
m 16 64 365 <
m 28 80 366 <
m 32 80 367 <
m 16 64 368 <
f 64 365 <
f 112 364 <
f 64 363 <
f 80 362 <
m 16 64 369 <
m 16 64 370 <
m 16 64 371 <
m 48 96 372 <
f 64 370 <
m 48 96 373 <
f 64 371 <
f 96 372 <
m 32 80 374 <
m 16 64 375 <
m 64 112 376 <
f 96 373 <
f 64 375 <
f 80 374 <
m 16 64 377 <
m 32 80 378 <
f 64 377 <
m 32 80 379 <
f 80 378 <
m 80 128 380 <
f 112 376 <
f 80 379 <
f 64 361 <
f 64 360 <
f 80 359 <
m 32 80 381 <
m 48 96 382 <
f 80 381 <
m 48 96 383 <
f 96 382 <
m 112 176 384 <
f 128 380 <
f 96 383 <
f 64 368 <
f 80 367 <
f 80 366 <
m 1572 1624 385 <
m 20 72 386 <
f 176 384 <
f 64 352 <
f 64 351 <
f 64 350 <
f 72 349 <
f 72 226 <
m 20 72 387 <
f 64 197 <
m 16 64 388 <
f 64 199 <
m 16 64 389 <
f 64 388 <
m 16 64 390 <
f 64 389 <
m 16 64 391 <
f 64 390 <
m 16 64 392 <
f 64 187 <
m 16 64 393 <
f 64 391 <
m 16 64 394 <
f 64 392 <
m 16 64 395 <
f 72 387 <
m 20 72 396 <
f 1624 385 <
f 72 386 <
m 112 160 397 <
m 20 72 398 <
f 160 397 <
f 72 398 <
m 112 160 399 <
f 224 190 <
f 56 225 <
f 56 221 <
f 56 223 <
f 56 215 <
f 56 217 <
f 56 219 <
f 224 201 <
f 64 203 <
f 64 369 <
f 64 354 <
f 104 353 <
f 64 213 <
f 64 212 <
f 64 211 <
f 64 210 <
f 64 209 <
f 64 208 <
f 64 207 <
f 64 206 <
f 64 205 <
f 168 246 <
f 272 202 <
m 21 72 400 <
Fatal exception 28f 72 400 <
m 25 80 401 <
(LoadProhibitedCause):
f 80 401 <
m 69 120 402 <
epc1=0x4021dec7, epc2=0x00000000, epc3=0x00000000, excvaddr=0x007e8000, depc=0x00000000
f 120 402 <

Exception (28):
epc1=0x4021dec7 epc2=0x00000000 epc3=0x00000000 excvaddr=0x007e8000 depc=0x00000000

ctx: sys 
sp: 3ffffd70 end: 3fffffb0 offset: 01a0

>>>stack>>>
3fffff10:  4021bee2 3fff0d20 3fff1358 3ffee820  
3fffff20:  3ffee844 4021baf1 3fff0d20 00000000  
3fffff30:  3ffee844 3ffee820 0000cccc 4021bae0  
3fffff40:  69b13f15 000019dc 00000001 00000011  
3fffff50:  00000000 00000000 4021a8f6 3fff0cd8  
3fffff60:  3fff0b98 3ffe9be6 3fff0b98 4021968b  
3fffff70:  3fff0b98 00000014 40219c36 3fff0cd8  
3fffff80:  3fff0b98 3fffdc80 3fff0c38 00000001  
3fffff90:  402255ef 3fff0cd8 00000000 40205bdb  
3fffffa0:  40000f49 3fffdab0 3fffdab0 40000f49  
<<<stack<<<

 ets Jan  8 2013,rst cause:1, boot mode:(1,7)


 ets Jan  8 2013,rst cause:4, boot mode:(1,7)

wdt reset

PROGMEM expamle with binary array

Example code here: http://www.pastebin.ca/3595833

When using the PROGMEM example, it works great when the PROGMEM variable is declared like:
const char DataText[] PROGMEM = R"=====(Blah blah Blah)=====";

I have had no issue with it for data over 100Kb.

If however the PROGMEM variable lie like this:
const char power_png[] PROGMEM = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0xc8 }
the example fails because strlen_P always returns 9

If I replace the strlen_P with sizeof, it will work some times, but take 5-7minutes to transfer.

Is there a way to be able to use PROGMEM arrays for binary data?

Dump when reloading web page while requests are open

Reloading page while 1~5 requests are still pending triggers crash:

ERROR[-8] Address in use, state: Closed
Fatal exception 3(LoadStoreErrorCause):
epc1=0x4010011d, epc2=0x00000000, epc3=0x00000000, excvaddr=0x40034d08, depc=0x00000000

Exception (3):
epc1=0x4010011d epc2=0x00000000 epc3=0x00000000 excvaddr=0x40034d08 depc=0x00000000

ctx: sys 
sp: 3ffffcb0 end: 3fffffb0 offset: 01a0

>>>stack>>>
3ffffe50:  4020bff8 000000f8 3fff4f9c 401003e1  
3ffffe60:  3fff0484 000006ce 000006ce 4010020c  
3ffffe70:  3fff4c54 4020f798 3fff4c4c 401008c8  
3ffffe80:  00007526 4020f798 3fff4c54 40107158  
3ffffe90:  3fff4c54 3fff4c54 3fff54f4 40228734  
3ffffea0:  1e00a8c0 a500a8c0 00000001 3fff4f9c  
3ffffeb0:  00007526 f4a83f0d 00000050 0000c62a  
3ffffec0:  4022874a 3fff4f9c 000000ff 402268de  
3ffffed0:  4020f2e9 3fff4f9c 00000000 00000000  
3ffffee0:  4020f5cf 3fff4e54 000000ff 4020f5d9  
3ffffef0:  0000004a 3fff4f9c 00000000 4020f634  
3fffff00:  00000000 00000000 00000002 40107148  
3fffff10:  40227916 3fff4c54 3ffef684 3ffef660  
3fffff20:  3ffef684 00000000 3fff4c54 4020f6cf  
3fffff30:  402273d1 3fff4c54 00000000 40227514  
3fffff40:  f4a83f0c 000069be 00000001 00000011  
3fffff50:  00000000 00000000 4022632a 3fff4d14  
3fffff60:  3fff1ae4 3ffec336 3fff1ae4 402250bf  
3fffff70:  3fff1ae4 00000014 4022566a 3fff4d14  
3fffff80:  3fff1ae4 3fffdc80 3fff1b44 00000001  
3fffff90:  4023101f 3fff4d14 00000000 4021173f  
3fffffa0:  40000f49 3fffdab0 3fffdab0 40000f49  
<<<stack<<<


Decoding 30 results
0x4010011d: umm_assimilate_up at C:\andi\arduino\hardware\esp8266com\esp8266\cores\esp8266\umm_malloc/umm_malloc.c:1161
0x40034d08: ?? ??:0
0x4010011d: umm_assimilate_up at C:\andi\arduino\hardware\esp8266com\esp8266\cores\esp8266\umm_malloc/umm_malloc.c:1161
0x40034d08: ?? ??:0
0x4020bff8: operator() at C:\andi\arduino\hardware\esp8266com\esp8266\libraries\ESPAsyncWebServer\src/WebRequest.cpp:568
 (inlined by) _M_invoke at c:\andi\arduino\hardware\esp8266com\esp8266\tools\xtensa-lx106-elf\xtensa-lx106-elf\include\c++\4.8.2/functional:2071
0x401003e1: check_poison_block at C:\andi\arduino\hardware\esp8266com\esp8266\cores\esp8266\umm_malloc/umm_malloc.c:835
0x4010020c: _umm_free at C:\andi\arduino\hardware\esp8266com\esp8266\cores\esp8266\umm_malloc/umm_malloc.c:1285
0x4020f798: AsyncClient::_s_error(void*, signed char) at C:\andi\arduino\hardware\esp8266com\esp8266\libraries\ESPAsyncTCP\src/ESPAsyncTCP.cpp:506 (discriminator 4)
0x401008c8: free at C:\andi\arduino\hardware\esp8266com\esp8266\cores\esp8266\umm_malloc/umm_malloc.c:1731
0x4020f798: AsyncClient::_s_error(void*, signed char) at C:\andi\arduino\hardware\esp8266com\esp8266\libraries\ESPAsyncTCP\src/ESPAsyncTCP.cpp:506 (discriminator 4)
0x40107158: vPortFree at C:\andi\arduino\hardware\esp8266com\esp8266\cores\esp8266/heap.c:18
0x40228734: tcp_abandon at ??:?
0x4022874a: tcp_abort at ??:?
0x402268de: pbuf_alloc at ??:?
0x4020f2e9: AsyncClient::abort() at C:\andi\arduino\hardware\esp8266com\esp8266\libraries\ESPAsyncTCP\src/ESPAsyncTCP.cpp:506 (discriminator 4)
0x4020f5cf: AsyncClient::_close() at C:\andi\arduino\hardware\esp8266com\esp8266\libraries\ESPAsyncTCP\src/ESPAsyncTCP.cpp:506 (discriminator 4)
0x4020f5d9: AsyncClient::_close() at C:\andi\arduino\hardware\esp8266com\esp8266\libraries\ESPAsyncTCP\src/ESPAsyncTCP.cpp:506 (discriminator 4)
0x4020f634: AsyncClient::_recv(tcp_pcb*, pbuf*, signed char) at C:\andi\arduino\hardware\esp8266com\esp8266\libraries\ESPAsyncTCP\src/ESPAsyncTCP.cpp:506 (discriminator 4)
0x40107148: pvPortMalloc at C:\andi\arduino\hardware\esp8266com\esp8266\cores\esp8266/heap.c:13
0x40227916: tcp_input at ??:?
0x4020f6cf: AsyncClient::_s_recv(void*, tcp_pcb*, pbuf*, signed char) at C:\andi\arduino\hardware\esp8266com\esp8266\libraries\ESPAsyncTCP\src/ESPAsyncTCP.cpp:506 (discriminator 4)
0x402273d1: tcp_input at ??:?
0x40227514: tcp_input at ??:?
0x4022632a: ip_input at ??:?
0x402250bf: etharp_find_addr at ??:?
0x4022566a: ethernet_input at ??:?
0x4023101f: ets_snprintf at ??:?
0x4021173f: loop_task at C:\andi\arduino\hardware\esp8266com\esp8266\cores\esp8266/core_esp8266_main.cpp:43
0x40000f49: ?? ??:0
0x40000f49: ?? ??:0

File upload: data past the boundary end

Hello, I noticed a problem with the upload of files. I used the code in the example at the readme page:

void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
  if(!index){
    Serial.printf("UploadStart: %s\n", filename.c_str());
  }
  for(size_t i=0; i<len; i++){
    Serial.write(data[i]);
  }
  if(final){
    Serial.printf("UploadEnd: %s, %u B\n", filename.c_str(), index+len);
  }
}

On the client side a form that submits the file to upload looks like this:

<form action="upload" method="POST" enctype="multipart/form-data">
<input type="file" required name="path">
<input type="submit" name="_submit" value="Upload">
</form>

so the browser sends to the server the following data:

`Content-Type: multipart/form-data; boundary=---------------------------109992776831753
Content-Length: 1191

-----------------------------109992776831753
Content-Disposition: form-data; name="path"; filename="index.htm"
Content-Type: text/html

...(omitted data for brevity)....

-----------------------------109992776831753
Content-Disposition: form-data; name="_submit"

Upload
-----------------------------109992776831753--
`

The intended data to be received should be limited to the part after the two carriage return and before the following boundary marker, however the actual data returned by the function overflow.
This is what the function put into the file:

`

...(omitted data for brevity).... /body>

-----------------------------109992776831753
Content-Disposition: form-data; name="_submit"

Upload`

Submitted file size: 897 bytes, stored file: 1001 bytes.

2016.07.19 - UPDATE:

I have tested with a modified HTML code to submit the file in a way that just the file field is submitted pulling out the from the form:

<form id="ff" action="upload" method="POST" enctype="multipart/form-data">
<input type="file" required name="path"></form>
<button type="button" onclick="document.getElementById('ff').submit();" >Upload</button>

This time the file is uploaded missing the final part, or likely saving only the first portion of about 880 bytes, missing the final 17 or so bytes.

After reverting to the previous form, I also tried to remove the name attribute from the input tag of type submit to see what happen, and the application simply crashes (the stack is output via serial monitor).

UPDATE 2:

Crash happens when the browser send a whole block of data larger than a given amount. Example, some time it sends a first chunk of 884 bytes followed by a second final chunk of 1254 bytes, and this is completed succeessfully (though with the spurious chunk appended); some time it sends a whole block of 2139 bytes and in this case the server crashes. Note the 1 byte difference.
Crash happens at exit of the upload handling function.

WS2812B RGB LED with NeoPixelBus Animation now flickering

Hi @me-no-dev
I've just migrated my project to AsyncLib, all is working fine and it's fast. I've got one question, I had some WS2812B led breathing and it was working fine with the classic WebServer (no async) but with the async version, LED are flickering, of course I understand Async does job under the hood but is there any option to slow down this kind of network "loop task" ? I just would like testing to see if it's due to some timing problem since these LED are very stric on this point.

By the way since we do nothing in loop nor call to async, how the TCP stuff is handled (interrupts, timer, ...), just curious ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.