Skip to content

SSL/TLS Implementation for ESPAsyncWebServer - #121

Open
dimecho wants to merge 21 commits into
ESP32Async:mainfrom
dimecho:main
Open

SSL/TLS Implementation for ESPAsyncWebServer#121
dimecho wants to merge 21 commits into
ESP32Async:mainfrom
dimecho:main

Conversation

@dimecho

@dimecho dimecho commented Aug 25, 2026

Copy link
Copy Markdown

No description provided.

@mathieucarbou

Copy link
Copy Markdown
Member

Thanks! We’ll have a look as soon as we can!

@me-no-dev

Copy link
Copy Markdown
Member

Since this is the AsyncTCP repo and not the WebServer one, please make the example use only AsyncTCP. WebServer example should be submitted to the WebServer repo, along with the changes necessary there.

Another thing for the example is to add a file called build_opt.h with one line inside -DASYNC_TCP_SSL_ENABLED=1 in order to turn on SSL. Same should be done for the WebServer example in the WebServer repo.

There are slight changes in mbedtls in the upcoming Arduino for ESP32 v4. They come from the updated mbedtls in ESP-IDF v6. Changes here should be compatible with both current Arduino (v3) and the upcoming one (v4).

Would be great if the example uses an actual certificate and key that can be used to directly build and test it (by CI and users)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds optional mbedTLS-based client/server TLS support to AsyncTCP for ESPAsyncWebServer HTTPS usage.

Changes:

  • Adds TLS contexts, handshakes, encrypted I/O, and certificate handling.
  • Exposes secure AsyncClient/AsyncServer APIs.
  • Adds an HTTPS example and bumps the header version.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
src/AsyncTCPVersion.h Updates the reported minor version.
src/AsyncTCPTLS.h Declares the TLS context API.
src/AsyncTCPTLS.cpp Implements mbedTLS over lwIP.
src/AsyncTCP.h Exposes secure client/server interfaces.
src/AsyncTCP.cpp Integrates TLS into connection and I/O flows.
examples/AsyncWebServerSSL/AsyncWebServerSSL.ino Demonstrates an HTTPS web server.
Suppressed comments (2)

src/AsyncTCP.cpp:1190

  • Returning ERR_ABRT does not abort anything here: _connected() is invoked by handle_async_event() and its return value is discarded at AsyncTCP.cpp:324. This and the two setup-failure returns above can leave a live PCB/failing TLS context after callbacks run. Explicitly call abort() or _close() on every failure path and avoid manually firing the disconnect callback twice.
        }
        if (_discard_cb) {
          _discard_cb(_discard_cb_arg, this);
        }
        return ERR_ABRT;

