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