/* Web Polygraph http://www.web-polygraph.org/
* (C) 2003-2006 The Measurement Factory
* Licensed under the Apache License, Version 2.0 */
#include "base/polygraph.h"
#include <fstream>
#include "xstd/h/iomanip.h"
#include "xstd/rndDistrs.h"
#include "xstd/StrIdentifier.h"
#include "xstd/Ssl.h"
#include "base/RndPermut.h"
#include "base/OidGenStat.h"
#include "pgl/RobotSym.h"
#include "pgl/SessionSym.h"
#include "pgl/AclSym.h"
#include "pgl/GoalSym.h"
#include "pgl/PglStaticSemx.h"
#include "runtime/AddrMap.h"
#include "runtime/HostMap.h"
#include "runtime/PubWorld.h"
#include "runtime/PopModel.h"
#include "runtime/Goal.h"
#include "runtime/XactAbortCoord.h"
#include "runtime/LogComment.h"
#include "runtime/httpHdrs.h"
#include "csm/XmlTagIdentifier.h"
#include "client/WarmupPlan.h"
#include "client/ServerRep.h"
#include "client/RegExGroup.h"
#include "client/ForeignWorld.h"
#include "client/Client.h"
#include "client/CltCfg.h"
#include "client/CltOpts.h"
Memberships CltCfg::TheGlbMemberships;
CltCfg::CltCfg(): theRobot(0),
theOriginSel(0), theInterestSel(0), theReqTypeSel(0), theReqMethodSel(0),
thePopModel(0),
theBusyPeriod(0), theIdlePeriodDur(0), theProxyCycleCnt(0),
thePipelineDepth(0),
theRecurRatio(-1), theEmbedRecurRatio(-1),
theAbortProb(0), theAuthError(0),
theWaitXactLmt(-1), theIcpPort(-1),
theCredentialCycleCnt(0), theForeignWorld(0),
theContainerTags(0),
theAcceptedContentCodings(0), acceptingGzipContent(false),
theCookiesKeepLimitSel(0),
theWarmupPlan(0),
genUniqUrls(false), didWarmup(false) {
}
CltCfg::~CltCfg() {
while (theCredentials.count())
delete theCredentials.dequeue();
while (theProxies.count())
delete theProxies.dequeue();
delete theContainerTags;
delete theAcceptedContentCodings;
delete thePipelineDepth;
delete theCookiesKeepLimitSel;
}
void CltCfg::configure(const RobotSym *aRobot) {
AgentCfg::configure(aRobot);
Assert(!theRobot && aRobot);
theRobot = aRobot;
theRobot->waitXactLimit(theWaitXactLmt);
thePeerHttp = theRobot->peerHttp();
thePeerIcp = theRobot->peerIcp();
theRobot->icpPort(theIcpPort);
theRobot->recurRatio(theRecurRatio);
theRobot->embedRecurRatio(theEmbedRecurRatio);
theRobot->abortProb(theAbortProb);
theRobot->authError(theAuthError);
theRobot->uniqueUrls(genUniqUrls);
theUriThrower = theRobot->rawUriThrower();
if (PopModelSym *pms = theRobot->popModel()) {
thePopModel = new PopModel;
thePopModel->configure(pms);
}
configureInterests();
configureReqTypes();
configureReqMethods();
configureOrigins();
configureProxies();
configureCredentials();
configureMemberships();
configureContainerTags();
configureAcceptedContentCodings();
configurePipeDepth();
if (AclSym *acl = theRobot->acl())
theAcl.configure(*acl);
if (const String fname = theRobot->foreignTrace())
configureTrace(fname);
if (SessionSym *ss = theRobot->session()) {
if (GoalSym *bps = ss->busyPeriod()) {
theBusyPeriod = new Goal;
theBusyPeriod->configure(*bps);
}
theIdlePeriodDur = ss->idlePeriodDuration();
ss->heartbeatGap(theSessionHeartbitGap);
if (!theBusyPeriod != !theIdlePeriodDur) {
cerr << theRobot->loc() << "both busy and idle periods "
<< "should be specified (or not specified) for a robot session"
<< endl << xexit;
}
}
if (!thePopModel && theRecurRatio > 0)
cerr << theRobot->loc() << "popularity model must be specified for"
<< " positive recurrence ratio (robot " << theRobot->kind() << ')'
<< endl << xexit;
theCookiesKeepLimitSel = theRobot->cookiesKeepLimit();
}
int CltCfg::viservLimit() const {
return TheHostMap->iterationCount();
}
void CltCfg::configureInterests() {
static StrIdentifier sidf;
if (!sidf.count()) {
sidf.add("private", OidGenStat::intPrivate);
sidf.add("public", OidGenStat::intPublic);
sidf.add("foreign", OidGenStat::intForeign);
sidf.optimize();
}
theInterestSel = theRobot->interests(sidf);
if (!theInterestSel)
theInterestSel = new ConstDistr(new RndGen, OidGenStat::intPrivate); // default
theInterestSel->rndGen(LclRndGen("client_interests"));
}
void CltCfg::configureReqTypes() {
static StrIdentifier sidf;
if (!sidf.count()) {
sidf.add("Basic", Client::rqtBasic);
sidf.add("Ims200", Client::rqtIms200);
sidf.add("Ims304", Client::rqtIms304);
sidf.add("Reload", Client::rqtReload);
sidf.optimize();
}
theReqTypeSel = theRobot->msgTypes(sidf);
if (!theReqTypeSel)
theReqTypeSel = new ConstDistr(new RndGen, Client::rqtBasic); // default
theReqTypeSel->rndGen(LclRndGen("client_req_types"));
}
void CltCfg::configurePipeDepth() {
thePipelineDepth = theRobot->pipelineDepth();
if (thePipelineDepth)
thePipelineDepth->rndGen(LclRndGen("client_pipe_depth"));
}
void CltCfg::configureReqMethods() {
static StrIdentifier sidf;
if (!sidf.count()) {
sidf.add("GET", Client::rqmGet);
sidf.add("HEAD", Client::rqmHead);
sidf.add("POST", Client::rqmPost);
sidf.add("PUT", Client::rqmPut);
sidf.optimize();
}
theReqMethodSel = theRobot->reqMethods(sidf);
if (!theReqMethodSel)
theReqMethodSel = new ConstDistr(new RndGen, Client::rqmGet); // default
theReqMethodSel->rndGen(LclRndGen("client_req_methods"));
}
void CltCfg::configureOrigins() {
Array<NetAddr*> origNames;
RndDistr *iad = 0;
if (!theRobot->origins(origNames, theOriginSel) || !origNames.count()) {
if (!theRobot->reqInterArrival(iad) || iad)
cerr << theRobot->loc() << "no origin addresses specified for active robot "
<< theRobot->kind() << endl << xexit;
return;
}
Assert(theOriginSel);
theOriginSel->rndGen(LclRndGen("client_origins"));
theViservs.stretch(origNames.count());
for (int i = 0; i < origNames.count(); ++i)
addOrigName(*origNames[i]);
}
// converts origin name into viserv idx
void CltCfg::addOrigName(const NetAddr &oname) {
if (!TheAddrMap->has(oname)) {
if (oname.isDomainName())
cerr << here << "visible server name " << oname
<< " is not found in address maps" << endl << xexit;
TheAddrMap->add(oname);
}
int viserv = -1;
if (!TheHostMap->find(oname, viserv))
TheHostMap->addAt(viserv, oname);
HostCfg *host = TheHostMap->at(viserv);
if (!host->thePubWorld)
PubWorld::Add(host, new PubWorld(UniqId::Create()));
if (!host->theServerRep)
host->theServerRep = new ServerRep(oname, viserv);
// quit if an origin entry is repeated because it complicates
// private world accounting (two origins would have one world)
if (hasViserv(viserv)) {
Comment << theRobot->loc() << "error: origin " << oname <<
" is listed more than once in robot's origins" <<
endc << xexit;
}
theViservs.append(viserv);
checkTargets(viserv);
}
// checks content cfg and creates new server representative for viserv
void CltCfg::checkTargets(int viserv) {
const NetAddr &visName = TheHostMap->at(viserv)->theAddr;
int niamIdx; // name in AddrMap index
Assert(TheAddrMap->find(visName, niamIdx));
for (AddrMapAddrIter i = TheAddrMap->addrIter(niamIdx); i; ++i) {
const NetAddr &addr = i.addr();
int targetIdx = -1;
if (!TheHostMap->find(addr, targetIdx) ||
!TheHostMap->at(targetIdx)->theContent) {
Comment << theRobot->loc() << "error: Robot cannot find"
<< " configuration for server@" << addr;
if (addr != i.name())
Comment << " (visible as " << i.name() << ")";
Comment << endc << xexit;
}
}
}
void CltCfg::configureProxies() {
Array<NetAddr*> addrs;
theRobot->proxies(addrs);
theProxies.resize(addrs.count());
while (addrs.count())
theProxies.enqueue(addrs.pop());
if (TheCltOpts.theProxyAddr && theProxies.count()) {
Comment << theRobot->loc() << "--proxy option and "
<< "Robot.proxies field are mutually exclusive" << endc << xexit;
}
}
void CltCfg::configureCredentials() {
Array<String*> creds;
theRobot->credentials(creds);
theCredentials.resize(creds.count());
while (creds.count())
theCredentials.enqueue(creds.pop());
}
void CltCfg::configureContainerTags() {
Array<String*> tags;
if (!theRobot->containerTags(tags))
tags.append(new String("<embed src>")); // default
theContainerTags = new XmlTagIdentifier();
theContainerTags->configure(tags);
}
void CltCfg::configureAcceptedContentCodings() {
Array<String*> codings;
if (theRobot->acceptedContentCodings(codings)) {
theAcceptedContentCodings = new String;
for (int i = 0; i < codings.count(); ++i) {
const String &coding = *codings[i];
if (coding.startsWith("*") || coding.startsWith("gzip"))
acceptingGzipContent = true;
if (*theAcceptedContentCodings)
*theAcceptedContentCodings += ", ";
*theAcceptedContentCodings += coding;
delete codings[i];
}
}
}
void CltCfg::configureMemberships() {
// initialize global array once
const Array<MembershipMapSym*> &syms = PglStaticSemx::TheMembershipsToUse;
if (TheGlbMemberships.count() != syms.count()) {
TheGlbMemberships.stretch(syms.count());
for (int i = 0; i < syms.count(); ++i) {
MembershipMap *m = new MembershipMap;
m->configure(*syms[i], i+1);
TheGlbMemberships.append(m);
}
}
for (int i = 0; i < TheGlbMemberships.count(); ++i) {
MembershipMap *g = TheGlbMemberships[i];
bool belongs = false;
for (int u = 0; !belongs && u < theCredentials.count(); ++u) {
belongs = g->hasMember(*theCredentials.top(u));
}
if (belongs)
theLclMemberships.append(g);
}
}
void CltCfg::configureTrace(const String &fname) {
theForeignWorld = new ForeignWorld;
theForeignWorld->configure(fname);
}
bool CltCfg::hasViserv(int viserv) const {
for (int origin = 0; origin < theViservs.count(); ++origin) {
if (theViservs[origin] == viserv)
return true;
}
return false;
}
bool CltCfg::selectCredentials(String &newCred) {
if (theCredentials.empty())
return false;
// randomize in the beginning of each cycle
if (theCredentialCycleCnt >= theCredentials.count())
theCredentialCycleCnt = 0;
if (!theCredentialCycleCnt) {
static RndGen rng;
theCredentials.randomize(rng);
}
theCredentialCycleCnt++;
String *credential = theCredentials.dequeue();
theCredentials.enqueue(credential);
newCred = *credential;
return true;
}
int CltCfg::selectInterest() {
const int interest = (int)theInterestSel->trial();
Assert(interest == OidGenStat::intPrivate ||
interest == OidGenStat::intPublic ||
interest == OidGenStat::intForeign);
return interest;
}
int CltCfg::selectViserv() {
if (theWarmupPlan) {
const int viservIdx = theWarmupPlan->selectViserv();
if (viservIdx >= 0)
return viservIdx;
stopWarmup();
}
const int viservIdx = (int)theOriginSel->trial();
Assert(0 <= viservIdx && viservIdx < theViservs.count());
return theViservs[viservIdx];
}
bool CltCfg::selectProxy(NetAddr &newAddr) {
if (TheCltOpts.theProxyAddr) {
newAddr = TheCltOpts.theProxyAddr;
return true;
}
if (!theProxies.count())
return false;
// randomize in the beginning of each cycle
if (theProxyCycleCnt >= theProxies.count())
theProxyCycleCnt = 0;
if (!theProxyCycleCnt) {
static RndGen rng;
theProxies.randomize(rng);
}
theProxyCycleCnt++;
NetAddr *addr = theProxies.dequeue();
theProxies.enqueue(addr);
newAddr = *addr;
return true;
}
// XXX: merge this with SrvCfg::selectAbortCoord()
void CltCfg::selectAbortCoord(XactAbortCoord &coord) {
static RndGen rng1, rng2; // uncorrelated unless theAbortProb is 1
if (rng1.event(theAbortProb)) {
const int whether = rng2.state();
(void)rng2.trial();
coord.configure(rng2.state(), whether);
} else {
const int whether = rng1.state();
(void)rng1.trial();
coord.configure(whether, rng1.state());
}
}
int CltCfg::findMemberships(const String &user, Memberships &groups) const {
if (user) {
for (int i = 0; i < theLclMemberships.count(); ++i) {
MembershipMap *g = theLclMemberships[i];
if (g->hasMember(user))
groups.append(g);
}
}
return groups.count();
}
bool CltCfg::followAllUris(const RepHdr &rep) const {
if (theUriThrower.len() > 0) {
return theUriThrower == "*" ||
rep.theServer.startsWith(theUriThrower);
} else {
return false;
}
}
// may be called multiple times
void CltCfg::startWarmup() {
if (!theWarmupPlan && !didWarmup)
theWarmupPlan = new WarmupPlan(theViservs);
}
void CltCfg::stopWarmup() {
Assert(theWarmupPlan && !didWarmup);
delete theWarmupPlan;
theWarmupPlan = 0;
didWarmup = true;
}
/* CltSharedCfgs */
CltCfg *CltSharedCfgs::getConfig(const RobotSym *rs) {
for (int i = 0; i < count(); ++i) {
if (item(i)->theRobot == rs)
return item(i);
}
return addConfig(rs);
}
CltCfg *CltSharedCfgs::addConfig(const RobotSym *rs) {
CltCfg *cfg = new CltCfg;
cfg->configure(rs);
append(cfg);
return cfg;
}
syntax highlighted by Code2HTML, v. 0.9.1