| Index: net/quic/chromium/quic_chromium_client_session.cc
|
| diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
|
| index b5ea8776b47d955d8cdfd43bc797ad5be3775fbe..c2bdf2b1df8f3b622d829ceb9a7f644fbf9fe9f5 100644
|
| --- a/net/quic/chromium/quic_chromium_client_session.cc
|
| +++ b/net/quic/chromium/quic_chromium_client_session.cc
|
| @@ -241,8 +241,7 @@ QuicChromiumClientSession::QuicChromiumClientSession(
|
| token_binding_signatures_(kTokenBindingSignatureMapSize),
|
| streams_pushed_count_(0),
|
| streams_pushed_and_claimed_count_(0),
|
| - error_code_from_rewrite_(OK),
|
| - use_error_code_from_rewrite_(false),
|
| + migration_pending_(false),
|
| weak_factory_(this) {
|
| sockets_.push_back(std::move(socket));
|
| packet_readers_.push_back(base::MakeUnique<QuicChromiumPacketReader>(
|
| @@ -969,12 +968,86 @@ void QuicChromiumClientSession::OnSuccessfulVersionNegotiation(
|
| int QuicChromiumClientSession::HandleWriteError(
|
| int error_code,
|
| scoped_refptr<StringIOBuffer> packet) {
|
| + if (stream_factory_ == nullptr ||
|
| + !stream_factory_->migrate_sessions_on_network_change()) {
|
| + return error_code;
|
| + }
|
| DCHECK(packet != nullptr);
|
| - use_error_code_from_rewrite_ = false;
|
| - if (stream_factory_) {
|
| - stream_factory_->MaybeMigrateSingleSession(this, WRITE_ERROR, packet);
|
| + DCHECK_NE(ERR_IO_PENDING, error_code);
|
| + DCHECK_GT(0, error_code);
|
| + DCHECK(!migration_pending_);
|
| + DCHECK(packet_ == nullptr);
|
| +
|
| + // Post a task to migrate the session onto a new network.
|
| + task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&QuicChromiumClientSession::MigrateSessionOnWriteError,
|
| + weak_factory_.GetWeakPtr()));
|
| +
|
| + // Store packet in the session since the actual migration and packet rewrite
|
| + // can happen via this posted task or via an async network notification.
|
| + packet_ = packet;
|
| + migration_pending_ = true;
|
| +
|
| + // Cause the packet writer to return ERR_IO_PENDING and block so
|
| + // that the actual migration happens from the message loop instead
|
| + // of under the call stack of QuicConnection::WritePacket.
|
| + return ERR_IO_PENDING;
|
| +}
|
| +
|
| +void QuicChromiumClientSession::MigrateSessionOnWriteError() {
|
| + // If migration_pending_ is false, an earlier task completed migration.
|
| + if (!migration_pending_)
|
| + return;
|
| +
|
| + if (stream_factory_ != nullptr &&
|
| + stream_factory_->MaybeMigrateSingleSession(this, WRITE_ERROR) ==
|
| + MigrationResult::SUCCESS) {
|
| + return;
|
| + }
|
| +
|
| + // Close the connection if migration failed. Do not cause a
|
| + // connection close packet to be sent since socket may be borked.
|
| + connection()->CloseConnection(QUIC_PACKET_WRITE_ERROR,
|
| + "Write and subsequent migration failed",
|
| + ConnectionCloseBehavior::SILENT_CLOSE);
|
| +}
|
| +
|
| +void QuicChromiumClientSession::WriteToNewSocket() {
|
| + // Prevent any pending migration from executing.
|
| + migration_pending_ = false;
|
| +
|
| + static_cast<QuicChromiumPacketWriter*>(connection()->writer())
|
| + ->set_write_blocked(false);
|
| + DCHECK(!connection()->writer()->IsWriteBlocked());
|
| +
|
| + if (packet_ == nullptr) {
|
| + // Unblock the connection before sending a PING packet, since it
|
| + // may have been blocked before the migration started.
|
| + connection()->OnCanWrite();
|
| + connection()->SendPing();
|
| + return;
|
| }
|
| - return use_error_code_from_rewrite_ ? error_code_from_rewrite_ : error_code;
|
| +
|
| + // Set packet_ to null first before calling WritePacketToSocket since
|
| + // that method may set packet_ if there is a write error.
|
| + scoped_refptr<StringIOBuffer> packet = packet_;
|
| + packet_ = nullptr;
|
| +
|
| + // The connection is waiting for the original write to complete
|
| + // asynchronously. The new writer will notify the connection if the
|
| + // write below completes asynchronously, but a synchronous competion
|
| + // must be propagated back to the connection here.
|
| + WriteResult result =
|
| + static_cast<QuicChromiumPacketWriter*>(connection()->writer())
|
| + ->WritePacketToSocket(packet);
|
| +
|
| + if (result.error_code == ERR_IO_PENDING)
|
| + return;
|
| + // All write errors should be mapped into ERR_IO_PENDING by
|
| + // HandleWriteError.
|
| + DCHECK_LT(0, result.error_code);
|
| + connection()->OnCanWrite();
|
| }
|
|
|
| void QuicChromiumClientSession::OnWriteError(int error_code) {
|
| @@ -989,7 +1062,7 @@ void QuicChromiumClientSession::OnWriteUnblocked() {
|
|
|
| void QuicChromiumClientSession::OnPathDegrading() {
|
| if (stream_factory_) {
|
| - stream_factory_->MaybeMigrateSingleSession(this, EARLY_MIGRATION, nullptr);
|
| + stream_factory_->MaybeMigrateSingleSession(this, EARLY_MIGRATION);
|
| }
|
| }
|
|
|
| @@ -1196,25 +1269,28 @@ void QuicChromiumClientSession::NotifyFactoryOfSessionClosed() {
|
| bool QuicChromiumClientSession::MigrateToSocket(
|
| std::unique_ptr<DatagramClientSocket> socket,
|
| std::unique_ptr<QuicChromiumPacketReader> reader,
|
| - std::unique_ptr<QuicChromiumPacketWriter> writer,
|
| - scoped_refptr<StringIOBuffer> packet) {
|
| + std::unique_ptr<QuicChromiumPacketWriter> writer) {
|
| DCHECK_EQ(sockets_.size(), packet_readers_.size());
|
| - if (sockets_.size() >= kMaxReadersPerQuicSession) {
|
| + if (sockets_.size() >= kMaxReadersPerQuicSession)
|
| return false;
|
| - }
|
| +
|
| // TODO(jri): Make SetQuicPacketWriter take a scoped_ptr.
|
| packet_readers_.push_back(std::move(reader));
|
| sockets_.push_back(std::move(socket));
|
| StartReading();
|
| - QuicChromiumPacketWriter* raw_writer = writer.get();
|
| + // Block the writer to prevent is being used until WriteToNewSocket
|
| + // completes.
|
| + writer->set_write_blocked(true);
|
| connection()->SetQuicPacketWriter(writer.release(), /*owns_writer=*/true);
|
| - if (packet == nullptr) {
|
| - connection()->SendPing();
|
| - return true;
|
| - }
|
| - // Packet rewrite after migration on socket write error.
|
| - error_code_from_rewrite_ = raw_writer->WritePacketToSocket(packet.get());
|
| - use_error_code_from_rewrite_ = true;
|
| +
|
| + // Post task to write the pending packet or a PING packet to the new
|
| + // socket. This avoids reentrancy issues if there is a write error
|
| + // on the write to the new socket.
|
| + task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&QuicChromiumClientSession::WriteToNewSocket,
|
| + weak_factory_.GetWeakPtr()));
|
| + // Migration completed.
|
| + migration_pending_ = false;
|
| return true;
|
| }
|
|
|
|
|