OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 #include "net/quic/core/spdy_utils.h" | 4 #include "net/quic/core/spdy_utils.h" |
5 | 5 |
6 #include <memory> | 6 #include <memory> |
7 | 7 |
8 #include "base/macros.h" | 8 #include "base/macros.h" |
9 #include "net/quic/platform/api/quic_string_piece.h" | 9 #include "net/quic/platform/api/quic_string_piece.h" |
10 #include "net/quic/platform/api/quic_test.h" | |
11 #include "net/quic/platform/api/quic_text_utils.h" | 10 #include "net/quic/platform/api/quic_text_utils.h" |
12 #include "net/test/gtest_util.h" | 11 #include "net/test/gtest_util.h" |
13 | 12 |
14 using std::string; | 13 using std::string; |
15 using testing::UnorderedElementsAre; | 14 using testing::UnorderedElementsAre; |
16 using testing::Pair; | 15 using testing::Pair; |
17 | 16 |
18 namespace net { | 17 namespace net { |
19 namespace test { | 18 namespace test { |
20 | 19 |
21 static std::unique_ptr<QuicHeaderList> FromList( | 20 static std::unique_ptr<QuicHeaderList> FromList( |
22 const QuicHeaderList::ListType& src) { | 21 const QuicHeaderList::ListType& src) { |
23 std::unique_ptr<QuicHeaderList> headers(new QuicHeaderList); | 22 std::unique_ptr<QuicHeaderList> headers(new QuicHeaderList); |
24 headers->OnHeaderBlockStart(); | 23 headers->OnHeaderBlockStart(); |
25 for (const auto& p : src) { | 24 for (const auto& p : src) { |
26 headers->OnHeader(p.first, p.second); | 25 headers->OnHeader(p.first, p.second); |
27 } | 26 } |
28 headers->OnHeaderBlockEnd(0, 0); | 27 headers->OnHeaderBlockEnd(0, 0); |
29 return headers; | 28 return headers; |
30 } | 29 } |
31 | 30 |
32 class SpdyUtilsTest : public QuicTest {}; | 31 TEST(CopyAndValidateHeaders, NormalUsage) { |
33 | |
34 TEST_F(SpdyUtilsTest, CopyAndValidateHeaders) { | |
35 auto headers = FromList({// All cookie crumbs are joined. | 32 auto headers = FromList({// All cookie crumbs are joined. |
36 {"cookie", " part 1"}, | 33 {"cookie", " part 1"}, |
37 {"cookie", "part 2 "}, | 34 {"cookie", "part 2 "}, |
38 {"cookie", "part3"}, | 35 {"cookie", "part3"}, |
39 | 36 |
40 // Already-delimited headers are passed through. | 37 // Already-delimited headers are passed through. |
41 {"passed-through", string("foo\0baz", 7)}, | 38 {"passed-through", string("foo\0baz", 7)}, |
42 | 39 |
43 // Other headers are joined on \0. | 40 // Other headers are joined on \0. |
44 {"joined", "value 1"}, | 41 {"joined", "value 1"}, |
(...skipping 18 matching lines...) Expand all Loading... |
63 EXPECT_THAT(block, | 60 EXPECT_THAT(block, |
64 UnorderedElementsAre( | 61 UnorderedElementsAre( |
65 Pair("cookie", " part 1; part 2 ; part3; fin!"), | 62 Pair("cookie", " part 1; part 2 ; part3; fin!"), |
66 Pair("passed-through", QuicStringPiece("foo\0baz", 7)), | 63 Pair("passed-through", QuicStringPiece("foo\0baz", 7)), |
67 Pair("joined", QuicStringPiece("value 1\0value 2", 15)), | 64 Pair("joined", QuicStringPiece("value 1\0value 2", 15)), |
68 Pair("empty", ""), | 65 Pair("empty", ""), |
69 Pair("empty-joined", QuicStringPiece("\0foo\0\0", 6)))); | 66 Pair("empty-joined", QuicStringPiece("\0foo\0\0", 6)))); |
70 EXPECT_EQ(-1, content_length); | 67 EXPECT_EQ(-1, content_length); |
71 } | 68 } |
72 | 69 |
73 TEST_F(SpdyUtilsTest, CopyAndValidateHeadersEmptyName) { | 70 TEST(CopyAndValidateHeaders, EmptyName) { |
74 auto headers = FromList({{"foo", "foovalue"}, {"", "barvalue"}, {"baz", ""}}); | 71 auto headers = FromList({{"foo", "foovalue"}, {"", "barvalue"}, {"baz", ""}}); |
75 int64_t content_length = -1; | 72 int64_t content_length = -1; |
76 SpdyHeaderBlock block; | 73 SpdyHeaderBlock block; |
77 ASSERT_FALSE( | 74 ASSERT_FALSE( |
78 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); | 75 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); |
79 } | 76 } |
80 | 77 |
81 TEST_F(SpdyUtilsTest, CopyAndValidateHeadersUpperCaseName) { | 78 TEST(CopyAndValidateHeaders, UpperCaseName) { |
82 auto headers = | 79 auto headers = |
83 FromList({{"foo", "foovalue"}, {"bar", "barvalue"}, {"bAz", ""}}); | 80 FromList({{"foo", "foovalue"}, {"bar", "barvalue"}, {"bAz", ""}}); |
84 int64_t content_length = -1; | 81 int64_t content_length = -1; |
85 SpdyHeaderBlock block; | 82 SpdyHeaderBlock block; |
86 ASSERT_FALSE( | 83 ASSERT_FALSE( |
87 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); | 84 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); |
88 } | 85 } |
89 | 86 |
90 TEST_F(SpdyUtilsTest, CopyAndValidateHeadersMultipleContentLengths) { | 87 TEST(CopyAndValidateHeaders, MultipleContentLengths) { |
91 auto headers = FromList({{"content-length", "9"}, | 88 auto headers = FromList({{"content-length", "9"}, |
92 {"foo", "foovalue"}, | 89 {"foo", "foovalue"}, |
93 {"content-length", "9"}, | 90 {"content-length", "9"}, |
94 {"bar", "barvalue"}, | 91 {"bar", "barvalue"}, |
95 {"baz", ""}}); | 92 {"baz", ""}}); |
96 int64_t content_length = -1; | 93 int64_t content_length = -1; |
97 SpdyHeaderBlock block; | 94 SpdyHeaderBlock block; |
98 ASSERT_TRUE( | 95 ASSERT_TRUE( |
99 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); | 96 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); |
100 EXPECT_THAT(block, UnorderedElementsAre( | 97 EXPECT_THAT(block, UnorderedElementsAre( |
101 Pair("foo", "foovalue"), Pair("bar", "barvalue"), | 98 Pair("foo", "foovalue"), Pair("bar", "barvalue"), |
102 Pair("content-length", QuicStringPiece("9" | 99 Pair("content-length", QuicStringPiece("9" |
103 "\0" | 100 "\0" |
104 "9", | 101 "9", |
105 3)), | 102 3)), |
106 Pair("baz", ""))); | 103 Pair("baz", ""))); |
107 EXPECT_EQ(9, content_length); | 104 EXPECT_EQ(9, content_length); |
108 } | 105 } |
109 | 106 |
110 TEST_F(SpdyUtilsTest, CopyAndValidateHeadersInconsistentContentLengths) { | 107 TEST(CopyAndValidateHeaders, InconsistentContentLengths) { |
111 auto headers = FromList({{"content-length", "9"}, | 108 auto headers = FromList({{"content-length", "9"}, |
112 {"foo", "foovalue"}, | 109 {"foo", "foovalue"}, |
113 {"content-length", "8"}, | 110 {"content-length", "8"}, |
114 {"bar", "barvalue"}, | 111 {"bar", "barvalue"}, |
115 {"baz", ""}}); | 112 {"baz", ""}}); |
116 int64_t content_length = -1; | 113 int64_t content_length = -1; |
117 SpdyHeaderBlock block; | 114 SpdyHeaderBlock block; |
118 ASSERT_FALSE( | 115 ASSERT_FALSE( |
119 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); | 116 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); |
120 } | 117 } |
121 | 118 |
122 TEST_F(SpdyUtilsTest, CopyAndValidateHeadersLargeContentLength) { | 119 TEST(CopyAndValidateHeaders, LargeContentLength) { |
123 auto headers = FromList({{"content-length", "9000000000"}, | 120 auto headers = FromList({{"content-length", "9000000000"}, |
124 {"foo", "foovalue"}, | 121 {"foo", "foovalue"}, |
125 {"bar", "barvalue"}, | 122 {"bar", "barvalue"}, |
126 {"baz", ""}}); | 123 {"baz", ""}}); |
127 int64_t content_length = -1; | 124 int64_t content_length = -1; |
128 SpdyHeaderBlock block; | 125 SpdyHeaderBlock block; |
129 ASSERT_TRUE( | 126 ASSERT_TRUE( |
130 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); | 127 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); |
131 EXPECT_THAT(block, UnorderedElementsAre( | 128 EXPECT_THAT(block, UnorderedElementsAre( |
132 Pair("foo", "foovalue"), Pair("bar", "barvalue"), | 129 Pair("foo", "foovalue"), Pair("bar", "barvalue"), |
133 Pair("content-length", QuicStringPiece("9000000000")), | 130 Pair("content-length", QuicStringPiece("9000000000")), |
134 Pair("baz", ""))); | 131 Pair("baz", ""))); |
135 EXPECT_EQ(9000000000, content_length); | 132 EXPECT_EQ(9000000000, content_length); |
136 } | 133 } |
137 | 134 |
138 TEST_F(SpdyUtilsTest, CopyAndValidateHeadersMultipleValues) { | 135 TEST(CopyAndValidateHeaders, MultipleValues) { |
139 auto headers = FromList({{"foo", "foovalue"}, | 136 auto headers = FromList({{"foo", "foovalue"}, |
140 {"bar", "barvalue"}, | 137 {"bar", "barvalue"}, |
141 {"baz", ""}, | 138 {"baz", ""}, |
142 {"foo", "boo"}, | 139 {"foo", "boo"}, |
143 {"baz", "buzz"}}); | 140 {"baz", "buzz"}}); |
144 int64_t content_length = -1; | 141 int64_t content_length = -1; |
145 SpdyHeaderBlock block; | 142 SpdyHeaderBlock block; |
146 ASSERT_TRUE( | 143 ASSERT_TRUE( |
147 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); | 144 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); |
148 EXPECT_THAT(block, UnorderedElementsAre( | 145 EXPECT_THAT(block, UnorderedElementsAre( |
149 Pair("foo", QuicStringPiece("foovalue\0boo", 12)), | 146 Pair("foo", QuicStringPiece("foovalue\0boo", 12)), |
150 Pair("bar", "barvalue"), | 147 Pair("bar", "barvalue"), |
151 Pair("baz", QuicStringPiece("\0buzz", 5)))); | 148 Pair("baz", QuicStringPiece("\0buzz", 5)))); |
152 EXPECT_EQ(-1, content_length); | 149 EXPECT_EQ(-1, content_length); |
153 } | 150 } |
154 | 151 |
155 TEST_F(SpdyUtilsTest, CopyAndValidateHeadersMoreThanTwoValues) { | 152 TEST(CopyAndValidateHeaders, MoreThanTwoValues) { |
156 auto headers = FromList({{"set-cookie", "value1"}, | 153 auto headers = FromList({{"set-cookie", "value1"}, |
157 {"set-cookie", "value2"}, | 154 {"set-cookie", "value2"}, |
158 {"set-cookie", "value3"}}); | 155 {"set-cookie", "value3"}}); |
159 int64_t content_length = -1; | 156 int64_t content_length = -1; |
160 SpdyHeaderBlock block; | 157 SpdyHeaderBlock block; |
161 ASSERT_TRUE( | 158 ASSERT_TRUE( |
162 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); | 159 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); |
163 EXPECT_THAT( | 160 EXPECT_THAT( |
164 block, UnorderedElementsAre(Pair( | 161 block, UnorderedElementsAre(Pair( |
165 "set-cookie", QuicStringPiece("value1\0value2\0value3", 20)))); | 162 "set-cookie", QuicStringPiece("value1\0value2\0value3", 20)))); |
166 EXPECT_EQ(-1, content_length); | 163 EXPECT_EQ(-1, content_length); |
167 } | 164 } |
168 | 165 |
169 TEST_F(SpdyUtilsTest, CopyAndValidateHeadersCookie) { | 166 TEST(CopyAndValidateHeaders, Cookie) { |
170 auto headers = FromList({{"foo", "foovalue"}, | 167 auto headers = FromList({{"foo", "foovalue"}, |
171 {"bar", "barvalue"}, | 168 {"bar", "barvalue"}, |
172 {"cookie", "value1"}, | 169 {"cookie", "value1"}, |
173 {"baz", ""}}); | 170 {"baz", ""}}); |
174 int64_t content_length = -1; | 171 int64_t content_length = -1; |
175 SpdyHeaderBlock block; | 172 SpdyHeaderBlock block; |
176 ASSERT_TRUE( | 173 ASSERT_TRUE( |
177 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); | 174 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); |
178 EXPECT_THAT(block, UnorderedElementsAre( | 175 EXPECT_THAT(block, UnorderedElementsAre( |
179 Pair("foo", "foovalue"), Pair("bar", "barvalue"), | 176 Pair("foo", "foovalue"), Pair("bar", "barvalue"), |
180 Pair("cookie", "value1"), Pair("baz", ""))); | 177 Pair("cookie", "value1"), Pair("baz", ""))); |
181 EXPECT_EQ(-1, content_length); | 178 EXPECT_EQ(-1, content_length); |
182 } | 179 } |
183 | 180 |
184 TEST_F(SpdyUtilsTest, CopyAndValidateHeadersMultipleCookies) { | 181 TEST(CopyAndValidateHeaders, MultipleCookies) { |
185 auto headers = FromList({{"foo", "foovalue"}, | 182 auto headers = FromList({{"foo", "foovalue"}, |
186 {"bar", "barvalue"}, | 183 {"bar", "barvalue"}, |
187 {"cookie", "value1"}, | 184 {"cookie", "value1"}, |
188 {"baz", ""}, | 185 {"baz", ""}, |
189 {"cookie", "value2"}}); | 186 {"cookie", "value2"}}); |
190 int64_t content_length = -1; | 187 int64_t content_length = -1; |
191 SpdyHeaderBlock block; | 188 SpdyHeaderBlock block; |
192 ASSERT_TRUE( | 189 ASSERT_TRUE( |
193 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); | 190 SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); |
194 EXPECT_THAT(block, UnorderedElementsAre( | 191 EXPECT_THAT(block, UnorderedElementsAre( |
195 Pair("foo", "foovalue"), Pair("bar", "barvalue"), | 192 Pair("foo", "foovalue"), Pair("bar", "barvalue"), |
196 Pair("cookie", "value1; value2"), Pair("baz", ""))); | 193 Pair("cookie", "value1; value2"), Pair("baz", ""))); |
197 EXPECT_EQ(-1, content_length); | 194 EXPECT_EQ(-1, content_length); |
198 } | 195 } |
199 | 196 |
200 TEST_F(SpdyUtilsTest, GetUrlFromHeaderBlock) { | 197 TEST(GetUrlFromHeaderBlock, Basic) { |
201 SpdyHeaderBlock headers; | 198 SpdyHeaderBlock headers; |
202 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), ""); | 199 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), ""); |
203 headers[":scheme"] = "https"; | 200 headers[":scheme"] = "https"; |
204 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), ""); | 201 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), ""); |
205 headers[":authority"] = "www.google.com"; | 202 headers[":authority"] = "www.google.com"; |
206 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), ""); | 203 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), ""); |
207 headers[":path"] = "/index.html"; | 204 headers[":path"] = "/index.html"; |
208 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), | 205 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), |
209 "https://www.google.com/index.html"); | 206 "https://www.google.com/index.html"); |
210 headers["key1"] = "value1"; | 207 headers["key1"] = "value1"; |
211 headers["key2"] = "value2"; | 208 headers["key2"] = "value2"; |
212 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), | 209 EXPECT_EQ(SpdyUtils::GetUrlFromHeaderBlock(headers), |
213 "https://www.google.com/index.html"); | 210 "https://www.google.com/index.html"); |
214 } | 211 } |
215 | 212 |
216 TEST_F(SpdyUtilsTest, GetHostNameFromHeaderBlock) { | 213 TEST(GetHostNameFromHeaderBlock, NormalUsage) { |
217 SpdyHeaderBlock headers; | 214 SpdyHeaderBlock headers; |
218 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), ""); | 215 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), ""); |
219 headers[":scheme"] = "https"; | 216 headers[":scheme"] = "https"; |
220 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), ""); | 217 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), ""); |
221 headers[":authority"] = "www.google.com"; | 218 headers[":authority"] = "www.google.com"; |
222 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), ""); | 219 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), ""); |
223 headers[":path"] = "/index.html"; | 220 headers[":path"] = "/index.html"; |
224 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "www.google.com"); | 221 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "www.google.com"); |
225 headers["key1"] = "value1"; | 222 headers["key1"] = "value1"; |
226 headers["key2"] = "value2"; | 223 headers["key2"] = "value2"; |
227 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "www.google.com"); | 224 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "www.google.com"); |
228 headers[":authority"] = "www.google.com:6666"; | 225 headers[":authority"] = "www.google.com:6666"; |
229 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "www.google.com"); | 226 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "www.google.com"); |
230 headers[":authority"] = "192.168.1.1"; | 227 headers[":authority"] = "192.168.1.1"; |
231 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "192.168.1.1"); | 228 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "192.168.1.1"); |
232 headers[":authority"] = "192.168.1.1:6666"; | 229 headers[":authority"] = "192.168.1.1:6666"; |
233 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "192.168.1.1"); | 230 EXPECT_EQ(SpdyUtils::GetHostNameFromHeaderBlock(headers), "192.168.1.1"); |
234 } | 231 } |
235 | 232 |
236 TEST_F(SpdyUtilsTest, PopulateHeaderBlockFromUrl) { | 233 TEST(PopulateHeaderBlockFromUrl, NormalUsage) { |
237 string url = "https://www.google.com/index.html"; | 234 string url = "https://www.google.com/index.html"; |
238 SpdyHeaderBlock headers; | 235 SpdyHeaderBlock headers; |
239 EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers)); | 236 EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers)); |
240 EXPECT_EQ("https", headers[":scheme"].as_string()); | 237 EXPECT_EQ("https", headers[":scheme"].as_string()); |
241 EXPECT_EQ("www.google.com", headers[":authority"].as_string()); | 238 EXPECT_EQ("www.google.com", headers[":authority"].as_string()); |
242 EXPECT_EQ("/index.html", headers[":path"].as_string()); | 239 EXPECT_EQ("/index.html", headers[":path"].as_string()); |
243 } | 240 } |
244 | 241 |
245 TEST_F(SpdyUtilsTest, PopulateHeaderBlockFromUrlWithNoPath) { | 242 TEST(PopulateHeaderBlockFromUrl, UrlWithNoPath) { |
246 string url = "https://www.google.com"; | 243 string url = "https://www.google.com"; |
247 SpdyHeaderBlock headers; | 244 SpdyHeaderBlock headers; |
248 EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers)); | 245 EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers)); |
249 EXPECT_EQ("https", headers[":scheme"].as_string()); | 246 EXPECT_EQ("https", headers[":scheme"].as_string()); |
250 EXPECT_EQ("www.google.com", headers[":authority"].as_string()); | 247 EXPECT_EQ("www.google.com", headers[":authority"].as_string()); |
251 EXPECT_EQ("/", headers[":path"].as_string()); | 248 EXPECT_EQ("/", headers[":path"].as_string()); |
252 } | 249 } |
253 | 250 |
254 TEST_F(SpdyUtilsTest, PopulateHeaderBlockFromUrlFails) { | 251 TEST(PopulateHeaderBlockFromUrl, Failure) { |
255 SpdyHeaderBlock headers; | 252 SpdyHeaderBlock headers; |
256 EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/", &headers)); | 253 EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/", &headers)); |
257 EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/index.html", &headers)); | 254 EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/index.html", &headers)); |
258 EXPECT_FALSE( | 255 EXPECT_FALSE( |
259 SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers)); | 256 SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers)); |
260 } | 257 } |
261 | 258 |
262 } // namespace test | 259 } // namespace test |
263 } // namespace net | 260 } // namespace net |
OLD | NEW |