src/AsyncTCP.cpp:1279

  • A handshake error received through this path only invokes callbacks and then returns ERR_OK; it never closes or aborts the still-live connection. Clients without a disconnect callback remain stuck and subsequent polls retry the failed TLS state. Match the poll failure path by closing the client (letting _close() issue the disconnect callback).
      async_tcp_log_e("SSL handshake failed in _recv: %d", ret);
      if (_error_cb) {
        _error_cb(_error_cb_arg, this, -60);
      }
      if (_discard_cb) {

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/AsyncTCPVersion.h Outdated
Comment thread src/AsyncTCP.h Outdated
Comment thread src/AsyncTCP.h Outdated
Comment thread src/AsyncTCPTLS.cpp Outdated
Comment thread src/AsyncTCPTLS.cpp Outdated
Comment thread src/AsyncTCP.cpp
Comment thread src/AsyncTCP.cpp
Comment thread src/AsyncTCPTLS.cpp Outdated
Comment thread src/AsyncTCPTLS.cpp Outdated
Comment thread src/AsyncTCPTLS.cpp Outdated

@mathieucarbou mathieucarbou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lof for having started this effort!

This is something that a few users were asking for, even if for a webserver on a MCU, support for SSL is quite a niche since any self-signed cert will cause issues on most clients / OS now. So this reduces valid use cases to ssl com with client cert, or use cases where the MCU would be exposed with a cert that is signed from a valid authority (internal company CA or valid internet authority).

So I am sure a lot of people will find it useful.

Comment thread src/AsyncTCPVersion.h Outdated
Comment thread examples/AsyncWebServerSSL/AsyncWebServerSSL.ino Outdated
Comment thread examples/AsyncWebServerSSL/AsyncWebServerSSL.ino Outdated
Comment thread src/AsyncTCP.cpp
Comment thread src/AsyncTCP.cpp
Comment thread src/AsyncTCP.h Outdated
Comment thread src/AsyncTCP.h
@dimecho

dimecho commented Aug 25, 2026

Copy link
Copy Markdown
Author

Thanks a lof for having started this effort!

This is something that a few users were asking for, even if for a webserver on a MCU, support for SSL is quite a niche since any self-signed cert will cause issues on most clients / OS now. So this reduces valid use cases to ssl com with client cert, or use cases where the MCU would be exposed with a cert that is signed from a valid authority (internal company CA or valid internet authority).

So I am sure a lot of people will find it useful.

There are use cases for https (oauth2, MFA/Passkeys ...etc) work arounds TLS trust can be with domain to local pointers - example https://192.168.4.1.sslip.io

@willmmiles willmmiles left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry to say that the changes to the core logic are grossly incorrect, suggesting a poor understanding of LwIP and the purposes of a generic TCP client library. I haven't reviewed the SSL changes in detail yet, because to be frank, this makes me think I'd be wasting my time reviewing AI slop instead of well considered code.

From an architectual standpoint, what is the rationale for integrating the SSL layer deeply in to the AsyncClient class as opposed to constructing a wrapper class? The deep integration adds a lot of complexity and penalizes all clients whether they use the feature or not.

Comment thread src/AsyncTCP.cpp
Comment on lines +419 to +431
// Drain callback: ACKs and drops any late data arriving after close is initiated.
// Without this, LwIP sends RST when data arrives on a PCB with tcp_recv=NULL.
static err_t _tcp_drain_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
(void)arg; (void)err;
if (p) {
tcp_recved(pcb, p->tot_len);
pbuf_free(p);
} else {
// NULL pbuf = remote closed — safe to ignore, close already in progress
}
return ERR_OK;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NACK on this. Sending RST on unacked data is the correct behaviour for a TCP library. It is not for us to enforce this -- If a client wants to ensure the incoming data is drained before closing, it's the client's responsibility to drain the buffer before closing.

Comment thread src/AsyncTCP.cpp
// Flush pending output before close — gives tcp_close the best chance
// of succeeding. Without this, queued data forces tcp_close to fail,
// and the tcp_shutdown fallback sends FIN with unACKed data → RST.
tcp_output(pcb);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also not correct or necessary. tcp_output() is called internally by tcp_close() if necessary.

Comment thread src/AsyncTCP.cpp
tcp_abort(pcb);
// tcp_close fails when unsent data remains (e.g. HTTP response not yet ACKed).
// Send FIN gracefully instead of RST to avoid NS_ERROR_NET_RESET.
tcp_shutdown(pcb, 0, 1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is also incorrect. tcp_close behaves correctly when unacked data is present. Please remove this change.

@dimecho

dimecho commented Sep 8, 2026

Copy link
Copy Markdown
Author

I'm sorry to say that the changes to the core logic are grossly incorrect, suggesting a poor understanding of LwIP and the purposes of a generic TCP client library. I haven't reviewed the SSL changes in detail yet, because to be frank, this makes me think I'd be wasting my time reviewing AI slop instead of well considered code.

From an architectual standpoint, what is the rationale for integrating the SSL layer deeply in to the AsyncClient class as opposed to constructing a wrapper class? The deep integration adds a lot of complexity and penalizes all clients whether they use the feature or not.

You are right things got a bit messy with this pull request. Copilot got very strict, in the process to greenlight it.

Probably right about an "SSL wrapper class" instead of deep intergration.

Cancel this pull request, will do more harware testing. For now will keep a fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants