| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/quic/quic_http_stream.h" |
| |
| #include <vector> |
| |
| #include "net/base/chunked_upload_data_stream.h" |
| #include "net/base/elements_upload_data_stream.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/base/upload_bytes_element_reader.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/transport_security_state.h" |
| #include "net/quic/congestion_control/receive_algorithm_interface.h" |
| #include "net/quic/congestion_control/send_algorithm_interface.h" |
| #include "net/quic/crypto/crypto_protocol.h" |
| #include "net/quic/crypto/quic_decrypter.h" |
| #include "net/quic/crypto/quic_encrypter.h" |
| #include "net/quic/crypto/quic_server_info.h" |
| #include "net/quic/quic_client_session.h" |
| #include "net/quic/quic_connection.h" |
| #include "net/quic/quic_connection_helper.h" |
| #include "net/quic/quic_default_packet_writer.h" |
| #include "net/quic/quic_http_utils.h" |
| #include "net/quic/quic_reliable_client_stream.h" |
| #include "net/quic/quic_write_blocked_list.h" |
| #include "net/quic/spdy_utils.h" |
| #include "net/quic/test_tools/mock_clock.h" |
| #include "net/quic/test_tools/mock_crypto_client_stream_factory.h" |
| #include "net/quic/test_tools/mock_random.h" |
| #include "net/quic/test_tools/quic_connection_peer.h" |
| #include "net/quic/test_tools/quic_test_packet_maker.h" |
| #include "net/quic/test_tools/quic_test_utils.h" |
| #include "net/quic/test_tools/test_task_runner.h" |
| #include "net/socket/socket_test_util.h" |
| #include "net/spdy/spdy_frame_builder.h" |
| #include "net/spdy/spdy_framer.h" |
| #include "net/spdy/spdy_http_utils.h" |
| #include "net/spdy/spdy_protocol.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::Return; |
| |
| namespace net { |
| namespace test { |
| namespace { |
| |
| const char kUploadData[] = "Really nifty data!"; |
| const char kServerHostname[] = "www.google.com"; |
| const uint16 kServerPort = 80; |
| |
| class TestQuicConnection : public QuicConnection { |
| public: |
| TestQuicConnection(const QuicVersionVector& versions, |
| QuicConnectionId connection_id, |
| IPEndPoint address, |
| QuicConnectionHelper* helper, |
| const QuicConnection::PacketWriterFactory& writer_factory) |
| : QuicConnection(connection_id, |
| address, |
| helper, |
| writer_factory, |
| true /* owns_writer */, |
| false /* is_server */, |
| false /* is_secure */, |
| versions) { |
| } |
| |
| void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { |
| QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm); |
| } |
| |
| void SetReceiveAlgorithm(ReceiveAlgorithmInterface* receive_algorithm) { |
| QuicConnectionPeer::SetReceiveAlgorithm(this, receive_algorithm); |
| } |
| }; |
| |
| class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { |
| public: |
| virtual bool GenerateCongestionFeedback( |
| QuicCongestionFeedbackFrame* /*congestion_feedback*/) { |
| return false; |
| } |
| |
| MOCK_METHOD3(RecordIncomingPacket, |
| void(QuicByteCount, QuicPacketSequenceNumber, QuicTime)); |
| }; |
| |
| // Subclass of QuicHttpStream that closes itself when the first piece of data |
| // is received. |
| class AutoClosingStream : public QuicHttpStream { |
| public: |
| explicit AutoClosingStream(const base::WeakPtr<QuicClientSession>& session) |
| : QuicHttpStream(session) { |
| } |
| |
| int OnDataReceived(const char* data, int length) override { |
| Close(false); |
| return OK; |
| } |
| }; |
| |
| class TestPacketWriterFactory : public QuicConnection::PacketWriterFactory { |
| public: |
| explicit TestPacketWriterFactory(DatagramClientSocket* socket) |
| : socket_(socket) {} |
| ~TestPacketWriterFactory() override {} |
| |
| QuicPacketWriter* Create(QuicConnection* connection) const override { |
| return new QuicDefaultPacketWriter(socket_); |
| } |
| |
| private: |
| DatagramClientSocket* socket_; |
| }; |
| |
| } // namespace |
| |
| class QuicHttpStreamPeer { |
| public: |
| static QuicReliableClientStream* GetQuicReliableClientStream( |
| QuicHttpStream* stream) { |
| return stream->stream_; |
| } |
| }; |
| |
| class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> { |
| protected: |
| static const bool kFin = true; |
| static const bool kIncludeVersion = true; |
| static const bool kIncludeCongestionFeedback = true; |
| |
| // Holds a packet to be written to the wire, and the IO mode that should |
| // be used by the mock socket when performing the write. |
| struct PacketToWrite { |
| PacketToWrite(IoMode mode, QuicEncryptedPacket* packet) |
| : mode(mode), |
| packet(packet) { |
| } |
| IoMode mode; |
| QuicEncryptedPacket* packet; |
| }; |
| |
| QuicHttpStreamTest() |
| : net_log_(BoundNetLog()), |
| use_closing_stream_(false), |
| read_buffer_(new IOBufferWithSize(4096)), |
| connection_id_(2), |
| stream_id_(kClientDataStreamId1), |
| maker_(GetParam(), connection_id_, &clock_), |
| random_generator_(0) { |
| IPAddressNumber ip; |
| CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); |
| peer_addr_ = IPEndPoint(ip, 443); |
| self_addr_ = IPEndPoint(ip, 8435); |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); |
| } |
| |
| ~QuicHttpStreamTest() { |
| session_->CloseSessionOnError(ERR_ABORTED); |
| for (size_t i = 0; i < writes_.size(); i++) { |
| delete writes_[i].packet; |
| } |
| } |
| |
| // Adds a packet to the list of expected writes. |
| void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) { |
| writes_.push_back(PacketToWrite(SYNCHRONOUS, packet.release())); |
| } |
| |
| // Returns the packet to be written at position |pos|. |
| QuicEncryptedPacket* GetWrite(size_t pos) { |
| return writes_[pos].packet; |
| } |
| |
| bool AtEof() { |
| return socket_data_->at_read_eof() && socket_data_->at_write_eof(); |
| } |
| |
| void ProcessPacket(scoped_ptr<QuicEncryptedPacket> packet) { |
| connection_->ProcessUdpPacket(self_addr_, peer_addr_, *packet); |
| } |
| |
| // Configures the test fixture to use the list of expected writes. |
| void Initialize() { |
| mock_writes_.reset(new MockWrite[writes_.size()]); |
| for (size_t i = 0; i < writes_.size(); i++) { |
| mock_writes_[i] = MockWrite(writes_[i].mode, |
| writes_[i].packet->data(), |
| writes_[i].packet->length()); |
| }; |
| |
| socket_data_.reset(new StaticSocketDataProvider( |
| nullptr, 0, mock_writes_.get(), writes_.size())); |
| |
| MockUDPClientSocket* socket = new MockUDPClientSocket(socket_data_.get(), |
| net_log_.net_log()); |
| socket->Connect(peer_addr_); |
| runner_ = new TestTaskRunner(&clock_); |
| send_algorithm_ = new MockSendAlgorithm(); |
| receive_algorithm_ = new TestReceiveAlgorithm(); |
| EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _)). |
| Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, |
| OnPacketSent(_, _, _, _, _)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly( |
| Return(QuicTime::Delta::Zero())); |
| EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly( |
| Return(kMaxPacketSize)); |
| EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)). |
| WillRepeatedly(Return(QuicTime::Delta::Zero())); |
| EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly( |
| Return(QuicBandwidth::Zero())); |
| EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _, _)).Times(AnyNumber()); |
| helper_.reset(new QuicConnectionHelper(runner_.get(), &clock_, |
| &random_generator_)); |
| TestPacketWriterFactory writer_factory(socket); |
| connection_ = new TestQuicConnection(SupportedVersions(GetParam()), |
| connection_id_, peer_addr_, |
| helper_.get(), writer_factory); |
| connection_->set_visitor(&visitor_); |
| connection_->SetSendAlgorithm(send_algorithm_); |
| connection_->SetReceiveAlgorithm(receive_algorithm_); |
| session_.reset( |
| new QuicClientSession(connection_, |
| scoped_ptr<DatagramClientSocket>(socket), |
| nullptr, |
| &transport_security_state_, |
| make_scoped_ptr((QuicServerInfo*)nullptr), |
| DefaultQuicConfig(), |
| base::MessageLoop::current()-> |
| message_loop_proxy().get(), |
| nullptr)); |
| session_->InitializeSession(QuicServerId(kServerHostname, kServerPort, |
| /*is_secure=*/false, |
| PRIVACY_MODE_DISABLED), |
| &crypto_config_, |
| &crypto_client_stream_factory_); |
| session_->GetCryptoStream()->CryptoConnect(); |
| EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed()); |
| stream_.reset(use_closing_stream_ ? |
| new AutoClosingStream(session_->GetWeakPtr()) : |
| new QuicHttpStream(session_->GetWeakPtr())); |
| } |
| |
| void SetRequest(const std::string& method, |
| const std::string& path, |
| RequestPriority priority) { |
| request_headers_ = maker_.GetRequestHeaders(method, "http", path); |
| } |
| |
| void SetResponse(const std::string& status, const std::string& body) { |
| response_headers_ = maker_.GetResponseHeaders(status); |
| response_data_ = body; |
| } |
| |
| scoped_ptr<QuicEncryptedPacket> ConstructDataPacket( |
| QuicPacketSequenceNumber sequence_number, |
| bool should_include_version, |
| bool fin, |
| QuicStreamOffset offset, |
| base::StringPiece data) { |
| return maker_.MakeDataPacket( |
| sequence_number, stream_id_, should_include_version, fin, offset, data); |
| } |
| |
| scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket( |
| QuicPacketSequenceNumber sequence_number, |
| bool fin) { |
| return maker_.MakeRequestHeadersPacket( |
| sequence_number, stream_id_, kIncludeVersion, fin, request_headers_); |
| } |
| |
| scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket( |
| QuicPacketSequenceNumber sequence_number, |
| bool fin) { |
| return maker_.MakeResponseHeadersPacket( |
| sequence_number, stream_id_, !kIncludeVersion, fin, response_headers_); |
| } |
| |
| scoped_ptr<QuicEncryptedPacket> ConstructRstStreamPacket( |
| QuicPacketSequenceNumber sequence_number) { |
| return maker_.MakeRstPacket( |
| sequence_number, true, stream_id_, |
| AdjustErrorForVersion(QUIC_RST_ACKNOWLEDGEMENT, GetParam())); |
| } |
| |
| scoped_ptr<QuicEncryptedPacket> ConstructAckAndRstStreamPacket( |
| QuicPacketSequenceNumber sequence_number) { |
| return maker_.MakeAckAndRstPacket( |
| sequence_number, !kIncludeVersion, stream_id_, QUIC_STREAM_CANCELLED, |
| 2, 1, !kIncludeCongestionFeedback); |
| } |
| |
| scoped_ptr<QuicEncryptedPacket> ConstructAckPacket( |
| QuicPacketSequenceNumber sequence_number, |
| QuicPacketSequenceNumber largest_received, |
| QuicPacketSequenceNumber least_unacked) { |
| return maker_.MakeAckPacket(sequence_number, largest_received, |
| least_unacked, !kIncludeCongestionFeedback); |
| } |
| |
| BoundNetLog net_log_; |
| bool use_closing_stream_; |
| MockSendAlgorithm* send_algorithm_; |
| TestReceiveAlgorithm* receive_algorithm_; |
| scoped_refptr<TestTaskRunner> runner_; |
| scoped_ptr<MockWrite[]> mock_writes_; |
| MockClock clock_; |
| TestQuicConnection* connection_; |
| scoped_ptr<QuicConnectionHelper> helper_; |
| testing::StrictMock<MockConnectionVisitor> visitor_; |
| scoped_ptr<QuicHttpStream> stream_; |
| TransportSecurityState transport_security_state_; |
| scoped_ptr<QuicClientSession> session_; |
| QuicCryptoClientConfig crypto_config_; |
| TestCompletionCallback callback_; |
| HttpRequestInfo request_; |
| HttpRequestHeaders headers_; |
| HttpResponseInfo response_; |
| scoped_refptr<IOBufferWithSize> read_buffer_; |
| SpdyHeaderBlock request_headers_; |
| SpdyHeaderBlock response_headers_; |
| std::string request_data_; |
| std::string response_data_; |
| |
| private: |
| const QuicConnectionId connection_id_; |
| const QuicStreamId stream_id_; |
| QuicTestPacketMaker maker_; |
| IPEndPoint self_addr_; |
| IPEndPoint peer_addr_; |
| MockRandom random_generator_; |
| MockCryptoClientStreamFactory crypto_client_stream_factory_; |
| scoped_ptr<StaticSocketDataProvider> socket_data_; |
| std::vector<PacketToWrite> writes_; |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(Version, QuicHttpStreamTest, |
| ::testing::ValuesIn(QuicSupportedVersions())); |
| |
| TEST_P(QuicHttpStreamTest, RenewStreamForAuth) { |
| Initialize(); |
| EXPECT_EQ(nullptr, stream_->RenewStreamForAuth()); |
| } |
| |
| TEST_P(QuicHttpStreamTest, CanFindEndOfResponse) { |
| Initialize(); |
| EXPECT_TRUE(stream_->CanFindEndOfResponse()); |
| } |
| |
| TEST_P(QuicHttpStreamTest, IsConnectionReusable) { |
| Initialize(); |
| EXPECT_FALSE(stream_->IsConnectionReusable()); |
| } |
| |
| TEST_P(QuicHttpStreamTest, GetRequest) { |
| SetRequest("GET", "/", DEFAULT_PRIORITY); |
| AddWrite(ConstructRequestHeadersPacket(1, kFin)); |
| Initialize(); |
| |
| request_.method = "GET"; |
| request_.url = GURL("http://www.google.com/"); |
| |
| EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, |
| net_log_, callback_.callback())); |
| EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| |
| // Ack the request. |
| ProcessPacket(ConstructAckPacket(1, 0, 0)); |
| |
| EXPECT_EQ(ERR_IO_PENDING, |
| stream_->ReadResponseHeaders(callback_.callback())); |
| |
| SetResponse("404 Not Found", std::string()); |
| ProcessPacket(ConstructResponseHeadersPacket(2, kFin)); |
| |
| // Now that the headers have been processed, the callback will return. |
| EXPECT_EQ(OK, callback_.WaitForResult()); |
| ASSERT_TRUE(response_.headers.get()); |
| EXPECT_EQ(404, response_.headers->response_code()); |
| EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain")); |
| EXPECT_FALSE(response_.response_time.is_null()); |
| EXPECT_FALSE(response_.request_time.is_null()); |
| |
| // There is no body, so this should return immediately. |
| EXPECT_EQ(0, stream_->ReadResponseBody(read_buffer_.get(), |
| read_buffer_->size(), |
| callback_.callback())); |
| EXPECT_TRUE(stream_->IsResponseBodyComplete()); |
| EXPECT_TRUE(AtEof()); |
| } |
| |
| // Regression test for http://crbug.com/288128 |
| TEST_P(QuicHttpStreamTest, GetRequestLargeResponse) { |
| SetRequest("GET", "/", DEFAULT_PRIORITY); |
| AddWrite(ConstructRequestHeadersPacket(1, kFin)); |
| Initialize(); |
| |
| request_.method = "GET"; |
| request_.url = GURL("http://www.google.com/"); |
| |
| EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, |
| net_log_, callback_.callback())); |
| EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| |
| // Ack the request. |
| ProcessPacket(ConstructAckPacket(1, 0, 0)); |
| |
| EXPECT_EQ(ERR_IO_PENDING, |
| stream_->ReadResponseHeaders(callback_.callback())); |
| |
| SpdyHeaderBlock headers; |
| headers[":status"] = "200 OK"; |
| headers[":version"] = "HTTP/1.1"; |
| headers["content-type"] = "text/plain"; |
| headers["big6"] = std::string(10000, 'x'); // Lots of x's. |
| |
| std::string response = SpdyUtils::SerializeUncompressedHeaders(headers); |
| EXPECT_LT(4096u, response.length()); |
| stream_->OnDataReceived(response.data(), response.length()); |
| stream_->OnClose(QUIC_NO_ERROR); |
| |
| // Now that the headers have been processed, the callback will return. |
| EXPECT_EQ(OK, callback_.WaitForResult()); |
| ASSERT_TRUE(response_.headers.get()); |
| EXPECT_EQ(200, response_.headers->response_code()); |
| EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain")); |
| |
| // There is no body, so this should return immediately. |
| EXPECT_EQ(0, stream_->ReadResponseBody(read_buffer_.get(), |
| read_buffer_->size(), |
| callback_.callback())); |
| EXPECT_TRUE(stream_->IsResponseBodyComplete()); |
| EXPECT_TRUE(AtEof()); |
| } |
| |
| // Regression test for http://crbug.com/409101 |
| TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendRequest) { |
| SetRequest("GET", "/", DEFAULT_PRIORITY); |
| Initialize(); |
| |
| request_.method = "GET"; |
| request_.url = GURL("http://www.google.com/"); |
| |
| EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, |
| net_log_, callback_.callback())); |
| |
| session_->connection()->CloseConnection(QUIC_NO_ERROR, true); |
| |
| EXPECT_EQ(ERR_CONNECTION_CLOSED, |
| stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| } |
| |
| // Regression test for http://crbug.com/409871 |
| TEST_P(QuicHttpStreamTest, SessionClosedBeforeReadResponseHeaders) { |
| SetRequest("GET", "/", DEFAULT_PRIORITY); |
| AddWrite(ConstructRequestHeadersPacket(1, kFin)); |
| Initialize(); |
| |
| request_.method = "GET"; |
| request_.url = GURL("http://www.google.com/"); |
| |
| EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, |
| net_log_, callback_.callback())); |
| |
| EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| |
| session_->connection()->CloseConnection(QUIC_NO_ERROR, true); |
| |
| EXPECT_NE(OK, stream_->ReadResponseHeaders(callback_.callback())); |
| } |
| |
| TEST_P(QuicHttpStreamTest, SendPostRequest) { |
| SetRequest("POST", "/", DEFAULT_PRIORITY); |
| AddWrite(ConstructRequestHeadersPacket(1, !kFin)); |
| AddWrite(ConstructDataPacket(2, kIncludeVersion, kFin, 0, kUploadData)); |
| AddWrite(ConstructAckPacket(3, 3, 1)); |
| |
| Initialize(); |
| |
| ScopedVector<UploadElementReader> element_readers; |
| element_readers.push_back( |
| new UploadBytesElementReader(kUploadData, strlen(kUploadData))); |
| ElementsUploadDataStream upload_data_stream(element_readers.Pass(), 0); |
| request_.method = "POST"; |
| request_.url = GURL("http://www.google.com/"); |
| request_.upload_data_stream = &upload_data_stream; |
| ASSERT_EQ(OK, request_.upload_data_stream->Init(CompletionCallback())); |
| |
| EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, |
| net_log_, callback_.callback())); |
| EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| |
| // Ack both packets in the request. |
| ProcessPacket(ConstructAckPacket(1, 0, 0)); |
| |
| // Send the response headers (but not the body). |
| SetResponse("200 OK", std::string()); |
| ProcessPacket(ConstructResponseHeadersPacket(2, !kFin)); |
| |
| // Since the headers have already arrived, this should return immediately. |
| EXPECT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback())); |
| ASSERT_TRUE(response_.headers.get()); |
| EXPECT_EQ(200, response_.headers->response_code()); |
| EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain")); |
| |
| // Send the response body. |
| const char kResponseBody[] = "Hello world!"; |
| ProcessPacket(ConstructDataPacket(3, false, kFin, 0, kResponseBody)); |
| // Since the body has already arrived, this should return immediately. |
| EXPECT_EQ(static_cast<int>(strlen(kResponseBody)), |
| stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(), |
| callback_.callback())); |
| |
| EXPECT_TRUE(stream_->IsResponseBodyComplete()); |
| EXPECT_TRUE(AtEof()); |
| } |
| |
| TEST_P(QuicHttpStreamTest, SendChunkedPostRequest) { |
| SetRequest("POST", "/", DEFAULT_PRIORITY); |
| size_t chunk_size = strlen(kUploadData); |
| AddWrite(ConstructRequestHeadersPacket(1, !kFin)); |
| AddWrite(ConstructDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData)); |
| AddWrite(ConstructDataPacket(3, kIncludeVersion, kFin, chunk_size, |
| kUploadData)); |
| AddWrite(ConstructAckPacket(4, 3, 1)); |
| Initialize(); |
| |
| ChunkedUploadDataStream upload_data_stream(0); |
| upload_data_stream.AppendData(kUploadData, chunk_size, false); |
| |
| request_.method = "POST"; |
| request_.url = GURL("http://www.google.com/"); |
| request_.upload_data_stream = &upload_data_stream; |
| ASSERT_EQ(OK, request_.upload_data_stream->Init( |
| TestCompletionCallback().callback())); |
| |
| ASSERT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, |
| net_log_, callback_.callback())); |
| ASSERT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| |
| upload_data_stream.AppendData(kUploadData, chunk_size, true); |
| |
| // Ack both packets in the request. |
| ProcessPacket(ConstructAckPacket(1, 0, 0)); |
| |
| // Send the response headers (but not the body). |
| SetResponse("200 OK", std::string()); |
| ProcessPacket(ConstructResponseHeadersPacket(2, !kFin)); |
| |
| // Since the headers have already arrived, this should return immediately. |
| ASSERT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback())); |
| ASSERT_TRUE(response_.headers.get()); |
| EXPECT_EQ(200, response_.headers->response_code()); |
| EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain")); |
| |
| // Send the response body. |
| const char kResponseBody[] = "Hello world!"; |
| ProcessPacket(ConstructDataPacket(3, false, kFin, response_data_.length(), |
| kResponseBody)); |
| |
| // Since the body has already arrived, this should return immediately. |
| ASSERT_EQ(static_cast<int>(strlen(kResponseBody)), |
| stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(), |
| callback_.callback())); |
| |
| EXPECT_TRUE(stream_->IsResponseBodyComplete()); |
| EXPECT_TRUE(AtEof()); |
| } |
| |
| TEST_P(QuicHttpStreamTest, SendChunkedPostRequestWithFinalEmptyDataPacket) { |
| SetRequest("POST", "/", DEFAULT_PRIORITY); |
| size_t chunk_size = strlen(kUploadData); |
| AddWrite(ConstructRequestHeadersPacket(1, !kFin)); |
| AddWrite(ConstructDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData)); |
| AddWrite(ConstructDataPacket(3, kIncludeVersion, kFin, chunk_size, "")); |
| AddWrite(ConstructAckPacket(4, 3, 1)); |
| Initialize(); |
| |
| ChunkedUploadDataStream upload_data_stream(0); |
| upload_data_stream.AppendData(kUploadData, chunk_size, false); |
| |
| request_.method = "POST"; |
| request_.url = GURL("http://www.google.com/"); |
| request_.upload_data_stream = &upload_data_stream; |
| ASSERT_EQ(OK, request_.upload_data_stream->Init( |
| TestCompletionCallback().callback())); |
| |
| ASSERT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, |
| net_log_, callback_.callback())); |
| ASSERT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| |
| upload_data_stream.AppendData(nullptr, 0, true); |
| |
| ProcessPacket(ConstructAckPacket(1, 0, 0)); |
| |
| // Send the response headers (but not the body). |
| SetResponse("200 OK", std::string()); |
| ProcessPacket(ConstructResponseHeadersPacket(2, !kFin)); |
| |
| // Since the headers have already arrived, this should return immediately. |
| ASSERT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback())); |
| ASSERT_TRUE(response_.headers.get()); |
| EXPECT_EQ(200, response_.headers->response_code()); |
| EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain")); |
| |
| // Send the response body. |
| const char kResponseBody[] = "Hello world!"; |
| ProcessPacket(ConstructDataPacket(3, false, kFin, response_data_.length(), |
| kResponseBody)); |
| |
| // Since the body has already arrived, this should return immediately. |
| ASSERT_EQ(static_cast<int>(strlen(kResponseBody)), |
| stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(), |
| callback_.callback())); |
| |
| EXPECT_TRUE(stream_->IsResponseBodyComplete()); |
| EXPECT_TRUE(AtEof()); |
| } |
| |
| TEST_P(QuicHttpStreamTest, SendChunkedPostRequestWithOneEmptyDataPacket) { |
| SetRequest("POST", "/", DEFAULT_PRIORITY); |
| AddWrite(ConstructRequestHeadersPacket(1, !kFin)); |
| AddWrite(ConstructDataPacket(2, kIncludeVersion, kFin, 0, "")); |
| AddWrite(ConstructAckPacket(3, 3, 1)); |
| Initialize(); |
| |
| ChunkedUploadDataStream upload_data_stream(0); |
| |
| request_.method = "POST"; |
| request_.url = GURL("http://www.google.com/"); |
| request_.upload_data_stream = &upload_data_stream; |
| ASSERT_EQ(OK, request_.upload_data_stream->Init( |
| TestCompletionCallback().callback())); |
| |
| ASSERT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, |
| net_log_, callback_.callback())); |
| ASSERT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| |
| upload_data_stream.AppendData(nullptr, 0, true); |
| |
| ProcessPacket(ConstructAckPacket(1, 0, 0)); |
| |
| // Send the response headers (but not the body). |
| SetResponse("200 OK", std::string()); |
| ProcessPacket(ConstructResponseHeadersPacket(2, !kFin)); |
| |
| // Since the headers have already arrived, this should return immediately. |
| ASSERT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback())); |
| ASSERT_TRUE(response_.headers.get()); |
| EXPECT_EQ(200, response_.headers->response_code()); |
| EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain")); |
| |
| // Send the response body. |
| const char kResponseBody[] = "Hello world!"; |
| ProcessPacket(ConstructDataPacket(3, false, kFin, response_data_.length(), |
| kResponseBody)); |
| |
| // Since the body has already arrived, this should return immediately. |
| ASSERT_EQ(static_cast<int>(strlen(kResponseBody)), |
| stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(), |
| callback_.callback())); |
| |
| EXPECT_TRUE(stream_->IsResponseBodyComplete()); |
| EXPECT_TRUE(AtEof()); |
| } |
| |
| TEST_P(QuicHttpStreamTest, DestroyedEarly) { |
| SetRequest("GET", "/", DEFAULT_PRIORITY); |
| AddWrite(ConstructRequestHeadersPacket(1, kFin)); |
| AddWrite(ConstructAckAndRstStreamPacket(2)); |
| use_closing_stream_ = true; |
| Initialize(); |
| |
| request_.method = "GET"; |
| request_.url = GURL("http://www.google.com/"); |
| |
| EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, |
| net_log_, callback_.callback())); |
| EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| |
| // Ack the request. |
| ProcessPacket(ConstructAckPacket(1, 0, 0)); |
| EXPECT_EQ(ERR_IO_PENDING, |
| stream_->ReadResponseHeaders(callback_.callback())); |
| |
| // Send the response with a body. |
| SetResponse("404 OK", "hello world!"); |
| // In the course of processing this packet, the QuicHttpStream close itself. |
| ProcessPacket(ConstructResponseHeadersPacket(2, kFin)); |
| |
| EXPECT_TRUE(AtEof()); |
| } |
| |
| TEST_P(QuicHttpStreamTest, Priority) { |
| SetRequest("GET", "/", MEDIUM); |
| AddWrite(ConstructRequestHeadersPacket(1, kFin)); |
| AddWrite(ConstructAckAndRstStreamPacket(2)); |
| use_closing_stream_ = true; |
| Initialize(); |
| |
| request_.method = "GET"; |
| request_.url = GURL("http://www.google.com/"); |
| |
| EXPECT_EQ(OK, stream_->InitializeStream(&request_, MEDIUM, |
| net_log_, callback_.callback())); |
| |
| // Check that priority is highest. |
| QuicReliableClientStream* reliable_stream = |
| QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get()); |
| DCHECK(reliable_stream); |
| DCHECK_EQ(QuicWriteBlockedList::kHighestPriority, |
| reliable_stream->EffectivePriority()); |
| |
| EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, |
| callback_.callback())); |
| |
| // Check that priority has now dropped back to MEDIUM. |
| DCHECK_EQ(MEDIUM, ConvertQuicPriorityToRequestPriority( |
| reliable_stream->EffectivePriority())); |
| |
| // Ack the request. |
| ProcessPacket(ConstructAckPacket(1, 0, 0)); |
| EXPECT_EQ(ERR_IO_PENDING, |
| stream_->ReadResponseHeaders(callback_.callback())); |
| |
| // Send the response with a body. |
| SetResponse("404 OK", "hello world!"); |
| // In the course of processing this packet, the QuicHttpStream close itself. |
| ProcessPacket(ConstructResponseHeadersPacket(2, kFin)); |
| |
| EXPECT_TRUE(AtEof()); |
| } |
| |
| // Regression test for http://crbug.com/294870 |
| TEST_P(QuicHttpStreamTest, CheckPriorityWithNoDelegate) { |
| SetRequest("GET", "/", MEDIUM); |
| use_closing_stream_ = true; |
| |
| AddWrite(ConstructRstStreamPacket(1)); |
| |
| Initialize(); |
| |
| request_.method = "GET"; |
| request_.url = GURL("http://www.google.com/"); |
| |
| EXPECT_EQ(OK, stream_->InitializeStream(&request_, MEDIUM, |
| net_log_, callback_.callback())); |
| |
| // Check that priority is highest. |
| QuicReliableClientStream* reliable_stream = |
| QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get()); |
| DCHECK(reliable_stream); |
| QuicReliableClientStream::Delegate* delegate = reliable_stream->GetDelegate(); |
| DCHECK(delegate); |
| DCHECK_EQ(QuicWriteBlockedList::kHighestPriority, |
| reliable_stream->EffectivePriority()); |
| |
| // Set Delegate to nullptr and make sure EffectivePriority returns highest |
| // priority. |
| reliable_stream->SetDelegate(nullptr); |
| DCHECK_EQ(QuicWriteBlockedList::kHighestPriority, |
| reliable_stream->EffectivePriority()); |
| reliable_stream->SetDelegate(delegate); |
| } |
| |
| } // namespace test |
| } // namespace net |