Switch to unified view

a/src/mediaserver/cdplugins/plgwithslave.cxx b/src/mediaserver/cdplugins/plgwithslave.cxx
...
...
19
#include "plgwithslave.hxx"
19
#include "plgwithslave.hxx"
20
20
21
#include <string>
21
#include <string>
22
#include <vector>
22
#include <vector>
23
#include <sstream>
23
#include <sstream>
24
#include <functional>
25
24
#include <string.h>
26
#include <string.h>
25
#include <fcntl.h>
27
#include <fcntl.h>
26
#include <upnp/upnp.h>
28
#include <upnp/upnp.h>
27
#include <microhttpd.h>
29
#include <microhttpd.h>
28
#include <json/json.h>
30
#include <json/json.h>
...
...
32
#include "pathut.h"
34
#include "pathut.h"
33
#include "smallut.h"
35
#include "smallut.h"
34
#include "conftree.h"
36
#include "conftree.h"
35
#include "sysvshm.h"
37
#include "sysvshm.h"
36
#include "main.hxx"
38
#include "main.hxx"
39
#include "streamproxy.h"
37
40
38
using namespace std;
41
using namespace std;
39
using namespace std::placeholders;
42
using namespace std::placeholders;
40
//using json = nlohmann::json;
43
//using json = nlohmann::json;
41
using namespace UPnPProvider;
44
using namespace UPnPProvider;
...
...
88
    
91
    
89
    // Cached uri translation
92
    // Cached uri translation
90
    StreamHandle laststream;
93
    StreamHandle laststream;
91
};
94
};
92
95
93
// microhttpd daemon handle. There is only one of these, and one port, we find
96
// HTTP Proxy/Redirect handler
94
// the right plugin by looking at the url path.
97
static StreamProxy *o_proxy;
95
static struct MHD_Daemon *o_mhd;
96
98
97
// Microhttpd connection handler. We re-build the complete url + query
99
StreamProxy::UrlTransReturn translateurl(
98
// string (&trackid=value), use this to retrieve a service URL
100
    CDPluginServices *cdsrv,
99
// (tidal/qobuz...), and redirect to it (HTTP). A previous version
101
    std::string& url,
100
// handled rtmp streams, and had to read them. Look up the history if
102
    const std::unordered_map<std::string, std::string>& querymap)
101
// you need the code again (the apparition of RTMP streams was
102
// apparently linked to the use of a different API key).
103
static int answer_to_connection(void *cls, struct MHD_Connection *connection, 
104
                                const char *url, 
105
                                const char *method, const char *version, 
106
                                const char *upload_data, 
107
                                size_t *upload_data_size, void **con_cls)
108
{
103
{
109
    static int aptr;
104
    LOGDEB("PlgWithSlave::translateurl: url " << url << endl);
110
    if (&aptr != *con_cls) {
111
        /* do not respond on first call */
112
        *con_cls = &aptr;
113
        return MHD_YES;
114
    }
115
105
116
    LOGDEB("answer_to_connection: url " << url << " method " << method << 
117
           " version " << version << endl);
118
119
    // The 'plgi' here is just whatever plugin started up the httpd task
120
    // We just use it to find the appropriate plugin for this path,
121
    // and then dispatch the request.
122
    CDPluginServices *cdpsrv = (CDPluginServices*)cls;
123
    PlgWithSlave *realplg =
106
    PlgWithSlave *realplg =
124
        dynamic_cast<PlgWithSlave*>(cdpsrv->getpluginforpath(url));
107
        dynamic_cast<PlgWithSlave*>(cdsrv->getpluginforpath(url));
125
    if (nullptr == realplg) {
108
    if (nullptr == realplg) {
126
        LOGERR("answer_to_connection: no plugin for path [" << url << endl);
109
        LOGERR("PlgWithSlave::translateurl: no plugin for path ["<<url<< endl);
127
        return MHD_NO;
110
        return StreamProxy::Error;
128
    }
111
    }
129
112
130
    // We may need one day to subclass PlgWithSlave to implement a
113
    // We may need one day to subclass PlgWithSlave to implement a
131
    // plugin-specific method. For now, existing plugins have
114
    // plugin-specific method. For now, existing plugins have
132
    // compatible python code, and we can keep one c++ method.
115
    // compatible python code, and we can keep one c++ method.
...
...
140
    // The streaming services plugins set a trackId parameter in the
123
    // The streaming services plugins set a trackId parameter in the
141
    // URIs. This gets parsed out by mhttpd. We rebuild a full url
124
    // URIs. This gets parsed out by mhttpd. We rebuild a full url
142
    // which we pass to them for translation (they will extract the
125
    // which we pass to them for translation (they will extract the
143
    // trackid and use it, the rest of the path is bogus).
126
    // trackid and use it, the rest of the path is bogus).
144
    // The uprcl module has a real path and no trackid. Handle both cases
127
    // The uprcl module has a real path and no trackid. Handle both cases
145
    const char* stid =
128
    const auto it = querymap.find("trackId");
146
        MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND,
129
    if (it != querymap.end() && !it->second.empty()) {
147
                                    "trackId");
148
    if (stid && *stid) {
149
        path += string("?version=1&trackId=") + stid;
130
        path += string("?version=1&trackId=") + it->second;
150
    }
131
    }
151
132
152
    // Translate to Tidal/Qobuz etc real temporary URL
133
    // Translate to Tidal/Qobuz etc real temporary URL
153
    string media_url = realplg->get_media_url(path);
134
    url = realplg->get_media_url(path);
154
    if (media_url.empty()) {
135
    if (url.empty()) {
155
        LOGERR("answer_to_connection: no media_uri for: " << url << endl);
136
        LOGERR("answer_to_connection: no media_uri for: " << url << endl);
156
        return MHD_NO;
137
        return StreamProxy::Error;
157
    }
158
159
    if (media_url.find("http") == 0) {
160
        static char data[] = "<html><body></body></html>";
161
        struct MHD_Response *response =
162
            MHD_create_response_from_buffer(strlen(data), data,
163
                                            MHD_RESPMEM_PERSISTENT);
164
        if (response == NULL) {
165
            LOGERR("answer_to_connection: could not create response" << endl);
166
            return MHD_NO;
167
        }
168
        MHD_add_response_header (response, "Location", media_url.c_str());
169
        int ret = MHD_queue_response(connection, 302, response);
170
        MHD_destroy_response(response);
171
        return ret;
172
    } else {
173
        LOGERR("PlgWithSlave: got non-http URL !: " << media_url << endl);
174
        LOGERR("PlgWithSlave:   the code for handling these is gone !\n");
175
        LOGERR("    will have to fetch it from git history\n");
176
        return MHD_NO;
177
    } 
138
    }
178
}
139
    return StreamProxy::Proxy;
