365 lines
14 KiB
C
365 lines
14 KiB
C
|
|
// Copyright 2012 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
//
|
||
|
|
// NOTE: This code is not shared between Google and Chrome.
|
||
|
|
|
||
|
|
#ifndef NET_QUIC_QUIC_CHROMIUM_CLIENT_STREAM_H_
|
||
|
|
#define NET_QUIC_QUIC_CHROMIUM_CLIENT_STREAM_H_
|
||
|
|
|
||
|
|
#include <stddef.h>
|
||
|
|
|
||
|
|
#include <memory>
|
||
|
|
#include <vector>
|
||
|
|
|
||
|
|
#include "base/containers/circular_deque.h"
|
||
|
|
#include "base/functional/callback_forward.h"
|
||
|
|
#include "base/memory/raw_ptr.h"
|
||
|
|
#include "base/time/time.h"
|
||
|
|
#include "net/base/completion_once_callback.h"
|
||
|
|
#include "net/base/idempotency.h"
|
||
|
|
#include "net/base/ip_endpoint.h"
|
||
|
|
#include "net/base/net_export.h"
|
||
|
|
#include "net/base/upload_data_stream.h"
|
||
|
|
#include "net/http/http_request_info.h"
|
||
|
|
#include "net/http/http_response_info.h"
|
||
|
|
#include "net/http/http_stream.h"
|
||
|
|
#include "net/log/net_log_with_source.h"
|
||
|
|
#include "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_stream.h"
|
||
|
|
#include "net/third_party/quiche/src/quiche/spdy/core/http2_header_block.h"
|
||
|
|
#include "net/traffic_annotation/network_traffic_annotation.h"
|
||
|
|
|
||
|
|
namespace quic {
|
||
|
|
class QuicSpdyClientSessionBase;
|
||
|
|
} // namespace quic
|
||
|
|
namespace net {
|
||
|
|
|
||
|
|
// A client-initiated ReliableQuicStream. Instances of this class
|
||
|
|
// are owned by the QuicClientSession which created them.
|
||
|
|
class NET_EXPORT_PRIVATE QuicChromiumClientStream
|
||
|
|
: public quic::QuicSpdyStream {
|
||
|
|
public:
|
||
|
|
// Wrapper for interacting with the session in a restricted fashion.
|
||
|
|
class NET_EXPORT_PRIVATE Handle {
|
||
|
|
public:
|
||
|
|
Handle(const Handle&) = delete;
|
||
|
|
Handle& operator=(const Handle&) = delete;
|
||
|
|
|
||
|
|
~Handle();
|
||
|
|
|
||
|
|
// Returns true if the stream is still connected.
|
||
|
|
bool IsOpen() { return stream_ != nullptr; }
|
||
|
|
|
||
|
|
// Reads initial or 103 Early Hints headers into |header_block| and returns
|
||
|
|
// the length of the HEADERS frame which contained them. If headers are not
|
||
|
|
// available, returns ERR_IO_PENDING and will invoke |callback|
|
||
|
|
// asynchronously when the headers arrive.
|
||
|
|
// TODO(rch): Invoke |callback| when there is a stream or connection error
|
||
|
|
// instead of calling OnClose() or OnError().
|
||
|
|
int ReadInitialHeaders(spdy::Http2HeaderBlock* header_block,
|
||
|
|
CompletionOnceCallback callback);
|
||
|
|
|
||
|
|
// Reads at most |buffer_len| bytes of body into |buffer| and returns the
|
||
|
|
// number of bytes read. If body is not available, returns ERR_IO_PENDING
|
||
|
|
// and will invoke |callback| asynchronously when data arrive.
|
||
|
|
// TODO(rch): Invoke |callback| when there is a stream or connection error
|
||
|
|
// instead of calling OnClose() or OnError().
|
||
|
|
int ReadBody(IOBuffer* buffer,
|
||
|
|
int buffer_len,
|
||
|
|
CompletionOnceCallback callback);
|
||
|
|
|
||
|
|
// Reads trailing headers into |header_block| and returns the length of
|
||
|
|
// the HEADERS frame which contained them. If headers are not available,
|
||
|
|
// returns ERR_IO_PENDING and will invoke |callback| asynchronously when
|
||
|
|
// the headers arrive.
|
||
|
|
// TODO(rch): Invoke |callback| when there is a stream or connection error
|
||
|
|
// instead of calling OnClose() or OnError().
|
||
|
|
int ReadTrailingHeaders(spdy::Http2HeaderBlock* header_block,
|
||
|
|
CompletionOnceCallback callback);
|
||
|
|
|
||
|
|
// Writes |header_block| to the peer. Closes the write side if |fin| is
|
||
|
|
// true. If non-null, |ack_notifier_delegate| will be notified when the
|
||
|
|
// headers are ACK'd by the peer. Returns a net error code if there is
|
||
|
|
// an error writing the headers, or the number of bytes written on
|
||
|
|
// success. Will not return ERR_IO_PENDING.
|
||
|
|
int WriteHeaders(
|
||
|
|
spdy::Http2HeaderBlock header_block,
|
||
|
|
bool fin,
|
||
|
|
quiche::QuicheReferenceCountedPointer<quic::QuicAckListenerInterface>
|
||
|
|
ack_notifier_delegate);
|
||
|
|
|
||
|
|
// Writes |data| to the peer. Closes the write side if |fin| is true.
|
||
|
|
// If the data could not be written immediately, returns ERR_IO_PENDING
|
||
|
|
// and invokes |callback| asynchronously when the write completes.
|
||
|
|
int WriteStreamData(base::StringPiece data,
|
||
|
|
bool fin,
|
||
|
|
CompletionOnceCallback callback);
|
||
|
|
|
||
|
|
// Same as WriteStreamData except it writes data from a vector of IOBuffers,
|
||
|
|
// with the length of each buffer at the corresponding index in |lengths|.
|
||
|
|
int WritevStreamData(const std::vector<scoped_refptr<IOBuffer>>& buffers,
|
||
|
|
const std::vector<int>& lengths,
|
||
|
|
bool fin,
|
||
|
|
CompletionOnceCallback callback);
|
||
|
|
|
||
|
|
// Reads at most |buf_len| bytes into |buf|. Returns the number of bytes
|
||
|
|
// read.
|
||
|
|
int Read(IOBuffer* buf, int buf_len);
|
||
|
|
|
||
|
|
// Called to notify the stream when the final incoming data is read.
|
||
|
|
void OnFinRead();
|
||
|
|
|
||
|
|
// Prevents the connection from migrating to a cellular network while this
|
||
|
|
// stream is open.
|
||
|
|
void DisableConnectionMigrationToCellularNetwork();
|
||
|
|
|
||
|
|
// Sets the precedence of the stream to |priority|.
|
||
|
|
void SetPriority(const quic::QuicStreamPriority& priority);
|
||
|
|
|
||
|
|
// Sends a RST_STREAM frame to the peer and closes the streams.
|
||
|
|
void Reset(quic::QuicRstStreamErrorCode error_code);
|
||
|
|
|
||
|
|
quic::QuicStreamId id() const;
|
||
|
|
quic::QuicErrorCode connection_error() const;
|
||
|
|
quic::QuicRstStreamErrorCode stream_error() const;
|
||
|
|
bool fin_sent() const;
|
||
|
|
bool fin_received() const;
|
||
|
|
uint64_t stream_bytes_read() const;
|
||
|
|
uint64_t stream_bytes_written() const;
|
||
|
|
size_t NumBytesConsumed() const;
|
||
|
|
bool HasBytesToRead() const;
|
||
|
|
bool IsDoneReading() const;
|
||
|
|
bool IsFirstStream() const;
|
||
|
|
|
||
|
|
base::TimeTicks first_early_hints_time() const {
|
||
|
|
return first_early_hints_time_;
|
||
|
|
}
|
||
|
|
|
||
|
|
base::TimeTicks headers_received_start_time() const {
|
||
|
|
return headers_received_start_time_;
|
||
|
|
}
|
||
|
|
|
||
|
|
// TODO(rch): Move these test-only methods to a peer, or else remove.
|
||
|
|
void OnPromiseHeaderList(quic::QuicStreamId promised_id,
|
||
|
|
size_t frame_len,
|
||
|
|
const quic::QuicHeaderList& header_list);
|
||
|
|
bool can_migrate_to_cellular_network();
|
||
|
|
|
||
|
|
const NetLogWithSource& net_log() const;
|
||
|
|
|
||
|
|
// Sets the idempotency of the request.
|
||
|
|
void SetRequestIdempotency(Idempotency idempotency);
|
||
|
|
// Returns the idempotency of the request.
|
||
|
|
Idempotency GetRequestIdempotency() const;
|
||
|
|
|
||
|
|
private:
|
||
|
|
friend class QuicChromiumClientStream;
|
||
|
|
|
||
|
|
// Constucts a new Handle for |stream|.
|
||
|
|
explicit Handle(QuicChromiumClientStream* stream);
|
||
|
|
|
||
|
|
// Methods invoked by the stream.
|
||
|
|
void OnEarlyHintsAvailable();
|
||
|
|
void OnInitialHeadersAvailable();
|
||
|
|
void OnTrailingHeadersAvailable();
|
||
|
|
void OnDataAvailable();
|
||
|
|
void OnCanWrite();
|
||
|
|
void OnClose();
|
||
|
|
void OnError(int error);
|
||
|
|
|
||
|
|
// Invokes async IO callbacks because of |error|.
|
||
|
|
void InvokeCallbacksOnClose(int error);
|
||
|
|
|
||
|
|
// Saves various fields from the stream before the stream goes away.
|
||
|
|
void SaveState();
|
||
|
|
|
||
|
|
void SetCallback(CompletionOnceCallback new_callback,
|
||
|
|
CompletionOnceCallback* callback);
|
||
|
|
|
||
|
|
void ResetAndRun(CompletionOnceCallback callback, int rv);
|
||
|
|
|
||
|
|
int HandleIOComplete(int rv);
|
||
|
|
|
||
|
|
raw_ptr<QuicChromiumClientStream> stream_; // Unowned.
|
||
|
|
|
||
|
|
bool may_invoke_callbacks_ = true; // True when callbacks may be invoked.
|
||
|
|
|
||
|
|
// Callback to be invoked when ReadInitialHeaders completes asynchronously.
|
||
|
|
CompletionOnceCallback read_headers_callback_;
|
||
|
|
// Provided by the owner of this handle when ReadInitialHeaders is called.
|
||
|
|
raw_ptr<spdy::Http2HeaderBlock> read_headers_buffer_ = nullptr;
|
||
|
|
|
||
|
|
// Callback to be invoked when ReadBody completes asynchronously.
|
||
|
|
CompletionOnceCallback read_body_callback_;
|
||
|
|
raw_ptr<IOBuffer> read_body_buffer_;
|
||
|
|
int read_body_buffer_len_ = 0;
|
||
|
|
|
||
|
|
// Callback to be invoked when WriteStreamData or WritevStreamData completes
|
||
|
|
// asynchronously.
|
||
|
|
CompletionOnceCallback write_callback_;
|
||
|
|
|
||
|
|
quic::QuicStreamId id_;
|
||
|
|
quic::QuicErrorCode connection_error_;
|
||
|
|
quic::QuicRstStreamErrorCode stream_error_;
|
||
|
|
bool fin_sent_;
|
||
|
|
bool fin_received_;
|
||
|
|
uint64_t stream_bytes_read_;
|
||
|
|
uint64_t stream_bytes_written_;
|
||
|
|
bool is_done_reading_;
|
||
|
|
bool is_first_stream_;
|
||
|
|
size_t num_bytes_consumed_;
|
||
|
|
Idempotency idempotency_ = DEFAULT_IDEMPOTENCY;
|
||
|
|
|
||
|
|
int net_error_ = ERR_UNEXPECTED;
|
||
|
|
|
||
|
|
NetLogWithSource net_log_;
|
||
|
|
|
||
|
|
// The time at which the first 103 Early Hints response is received.
|
||
|
|
base::TimeTicks first_early_hints_time_;
|
||
|
|
|
||
|
|
base::TimeTicks headers_received_start_time_;
|
||
|
|
|
||
|
|
base::WeakPtrFactory<Handle> weak_factory_{this};
|
||
|
|
};
|
||
|
|
|
||
|
|
QuicChromiumClientStream(
|
||
|
|
quic::QuicStreamId id,
|
||
|
|
quic::QuicSpdyClientSessionBase* session,
|
||
|
|
quic::StreamType type,
|
||
|
|
const NetLogWithSource& net_log,
|
||
|
|
const NetworkTrafficAnnotationTag& traffic_annotation);
|
||
|
|
QuicChromiumClientStream(
|
||
|
|
quic::PendingStream* pending,
|
||
|
|
quic::QuicSpdyClientSessionBase* session,
|
||
|
|
const NetLogWithSource& net_log,
|
||
|
|
const NetworkTrafficAnnotationTag& traffic_annotation);
|
||
|
|
|
||
|
|
QuicChromiumClientStream(const QuicChromiumClientStream&) = delete;
|
||
|
|
QuicChromiumClientStream& operator=(const QuicChromiumClientStream&) = delete;
|
||
|
|
|
||
|
|
~QuicChromiumClientStream() override;
|
||
|
|
|
||
|
|
// quic::QuicSpdyStream
|
||
|
|
void OnInitialHeadersComplete(
|
||
|
|
bool fin,
|
||
|
|
size_t frame_len,
|
||
|
|
const quic::QuicHeaderList& header_list) override;
|
||
|
|
void OnTrailingHeadersComplete(
|
||
|
|
bool fin,
|
||
|
|
size_t frame_len,
|
||
|
|
const quic::QuicHeaderList& header_list) override;
|
||
|
|
void OnPromiseHeaderList(quic::QuicStreamId promised_id,
|
||
|
|
size_t frame_len,
|
||
|
|
const quic::QuicHeaderList& header_list) override;
|
||
|
|
void OnBodyAvailable() override;
|
||
|
|
void OnClose() override;
|
||
|
|
void OnCanWrite() override;
|
||
|
|
size_t WriteHeaders(
|
||
|
|
spdy::Http2HeaderBlock header_block,
|
||
|
|
bool fin,
|
||
|
|
quiche::QuicheReferenceCountedPointer<quic::QuicAckListenerInterface>
|
||
|
|
ack_listener) override;
|
||
|
|
|
||
|
|
// While the server's set_priority shouldn't be called externally, the creator
|
||
|
|
// of client-side streams should be able to set the priority.
|
||
|
|
using quic::QuicSpdyStream::SetPriority;
|
||
|
|
|
||
|
|
// Writes |data| to the peer and closes the write side if |fin| is true.
|
||
|
|
// Returns true if the data have been fully written. If the data was not fully
|
||
|
|
// written, returns false and OnCanWrite() will be invoked later.
|
||
|
|
bool WriteStreamData(absl::string_view data, bool fin);
|
||
|
|
// Same as WriteStreamData except it writes data from a vector of IOBuffers,
|
||
|
|
// with the length of each buffer at the corresponding index in |lengths|.
|
||
|
|
bool WritevStreamData(const std::vector<scoped_refptr<IOBuffer>>& buffers,
|
||
|
|
const std::vector<int>& lengths,
|
||
|
|
bool fin);
|
||
|
|
|
||
|
|
// Creates a new Handle for this stream. Must only be called once.
|
||
|
|
std::unique_ptr<QuicChromiumClientStream::Handle> CreateHandle();
|
||
|
|
|
||
|
|
// Clears |handle_| from this stream.
|
||
|
|
void ClearHandle();
|
||
|
|
|
||
|
|
// Notifies the stream handle of error, but doesn't close the stream.
|
||
|
|
void OnError(int error);
|
||
|
|
|
||
|
|
// Reads at most |buf_len| bytes into |buf|. Returns the number of bytes read.
|
||
|
|
int Read(IOBuffer* buf, int buf_len);
|
||
|
|
|
||
|
|
const NetLogWithSource& net_log() const { return net_log_; }
|
||
|
|
|
||
|
|
// Prevents this stream from migrating to a cellular network. May be reset
|
||
|
|
// when connection migrates to a cellular network.
|
||
|
|
void DisableConnectionMigrationToCellularNetwork();
|
||
|
|
|
||
|
|
bool can_migrate_to_cellular_network() {
|
||
|
|
return can_migrate_to_cellular_network_;
|
||
|
|
}
|
||
|
|
|
||
|
|
// True if this stream is the first data stream created on this session.
|
||
|
|
bool IsFirstStream();
|
||
|
|
|
||
|
|
int DeliverEarlyHints(spdy::Http2HeaderBlock* header_block);
|
||
|
|
|
||
|
|
int DeliverInitialHeaders(spdy::Http2HeaderBlock* header_block);
|
||
|
|
|
||
|
|
bool DeliverTrailingHeaders(spdy::Http2HeaderBlock* header_block,
|
||
|
|
int* frame_len);
|
||
|
|
|
||
|
|
using quic::QuicSpdyStream::HasBufferedData;
|
||
|
|
using quic::QuicStream::sequencer;
|
||
|
|
|
||
|
|
private:
|
||
|
|
void NotifyHandleOfInitialHeadersAvailableLater();
|
||
|
|
void NotifyHandleOfInitialHeadersAvailable();
|
||
|
|
void NotifyHandleOfTrailingHeadersAvailableLater();
|
||
|
|
void NotifyHandleOfTrailingHeadersAvailable();
|
||
|
|
void NotifyHandleOfDataAvailableLater();
|
||
|
|
void NotifyHandleOfDataAvailable();
|
||
|
|
|
||
|
|
NetLogWithSource net_log_;
|
||
|
|
raw_ptr<Handle> handle_ = nullptr;
|
||
|
|
|
||
|
|
// True when initial headers have been sent.
|
||
|
|
bool initial_headers_sent_ = false;
|
||
|
|
|
||
|
|
raw_ptr<quic::QuicSpdyClientSessionBase> session_;
|
||
|
|
quic::QuicTransportVersion quic_version_;
|
||
|
|
|
||
|
|
// Set to false if this stream should not be migrated to a cellular network
|
||
|
|
// during connection migration.
|
||
|
|
bool can_migrate_to_cellular_network_ = true;
|
||
|
|
|
||
|
|
// True if non-informational (non-1xx) initial headers have arrived.
|
||
|
|
bool initial_headers_arrived_ = false;
|
||
|
|
// True if non-informational (non-1xx) initial headers have been delivered to
|
||
|
|
// the handle.
|
||
|
|
bool headers_delivered_ = false;
|
||
|
|
// Stores the initial header until they are delivered to the handle.
|
||
|
|
spdy::Http2HeaderBlock initial_headers_;
|
||
|
|
// Length of the HEADERS frame containing initial headers.
|
||
|
|
size_t initial_headers_frame_len_ = 0;
|
||
|
|
|
||
|
|
// Length of the HEADERS frame containing trailing headers.
|
||
|
|
size_t trailing_headers_frame_len_ = 0;
|
||
|
|
|
||
|
|
struct EarlyHints {
|
||
|
|
EarlyHints(spdy::Http2HeaderBlock headers, size_t frame_len)
|
||
|
|
: headers(std::move(headers)), frame_len(frame_len) {}
|
||
|
|
EarlyHints(EarlyHints&& other) = default;
|
||
|
|
EarlyHints& operator=(EarlyHints&& other) = default;
|
||
|
|
EarlyHints(const EarlyHints& other) = delete;
|
||
|
|
EarlyHints& operator=(const EarlyHints& other) = delete;
|
||
|
|
|
||
|
|
spdy::Http2HeaderBlock headers;
|
||
|
|
size_t frame_len = 0;
|
||
|
|
};
|
||
|
|
base::circular_deque<EarlyHints> early_hints_;
|
||
|
|
|
||
|
|
base::WeakPtrFactory<QuicChromiumClientStream> weak_factory_{this};
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace net
|
||
|
|
|
||
|
|
#endif // NET_QUIC_QUIC_CHROMIUM_CLIENT_STREAM_H_
|