blob: 6e0053e62551678b98d1fcdc045be24535744334 [file] [log] [blame]
[email protected]6db833d12012-01-21 00:45:191// 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
3// found in the LICENSE file.
4
5#include "net/http/http_stream_parser.h"
6
[email protected]7a1fcff2012-01-24 01:07:497#include "base/file_util.h"
[email protected]57999812013-02-24 05:40:528#include "base/files/file_path.h"
[email protected]ea1a3f62012-11-16 20:34:239#include "base/files/scoped_temp_dir.h"
[email protected]4750937f2012-06-15 20:44:2110#include "base/memory/ref_counted.h"
[email protected]0f972882013-09-20 04:34:2011#include "base/run_loop.h"
[email protected]d069c11a2013-04-13 00:01:5512#include "base/strings/string_piece.h"
[email protected]125ef482013-06-11 18:32:4713#include "base/strings/stringprintf.h"
[email protected]4750937f2012-06-15 20:44:2114#include "net/base/io_buffer.h"
[email protected]6db833d12012-01-21 00:45:1915#include "net/base/net_errors.h"
[email protected]4750937f2012-06-15 20:44:2116#include "net/base/test_completion_callback.h"
[email protected]b2d26cfd2012-12-11 10:36:0617#include "net/base/upload_bytes_element_reader.h"
[email protected]7a1fcff2012-01-24 01:07:4918#include "net/base/upload_data_stream.h"
[email protected]b2d26cfd2012-12-11 10:36:0619#include "net/base/upload_file_element_reader.h"
[email protected]4750937f2012-06-15 20:44:2120#include "net/http/http_request_headers.h"
21#include "net/http/http_request_info.h"
22#include "net/http/http_response_info.h"
23#include "net/socket/client_socket_handle.h"
24#include "net/socket/socket_test_util.h"
[email protected]6db833d12012-01-21 00:45:1925#include "testing/gtest/include/gtest/gtest.h"
[email protected]f89276a72013-07-12 06:41:5426#include "url/gurl.h"
[email protected]6db833d12012-01-21 00:45:1927
28namespace net {
29
30const size_t kOutputSize = 1024; // Just large enough for this test.
31// The number of bytes that can fit in a buffer of kOutputSize.
32const size_t kMaxPayloadSize =
33 kOutputSize - HttpStreamParser::kChunkHeaderFooterSize;
34
35// The empty payload is how the last chunk is encoded.
36TEST(HttpStreamParser, EncodeChunk_EmptyPayload) {
37 char output[kOutputSize];
38
39 const base::StringPiece kPayload = "";
40 const base::StringPiece kExpected = "0\r\n\r\n";
41 const int num_bytes_written =
42 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
43 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
44 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
45}
46
47TEST(HttpStreamParser, EncodeChunk_ShortPayload) {
48 char output[kOutputSize];
49
50 const std::string kPayload("foo\x00\x11\x22", 6);
51 // 11 = payload size + sizeof("6") + CRLF x 2.
52 const std::string kExpected("6\r\nfoo\x00\x11\x22\r\n", 11);
53 const int num_bytes_written =
54 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
55 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
56 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
57}
58
59TEST(HttpStreamParser, EncodeChunk_LargePayload) {
60 char output[kOutputSize];
61
62 const std::string kPayload(1000, '\xff'); // '\xff' x 1000.
63 // 3E8 = 1000 in hex.
64 const std::string kExpected = "3E8\r\n" + kPayload + "\r\n";
65 const int num_bytes_written =
66 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
67 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
68 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
69}
70
71TEST(HttpStreamParser, EncodeChunk_FullPayload) {
72 char output[kOutputSize];
73
74 const std::string kPayload(kMaxPayloadSize, '\xff');
75 // 3F4 = 1012 in hex.
76 const std::string kExpected = "3F4\r\n" + kPayload + "\r\n";
77 const int num_bytes_written =
78 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
79 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written));
80 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written));
81}
82
83TEST(HttpStreamParser, EncodeChunk_TooLargePayload) {
84 char output[kOutputSize];
85
86 // The payload is one byte larger the output buffer size.
87 const std::string kPayload(kMaxPayloadSize + 1, '\xff');
88 const int num_bytes_written =
89 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output));
90 ASSERT_EQ(ERR_INVALID_ARGUMENT, num_bytes_written);
91}
92
[email protected]75577ec2012-01-24 23:41:5093TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_NoBody) {
[email protected]7a1fcff2012-01-24 01:07:4994 // Shouldn't be merged if upload data is non-existent.
[email protected]75577ec2012-01-24 23:41:5095 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
96 "some header", NULL));
[email protected]7a1fcff2012-01-24 01:07:4997}
98
[email protected]75577ec2012-01-24 23:41:5099TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_EmptyBody) {
[email protected]b2d26cfd2012-12-11 10:36:06100 ScopedVector<UploadElementReader> element_readers;
101 scoped_ptr<UploadDataStream> body(new UploadDataStream(&element_readers, 0));
[email protected]4db27d82012-12-20 11:50:24102 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49103 // Shouldn't be merged if upload data is empty.
[email protected]75577ec2012-01-24 23:41:50104 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
105 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49106}
107
[email protected]75577ec2012-01-24 23:41:50108TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_ChunkedBody) {
[email protected]7a1fcff2012-01-24 01:07:49109 const std::string payload = "123";
[email protected]b2d26cfd2012-12-11 10:36:06110 scoped_ptr<UploadDataStream> body(
111 new UploadDataStream(UploadDataStream::CHUNKED, 0));
112 body->AppendChunk(payload.data(), payload.size(), true);
[email protected]4db27d82012-12-20 11:50:24113 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49114 // Shouldn't be merged if upload data carries chunked data.
[email protected]75577ec2012-01-24 23:41:50115 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
116 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49117}
118
[email protected]75577ec2012-01-24 23:41:50119TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_FileBody) {
[email protected]0f972882013-09-20 04:34:20120 {
121 ScopedVector<UploadElementReader> element_readers;
[email protected]7a1fcff2012-01-24 01:07:49122
[email protected]0f972882013-09-20 04:34:20123 // Create an empty temporary file.
124 base::ScopedTempDir temp_dir;
125 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
126 base::FilePath temp_file_path;
127 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir.path(),
128 &temp_file_path));
[email protected]7a1fcff2012-01-24 01:07:49129
[email protected]0f972882013-09-20 04:34:20130 element_readers.push_back(
131 new UploadFileElementReader(base::MessageLoopProxy::current().get(),
132 temp_file_path,
133 0,
134 0,
135 base::Time()));
[email protected]7a1fcff2012-01-24 01:07:49136
[email protected]0f972882013-09-20 04:34:20137 scoped_ptr<UploadDataStream> body(
138 new UploadDataStream(&element_readers, 0));
139 TestCompletionCallback callback;
140 ASSERT_EQ(ERR_IO_PENDING, body->Init(callback.callback()));
141 ASSERT_EQ(OK, callback.WaitForResult());
142 // Shouldn't be merged if upload data carries a file, as it's not in-memory.
143 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
144 "some header", body.get()));
145 }
146 // UploadFileElementReaders may post clean-up tasks on destruction.
147 base::RunLoop().RunUntilIdle();
[email protected]7a1fcff2012-01-24 01:07:49148}
149
[email protected]75577ec2012-01-24 23:41:50150TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_SmallBodyInMemory) {
[email protected]b2d26cfd2012-12-11 10:36:06151 ScopedVector<UploadElementReader> element_readers;
[email protected]7a1fcff2012-01-24 01:07:49152 const std::string payload = "123";
[email protected]b2d26cfd2012-12-11 10:36:06153 element_readers.push_back(new UploadBytesElementReader(
154 payload.data(), payload.size()));
[email protected]7a1fcff2012-01-24 01:07:49155
[email protected]b2d26cfd2012-12-11 10:36:06156 scoped_ptr<UploadDataStream> body(new UploadDataStream(&element_readers, 0));
[email protected]4db27d82012-12-20 11:50:24157 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49158 // Yes, should be merged if the in-memory body is small here.
[email protected]75577ec2012-01-24 23:41:50159 ASSERT_TRUE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
160 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49161}
162
[email protected]75577ec2012-01-24 23:41:50163TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_LargeBodyInMemory) {
[email protected]b2d26cfd2012-12-11 10:36:06164 ScopedVector<UploadElementReader> element_readers;
[email protected]7a1fcff2012-01-24 01:07:49165 const std::string payload(10000, 'a'); // 'a' x 10000.
[email protected]b2d26cfd2012-12-11 10:36:06166 element_readers.push_back(new UploadBytesElementReader(
167 payload.data(), payload.size()));
[email protected]7a1fcff2012-01-24 01:07:49168
[email protected]b2d26cfd2012-12-11 10:36:06169 scoped_ptr<UploadDataStream> body(new UploadDataStream(&element_readers, 0));
[email protected]4db27d82012-12-20 11:50:24170 ASSERT_EQ(OK, body->Init(CompletionCallback()));
[email protected]7a1fcff2012-01-24 01:07:49171 // Shouldn't be merged if the in-memory body is large here.
[email protected]75577ec2012-01-24 23:41:50172 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody(
173 "some header", body.get()));
[email protected]7a1fcff2012-01-24 01:07:49174}
175
[email protected]4750937f2012-06-15 20:44:21176// Test to ensure the HttpStreamParser state machine does not get confused
177// when sending a request with a chunked body, where chunks become available
178// asynchronously, over a socket where writes may also complete
179// asynchronously.
180// This is a regression test for http://crbug.com/132243
181TEST(HttpStreamParser, AsyncChunkAndAsyncSocket) {
182 // The chunks that will be written in the request, as reflected in the
183 // MockWrites below.
184 static const char kChunk1[] = "Chunk 1";
185 static const char kChunk2[] = "Chunky 2";
186 static const char kChunk3[] = "Test 3";
187
188 MockWrite writes[] = {
189 MockWrite(ASYNC, 0,
190 "GET /one.html HTTP/1.1\r\n"
191 "Host: localhost\r\n"
192 "Transfer-Encoding: chunked\r\n"
193 "Connection: keep-alive\r\n\r\n"),
194 MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"),
195 MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"),
196 MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"),
197 MockWrite(ASYNC, 4, "0\r\n\r\n"),
198 };
199
200 // The size of the response body, as reflected in the Content-Length of the
201 // MockRead below.
202 static const int kBodySize = 8;
203
204 MockRead reads[] = {
205 MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"),
206 MockRead(ASYNC, 6, "Content-Length: 8\r\n\r\n"),
207 MockRead(ASYNC, 7, "one.html"),
[email protected]d55b30a2012-07-21 00:35:39208 MockRead(SYNCHRONOUS, 0, 8), // EOF
[email protected]4750937f2012-06-15 20:44:21209 };
210
[email protected]b2d26cfd2012-12-11 10:36:06211 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0);
212 upload_stream.AppendChunk(kChunk1, arraysize(kChunk1) - 1, false);
[email protected]4db27d82012-12-20 11:50:24213 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback()));
[email protected]9a963892012-11-01 11:48:13214
[email protected]dd54bd82012-07-19 23:44:57215 DeterministicSocketData data(reads, arraysize(reads),
216 writes, arraysize(writes));
217 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
[email protected]4750937f2012-06-15 20:44:21218
219 scoped_ptr<DeterministicMockTCPClientSocket> transport(
[email protected]dd54bd82012-07-19 23:44:57220 new DeterministicMockTCPClientSocket(NULL, &data));
[email protected]0edce6a2013-05-08 18:02:40221 data.set_delegate(transport->AsWeakPtr());
[email protected]4750937f2012-06-15 20:44:21222
223 TestCompletionCallback callback;
224 int rv = transport->Connect(callback.callback());
225 rv = callback.GetResult(rv);
226 ASSERT_EQ(OK, rv);
227
228 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
[email protected]18ccfdb2013-08-15 00:13:44229 socket_handle->SetSocket(transport.PassAs<StreamSocket>());
[email protected]4750937f2012-06-15 20:44:21230
231 HttpRequestInfo request_info;
232 request_info.method = "GET";
233 request_info.url = GURL("http://localhost");
234 request_info.load_flags = LOAD_NORMAL;
[email protected]bf3eb002012-11-15 05:50:11235 request_info.upload_data_stream = &upload_stream;
[email protected]4750937f2012-06-15 20:44:21236
237 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
[email protected]90499482013-06-01 00:39:50238 HttpStreamParser parser(
239 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
[email protected]4750937f2012-06-15 20:44:21240
[email protected]4750937f2012-06-15 20:44:21241 HttpRequestHeaders request_headers;
242 request_headers.SetHeader("Host", "localhost");
243 request_headers.SetHeader("Transfer-Encoding", "chunked");
244 request_headers.SetHeader("Connection", "keep-alive");
245
246 HttpResponseInfo response_info;
247 // This will attempt to Write() the initial request and headers, which will
248 // complete asynchronously.
249 rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
[email protected]bf3eb002012-11-15 05:50:11250 &response_info, callback.callback());
[email protected]4750937f2012-06-15 20:44:21251 ASSERT_EQ(ERR_IO_PENDING, rv);
252
253 // Complete the initial request write. Additionally, this should enqueue the
254 // first chunk.
[email protected]dd54bd82012-07-19 23:44:57255 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21256 ASSERT_FALSE(callback.have_result());
257
258 // Now append another chunk (while the first write is still pending), which
259 // should not confuse the state machine.
[email protected]b2d26cfd2012-12-11 10:36:06260 upload_stream.AppendChunk(kChunk2, arraysize(kChunk2) - 1, false);
[email protected]4750937f2012-06-15 20:44:21261 ASSERT_FALSE(callback.have_result());
262
263 // Complete writing the first chunk, which should then enqueue the second
264 // chunk for writing and return, because it is set to complete
265 // asynchronously.
[email protected]dd54bd82012-07-19 23:44:57266 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21267 ASSERT_FALSE(callback.have_result());
268
269 // Complete writing the second chunk. However, because no chunks are
270 // available yet, no further writes should be called until a new chunk is
271 // added.
[email protected]dd54bd82012-07-19 23:44:57272 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21273 ASSERT_FALSE(callback.have_result());
274
275 // Add the final chunk. This will enqueue another write, but it will not
276 // complete due to the async nature.
[email protected]b2d26cfd2012-12-11 10:36:06277 upload_stream.AppendChunk(kChunk3, arraysize(kChunk3) - 1, true);
[email protected]4750937f2012-06-15 20:44:21278 ASSERT_FALSE(callback.have_result());
279
280 // Finalize writing the last chunk, which will enqueue the trailer.
[email protected]dd54bd82012-07-19 23:44:57281 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21282 ASSERT_FALSE(callback.have_result());
283
284 // Finalize writing the trailer.
[email protected]dd54bd82012-07-19 23:44:57285 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21286 ASSERT_TRUE(callback.have_result());
287
288 // Warning: This will hang if the callback doesn't already have a result,
289 // due to the deterministic socket provider. Do not remove the above
290 // ASSERT_TRUE, which will avoid this hang.
291 rv = callback.WaitForResult();
292 ASSERT_EQ(OK, rv);
293
294 // Attempt to read the response status and the response headers.
295 rv = parser.ReadResponseHeaders(callback.callback());
296 ASSERT_EQ(ERR_IO_PENDING, rv);
[email protected]dd54bd82012-07-19 23:44:57297 data.RunFor(2);
[email protected]4750937f2012-06-15 20:44:21298
299 ASSERT_TRUE(callback.have_result());
300 rv = callback.WaitForResult();
301 ASSERT_GT(rv, 0);
302
303 // Finally, attempt to read the response body.
304 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
[email protected]90499482013-06-01 00:39:50305 rv = parser.ReadResponseBody(
306 body_buffer.get(), kBodySize, callback.callback());
[email protected]4750937f2012-06-15 20:44:21307 ASSERT_EQ(ERR_IO_PENDING, rv);
[email protected]dd54bd82012-07-19 23:44:57308 data.RunFor(1);
[email protected]4750937f2012-06-15 20:44:21309
310 ASSERT_TRUE(callback.have_result());
311 rv = callback.WaitForResult();
312 ASSERT_EQ(kBodySize, rv);
313}
314
[email protected]9c18dbc2013-05-29 19:06:53315TEST(HttpStreamParser, TruncatedHeaders) {
316 MockRead truncated_status_reads[] = {
317 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"),
318 MockRead(SYNCHRONOUS, 0, 2), // EOF
319 };
320
321 MockRead truncated_after_status_reads[] = {
322 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"),
323 MockRead(SYNCHRONOUS, 0, 2), // EOF
324 };
325
326 MockRead truncated_in_header_reads[] = {
327 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHead"),
328 MockRead(SYNCHRONOUS, 0, 2), // EOF
329 };
330
331 MockRead truncated_after_header_reads[] = {
332 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n"),
333 MockRead(SYNCHRONOUS, 0, 2), // EOF
334 };
335
336 MockRead truncated_after_final_newline_reads[] = {
337 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r"),
338 MockRead(SYNCHRONOUS, 0, 2), // EOF
339 };
340
341 MockRead not_truncated_reads[] = {
342 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r\n"),
343 MockRead(SYNCHRONOUS, 0, 2), // EOF
344 };
345
346 MockRead* reads[] = {
347 truncated_status_reads,
348 truncated_after_status_reads,
349 truncated_in_header_reads,
350 truncated_after_header_reads,
351 truncated_after_final_newline_reads,
352 not_truncated_reads,
353 };
354
355 MockWrite writes[] = {
356 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"),
357 };
358
359 enum {
360 HTTP = 0,
361 HTTPS,
362 NUM_PROTOCOLS,
363 };
364
365 for (size_t protocol = 0; protocol < NUM_PROTOCOLS; protocol++) {
366 SCOPED_TRACE(protocol);
367
368 for (size_t i = 0; i < arraysize(reads); i++) {
369 SCOPED_TRACE(i);
370 DeterministicSocketData data(reads[i], 2, writes, arraysize(writes));
371 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
372 data.SetStop(3);
373
374 scoped_ptr<DeterministicMockTCPClientSocket> transport(
375 new DeterministicMockTCPClientSocket(NULL, &data));
376 data.set_delegate(transport->AsWeakPtr());
377
378 TestCompletionCallback callback;
379 int rv = transport->Connect(callback.callback());
380 rv = callback.GetResult(rv);
381 ASSERT_EQ(OK, rv);
382
383 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
[email protected]18ccfdb2013-08-15 00:13:44384 socket_handle->SetSocket(transport.PassAs<StreamSocket>());
[email protected]9c18dbc2013-05-29 19:06:53385
386 HttpRequestInfo request_info;
387 request_info.method = "GET";
388 if (protocol == HTTP) {
389 request_info.url = GURL("http://localhost");
390 } else {
391 request_info.url = GURL("https://localhost");
392 }
393 request_info.load_flags = LOAD_NORMAL;
394
395 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
[email protected]90499482013-06-01 00:39:50396 HttpStreamParser parser(
397 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
[email protected]9c18dbc2013-05-29 19:06:53398
399 HttpRequestHeaders request_headers;
400 HttpResponseInfo response_info;
401 rv = parser.SendRequest("GET / HTTP/1.1\r\n", request_headers,
402 &response_info, callback.callback());
403 ASSERT_EQ(OK, rv);
404
405 rv = parser.ReadResponseHeaders(callback.callback());
406 if (i == arraysize(reads) - 1) {
407 EXPECT_EQ(OK, rv);
408 EXPECT_TRUE(response_info.headers.get());
409 } else {
410 if (protocol == HTTP) {
411 EXPECT_EQ(ERR_CONNECTION_CLOSED, rv);
412 EXPECT_TRUE(response_info.headers.get());
413 } else {
[email protected]6a621f52013-06-12 19:43:00414 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, rv);
[email protected]9c18dbc2013-05-29 19:06:53415 EXPECT_FALSE(response_info.headers.get());
416 }
417 }
418 }
419 }
420}
421
[email protected]6db833d12012-01-21 00:45:19422} // namespace net