Switch to side-by-side view

--- a/src/mediaserver/cdplugins/plgwithslave.cxx
+++ b/src/mediaserver/cdplugins/plgwithslave.cxx
@@ -21,6 +21,8 @@
 #include <string>
 #include <vector>
 #include <sstream>
+#include <functional>
+
 #include <string.h>
 #include <fcntl.h>
 #include <upnp/upnp.h>
@@ -34,6 +36,7 @@
 #include "conftree.h"
 #include "sysvshm.h"
 #include "main.hxx"
+#include "streamproxy.h"
 
 using namespace std;
 using namespace std::placeholders;
@@ -90,41 +93,21 @@
     StreamHandle laststream;
 };
 
-// microhttpd daemon handle. There is only one of these, and one port, we find
-// the right plugin by looking at the url path.
-static struct MHD_Daemon *o_mhd;
-
-// Microhttpd connection handler. We re-build the complete url + query
-// string (&trackid=value), use this to retrieve a service URL
-// (tidal/qobuz...), and redirect to it (HTTP). A previous version
-// handled rtmp streams, and had to read them. Look up the history if
-// you need the code again (the apparition of RTMP streams was
-// apparently linked to the use of a different API key).
-static int answer_to_connection(void *cls, struct MHD_Connection *connection, 
-                                const char *url, 
-                                const char *method, const char *version, 
-                                const char *upload_data, 
-                                size_t *upload_data_size, void **con_cls)
-{
-    static int aptr;
-    if (&aptr != *con_cls) {
-        /* do not respond on first call */
-        *con_cls = &aptr;
-        return MHD_YES;
-    }
-
-    LOGDEB("answer_to_connection: url " << url << " method " << method << 
-           " version " << version << endl);
-
-    // The 'plgi' here is just whatever plugin started up the httpd task
-    // We just use it to find the appropriate plugin for this path,
-    // and then dispatch the request.
-    CDPluginServices *cdpsrv = (CDPluginServices*)cls;
+// HTTP Proxy/Redirect handler
+static StreamProxy *o_proxy;
+
+StreamProxy::UrlTransReturn translateurl(
+    CDPluginServices *cdsrv,
+    std::string& url,
+    const std::unordered_map<std::string, std::string>& querymap)
+{
+    LOGDEB("PlgWithSlave::translateurl: url " << url << endl);
+
     PlgWithSlave *realplg =
-        dynamic_cast<PlgWithSlave*>(cdpsrv->getpluginforpath(url));
+        dynamic_cast<PlgWithSlave*>(cdsrv->getpluginforpath(url));
     if (nullptr == realplg) {
-        LOGERR("answer_to_connection: no plugin for path [" << url << endl);
-        return MHD_NO;
+        LOGERR("PlgWithSlave::translateurl: no plugin for path ["<<url<< endl);
+        return StreamProxy::Error;
     }
 
     // We may need one day to subclass PlgWithSlave to implement a
@@ -142,44 +125,18 @@
     // which we pass to them for translation (they will extract the
     // trackid and use it, the rest of the path is bogus).
     // The uprcl module has a real path and no trackid. Handle both cases
-    const char* stid =
-        MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND,
-                                    "trackId");
-    if (stid && *stid) {
-        path += string("?version=1&trackId=") + stid;
+    const auto it = querymap.find("trackId");
+    if (it != querymap.end() && !it->second.empty()) {
+        path += string("?version=1&trackId=") + it->second;
     }
 
     // Translate to Tidal/Qobuz etc real temporary URL
-    string media_url = realplg->get_media_url(path);
-    if (media_url.empty()) {
+    url = realplg->get_media_url(path);
+    if (url.empty()) {
         LOGERR("answer_to_connection: no media_uri for: " << url << endl);
-        return MHD_NO;
-    }
-
-    if (media_url.find("http") == 0) {
-        static char data[] = "<html><body></body></html>";
-        struct MHD_Response *response =
-            MHD_create_response_from_buffer(strlen(data), data,
-                                            MHD_RESPMEM_PERSISTENT);
-        if (response == NULL) {
-            LOGERR("answer_to_connection: could not create response" << endl);
-            return MHD_NO;
-        }
-        MHD_add_response_header (response, "Location", media_url.c_str());
-        int ret = MHD_queue_response(connection, 302, response);
-        MHD_destroy_response(response);
-        return ret;
-    } else {
-        LOGERR("PlgWithSlave: got non-http URL !: " << media_url << endl);
-        LOGERR("PlgWithSlave:   the code for handling these is gone !\n");
-        LOGERR("    will have to fetch it from git history\n");
-        return MHD_NO;
-    } 
-}
-
-static int accept_policy(void *, const struct sockaddr* sa, socklen_t addrlen)
-{
-    return MHD_YES;
+        return StreamProxy::Error;
+    }
+    return StreamProxy::Proxy;
 }
 
 // Static
@@ -209,27 +166,16 @@
 }
 
 // Static
-bool PlgWithSlave::maybeStartMHD(CDPluginServices *cdsrv)
-{
-    if (nullptr == o_mhd) {
+bool PlgWithSlave::maybeStartProxy(CDPluginServices *cdsrv)
+{
+    if (nullptr == o_proxy) {
         int port = CDPluginServices::microhttpport();
-        // Start the microhttpd daemon. There can be only one, and it
-        // is started with a context handle which points to whatever
-        // plugin got there first. The callback will only use the
-        // handle to get to the plugin services, and retrieve the
-        // appropriate plugin based on the url path prefix.
-        LOGDEB("PlgWithSlave: starting httpd on port "<< port << endl);
-        o_mhd = MHD_start_daemon(
-            MHD_USE_THREAD_PER_CONNECTION,
-            //MHD_USE_SELECT_INTERNALLY, 
-            port, 
-            /* Accept policy callback and arg */
-            accept_policy, NULL, 
-            /* handler and arg */
-            &answer_to_connection, cdsrv,
-            MHD_OPTION_END);
-        if (nullptr == o_mhd) {
-            LOGERR("PlgWithSlave: MHD_start_daemon failed\n");
+        o_proxy = new StreamProxy(
+            port,
+            std::bind(&translateurl, cdsrv, _1, _2));
+            
+        if (nullptr == o_proxy) {
+            LOGERR("PlgWithSlave: Proxy creation failed\n");
             return false;
         }
     }
@@ -243,7 +189,7 @@
         LOGDEB1("PlgWithSlave::maybeStartCmd: already running\n");
         return true;
     }
-    if (!maybeStartMHD(this->plg->m_services)) {
+    if (!maybeStartProxy(this->plg->m_services)) {
         LOGDEB1("PlgWithSlave::maybeStartCmd: maybeStartMHD failed\n");
         return false;
     }