/* 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 "base/ObjId.h"
#include "runtime/Connection.h"
#include "runtime/HostMap.h"
#include "runtime/HttpDate.h"
#include "runtime/polyErrors.h"
#include "runtime/httpText.h"
#include "runtime/globals.h"
#include "csm/ContentCfg.h"
#include "server/Server.h"
#include "cache/Cache.h"
#include "cache/DistrPoint.h"
#include "proxy/PxySrvXact.h"


void PxySrvXact::reset() {
	CacheReader::reset();
	SrvXact::reset();
	waitForCache = true;
}

// also called from noteCacheReady
void PxySrvXact::noteWriteReady(int fd) {
	Assert(theDistrPoint);

	const Size prodObjSize = theProducedSize - theRepHdrSize;
	const Size objSize = theDistrPoint->entry()->objSize();
	const Size pendSize = theDistrPoint->readySize() <= 0 ?
		Size(0) : theDistrPoint->readySize() - prodObjSize;

	if (objSize >= 0) {
		Assert(prodObjSize < objSize);
		if (theRepSize.expected().known())
			Assert(objSize == theRepSize.expected() - theRepHdrSize);
	}

	waitForCache = pendSize <= 0;

	if (waitForCache) {
		// writer may not exist yet
		// XXX: we need to set a timeout if waiting for the writer
		// Assert(theDistrPoint->writer());
		Assert(prodObjSize < objSize || objSize < 0);
		theConn->theWr.stop(this);
	} else {
		Assert(pendSize > 0);
		theConn->maxIoSize(pendSize);
		SrvXact::noteWriteReady(fd);
	}
}

// returns content length
Size PxySrvXact::put200OkHead(ostream &os) {
	Assert(theDistrPoint);
	if (theDistrPoint->hasRepHdr())
		return put200OkMiss(os);
	else
		return put200OkHit(os);
}

Size PxySrvXact::put200OkMiss(ostream &os) {
	const RepHdr &rep = theDistrPoint->cachedRepHdr();

	os << (theOid.cachable() ? hfCcCachable : hfCcUncachable);

	HttpDatePrint(os << hfpDate, rep.theDate >= 0 ? rep.theDate : TheClock.time()) << crlf;
	
	// indicate persistency (HTTP/1.0 defaults to non-persistent)
	if (theConn->reusable())
		os << hfConnAlivePxy;

	if (rep.theExpires >= 0)
		HttpDatePrint(os << hfpExpires, rep.theExpires) << crlf;

	if (rep.theLMT >= 0)
		HttpDatePrint(os << hfpLmt, rep.theLMT) << crlf;

	Assert(rep.theContSize >= 0);
	os << hfpContLength << (int)rep.theContSize << crlf;

	if (theContentCfg->theMimeType) // XXX: should copy
		os << hfpContType << theContentCfg->theMimeType << crlf;

	if (rep.theTarget)
		os << hfpXTarget << rep.theTarget << crlf;

	if (rep.theXactId)
		os << hfpXXact << rep.theGroupId << ' ' << rep.theXactId << crlf;

	if (rep.thePhaseSyncPos)
		os << hfpXPhaseSyncPos << rep.thePhaseSyncPos << crlf;

	return rep.theContSize;
}

Size PxySrvXact::put200OkHit(ostream &os) {
	os << (theOid.cachable() ? hfCcCachable : hfCcUncachable);

	HttpDatePrint(os << hfpDate) << crlf;
	
	// indicate persistency (HTTP/1.0 defaults to non-persistent)
	if (theConn->reusable())
		os << hfConnAlivePxy;

	if (theTimes.knownExp())
		HttpDatePrint(os << hfpExpires, theTimes.exp()) << crlf;

	if (theTimes.showLmt())
		HttpDatePrint(os << hfpLmt, theTimes.lmt()) << crlf;

	const Size clen = theContentCfg->calcRepSize(theOid);
	os << hfpContLength << (int)clen << crlf;

	if (theContentCfg->theMimeType)
		os << hfpContType << theContentCfg->theMimeType << crlf;

	os << hfpXXact << TheGroupId << ' ' << theId << crlf;

	return clen;
}

// XXX: nobody calls these?
void PxySrvXact::noteWriterLeft() {
	finish(errPrematureEof);
}

void PxySrvXact::noteCacheReady() {
	if (waitForCache)
		SrvXact::noteWriteReady(theConn->fd()); 
}

void PxySrvXact::newState(State aState) {
	SrvXact::newState(aState);
	if (theState == stSpaceWaiting) {
		Assert(!theDistrPoint);

		theOid.hit(theOwner->cache()->cached(theOid));

		theDistrPoint = theOwner->cache()->addReader(theOid, this);
		if (!theDistrPoint) {
			// the cache must report an error
			// XXX: newState(stSpaceWaiting) may not be the last call in chain!
			finish(-1);
			return;
		}
	} else
	if (theState == stDone) {
		if (theDistrPoint) {
			theDistrPoint->delReader(this); // XXX: check that it resets theDistrPoint!
			Assert(!theDistrPoint);
		}
	}
}

const ReqHdr *PxySrvXact::origReqHdrs() const {
	return &theReqHdr;
}

Error PxySrvXact::setTarget(const NetAddr &target) {
	Assert(theOwner->hostIdx() < 0);

	int targetIdx = -1;
	if (!TheHostMap->find(target, targetIdx))
		return errForeignTarget;

	theOid.target(targetIdx);
	return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1