Hi everyone!

I was wondering if someone with a better understanding of httpd and
mod_proxy could review my module idea and prototype implementation and
warn me of any unforeseen pitfalls looming ahead before I commit to
implementing this fully.

Following is a short text on my motivation and I've attached the 62
lines of prototype code. I fully understand if no one's in the mood to
have a look at this and apologize for having wasted some of your time.

Otherwise: Thank you for reading on.

Wanting to switch to mod_proxy_http for deploying backend applications
(and opening the way for WebSockets through mod_proxy_wstunnel) I'm
missing the process management provided by mod_fastcgi [1].

While fully understanding that one can be of the opinion that process
management is best kept out of httpd, I personally like the convenience
and more importantly clarity offered by having the complete command,
arguments and environment required to run the backend application in
the httpd configuration. Authentication, URL rewriting and whatelse
will already be setup there, anyways.

So I took a shot at seeing if I could implement a module to do just
that. My current idea/prototype:

 1. Register a hook to run before mod_proxy.c:proxy_handler and have a
    look at the request filename and handler to see if they start with
    "proxy:spawn://".

 2. Use everything after that and until a pipe character as the command
    to spawn. No process management in that module, yet, of course, but
    that could easily be lifted from mod_fastcgi or mod_fcgid. As done
    in those implementations the process manager will create a AF_UNIX
    socket and pass its file descriptor to the spawned process.

 3. Rewrite the request filename/handler to "unix://uds_path|rest" form
    using the AF_UNIX socket from the process manager as well as the
    proxy configuration included in the filename/handler after the
    pipe. Then let httpd/mod_proxy continue onward.

Repository with code and httpd.conf for testing this is at [2].

Thanks in advance and regards
Florian 

[1] https://fastcgi-archives.github.io/mod_fastcgi.html#FastCgiServer
[2] https://github.com/wagnerflo/mod_proxy_spawn
#include "http_core.h"
#include "mod_proxy.h"

static int start_process(request_rec* r, const char* name, char** uds_path) {
  ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "start_process(\"%s\", ...)", name);
  *uds_path = "...";
  return OK;
}

static int proxy_spawn_handler(request_rec* r) {
  ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                "proxy_handler({ filename=\"%s\", handler=\"%s\", ... })",
                r->filename, r->handler);

  char** url;

  // forced proxy handler by SetHandler
  if (!r->proxyreq && r->handler && strncmp(r->handler, "proxy:", 6) == 0)
    url = (char**) &r->handler;
  // filename rewritten by proxy_trans
  else if (strncmp(r->filename, "proxy:", 6) == 0)
    url = &r->filename;
  else
    return DECLINED;

  if (ap_cstr_casecmpn(*url + 6, "spawn://", 8) != 0)
    return DECLINED;

  char* name = *url + 14;
  char* real = ap_strchr_c(name, '|');

  if (real == NULL) {
    return HTTP_INTERNAL_SERVER_ERROR;
  }

  char* uds_path;

  if (start_process(r, apr_pstrndup(r->pool, name, real - name), &uds_path)) {
    return HTTP_INTERNAL_SERVER_ERROR;
  }

  *url = apr_pstrcat(r->pool, "proxy:unix://", uds_path, real, NULL);
  ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "rewrite proxy url to %s", *url);

  return DECLINED;
}

static void register_hooks(apr_pool_t* pool) {
  // make sure we get called before proxy_handler
  static const char* const aszSucc[] = { "mod_proxy.c", NULL };
  ap_hook_handler(proxy_spawn_handler, NULL, aszSucc, APR_HOOK_FIRST);
}

AP_DECLARE_MODULE(proxy_spawn) = {
  STANDARD20_MODULE_STUFF,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  register_hooks
};

Reply via email to