| // Copyright 2013 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. |
| |
| #ifndef QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_ |
| #define QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_ |
| |
| #include <cstddef> |
| #include <utility> |
| |
| #include "absl/types/optional.h" |
| #include "quiche/quic/core/quic_packets.h" |
| #include "quiche/quic/platform/api/quic_export.h" |
| #include "quiche/quic/platform/api/quic_ip_address.h" |
| #include "quiche/quic/platform/api/quic_socket_address.h" |
| |
| namespace quic { |
| |
| struct WriteResult; |
| |
| struct QUIC_EXPORT_PRIVATE PerPacketOptions { |
| virtual ~PerPacketOptions() {} |
| |
| // Returns a heap-allocated copy of |this|. |
| // |
| // The subclass implementation of this method should look like this: |
| // return std::make_unique<MyAwesomePerPacketOptions>(*this); |
| // |
| // This method is declared pure virtual in order to ensure the subclasses |
| // would not forget to override it. |
| virtual std::unique_ptr<PerPacketOptions> Clone() const = 0; |
| |
| // Specifies ideal release time delay for this packet. |
| QuicTime::Delta release_time_delay = QuicTime::Delta::Zero(); |
| // Whether it is allowed to send this packet without |release_time_delay|. |
| bool allow_burst = false; |
| }; |
| |
| // An interface between writers and the entity managing the |
| // socket (in our case the QuicDispatcher). This allows the Dispatcher to |
| // control writes, and manage any writers who end up write blocked. |
| // A concrete writer works in one of the two modes: |
| // - PassThrough mode. This is the default mode. Caller calls WritePacket with |
| // caller-allocated packet buffer. Unless the writer is blocked, each call to |
| // WritePacket triggers a write using the underlying socket API. |
| // |
| // - Batch mode. In this mode, a call to WritePacket may not cause a packet to |
| // be sent using the underlying socket API. Instead, multiple packets are |
| // saved in the writer's internal buffer until they are flushed. The flush can |
| // be explicit, by calling Flush, or implicit, e.g. by calling |
| // WritePacket when the internal buffer is near full. |
| // |
| // Buffer management: |
| // In Batch mode, a writer manages an internal buffer, which is large enough to |
| // hold multiple packets' data. If the caller calls WritePacket with a |
| // caller-allocated packet buffer, the writer will memcpy the buffer into the |
| // internal buffer. Caller can also avoid this memcpy by: |
| // 1. Call GetNextWriteLocation to get a pointer P into the internal buffer. |
| // 2. Serialize the packet directly to P. |
| // 3. Call WritePacket with P as the |buffer|. |
| class QUIC_EXPORT_PRIVATE QuicPacketWriter { |
| public: |
| virtual ~QuicPacketWriter() {} |
| |
| // PassThrough mode: |
| // Sends the packet out to the peer, with some optional per-packet options. |
| // If the write succeeded, the result's status is WRITE_STATUS_OK and |
| // bytes_written is populated. If the write failed, the result's status is |
| // WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR and error_code is populated. |
| // |
| // Batch mode: |
| // If the writer is blocked, return WRITE_STATUS_BLOCKED immediately. |
| // If the packet can be batched with other buffered packets, save the packet |
| // to the internal buffer. |
| // If the packet can not be batched, or the internal buffer is near full after |
| // it is buffered, the internal buffer is flushed to free up space. |
| // Return WriteResult(WRITE_STATUS_OK, <bytes_flushed>) on success. When |
| // <bytes_flushed> is zero, it means the packet is buffered and not flushed. |
| // Return WRITE_STATUS_BLOCKED if the packet is not buffered and the socket is |
| // blocked while flushing. |
| // Otherwise return an error status. |
| // |
| // Options must be either null, or created for the particular QuicPacketWriter |
| // implementation. Options may be ignored, depending on the implementation. |
| // |
| // Some comment about memory management if |buffer| was previously acquired |
| // by a call to "GetNextWriteLocation()": |
| // |
| // a) When WRITE_STATUS_OK is returned, the caller expects the writer owns the |
| // packet buffers and they will be released when the write finishes. |
| // |
| // b) When this function returns any status >= WRITE_STATUS_ERROR, the caller |
| // expects the writer releases the buffer (if needed) before the function |
| // returns. |
| // |
| // c) When WRITE_STATUS_BLOCKED is returned, the caller makes a copy of the |
| // buffer and will retry after unblock, so if |payload| is allocated from |
| // GetNextWriteLocation(), it |
| // 1) needs to be released before return, and |
| // 2) the content of |payload| should not change after return. |
| // |
| // d) When WRITE_STATUS_BLOCKED_DATA_BUFFERED is returned, the caller expects |
| // 1) the writer owns the packet buffers, and 2) the writer will re-send the |
| // packet when it unblocks. |
| virtual WriteResult WritePacket(const char* buffer, size_t buf_len, |
| const QuicIpAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| PerPacketOptions* options) = 0; |
| |
| // Returns true if the network socket is not writable. |
| virtual bool IsWriteBlocked() const = 0; |
| |
| // Records that the socket has become writable, for example when an EPOLLOUT |
| // is received or an asynchronous write completes. |
| virtual void SetWritable() = 0; |
| |
| // The error code used by the writer to indicate that the write failed due to |
| // supplied packet being too big. This is equivalent to returning |
| // WRITE_STATUS_MSG_TOO_BIG as a status. |
| virtual absl::optional<int> MessageTooBigErrorCode() const = 0; |
| |
| // Returns the maximum size of the packet which can be written using this |
| // writer for the supplied peer address. This size may actually exceed the |
| // size of a valid QUIC packet. |
| virtual QuicByteCount GetMaxPacketSize( |
| const QuicSocketAddress& peer_address) const = 0; |
| |
| // Returns true if the socket supports release timestamp. |
| virtual bool SupportsReleaseTime() const = 0; |
| |
| // True=Batch mode. False=PassThrough mode. |
| virtual bool IsBatchMode() const = 0; |
| |
| // PassThrough mode: Return {nullptr, nullptr} |
| // |
| // Batch mode: |
| // Return the QuicPacketBuffer for the next packet. A minimum of |
| // kMaxOutgoingPacketSize is guaranteed to be available from the returned |
| // address. If the internal buffer does not have enough space, |
| // {nullptr, nullptr} is returned. All arguments should be identical to the |
| // follow-up call to |WritePacket|, they are here to allow advanced packet |
| // memory management in packet writers, e.g. one packet buffer pool per |
| // |peer_address|. |
| // |
| // If QuicPacketBuffer.release_buffer is !nullptr, it should be called iff |
| // the caller does not call WritePacket for the returned buffer. |
| virtual QuicPacketBuffer GetNextWriteLocation( |
| const QuicIpAddress& self_address, |
| const QuicSocketAddress& peer_address) = 0; |
| |
| // PassThrough mode: Return WriteResult(WRITE_STATUS_OK, 0). |
| // |
| // Batch mode: |
| // Try send all buffered packets. |
| // - Return WriteResult(WRITE_STATUS_OK, <bytes_flushed>) if all buffered |
| // packets were sent successfully. |
| // - Return WRITE_STATUS_BLOCKED if the underlying socket is blocked while |
| // sending. Some packets may have been sent, packets not sent will stay in |
| // the internal buffer. |
| // - Return a status >= WRITE_STATUS_ERROR if an error was encuontered while |
| // sending. As this is not a re-tryable error, any batched packets which |
| // were on memory acquired via GetNextWriteLocation() should be released and |
| // the batch should be dropped. |
| virtual WriteResult Flush() = 0; |
| }; |
| |
| } // namespace quic |
| |
| #endif // QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_ |