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