Skip to content

feat(esp32): Server Side Events (SSE) #5373

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

Closed
wants to merge 4 commits into from

Conversation

miqmago
Copy link
Contributor

@miqmago miqmago commented Jul 10, 2021

Tested in Chrome

This way we can leave connection opened and send SSE events like this:

class HttpServer : public WebServer {
   public:
    HttpServer(int port = 80) : WebServer(port) {
    }

    void begin() {
        if (!this->isStarted) {
            this->isStarted = true;
            this->enableCORS();
            on("/logs", std::bind(&HttpServer::handleLogs, this));
            on("/logs-stream", std::bind(&HttpServer::handleLogsStream, this));

            WebServer::begin();  // Web server start
        }
    }

    void close() {
        if (this->isStarted) {
            this->isStarted = false;
            WebServer::stop();
        }
    }

    void handleLogMessage(const char *message) {
        if (this->logClient && this->logClient->connected()) {
            this->logClient->println("event:message");
            this->logClient->println("data:" + String(message));
            this->logClient->println();
            this->logClient->flush();
        } else if (this->logClient) {
            // give the web browser time to receive the data
            delay(1);
            // close the connection:
            this->logClient->stop();
            this->logClient = NULL;
        }
    }

   private:
    bool isStarted = false;
    WiFiClient *logClient = NULL;

    void handleLogs() {
        this->prepareResponse(200, "text/html");
        sendContent(
            "<!DOCTYPE html>"
            "<html><head>"
            "<meta name='viewport' content='width=device-width, initial-scale=1.0'>"
            "<style>.E .tag{color:red}.W .tag{color:orange}.I .tag{color:lightblue}.D .tag{color:yellow}.V .tag{color:gray}</style>"
            "</head><body>"
            "<h1>Messages sent via SSE</h1>"
            "<div id='logs' style='width:100%;height:100%;background-color:black;color:white;'></div>"
            "<script>var logDiv = document.getElementById('logs');"
            "var source = new EventSource('logs-stream');"
            "source.onmessage = function (event) {"
            "  var data = JSON.parse(event.data);"
            "  logDiv.innerHTML += '<span class=\"' + data.level + '\">"
            "<span class=\"tag\">[' + data.tag + ']</span> <span class=\"message\">' + data.message + '</span></span><br/>';"
            "};"
            "</script>"
            "</body></html>");
        this->finishResponse();
    }

    void handleLogsStream() {
        // TODO: only one client supported
        this->logClient = &_currentClient;

        if (this->logClient) {
            this->logClient->println("HTTP/1.1 200 OK");
            this->logClient->println("Content-Type: text/event-stream;charset=UTF-8");
            this->logClient->println("Connection: close");
            this->logClient->println("Access-Control-Allow-Origin: *");
            this->logClient->println("Cache-Control: no-cache");
            this->logClient->println();
            this->logClient->flush();
        }

        this->logClient->setSSE(true);
    }

    void prepareResponse(int code, const char *content_type = (const char *)__null, const String &content = ((String)(""))) {
        sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        sendHeader("X-Frame-Options", "");
        sendHeader("Pragma", "no-cache");
        sendHeader("Expires", "-1");
        setContentLength(CONTENT_LENGTH_UNKNOWN);
        send(code, content_type, "");
        if (content.length() > 0) {
            sendContent(content);
        }
    }

    void finishResponse() {
        sendContent("");
        client().flush();
        client().stop();
    }
}

main.ino

HttpServer *server;

void setup() {
    server = new HttpServer(80);
    server->begin();
}

void loop() {
    server->handleLogMessage("{\"message\":\"Some message ;)\"}");
    server->handleClient();
    delay(2000);
}

@miqmago
Copy link
Contributor Author

miqmago commented Jul 17, 2021

@Rotzbua SSE is the easiest and simplest way to achieve a kind of websockets

@Rotzbua
Copy link
Contributor

Rotzbua commented Jul 19, 2021

@Rotzbua SSE is the easiest and simplest way to achieve a kind of websockets

I miss an explicit reason for the SSE feature because websockets are already available. SSE also has limitations e.g. a connection limit with http1x or some firewalls block the connection.
Furthermore why is the example not added to commit? And a reasonable documentation is missing!

@miqmago
Copy link
Contributor Author

miqmago commented Aug 22, 2021

I miss an explicit reason for the SSE feature because websockets are already available. SSE also has limitations e.g. a connection limit with http1x or some firewalls block the connection.

Totally agree with you that websockets are more powerful and better option in some scenarios. Also WebSockets are undoubtedly more complex and demanding than SSEs ref and more extensive comparative. It can be overkill for some types of application, and the backend could be easier to implement with a protocol such as SSE ref and more extensive comparative.

That was the point for me, I was looking for a fast way to send events from esp32 to the browser without implementing all the requirements to achieve WS. In my particular case, I think that I can implement SSE really fast but WS are more tricky and I'm not really sure if I could implement that fast.

Also this pull request is adding SSE feature, not replacing websockets feature. The fact that there are websockets available does not mean that other features like SSE might be unsupported or removed. Many browsers do support both websockets and SSE, among other features that are alternatives one each other like long polling.

At the end, the developers are the ones who should choose the best option for their code and the more functionalities they have on the table, the better choice can be done.

I don't see a reason for not to add this feature when there are benefits doing so.

Furthermore why is the example not added to commit? And a reasonable documentation is missing!

I couldn't find the documentation for WebServer component, which I think it should be the ideal place to describe this additional feature of WebServer and place the example already posted here. @Rotzbua, please, could you point me to the better place to add documentation and example?

