AsyncWebServer_Teensy41 bug onUpload

Shuptuu

Active member
Hi,
That's following my posts in the general discussion https://forum.pjrc.com/threads/6982...for-QNEthernet?p=321126&viewfull=1#post321126

So, to summarize, the file upload from a web browser to a teensy 4.1 using this asyncWebServer library doesn't work. I found the problem, see below.
Full code to test:
Code:
/****************************************************************************************************************************
  Async_AdvancedWebServer.ino - Dead simple AsyncWebServer for Teensy41 QNEthernet

  For Teensy41 with QNEthernet

  AsyncWebServer_Teensy41 is a library for the Teensy41 with QNEthernet

  Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer)
  Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_Teensy41
  Licensed under GPLv3 license

  Copyright (c) 2015, Majenko Technologies
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification,
  are permitted provided that the following conditions are met:

  Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  Redistributions in binary form must reproduce the above copyright notice, this
  list of conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution.

  Neither the name of Majenko Technologies nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************************************************************/

#if !( defined(CORE_TEENSY) && defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) )
  #error Only Teensy 4.1 supported
#endif

// Debug Level from 0 to 4
#define _TEENSY41_ASYNC_TCP_LOGLEVEL_       1
#define _AWS_TEENSY41_LOGLEVEL_             1

#define SHIELD_TYPE     "Teensy4.1 QNEthernet"

#if (_AWS_TEENSY41_LOGLEVEL_ > 3)
  #warning Using QNEthernet lib for Teensy 4.1. Must also use Teensy Packages Patch or error
#endif

#define USING_DHCP            true
//#define USING_DHCP            false

#if !USING_DHCP
  // Set the static IP address to use if the DHCP fails to assign
  IPAddress myIP(192, 168, 2, 222);
  IPAddress myNetmask(255, 255, 255, 0);
  IPAddress myGW(192, 168, 2, 1);
  //IPAddress mydnsServer(192, 168, 2, 1);
  IPAddress mydnsServer(8, 8, 8, 8);
#endif

#include "QNEthernet.h"       // https://github.com/ssilverman/QNEthernet
using namespace qindesign::network;

#include <AsyncWebServer_Teensy41.h>

AsyncWebServer    server(80);

int reqCount = 0;                // number of requests received


void handleNotFound(AsyncWebServerRequest *request)
{
  String message = "File Not Found\n\n";

  message += "URI: ";
  message += request->url();
  message += "\nMethod: ";
  message += (request->method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += request->args();
  message += "\n";

  for (uint8_t i = 0; i < request->args(); i++)
  {
    message += " " + request->argName(i) + ": " + request->arg(i) + "\n";
  }

  request->send(404, "text/plain", message);
}

void setup()
{
  Serial.begin(115200);

  while (!Serial && millis() < 5000);

  delay(200);

  Serial.print("\nStart Async_AdvancedWebServer on ");
  Serial.print(BOARD_NAME);
  Serial.print(" with ");
  Serial.println(SHIELD_TYPE);
  Serial.println(ASYNC_WEBSERVER_TEENSY41_VERSION);

  delay(500);

#if USING_DHCP
  // Start the Ethernet connection, using DHCP
  Serial.print("Initialize Ethernet using DHCP => ");
  Ethernet.begin();
#else
  // Start the Ethernet connection, using static IP
  Serial.print("Initialize Ethernet using static IP => ");
  Ethernet.begin(myIP, myNetmask, myGW);
  Ethernet.setDNSServerIP(mydnsServer);
#endif

  if (!Ethernet.waitForLocalIP(10000))
  {
    Serial.println(F("Failed to configure Ethernet"));

    if (!Ethernet.linkStatus())
    {
      Serial.println(F("Ethernet cable is not connected."));
    }

    // Stay here forever
    while (true)
    {
      delay(1);
    }
  }
  else
  {
    Serial.print(F("Connected! IP address:"));
    Serial.println(Ethernet.localIP());
  }

#if USING_DHCP
  delay(1000);
#else
  delay(2000);
#endif

  server.onNotFound(handleNotFound);

  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    String html = "<body><div><form method='POST' enctype='multipart/form-data' action='/'><input type='file' name='dummy'><button type='submit'>Send</button></form></div></body>";
    request->send(200, "text/html", html);
    });

  server.on("/", HTTP_POST, [&](AsyncWebServerRequest *request) {
            AsyncWebParameter* p = request->getParam(0);
            Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); //<= I can see this
    },[&](AsyncWebServerRequest *request, size_t index, uint8_t *data, size_t len, bool final) {
            Serial.println(len);  //<= never called
            return;
    });

  server.begin();

  Serial.print(F("HTTP EthernetWebServer is @ IP : "));
  Serial.println(Ethernet.localIP());
}