179
180
static int accept_policy(void *, const struct sockaddr* sa, socklen_t addrlen)
181
{
182
    return MHD_YES;
183
}
140
}
184
141
185
// Static
142
// Static
186
bool PlgWithSlave::startPluginCmd(CmdTalk& cmd, const string& appname,
143
bool PlgWithSlave::startPluginCmd(CmdTalk& cmd, const string& appname,
187
                                  const string& host, unsigned int port,
144
                                  const string& host, unsigned int port,
...
...
207
    }
164
    }
208
    return true;
165
    return true;
209
}
166
}
210
167
211
// Static
168
// Static
212
bool PlgWithSlave::maybeStartMHD(CDPluginServices *cdsrv)
169
bool PlgWithSlave::maybeStartProxy(CDPluginServices *cdsrv)
213
{
170
{
214
    if (nullptr == o_mhd) {
171
    if (nullptr == o_proxy) {
215
        int port = CDPluginServices::microhttpport();
172
        int port = CDPluginServices::microhttpport();
216
        // Start the microhttpd daemon. There can be only one, and it
173
        o_proxy = new StreamProxy(
217
        // is started with a context handle which points to whatever
218
        // plugin got there first. The callback will only use the
219
        // handle to get to the plugin services, and retrieve the
220
        // appropriate plugin based on the url path prefix.
221
        LOGDEB("PlgWithSlave: starting httpd on port "<< port << endl);
222
        o_mhd = MHD_start_daemon(
223
            MHD_USE_THREAD_PER_CONNECTION,
224
            //MHD_USE_SELECT_INTERNALLY, 
225
            port, 
174
            port,
226
            /* Accept policy callback and arg */
175
            std::bind(&translateurl, cdsrv, _1, _2));
227
            accept_policy, NULL, 
176
            
228
            /* handler and arg */
229
            &answer_to_connection, cdsrv,
230
            MHD_OPTION_END);
231
        if (nullptr == o_mhd) {
177
        if (nullptr == o_proxy) {
232
            LOGERR("PlgWithSlave: MHD_start_daemon failed\n");
178
            LOGERR("PlgWithSlave: Proxy creation failed\n");
233
            return false;
179
            return false;
234
        }
180
        }
235
    }
181
    }
236
    return true;
182
    return true;
237
}
183
}
...
...
241
{
187
{
242
    if (cmd.running()) {
188
    if (cmd.running()) {
243
        LOGDEB1("PlgWithSlave::maybeStartCmd: already running\n");
189
        LOGDEB1("PlgWithSlave::maybeStartCmd: already running\n");
244
        return true;
190
        return true;
245
    }
191
    }
246
    if (!maybeStartMHD(this->plg->m_services)) {
192
    if (!maybeStartProxy(this->plg->m_services)) {
247
        LOGDEB1("PlgWithSlave::maybeStartCmd: maybeStartMHD failed\n");
193
        LOGDEB1("PlgWithSlave::maybeStartCmd: maybeStartMHD failed\n");
248
        return false;
194
        return false;
249
    }
195
    }
250
    int port = CDPluginServices::microhttpport();
196
    int port = CDPluginServices::microhttpport();
251
    if (!startPluginCmd(cmd, plg->m_name, upnphost, port, pathprefix)) {
197
    if (!startPluginCmd(cmd, plg->m_name, upnphost, port, pathprefix)) {