%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/streams.h |
#pragma once
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
#include <aliased_struct.h>
#include <async_wrap.h>
#include <base_object.h>
#include <dataqueue/queue.h>
#include <env.h>
#include <memory_tracker.h>
#include <node_blob.h>
#include <node_bob.h>
#include <node_http_common.h>
#include "bindingdata.h"
#include "data.h"
namespace node {
namespace quic {
class Session;
using Ngtcp2Source = bob::SourceImpl<ngtcp2_vec>;
// QUIC Stream's are simple data flows that may be:
//
// * Bidirectional (both sides can send) or Unidirectional (one side can send)
// * Server or Client Initiated
//
// The flow direction and origin of the stream are important in determining the
// write and read state (Open or Closed). Specifically:
//
// Bidirectional Stream States:
// +--------+--------------+----------+----------+
// | ON | Initiated By | Readable | Writable |
// +--------+--------------+----------+----------+
// | Server | Server | Y | Y |
// +--------+--------------+----------+----------+
// | Server | Client | Y | Y |
// +--------+--------------+----------+----------+
// | Client | Server | Y | Y |
// +--------+--------------+----------+----------+
// | Client | Client | Y | Y |
// +--------+--------------+----------+----------+
//
// Unidirectional Stream States
// +--------+--------------+----------+----------+
// | ON | Initiated By | Readable | Writable |
// +--------+--------------+----------+----------+
// | Server | Server | N | Y |
// +--------+--------------+----------+----------+
// | Server | Client | Y | N |
// +--------+--------------+----------+----------+
// | Client | Server | Y | N |
// +--------+--------------+----------+----------+
// | Client | Client | N | Y |
// +--------+--------------+----------+----------+
//
// All data sent via the Stream is buffered internally until either receipt is
// acknowledged from the peer or attempts to send are abandoned. The fact that
// data is buffered in memory makes it essential that the flow control for the
// session and the stream are properly handled. For now, we are largely relying
// on ngtcp2's default flow control mechanisms which generally should be doing
// the right thing.
//
// A Stream may be in a fully closed state (No longer readable nor writable)
// state but still have unacknowledged data in it's inbound and outbound
// queues.
//
// A Stream is gracefully closed when (a) both read and write states are closed,
// (b) all queued data has been acknowledged.
//
// The Stream may be forcefully closed immediately using destroy(err). This
// causes all queued outbound data and pending JavaScript writes are abandoned,
// and causes the Stream to be immediately closed at the ngtcp2 level without
// waiting for any outstanding acknowledgements. Keep in mind, however, that the
// peer is not notified that the stream is destroyed and may attempt to continue
// sending data and acknowledgements.
//
// QUIC streams in general do not have headers. Some QUIC applications, however,
// may associate headers with the stream (HTTP/3 for instance).
class Stream : public AsyncWrap,
public Ngtcp2Source,
public DataQueue::BackpressureListener {
public:
using Header = NgHeaderBase<BindingData>;
static Stream* From(void* stream_user_data);
static bool HasInstance(Environment* env, v8::Local<v8::Value> value);
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
static void Initialize(Environment* env, v8::Local<v8::Object> target);
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
static BaseObjectPtr<Stream> Create(
Session* session,
int64_t id,
std::shared_ptr<DataQueue> source = nullptr);
// The constructor is only public to be visible by MakeDetachedBaseObject.
// Call Create to create new instances of Stream.
Stream(BaseObjectWeakPtr<Session> session,
v8::Local<v8::Object> obj,
int64_t id,
std::shared_ptr<DataQueue> source);
~Stream() override;
int64_t id() const;
Side origin() const;
Direction direction() const;
Session& session() const;
bool is_destroyed() const;
// True if we've completely sent all outbound data for this stream.
bool is_eos() const;
bool is_readable() const;
bool is_writable() const;
// Called by the session/application to indicate that the specified number
// of bytes have been acknowledged by the peer.
void Acknowledge(size_t datalen);
void Commit(size_t datalen);
void EndWritable();
void EndReadable(std::optional<uint64_t> maybe_final_size = std::nullopt);
void EntryRead(size_t amount) override;
// Pulls data from the internal outbound DataQueue configured for this stream.
int DoPull(bob::Next<ngtcp2_vec> next,
int options,
ngtcp2_vec* data,
size_t count,
size_t max_count_hint) override;
// Forcefully close the stream immediately. All queued data and pending
// writes are abandoned, and the stream is immediately closed at the ngtcp2
// level without waiting for any outstanding acknowledgements.
void Destroy(QuicError error = QuicError());
struct ReceiveDataFlags final {
// Identifies the final chunk of data that the peer will send for the
// stream.
bool fin = false;
// Indicates that this chunk of data was received in a 0RTT packet before
// the TLS handshake completed, suggesting that is is not as secure and
// could be replayed by an attacker.
bool early = false;
};
void ReceiveData(const uint8_t* data, size_t len, ReceiveDataFlags flags);
void ReceiveStopSending(QuicError error);
void ReceiveStreamReset(uint64_t final_size, QuicError error);
void BeginHeaders(HeadersKind kind);
// Returns false if the header cannot be added. This will typically happen
// if the application does not support headers, a maximimum number of headers
// have already been added, or the maximum total header length is reached.
bool AddHeader(const Header& header);
void set_headers_kind(HeadersKind kind);
SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(Stream)
SET_SELF_SIZE(Stream)
struct State;
struct Stats;
// Notifies the JavaScript side that sending data on the stream has been
// blocked because of flow control restriction.
void EmitBlocked();
// Delivers the set of inbound headers that have been collected.
void EmitHeaders();
private:
struct Impl;
class Outbound;
// Gets a reader for the data received for this stream from the peer,
BaseObjectPtr<Blob::Reader> get_reader();
void set_final_size(uint64_t amount);
void set_outbound(std::shared_ptr<DataQueue> source);
// JavaScript callouts
// Notifies the JavaScript side that the stream has been destroyed.
void EmitClose(const QuicError& error);
// Notifies the JavaScript side that the stream has been reset.
void EmitReset(const QuicError& error);
// Notifies the JavaScript side that the application is ready to receive
// trailing headers.
void EmitWantTrailers();
AliasedStruct<Stats> stats_;
AliasedStruct<State> state_;
BaseObjectWeakPtr<Session> session_;
const Side origin_;
const Direction direction_;
std::unique_ptr<Outbound> outbound_;
std::shared_ptr<DataQueue> inbound_;
std::vector<v8::Local<v8::Value>> headers_;
HeadersKind headers_kind_ = HeadersKind::INITIAL;
size_t headers_length_ = 0;
friend struct Impl;
public:
// The Queue/Schedule/Unschedule here are part of the mechanism used to
// determine which streams have data to send on the session. When a stream
// potentially has data available, it will be scheduled in the Queue. Then,
// when the Session::Application starts sending pending data, it will check
// the queue to see if there are streams waiting. If there are, it will grab
// one and check to see if there is data to send. When a stream does not have
// data to send (such as when it is initially created or is using an async
// source that is still waiting for data to be pushed) it will not appear in
// the queue.
ListNode<Stream> stream_queue_;
using Queue = ListHead<Stream, &Stream::stream_queue_>;
void Schedule(Queue* queue);
void Unschedule();
};
} // namespace quic
} // namespace node
#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS