OLD | NEW |
---|---|
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 // Portions of this code based on Mozilla: | 5 // Portions of this code based on Mozilla: |
6 // (netwerk/cookie/src/nsCookieService.cpp) | 6 // (netwerk/cookie/src/nsCookieService.cpp) |
7 /* ***** BEGIN LICENSE BLOCK ***** | 7 /* ***** BEGIN LICENSE BLOCK ***** |
8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | 8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
9 * | 9 * |
10 * The contents of this file are subject to the Mozilla Public License Version | 10 * The contents of this file are subject to the Mozilla Public License Version |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
104 | 104 |
105 namespace net { | 105 namespace net { |
106 | 106 |
107 // See comments at declaration of these variables in cookie_monster.h | 107 // See comments at declaration of these variables in cookie_monster.h |
108 // for details. | 108 // for details. |
109 const size_t CookieMonster::kDomainMaxCookies = 180; | 109 const size_t CookieMonster::kDomainMaxCookies = 180; |
110 const size_t CookieMonster::kDomainPurgeCookies = 30; | 110 const size_t CookieMonster::kDomainPurgeCookies = 30; |
111 const size_t CookieMonster::kMaxCookies = 3300; | 111 const size_t CookieMonster::kMaxCookies = 3300; |
112 const size_t CookieMonster::kPurgeCookies = 300; | 112 const size_t CookieMonster::kPurgeCookies = 300; |
113 | 113 |
114 const size_t CookieMonster::kDomainCookiesQuotaLow = 30; | 114 const float CookieMonster::kDomainCookiesQuotaLow = 0.2; |
115 const size_t CookieMonster::kDomainCookiesQuotaMedium = 50; | 115 const float CookieMonster::kDomainCookiesQuotaMedium = 0.3333333333333333333333; |
116 const size_t CookieMonster::kDomainCookiesQuotaHigh = | 116 const float CookieMonster::kDomainCookiesQuotaHigh = |
117 kDomainMaxCookies - kDomainPurgeCookies - kDomainCookiesQuotaLow - | 117 1 - kDomainCookiesQuotaLow - kDomainCookiesQuotaMedium; |
118 kDomainCookiesQuotaMedium; | |
119 | 118 |
120 const int CookieMonster::kSafeFromGlobalPurgeDays = 30; | 119 const int CookieMonster::kSafeFromGlobalPurgeDays = 30; |
121 | 120 |
122 namespace { | 121 namespace { |
123 | 122 |
124 bool ContainsControlCharacter(const std::string& s) { | 123 bool ContainsControlCharacter(const std::string& s) { |
125 for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { | 124 for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { |
126 if ((*i >= 0) && (*i <= 31)) | 125 if ((*i >= 0) && (*i <= 31)) |
127 return true; | 126 return true; |
128 } | 127 } |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
243 CookieMonster::CookieItVector* non_secure_cookie_its) { | 242 CookieMonster::CookieItVector* non_secure_cookie_its) { |
244 DCHECK(secure_cookie_its && non_secure_cookie_its); | 243 DCHECK(secure_cookie_its && non_secure_cookie_its); |
245 for (const auto& curit : cookie_its) { | 244 for (const auto& curit : cookie_its) { |
246 if (curit->second->IsSecure()) | 245 if (curit->second->IsSecure()) |
247 secure_cookie_its->push_back(curit); | 246 secure_cookie_its->push_back(curit); |
248 else | 247 else |
249 non_secure_cookie_its->push_back(curit); | 248 non_secure_cookie_its->push_back(curit); |
250 } | 249 } |
251 } | 250 } |
252 | 251 |
253 // Predicate to support PartitionCookieByPriority(). | |
254 struct CookiePriorityEqualsTo | |
255 : std::unary_function<const CookieMonster::CookieMap::iterator, bool> { | |
256 explicit CookiePriorityEqualsTo(CookiePriority priority) | |
257 : priority_(priority) {} | |
258 | |
259 bool operator()(const CookieMonster::CookieMap::iterator it) const { | |
260 return it->second->Priority() == priority_; | |
261 } | |
262 | |
263 const CookiePriority priority_; | |
264 }; | |
265 | |
266 // For a CookieItVector iterator range [|it_begin|, |it_end|), | 252 // For a CookieItVector iterator range [|it_begin|, |it_end|), |
267 // moves all cookies with a given |priority| to the beginning of the list. | 253 // moves all cookies with a given |priority| to the beginning of the list. |
268 // Returns: An iterator in [it_begin, it_end) to the first element with | 254 // Returns: An iterator in [it_begin, it_end) to the first element with |
269 // priority != |priority|, or |it_end| if all have priority == |priority|. | 255 // priority != |priority|, or |it_end| if all have priority == |priority|. |
270 CookieMonster::CookieItVector::iterator PartitionCookieByPriority( | 256 CookieMonster::CookieItVector::iterator PartitionCookieByPriority( |
271 CookieMonster::CookieItVector::iterator it_begin, | 257 CookieMonster::CookieItVector::iterator it_begin, |
272 CookieMonster::CookieItVector::iterator it_end, | 258 CookieMonster::CookieItVector::iterator it_end, |
273 CookiePriority priority) { | 259 CookiePriority priority) { |
274 return std::partition(it_begin, it_end, CookiePriorityEqualsTo(priority)); | 260 return std::partition( |
261 it_begin, it_end, | |
262 [priority](const CookieMonster::CookieMap::iterator& it) { | |
263 return it->second->Priority() == priority; | |
264 }); | |
275 } | 265 } |
276 | 266 |
277 bool LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it, | 267 bool LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it, |
278 const Time& access_date) { | 268 const Time& access_date) { |
279 return it->second->LastAccessDate() < access_date; | 269 return it->second->LastAccessDate() < access_date; |
280 } | 270 } |
281 | 271 |
282 // For a CookieItVector iterator range [|it_begin|, |it_end|) | 272 // For a CookieItVector iterator range [|it_begin|, |it_end|) |
283 // from a CookieItVector sorted by LastAccessDate(), returns the | 273 // from a CookieItVector sorted by LastAccessDate(), returns the |
284 // first iterator with access date >= |access_date|, or cookie_its_end if this | 274 // first iterator with access date >= |access_date|, or cookie_its_end if this |
(...skipping 1648 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1933 bool enforce_strict_secure) { | 1923 bool enforce_strict_secure) { |
1934 lock_.AssertAcquired(); | 1924 lock_.AssertAcquired(); |
1935 | 1925 |
1936 size_t num_deleted = 0; | 1926 size_t num_deleted = 0; |
1937 Time safe_date(Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays)); | 1927 Time safe_date(Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays)); |
1938 | 1928 |
1939 // Collect garbage for this key, minding cookie priorities. | 1929 // Collect garbage for this key, minding cookie priorities. |
1940 if (cookies_.count(key) > kDomainMaxCookies) { | 1930 if (cookies_.count(key) > kDomainMaxCookies) { |
1941 VLOG(kVlogGarbageCollection) << "GarbageCollect() key: " << key; | 1931 VLOG(kVlogGarbageCollection) << "GarbageCollect() key: " << key; |
1942 | 1932 |
1943 CookieItVector* cookie_its; | 1933 CookieItVector cookie_its; |
1944 | 1934 |
1945 CookieItVector non_expired_cookie_its; | 1935 // First, we purge all expired cookies for this key (regardless of our |
1946 cookie_its = &non_expired_cookie_its; | 1936 // target number of cookies, as we have no need to keep expired cookies |
1937 // around). | |
1947 num_deleted += | 1938 num_deleted += |
1948 GarbageCollectExpired(current, cookies_.equal_range(key), cookie_its); | 1939 GarbageCollectExpired(current, cookies_.equal_range(key), &cookie_its); |
1949 | 1940 |
1950 CookieItVector secure_cookie_its; | 1941 if (cookie_its.size() > kDomainMaxCookies) { |
1951 if (enforce_strict_secure && cookie_its->size() > kDomainMaxCookies) { | 1942 // Then, if we're over the maximum allowed for this key, we'll remove |
1952 VLOG(kVlogGarbageCollection) << "Garbage collecting non-Secure cookies."; | 1943 // cookies in the following order: |
1953 num_deleted += | 1944 // |
1954 GarbageCollectNonSecure(non_expired_cookie_its, &secure_cookie_its); | 1945 // 1. Low-priority non-secure cookies. |
1955 cookie_its = &secure_cookie_its; | 1946 // 2. Medium-priority non-secure cookies. |
1956 } | 1947 // 3. High-priority non-secure cookies. |
1948 // 4. Low-priority secure cookies. | |
1949 // 5. Medium-priority secure cookies. | |
1950 // 6. High-priority secure cookies. | |
1951 // | |
1952 // Note that this implies that _all_ non-secure cookies will be removed | |
1953 // before _any_ secure cookie is removed, in accordance with | |
1954 // https://tools.ietf.org/html/draft-west-leave-secure-cookies-alone-05 | |
jww
2016/02/18 01:05:19
Should this link really have the '-05' at the end?
| |
1957 | 1955 |
1958 if (cookie_its->size() > kDomainMaxCookies) { | |
1959 VLOG(kVlogGarbageCollection) << "Deep Garbage Collect domain."; | 1956 VLOG(kVlogGarbageCollection) << "Deep Garbage Collect domain."; |
1960 size_t purge_goal = | 1957 size_t purge_goal = |
1961 cookie_its->size() - (kDomainMaxCookies - kDomainPurgeCookies); | 1958 cookie_its.size() - (kDomainMaxCookies - kDomainPurgeCookies); |
1962 DCHECK(purge_goal > kDomainPurgeCookies); | 1959 DCHECK(purge_goal > kDomainPurgeCookies); |
1963 | 1960 |
1964 // Boundary iterators into |cookie_its| for different priorities. | 1961 // To execute the above removals, we'll divide |cookies_its| into 6 |
1965 CookieItVector::iterator it_bdd[4]; | 1962 // partitions, using 7 boundaries (note that the last boundary is off the |
1966 // Intialize |it_bdd| while sorting |cookie_its| by priorities. | 1963 // end of the list): |
1967 // Schematic: [MLLHMHHLMM] => [LLL|MMMM|HHH], with 4 boundaries. | 1964 // |
1968 it_bdd[0] = cookie_its->begin(); | 1965 // If both non-secure and secure cookies are present, the list will look |
1969 it_bdd[3] = cookie_its->end(); | 1966 // like this: |
1970 it_bdd[1] = | 1967 // |
1971 PartitionCookieByPriority(it_bdd[0], it_bdd[3], COOKIE_PRIORITY_LOW); | 1968 // LLLLMMMMHHHHHLLLLMMMMHHHH |
1972 it_bdd[2] = PartitionCookieByPriority(it_bdd[1], it_bdd[3], | 1969 // ^ ^ ^ ^ ^ ^ ^ |
1973 COOKIE_PRIORITY_MEDIUM); | 1970 // 0 1 2 3 4 5 6 |
1974 size_t quota[3] = {kDomainCookiesQuotaLow, | 1971 // |
1975 kDomainCookiesQuotaMedium, | 1972 // If only secure cookies are present, the list will look like this: |
1976 kDomainCookiesQuotaHigh}; | 1973 // |
1974 // LLLLMMMMHHHHH | |
1975 // ^ ^ ^ ^ | |
1976 // 0 4 5 6 | |
1977 // 1 | |
1978 // 2 | |
1979 // 3 | |
1980 // | |
1981 // If only non-secure cookies are present, the list will look like this: | |
1982 // | |
1983 // LLLLMMMMHHHHH | |
1984 // ^ ^ ^ ^ | |
1985 // 0 1 2 3 | |
1986 // 4 | |
1987 // 5 | |
1988 // 6 | |
1989 CookieItVector::iterator it_bdd[7]; | |
1990 it_bdd[0] = cookie_its.begin(); | |
1991 it_bdd[1] = it_bdd[0]; | |
1992 it_bdd[2] = it_bdd[0]; | |
1993 it_bdd[3] = cookie_its.end(); | |
1994 it_bdd[4] = it_bdd[3]; | |
1995 it_bdd[5] = it_bdd[3]; | |
1996 it_bdd[6] = it_bdd[3]; | |
1977 | 1997 |
1978 // Purge domain cookies in 3 rounds. | 1998 size_t num_secure = 0; |
1979 // Round 1: consider low-priority cookies only: evict least-recently | 1999 size_t num_nonsecure = 0; |
1980 // accessed, while protecting quota[0] of these from deletion. | |
1981 // Round 2: consider {low, medium}-priority cookies, evict least-recently | |
1982 // accessed, while protecting quota[0] + quota[1]. | |
1983 // Round 3: consider all cookies, evict least-recently accessed. | |
1984 size_t accumulated_quota = 0; | |
1985 CookieItVector::iterator it_purge_begin = it_bdd[0]; | |
1986 for (int i = 0; i < 3 && purge_goal > 0; ++i) { | |
1987 accumulated_quota += quota[i]; | |
1988 | 2000 |
1989 size_t num_considered = it_bdd[i + 1] - it_purge_begin; | 2001 // Move all non-secure cookies to the front of the list, and set boundary |
1990 if (num_considered <= accumulated_quota) | 2002 // #3 to the first secure cookie (or off the end of the list, in the case |
1991 continue; | 2003 // where no secure cookies are present). |
2004 it_bdd[3] = std::partition(it_bdd[0], it_bdd[6], | |
2005 [](const CookieMap::iterator& it) { | |
2006 return !it->second->IsSecure(); | |
2007 }); | |
1992 | 2008 |
1993 // Number of cookies that will be purged in this round. | 2009 // If we have non-secure cookies, partition them into priorities: |
1994 size_t round_goal = | 2010 if (it_bdd[3] > it_bdd[0]) { |
1995 std::min(purge_goal, num_considered - accumulated_quota); | 2011 it_bdd[1] = PartitionCookieByPriority(it_bdd[0], it_bdd[3], |
1996 purge_goal -= round_goal; | 2012 COOKIE_PRIORITY_LOW); |
2013 it_bdd[2] = PartitionCookieByPriority(it_bdd[1], it_bdd[3], | |
2014 COOKIE_PRIORITY_MEDIUM); | |
mmenke
2016/02/17 18:41:31
This seems so much more complicated than necessary
| |
2015 num_nonsecure = it_bdd[3] - it_bdd[0]; | |
2016 } | |
1997 | 2017 |
1998 SortLeastRecentlyAccessed(it_purge_begin, it_bdd[i + 1], round_goal); | 2018 // Likewise, if we have secure cookies, partition them into priorities: |
1999 // Cookies accessed on or after |safe_date| would have been safe from | 2019 if (it_bdd[3] < it_bdd[6]) { |
2000 // global purge, and we want to keep track of this. | 2020 it_bdd[4] = PartitionCookieByPriority(it_bdd[3], it_bdd[6], |
2001 CookieItVector::iterator it_purge_end = it_purge_begin + round_goal; | 2021 COOKIE_PRIORITY_LOW); |
2002 CookieItVector::iterator it_purge_middle = | 2022 it_bdd[5] = PartitionCookieByPriority(it_bdd[4], it_bdd[6], |
2003 LowerBoundAccessDate(it_purge_begin, it_purge_end, safe_date); | 2023 COOKIE_PRIORITY_MEDIUM); |
2004 // Delete cookies accessed before |safe_date|. | 2024 num_secure = it_bdd[6] - it_bdd[3]; |
2025 } | |
2026 | |
2027 // Start with the non-secure cookies. | |
2028 if (purge_goal >= num_nonsecure) { | |
2029 // If we need to purge more cookies than we have non-secure, remove | |
2030 // them all, update |purge_goal| then purge the new |purge_goal| from | |
2031 // the secure cookies. | |
2005 num_deleted += GarbageCollectDeleteRange( | 2032 num_deleted += GarbageCollectDeleteRange( |
2006 current, DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, it_purge_begin, | 2033 current, DELETE_COOKIE_NON_SECURE, it_bdd[0], it_bdd[3]); |
2007 it_purge_middle); | 2034 num_deleted += GarbageCollectNumFromRangeWithQuota( |
2008 // Delete cookies accessed on or after |safe_date|. | 2035 current, safe_date, purge_goal - num_deleted, it_bdd, GC_SECURE); |
2009 num_deleted += GarbageCollectDeleteRange( | 2036 } else { |
2010 current, DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, it_purge_middle, | 2037 num_deleted += GarbageCollectNumFromRangeWithQuota( |
2011 it_purge_end); | 2038 current, safe_date, purge_goal, it_bdd, GC_NONSECURE); |
2012 it_purge_begin = it_purge_end; | |
2013 } | 2039 } |
2040 purge_goal -= num_deleted; | |
2041 | |
2014 DCHECK_EQ(0U, purge_goal); | 2042 DCHECK_EQ(0U, purge_goal); |
2015 } | 2043 } |
2016 } | 2044 } |
2017 | 2045 |
2018 // Collect garbage for everything. With firefox style we want to preserve | 2046 // Collect garbage for everything. With firefox style we want to preserve |
2019 // cookies accessed in kSafeFromGlobalPurgeDays, otherwise evict. | 2047 // cookies accessed in kSafeFromGlobalPurgeDays, otherwise evict. |
2020 if (cookies_.size() > kMaxCookies && earliest_access_time_ < safe_date) { | 2048 if (cookies_.size() > kMaxCookies && earliest_access_time_ < safe_date) { |
2021 VLOG(kVlogGarbageCollection) << "GarbageCollect() everything"; | 2049 VLOG(kVlogGarbageCollection) << "GarbageCollect() everything"; |
2022 CookieItVector cookie_its; | 2050 CookieItVector cookie_its; |
2023 | 2051 |
(...skipping 27 matching lines...) Expand all Loading... | |
2051 } else { | 2079 } else { |
2052 num_deleted += GarbageCollectLeastRecentlyAccessed( | 2080 num_deleted += GarbageCollectLeastRecentlyAccessed( |
2053 current, safe_date, purge_goal, cookie_its); | 2081 current, safe_date, purge_goal, cookie_its); |
2054 } | 2082 } |
2055 } | 2083 } |
2056 } | 2084 } |
2057 | 2085 |
2058 return num_deleted; | 2086 return num_deleted; |
2059 } | 2087 } |
2060 | 2088 |
2089 size_t CookieMonster::GarbageCollectNumFromRangeWithQuota( | |
2090 const Time& current, | |
2091 const Time& safe_date, | |
2092 size_t purge_goal, | |
2093 CookieItVector::iterator* it_bdd, | |
2094 GCType type) { | |
2095 size_t num_deleted = 0; | |
2096 size_t begin_partition = type == GC_SECURE ? 3 : 0; | |
2097 size_t num_cookies = it_bdd[begin_partition + 3] - it_bdd[begin_partition]; | |
2098 size_t num_to_keep = num_cookies - purge_goal; | |
2099 size_t quota[3] = {std::floor(num_to_keep * kDomainCookiesQuotaLow), | |
2100 std::floor(num_to_keep * kDomainCookiesQuotaMedium), | |
2101 std::floor(num_to_keep * kDomainCookiesQuotaHigh)}; | |
2102 | |
2103 // The quota calculation will be up to 3 fewer than the number of | |
2104 // cookies we can keep. Bump up the numbers to get the right answer: | |
2105 if (quota[0] + quota[1] + quota[2] < num_to_keep) | |
2106 quota[2] += 1; | |
2107 if (quota[0] + quota[1] + quota[2] < num_to_keep) | |
2108 quota[1] += 1; | |
2109 if (quota[0] + quota[1] + quota[2] < num_to_keep) | |
2110 quota[0] += 1; | |
2111 DCHECK_EQ(num_to_keep, quota[0] + quota[1] + quota[2]); | |
2112 | |
2113 // Purge domain cookies in 3 rounds. | |
2114 // Round 1: consider low-priority cookies only: evict least-recently | |
2115 // accessed, while protecting quota[0] of these from deletion. | |
2116 // Round 2: consider {low, medium}-priority cookies, evict least-recently | |
2117 // accessed, while protecting quota[0] + quota[1]. | |
2118 // Round 3: consider all cookies, evict least-recently accessed. | |
2119 size_t accumulated_quota = 0; | |
2120 CookieItVector::iterator it_purge_begin = it_bdd[begin_partition]; | |
2121 for (int i = 0; i < 3 && purge_goal > 0; ++i) { | |
jww
2016/02/18 01:05:19
'i < 3' -> 'i < ARRAY_SIZE(quota)' in case the len
| |
2122 accumulated_quota += quota[i]; | |
2123 | |
2124 size_t num_considered = it_bdd[i + begin_partition + 1] - it_purge_begin; | |
2125 if (num_considered <= accumulated_quota) | |
2126 continue; | |
2127 | |
2128 // Number of cookies that will be purged in this round. | |
2129 size_t round_goal = | |
2130 std::min(purge_goal, num_considered - accumulated_quota); | |
2131 purge_goal -= round_goal; | |
2132 | |
2133 SortLeastRecentlyAccessed(it_purge_begin, it_bdd[i + begin_partition + 1], | |
2134 round_goal); | |
2135 // Cookies accessed on or after |safe_date| would have been safe from | |
2136 // global purge, and we want to keep track of this. | |
2137 CookieItVector::iterator it_purge_end = it_purge_begin + round_goal; | |
2138 CookieItVector::iterator it_purge_middle = | |
2139 LowerBoundAccessDate(it_purge_begin, it_purge_end, safe_date); | |
2140 // Delete cookies accessed before |safe_date|. | |
2141 num_deleted += GarbageCollectDeleteRange( | |
2142 current, DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, it_purge_begin, | |
2143 it_purge_middle); | |
2144 // Delete cookies accessed on or after |safe_date|. | |
2145 num_deleted += GarbageCollectDeleteRange( | |
2146 current, DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, it_purge_middle, | |
2147 it_purge_end); | |
2148 it_purge_begin = it_purge_end; | |
2149 } | |
2150 return num_deleted; | |
2151 } | |
2152 | |
2061 size_t CookieMonster::GarbageCollectExpired(const Time& current, | 2153 size_t CookieMonster::GarbageCollectExpired(const Time& current, |
2062 const CookieMapItPair& itpair, | 2154 const CookieMapItPair& itpair, |
2063 CookieItVector* cookie_its) { | 2155 CookieItVector* cookie_its) { |
2064 lock_.AssertAcquired(); | 2156 lock_.AssertAcquired(); |
2065 | 2157 |
2066 int num_deleted = 0; | 2158 int num_deleted = 0; |
2067 for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) { | 2159 for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) { |
2068 CookieMap::iterator curit = it; | 2160 CookieMap::iterator curit = it; |
2069 ++it; | 2161 ++it; |
2070 | 2162 |
(...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2371 it != hook_map_.end(); ++it) { | 2463 it != hook_map_.end(); ++it) { |
2372 std::pair<GURL, std::string> key = it->first; | 2464 std::pair<GURL, std::string> key = it->first; |
2373 if (cookie.IncludeForRequestURL(key.first, opts) && | 2465 if (cookie.IncludeForRequestURL(key.first, opts) && |
2374 cookie.Name() == key.second) { | 2466 cookie.Name() == key.second) { |
2375 it->second->Notify(cookie, removed); | 2467 it->second->Notify(cookie, removed); |
2376 } | 2468 } |
2377 } | 2469 } |
2378 } | 2470 } |
2379 | 2471 |
2380 } // namespace net | 2472 } // namespace net |
OLD | NEW |