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/RndPermut.h"
9 #include "base/StatIntvlRec.h"
10 #include "csm/oid2Url.h"
11 #include "csm/BodyIter.h"
12 #include "csm/ContentMgr.h"
13 #include "runtime/globals.h"
14 #include "runtime/httpText.h"
15 #include "runtime/polyErrors.h"
16 #include "runtime/AddrMap.h"
17 #include "runtime/ErrorMgr.h"
18 #include "runtime/HostMap.h"
19 #include "runtime/HttpDate.h"
20 #include "runtime/HttpPrinter.h"
21 #include "runtime/LogComment.h"
22 #include "runtime/ObjUniverse.h"
23 #include "runtime/PopModel.h"
24 #include "runtime/SharedOpts.h"
25 #include "runtime/StatPhase.h"
26 #include "runtime/StatPhaseMgr.h"
27 #include "runtime/StatPhaseSync.h"
28 #include "server/Server.h"
29 #include "server/SrvCfg.h"
30 
31 #include "server/HttpSrvXact.h"
32 
HttpSrvXact()33 HttpSrvXact::HttpSrvXact() {
34 	HttpSrvXact::reset();
35 }
36 
reset()37 void HttpSrvXact::reset() {
38 	SrvXact::reset();
39 	theReqHdr.reset();
40 	theHttpVersion.reset();
41 	theTimes.reset();
42 	theRanges.clear();
43 	the100ContinueState = csNone;
44 	theCookiesSentCount = 0;
45 
46 	theRepFlags = 0;
47 }
48 
doStart()49 void HttpSrvXact::doStart() {
50 	theHttpVersion = theOwner->httpVersion(); // may be downgraded later
51 	newState(stHdrWaiting);
52 	SrvXact::doStart();
53 }
54 
logStats(OLog & ol) const55 void HttpSrvXact::logStats(OLog &ol) const {
56 	SrvXact::logStats(ol);
57 	ol << (int)theReqHdr.theIms.sec();
58 }
59 
noteBodyDataReady()60 void HttpSrvXact::noteBodyDataReady() {
61 	consume(theReqSize.expectToGet(theConn->theRdBuf.contSize()));
62 	theConn->theRdBuf.pack();
63 	if (theReqSize.gotAll()) { // got everything expected
64 		theConn->theRd.stop(this);
65 		newState(stSpaceWaiting);
66 		if (theLastReqByteTime < 0)
67 			theLastReqByteTime = TheClock - theStartTime;
68 		if (the100ContinueState == csAllowed)
69 			the100ContinueState = csDone; // send no "100 Continue" if got all
70 	} else
71 	if (theConn->atEof()) { // premature eof
72 		if (theHttpStatus == RepHdr::sc417_ExpectationFailed) {
73 			finish(0); // not an error
74 			return;
75 		} else
76 		if (cfgAbortedReq()) {
77 			theOid.aborted(true);
78 			finish(0); // not an error, configuration told client to abort
79 			return;
80 		} else {
81 			finish(errPrematureEof);
82 			return;
83 		}
84 	} else // client probably wants to send more body data
85 	if (the100ContinueState == csAllowed || the100ContinueState == csDenied)
86 		newState(stSpaceWaiting); // but we need to respond with 100 or 417
87 }
88 
noteBufReady()89 void HttpSrvXact::noteBufReady() {
90 	// keep the write buffer full
91 	if (the100ContinueState != csAllowed && theBodyIter)
92 		theBodyIter->pour();
93 }
94 
noteHdrDataReady()95 void HttpSrvXact::noteHdrDataReady() {
96 	if (theReqHdr.parse(theConn->theRdBuf.content(), theConn->theRdBuf.contSize())) {
97 		theReqSize.expect(theReqHdr.theHdrSize);
98 		if (const Error err = interpretHeader()) {
99 			finish(err);
100 			return;
101 		}
102 		Assert(the100ContinueState != csDone);
103 		newState(stBodyWaiting); // may become stSpaceWaiting to send 100/417
104 	} else
105 	if (theConn->theRdBuf.full()) { // header too big
106 		finish(errHugeHdr);
107 		return;
108 	} else
109 	if (theConn->atEof()) {         // premature end of headers
110 		finish(errPrematureEoh);
111 		return;
112 	}
113 }
114 
noteRepSent()115 void HttpSrvXact::noteRepSent() {
116 	if (the100ContinueState == csAllowed) {
117 		theContinueMsgTime = TheClock - theStartTime;
118 		the100ContinueState = csDone;
119 		theRepSize.reset();
120 		if (!theConn->theRd.theReserv)
121 			theConn->theRd.start(this);
122 		Assert(theState == stSpaceWaiting);
123 		newState(stBodyWaiting);
124 	} else
125 		finish(0);
126 }
127 
128 // decide what kind of reply to build
makeRep(WrBuf & buf)129 void HttpSrvXact::makeRep(WrBuf &buf) {
130 	Assert(theRepContentCfg);
131 
132 	const bool acceptableCoding =
133 		theRepContentCfg->calcContentCoding(theOid, theReqHdr);
134 
135 	// timestamps, cachability, aborts may be used for all types of replies
136 	theRepContentCfg->calcTimes(theOid, theTimes);
137 
138 	theOid.cachable(theRepContentCfg->calcCachability(theOid));
139 	theOwner->cfg()->selectAbortCoord(theAbortCoord);
140 	theOwner->selectRepType(theOid);
141 
142 	if (!theReqHdr.theRanges.empty()) {
143 		theOid.range(true);
144 		Assert(!theBodyIter);
145 		theBodyIter = theRepContentCfg->getBodyIter(theOid, &theRanges);
146 		normalizeRanges();
147 	}
148 
149 	const char *repStart = buf.space();
150 	HttpPrinter hp(buf.space(), buf.spaceSize(),
151 		theReqHdr.theXactId ? theReqHdr.theXactId : theId); // foreign requests get internal server xact ID
152 
153 	if (the100ContinueState == csAllowed)
154 		make100Continue(hp);
155 	else
156 	if (the100ContinueState == csDenied)
157 		make417ExpectationFailed(hp);
158 	else
159 	if (!acceptableCoding)
160 		make406NotAcceptable(hp);
161 	else
162 	if (shouldMake302Found() && make302Found(hp))
163 		; // nothing to be done here
164 	else
165 	if (shouldMake304NotMod())
166 		make304NotMod(hp);
167 	else
168 	if (shouldMake416RequestedRangeNotSatisfiable())
169 		make416RequestedRangeNotSatisfiable(hp);
170 	else
171 	if (theOid.patch())
172 		make204NoContent(hp);
173 	else
174 		make2xxContent(hp);
175 
176 	buf.appended(Size(hp.tellp()));
177 
178 	Assert(theRepSize.header().known() && theRepSize.expected() >= theRepSize.header());
179 
180 	theAbortSize = theAbortCoord.pos(theRepSize.header(), theRepSize.expected()-theRepSize.header());
181 
182 	// dump reply header
183 	static int respCount = 0;
184 	if (!respCount++ || TheOpts.theDumpFlags(dumpRep, dumpHdr))
185 		printMsg(repStart, theRepSize.header());
186 }
187 
interpretHeader()188 Error HttpSrvXact::interpretHeader() {
189 	// do this before we start overwriting or dumping the URL/oid
190 	theOid = theReqHdr.theUri.oid;
191 	theOid.secure(theConn->sslActive());
192 
193 	// set default port for Host: header
194 	if (!theReqHdr.theUri.host.port())
195 		theReqHdr.theUri.host.port(theOid.secure() ? 443 : 80);
196 
197 	// XXX: pxy server cannot support ignoreUrls; see Server::hostIdx
198 	const bool ignoreUrls = grokUrl(theReqHdr.isHealthCheck);
199 
200 	// XXX: report healthchecks once in a while and collect stats
201 	if (theReqHdr.isHealthCheck) {
202 		static bool didOnce = false;
203 		if (!didOnce) {
204 			Comment(6) << "fyi: first health check received from " << theConn->raddr() << ':' << endc;
205 			printMsg(theConn->theRdBuf, theReqHdr.theHdrSize);
206 			didOnce = true;
207 		}
208 	}
209 
210 	// dump request header
211 	static int reqCount = 0;
212 	if (!reqCount++ || TheOpts.theDumpFlags(dumpReq, dumpHdr))
213 		printMsg(theConn->theRdBuf, theReqHdr.theHdrSize);
214 
215 	if (theOid.foreignUrl())
216 		return errForeignUrl;
217 
218 	// downgrade HTTP version if needed
219 	if (theReqHdr.theHttpVersion < theHttpVersion)
220 		theHttpVersion = theReqHdr.theHttpVersion;
221 
222 	// calculate request size
223 	if (theReqHdr.expectBody()) {
224 		theReqOid.type(TheUnknownContentId);
225 		theReqSize.expectedBody(true);
226 		if (theReqHdr.theContSize < 0)
227 			return errReqBodyButNoCLen;
228 		theReqSize.expectMore(theReqHdr.theContSize);
229 	} else {
230 		theReqOid.type(TheBodilessContentId);
231 		if (!(theOid.get() && theReqHdr.theContSize == 0) &&
232 			theReqHdr.theContSize >= 0)
233 			return errUnexpectedCLen;
234 	}
235 
236 	if (!ignoreUrls) {
237 		const NetAddr &host = theReqHdr.theUri.host;
238 		if (!host.knownAddr())
239 			return errNoHostName;
240 
241 		if (const Error err = setViserv(host))
242 			return err; // setViserv called finish
243 
244 		if (theOid.target() < 0) {
245 			// currently, servers do not use target but we complain
246 			// do identify prefetch requests and such
247 			if (!theReqHdr.theTarget && ReportError(errNoTarget) &&
248 				TheOpts.theDumpFlags(dumpErr, dumpAny))
249 					printMsg(theConn->theRdBuf, theReqHdr.theHdrSize);
250 			if (const Error err = setTarget(theReqHdr.theTarget))
251 				return err; // setTarget called finish
252 			Assert(theOid.target() >= 0);
253 		}
254 
255 		if (const Error err = checkUri()) // must be done before we consume()
256 			return err;
257 	}
258 
259 	// note: clt and srv code assume we consume only after parse!
260 	consume(theReqHdr.theHdrSize);
261 	theConn->theRdBuf.pack();
262 
263 	// update history for this server
264 	if (theOid.viserv() >= 0 && theReqHdr.theLocWorld)
265 		updateUniverse(theReqHdr.theLocWorld);
266 
267 	doPhaseSync(theReqHdr);
268 
269 	// note if the client will close the connection
270 	theConn->lastUse(!theReqHdr.persistentConnection());
271 
272 	theOid.reload(!theReqHdr.isCachable);
273 
274 	// get content specs for this reply
275 	Assert(theOid.type() >= ContType::NormalContentStart());
276 	theRepContentCfg = TheContentMgr.get(theOid.type());
277 
278 	if (theReqHdr.expect100Continue) {
279 		static RndGen rng;
280 		Assert(the100ContinueState == csNone);
281 		if (theOwner->cfg()->theReqBodyAllowed < 0 ||
282 			rng.event(theOwner->cfg()->theReqBodyAllowed))
283 			the100ContinueState = csAllowed;
284 		else
285 			the100ContinueState = csDenied;
286 	}
287 
288 	theReqFlags = theReqHdr.theXactFlags;
289 
290 	Assert(!theRepSize.header().known());
291 
292 	return 0;
293 }
294 
295 // merge with CltXact::cfgAbortedReply()?
cfgAbortedReq() const296 bool HttpSrvXact::cfgAbortedReq() const {
297 	if (!theReqHdr.theAbortCoord)
298 		return false;
299 
300 	const Size abSz = theReqHdr.theAbortCoord.pos(theReqHdr.theHdrSize, theReqSize.expected()-theReqHdr.theHdrSize);
301 	if (!Should(abSz >= 0))
302 		return false;
303 
304 	if (abSz > theReqSize.actual())
305 		return false;
306 
307 	return true;
308 }
309 
310 // check that requested URI belongs to our server
checkUri()311 Error HttpSrvXact::checkUri() {
312 	const int pathLen = theReqHdr.theUri.pathLen;
313 	const bool sane = Should(pathLen >= 0) &&
314 		Should(pathLen <= theConn->theRdBuf.contSize()) &&
315 		Should(theReqHdr.theUri.pathBuf);
316 
317 	if (!sane) { // no point in high-level checks
318 		Comment << "internal error: inconsistent request, salvaged" << endc;
319 		return errOther;
320 	}
321 
322 	// first check if the content type belongs to us
323 	if (!theOwner->cfg()->hasContType(theOid.type())) {
324 		if (ReportError(errMisdirRequest)) {
325 			Comment << "host " << theOwner->host()
326 				<< " does not have content type implied by request URI: ";
327 			Comment.write(theReqHdr.theUri.pathBuf, pathLen);
328 			Comment << endc;
329 		}
330 		return errOther;
331 	}
332 
333 	// we cannot compare Oid2UrlPath() with pathBuf of special URLs
334 	if (pathLen >= 4 && strncmp("/pg/", theReqHdr.theUri.pathBuf, 4) == 0)
335 		return 0;
336 
337 	static char buf[16*1024];
338 	ofixedstream os(buf, sizeof(buf)/sizeof(*buf));
339 	Oid2UrlPath(theOid, os);
340 	os << ends;
341 	buf[sizeof(buf)/sizeof(*buf) - 1] = '\0';
342 
343 	const bool res = (pathLen+1 == (int)os.tellp()) &&
344 		strncmp(buf, theReqHdr.theUri.pathBuf, pathLen) == 0;
345 
346 	if (!res) {
347 		if (ReportError(errMisdirRequest)) {
348 			Comment << "host: " << theOwner->host() << endc;
349 			(Comment << "received: ").write(theReqHdr.theUri.pathBuf, pathLen);
350 			Comment << endc;
351 			Oid2UrlPath(theOid, Comment << "expected: ");
352 			Comment << endc;
353 		}
354 		return errOther;
355 	}
356 	return 0;
357 }
358 
setViserv(const NetAddr & name)359 Error HttpSrvXact::setViserv(const NetAddr &name) {
360 	int viserv = -1;
361 	HostCfg *host = TheHostMap->find(name, viserv);
362 	if (!host) {
363 		host = TheHostMap->find(theOwner->host(), viserv);
364 		const bool salvaged = Should(host != 0);
365 
366 		int nameIdx = -1;
367 		bool mapsToHostIp = false;
368 		if (host && name.isDomainName() && TheAddrMap->find(name, nameIdx)) {
369 			for (AddrMapAddrIter i = TheAddrMap->addrIter(nameIdx); !mapsToHostIp && i; ++i) {
370 				mapsToHostIp = (i.addr().addrA() == theOwner->host().addrA());
371 			}
372 		}
373 
374 		if (!mapsToHostIp && ReportError(errForeignHostName)) {
375 			Comment << theOwner->host() << " server received request for " <<
376 				name << " which is not an address of any visible server, " <<
377 				(salvaged ? "salvaged" : "aborting transaction") <<
378 				endc;
379 		}
380 		if (!salvaged)
381 			return errForeignHostName;
382 	}
383 
384 	if (!host->theUniverse)
385 		ObjUniverse::Add(host, new ObjUniverse());
386 
387 	theOid.viserv(viserv);
388 	return 0;
389 }
390 
setTarget(const NetAddr &)391 Error HttpSrvXact::setTarget(const NetAddr &) {
392 	// XXX: the check below should know about DNS RR, etc.
393 	//if (target != theOwner->host())
394 	//	ReportError(errMisdirRequest);
395 
396 	theOid.target(theOwner->hostIdx());
397 	return 0;
398 }
399 
400 // take all ranges from theReqHdr and do "normalization"
401 // That is:
402 // 1. Remove unsatisfiable ranges.
403 // 2. Convert to a-b form where 0 <= a <= b <= full-entity-size - 1
404 //
405 // result is stored in theRanges member
normalizeRanges()406 void HttpSrvXact::normalizeRanges() {
407 	Assert(theRanges.empty());
408 	const Size contSize = theBodyIter->fullEntitySize();
409 
410 	for (RangeList::const_iterator i = theReqHdr.theRanges.begin();
411 		i != theReqHdr.theRanges.end();
412 		++i) {
413 		if (i->theFirstByte >= contSize ||
414 			(i->theFirstByte < 0 &&
415 			i->theLastByte == 0))
416 			// Unsatisfiable range
417 			continue;
418 
419 		ByteRange range;
420 		if (i->theFirstByte >= 0) {
421 			range.theFirstByte = i->theFirstByte;
422 			if (i->theLastByte < 0 ||
423 				i->theLastByte >= contSize)
424 				range.theLastByte = contSize - Size(1);
425 			else
426 				range.theLastByte = i->theLastByte;
427 		} else {
428 			// Suffix range
429 			if (i->theLastByte < contSize)
430 				range.theFirstByte = contSize - i->theLastByte;
431 			else
432 				range.theFirstByte = 0;
433 			range.theLastByte = contSize - Size(1);
434 		}
435 		theRanges.push_back(range);
436 	}
437 }
438 
make100Continue(HttpPrinter & hp)439 void HttpSrvXact::make100Continue(HttpPrinter &hp) {
440 	putResponseLine(hp, rls100Continue);
441 	hp << crlf; // end-of-headers
442 
443 	theRepSize.header(Size(hp.tellp()));
444 	theRepSize.expect(theRepSize.header());
445 }
446 
447 // build headers for a happy "200 OK" or "206 Partial Content" reply
make2xxContent(HttpPrinter & hp)448 void HttpSrvXact::make2xxContent(HttpPrinter &hp) {
449 	if (theBodyIter == 0)
450 		theBodyIter = theRepContentCfg->getBodyIter(theOid, &theRanges);
451 
452 	if (!theOid.range()) {
453 		theHttpStatus = RepHdr::sc200_OK;
454 		putResponseLine(hp, rls200Ok);
455 	} else {
456 		theHttpStatus = RepHdr::sc206_PartialContent;
457 		putResponseLine(hp, rls206PartialContent);
458 	}
459 
460 	put2xxContentHead(hp);
461 	hp << crlf; // end-of-headers
462 
463 	theRepSize.header(Size(hp.tellp()));
464 	if (theOid.head()) {
465 		// do not send message body for HEAD
466 		theRepSize.expect(theRepSize.header());
467 		theBodyIter->putBack();
468 		theBodyIter = 0;
469 		theOid.type(TheBodilessContentId);
470 	}
471 	else {
472 		theRepSize.expectedBody(true);
473 		theRepSize.expect(theRepSize.header() + theBodyIter->contentSize());
474 		theBodyIter->start(&theConn->theWrBuf);
475 	}
476 
477 	// note: non-200 and non-304 responses to IMS requests not covered yet
478 	theOid.ims200(theReqHdr.theIms >= 0);
479 }
480 
make204NoContent(HttpPrinter & hp)481 void HttpSrvXact::make204NoContent(HttpPrinter &hp)
482 {
483 	theHttpStatus = RepHdr::sc204_NoContent;
484 	putResponseLine(hp, rls204NoContent);
485 	put2xxContentHead(hp);
486 	hp << crlf; // end-of-headers
487 
488 	theRepSize.header(Size(hp.tellp()));
489 	theRepSize.expect(theRepSize.header());
490 	theOid.type(TheBodilessContentId);
491 
492 	theOid.ims200(theReqHdr.theIms >= 0);
493 }
494 
canMake302Found(ObjId & oid) const495 bool HttpSrvXact::canMake302Found(ObjId &oid) const {
496 	Assert(theOwner->popModel());
497 
498 	// at this time redirect to the same server only
499 	if (theOid.viserv() < 0)
500 		return false;
501 
502 	// misconfiguration
503 	if (!theOwner->popModel())
504 		return false;
505 
506 	ObjUniverse *const universe =
507 		TheHostMap->findUniverseAt(theOid.viserv());
508 
509 	if (!universe || !universe->canRepeat(theOid.type()))
510 		return false;
511 
512 	oid = theOid; // set viserv and such
513 	oid.name(-1);
514 	oid.type(-1); // overwrite name and type
515 	universe->repeat(oid, theOwner->popModel());
516 	return true;
517 }
518 
make302Found(HttpPrinter & hp)519 bool HttpSrvXact::make302Found(HttpPrinter &hp) {
520 	if (theBodyIter) {
521 		theBodyIter->putBack();
522 		theBodyIter = 0;
523 	}
524 
525 	ObjId newOid;
526 
527 	// make sure we have an object to redirect to
528 	if (!canMake302Found(newOid)) {
529 		ReportError(errMake302Found);
530 		return false;
531 	}
532 
533 	theHttpStatus = RepHdr::sc302_Found;
534 	putResponseLine(hp, rls302Found);
535 
536 	hp.putHeaders(theOwner->cfg()->httpHeaders());
537 	putStdFields(hp);
538 
539 	String url;
540 	{
541 		const int buf_size = 4*1024;
542 		static char buf[buf_size];
543 		ofixedstream url_os(buf, buf_size);
544 		Oid2Url(newOid, url_os) << ends;
545 		buf[buf_size-1] = '\0';
546 		url = buf;
547 	}
548 	if (hp.putHeader(hfpLocation))
549 		hp << url << crlf;
550 
551 	const int clen = url.len() + text302Found.len();
552 	if (hp.putHeader(hfpContLength))
553 		hp << clen << crlf;
554 
555 	putXFields(hp);
556 
557 	hp << crlf; // end-of-headers
558 
559 	// we determine the type of IMS request later
560 	theRepSize.header(Size(hp.tellp()));
561 
562 	// text for humans
563 	Oid2Url(newOid, hp << text302Found);
564 
565 	theRepSize.expect(theRepSize.header() + Size(clen));
566 	return true;
567 }
568 
shouldMake302Found() const569 bool HttpSrvXact::shouldMake302Found() const {
570 	return theOid.repToRedir();
571 }
572 
make304NotMod(HttpPrinter & hp)573 void HttpSrvXact::make304NotMod(HttpPrinter &hp) {
574 	openSimpleMessage(hp, RepHdr::sc304_NotModified, rls304NotModified, 0);
575 	closeSimpleMessage(hp, 0);
576 	theOid.ims304(true);
577 }
578 
shouldMake304NotMod() const579 bool HttpSrvXact::shouldMake304NotMod() const {
580 	return
581 		theReqHdr.theIms >= 0 && theTimes.lmt() >= 0 &&
582 		theTimes.lmt() <= theReqHdr.theIms;
583 }
584 
make406NotAcceptable(HttpPrinter & hp)585 void HttpSrvXact::make406NotAcceptable(HttpPrinter &hp) {
586 	ReportError(errNoAcceptableContentCoding);
587 
588 	const String &body = text406NotAcceptable;
589 	openSimpleMessage(hp, RepHdr::sc406_NotAcceptable, rls406NotAcceptable, &body);
590 
591 	if (theRepContentCfg->multipleContentCodings())
592 		hp.putHeader(hfVaryAcceptEncoding);
593 
594 	closeSimpleMessage(hp, &body);
595 }
596 
make416RequestedRangeNotSatisfiable(HttpPrinter & hp)597 void HttpSrvXact::make416RequestedRangeNotSatisfiable(HttpPrinter &hp) {
598 	Assert(theBodyIter);
599 
600 	const String &body = text416RequestedRangeNotSatisfiable;
601 	openSimpleMessage(hp, RepHdr::sc416_RequestedRangeNotSatisfiable, rls416RequestedRangeNotSatisfiable, &body);
602 
603 	if (hp.putHeader(hfpContRange))
604 		hp << "*/" << (int)theBodyIter->fullEntitySize() << crlf;
605 
606 	closeSimpleMessage(hp, &body);
607 }
608 
shouldMake416RequestedRangeNotSatisfiable() const609 bool HttpSrvXact::shouldMake416RequestedRangeNotSatisfiable() const {
610 	return (theOid.range() && theRanges.empty());
611 }
612 
make417ExpectationFailed(HttpPrinter & hp)613 void HttpSrvXact::make417ExpectationFailed(HttpPrinter &hp) {
614 	const String &body = text417ExpectationFailed;
615 	openSimpleMessage(hp, RepHdr::sc417_ExpectationFailed, rls417ExpectationFailed, &body);
616 	closeSimpleMessage(hp, &body);
617 }
618 
putResponseLine(ostream & os,const String & suffix)619 void HttpSrvXact::putResponseLine(ostream &os, const String &suffix) {
620 	if (theHttpVersion <= HttpVersion(1,0))
621 		os << protoHttp1p0;
622 	else
623 		os << protoHttp1p1;
624 	os << suffix;
625 }
626 
627 // put well-known fields acceptable for both 304 and 200 replies
putStdFields(HttpPrinter & hp) const628 void HttpSrvXact::putStdFields(HttpPrinter &hp) const {
629 	// general-header fields
630 
631 	if (hp.putHeader(hfpDate))
632 		HttpDatePrint(hp) << crlf;
633 
634 	// persistency indication depends on HTTP version
635 	if (theHttpVersion <= HttpVersion(1,0)) {
636 		if (theConn->reusable())
637 			hp.putHeader(hfConnAliveOrg);
638 	} else {
639 		if (!theConn->reusable())
640 			hp.putHeader(hfConnCloseOrg);
641 	}
642 
643 	// response-header fields (none)
644 	// entity-header fields
645 
646 	if (theOid.cachable() &&
647 		theTimes.knownExp() &&
648 		hp.putHeader(hfpExpires))
649 		HttpDatePrint(hp, theTimes.exp()) << crlf;
650 }
651 
652 // put extension header fields acceptable for both 302 and 200 replies
putXFields(HttpPrinter & hp) const653 void HttpSrvXact::putXFields(HttpPrinter &hp) const {
654 	// put group ids with source/target ids on one line?
655 	if (hp.putHeader(hfpXTarget))
656 		hp << theOwner->host() << crlf;
657 
658 	if (theReqHdr.theXactId &&
659 		hp.putHeader(hfpXXact)) {
660 		hp << TheGroupId
661 			<< ' ' << theReqHdr.theXactId.genMutant()
662 			<< ' ' << hex << theRepFlags << dec
663 			<< crlf;
664 	}
665 
666 	if (theOid.viserv() >= 0 && theReqHdr.theRemWorld)
667 		putRemWorld(hp, theReqHdr.theRemWorld);
668 
669 	if (hp.putHeader(hfpXAbort)) {
670 		hp << theAbortCoord.whether()
671 		<< ' ' << theAbortCoord.where()	<< crlf;
672 	}
673 
674 	putPhaseSyncPos(hp, TheStatPhaseSync.phaseSyncPos());
675 }
676 
put2xxContentHead(HttpPrinter & hp)677 void HttpSrvXact::put2xxContentHead(HttpPrinter &hp) {
678 	/* user-configured headers */
679 
680 	if (theRepContentCfg)
681 		hp.putHeaders(theRepContentCfg->mimeHeaders());
682 	hp.putHeaders(theOwner->cfg()->httpHeaders());
683 
684 	/* general-header fields */
685 
686 	if (theOid.cachable())
687 		hp.putHeader(hfCcCachable);
688 	else {
689 		hp.putHeader(hfCcUncachable);
690 		hp.putHeader(hfPragmaUncachable);
691 	}
692 
693 	putStdFields(hp);
694 
695 	/* other entity-header fields */
696 
697 	if (theOid.cachable() &&
698 		theTimes.showLmt() &&
699 		hp.putHeader(hfpLmt))
700 		HttpDatePrint(hp, theTimes.lmt()) << crlf;
701 
702 	if (theRepContentCfg->multipleContentCodings())
703 		hp.putHeader(hfVaryAcceptEncoding);
704 
705 	// Servers probably MUST NOT send a Content-MD5 header with
706 	// 206 responses. See
707 	// http://trac.tools.ietf.org/wg/httpbis/trac/ticket/178
708 	if (!theOid.range() && theRepContentCfg->calcChecksumNeed(theOid))
709 		putChecksum(*theRepContentCfg, theOid, hp);
710 
711 	if (theOwner->isCookieSender)
712 		putCookies(hp);
713 
714 	if (theBodyIter)
715 		theBodyIter->putHeaders(hp);
716 
717 	/* extention-header fields (they are entity-header fields as well) */
718 	putXFields(hp);
719 }
720 
putRemWorld(HttpPrinter & hp,const ObjWorld & oldWorld) const721 void HttpSrvXact::putRemWorld(HttpPrinter &hp, const ObjWorld &oldWorld) const {
722 	ObjUniverse &universe = *TheHostMap->findUniverseAt(theOid.viserv());
723 
724 	int sliceIdx;
725 	if (universe.find(oldWorld.id(), sliceIdx)) {
726 		const ObjWorld *const world = universe.newerWorld(oldWorld, sliceIdx);
727 		if (world && hp.putHeader(hfpXRemWorld))
728 			hp << *world << crlf;
729 	}
730 
731 	// else client-side sent remote world ID before the same public world ID
732 }
733 
putCookies(ostream & os)734 void HttpSrvXact::putCookies(ostream &os) {
735 	SrvCfg *cfg = theOwner->cfg();
736 	const int oidSeed = theOid.hash();
737 
738 	// check wether we should send (set) cookies with this response
739 	const int seedSend = GlbPermut(oidSeed, rndCookieSend);
740 	RndGen rng(seedSend);
741 	if (!rng.event(cfg->theCookieSendProb))
742 		return;
743 
744 	// calculate how many cookies we should send
745 	RndDistr *distrCount = seedOidDistr(cfg->theCookieCounts, rndCookieCount);
746 	theCookiesSentCount = (int)MiniMax(1.0, distrCount->trial(), (double)INT_MAX);
747 
748 	RndDistr *sizeDistr = seedOidDistr(cfg->theCookieSizes, rndCookieSize);
749 	Size accSize = 0;
750 	for (int i = 0; i < theCookiesSentCount; ++i) {
751 		static const String cookieValuePfx = "sess";
752 		static const String cookieValueSfx = "\"";
753 
754 		os << hfpSetCookie << cookieValuePfx << i << "=\"";
755 
756 		WrBuf &buf = theConn->theWrBuf;
757 		const Size cookieSize = (int)MiniMax(0.0, sizeDistr->trial(), (double)INT_MAX);
758 		const Size cookieContentOff = IOBuf::RandomOffset(oidSeed, accSize);
759 		const Size usedSize = (streamoff)os.tellp();
760 		const Size spaceRemaining = buf.spaceSize() - usedSize;
761 		const bool fit = cookieSize + Size(cookieValueSfx.len() + 2) <=
762 			spaceRemaining;
763 
764 		if (!fit && ReportError(errCookiesDontFit)) {
765 			Comment << "cookie size: " << cookieSize <<
766 				" cookies buffered: " << i << '/' <<
767 				theCookiesSentCount << " or " <<
768 				accSize << "; space left: " << spaceRemaining << endc;
769 		}
770 
771 		// yes, make buffer (ostream) full
772 		IOBuf::RandomFill(os, cookieContentOff, cookieSize, RndText());
773 		accSize += cookieSize;
774 
775 		os << cookieValueSfx << crlf;
776 
777 		if (!fit)
778 			break;
779 	}
780 }
781 
openSimpleMessage(HttpPrinter & hp,const int status,const String & header,const String * const body)782 void HttpSrvXact::openSimpleMessage(HttpPrinter &hp, const int status, const String &header, const String *const body) {
783 	theHttpStatus = status;
784 	putResponseLine(hp, header);
785 
786 	hp.putHeaders(theOwner->cfg()->httpHeaders());
787 	putStdFields(hp);
788 
789 	if (body && hp.putHeader(hfpContLength))
790 		hp << body->len() << crlf;
791 
792 	putXFields(hp);
793 }
794 
closeSimpleMessage(ostream & os,const String * const body)795 void HttpSrvXact::closeSimpleMessage(ostream &os, const String *const body) {
796 	if (theBodyIter) {
797 		theBodyIter->putBack();
798 		theBodyIter = 0;
799 	}
800 
801 	os << crlf; // end-of-headers
802 
803 	theRepSize.header(Size(os.tellp()));
804 
805 	if (!theOid.head() && body) {
806 		// text for humans
807 		os << *body;
808 		theRepSize.expect(theRepSize.header() + Size(body->len()));
809 		theOid.type(TheUnknownContentId);
810 	} else {
811 		theRepSize.expect(theRepSize.header());
812 		theOid.type(TheBodilessContentId);
813 	}
814 }
815 
doPhaseSync(const MsgHdr & hdr) const816 void HttpSrvXact::doPhaseSync(const MsgHdr &hdr) const {
817 	Xaction::doPhaseSync(hdr);
818 	if (TheStatPhaseMgr.phaseSyncPos() < TheStatPhaseSync.phaseSyncPosMin()) {
819 		TheStatPhaseMgr->reachedPositiveGoal("all clients reached "
820 			"their phase goals");
821 	}
822 }
823 
cookiesSent() const824 int HttpSrvXact::cookiesSent() const {
825 	return theCookiesSentCount;
826 }
827 
cookiesRecv() const828 int HttpSrvXact::cookiesRecv() const {
829 	return theReqHdr.theCookieCount;
830 }
831