@me-no-dev me-no-dev added the Status: Test needed Issue needs testing label Nov 9, 2021
@stale
Copy link

stale bot commented Apr 16, 2022

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@stale stale bot added the wontfix label Apr 16, 2022
@miqmago
Copy link
Contributor Author

miqmago commented Apr 16, 2022

I still don't see a reason for not to add this feature when there are benefits doing so.
This just adds support to an existing technology.

@stale stale bot removed the wontfix label Apr 16, 2022
@miqmago
Copy link
Contributor Author

miqmago commented Jun 15, 2022

Any updates on this? Would be really useful if this changes could be merged

@CLAassistant
Copy link

CLAassistant commented May 6, 2023

CLA assistant check
All committers have signed the CLA.

@VojtechBartoska VojtechBartoska added Status: Review needed Issue or PR is awaiting review and removed Status: Test needed Issue needs testing labels Jan 30, 2024
@VojtechBartoska VojtechBartoska added this to the 3.0.0-RC1 milestone Jan 30, 2024
@lucasssvaz
Copy link
Collaborator

Working as expected but needs to be updated to the newest master. @miqmago Would you be able to do this ?

Copy link
Member

@P-R-O-C-H-Y P-R-O-C-H-Y left a comment

Choose a reason for hiding this comment

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

Approving, just fix what is requested.

@VojtechBartoska VojtechBartoska added Resolution: Awaiting response Waiting for response of author and removed Status: Review needed Issue or PR is awaiting review labels Feb 5, 2024
Copy link
Contributor

github-actions bot commented Feb 5, 2024

Warnings
⚠️

Some issues found for the commit messages in this PR:

  • the commit message "Merge branches 'master' and 'master' of github.com:espressif/arduino-esp32":
    • summary looks empty
    • type/action looks empty
  • the commit message "Update libraries/WiFi/src/WiFiClient.cpp":
    • summary looks empty
    • type/action looks empty

Please fix these commit messages - here are some basic tips:

  • follow Conventional Commits style
  • correct format of commit message should be: <type/action>(<scope/component>): <summary>, for example fix(esp32): Fixed startup timeout issue
  • allowed types are: change,ci,docs,feat,fix,refactor,remove,revert,test
  • sufficiently descriptive message summary should be between 20 to 72 characters and start with upper case letter
  • avoid Jira references in commit messages (unavailable/irrelevant for our customers)

TIP: Install pre-commit hooks and run this check when committing (uses the Conventional Precommit Linter).

Messages
📖 You might consider squashing your 4 commits (simplifying branch history).
📖 This PR seems to be quite large (total lines of code: 2442807), you might consider splitting it into smaller PRs

👋 Hello miqmago, we appreciate your contribution to this project!


Click to see more instructions ...


This automated output is generated by the PR linter DangerJS, which checks if your Pull Request meets the project's requirements and helps you fix potential issues.

DangerJS is triggered with each push event to a Pull Request and modify the contents of this comment.

Please consider the following:
- Danger mainly focuses on the PR structure and formatting and can't understand the meaning behind your code or changes.
- Danger is not a substitute for human code reviews; it's still important to request a code review from your colleagues.
- Resolve all warnings (⚠️ ) before requesting a review from human reviewers - they will appreciate it.
- Addressing info messages (📖) is strongly recommended; they're less critical but valuable.
- To manually retry these Danger checks, please navigate to the Actions tab and re-run last Danger workflow.

Review and merge process you can expect ...


We do welcome contributions in the form of bug reports, feature requests and pull requests.

1. An internal issue has been created for the PR, we assign it to the relevant engineer.
2. They review the PR and either approve it or ask you for changes or clarifications.
3. Once the GitHub PR is approved we do the final review, collect approvals from core owners and make sure all the automated tests are passing.
- At this point we may do some adjustments to the proposed change, or extend it by adding tests or documentation.
4. If the change is approved and passes the tests it is merged into the default branch.

Generated by 🚫 dangerJS against 0571883

@lucasssvaz lucasssvaz added Status: Pending Merge Pull Request is ready to be merged and removed Resolution: Awaiting response Waiting for response of author labels Feb 5, 2024
@miqmago miqmago changed the title feat: Server Side Events (SSE) feat(esp32): Server Side Events (SSE) Feb 5, 2024
@lucasssvaz lucasssvaz removed the Status: Pending Merge Pull Request is ready to be merged label Feb 5, 2024
@lucasssvaz
Copy link
Collaborator

@miqmago Something is wrong in the Merge commit. Please fix it or enable the option that maintainers can edit the PR.

@lucasssvaz lucasssvaz added the Resolution: Awaiting response Waiting for response of author label Feb 6, 2024
@lucasssvaz lucasssvaz mentioned this pull request Feb 7, 2024
@lucasssvaz
Copy link
Collaborator

Closing in favor of #9222

@lucasssvaz lucasssvaz closed this Feb 7, 2024
@miqmago
Copy link
Contributor Author

miqmago commented Feb 7, 2024

@lucasssvaz yes, something went really wrong while doing a pull from master and trying to squash commits... There were so many changes as this PR is 3 years old...

Not sure if the close of the PR is because changes will be integrated on other place or is better to open new PR with changes on fresh branch...

@lucasssvaz
Copy link
Collaborator

@miqmago Your changes were merged in #9222

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: Awaiting response Waiting for response of author
Projects
Development

Successfully merging this pull request may close these issues.

7 participants