root/trunk/whisperlib/net/http/http_header.cc

Revision 7, 20.7 kB (checked in by whispercastorg, 2 years ago)

version 0.2.0

Line 
1 // Copyright (c) 2009, Whispersoft s.r.l.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Whispersoft s.r.l. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Catalin Popescu
31
32 #include <time.h>
33 #include "net/http/http_header.h"
34 #include "common/io/buffer/memory_stream.h"
35 #include "common/base/errno.h"
36 #include "common/base/strutil.h"
37 #include "net/util/base64.h"
38
39 namespace http {
40 Header::Header(bool is_strict)
41   : is_strict_(is_strict) {
42   Clear();
43 }
44 Header::~Header() {
45 }
46
47 void Header::Clear() {
48   bytes_parsed_ = 0;
49   parse_error_ = READ_INIT;
50   last_parse_error_ = READ_INIT;
51   crt_parsing_field_name_.clear();
52   crt_parsing_field_content_.clear();
53
54   http_version_ = VERSION_UNKNOWN;
55   method_ = METHOD_UNKNOWN;
56   uri_.clear();
57   status_code_ = UNKNOWN;
58   reason_.clear();
59   first_line_type_ = UNKNOWN_LINE;
60
61   fields_.clear();
62   verbatim_.clear();
63 }
64
65 const char* Header::ParseErrorName(ParseError err) {
66   switch (err) {
67     CONSIDER(READ_INIT);
68     CONSIDER(READ_OK);
69     CONSIDER(READ_NO_DATA);
70     CONSIDER(READ_BAD_FIELD_SPEC);
71     CONSIDER(READ_NO_FIELD);
72     CONSIDER(READ_NO_STATUS_REASON);
73     CONSIDER(READ_NO_REQUEST_VERSION);
74     CONSIDER(READ_INVALID_STATUS_CODE);
75     CONSIDER(READ_NO_STATUS_CODE);
76     CONSIDER(READ_NO_REQUEST_URI);
77   }
78   return "UNKNOWN";
79 }
80
81
82 string Header::NormalizeFieldName(const char* field_name, int32 len) {
83   const char* last = field_name + len - 1;
84   while ( last >= field_name && IsLwfChar(*last) ) {
85     --last;
86     --len;
87   }
88   while ( IsLwfChar(*field_name) ) {
89     ++field_name;
90     --len;
91   }
92
93   string s(field_name, len);
94   char* p = const_cast<char*>(s.c_str());
95   bool should_upcase = true;
96   while ( *p ) {
97     if ( isalpha(*p) ) {
98       if ( should_upcase ) {
99         should_upcase = false;
100         if ( islower(*p) ) {
101           *p = toupper(*p);
102         }
103       } else if ( isupper(*p) ) {
104         *p = tolower(*p);
105       }
106     } else {
107       should_upcase = true;
108       if ( IsWhiteSpace(*p) ) {
109         *p = '-';
110       }
111     }
112     ++p;
113   }
114   return s;
115 }
116
117 void Header::PrepareStatusLine(HttpReturnCode code,
118                                HttpVersion version) {
119   first_line_type_ = STATUS_LINE;
120   status_code_ = code;
121   reason_ = GetHttpReturnCodeDescription(code);
122   http_version_ = version;
123 }
124
125 void Header::PrepareRequestLine(const char* uri,
126                                 HttpMethod method,
127                                 HttpVersion version) {
128   first_line_type_ = REQUEST_LINE;
129   uri_ = uri;
130   method_ = method;
131   http_version_ = version;
132 }
133
134 bool Header::AddField(const char* field_name, int32 field_name_len,
135                       const char* field_content, int32 field_content_len,
136                       bool replace, bool as_is) {
137   if ( !IsValidFieldName(field_name, field_name_len) ||
138        !IsValidFieldContent(field_content, field_content_len) ) {
139     return false;
140   }
141   string normalized_name(as_is ?
142       NormalizeFieldName(field_name, field_name_len) :
143       string(field_name, field_name_len));
144   const FieldMap::iterator it = fields_.find(normalized_name);
145   if ( it == fields_.end() ) {
146     fields_.insert(make_pair(normalized_name, string(field_content)));
147   } else if ( replace || it->second.empty() ) {
148     it->second = field_content;
149   } else {
150     it->second += ", ";
151     it->second += field_content;
152   }
153   return true;
154 }
155
156 // Removes the field alltogether from the field map
157 bool Header::ClearField(const char* field_name, int32 len, bool as_is) {
158   string normalized_name(as_is ?
159       NormalizeFieldName(field_name, len) :
160       string(field_name, len));
161   return fields_.erase(normalized_name) > 0;
162 }
163
164 bool Header::IsValidFieldName(const char* field_name, int32 len) {
165   const char* p = field_name;
166   bool valid = false;  // ensures we do have something else beside
167                        // spaces
168   while ( len-- > 0 ) {
169     if ( !IsTokenChar(*p) &&
170          (!IsWhiteSpace(*p) || p == field_name) ) {
171       return false;
172     } else {
173       valid = true;
174     }
175     ++p;
176   }
177   return valid && p > field_name;
178 }
179
180 bool Header::IsValidFieldContent(const char* field_content, int32 len) {
181   const char* p = field_content;
182   while ( len-- > 0 ) {
183     if ( IsCtlChar(*p) && !IsLwfChar(*p) )
184       return false;
185     ++p;
186   }
187   return true;   // empty content fine..
188 }
189
190 int32 Header::CopyHeaderFields(const Header& src, bool replace) {
191   int32 num = 0;
192   for ( FieldMap::const_iterator it = src.fields().begin();
193         it != src.fields().end(); ++it ) {
194     if ( AddField(it->first, it->second, replace) ) {
195       ++num;
196     }
197   }
198   return num;
199 }
200
201 int32 Header::CopyHeaders(const Header& src, bool replace) {
202   const int32 num = CopyHeaderFields(src, replace);
203   http_version_ = src.http_version_;
204   method_ = src.method_;
205   status_code_ = src.status_code_;
206   uri_ = src.uri_;
207   reason_ = src.reason_;
208   first_line_type_ = src.first_line_type_;
209
210   return num;
211 }
212
213 const char* Header::FindField(const string& field_name,
214                               int32* len) const {
215   const FieldMap::const_iterator it = fields_.find(field_name);
216   if ( it == fields_.end() ) {
217     return NULL;
218   }
219   *len = it->second.size();
220   return it->second.data();
221 }
222
223 void Header::AppendToStream(io::MemoryStream* io) const {
224   io->Write(ComposeFirstLine());
225   for ( FieldMap::const_iterator it = fields_.begin();
226         it != fields_.end(); ++it ) {
227     io->Write(it->first);
228     io->Write(": ");
229     io->Write(it->second);
230     io->Write("\r\n");
231   }
232   if ( !verbatim_.empty() ) {
233     io->Write(verbatim_);
234   }
235   io->Write("\r\n");
236 }
237 string Header::ToString() const {
238   io::MemoryStream tmp;
239   AppendToStream(&tmp);
240   return tmp.ToString();
241 }
242
243
244 string Header::ComposeFirstLine() const {
245   if ( first_line_type_ == REQUEST_LINE ) {
246     //   Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
247     return (string(GetHttpMethodName(method_)) + " " + uri_ + " " +
248             GetHttpVersionName(http_version_) + "\r\n");
249   } else if ( first_line_type_ == STATUS_LINE ) {
250     //   Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
251     return (GetHttpVersionName(http_version_) +
252             strutil::StringPrintf(" %d ", static_cast<int>(status_code_)) +
253             reason_ + "\r\n");
254   }
255   return string(strutil::StringPrintf("--- %d UNKNOWN\r\n", first_line_type_));
256 }
257
258 bool Header::ParseHeader(io::MemoryStream* io,
259                          FirstLineType expected_first_line) {
260   CHECK_NE(last_parse_error_, READ_OK);
261   if ( bytes_parsed_ == 0 ) {
262     CHECK(parse_error_ == READ_INIT ||
263           parse_error_ == READ_NO_DATA)
264             << "Parse Error: " << ParseErrorName();
265     if ( !ReadFirstLine(io, expected_first_line) ) {
266       return false;
267     }
268   } else {
269     CHECK_NE(parse_error_, READ_INIT);
270   }
271   return ReadHeaderFields(io);
272 }
273
274 //////////////////////////////////////////////////////////////////////
275 //
276 // Parsing for the first line
277 //
278 bool Header::ReadFirstLine(io::MemoryStream* io,
279                            FirstLineType expected_first_line) {
280   string line;
281   if ( !io->ReadCRLFLine(&line) ) {
282     set_parse_error(READ_NO_DATA);
283     return false;
284   }
285   DLOG_DEBUG << "HTTP first line parse: " << line.size() << " ["
286              << strutil::JsonStrEscape(line.data(),
287                                        min(line.size(), 0x100U)) << "]";
288   DCHECK_GE(line.size(), 2);  // at least CRLF
289   bytes_parsed_ += line.size();
290
291   // Cut the last CRLF
292   line.resize(line.size() - 2);
293
294   // We have three ' ' separated tokens. The meaning of each depend
295   // on the type of line (status vs. request)
296
297   //   Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
298   //   Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
299
300   //////////////////////////////////////////////////////////////////////
301   //
302   // 1nd Token
303   //
304   // Pull the first token - method / http_version
305   size_t method_pos = line.find(' ');
306   if ( method_pos == string::npos ) {
307     // Error at the first step - save whatever is to be saved and return
308     first_line_type_ = ERROR_LINE;
309     if ( expected_first_line == REQUEST_LINE ) {
310       set_parse_error(READ_NO_REQUEST_URI);
311       method_ = GetHttpMethod(line.c_str());
312     } else {
313       set_parse_error(READ_NO_STATUS_CODE);
314       http_version_ = GetHttpVersion(line.c_str());
315     }
316     return true;  // we can parse more
317   }
318   // Got a good first token !
319   if ( expected_first_line == REQUEST_LINE ) {
320     method_ = GetHttpMethod(line.substr(0, method_pos).c_str());
321   } else {
322     http_version_ = GetHttpVersion(line.substr(0, method_pos).c_str());
323   }
324   // Pass through any following spaces
325   while ( line[method_pos] == ' ' ) {
326     method_pos++;
327   }
328
329   //////////////////////////////////////////////////////////////////////
330   //
331   // 2nd Token
332   //
333   // Pull the second token - uri / status code (right after the first space)
334   DCHECK_LE(method_pos, line.size());
335   size_t uri_pos = line.find(' ', method_pos);
336   if ( uri_pos == string::npos ) {
337     // Bad - no next space - set the error
338     first_line_type_ = ERROR_LINE;
339     if ( expected_first_line == REQUEST_LINE ) {
340       set_parse_error(READ_NO_REQUEST_VERSION);
341       uri_ = line.substr(method_pos);
342     } else {
343       // This is not an error for 1.0 or less..
344       if ( http_version_ <= VERSION_1_0 ) {
345         first_line_type_ = expected_first_line;
346       } else {
347         set_parse_error(READ_NO_STATUS_REASON);
348       }
349       ParseStatusCode(line.substr(method_pos));
350     }
351     return true;  // we can parse more
352   }
353
354   // Got the second token !!
355   if ( expected_first_line == REQUEST_LINE ) {
356     uri_ = line.substr(method_pos, uri_pos - method_pos);
357   } else {
358     ParseStatusCode(line.substr(method_pos, uri_pos - method_pos));
359   }
360   // Pass through any following spaces
361   while ( line[uri_pos] == ' ' ) {
362     uri_pos++;
363   }
364
365   //////////////////////////////////////////////////////////////////////
366   //
367   // 3rd Token
368   //
369
370   // Pull the third token - if any - http version / Reason
371   if ( expected_first_line == REQUEST_LINE ) {
372     http_version_ = GetHttpVersion(line.substr(uri_pos).c_str());
373   } else {
374     reason_ = line.substr(uri_pos);
375   }
376   // We met our expectation !
377   first_line_type_ = expected_first_line;
378
379   return true;
380 }
381
382 void Header::ParseStatusCode(const string& status_code_str) {
383   errno = 0;  // essential !
384   const int status_code_int = strtol(status_code_str.c_str(), NULL, 10);
385   if ( errno ) {
386     set_parse_error(READ_INVALID_STATUS_CODE);
387     status_code_ = UNKNOWN;
388   } else {
389     status_code_ = HttpReturnCode(status_code_int);
390   }
391 }
392
393 bool Header::AddCrtParsingData() {
394   bool ret = true;
395   if ( !crt_parsing_field_name_.empty() ) {
396     if ( !IsValidFieldName(crt_parsing_field_name_) ||
397          !IsValidFieldContent(crt_parsing_field_content_) ) {
398       set_parse_error(READ_BAD_FIELD_SPEC);
399       ret = false;
400     }
401     if ( !is_strict_ || ret ) {
402       string normalized_name(
403         NormalizeFieldName(crt_parsing_field_name_));
404       const FieldMap::iterator it = fields_.find(normalized_name);
405       if ( it == fields_.end() ) {
406         fields_.insert(make_pair(normalized_name,
407                                  crt_parsing_field_content_));
408       } else {
409         it->second.append(", ");
410         it->second.append(crt_parsing_field_content_);
411       }
412     }
413   }
414   crt_parsing_field_name_.clear();
415   crt_parsing_field_content_.clear();
416   return ret;
417 }
418
419 bool Header::ReadHeaderFields(io::MemoryStream* io) {
420   string line;
421   while ( io->ReadCRLFLine(&line) ) {
422     DCHECK_GE(line.size(), 2);  // at least CRLF
423     bytes_parsed_ += line.size();
424     // Cut the last CRLF
425     line.resize(line.size() - 2);
426
427     // Process the line:
428     if ( line.empty() ) {
429       // The last empty line w/ CRLF
430       if ( AddCrtParsingData() )
431         set_parse_error(READ_OK);
432       return true;
433     }
434     // Is a continuation header ?
435     int32 lwf_end = 0;
436     while ( IsLwfChar(line[lwf_end]) )
437       ++lwf_end;
438     if ( lwf_end > 0 ) {
439       // Continuation field
440       if ( crt_parsing_field_name_.empty() ) {
441         // continuation w/ no previous field - discard
442         set_parse_error(READ_NO_FIELD);
443       } else {
444         // continue field content..
445         crt_parsing_field_content_.append(" ");
446         crt_parsing_field_content_.append(line.substr(lwf_end));
447       }
448     } else {
449       // Save whatever is in for us ..
450       AddCrtParsingData();
451       // Parse the current line ..
452       size_t field_pos = line.find(':');
453       if ( field_pos == string::npos ) {
454         set_parse_error(READ_NO_FIELD);
455       } else {
456         crt_parsing_field_name_ = line.substr(0, field_pos++);
457         // Pass over leading spaces before field content ..
458         while ( IsLwfChar(line[field_pos]) )
459           ++field_pos;
460         crt_parsing_field_content_ = line.substr(field_pos);
461       }
462     }
463   }
464   set_parse_error(READ_NO_DATA);
465   return false;
466 }
467
468 // Acceptable HTTP date formats
469 //
470 static const char kHttpDateFormats[][128] = {
471   //    Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
472   "%a, %d %b %Y %H:%M:%S %Z",
473   //    Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
474   "%A, %d-%b-%y %H:%M:%S %Z",
475   //    Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
476   "%a %b %d %H:%M:%S %Y",
477 };
478
479 time_t Header::GetDateField(const char* field_name) {
480   int32 len;
481   const char* s = FindField(field_name, &len);
482   if ( !s ) {
483     return time_t(0);
484   }
485   if ( *(s+len) != '\0' )
486     return time_t(0);
487   for ( int32 i = 0; i < NUMBEROF(kHttpDateFormats); ++i ) {
488     struct tm t;
489     if ( strptime(s, kHttpDateFormats[i], &t) != NULL ) {
490       return mktime(&t);
491     }
492   }
493   return time_t(0);
494 }
495
496 bool Header::SetDateField(const string& field_name, time_t t) {
497   char buffer[128];
498   struct tm tstruct;
499   if ( NULL == gmtime_r(&t, &tstruct) ) {
500     return false;
501   }
502   if ( strftime(buffer, sizeof(buffer), kHttpDateFormats[0], &tstruct) == 0 ) {
503     return false;
504   }
505   return AddField(field_name, string(buffer), true);
506 }
507
508 bool Header::GetAuthorizationField(string* user, string* passwd) {
509   int32 len;
510   const char* s = FindField(kHeaderAuthorization, &len);
511   if ( !s || len == 0 ) {
512     return false;
513   }
514   const char* p = strchr(strutil::StrFrontTrim(s), ' ');
515   len -= p - s;
516   char* const decoded_field = new char[len];
517   base64::Decoder decoder;
518   int32 decoded_len = decoder.Decode(p, len, decoded_field);
519   if ( decoded_len == 0 ) {
520     delete [] decoded_field;
521     return false;
522   }
523   decoded_field[decoded_len] = '\0';
524   const char* found_colon = strchr(decoded_field, ':');
525   if ( found_colon == NULL ) {
526     delete [] decoded_field;
527     return false;
528   }
529   *user = string(decoded_field, found_colon - decoded_field);
530   *passwd = string(found_colon + 1,
531                    (decoded_field + decoded_len - found_colon - 1));
532   delete [] decoded_field;
533   return true;
534 }
535
536
537 // Sets the Authorization header and sets the corresponding header
538 // according to the specified parametes.
539 // Returns false if an error occured (like the user contains : and other stuff)
540 bool Header::SetAuthorizationField(const string& user,
541                                    const string& passwd) {
542   if ( user.find(':') != string::npos ) return false;
543   const string to_encode(user + ":" + passwd);
544   const int32 buflen = 2 * to_encode.size() + 4;
545   char* encoded_header = new char[buflen];
546   base64::Encoder encoder;
547   const int32 len = encoder.Encode(to_encode.c_str(), to_encode.size(),
548                                    encoded_header, buflen);
549   const int32 len2 = encoder.EncodeEnd(encoded_header + len);
550   *(encoded_header + len + len2) = '\0';
551   bool ret = AddField(kHeaderAuthorization, sizeof(kHeaderAuthorization) - 1,
552                       string("Basic ") + encoded_header,
553                       true);
554   delete [] encoded_header;
555   return ret;
556 }
557
558
559 static const char kChunked[] = "chunked";
560 bool Header::IsChunkedTransfer() const {
561   return strutil::StrCasePrefix(
562     strutil::StrTrim(FindField(kHeaderTransferEncoding)).c_str(),
563     kChunked);
564 }
565 void Header::SetChunkedTransfer(bool is_chunked) {
566   if ( is_chunked ) {
567     AddField(kHeaderTransferEncoding, kChunked, true);
568   } else {
569     ClearField(kHeaderTransferEncoding);
570   }
571 }
572
573 static const char kGzip[] = "gzip";
574 static const char kDeflate[] = "deflate";
575 bool Header::IsGzipContentEncoding() const {
576   return strutil::StrCasePrefix(
577     strutil::StrTrim(FindField(kHeaderContentEncoding)).c_str(),
578     kGzip);
579 }
580 bool Header::IsDeflateContentEncoding() const {
581   return strutil::StrCasePrefix(
582     strutil::StrTrim(FindField(kHeaderContentEncoding)).c_str(),
583     kDeflate);
584 }
585 void Header::SetContentEncoding(const char* encoding) {
586   if ( encoding ) {
587     AddField(kHeaderContentEncoding, encoding, true);
588   } else {
589     ClearField(kHeaderContentEncoding);
590   }
591 }
592
593 bool Header::IsKeepAliveConnection() const {
594   return strutil::StrCasePrefix(
595     strutil::StrTrim(FindField(kHeaderConnection)).c_str(),
596     "keep-alive");
597 }
598
599
600 // For format of these things please check:
601 //
602 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
603 //
604 //     Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
605 //     Accept: text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c
606 //     Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
607 //
608 float Header::GetHeaderAcceptance(const string& field,
609                                   const string& value,
610                                   const string& local_wildcard_value,
611                                   const string& global_wildcard_value) const {
612   int32 len;
613   const char* s = FindField(field, &len);
614   if ( !s ) {
615     return 0.0f;
616   }
617   vector<string> components;
618   strutil::SplitString(strutil::StrTrimCompress(string(s, len)), ",",
619                        &components);
620
621   bool found_local_wildcard = false;
622   float local_wildcard_pref = 0.0f;
623   bool found_global_wildcard = false;
624   float global_wildcard_pref = 0.0f;
625
626   for ( size_t i = 0; i < components.size(); i++ ) {
627     vector<string> specs;
628     strutil::SplitString(components[i], ";", &specs);
629     float crt_quality = 1.0f;
630     for ( int i = 1; i < specs.size(); i++ ) {
631       if ( strutil::StrPrefix(specs[i].c_str(), "q=") ) {
632         crt_quality = strtof(specs[i].c_str() + 2, NULL);
633       }
634     }
635     if ( strutil::StrCaseEqual(specs[0], value) ) {
636       // We found the value - this is definitive ..
637       return crt_quality;
638     } else if ( !local_wildcard_value.empty() &&
639                 strutil::StrCaseEqual(specs[0], local_wildcard_value) ) {
640       found_local_wildcard = true;
641       local_wildcard_pref = crt_quality;
642     } else if ( !global_wildcard_value.empty() &&
643                 strutil::StrCaseEqual(specs[0], global_wildcard_value) ) {
644       found_global_wildcard = true;
645       global_wildcard_pref = crt_quality;
646     }
647   }
648   if ( found_local_wildcard ) return local_wildcard_pref;
649   if ( found_global_wildcard ) return global_wildcard_pref;
650   return 0.0f;
651 }
652
653 bool Header::IsGzipAcceptableEncoding() const {
654   if ( http_version_ < VERSION_1_0 ) {
655     return false;
656   }
657   return GetHeaderAcceptance(kHeaderAcceptEncoding, kGzip, "", "*") > 0.0f;
658 }
659
660 bool Header::IsDeflateAcceptableEncoding() const {
661   if ( http_version_ < VERSION_1_0 ) {
662     return false;
663   }
664   return GetHeaderAcceptance(kHeaderAcceptEncoding, kDeflate, "", "*") > 0.0f;
665 }
666
667 bool Header::IsZippableContentType() const {
668   int len;
669   const char* s = FindField(kHeaderContentType, &len);
670   if ( !s ) {
671     return false;
672   }
673   if ( strutil::StrCasePrefix(s, "text/") )
674     return true;
675   if ( strutil::StrCasePrefix(s, "application/") )
676     return true;
677   return false;
678 }
679
680 int64 Header::DefaultBodyLen() const {
681   if ( first_line_type_ == REQUEST_LINE ) {
682     if ( method_ == METHOD_PUT ||
683          method_ == METHOD_POST ) {
684       return kMaxInt64;
685     }
686   } else if ( first_line_type_ == STATUS_LINE ) {
687     return kMaxInt64;
688   }
689   return 0;
690 }
691 }
Note: See TracBrowser for help on using the browser.