15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/quic/quic_reliable_client_stream.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "net/base/test_completion_callback.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/quic/quic_client_session.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/quic/quic_utils.h"
11a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "net/quic/spdy_utils.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/quic/test_tools/quic_test_utils.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gmock/include/gmock/gmock.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using testing::AnyNumber;
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using testing::Return;
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using testing::StrEq;
1858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using testing::_;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace test {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const QuicConnectionId kStreamId = 3;
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MockDelegate : public QuicReliableClientStream::Delegate {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockDelegate() {}
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MOCK_METHOD0(OnSendData, int());
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MOCK_METHOD2(OnSendDataComplete, int(int, bool*));
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MOCK_METHOD2(OnDataReceived, int(const char*, int));
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MOCK_METHOD1(OnClose, void(QuicErrorCode));
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  MOCK_METHOD1(OnError, void(int));
35d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  MOCK_METHOD0(HasSendHeadersComplete, bool());
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(MockDelegate);
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class QuicReliableClientStreamTest
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : public ::testing::TestWithParam<QuicVersion> {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  QuicReliableClientStreamTest()
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      : session_(new MockConnection(false, SupportedVersions(GetParam()))) {
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    stream_ = new QuicReliableClientStream(kStreamId, &session_, BoundNetLog());
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    session_.ActivateStream(stream_);
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    stream_->SetDelegate(&delegate_);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void InitializeHeaders() {
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    headers_[":host"] = "www.google.com";
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    headers_[":path"] = "/index.hml";
54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    headers_[":scheme"] = "https";
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    headers_["cookie"] =
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "__utmc=160408618; "
58a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
60a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "1zFMi5vzcns38-8_Sns; "
64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
65a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
67a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
69a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
70a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
71a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
78a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
80a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  testing::StrictMock<MockDelegate> delegate_;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockSession session_;
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  QuicReliableClientStream* stream_;
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  QuicCryptoClientConfig crypto_config_;
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  SpdyHeaderBlock headers_;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)INSTANTIATE_TEST_CASE_P(Version, QuicReliableClientStreamTest,
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        ::testing::ValuesIn(QuicSupportedVersions()));
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)TEST_P(QuicReliableClientStreamTest, OnFinRead) {
92a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  InitializeHeaders();
93a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  string uncompressed_headers =
94a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      SpdyUtils::SerializeUncompressedHeaders(headers_);
95a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  EXPECT_CALL(delegate_, OnDataReceived(StrEq(uncompressed_headers.data()),
96a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                        uncompressed_headers.size()));
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  QuicStreamOffset offset = 0;
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  stream_->OnStreamHeaders(uncompressed_headers);
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  stream_->OnStreamHeadersComplete(false, uncompressed_headers.length());
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
101a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  IOVector iov;
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  QuicStreamFrame frame2(kStreamId, true, offset, iov);
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  stream_->OnStreamFrame(frame2);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)TEST_P(QuicReliableClientStreamTest, ProcessData) {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char data[] = "hello world!";
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(delegate_, OnDataReceived(StrEq(data), arraysize(data)));
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  EXPECT_EQ(arraysize(data), stream_->ProcessData(data, arraysize(data)));
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)TEST_P(QuicReliableClientStreamTest, ProcessDataWithError) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char data[] = "hello world!";
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(delegate_,
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              OnDataReceived(StrEq(data),
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             arraysize(data))).WillOnce(Return(ERR_UNEXPECTED));
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  EXPECT_EQ(0u, stream_->ProcessData(data, arraysize(data)));
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)TEST_P(QuicReliableClientStreamTest, OnError) {
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_CALL(delegate_, OnError(ERR_INTERNET_DISCONNECTED));
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  stream_->OnError(ERR_INTERNET_DISCONNECTED);
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  EXPECT_FALSE(stream_->GetDelegate());
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)TEST_P(QuicReliableClientStreamTest, WriteStreamData) {
13458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
13558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
13658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  const char kData1[] = "hello world";
13758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  const size_t kDataLen = arraysize(kData1);
13858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
13958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // All data written.
140f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  EXPECT_CALL(session_, WritevData(stream_->id(), _,  _, _, _, _)).WillOnce(
14158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      Return(QuicConsumedData(kDataLen, true)));
14258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  TestCompletionCallback callback;
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  EXPECT_EQ(OK, stream_->WriteStreamData(base::StringPiece(kData1, kDataLen),
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                         true, callback.callback()));
14558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
14658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)TEST_P(QuicReliableClientStreamTest, WriteStreamDataAsync) {
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  EXPECT_CALL(delegate_, HasSendHeadersComplete()).Times(AnyNumber());
14958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
15058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
15158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  const char kData1[] = "hello world";
15258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  const size_t kDataLen = arraysize(kData1);
15358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
15458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // No data written.
155f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  EXPECT_CALL(session_, WritevData(stream_->id(),  _, _, _, _, _)).WillOnce(
15658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      Return(QuicConsumedData(0, false)));
15758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  TestCompletionCallback callback;
15858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  EXPECT_EQ(ERR_IO_PENDING,
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            stream_->WriteStreamData(base::StringPiece(kData1, kDataLen),
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                     true, callback.callback()));
16158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ASSERT_FALSE(callback.have_result());
16258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
16358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // All data written.
164f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  EXPECT_CALL(session_, WritevData(stream_->id(),  _, _, _, _, _)).WillOnce(
16558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      Return(QuicConsumedData(kDataLen, true)));
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  stream_->OnCanWrite();
16758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ASSERT_TRUE(callback.have_result());
16858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  EXPECT_EQ(OK, callback.WaitForResult());
16958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
17058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace test
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
174