blob: 6f7a5dc9bd6077a2e1a864cd792d466a24fbc908 [file] [log] [blame]
// Copyright (c) 2012 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_SPDY_CORE_SPDY_FRAMER_H_
#define QUICHE_SPDY_CORE_SPDY_FRAMER_H_
#include <stddef.h>
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include "absl/strings/string_view.h"
#include "common/platform/api/quiche_export.h"
#include "spdy/core/hpack/hpack_encoder.h"
#include "spdy/core/spdy_alt_svc_wire_format.h"
#include "spdy/core/spdy_header_block.h"
#include "spdy/core/spdy_headers_handler_interface.h"
#include "spdy/core/spdy_protocol.h"
#include "spdy/core/zero_copy_output_buffer.h"
namespace spdy {
namespace test {
class SpdyFramerPeer;
class SpdyFramerTest_MultipleContinuationFramesWithIterator_Test;
class SpdyFramerTest_PushPromiseFramesWithIterator_Test;
} // namespace test
class QUICHE_EXPORT_PRIVATE SpdyFrameSequence {
public:
virtual ~SpdyFrameSequence() {}
// Serializes the next frame in the sequence to |output|. Returns the number
// of bytes written to |output|.
virtual size_t NextFrame(ZeroCopyOutputBuffer* output) = 0;
// Returns true iff there is at least one more frame in the sequence.
virtual bool HasNextFrame() const = 0;
// Get SpdyFrameIR of the frame to be serialized.
virtual const SpdyFrameIR& GetIR() const = 0;
};
class QUICHE_EXPORT_PRIVATE SpdyFramer {
public:
enum CompressionOption {
ENABLE_COMPRESSION,
DISABLE_COMPRESSION,
};
// Create a SpdyFrameSequence to serialize |frame_ir|.
static std::unique_ptr<SpdyFrameSequence> CreateIterator(
SpdyFramer* framer,
std::unique_ptr<const SpdyFrameIR> frame_ir);
// Gets the serialized flags for the given |frame|.
static uint8_t GetSerializedFlags(const SpdyFrameIR& frame);
// Serialize a data frame.
static SpdySerializedFrame SerializeData(const SpdyDataIR& data_ir);
// Serializes the data frame header and optionally padding length fields,
// excluding actual data payload and padding.
static SpdySerializedFrame SerializeDataFrameHeaderWithPaddingLengthField(
const SpdyDataIR& data_ir);
// Serializes a WINDOW_UPDATE frame. The WINDOW_UPDATE
// frame is used to implement per stream flow control.
static SpdySerializedFrame SerializeWindowUpdate(
const SpdyWindowUpdateIR& window_update);
explicit SpdyFramer(CompressionOption option);
virtual ~SpdyFramer();
// Set debug callbacks to be called from the framer. The debug visitor is
// completely optional and need not be set in order for normal operation.
// If this is called multiple times, only the last visitor will be used.
void set_debug_visitor(SpdyFramerDebugVisitorInterface* debug_visitor);
SpdySerializedFrame SerializeRstStream(
const SpdyRstStreamIR& rst_stream) const;
// Serializes a SETTINGS frame. The SETTINGS frame is
// used to communicate name/value pairs relevant to the communication channel.
SpdySerializedFrame SerializeSettings(const SpdySettingsIR& settings) const;
// Serializes a PING frame. The unique_id is used to
// identify the ping request/response.
SpdySerializedFrame SerializePing(const SpdyPingIR& ping) const;
// Serializes a GOAWAY frame. The GOAWAY frame is used
// prior to the shutting down of the TCP connection, and includes the
// stream_id of the last stream the sender of the frame is willing to process
// to completion.
SpdySerializedFrame SerializeGoAway(const SpdyGoAwayIR& goaway) const;
// Serializes a HEADERS frame. The HEADERS frame is used
// for sending headers.
SpdySerializedFrame SerializeHeaders(const SpdyHeadersIR& headers);
// Serializes a PUSH_PROMISE frame. The PUSH_PROMISE frame is used
// to inform the client that it will be receiving an additional stream
// in response to the original request. The frame includes synthesized
// headers to explain the upcoming data.
SpdySerializedFrame SerializePushPromise(
const SpdyPushPromiseIR& push_promise);
// Serializes a CONTINUATION frame. The CONTINUATION frame is used
// to continue a sequence of header block fragments.
SpdySerializedFrame SerializeContinuation(
const SpdyContinuationIR& continuation) const;
// Serializes an ALTSVC frame. The ALTSVC frame advertises the
// availability of an alternative service to the client.
SpdySerializedFrame SerializeAltSvc(const SpdyAltSvcIR& altsvc);
// Serializes a PRIORITY frame. The PRIORITY frame advises a change in
// the relative priority of the given stream.
SpdySerializedFrame SerializePriority(const SpdyPriorityIR& priority) const;
// Serializes a PRIORITY_UPDATE frame.
// See https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html.
SpdySerializedFrame SerializePriorityUpdate(
const SpdyPriorityUpdateIR& priority_update) const;
// Serializes an ACCEPT_CH frame. See
// https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02.
SpdySerializedFrame SerializeAcceptCh(const SpdyAcceptChIR& accept_ch) const;
// Serializes an unknown frame given a frame header and payload.
SpdySerializedFrame SerializeUnknown(const SpdyUnknownIR& unknown) const;
// Serialize a frame of unknown type.
SpdySerializedFrame SerializeFrame(const SpdyFrameIR& frame);
// Serialize a data frame.
bool SerializeData(const SpdyDataIR& data,
ZeroCopyOutputBuffer* output) const;
// Serializes the data frame header and optionally padding length fields,
// excluding actual data payload and padding.
bool SerializeDataFrameHeaderWithPaddingLengthField(
const SpdyDataIR& data,
ZeroCopyOutputBuffer* output) const;
bool SerializeRstStream(const SpdyRstStreamIR& rst_stream,
ZeroCopyOutputBuffer* output) const;
// Serializes a SETTINGS frame. The SETTINGS frame is
// used to communicate name/value pairs relevant to the communication channel.
bool SerializeSettings(const SpdySettingsIR& settings,
ZeroCopyOutputBuffer* output) const;
// Serializes a PING frame. The unique_id is used to
// identify the ping request/response.
bool SerializePing(const SpdyPingIR& ping,
ZeroCopyOutputBuffer* output) const;
// Serializes a GOAWAY frame. The GOAWAY frame is used
// prior to the shutting down of the TCP connection, and includes the
// stream_id of the last stream the sender of the frame is willing to process
// to completion.
bool SerializeGoAway(const SpdyGoAwayIR& goaway,
ZeroCopyOutputBuffer* output) const;
// Serializes a HEADERS frame. The HEADERS frame is used
// for sending headers.
bool SerializeHeaders(const SpdyHeadersIR& headers,
ZeroCopyOutputBuffer* output);
// Serializes a WINDOW_UPDATE frame. The WINDOW_UPDATE
// frame is used to implement per stream flow control.
bool SerializeWindowUpdate(const SpdyWindowUpdateIR& window_update,
ZeroCopyOutputBuffer* output) const;
// Serializes a PUSH_PROMISE frame. The PUSH_PROMISE frame is used
// to inform the client that it will be receiving an additional stream
// in response to the original request. The frame includes synthesized
// headers to explain the upcoming data.
bool SerializePushPromise(const SpdyPushPromiseIR& push_promise,
ZeroCopyOutputBuffer* output);
// Serializes a CONTINUATION frame. The CONTINUATION frame is used
// to continue a sequence of header block fragments.
bool SerializeContinuation(const SpdyContinuationIR& continuation,
ZeroCopyOutputBuffer* output) const;
// Serializes an ALTSVC frame. The ALTSVC frame advertises the
// availability of an alternative service to the client.
bool SerializeAltSvc(const SpdyAltSvcIR& altsvc,
ZeroCopyOutputBuffer* output);
// Serializes a PRIORITY frame. The PRIORITY frame advises a change in
// the relative priority of the given stream.
bool SerializePriority(const SpdyPriorityIR& priority,
ZeroCopyOutputBuffer* output) const;
// Serializes a PRIORITY_UPDATE frame.
// See https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html.
bool SerializePriorityUpdate(const SpdyPriorityUpdateIR& priority_update,
ZeroCopyOutputBuffer* output) const;
// Serializes an ACCEPT_CH frame. See
// https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02.
bool SerializeAcceptCh(const SpdyAcceptChIR& accept_ch,
ZeroCopyOutputBuffer* output) const;
// Serializes an unknown frame given a frame header and payload.
bool SerializeUnknown(const SpdyUnknownIR& unknown,
ZeroCopyOutputBuffer* output) const;
// Serialize a frame of unknown type.
size_t SerializeFrame(const SpdyFrameIR& frame, ZeroCopyOutputBuffer* output);
// Returns whether this SpdyFramer will compress header blocks using HPACK.
bool compression_enabled() const {
return compression_option_ == ENABLE_COMPRESSION;
}
void SetHpackIndexingPolicy(HpackEncoder::IndexingPolicy policy) {
GetHpackEncoder()->SetIndexingPolicy(std::move(policy));
}
// Updates the maximum size of the header encoder compression table.
void UpdateHeaderEncoderTableSize(uint32_t value);
// Returns the maximum size of the header encoder compression table.
size_t header_encoder_table_size() const;
// Get (and lazily initialize) the HPACK encoder state.
HpackEncoder* GetHpackEncoder();
// Gets the HPACK encoder state. Returns nullptr if the encoder has not been
// initialized.
const HpackEncoder* GetHpackEncoder() const { return hpack_encoder_.get(); }
protected:
friend class test::SpdyFramerPeer;
friend class test::SpdyFramerTest_MultipleContinuationFramesWithIterator_Test;
friend class test::SpdyFramerTest_PushPromiseFramesWithIterator_Test;
// Iteratively converts a SpdyFrameIR into an appropriate sequence of Spdy
// frames.
// Example usage:
// std::unique_ptr<SpdyFrameSequence> it = CreateIterator(framer, frame_ir);
// while (it->HasNextFrame()) {
// if(it->NextFrame(output) == 0) {
// // Write failed;
// }
// }
class QUICHE_EXPORT_PRIVATE SpdyFrameIterator : public SpdyFrameSequence {
public:
// Creates an iterator with the provided framer.
// Does not take ownership of |framer|.
// |framer| must outlive this instance.
explicit SpdyFrameIterator(SpdyFramer* framer);
~SpdyFrameIterator() override;
// Serializes the next frame in the sequence to |output|. Returns the number
// of bytes written to |output|.
size_t NextFrame(ZeroCopyOutputBuffer* output) override;
// Returns true iff there is at least one more frame in the sequence.
bool HasNextFrame() const override;
// SpdyFrameIterator is neither copyable nor movable.
SpdyFrameIterator(const SpdyFrameIterator&) = delete;
SpdyFrameIterator& operator=(const SpdyFrameIterator&) = delete;
protected:
virtual size_t GetFrameSizeSansBlock() const = 0;
virtual bool SerializeGivenEncoding(const std::string& encoding,
ZeroCopyOutputBuffer* output) const = 0;
SpdyFramer* GetFramer() const { return framer_; }
void SetEncoder(const SpdyFrameWithHeaderBlockIR* ir) {
encoder_ =
framer_->GetHpackEncoder()->EncodeHeaderSet(ir->header_block());
}
bool has_next_frame() const { return has_next_frame_; }
private:
SpdyFramer* const framer_;
std::unique_ptr<HpackEncoder::ProgressiveEncoder> encoder_;
bool is_first_frame_;
bool has_next_frame_;
};
// Iteratively converts a SpdyHeadersIR (with a possibly huge
// SpdyHeaderBlock) into an appropriate sequence of SpdySerializedFrames, and
// write to the output.
class QUICHE_EXPORT_PRIVATE SpdyHeaderFrameIterator
: public SpdyFrameIterator {
public:
// Does not take ownership of |framer|. Take ownership of |headers_ir|.
SpdyHeaderFrameIterator(SpdyFramer* framer,
std::unique_ptr<const SpdyHeadersIR> headers_ir);
~SpdyHeaderFrameIterator() override;
private:
const SpdyFrameIR& GetIR() const override;
size_t GetFrameSizeSansBlock() const override;
bool SerializeGivenEncoding(const std::string& encoding,
ZeroCopyOutputBuffer* output) const override;
const std::unique_ptr<const SpdyHeadersIR> headers_ir_;
};
// Iteratively converts a SpdyPushPromiseIR (with a possibly huge
// SpdyHeaderBlock) into an appropriate sequence of SpdySerializedFrames, and
// write to the output.
class QUICHE_EXPORT_PRIVATE SpdyPushPromiseFrameIterator
: public SpdyFrameIterator {
public:
// Does not take ownership of |framer|. Take ownership of |push_promise_ir|.
SpdyPushPromiseFrameIterator(
SpdyFramer* framer,
std::unique_ptr<const SpdyPushPromiseIR> push_promise_ir);
~SpdyPushPromiseFrameIterator() override;
private:
const SpdyFrameIR& GetIR() const override;
size_t GetFrameSizeSansBlock() const override;
bool SerializeGivenEncoding(const std::string& encoding,
ZeroCopyOutputBuffer* output) const override;
const std::unique_ptr<const SpdyPushPromiseIR> push_promise_ir_;
};
// Converts a SpdyFrameIR into one Spdy frame (a sequence of length 1), and
// write it to the output.
class QUICHE_EXPORT_PRIVATE SpdyControlFrameIterator
: public SpdyFrameSequence {
public:
SpdyControlFrameIterator(SpdyFramer* framer,
std::unique_ptr<const SpdyFrameIR> frame_ir);
~SpdyControlFrameIterator() override;
size_t NextFrame(ZeroCopyOutputBuffer* output) override;
bool HasNextFrame() const override;
const SpdyFrameIR& GetIR() const override;
private:
SpdyFramer* const framer_;
std::unique_ptr<const SpdyFrameIR> frame_ir_;
bool has_next_frame_ = true;
};
private:
void SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers,
uint8_t* flags,
size_t* size,
std::string* hpack_encoding,
int* weight,
size_t* length_field);
void SerializePushPromiseBuilderHelper(const SpdyPushPromiseIR& push_promise,
uint8_t* flags,
std::string* hpack_encoding,
size_t* size);
std::unique_ptr<HpackEncoder> hpack_encoder_;
SpdyFramerDebugVisitorInterface* debug_visitor_;
// Determines whether HPACK compression is used.
const CompressionOption compression_option_;
};
} // namespace spdy
#endif // QUICHE_SPDY_CORE_SPDY_FRAMER_H_