--- a/libupnpp/control/discovery.cxx
+++ b/libupnpp/control/discovery.cxx
@@ -30,6 +30,7 @@
#include <map> // for _Rb_tree_iterator, map, etc
#include <utility> // for pair
#include <vector> // for vector
+#include <unordered_set>
#include "description.hxx" // for UPnPDeviceDesc, etc
@@ -38,6 +39,7 @@
#include "libupnpp/upnpplib.hxx" // for LibUPnP
#include "libupnpp/upnpputils.hxx" // for timespec_addnanos
#include "libupnpp/workqueue.hxx" // for WorkQueue
+#include "libupnpp/control/httpdownload.hxx"
using namespace std;
using namespace std::placeholders;
@@ -81,6 +83,7 @@
bool alive;
string url;
+ string description;
string deviceId;
int expires; // Seconds valid
};
@@ -89,18 +92,16 @@
// discovered object descriptors for processing by our dedicated
// thread.
static WorkQueue<DiscoveredTask*> discoveredQueue("DiscoveredQueue");
+static unordered_set<string> o_downloading;
+static PTMutexInit o_downloading_mutex;
// This gets called in a libupnp thread context for all asynchronous
// events which we asked for.
// Example: ContentDirectories appearing and disappearing from the network
// We queue a task for our worker thread(s)
-// It seems that this can get called by several threads. We have a
-// mutex just for clarifying the message printing, the workqueue is
-// mt-safe of course.
+// We can get called by several threads.
static int cluCallBack(Upnp_EventType et, void* evp, void*)
{
- static PTMutexInit cblock;
- PTMutexLocker lock(cblock);
LOGDEB1("discovery:cluCallBack: " << LibUPnP::evTypeAsString(et) << endl);
switch (et) {
@@ -116,7 +117,44 @@
if (!disco->DeviceType[0] && !disco->ServiceType[0]) {
LOGDEB1("discovery:cllb:ALIVE: " << cluDiscoveryToStr(disco)
<< endl);
+ // Device signals its existence and well-being. Perform the
+ // UPnP "description" phase by downloading and decoding the
+ // description document.
+
DiscoveredTask *tp = new DiscoveredTask(1, disco);
+
+ {
+ // Note that this does not prevent multiple successive
+ // downloads of a normal url, just multiple
+ // simultaneous downloads of a slow one, to avoid
+ // tying up threads.
+ PTMutexLocker lock(o_downloading_mutex);
+ auto res = o_downloading.insert(tp->url);
+ if (!res.second) {
+ LOGDEB("discoExplorer: already downloading " <<
+ tp->url << endl);
+ return UPNP_E_SUCCESS;
+ }
+ }
+
+ LOGDEB("discoExplorer: downloading " << tp->url << endl);
+ string sdesc;
+ if (!downloadUrlWithCurl(tp->url, tp->description, 5)) {
+ LOGERR("discoExplorer: downloadUrlWithCurl error for: " <<
+ tp->url << endl);
+ {PTMutexLocker lock(o_downloading_mutex);
+ o_downloading.erase(tp->url);
+ }
+ delete tp;
+ return UPNP_E_SUCCESS;
+ }
+ LOGDEB1("discoExplorer: downloaded description document of " <<
+ tp->description.size() << " bytes" << endl);
+
+ {PTMutexLocker lock(o_downloading_mutex);
+ o_downloading.erase(tp->url);
+ }
+
if (discoveredQueue.put(tp)) {
return UPNP_E_FINISH;
}
@@ -215,27 +253,8 @@
// endl);
}
} else {
- // Device signals its existence and well-being. Perform the
- // UPnP "description" phase by downloading and decoding the
- // description document.
- char *buf = 0;
- // LINE_SIZE is defined by libupnp's upnp.h...
- char contentType[LINE_SIZE];
- int code = UpnpDownloadUrlItem(tsk->url.c_str(), &buf, contentType);
- if (code != UPNP_E_SUCCESS) {
- LOGERR(LibUPnP::errAsString("discoExplorer:UpnpDownloadUrlItem", code) <<
- " trying to fetch " << tsk->url << endl);
- delete tsk;
- continue;
- }
- string sdesc(buf);
- free(buf);
-
- LOGDEB1("discoExplorer: downloaded description document of " <<
- sdesc.size() << " bytes" << endl);
-
// Update or insert the device
- DeviceDescriptor d(tsk->url, sdesc, time(0), tsk->expires);
+ DeviceDescriptor d(tsk->url, tsk->description, time(0), tsk->expires);
if (!d.device.ok) {
LOGERR("discoExplorer: description parse failed for " <<
tsk->deviceId << endl);