void loop()
{
}

Investigating the library, I found weird things:
# in AsyncWebServer_Teensy41.hpp
on line 700:
Code:
typedef std::function<void(AsyncWebServerRequest *request, /*const String& filename,*/ size_t index, uint8_t *data, size_t len, bool final)> ArUploadHandlerFunction;
on line 632:
Code:
virtual void handleUpload(AsyncWebServerRequest *request  __attribute__((unused)), const String& filename __attribute__((unused)),  size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final  __attribute__((unused))) {}
So at least, there's something incoherent with this "filename" parameter.

# in AsyncWebRequest_Teensy41.cpp, handlerUpload makes reference to this filename:
on line 590:
Code:
_handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false);
on line 826:
Code:
_handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);

# in AsyncWebHandlerImpl_Teensy41.h, there's an implementation for handleRequest (line 186), for handleBody (line 197) but nothing for handleUpload
by adding this code between this two others (and disabling the filename parameter everywhere):
Code:
/////////////////////////////////////////////////

    virtual void handleUpload(AsyncWebServerRequest *request, size_t index, uint8_t *data, size_t len, bool final) override final
    {
      if (_onUpload)
        _onUpload(request, index, data, len, final);
    }
it works!!!!!

I guess it would work also enabling the filename parameter everywhere and It would be better for code compatibility with the original esp32 library.

The problem now is that the library is marked as archived so I can't submit this issue and fix...
How to proceed in such case?
 
You could open your own GitHub page, copy all the code there, stating that it is copied from the original site, and post the changes there.
Everything on the original site referring to web servers has been archived as has nearly everything mentioning Teensy.
Only one Teensy mentioned item has NOT been archived.
 
Hey thanks for sharing this Shuptuu. I ran your example code and found the same thing. Instead of commenting out the filename parameter, I enabled it everywhere and it worked just fine too.
 
Many thanks for posting the update in a ZIP file!

I'd like to suggest one additional fix. With the version in your .zip file, it will break as soon as you add:
Code:
#include <AsyncJson_Teensy41.h>

with a compiler error like:

Code:
In file included from (sketch).ino:40:
c:\path_removed_for_privacy\AsyncWebServer_Teensy41-FixUpload\src/AsyncJson_Teensy41.h:408:18: error: 
  'virtual void AsyncCallbackJsonWebHandler::handleUpload(AsyncWebServerRequest*, const String&, size_t, uint8_t*, size_t, bool)'
  marked 'override', but does not override

  408 |     virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data,
      |                  ^~~~~~~~~~~~

The error is thrown in AsyncJson_Teensy41.h, in the definition of class AsyncCallbackJsonWebHandler, which extends AsyncWebHandler.
The "does not override" part is a little confusing, but it turns out that's because the function signatures don't match: this one still has const String& filename in the arg list, so it no longer matches the definition in the parent class (AsyncWebHandler).

Solution: edit the definition of handleUpload() in AsyncJson_Teensy41.h:408;
change const String& filename to String filename to match the updated arguments in the parent class.
 
The owner of the `AsyncWebServer_Teensy41` repo was so kind to build a new release that contains the changes suggested here. Next step is to uptdate the package on platformio too.
 
Back
Top