pistacheio / pistache

A high-performance REST toolkit written in C++
https://pistacheio.github.io/pistache/
Apache License 2.0
3.18k stars 698 forks source link

Pistache single server exe but handling http and https #956

Open alexe100 opened 3 years ago

alexe100 commented 3 years ago

Hi I am looking for a simple way to launch a single pistache server exe but supporting same requests through http and https. Once serve() method is blocking how to launch the second one? Should I create a extra thread that that? Any clever alternative?

What I have do far:

Pistache::Address addr80(Pistache::Ipv4::any(), Pistache::Port(80)); Pistache::Address addr443(Pistache::Ipv4::any(), Pistache::Port(443)); ...

Http::Endpoint server80(addr80); server80.init(opts); server80.setHandler(Http::make_handler());

Http::Endpoint server443(addr443); server443.init(opts); server443.setHandler(Http::make_handler()); server443.useSSL("...", "...",true);

server80.serve(); //Blocking here... server443.serve(): //not reached...

Thank you very much

Tachi107 commented 3 years ago

I personally place my Pistache endpoint behind a reverse proxy (nginx), so I haven't looked into this.

Since serve() is blocking I would launch it in a separate std::jthread, what's wrong with this solution?

alexe100 commented 3 years ago

Sure, NGINX is an option but I am switching from Node to C++ in order to (try) have better performance with lower resources. If I have to rely into two servers (NGINX and PISTACHE based REST Server) both handling sockets probably this scenario is reaching the same resources and performance as nodejs single solution.

Well, if we launch the second Http::Endpoint into a separate thread (other than the main one) if that thread dies (for any reason) Linux systemd will not be able to identify it and restart it as it would if it was the main thread/process that went down. That is my probleam. Sorry if I am looking from the wrong way to this situation..

PRobably I need to run both Http::Endpoint (s) in separated threads and call join in the main one. If one of them returns (dies) I kill the whole process and systemd restarts it...

Tachi107 commented 3 years ago

PRobably I need to run both Http::Endpoint (s) in separated threads and call join in the main one. If one of them returns (dies) I kill the whole process and systemd restarts it...

Seems like a good solution :+1:

Having a single Http::Endpoint that handles both http and https would certainly be nice, but I don't know how difficult it would be to implement, nor how different it could be from your solution

dennisjenkins75 commented 3 years ago

I run multiple "servers" (different ports) in the same process. I don't use "Endpoint.serve()" though. I call "Endpoint.serveThreaded()", then my main thread waits for a shutdown signal, and when it receives it, called "Endpoint.shutdown()" on all endpoints.

dennisjenkins75 commented 3 years ago

PRobably I need to run both Http::Endpoint (s) in separated threads and call join in the main one. If one of them returns (dies) I kill the whole process and systemd restarts it...

Seems like a good solution 👍

Having a single Http::Endpoint that handles both http and https would certainly be nice, but I don't know how difficult it would be to implement, nor how different it could be from your solution

Adding extra code for this is not needed. "serveThreaded()" allows more flexibility and already exists.

Tachi107 commented 3 years ago

You're right, I haven't used Pistache in a while and forgot about it

alexe100 commented 3 years ago

So, you all do:

server80.serveThreaded(); server443.serveThreaded();

server80.shutdown(); server443.shutdown();

right? But what if (in this scenario) server443 dies and server 80 continues up and running? server443.shutdown(); would not be fired right?

dennisjenkins75 commented 3 years ago

How does a pistache EndPoint "die" and not take down the entire process? I suppose that someone could kill a thread, but that violates the API contract that the threads created by pistache be managed by pistache.

Do something like this (edited for compactness; not complete code). Note the call to "sigwait()". This blocks until one of those signals is received. Other signals, like SIGILL, SIGSEGV, SIGBUS, etc... still have their default behavior.

(Maybe we should put a complete example, fully compilable, into a "contrib" directory? My own project uses postgresql as a backend database, and I have a custom pistache-compatible connection pooler that I can also contribute).

void waitForShutdownRequest()
{
  sigset_t sigset;

  sigemptyset(&sigset);
  sigaddset(&sigset, SIGHUP);
  sigaddset(&sigset, SIGINT);
  sigaddset(&sigset, SIGTERM);
  sigprocmask(SIG_BLOCK, &sigset, nullptr);

  int sig = 0;
  sigwait(&sigset, &sig);
  std::cerr << "\nReceived signal: " << sig << ", " << strsignal(sig) << "\n";

  sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
}

int main() {
  // setup code

  server80.serveThreaded();
  server443.serveThreaded();

  waitForShutdownRequest();

  server80.shutdown();
  server443.shutdown();

  return 0;
}
alexe100 commented 3 years ago

Thank you dennisjenkins75 for your time and explanations. When I said "if a thread dies" I was meaning that for any reason, any unprotected call, null verification etc etc a thread could die and if only one thread dies (e.g. server443) the process continues and only connections of type http could be dispached, denying clients that rely on https.

My idea is to replace a (slow) nodejs API server by a C++ API (pistache seems great and simple) server that will run in poor hardware. This server must be always running so I plan to use systemd to monitor it (and restart it if...).

I think that we must "monitor" both threads. If one dies we have to kill the other (and the process) and let systemd restart all the thing again (or if one dies run it again from main thread...).