#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
+#include <chrono>
#include <algorithm>
-#include <apt-pkg/strutl.h>
+#include <apt-pkg/configuration.h>
#include <apt-pkg/error.h>
+#include <apt-pkg/strutl.h>
+
+
#include "srvrec.h"
+
bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result)
{
std::string target;
// sort them by priority
std::stable_sort(Result.begin(), Result.end());
+ for(std::vector<SrvRec>::iterator I = Result.begin();
+ I != Result.end(); ++I)
+ {
+ if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
+ {
+ std::cerr << "SrvRecs: got " << I->target
+ << " prio: " << I->priority
+ << " weight: " << I->weight
+ << std::endl;
+ }
+ }
+
+ return true;
+}
+
+SrvRec PopFromSrvRecs(std::vector<SrvRec> &Recs)
+{
+ // FIXME: instead of the simplistic shuffle below use the algorithm
+ // described in rfc2782 (with weights)
+ // and figure out how the weights need to be adjusted if
+ // a host refuses connections
+
+#if 0 // all code below is only needed for the weight adjusted selection
// assign random number ranges
int prev_weight = 0;
int prev_priority = 0;
I->random_number_range_end = prev_weight + I->weight;
prev_weight = I->random_number_range_end;
prev_priority = I->priority;
+
+ if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
+ std::cerr << "SrvRecs: got " << I->target
+ << " prio: " << I->priority
+ << " weight: " << I->weight
+ << std::endl;
}
// go over the code in reverse order and note the max random range
max = I->random_number_range_end;
I->random_number_range_max = max;
}
+#endif
- // FIXME: now shuffle
+ // shuffle in a very simplistic way for now (equal weights)
+ std::vector<SrvRec>::iterator I, J;
+ I = J = Recs.begin();
+ for(;I != Recs.end(); ++I)
+ {
+ if(I->priority != J->priority)
+ break;
+ }
- return true;
+ // FIXME: meeeeh, where to init this properly
+ unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
+ std::shuffle(J, I, std::default_random_engine(seed));
+
+ // meh, no pop_front() in std::vector?
+ SrvRec selected = *Recs.begin();
+ Recs.erase(Recs.begin());
+
+ if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
+ std::cerr << "PopFromSrvRecs: selecting " << selected.target << std::endl;
+
+ return selected;
}
if(LastHost != Host || LastPort != Port)
{
SrvRecords.clear();
- bool res = GetSrvRecords(Host, DefPort, SrvRecords);
+ if (_config->FindB("Acquire::EnableSrvRecods", true) == true)
+ GetSrvRecords(Host, DefPort, SrvRecords);
}
// we have no SrvRecords for this host, connect right away
if(SrvRecords.size() == 0)
// try to connect in the priority order of the srv records
while(SrvRecords.size() > 0)
{
- Host = SrvRecords[0].target;
+ Host = PopFromSrvRecs(SrvRecords).target;
if(ConnectToHostname(Host, Port, Service, DefPort, Fd, TimeOut, Owner))
return true;
--- /dev/null
+#include <config.h>
+
+#include <apt-pkg/srvrec.h>
+
+#include <string>
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+TEST(SrvRecTest, PopFromSrvRecs)
+{
+ // the PopFromSrvRecs() is using a random number so we must
+ // run it a bunch of times to ensure we are not fooled by randomness
+ std::set<std::string> selected;
+ for(int i=0;i<100;i++)
+ {
+ std::vector<SrvRec> Meep;
+ SrvRec foo = {target:"foo", priority: 20, weight: 0, port: 80};
+ Meep.push_back(foo);
+
+ SrvRec bar = {target:"bar", priority: 20, weight: 0, port: 80};
+ Meep.push_back(bar);
+
+ EXPECT_EQ(Meep.size(), 2);
+ SrvRec result = PopFromSrvRecs(Meep);
+ selected.insert(result.target);
+ // ensure that pop removed one element
+ EXPECT_EQ(Meep.size(), 1);
+ }
+
+ // ensure that after enough runs we end up with both selected
+ EXPECT_EQ(selected.size(), 2);
+}