blob: dff71e43e247c702ad2b2c5548bbeefcab88dccd [file] [log] [blame]
// Copyright 2014 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_client_session_base.h"
#include "net/quic/quic_client_promised_info.h"
#include "net/quic/quic_flags.h"
#include "net/quic/spdy_utils.h"
using base::StringPiece;
using std::string;
namespace net {
QuicClientSessionBase::QuicClientSessionBase(
QuicConnection* connection,
QuicClientPushPromiseIndex* push_promise_index,
const QuicConfig& config)
: QuicSpdySession(connection, config),
push_promise_index_(push_promise_index),
largest_promised_stream_id_(kInvalidStreamId) {}
QuicClientSessionBase::~QuicClientSessionBase() {
// all promised streams for this session
for (auto& it : promised_by_id_) {
DVLOG(1) << "erase stream " << it.first << " url " << it.second->url();
push_promise_index_->promised_by_url()->erase(it.second->url());
}
}
void QuicClientSessionBase::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
QuicSession::OnCryptoHandshakeEvent(event);
}
void QuicClientSessionBase::OnPromiseHeaders(QuicStreamId stream_id,
StringPiece headers_data) {
QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
if (!stream) {
// It's quite possible to receive headers after a stream has been reset.
return;
}
stream->OnPromiseHeaders(headers_data);
}
void QuicClientSessionBase::OnInitialHeadersComplete(
QuicStreamId stream_id,
const SpdyHeaderBlock& response_headers) {
// Note that the strong ordering of the headers stream means that
// QuicSpdyClientStream::OnPromiseHeadersComplete must have already
// been called (on the associated stream) if this is a promised
// stream. However, this stream may not have existed at this time,
// hence the need to query the session.
QuicClientPromisedInfo* promised = GetPromisedById(stream_id);
if (!promised)
return;
promised->OnResponseHeaders(response_headers);
}
void QuicClientSessionBase::OnPromiseHeadersComplete(
QuicStreamId stream_id,
QuicStreamId promised_stream_id,
size_t frame_len) {
if (promised_stream_id != kInvalidStreamId &&
promised_stream_id <= largest_promised_stream_id_) {
connection()->CloseConnection(
QUIC_INVALID_STREAM_ID,
"Received push stream id lesser or equal to the"
" last accepted before",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return;
}
largest_promised_stream_id_ = promised_stream_id;
QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
if (!stream) {
// It's quite possible to receive headers after a stream has been reset.
return;
}
stream->OnPromiseHeadersComplete(promised_stream_id, frame_len);
}
void QuicClientSessionBase::OnPromiseHeaderList(
QuicStreamId stream_id,
QuicStreamId promised_stream_id,
size_t frame_len,
const QuicHeaderList& header_list) {
if (promised_stream_id != kInvalidStreamId &&
promised_stream_id <= largest_promised_stream_id_) {
connection()->CloseConnection(
QUIC_INVALID_STREAM_ID,
"Received push stream id lesser or equal to the"
" last accepted before",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return;
}
largest_promised_stream_id_ = promised_stream_id;
QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
if (!stream) {
// It's quite possible to receive headers after a stream has been reset.
return;
}
stream->OnPromiseHeaderList(promised_stream_id, frame_len, header_list);
}
void QuicClientSessionBase::HandlePromised(QuicStreamId /* associated_id */,
QuicStreamId id,
const SpdyHeaderBlock& headers) {
// Due to pathalogical packet re-ordering, it is possible that
// frames for the promised stream have already arrived, and the
// promised stream could be active or closed.
if (IsClosedStream(id)) {
// There was a RST on the data stream already, perhaps
// QUIC_REFUSED_STREAM?
DVLOG(1) << "Promise ignored for stream " << id
<< " that is already closed";
return;
}
if (push_promise_index_->promised_by_url()->size() >= get_max_promises()) {
DVLOG(1) << "Too many promises, rejecting promise for stream " << id;
ResetPromised(id, QUIC_REFUSED_STREAM);
return;
}
const string url = SpdyUtils::GetUrlFromHeaderBlock(headers);
QuicClientPromisedInfo* old_promised = GetPromisedByUrl(url);
if (old_promised) {
DVLOG(1) << "Promise for stream " << id << " is duplicate URL " << url
<< " of previous promise for stream " << old_promised->id();
ResetPromised(id, QUIC_DUPLICATE_PROMISE_URL);
return;
}
if (GetPromisedById(id)) {
// OnPromiseHeadersComplete() would have closed the connection if
// promised id is a duplicate.
QUIC_BUG << "Duplicate promise for id " << id;
return;
}
QuicClientPromisedInfo* promised = new QuicClientPromisedInfo(this, id, url);
std::unique_ptr<QuicClientPromisedInfo> promised_owner(promised);
promised->Init();
DVLOG(1) << "stream " << id << " emplace url " << url;
(*push_promise_index_->promised_by_url())[url] = promised;
promised_by_id_[id] = std::move(promised_owner);
promised->OnPromiseHeaders(headers);
}
QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedByUrl(
const string& url) {
QuicPromisedByUrlMap::iterator it =
push_promise_index_->promised_by_url()->find(url);
if (it != push_promise_index_->promised_by_url()->end()) {
return it->second;
}
return nullptr;
}
QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedById(
const QuicStreamId id) {
QuicPromisedByIdMap::iterator it = promised_by_id_.find(id);
if (it != promised_by_id_.end()) {
return it->second.get();
}
return nullptr;
}
QuicSpdyStream* QuicClientSessionBase::GetPromisedStream(
const QuicStreamId id) {
if (IsClosedStream(id)) {
return nullptr;
}
StreamMap::iterator it = dynamic_streams().find(id);
if (it != dynamic_streams().end()) {
return static_cast<QuicSpdyStream*>(it->second);
}
QUIC_BUG << "Open promised stream " << id << " is missing!";
return nullptr;
}
void QuicClientSessionBase::DeletePromised(QuicClientPromisedInfo* promised) {
push_promise_index_->promised_by_url()->erase(promised->url());
// Since promised_by_id_ contains the unique_ptr, this will destroy
// promised.
promised_by_id_.erase(promised->id());
}
void QuicClientSessionBase::ResetPromised(QuicStreamId id,
QuicRstStreamErrorCode error_code) {
SendRstStream(id, error_code, 0);
if (!IsOpenStream(id)) {
MaybeIncreaseLargestPeerStreamId(id);
InsertLocallyClosedStreamsHighestOffset(id, 0);
}
}
} // namespace net