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

Create a ResourceNode for a method within a class #90

Open
surfsoft opened this issue May 16, 2020 · 5 comments · May be fixed by #91
Open

Create a ResourceNode for a method within a class #90

surfsoft opened this issue May 16, 2020 · 5 comments · May be fixed by #91
Labels

Comments

@surfsoft
Copy link

I've a situation where the method for handling a particular HTTP request is inside a class and I'm unable to work out how to registerit with the HTTP server. Here's a minimal self-contained example of my issue:

#include <HTTPServer.hpp>
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>

using namespace httpsserver;

class ConfigurationModule {

  public:

    void handleDisplayConfig(HTTPRequest *req, HTTPResponse *res) {
      
    }

    void init(HTTPServer server) {
        std::function<void(HTTPRequest *, HTTPResponse *)> handler = std::bind(&ConfigurationModule::handleDisplayConfig, this, std::placeholders::_1, std::placeholders::_2);
        server.registerNode(new ResourceNode("/configure", "GET", handler));
    }
    
};

HTTPServer webserver = HTTPServer();
ConfigurationModule config = ConfigurationModule();

void handler1(HTTPRequest *req, HTTPResponse *res) {
    
}

void handler2(HTTPRequest *req, HTTPResponse *res) {
    
}

void setup() {

    Serial.begin(115200);

    webserver.registerNode(new ResourceNode("/htmlResponse", "GET", handler1));
    webserver.registerNode(new ResourceNode("/jsonResponse", "GET", handler2));

    config.init(webserver);
    webserver.start();

}

void loop() {
    webserver.loop();
}

This is a technique I've used across my fairly extensive codebase (which I'm currently porting from the ESP8266). However, the code in the init function won't compile, specifically it complains that handler has an incorrect signature, even though the method in question has the same signature as handler1 and handler2:

no known conversion for argument 3 from 'std::function<void(httpsserver::HTTPRequest*, httpsserver::HTTPResponse*)>' to 'void (*)(httpsserver::HTTPRequest*, httpsserver::HTTPResponse*)'

I'm unable to work out the correct syntax for resolving the class method to the typedef HTTPSCallbackFunction, or how to tweak the existing code to get rid of the error I have.

Can anybody help me here?

@fhessel fhessel linked a pull request Jun 6, 2020 that will close this issue
@fhessel
Copy link
Owner

fhessel commented Jun 6, 2020

Sorry it took so long to get back to this.

That indeed wasn't possible by now, as you could only use classic function pointers with the ResourceNode constructor, which doesn't work with the outcome of an std::bind.

I created PR #91 which should now allow to use both, being backward-compatible. That still needs a bit of testing before I can safely merge into the master branch, but maybe it helps you to proceed.

This slightly modified version of your example should work with the PR:

// Only if you use PlatformIO
#include <Arduino.h>

// Added WiFi for convenience
#define WIFI_SSID "YourNetwork"
#define WIFI_PSK "someverysecretpassword"
#include <WiFi.h>

#include <HTTPServer.hpp>
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>
#include <HTTPSCallbackFunction.hpp>

using namespace httpsserver;

class ConfigurationModule {
  public:
    void handleDisplayConfig(HTTPRequest *req, HTTPResponse *res) {
      // Added some output to see it's actually working
      res->setHeader("Content-Type", "text/plain");
      res->println("handleDisplayConfig()");
    }

    void init(HTTPServer &server) {
        // Important: Use "&server" instead of "server". Passing the server by value will neither work
        // nor be what you intended to do here (you want the same server to modify it, not a copy).

        // HTTPSCallbackFunction is now an std::function, so you can assign the result of a bind
        HTTPSCallbackFunction handler =
          std::bind(&ConfigurationModule::handleDisplayConfig, this, std::placeholders::_1, std::placeholders::_2);
        // Note that you will not be able to retrieve that node anymore if you create it "on the fly" here.
        // I assume that's for the sake of an example, but if you wanted to tear down the application,
        // you would need to store a pointer to that ResourceNode anywhere in this class and clean it
        // up in the destructor.
        server.registerNode(new ResourceNode("/configure", "GET", handler));
    }
};

ConfigurationModule config = ConfigurationModule();
HTTPServer webserver = HTTPServer();

void handler1(HTTPRequest *req, HTTPResponse *res) {
    // Also added some text here for testing
    res->setHeader("Content-Type", "text/plain");
    res->println("handler1()");
}

void handler2(HTTPRequest *req, HTTPResponse *res) {
    res->setHeader("Content-Type", "text/plain");
    res->println("handler2()");
}

void setup() {
    Serial.begin(115200);
    // Connect to WiFi
    Serial.println("Setting up WiFi");
    WiFi.begin(WIFI_SSID, WIFI_PSK);
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(".");
      delay(500);
    }
    Serial.print("Connected. IP=");
    Serial.println(WiFi.localIP());

    webserver.registerNode(new ResourceNode("/htmlResponse", "GET", handler1));
    webserver.registerNode(new ResourceNode("/jsonResponse", "GET", handler2));
    config.init(webserver);
    webserver.start();
}

void loop() {
    webserver.loop();
}

I hope it's not too late for you, however, that issue has been on my todo list for some time now anyway. It would be nice if you could give me feedback in either case.

@fhessel fhessel added the feature label Jun 6, 2020
@Thomas-Blondeau
Copy link

Hello !

I got the same issue this week and I am happy to read that you just prepared a PR. Is it possible to use the fix right now ? Maybe if I pull the branch ?

Thank you !

@fhessel
Copy link
Owner

fhessel commented Jun 24, 2020

Hi,

you can clone the repository and switch to the feature branch:

git clone https://github.com/fhessel/esp32_https_server.git
git checkout origin/feature/functional-callbacks

If you're using Platform IO, do it in your project's lib folder, if you use the Arduino IDE, do it in the libraries folder in your Sketchbook.

It would be great if you could tell me if that works for you, or if you run into problems. If it works, I'd merge the PR soon.

@Thomas-Blondeau
Copy link

I will clone the branch tonight and test it. I use Visual code with the Arduino Plugin.

I'll give you my feedbacks very soon.

@surfsoft
Copy link
Author

Apologies for my much delayed response, I hope to get back to this in the next week or two...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants