Skip to content

feat: Server Side Events (SSE) #5372

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 1 commit into from
Closed

feat: Server Side Events (SSE) #5372

wants to merge 1 commit 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 ;)\"}");
    delay(2000);
}

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Miquel Martín seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@miqmago miqmago closed this Jul 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants