Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(718)

Side by Side Diff: net/http/http_util.cc

Issue 203013002: Fix "unreachable code" warnings (MSVC warning 4702) in net/. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // The rules for parsing content-types were borrowed from Firefox: 5 // The rules for parsing content-types were borrowed from Firefox:
6 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834 6 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834
7 7
8 #include "net/http/http_util.h" 8 #include "net/http/http_util.h"
9 9
10 #include <algorithm> 10 #include <algorithm>
11 11
12 #include "base/basictypes.h" 12 #include "base/basictypes.h"
13 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_piece.h" 15 #include "base/strings/string_piece.h"
16 #include "base/strings/string_tokenizer.h" 16 #include "base/strings/string_tokenizer.h"
17 #include "base/strings/string_util.h" 17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h" 18 #include "base/strings/stringprintf.h"
19 #include "base/time/time.h" 19 #include "base/time/time.h"
20 20
21 using std::string;
Peter Kasting 2014/03/18 03:02:51 (Much of the file already used std:: explicitly, s
22 21
23 namespace net { 22 namespace net {
24 23
25 //----------------------------------------------------------------------------- 24 // Helpers --------------------------------------------------------------------
26 25
27 // Return the index of the closing quote of the string, if any. 26 // Returns the index of the closing quote of the string, if any. |start| points
28 static size_t FindStringEnd(const string& line, size_t start, char delim) { 27 // at the opening quote.
29 DCHECK(start < line.length() && line[start] == delim && 28 static size_t FindStringEnd(const std::string& line, size_t start, char delim) {
30 (delim == '"' || delim == '\'')); 29 DCHECK_LT(start, line.length());
30 DCHECK_EQ(line[start], delim);
31 DCHECK((delim == '"') || (delim == '\''));
31 32
32 const char set[] = { delim, '\\', '\0' }; 33 const char set[] = { delim, '\\', '\0' };
33 for (;;) { 34 for (size_t end = line.find_first_of(set, start + 1);
34 // start points to either the start quote or the last 35 end != std::string::npos; end = line.find_first_of(set, end + 2)) {
Peter Kasting 2014/03/18 03:02:51 (This line relies on the fact that find_first_of()
35 // escaped char (the char following a '\\') 36 if (line[end] != '\\')
36 37 return end;
37 size_t end = line.find_first_of(set, start + 1);
38 if (end == string::npos)
39 return line.length();
40
41 if (line[end] == '\\') {
42 // Hit a backslash-escaped char. Need to skip over it.
43 start = end + 1;
44 if (start == line.length())
45 return start;
46
47 // Go back to looking for the next escape or the string end
48 continue;
49 }
50
51 return end;
52 } 38 }
53
54 NOTREACHED();
55 return line.length(); 39 return line.length();
56 } 40 }
57 41
58 //----------------------------------------------------------------------------- 42
43 // HttpUtil -------------------------------------------------------------------
59 44
60 // static 45 // static
61 size_t HttpUtil::FindDelimiter(const string& line, size_t search_start, 46 size_t HttpUtil::FindDelimiter(const std::string& line,
47 size_t search_start,
62 char delimiter) { 48 char delimiter) {
63 do { 49 do {
64 // search_start points to the spot from which we should start looking 50 // search_start points to the spot from which we should start looking
65 // for the delimiter. 51 // for the delimiter.
66 const char delim_str[] = { delimiter, '"', '\'', '\0' }; 52 const char delim_str[] = { delimiter, '"', '\'', '\0' };
67 size_t cur_delim_pos = line.find_first_of(delim_str, search_start); 53 size_t cur_delim_pos = line.find_first_of(delim_str, search_start);
68 if (cur_delim_pos == string::npos) 54 if (cur_delim_pos == std::string::npos)
69 return line.length(); 55 return line.length();
70 56
71 char ch = line[cur_delim_pos]; 57 char ch = line[cur_delim_pos];
72 if (ch == delimiter) { 58 if (ch == delimiter) {
73 // Found delimiter 59 // Found delimiter
74 return cur_delim_pos; 60 return cur_delim_pos;
75 } 61 }
76 62
77 // We hit the start of a quoted string. Look for its end. 63 // We hit the start of a quoted string. Look for its end.
78 search_start = FindStringEnd(line, cur_delim_pos, ch); 64 search_start = FindStringEnd(line, cur_delim_pos, ch);
79 if (search_start == line.length()) 65 if (search_start == line.length())
80 return search_start; 66 return search_start;
81 67
82 ++search_start; 68 ++search_start;
83 69
84 // search_start now points to the first char after the end of the 70 // search_start now points to the first char after the end of the
85 // string, so just go back to the top of the loop and look for 71 // string, so just go back to the top of the loop and look for
86 // |delimiter| again. 72 // |delimiter| again.
87 } while (true); 73 } while (true);
88 74
89 NOTREACHED(); 75 NOTREACHED();
90 return line.length(); 76 return line.length();
91 } 77 }
92 78
93 // static 79 // static
94 void HttpUtil::ParseContentType(const string& content_type_str, 80 void HttpUtil::ParseContentType(const std::string& content_type_str,
95 string* mime_type, 81 std::string* mime_type,
96 string* charset, 82 std::string* charset,
97 bool* had_charset, 83 bool* had_charset,
98 string* boundary) { 84 std::string* boundary) {
99 const string::const_iterator begin = content_type_str.begin(); 85 const std::string::const_iterator begin = content_type_str.begin();
100 86
101 // Trim leading and trailing whitespace from type. We include '(' in 87 // Trim leading and trailing whitespace from type. We include '(' in
102 // the trailing trim set to catch media-type comments, which are not at all 88 // the trailing trim set to catch media-type comments, which are not at all
103 // standard, but may occur in rare cases. 89 // standard, but may occur in rare cases.
104 size_t type_val = content_type_str.find_first_not_of(HTTP_LWS); 90 size_t type_val = content_type_str.find_first_not_of(HTTP_LWS);
105 type_val = std::min(type_val, content_type_str.length()); 91 type_val = std::min(type_val, content_type_str.length());
106 size_t type_end = content_type_str.find_first_of(HTTP_LWS ";(", type_val); 92 size_t type_end = content_type_str.find_first_of(HTTP_LWS ";(", type_val);
107 if (string::npos == type_end) 93 if (type_end == std::string::npos)
108 type_end = content_type_str.length(); 94 type_end = content_type_str.length();
109 95
110 size_t charset_val = 0; 96 size_t charset_val = 0;
111 size_t charset_end = 0; 97 size_t charset_end = 0;
112 bool type_has_charset = false; 98 bool type_has_charset = false;
113 99
114 // Iterate over parameters 100 // Iterate over parameters
115 size_t param_start = content_type_str.find_first_of(';', type_end); 101 size_t param_start = content_type_str.find_first_of(';', type_end);
116 if (param_start != string::npos) { 102 if (param_start != std::string::npos) {
117 base::StringTokenizer tokenizer(begin + param_start, content_type_str.end(), 103 base::StringTokenizer tokenizer(begin + param_start, content_type_str.end(),
118 ";"); 104 ";");
119 tokenizer.set_quote_chars("\""); 105 tokenizer.set_quote_chars("\"");
120 while (tokenizer.GetNext()) { 106 while (tokenizer.GetNext()) {
121 string::const_iterator equals_sign = 107 std::string::const_iterator equals_sign =
122 std::find(tokenizer.token_begin(), tokenizer.token_end(), '='); 108 std::find(tokenizer.token_begin(), tokenizer.token_end(), '=');
123 if (equals_sign == tokenizer.token_end()) 109 if (equals_sign == tokenizer.token_end())
124 continue; 110 continue;
125 111
126 string::const_iterator param_name_begin = tokenizer.token_begin(); 112 std::string::const_iterator param_name_begin = tokenizer.token_begin();
127 string::const_iterator param_name_end = equals_sign; 113 std::string::const_iterator param_name_end = equals_sign;
128 TrimLWS(&param_name_begin, &param_name_end); 114 TrimLWS(&param_name_begin, &param_name_end);
129 115
130 string::const_iterator param_value_begin = equals_sign + 1; 116 std::string::const_iterator param_value_begin = equals_sign + 1;
131 string::const_iterator param_value_end = tokenizer.token_end(); 117 std::string::const_iterator param_value_end = tokenizer.token_end();
132 DCHECK(param_value_begin <= tokenizer.token_end()); 118 DCHECK(param_value_begin <= tokenizer.token_end());
133 TrimLWS(&param_value_begin, &param_value_end); 119 TrimLWS(&param_value_begin, &param_value_end);
134 120
135 if (LowerCaseEqualsASCII(param_name_begin, param_name_end, "charset")) { 121 if (LowerCaseEqualsASCII(param_name_begin, param_name_end, "charset")) {
136 // TODO(abarth): Refactor this function to consistently use iterators. 122 // TODO(abarth): Refactor this function to consistently use iterators.
137 charset_val = param_value_begin - begin; 123 charset_val = param_value_begin - begin;
138 charset_end = param_value_end - begin; 124 charset_end = param_value_end - begin;
139 type_has_charset = true; 125 type_has_charset = true;
140 } else if (LowerCaseEqualsASCII(param_name_begin, param_name_end, 126 } else if (LowerCaseEqualsASCII(param_name_begin, param_name_end,
141 "boundary")) { 127 "boundary")) {
(...skipping 23 matching lines...) Expand all
165 151
166 // if the server sent "*/*", it is meaningless, so do not store it. 152 // if the server sent "*/*", it is meaningless, so do not store it.
167 // also, if type_val is the same as mime_type, then just update the 153 // also, if type_val is the same as mime_type, then just update the
168 // charset. however, if charset is empty and mime_type hasn't 154 // charset. however, if charset is empty and mime_type hasn't
169 // changed, then don't wipe-out an existing charset. We 155 // changed, then don't wipe-out an existing charset. We
170 // also want to reject a mime-type if it does not include a slash. 156 // also want to reject a mime-type if it does not include a slash.
171 // some servers give junk after the charset parameter, which may 157 // some servers give junk after the charset parameter, which may
172 // include a comma, so this check makes us a bit more tolerant. 158 // include a comma, so this check makes us a bit more tolerant.
173 if (content_type_str.length() != 0 && 159 if (content_type_str.length() != 0 &&
174 content_type_str != "*/*" && 160 content_type_str != "*/*" &&
175 content_type_str.find_first_of('/') != string::npos) { 161 content_type_str.find_first_of('/') != std::string::npos) {
176 // Common case here is that mime_type is empty 162 // Common case here is that mime_type is empty
177 bool eq = !mime_type->empty() && LowerCaseEqualsASCII(begin + type_val, 163 bool eq = !mime_type->empty() && LowerCaseEqualsASCII(begin + type_val,
178 begin + type_end, 164 begin + type_end,
179 mime_type->data()); 165 mime_type->data());
180 if (!eq) { 166 if (!eq) {
181 mime_type->assign(begin + type_val, begin + type_end); 167 mime_type->assign(begin + type_val, begin + type_end);
182 StringToLowerASCII(mime_type); 168 StringToLowerASCII(mime_type);
183 } 169 }
184 if ((!eq && *had_charset) || type_has_charset) { 170 if ((!eq && *had_charset) || type_has_charset) {
185 *had_charset = true; 171 *had_charset = true;
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
284 if (!range.IsValid()) 270 if (!range.IsValid())
285 return false; 271 return false;
286 ranges->push_back(range); 272 ranges->push_back(range);
287 } 273 }
288 return !ranges->empty(); 274 return !ranges->empty();
289 } 275 }
290 276
291 // static 277 // static
292 bool HttpUtil::HasHeader(const std::string& headers, const char* name) { 278 bool HttpUtil::HasHeader(const std::string& headers, const char* name) {
293 size_t name_len = strlen(name); 279 size_t name_len = strlen(name);
294 string::const_iterator it = 280 std::string::const_iterator it =
295 std::search(headers.begin(), 281 std::search(headers.begin(),
296 headers.end(), 282 headers.end(),
297 name, 283 name,
298 name + name_len, 284 name + name_len,
299 base::CaseInsensitiveCompareASCII<char>()); 285 base::CaseInsensitiveCompareASCII<char>());
300 if (it == headers.end()) 286 if (it == headers.end())
301 return false; 287 return false;
302 288
303 // ensure match is prefixed by newline 289 // ensure match is prefixed by newline
304 if (it != headers.begin() && it[-1] != '\n') 290 if (it != headers.begin() && it[-1] != '\n')
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
372 if (!should_remove) { 358 if (!should_remove) {
373 // Assume that name and values are on the same line. 359 // Assume that name and values are on the same line.
374 stripped_headers.append(it.name_begin(), it.values_end()); 360 stripped_headers.append(it.name_begin(), it.values_end());
375 stripped_headers.append("\r\n"); 361 stripped_headers.append("\r\n");
376 } 362 }
377 } 363 }
378 return stripped_headers; 364 return stripped_headers;
379 } 365 }
380 366
381 // static 367 // static
382 bool HttpUtil::IsNonCoalescingHeader(string::const_iterator name_begin, 368 bool HttpUtil::IsNonCoalescingHeader(std::string::const_iterator name_begin,
383 string::const_iterator name_end) { 369 std::string::const_iterator name_end) {
384 // NOTE: "set-cookie2" headers do not support expires attributes, so we don't 370 // NOTE: "set-cookie2" headers do not support expires attributes, so we don't
385 // have to list them here. 371 // have to list them here.
386 const char* kNonCoalescingHeaders[] = { 372 const char* kNonCoalescingHeaders[] = {
387 "date", 373 "date",
388 "expires", 374 "expires",
389 "last-modified", 375 "last-modified",
390 "location", // See bug 1050541 for details 376 "location", // See bug 1050541 for details
391 "retry-after", 377 "retry-after",
392 "set-cookie", 378 "set-cookie",
393 // The format of auth-challenges mixes both space separated tokens and 379 // The format of auth-challenges mixes both space separated tokens and
394 // comma separated properties, so coalescing on comma won't work. 380 // comma separated properties, so coalescing on comma won't work.
395 "www-authenticate", 381 "www-authenticate",
396 "proxy-authenticate", 382 "proxy-authenticate",
397 // STS specifies that UAs must not process any STS headers after the first 383 // STS specifies that UAs must not process any STS headers after the first
398 // one. 384 // one.
399 "strict-transport-security" 385 "strict-transport-security"
400 }; 386 };
401 for (size_t i = 0; i < arraysize(kNonCoalescingHeaders); ++i) { 387 for (size_t i = 0; i < arraysize(kNonCoalescingHeaders); ++i) {
402 if (LowerCaseEqualsASCII(name_begin, name_end, kNonCoalescingHeaders[i])) 388 if (LowerCaseEqualsASCII(name_begin, name_end, kNonCoalescingHeaders[i]))
403 return true; 389 return true;
404 } 390 }
405 return false; 391 return false;
406 } 392 }
407 393
408 bool HttpUtil::IsLWS(char c) { 394 bool HttpUtil::IsLWS(char c) {
409 return strchr(HTTP_LWS, c) != NULL; 395 return strchr(HTTP_LWS, c) != NULL;
410 } 396 }
411 397
412 void HttpUtil::TrimLWS(string::const_iterator* begin, 398 void HttpUtil::TrimLWS(std::string::const_iterator* begin,
413 string::const_iterator* end) { 399 std::string::const_iterator* end) {
414 // leading whitespace 400 // leading whitespace
415 while (*begin < *end && IsLWS((*begin)[0])) 401 while (*begin < *end && IsLWS((*begin)[0]))
416 ++(*begin); 402 ++(*begin);
417 403
418 // trailing whitespace 404 // trailing whitespace
419 while (*begin < *end && IsLWS((*end)[-1])) 405 while (*begin < *end && IsLWS((*end)[-1]))
420 --(*end); 406 --(*end);
421 } 407 }
422 408
423 bool HttpUtil::IsQuote(char c) { 409 bool HttpUtil::IsQuote(char c) {
424 // Single quote mark isn't actually part of quoted-text production, 410 // Single quote mark isn't actually part of quoted-text production,
425 // but apparently some servers rely on this. 411 // but apparently some servers rely on this.
426 return c == '"' || c == '\''; 412 return c == '"' || c == '\'';
427 } 413 }
428 414
429 // See RFC 2616 Sec 2.2 for the definition of |token|. 415 // See RFC 2616 Sec 2.2 for the definition of |token|.
430 bool HttpUtil::IsToken(string::const_iterator begin, 416 bool HttpUtil::IsToken(std::string::const_iterator begin,
431 string::const_iterator end) { 417 std::string::const_iterator end) {
432 if (begin == end) 418 if (begin == end)
433 return false; 419 return false;
434 for (std::string::const_iterator iter = begin; iter != end; ++iter) { 420 for (std::string::const_iterator iter = begin; iter != end; ++iter) {
435 unsigned char c = *iter; 421 unsigned char c = *iter;
436 if (c >= 0x80 || c <= 0x1F || c == 0x7F || 422 if (c >= 0x80 || c <= 0x1F || c == 0x7F ||
437 c == '(' || c == ')' || c == '<' || c == '>' || c == '@' || 423 c == '(' || c == ')' || c == '<' || c == '>' || c == '@' ||
438 c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' || 424 c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' ||
439 c == '/' || c == '[' || c == ']' || c == '?' || c == '=' || 425 c == '/' || c == '[' || c == ']' || c == '?' || c == '=' ||
440 c == '{' || c == '}' || c == ' ' || c == '\t') 426 c == '{' || c == '}' || c == ' ' || c == '\t')
441 return false; 427 return false;
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after
757 // BNF from section 4.2 of RFC 2616: 743 // BNF from section 4.2 of RFC 2616:
758 // 744 //
759 // message-header = field-name ":" [ field-value ] 745 // message-header = field-name ":" [ field-value ]
760 // field-name = token 746 // field-name = token
761 // field-value = *( field-content | LWS ) 747 // field-value = *( field-content | LWS )
762 // field-content = <the OCTETs making up the field-value 748 // field-content = <the OCTETs making up the field-value
763 // and consisting of either *TEXT or combinations 749 // and consisting of either *TEXT or combinations
764 // of token, separators, and quoted-string> 750 // of token, separators, and quoted-string>
765 // 751 //
766 752
767 HttpUtil::HeadersIterator::HeadersIterator(string::const_iterator headers_begin, 753 HttpUtil::HeadersIterator::HeadersIterator(
768 string::const_iterator headers_end, 754 std::string::const_iterator headers_begin,
769 const std::string& line_delimiter) 755 std::string::const_iterator headers_end,
756 const std::string& line_delimiter)
770 : lines_(headers_begin, headers_end, line_delimiter) { 757 : lines_(headers_begin, headers_end, line_delimiter) {
771 } 758 }
772 759
773 HttpUtil::HeadersIterator::~HeadersIterator() { 760 HttpUtil::HeadersIterator::~HeadersIterator() {
774 } 761 }
775 762
776 bool HttpUtil::HeadersIterator::GetNext() { 763 bool HttpUtil::HeadersIterator::GetNext() {
777 while (lines_.GetNext()) { 764 while (lines_.GetNext()) {
778 name_begin_ = lines_.token_begin(); 765 name_begin_ = lines_.token_begin();
779 values_end_ = lines_.token_end(); 766 values_end_ = lines_.token_end();
780 767
781 string::const_iterator colon = std::find(name_begin_, values_end_, ':'); 768 std::string::const_iterator colon(std::find(name_begin_, values_end_, ':'));
782 if (colon == values_end_) 769 if (colon == values_end_)
783 continue; // skip malformed header 770 continue; // skip malformed header
784 771
785 name_end_ = colon; 772 name_end_ = colon;
786 773
787 // If the name starts with LWS, it is an invalid line. 774 // If the name starts with LWS, it is an invalid line.
788 // Leading LWS implies a line continuation, and these should have 775 // Leading LWS implies a line continuation, and these should have
789 // already been joined by AssembleRawHeaders(). 776 // already been joined by AssembleRawHeaders().
790 if (name_begin_ == name_end_ || IsLWS(*name_begin_)) 777 if (name_begin_ == name_end_ || IsLWS(*name_begin_))
791 continue; 778 continue;
(...skipping 19 matching lines...) Expand all
811 while (GetNext()) { 798 while (GetNext()) {
812 if (LowerCaseEqualsASCII(name_begin_, name_end_, name)) { 799 if (LowerCaseEqualsASCII(name_begin_, name_end_, name)) {
813 return true; 800 return true;
814 } 801 }
815 } 802 }
816 803
817 return false; 804 return false;
818 } 805 }
819 806
820 HttpUtil::ValuesIterator::ValuesIterator( 807 HttpUtil::ValuesIterator::ValuesIterator(
821 string::const_iterator values_begin, 808 std::string::const_iterator values_begin,
822 string::const_iterator values_end, 809 std::string::const_iterator values_end,
823 char delimiter) 810 char delimiter)
824 : values_(values_begin, values_end, string(1, delimiter)) { 811 : values_(values_begin, values_end, std::string(1, delimiter)) {
825 values_.set_quote_chars("\'\""); 812 values_.set_quote_chars("\'\"");
826 } 813 }
827 814
828 HttpUtil::ValuesIterator::~ValuesIterator() { 815 HttpUtil::ValuesIterator::~ValuesIterator() {
829 } 816 }
830 817
831 bool HttpUtil::ValuesIterator::GetNext() { 818 bool HttpUtil::ValuesIterator::GetNext() {
832 while (values_.GetNext()) { 819 while (values_.GetNext()) {
833 value_begin_ = values_.token_begin(); 820 value_begin_ = values_.token_begin();
834 value_end_ = values_.token_end(); 821 value_end_ = values_.token_end();
835 TrimLWS(&value_begin_, &value_end_); 822 TrimLWS(&value_begin_, &value_end_);
836 823
837 // bypass empty values. 824 // bypass empty values.
838 if (value_begin_ != value_end_) 825 if (value_begin_ != value_end_)
839 return true; 826 return true;
840 } 827 }
841 return false; 828 return false;
842 } 829 }
843 830
844 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( 831 HttpUtil::NameValuePairsIterator::NameValuePairsIterator(
845 string::const_iterator begin, 832 std::string::const_iterator begin,
846 string::const_iterator end, 833 std::string::const_iterator end,
847 char delimiter) 834 char delimiter)
848 : props_(begin, end, delimiter), 835 : props_(begin, end, delimiter),
849 valid_(true), 836 valid_(true),
850 name_begin_(end), 837 name_begin_(end),
851 name_end_(end), 838 name_end_(end),
852 value_begin_(end), 839 value_begin_(end),
853 value_end_(end), 840 value_end_(end),
854 value_is_quoted_(false) { 841 value_is_quoted_(false) {
855 } 842 }
856 843
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
911 value_is_quoted_ = true; 898 value_is_quoted_ = true;
912 // Do not store iterators into this. See declaration of unquoted_value_. 899 // Do not store iterators into this. See declaration of unquoted_value_.
913 unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_); 900 unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_);
914 } 901 }
915 } 902 }
916 903
917 return true; 904 return true;
918 } 905 }
919 906
920 } // namespace net 907 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698