Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDF web server module #3502

Merged
merged 8 commits into from
Mar 5, 2022

Conversation

jmattsson
Copy link
Member

@jmattsson jmattsson commented Feb 6, 2022

  • This PR is for the dev branch rather than for the release branch.
  • This PR is compliant with the other contributing guidelines as well (if not, please describe why).
  • I have thoroughly tested my contribution.
  • The code changes are reflected in the documentation at docs/*.

This PR adds two new modules:

  • httpd which provides an interface to Espressif's web server that is included in the IDF.
  • eromfs (Embedded ReadOnly Mountable File Sets) which makes it easy to include files within the firmware itself, allowing for easy OTA upgrades. This is predominantly aimed at including the static web contents when using the httpd module, though other uses are of course also possible.

The httpd module is able to offload all static file serving to the IDF's web server task, for minimum impact to the LVM. Dynamic route handlers are bounced into the LVM task. Chunked body receiving and sending is supported. For details, refer to the documentation.

...since the IDF was telling me the name had changed.
Lua-interface to the standard esp_http_server component.
Also changed arguments to dynamic uri handlers to be more in line with
traditional interfaces.
Copy link
Member

@pjsg pjsg left a comment

Choose a reason for hiding this comment

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

Does the espressif server support websockets? I actually use a LUA based httpserver with support for websockets as it enabled some highly interactive use cases....


#pragma pack(push, 1)
typedef struct {
uint8_t rec_len;
Copy link
Member

Choose a reason for hiding this comment

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

If you moved the rec_len to be just before the name here and below, then you wouldn't need to do that pragma pack, and the unit16/unit32 would be aligned.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but then I'd still have filler at the end, and I didn't want to waste space needlessly. If you have a volume with a large pack of small icon files the padding adds up.

entry_ = (const record_t *)(((char *)entry_) + entry_->rec_len)) \
{ \
uint8_t name_len = entry_->rec_len - sizeof(record_t); \
if (strncmp(xname, entry_->name, name_len) == 0) \
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't quite work -- if xname is "foobar" and name is foo (with namelen 3), you will get a match.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call-out! There's a length comparison missing there. Will fix.

```

#### Parameters
- `route` The route prefix. Typically in the form of \*.ext to serve all files
Copy link
Member

Choose a reason for hiding this comment

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

I don't think that you want the backslash here

Copy link
Member Author

Choose a reason for hiding this comment

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

Don't I? Without that it'll start an italic section, will it not?

Copy link
Member

Choose a reason for hiding this comment

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

The joy and wonders of loosely defined Markdown 😄 Using a backslash means being on the safe side here as we have to cater for at least two interpreters: GitHub (GFM) and MkDocs. Did you consider putting *.ext in "" in both places?

Copy link
Member Author

Choose a reason for hiding this comment

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

Dare I ask what having it double-quoted would do to the markdown parsers?

Copy link
Member

Choose a reason for hiding this comment

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

I didn't mention this wrt the parser, sorry. It's just that once it'll read 'the ".ext" extension' and once 'the form of *.ext to serve' (w/o quotes here).

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, right. So *.ext is the wildcard form needed to register a handler for all files with the extension .ext. Maybe I should switch to code format and drop the quotes?

the body has been reached. May raise an error if reading the body fails
for some reason (e.g. timeout, network error).

Note that the provided `req` object is _only valid_ within the scope of this
Copy link
Member

Choose a reason for hiding this comment

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

How does this work with getbody()? Do I have to finish consuming the body in this callback? This might imply that the entire request has been bffered by the espressif server before the callback is invoked.

What actually happens if you do store this object? I hope that it doesn't crash the runtime.....

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, you have to finish processing the body within the call-back. This is a restriction of the esp_http_server. Afaik what it does underneath the hood is to close the receive window to the sender to avoid having to buffer excessively.

If you were to call getbody() (or access the headers object) outside the callback, you'll get a Lua error raised telling you off for doing so. Again, this is due to the limitations of the esp_http_server component itself.

@jmattsson
Copy link
Member Author

Does the espressif server support websockets? I actually use a LUA based httpserver with support for websockets as it enabled some highly interactive use cases....

There is some websocket support, yes. I haven't tried wiring it up yet as I haven't had a use-case for it. When I looked at it it wasn't entirely straight forward how one would best surface it into Lua.

@pjsg
Copy link
Member

pjsg commented Feb 26, 2022

It appears that the Espressif web server just calls the handler function for each websocket message received and then allows you to transmit a response later. In this way it is very similar to the standard processing except that (I think) the callback can send multiple messages back through the websocket.

The differences are that you have to use httpd_ws_recv_frame and httpd_ws_send_frame when interacting with the websocket.

I think that the LUA api should look like:

The handler function is called with two arguments:

  • the received frame
  • A context object (table) with a send method on it that can be used for sending response frames. The user program can also attach state variable to this object.

The received frame will be nil if this is the start of a session.

The context object can be saved until later.

The frame (in each direction) is represented by a table with two keys: type and data. The type is either text or binary. The data is a string. Three additional types are used for control frames -- close, ping, pong.

Receiving a close message means that this is the last message that will be received.

Sending a close message will close the connection.

@jmattsson
Copy link
Member Author

@pjsg That sounds pretty reasonable. Did you find any actual API documentation for the Espressif WebSocket API, or just the example?

Did you want to work on that, or did you want me to? I could possibly fit it in, but I couldn't give you a definite time frame. And are you okay with me merging in this PR in the meantime, and treating websocket support as a future enhancement?

@pjsg
Copy link
Member

pjsg commented Mar 4, 2022 via email

@jmattsson jmattsson merged commit cb43481 into nodemcu:dev-esp32-idf4 Mar 5, 2022
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.

3 participants