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 "xstd/String.h"
9 #include "xstd/Clock.h"
10 #include "xstd/PrefixIdentifier.h"
11 #include "xstd/gadgets.h"
12 #include "base/OLog.h"
13 #include "base/AddrParsers.h"
14 #include "base/polyLogCats.h"
15 #include "runtime/ErrorMgr.h"
16 #include "runtime/HostMap.h"
17 #include "runtime/HttpCookies.h"
18 #include "runtime/HttpDate.h"
19 #include "runtime/LogComment.h"
20 #include "runtime/httpText.h"
21 #include "runtime/httpHdrs.h"
22 #include "runtime/polyErrors.h"
23 
24 
25 /* internal type to store static parsing info */
26 class MsgHdrParsTab {
27 	public:
28 		MsgHdrParsTab();
29 		~MsgHdrParsTab();
30 
31 	public:
32 		PrefixIdentifier *ids;
33 		Array<MsgHdr::Parser*> *parsers;
34 };
35 
36 
37 MsgHdrParsTab *ReqHdr::TheParsTab = 0;
38 MsgHdrParsTab *RepHdr::TheParsTab = 0;
39 
40 
41 /* HttpUri */
HttpUri()42 HttpUri::HttpUri(): pathBuf(0), pathLen(-1) {
43 	oid.scheme(Agent::pHTTP);
44 }
45 
46 
47 /* MsgHdr */
48 
MsgHdr(const MsgHdrParsTab & aTab)49 MsgHdr::MsgHdr(const MsgHdrParsTab &aTab): theParsTab(aTab),
50 	theCookies(0), createdCookies(false) {
51 	reset();
52 }
53 
~MsgHdr()54 MsgHdr::~MsgHdr() {
55 	if (theCookies && createdCookies)
56 		delete theCookies;
57 }
58 
reset()59 void MsgHdr::reset() {
60 	theHdrSize = 0;
61 	theHttpVersion.reset();
62 	theContSize = -1;
63 	theChecksum.reset();
64 	theDate = Time();
65 	theGroupId.clear();
66 	theXactId.clear();
67 	theTarget = NetAddr();
68 	theRemWorld.reset();
69 	theAbortCoord.reset();
70 	thePhaseSyncPos = -1;
71 	theXactFlags = -1;
72 	theConnectionKeepAlive = kaDefault;
73 	theContType = ctUnknown;
74 	theTransferEncoding = tcNone;
75 	isCachable = true;
76 	theBoundary = String();
77 	theCookieCount = 0;
78 	if (theCookies) {
79 		if (createdCookies)
80 			delete theCookies;
81 		theCookies = 0;
82 	}
83 	needCookies = false;
84 	createdCookies = false;
85 
86 	theBufBeg = theBufEnd = theSrchPtr = 0;
87 	theFields.reset();
88 	theSrchState = ssFirst;
89 
90 	// do not reset parsing tables
91 }
92 
markupContent() const93 bool MsgHdr::markupContent() const {
94 	return theContType == ctMarkup;
95 }
96 
knownContentType() const97 bool MsgHdr::knownContentType() const {
98 	return theContType != ctUnknown;
99 }
100 
multiRange() const101 bool MsgHdr::multiRange() const {
102 	return theContType == ctMultiRange;
103 }
104 
chunkedEncoding() const105 bool MsgHdr::chunkedEncoding() const {
106 	return theTransferEncoding == tcChunked;
107 }
108 
persistentConnection() const109 bool MsgHdr::persistentConnection() const {
110 	if (theHttpVersion <= HttpVersion(1,0)) // 1.0: keep if explicitly told so
111 		return theConnectionKeepAlive == kaYes;
112 	else // 1.1: keep unless told otherwise
113 		return theConnectionKeepAlive != kaNo;
114 }
115 
116 // note: buf does not have to be zero-terminated!
parse(const char * buf,Size sz)117 bool MsgHdr::parse(const char *buf, Size sz) {
118 	if (!theBufBeg) { // have not started the search yet
119 		theBufBeg = theSrchPtr = buf;
120 		Assert(theSrchState == ssFirst);
121 	} else {           // continue search
122 		Assert(theBufBeg == buf);
123 		Assert(theSrchState != ssFound);
124 	}
125 
126 	theBufEnd = buf + sz; // to be refined later
127 	while (theSrchPtr < theBufEnd && theSrchState != ssFound) {
128 		// search for LF
129 		if (theSrchState == ssFirst) {
130 			do {
131 				if (*theSrchPtr++ == '\n') {
132 					theSrchState = ssSkip;
133 					break;
134 				}
135 			} while (theSrchPtr < theBufEnd);
136 		}
137 
138 		// LF after skipping optional CRs means end-of-headers
139 		while (theSrchState == ssSkip && theSrchPtr < theBufEnd) {
140 			if (*theSrchPtr == '\n') {
141 				theSrchState = ssFound;
142 			} else
143 			if (*theSrchPtr != '\r') {
144 				theFields.append(theSrchPtr); // start of a header!
145 				theSrchState = ssFirst;
146 			}
147 			++theSrchPtr;
148 		}
149 	}
150 
151 	if (theSrchState != ssFound)
152 		return false;
153 
154 	// found end-of-headers!
155 	theBufEnd = theSrchPtr;
156 	theHdrSize = theBufEnd - theBufBeg;
157 
158 	// now parse known fields
159 	// luckily, we already know field starts!
160 	parseFields();
161 	return true;
162 }
163 
parseFields()164 void MsgHdr::parseFields() {
165 	const char *eoh = theBufEnd;
166 	// skip end-of-headers CRLF
167 	while (theBufBeg < eoh && eoh[-1] == '\n') --eoh;
168 	while (theBufBeg < eoh && eoh[-1] == '\r') --eoh;
169 
170 	parseRLine(theBufBeg, theFields.count() ? theFields[0] : eoh);
171 
172 	for (int i = theFields.count()-1; i >= 0; --i) {
173 		const char *hdr = theFields[i];
174 		const int len = eoh-hdr; // approximate (includes crlfs)
175 		const int id = theParsTab.ids->lookup(hdr, len);
176 		if (id > 0) {
177 			const char *val = hdr + theParsTab.ids->string(id).len();
178 			while (isspace(*val)) ++val;
179 			Parser p = *theParsTab.parsers->item(id);
180 			(this->*p)(val, eoh);
181 		}
182 		eoh = hdr;
183 	}
184 }
185 
parseHttpVersion(const char * & beg,const char * end,HttpVersion & v)186 bool MsgHdr::parseHttpVersion(const char *&beg, const char *end, HttpVersion &v) {
187 	const char *p = 0;
188 	int major = -1, minor = -1;
189 	if (isInt(beg, major, &p) && p+1 < end && *p == '.' && isInt(p+1, minor, &p)) {
190 		v = HttpVersion(major, minor);
191 		beg = p;
192 		return true;
193 	}
194 	return false;
195 }
196 
ParseHostInUri(const char * & start,const char * eorl,NetAddr & host)197 bool MsgHdr::ParseHostInUri(const char *&start, const char *eorl, NetAddr &host) {
198 	if (const char *newStart = SkipHostInUri(start, eorl, host)) {
199 		start = newStart;
200 		return true;
201 	}
202 	return false;
203 }
204 
ParseSingleRange(const char * & buf,const char * eoh,Size & firstByte,Size & lastByte)205 bool MsgHdr::ParseSingleRange(const char *&buf, const char *eoh, Size &firstByte, Size &lastByte) {
206 	int i;
207 	const char *p;
208 	if (*buf == '-') {
209 		firstByte = -1;
210 		buf += 1;
211 	}
212 	else {
213 		if (!isInt(buf, i, &p) ||
214 			p >= eoh ||
215 			*p != '-')
216 			return false;
217 		firstByte = i;
218 		buf = p + 1;
219 	}
220 	i = -1;
221 	if (buf == eoh) {
222 		lastByte = -1;
223 		return (firstByte >= 0); // "a-" ranges
224 	}
225 	else
226 	if (isInt(buf, i, &p)) {
227 		buf = p;
228 		lastByte = i;
229 		return (firstByte <= lastByte); // "a-b" and "-b" ranges
230 	}
231 	return false;
232 }
233 
234 // Find the first supported auth scheme and parse its value.
235 // The scheme name may be followed by authentication data.
ParseAuthenticate(const char * & buf,const char * eoh,AuthChallenge & auth)236 bool MsgHdr::ParseAuthenticate(const char *&buf, const char *eoh, AuthChallenge &auth) {
237 	// Header fields are scanned in the reverse order of appearance so the
238 	// last header field we see and honor is the first one in the header.
239 
240 	static PrefixIdentifier authSchemeIdentifier;
241 	if (!authSchemeIdentifier.count()) {
242 		authSchemeIdentifier.add("Basic", authBasic);
243 		authSchemeIdentifier.add("NTLM", authNtlm);
244 		authSchemeIdentifier.add("Negotiate", authNegotiate);
245 		authSchemeIdentifier.optimize();
246 	}
247 
248 	// trim left
249 	while (buf < eoh && isspace((unsigned char) *buf))
250 		++buf;
251 
252 	const int scheme = authSchemeIdentifier.lookup(buf, eoh-buf);
253 
254 	if (!scheme) {
255 		static unsigned count(0);
256 		if (count++ < 10) {
257 			Comment(1) << "error: unsupported authentication scheme" << endl
258 				<< "header: ";
259 			Comment.write(buf, eoh-buf);
260 			Comment << endc;
261 		}
262 		return true; // not a parsing error
263 	}
264 
265 	auth.scheme = HttpAuthScheme(scheme);
266 
267 	// skip scheme name
268 	buf += authSchemeIdentifier.string(scheme).len();
269 
270 	// trim spaces after the scheme name
271 	while (buf < eoh && isspace((unsigned char) *buf))
272 		++buf;
273 
274 	// trim right
275 	while (buf < eoh && isspace((unsigned char) *(eoh-1)))
276 		--eoh;
277 
278 	auth.params = String(buf, eoh-buf);
279 	return true;
280 }
281 
parseUri(const char * & buf,const char * end,HttpUri & uri)282 bool MsgHdr::parseUri(const char *&buf, const char *end, HttpUri &uri) {
283 	const char *const start(buf);
284 	bool foreignHost(false);
285 	// see if there is a protocol://host prefix
286 	if (*start != '/') {
287 		ParseHostInUri(buf, end, uri.host);
288 		foreignHost = !TheHostMap->find(uri.host);
289 	}
290 
291 	uri.pathBuf = buf; // includes leading '/'
292 	if (!uri.oid.parse(buf, end) ||
293 		foreignHost)
294 		uri.oid.foreignUrl(String(start, buf - start));
295 	uri.pathLen = buf - uri.pathBuf;
296 	return true;
297 }
298 
parseContLen(const char * buf,const char *)299 bool MsgHdr::parseContLen(const char *buf, const char *) {
300 	theContSize = xatoi(buf, -1);
301 	return theContSize >= 0;
302 }
303 
parseContMd5(const char * buf,const char * eoh)304 bool MsgHdr::parseContMd5(const char *buf, const char *eoh) {
305 	if (DecodeBase64(buf, eoh - buf, theChecksum.buf(), theChecksum.size()) == theChecksum.size()) {
306 		theChecksum.set(true);
307 		return true;
308 	}
309 	static unsigned count(0);
310 	if (count++ < 10) {
311 		Comment(1) << "error: malformed Content-MD5 header: ";
312 		Comment.write(buf, eoh-buf);
313 		Comment << endc;
314 	}
315 	return false;
316 }
317 
parseContType(const char * buf,const char * eoh)318 bool MsgHdr::parseContType(const char *buf, const char *eoh) {
319 	theContType = ctOther; // default
320 	if (strncasecmp(buf, "text/", 5) == 0) {
321 		buf += 5;
322 		if (buf+4 <= eoh && strncasecmp(buf+2, "ml", 2) == 0)
323 			theContType = ctMarkup;
324 		else
325 		if (buf+4 <= eoh && strncasecmp(buf+1, "ml", 2) == 0)
326 			theContType = ctMarkup;
327 		else
328 		if (buf+3 <= eoh && strncasecmp(buf, "css", 3) == 0)
329 			theContType = ctMarkup;
330 	}
331 	else
332 	if (strncasecmp(buf, "multipart/byteranges; boundary=", 31) == 0) {
333 		buf += 31;
334 		// skip end-of-header CRLF
335 		if (buf < eoh && eoh[-1] == '\n')
336 			--eoh;
337 		if (buf < eoh && eoh[-1] == '\r')
338 			--eoh;
339 		if (buf < eoh) {
340 			theBoundary = String(buf, eoh - buf);
341 			theContType = ctMultiRange;
342 		}
343 	}
344 
345 	return true;
346 }
347 
parseDate(const char * buf,const char * eoh)348 bool MsgHdr::parseDate(const char *buf, const char *eoh) {
349     theDate = HttpDateParse(buf, eoh - buf);
350     return theDate >= 0;
351 }
352 
parsePragma(const char * buf,const char *)353 bool MsgHdr::parsePragma(const char *buf, const char *) {
354 	if (!strncasecmp("no-cache", buf, 8))
355 		isCachable = false;
356 	else
357 		return false;
358 	return true;
359 }
360 
parseCControl(const char * buf,const char *)361 bool MsgHdr::parseCControl(const char *buf, const char *) {
362 	if (!strncasecmp("no-cache", buf, 8)) {
363 		isCachable = false;
364 		return true;
365 	}
366 	return false;
367 }
368 
parseXXact(const char * buf,const char * eoh)369 bool MsgHdr::parseXXact(const char *buf, const char *eoh) {
370 	return
371 		theGroupId.parse(buf, eoh) && *buf == ' ' &&
372 		theXactId.parse(++buf, eoh) && *buf == ' ' &&
373 		isInt(++buf, theXactFlags, 0, 16);
374 }
375 
parseXRemWorld(const char * buf,const char * eoh)376 bool MsgHdr::parseXRemWorld(const char *buf, const char *eoh) {
377 	return theRemWorld.parse(buf, eoh);
378 }
379 
parseXAbort(const char * buf,const char *)380 bool MsgHdr::parseXAbort(const char *buf, const char *) {
381 	RndGen::Seed whether = 0;
382 	RndGen::Seed where = 0;
383 	const char *p = 0;
384 	if (isInt64(buf, whether, &p) && *p == ' ' && isInt64(p+1, where)
385 		&& whether && where) {
386 		theAbortCoord.configure(whether, where);
387 		return true;
388 	}
389 
390 	return false;
391 }
392 
parseXPhaseSyncPos(const char * buf,const char *)393 bool MsgHdr::parseXPhaseSyncPos(const char *buf, const char *) {
394 	thePhaseSyncPos = xatoi(buf, 0);
395 	return true;
396 }
397 
parseXTarget(const char * buf,const char * eoh)398 bool MsgHdr::parseXTarget(const char *buf, const char *eoh) {
399 	// always expect explicit port
400 	return ParseNetAddr(buf, eoh, -1, theTarget);
401 }
402 
403 /* XXX: Connection and other headers may have a _list_ of options */
404 
parseConnection(const char * buf,const char *)405 bool MsgHdr::parseConnection(const char *buf, const char *) {
406 	if (!strncasecmp("close", buf, 5))
407 		theConnectionKeepAlive = kaNo;
408 	else
409 	if (!strncasecmp("keep", buf, 4))
410 		theConnectionKeepAlive = kaYes;
411 	else
412 		return false;
413 	return true;
414 }
415 
parseTransferEncoding(const char * buf,const char *)416 bool MsgHdr::parseTransferEncoding(const char *buf, const char *) {
417 	if (!strncasecmp("chunked", buf, 7))
418 		theTransferEncoding = tcChunked;
419 	else
420 	if (!strncasecmp("identity", buf, 8))
421 		theTransferEncoding = tcIdentity;
422 	else
423 		theTransferEncoding = tcOther;
424 	return true;
425 }
426 
parseCookie(const char * buf,const char * eoh)427 bool MsgHdr::parseCookie(const char *buf, const char *eoh) {
428 	++theCookieCount;
429 	HttpCookie *cookie(0);
430 	if (needCookies) {
431 		cookie = HttpCookie::Parse(buf, eoh);
432 		if (Should(cookie)) {
433 			if (!theCookies) {
434 				theCookies = new HttpCookies;
435 				createdCookies = true;
436 			}
437 			theCookies->add(cookie);
438 		}
439 	}
440 	return cookie;
441 }
442 
443 // adds definitions common to replies and requests
Configure(MsgHdrParsTab & tab)444 void MsgHdr::Configure(MsgHdrParsTab &tab) {
445 	AddParser(hfpDate, &MsgHdr::parseDate, tab);
446 	AddParser(hfpContLength, &MsgHdr::parseContLen, tab);
447 	AddParser(hfpContMd5, &MsgHdr::parseContMd5, tab);
448 	AddParser(hfpContType, &MsgHdr::parseContType, tab);
449 	AddParser(hfpCacheControl, &MsgHdr::parseCControl, tab);
450 	AddParser(hfpConnection, &MsgHdr::parseConnection, tab);
451 	AddParser(hfpPragma, &MsgHdr::parsePragma, tab);
452 	AddParser(hfpProxyConnection, &MsgHdr::parseConnection, tab);
453 	AddParser(hfpTransferEncoding, &MsgHdr::parseTransferEncoding, tab);
454 	AddParser(hfpXXact, &MsgHdr::parseXXact, tab);
455 	AddParser(hfpXRemWorld, &MsgHdr::parseXRemWorld, tab);
456 	AddParser(hfpXAbort, &MsgHdr::parseXAbort, tab);
457 	AddParser(hfpXPhaseSyncPos, &MsgHdr::parseXPhaseSyncPos, tab);
458 	AddParser(hfpXTarget, &MsgHdr::parseXTarget, tab);
459 }
460 
AddParser(const String & field,Parser parser,MsgHdrParsTab & where)461 int MsgHdr::AddParser(const String &field, Parser parser, MsgHdrParsTab &where) {
462 	Assert(field);
463 	// remove trailing space from "Header: " (but not from "METHOD ") fields
464 	const String trimmedField = isspace(field.last()) && field.chr(':') ?
465 		field(0, field.len()-1) : field;
466 	Assert(trimmedField);
467 
468 	const int id = where.ids->add(trimmedField);
469 	where.parsers->put(new Parser(parser), id);
470 	return id;
471 }
472 
store(OLog & log) const473 void MsgHdr::store(OLog &log) const {
474 	log
475 		<< theHdrSize
476 		<< (int)theDate.sec()
477 		<< theContSize
478 		// << theChecksum
479 		<< theGroupId
480 		<< theXactId
481 		<< theTarget
482 		<< theHttpVersion.vMinor() // XXX: log major too
483 		<< (int)theConnectionKeepAlive
484 		<< isCachable
485 		// XXX: not stored or loaded: theTransferEncoding, theContType
486 		;
487 }
488 
collectCookies(HttpCookies * cookies)489 void MsgHdr::collectCookies(HttpCookies *cookies) {
490 	Assert(!needCookies);
491 	Assert(!theCookies);
492 	needCookies = true;
493 	theCookies = cookies;
494 }
495 
496 // these should never be called
dontCallMe()497 inline bool dontCallMe() { Assert(0); return false; }
parseGetReqLine(const char *,const char *)498 bool MsgHdr::parseGetReqLine(const char *, const char *) { return dontCallMe(); }
parseHeadReqLine(const char *,const char *)499 bool MsgHdr::parseHeadReqLine(const char *, const char *) { return dontCallMe(); }
parsePostReqLine(const char *,const char *)500 bool MsgHdr::parsePostReqLine(const char *, const char *) { return dontCallMe(); }
parsePutReqLine(const char *,const char *)501 bool MsgHdr::parsePutReqLine(const char *, const char *) { return dontCallMe(); }
parsePatchReqLine(const char *,const char *)502 bool MsgHdr::parsePatchReqLine(const char *, const char *) { return dontCallMe(); }
parseHost(const char *,const char *)503 bool MsgHdr::parseHost(const char *, const char *) { return dontCallMe(); }
parseServer(const char *,const char *)504 bool MsgHdr::parseServer(const char *, const char *) { return dontCallMe(); }
parseProxyAuthenticate(const char *,const char *)505 bool MsgHdr::parseProxyAuthenticate(const char *, const char *) { return dontCallMe(); }
parseWwwAuthenticate(const char *,const char *)506 bool MsgHdr::parseWwwAuthenticate(const char *, const char *) { return dontCallMe(); }
parseLocation(const char *,const char *)507 bool MsgHdr::parseLocation(const char *, const char *) { return dontCallMe(); }
parseLMT(const char *,const char *)508 bool MsgHdr::parseLMT(const char *, const char *) { return dontCallMe(); }
parseExpires(const char *,const char *)509 bool MsgHdr::parseExpires(const char *, const char *) { return dontCallMe(); }
parseIms(const char *,const char *)510 bool MsgHdr::parseIms(const char *, const char *) { return dontCallMe(); }
parseAcceptEncoding(const char *,const char *)511 bool MsgHdr::parseAcceptEncoding(const char *, const char *) { return dontCallMe(); }
parseXLocWorld(const char *,const char *)512 bool MsgHdr::parseXLocWorld(const char *, const char *) { return dontCallMe(); }
parseContRange(const char *,const char *)513 bool MsgHdr::parseContRange(const char *, const char *) { return dontCallMe(); }
parseRange(const char *,const char *)514 bool MsgHdr::parseRange(const char *, const char *) { return dontCallMe(); }
parseExpect(const char *,const char *)515 bool MsgHdr::parseExpect(const char *, const char *) { return dontCallMe(); }
516 
517 
518 /* ReqHdr */
519 
ReqHdr()520 ReqHdr::ReqHdr(): MsgHdr(*TheParsTab), isHealthCheck(false),
521 	isAcceptingGzip(false) {
522 }
523 
reset()524 void ReqHdr::reset() {
525 	MsgHdr::reset();
526 	theUri = HttpUri();
527 	theIms = Time();
528 	theLocWorld.reset();
529 	isHealthCheck = false;
530 	isAcceptingGzip = false;
531 	theRanges.clear();
532 	expect100Continue = false;
533 }
534 
parseRLine(const char * buf,const char * eorl)535 bool ReqHdr::parseRLine(const char *buf, const char *eorl) {
536 	const int id = theParsTab.ids->lookup(buf, eorl - buf);
537 	if (id > 0) {
538 		buf += theParsTab.ids->string(id).len();
539 		while (isspace(*buf)) ++buf;
540 
541 		Parser p = *theParsTab.parsers->item(id);
542 		return (this->*p)(buf, eorl);
543 	}
544 	return false;
545 }
546 
parseAnyReqLine(const char * buf,const char * eorl)547 bool ReqHdr::parseAnyReqLine(const char *buf, const char *eorl) {
548 	// a "well-known" health check uri
549 	static const String health = "/health";
550 	isHealthCheck = health.casePrefixOf(buf, eorl-buf);
551 
552 	parseUri(buf, eorl, theUri);
553 	if (const char *proto = StrBoundChr(buf, ' ', eorl)) {
554 		// optimization: not checking for "HTTP/" match
555 		proto += 6;
556 		if (proto < eorl)
557 			parseHttpVersion(proto, eorl, theHttpVersion);
558 	}
559 	return true;
560 }
561 
parseGetReqLine(const char * buf,const char * eorl)562 bool ReqHdr::parseGetReqLine(const char *buf, const char *eorl) {
563 	if (parseAnyReqLine(buf, eorl)) {
564 		theUri.oid.get(true);
565 		return true;
566 	}
567 	return false;
568 }
569 
parseHeadReqLine(const char * buf,const char * eorl)570 bool ReqHdr::parseHeadReqLine(const char *buf, const char *eorl) {
571 	if (parseAnyReqLine(buf, eorl)) {
572 		theUri.oid.head(true);
573 		return true;
574 	}
575 	return false;
576 }
577 
parsePostReqLine(const char * buf,const char * eorl)578 bool ReqHdr::parsePostReqLine(const char *buf, const char *eorl) {
579 	if (parseAnyReqLine(buf, eorl)) {
580 		theUri.oid.post(true);
581 		return true;
582 	}
583 	return false;
584 }
585 
parsePutReqLine(const char * buf,const char * eorl)586 bool ReqHdr::parsePutReqLine(const char *buf, const char *eorl) {
587 	if (parseAnyReqLine(buf, eorl)) {
588 		theUri.oid.put(true);
589 		return true;
590 	}
591 	return false;
592 }
593 
parsePatchReqLine(const char * buf,const char * eorl)594 bool ReqHdr::parsePatchReqLine(const char *buf, const char *eorl) {
595 	if (parseAnyReqLine(buf, eorl)) {
596 		theUri.oid.patch(true);
597 		return true;
598 	}
599 	return false;
600 }
601 
parseHost(const char * buf,const char * eoh)602 bool ReqHdr::parseHost(const char *buf, const char *eoh) {
603 	// if there is no port set it to 0
604 	return ParseNetAddr(buf, eoh, 0, theUri.host);
605 }
606 
parseIms(const char * buf,const char * eoh)607 bool ReqHdr::parseIms(const char *buf, const char *eoh) {
608 	theIms = HttpDateParse(buf, eoh - buf);
609 	return theIms >= 0;
610 }
611 
parseAcceptEncoding(const char * buf,const char * eoh)612 bool ReqHdr::parseAcceptEncoding(const char *buf, const char *eoh) {
613 	// XXX: these checks ignore "q=0" preferences
614 	isAcceptingGzip = StrBoundChr(buf, '*', eoh) ||
615 		StrBoundStr(buf, "gzip", eoh); // XXX: codings are case-insensitive
616 	return true;
617 }
618 
parseXLocWorld(const char * buf,const char * eoh)619 bool ReqHdr::parseXLocWorld(const char *buf, const char *eoh) {
620 	return theLocWorld.parse(buf, eoh);
621 }
622 
parseRange(const char * buf,const char * eoh)623 bool ReqHdr::parseRange(const char *buf, const char *eoh) {
624 	bool res = true;
625 
626 	// skip end-of-header CRLF
627 	if (buf < eoh && eoh[-1] == '\n')
628 		--eoh;
629 	if (buf < eoh && eoh[-1] == '\r')
630 		--eoh;
631 
632 	while (buf < eoh) {
633 		ByteRange range;
634 		if (!ParseSingleRange(buf, eoh, range.theFirstByte, range.theLastByte)) {
635 			res = false;
636 			break;
637 		}
638 		if (buf < eoh &&
639 			*buf != ',') {
640 			res = false;
641 			break;
642 		}
643 		++buf;
644 		theRanges.push_back(range);
645 	}
646 	if (!res)
647 		theRanges.clear();
648 	return !theRanges.empty();
649 }
650 
parseExpect(const char * buf,const char *)651 bool ReqHdr::parseExpect(const char *buf, const char *) {
652 	if (!strncasecmp("100-continue", buf, 12)) {
653 		expect100Continue = true;
654 		return true;
655 	}
656 	return false;
657 }
658 
store(OLog & log) const659 void ReqHdr::store(OLog &log) const {
660 	MsgHdr::store(log);
661 	log << theUri.host << theUri.oid << (int)theIms.sec();
662 }
663 
expectBody() const664 bool ReqHdr::expectBody() const {
665 	return theUri.oid.post() || theUri.oid.put() || theUri.oid.patch();
666 }
667 
acceptedEncoding(int coding) const668 bool ReqHdr::acceptedEncoding(int coding) const {
669 	return coding == codingIdentity || // always acceptable for now
670 		(coding == codingGzip && isAcceptingGzip);
671 }
672 
Configure()673 void ReqHdr::Configure() {
674 	TheParsTab = new MsgHdrParsTab();
675 	MsgHdr::Configure(*TheParsTab);
676 	AddParser(hfpHost, &MsgHdr::parseHost, *TheParsTab);
677 	AddParser(hfpIMS, &MsgHdr::parseIms, *TheParsTab);
678 	AddParser(hfpXLocWorld, &MsgHdr::parseXLocWorld, *TheParsTab);
679 	AddParser(hfpAcceptEncoding, &MsgHdr::parseAcceptEncoding, *TheParsTab);
680 	AddParser(hfpRange, &MsgHdr::parseRange, *TheParsTab);
681 	AddParser(hfpExpect, &MsgHdr::parseExpect, *TheParsTab);
682 	AddParser(hfpCookie, &MsgHdr::parseCookie, *TheParsTab);
683 
684 	// request method parsers use the same index/interface as field parsers
685 	AddParser(rlpGet, &MsgHdr::parseGetReqLine, *TheParsTab);
686 	AddParser(rlpHead, &MsgHdr::parseHeadReqLine, *TheParsTab);
687 	AddParser(rlpPost, &MsgHdr::parsePostReqLine, *TheParsTab);
688 	AddParser(rlpPut, &MsgHdr::parsePutReqLine, *TheParsTab);
689 	AddParser(rlpPatch, &MsgHdr::parsePatchReqLine, *TheParsTab);
690 }
691 
Clean()692 void ReqHdr::Clean() {
693 	delete TheParsTab;
694 	TheParsTab = 0;
695 }
696 
697 
698 /* RepHdr */
699 
PositiveStatusCode(int code)700 bool RepHdr::PositiveStatusCode(int code) {
701      /* 1xx: Informational - Request received, continuing process
702       * 2xx: Success - The action was successfully received,  understood, and accepted
703       * 3xx: Redirection - Further action must be taken in order to complete the request
704       * 4xx: Client Error - The request contains bad syntax or cannot be fulfilled
705       * 5xx: Server Error - The server failed to fulfill an apparently valid request */
706 	return 100 <= code && code < 400;
707 }
708 
RepHdr()709 RepHdr::RepHdr(): MsgHdr(*TheParsTab), theStatus(scUnknown) {
710 }
711 
reset()712 void RepHdr::reset() {
713 	MsgHdr::reset();
714 	theServer = String();
715 	theProxyAuthenticate = theOriginAuthenticate = AuthChallenge();
716 	theLocn = HttpUri();
717 	theLMT = theExpires = Time();
718 	theStatus = scUnknown;
719 	theContRangeFirstByte = -1;
720 	theContRangeLastByte = -1;
721 	theContRangeInstanceLength = -1;
722 }
723 
expectPolyHeaders() const724 bool RepHdr::expectPolyHeaders() const {
725 	return expectBody() &&
726 		theStatus != sc401_Unauthorized &&
727 		theStatus != sc407_ProxyAuthRequired &&
728 		theStatus != sc403_Forbidden &&
729 		!redirect();
730 }
731 
732 // RFC 2616: All responses to the HEAD request method MUST NOT include
733 // a message-body. All 1xx , 204, and 304 responses MUST NOT include a
734 // message-body. All other responses do include a message-body.
expectBody() const735 bool RepHdr::expectBody() const {
736 	// note: we cannot handle the HEAD case here; the caller should
737 	if ((100 <= theStatus && theStatus < 200) ||
738 		theStatus == sc204_NoContent ||
739 		theStatus == sc304_NotModified)
740 		return false;
741 	return true;
742 }
743 
redirect() const744 bool RepHdr::redirect() const {
745 	return
746 		theStatus == sc300_Choices ||
747 		theStatus == sc302_Found ||
748 		theStatus == sc303_Other ||
749 		theStatus == sc307_TmpRedir;
750 }
751 
calcLmt() const752 Time RepHdr::calcLmt() const {
753 	if (theLMT >= 0)
754 		return theLMT;
755 	if (theDate >= 0)
756 		return theDate;
757 	return TheClock;
758 }
759 
parseRLine(const char * buf,const char * eorl)760 bool RepHdr::parseRLine(const char *buf, const char *eorl) {
761 	if (strncasecmp("HTTP/", buf, 5) != 0)
762 		return false;
763 	buf += 5;
764 	if (buf >= eorl || !parseHttpVersion(buf, eorl, theHttpVersion))
765 		return false;
766 	buf += 1;
767 	return isInt(buf, theStatus);
768 }
769 
parseServer(const char * buf,const char * eoh)770 bool RepHdr::parseServer(const char *buf, const char *eoh) {
771 	theServer = String(buf, eoh-buf);
772 	return theServer.len() > 0;
773 }
774 
parseProxyAuthenticate(const char * buf,const char * eoh)775 bool RepHdr::parseProxyAuthenticate(const char *buf, const char *eoh) {
776 	return ParseAuthenticate(buf, eoh, theProxyAuthenticate);
777 }
778 
parseWwwAuthenticate(const char * buf,const char * eoh)779 bool RepHdr::parseWwwAuthenticate(const char *buf, const char *eoh) {
780 	return ParseAuthenticate(buf, eoh, theOriginAuthenticate);
781 }
782 
parseLocation(const char * buf,const char * eoh)783 bool RepHdr::parseLocation(const char *buf, const char *eoh) {
784 	return parseUri(buf, eoh, theLocn);
785 }
786 
parseLMT(const char * buf,const char * eoh)787 bool RepHdr::parseLMT(const char *buf, const char *eoh) {
788     theLMT = HttpDateParse(buf, eoh - buf);
789     return theLMT >= 0;
790 }
791 
parseExpires(const char * buf,const char * eoh)792 bool RepHdr::parseExpires(const char *buf, const char *eoh) {
793     theExpires = HttpDateParse(buf, eoh - buf);
794     return theExpires >= 0;
795 }
796 
parseContRange(const char * buf,const char * eoh)797 bool RepHdr::parseContRange(const char *buf, const char *eoh) {
798 	if (strncasecmp(buf, "*/", 2) == 0)
799 		buf += 2;
800 	else
801 	if (!ParseSingleRange(buf, eoh, theContRangeFirstByte, theContRangeLastByte) ||
802 		theContRangeFirstByte < 0 ||
803 		theContRangeLastByte < 0 ||
804 		buf >= eoh ||
805 		*buf != '/')
806 		return false;
807 	buf += 1;
808 	int i = -1;
809 	if (*buf == '*' ||
810 		isInt(buf, i)) {
811 		theContRangeInstanceLength = i;
812 		return true;
813 	}
814 	return false;
815 }
816 
store(OLog & log) const817 void RepHdr::store(OLog &log) const {
818 	MsgHdr::store(log);
819 	log
820 		<< theStatus
821 		<< (int)theLMT.sec()
822 		<< (int)theExpires.sec()
823 		<< theContRangeFirstByte
824 		<< theContRangeLastByte
825 		<< theContRangeInstanceLength
826 		;
827 }
828 
Configure()829 void RepHdr::Configure() {
830 	TheParsTab = new MsgHdrParsTab();
831 	MsgHdr::Configure(*TheParsTab);
832 	AddParser(hfpLocation, &MsgHdr::parseLocation, *TheParsTab);
833 	AddParser(hfpServer, &MsgHdr::parseServer, *TheParsTab);
834 	AddParser(hfpProxyAuthenticate, &MsgHdr::parseProxyAuthenticate, *TheParsTab);
835 	AddParser(hfpWwwAuthenticate, &MsgHdr::parseWwwAuthenticate, *TheParsTab);
836 	AddParser(hfpLmt, &MsgHdr::parseLMT, *TheParsTab);
837 	AddParser(hfpExpires, &MsgHdr::parseExpires, *TheParsTab);
838 	AddParser(hfpSetCookie, &MsgHdr::parseCookie, *TheParsTab);
839 	AddParser(hfpContRange, &MsgHdr::parseContRange, *TheParsTab);
840 }
841 
Clean()842 void RepHdr::Clean() {
843 	delete TheParsTab;
844 	TheParsTab = 0;
845 }
846 
847 
848 /* MsgHdrParsTab */
849 
MsgHdrParsTab()850 MsgHdrParsTab::MsgHdrParsTab() {
851 	ids = new PrefixIdentifier;
852 	parsers = new Array<MsgHdr::Parser*>;
853 }
854 
~MsgHdrParsTab()855 MsgHdrParsTab::~MsgHdrParsTab() {
856 	delete ids; ids = 0;
857 	while (parsers->count()) delete parsers->pop();
858 	delete parsers; parsers = 0;
859 }
860