1 
2 /* Web Polygraph       http://www.web-polygraph.org/
3  * Copyright 2003-2011 The Measurement Factory
4  * Licensed under the Apache License, Version 2.0 */
5 
6 #include "base/polygraph.h"
7 
8 #include "base/ObjId.h"
9 #include "base/RndPermut.h"
10 #include "runtime/HttpPrinter.h"
11 #include "runtime/httpText.h"
12 #include "runtime/IOBuf.h"
13 #include "csm/ContentCfg.h"
14 #include "csm/BodyIter.h"
15 
16 
BodyIter()17 BodyIter::BodyIter(): theContentCfg(0), theBuf(0), theContentHash(-1) {
18 }
19 
contentSize(Size aContentSize,Size aSuffixSize)20 void BodyIter::contentSize(Size aContentSize, Size aSuffixSize) {
21 	theContentSize = aContentSize;
22 	theSuffixSize = aSuffixSize;
23 }
24 
contentSize() const25 Size BodyIter::contentSize() const {
26 	if (!theSuffixSize.known()) {
27 		theSuffixSize = Should(theContentCfg && theOid) ?
28 			theContentCfg->calcContentSuffixSize(theOid) : Size(0);
29 	}
30 	// assumes response size is always known
31 	if (!theContentSize.known())
32 		calcContentSize();
33 	return theContentSize;
34 }
35 
fullEntitySize() const36 Size BodyIter::fullEntitySize() const {
37 	return contentSize();
38 }
39 
middleSizeLeft() const40 Size BodyIter::middleSizeLeft() const {
41 	if (!ShouldUs(theContentSize.known()))
42 		return theContentSize;
43 
44 	if (!(ShouldUs(theSuffixSize <= theContentSize)))
45 		return 0;
46 
47 	const Size middleGoal = theContentSize - theSuffixSize;
48 	if (theBuiltSize >= middleGoal)
49 		return 0;
50 
51 	return middleGoal - theBuiltSize;
52 }
53 
calcContentSize() const54 void BodyIter::calcContentSize() const {
55 	Should(!theContentSize.known());
56 
57 	Assert(!theBuiltSize.known());
58 	BodyIter *const encoder = clone();
59 	WrBuf buf;
60 	encoder->start(&buf);
61 	while (*encoder) {
62 		if (!encoder->pour())
63 			Assert(false);
64 		buf.reset();
65 	}
66 	theContentSize = encoder->builtSize();
67 	encoder->putBack();
68 }
69 
start(WrBuf * aBuf)70 void BodyIter::start(WrBuf *aBuf) {
71 	Assert(theOid);
72 	Assert(aBuf);
73 	theBuf = aBuf;
74 	theRng.seed(GlbPermut(theContentHash, rndBodyIter));
75 	theBuiltSize = 0;
76 }
77 
canPour() const78 bool BodyIter::canPour() const {
79 	return theBuf && !theBuf->full() && !pouredAll();
80 }
81 
pouredAll() const82 bool BodyIter::pouredAll() const {
83 	return theContentSize.known() && theBuiltSize >= theContentSize;
84 }
85 
86 // By default, pour() handles body prefix, middle, and suffix parts.
87 // BodyIterators that do not have those parts override this implementation.
pour()88 bool BodyIter::pour() {
89 	if (theBuiltSize == 0)
90 		pourPrefix();
91 	if (!pourMiddle())
92 		return false;
93 	if (canPour() && !middleSizeLeft())
94 		pourSuffix();
95 	return true;
96 }
97 
pourPrefix()98 void BodyIter::pourPrefix() {
99 	if (Should(theContentCfg && theBuf))
100 		theBuiltSize += theContentCfg->pourContentPrefix(theOid, *theBuf);
101 }
102 
pourSuffix()103 void BodyIter::pourSuffix() {
104 	if (Should(theContentCfg && theBuf))
105 		theBuiltSize += theContentCfg->pourContentSuffix(theOid, *theBuf);
106 }
107 
108 // kids must override either this method or pour() that calls this method
pourMiddle()109 bool BodyIter::pourMiddle() {
110 	Assert(false);
111 	return false;
112 }
113 
pourRandom(const Size upToSz)114 bool BodyIter::pourRandom(const Size upToSz) {
115 	const Size rndOff = IOBuf::RandomOffset(offSeed(), theBuiltSize);
116 	const RndBuf &rndBuf = theContentCfg->rndBuf();
117 	const Size poured = theBuf->appendRndUpTo(rndOff, upToSz, rndBuf);
118 	theBuiltSize += poured;
119 	return poured > 0;
120 }
121 
putHeaders(HttpPrinter & hp) const122 void BodyIter::putHeaders(HttpPrinter &hp) const {
123 	if (hp.putHeader(hfpContLength)) {
124 	const Size clen = contentSize();
125 	Assert(clen.known());
126 	// Debug suffix addition via HTTP headers. TODO: Make this configurable.
127 	//if (theSuffixSize.known())
128 	// 	hp << "X-Suffix-Length: " << theSuffixSize.byte() << crlf;
129 	hp << clen.byte() << crlf;
130 	}
131 
132 	if (theContentCfg->theMimeType &&
133 		hp.putHeader(hfpContType))
134 		hp << theContentCfg->theMimeType << crlf;
135 	if (theOid.gzipContent())
136 		hp.putHeader(hfGzipContentEncoding);
137 }
138 
putBack()139 void BodyIter::putBack() {
140 	theContentCfg->putBodyIter(this);
141 	// do not put code here
142 	// body iterator must not be used after its returned to the content configuration
143 }
144