blob: 17a9f7960843122c69862de0016df3648b89ded8 [file] [log] [blame]
// Copyright 2016 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/chromium/bidirectional_stream_quic_impl.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/timer/timer.h"
#include "net/http/bidirectional_stream_request_info.h"
#include "net/quic/core/quic_connection.h"
#include "net/quic/platform/api/quic_string_piece.h"
#include "net/socket/next_proto.h"
#include "net/spdy/chromium/spdy_http_utils.h"
#include "net/spdy/core/spdy_header_block.h"
namespace net {
BidirectionalStreamQuicImpl::BidirectionalStreamQuicImpl(
std::unique_ptr<QuicChromiumClientSession::Handle> session)
: session_(std::move(session)),
stream_(nullptr),
request_info_(nullptr),
delegate_(nullptr),
response_status_(OK),
negotiated_protocol_(kProtoUnknown),
read_buffer_len_(0),
headers_bytes_received_(0),
headers_bytes_sent_(0),
closed_stream_received_bytes_(0),
closed_stream_sent_bytes_(0),
closed_is_first_stream_(false),
has_sent_headers_(false),
send_request_headers_automatically_(true),
weak_factory_(this) {}
BidirectionalStreamQuicImpl::~BidirectionalStreamQuicImpl() {
if (stream_) {
delegate_ = nullptr;
stream_->Reset(QUIC_STREAM_CANCELLED);
}
}
void BidirectionalStreamQuicImpl::Start(
const BidirectionalStreamRequestInfo* request_info,
const NetLogWithSource& net_log,
bool send_request_headers_automatically,
BidirectionalStreamImpl::Delegate* delegate,
std::unique_ptr<base::Timer> /* timer */) {
DCHECK(!stream_);
CHECK(delegate);
send_request_headers_automatically_ = send_request_headers_automatically;
if (!session_->IsConnected()) {
NotifyError(session_->IsCryptoHandshakeConfirmed()
? ERR_QUIC_PROTOCOL_ERROR
: ERR_QUIC_HANDSHAKE_FAILED);
return;
}
delegate_ = delegate;
request_info_ = request_info;
int rv = session_->RequestStream(
request_info_->method == "POST",
base::Bind(&BidirectionalStreamQuicImpl::OnStreamReady,
weak_factory_.GetWeakPtr()));
if (rv == ERR_IO_PENDING)
return;
if (rv == OK) {
OnStreamReady(rv);
} else if (!session_->IsCryptoHandshakeConfirmed()) {
NotifyError(ERR_QUIC_HANDSHAKE_FAILED);
}
}
void BidirectionalStreamQuicImpl::SendRequestHeaders() {
DCHECK(!has_sent_headers_);
if (!stream_) {
LOG(ERROR)
<< "Trying to send request headers after stream has been destroyed.";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&BidirectionalStreamQuicImpl::NotifyError,
weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
return;
}
SpdyHeaderBlock headers;
HttpRequestInfo http_request_info;
http_request_info.url = request_info_->url;
http_request_info.method = request_info_->method;
http_request_info.extra_headers = request_info_->extra_headers;
CreateSpdyHeadersFromHttpRequest(
http_request_info, http_request_info.extra_headers, true, &headers);
size_t headers_bytes_sent = stream_->WriteHeaders(
std::move(headers), request_info_->end_stream_on_headers, nullptr);
headers_bytes_sent_ += headers_bytes_sent;
has_sent_headers_ = true;
}
int BidirectionalStreamQuicImpl::ReadData(IOBuffer* buffer, int buffer_len) {
DCHECK(buffer);
DCHECK(buffer_len);
if (!stream_) {
// If the stream is already closed, there is no body to read.
return response_status_;
}
int rv = stream_->Read(buffer, buffer_len);
if (rv != ERR_IO_PENDING) {
if (stream_->IsDoneReading()) {
// If the write side is closed, OnFinRead() will call
// BidirectionalStreamQuicImpl::OnClose().
stream_->OnFinRead();
}
return rv;
}
// Read will complete asynchronously and Delegate::OnReadCompleted will be
// called upon completion.
read_buffer_ = buffer;
read_buffer_len_ = buffer_len;
return ERR_IO_PENDING;
}
void BidirectionalStreamQuicImpl::SendData(const scoped_refptr<IOBuffer>& data,
int length,
bool end_stream) {
DCHECK(length > 0 || (length == 0 && end_stream));
if (!stream_) {
LOG(ERROR) << "Trying to send data after stream has been destroyed.";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&BidirectionalStreamQuicImpl::NotifyError,
weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
return;
}
std::unique_ptr<QuicConnection::ScopedPacketBundler> bundler;
if (!has_sent_headers_) {
DCHECK(!send_request_headers_automatically_);
// Creates a bundler only if there are headers to be sent along with the
// single data buffer.
bundler =
session_->CreatePacketBundler(QuicConnection::SEND_ACK_IF_PENDING);
SendRequestHeaders();
}
QuicStringPiece string_data(data->data(), length);
int rv = stream_->WriteStreamData(
string_data, end_stream,
base::Bind(&BidirectionalStreamQuicImpl::OnSendDataComplete,
weak_factory_.GetWeakPtr()));
DCHECK(rv == OK || rv == ERR_IO_PENDING);
if (rv == OK) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&BidirectionalStreamQuicImpl::OnSendDataComplete,
weak_factory_.GetWeakPtr(), OK));
}
}
void BidirectionalStreamQuicImpl::SendvData(
const std::vector<scoped_refptr<IOBuffer>>& buffers,
const std::vector<int>& lengths,
bool end_stream) {
DCHECK_EQ(buffers.size(), lengths.size());
if (!stream_) {
LOG(ERROR) << "Trying to send data after stream has been destroyed.";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&BidirectionalStreamQuicImpl::NotifyError,
weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
return;
}
std::unique_ptr<QuicConnection::ScopedPacketBundler> bundler(
session_->CreatePacketBundler(QuicConnection::SEND_ACK_IF_PENDING));
if (!has_sent_headers_) {
DCHECK(!send_request_headers_automatically_);
SendRequestHeaders();
}
int rv = stream_->WritevStreamData(
buffers, lengths, end_stream,
base::Bind(&BidirectionalStreamQuicImpl::OnSendDataComplete,
weak_factory_.GetWeakPtr()));
DCHECK(rv == OK || rv == ERR_IO_PENDING);
if (rv == OK) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&BidirectionalStreamQuicImpl::OnSendDataComplete,
weak_factory_.GetWeakPtr(), OK));
}
}
NextProto BidirectionalStreamQuicImpl::GetProtocol() const {
return negotiated_protocol_;
}
int64_t BidirectionalStreamQuicImpl::GetTotalReceivedBytes() const {
if (stream_)
return headers_bytes_received_ + stream_->stream_bytes_read();
return headers_bytes_received_ + closed_stream_received_bytes_;
}
int64_t BidirectionalStreamQuicImpl::GetTotalSentBytes() const {
if (stream_)
return headers_bytes_sent_ + stream_->stream_bytes_written();
return headers_bytes_sent_ + closed_stream_sent_bytes_;
}
bool BidirectionalStreamQuicImpl::GetLoadTimingInfo(
LoadTimingInfo* load_timing_info) const {
bool is_first_stream = closed_is_first_stream_;
if (stream_)
is_first_stream = stream_->IsFirstStream();
if (is_first_stream) {
load_timing_info->socket_reused = false;
load_timing_info->connect_timing = connect_timing_;
} else {
load_timing_info->socket_reused = true;
}
return true;
}
void BidirectionalStreamQuicImpl::OnTrailingHeadersAvailable(
const SpdyHeaderBlock& headers,
size_t frame_len) {
headers_bytes_received_ += frame_len;
if (delegate_)
delegate_->OnTrailersReceived(headers);
// |this| can be destroyed after this point.
}
void BidirectionalStreamQuicImpl::OnDataAvailable() {
// Return early if ReadData has not been called.
if (!read_buffer_)
return;
int rv = ReadData(read_buffer_.get(), read_buffer_len_);
if (rv == ERR_IO_PENDING) {
// Spurrious notification. Wait for the next one.
return;
}
read_buffer_ = nullptr;
read_buffer_len_ = 0;
if (delegate_)
delegate_->OnDataRead(rv);
}
void BidirectionalStreamQuicImpl::OnClose() {
DCHECK(stream_);
if (stream_->connection_error() != QUIC_NO_ERROR ||
stream_->stream_error() != QUIC_STREAM_NO_ERROR) {
NotifyError(session_->IsCryptoHandshakeConfirmed()
? ERR_QUIC_PROTOCOL_ERROR
: ERR_QUIC_HANDSHAKE_FAILED);
return;
}
if (!stream_->fin_sent() || !stream_->fin_received()) {
// The connection must have been closed by the peer with QUIC_NO_ERROR,
// which is improper.
NotifyError(ERR_UNEXPECTED);
return;
}
// The connection was closed normally so there is no need to notify
// the delegate.
ResetStream();
}
void BidirectionalStreamQuicImpl::OnError(int error) {
NotifyError(error);
}
void BidirectionalStreamQuicImpl::OnStreamReady(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
DCHECK(rv == OK || !stream_);
if (rv == OK) {
stream_ = session_->ReleaseStream(this);
NotifyStreamReady();
rv = stream_->ReadInitialHeaders(
&initial_headers_,
base::Bind(&BidirectionalStreamQuicImpl::OnReadInitialHeadersComplete,
weak_factory_.GetWeakPtr()));
if (rv == ERR_IO_PENDING)
return;
OnReadInitialHeadersComplete(rv);
} else {
NotifyError(rv);
}
}
void BidirectionalStreamQuicImpl::OnSendDataComplete(int rv) {
DCHECK(rv == OK || !stream_);
if (rv == OK) {
if (delegate_)
delegate_->OnDataSent();
} else {
NotifyError(rv);
}
}
void BidirectionalStreamQuicImpl::OnReadInitialHeadersComplete(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv < 0) {
NotifyError(rv);
return;
}
headers_bytes_received_ += rv;
negotiated_protocol_ = kProtoQUIC;
connect_timing_ = session_->GetConnectTiming();
if (delegate_)
delegate_->OnHeadersReceived(initial_headers_);
}
void BidirectionalStreamQuicImpl::NotifyError(int error) {
DCHECK_NE(OK, error);
DCHECK_NE(ERR_IO_PENDING, error);
ResetStream();
if (delegate_) {
response_status_ = error;
BidirectionalStreamImpl::Delegate* delegate = delegate_;
delegate_ = nullptr;
// Cancel any pending callback.
weak_factory_.InvalidateWeakPtrs();
delegate->OnFailed(error);
// |this| might be destroyed at this point.
}
}
void BidirectionalStreamQuicImpl::NotifyStreamReady() {
if (send_request_headers_automatically_) {
SendRequestHeaders();
}
if (delegate_)
delegate_->OnStreamReady(has_sent_headers_);
}
void BidirectionalStreamQuicImpl::ResetStream() {
if (!stream_)
return;
closed_stream_received_bytes_ = stream_->stream_bytes_read();
closed_stream_sent_bytes_ = stream_->stream_bytes_written();
closed_is_first_stream_ = stream_->IsFirstStream();
stream_->ClearDelegate();
stream_ = nullptr;
}
} // namespace net