blob: 4adfb33add21af33244f6cf932cdaadfcf56c9ef [file]
/** @file
*
* A brief file description
*
* @section license License
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <map>
#include "swoc/IntrusiveDList.h"
#include "iocore/eventsystem/EventSystem.h"
#include "iocore/eventsystem/Event.h"
#include "iocore/eventsystem/IOBuffer.h"
#include "tscore/Arena.h"
#include "proxy/hdrs/MIME.h"
#include "proxy/hdrs/HTTP.h"
#include "proxy/hdrs/XPACK.h"
#include "iocore/net/quic/QUICApplication.h"
#include "iocore/net/quic/QUICStreamVCAdapter.h"
#include "iocore/net/quic/QUICConnection.h"
class HTTPHdr;
enum {
QPACK_EVENT_DECODE_COMPLETE = QPACK_EVENT_EVENTS_START,
QPACK_EVENT_DECODE_FAILED,
};
class QPACK : public QUICApplication
{
public:
QPACK(QUICConnection *qc, uint32_t max_field_section_size, uint16_t max_table_size, uint16_t max_blocking_streams);
virtual ~QPACK();
void on_stream_open(QUICStream &stream) override;
void on_stream_close(QUICStream &stream) override;
int event_handler(int event, Event *data);
/*
* header_block must have enough size to store all headers in header_set.
* The maximum size can be estimated with QPACK::estimate_header_block_size().
*/
int encode(uint64_t stream_id, HTTPHdr &header_set, MIOBuffer *header_block, uint64_t &header_block_len);
/*
* This will emit either of two events below:
* - QPACK_EVENT_DECODE_COMPLETE (Data: *HTTPHdr)
* - QPACK_EVENT_DECODE_FAILED (Data: nullptr)
*/
int decode(uint64_t stream_id, const uint8_t *header_block, size_t header_block_len, HTTPHdr &hdr, Continuation *cont,
EThread *thread = this_ethread());
int cancel(uint64_t stream_id);
void set_encoder_stream(QUICStreamId id);
void set_decoder_stream(QUICStreamId id);
void update_max_field_section_size(uint32_t max_field_section_size);
void update_max_table_size(uint16_t max_table_size);
void update_max_blocking_streams(uint16_t max_blocking_streams);
static size_t estimate_header_block_size(const HTTPHdr &header_set);
private:
struct Header {
Header(const char *n, const char *v) : name(n), value(v), name_len(strlen(name)), value_len(strlen(value)) {}
const char *name;
const char *value;
const size_t name_len;
const size_t value_len;
};
class StaticTable
{
public:
static const XpackLookupResult lookup(uint16_t index, const char **name, size_t *name_len, const char **value,
size_t *value_len);
static const XpackLookupResult lookup(const char *name, size_t name_len, const char *value, size_t value_len);
private:
static const Header STATIC_HEADER_FIELDS[];
};
class DecodeRequest
{
public:
DecodeRequest(uint16_t largest_reference, EThread *thread, Continuation *continuation, uint64_t stream_id,
const uint8_t *header_block, size_t header_block_len, HTTPHdr &hdr)
: _largest_reference(largest_reference),
_thread(thread),
_continuation(continuation),
_stream_id(stream_id),
_header_block(header_block),
_header_block_len(header_block_len),
_hdr(hdr)
{
}
uint16_t
largest_reference() const
{
return this->_largest_reference;
}
EThread *
thread()
{
return this->_thread;
}
Continuation *
continuation()
{
return this->_continuation;
}
uint64_t
stream_id() const
{
return this->_stream_id;
}
const uint8_t *
header_block() const
{
return this->_header_block;
}
size_t
header_block_len() const
{
return this->_header_block_len;
}
HTTPHdr &
hdr()
{
return this->_hdr;
}
class Linkage
{
public:
static DecodeRequest *&
next_ptr(DecodeRequest *t)
{
return *reinterpret_cast<DecodeRequest **>(&t->_next);
}
static DecodeRequest *&
prev_ptr(DecodeRequest *t)
{
return *reinterpret_cast<DecodeRequest **>(&t->_prev);
}
};
private:
uint16_t _largest_reference;
EThread *_thread;
Continuation *_continuation;
uint64_t _stream_id;
const uint8_t *_header_block;
size_t _header_block_len;
HTTPHdr &_hdr;
// For IntrusiveDList support
DecodeRequest *_next = nullptr;
DecodeRequest *_prev = nullptr;
};
struct EntryReference {
uint16_t smallest;
uint16_t largest;
};
XpackDynamicTable _dynamic_table;
std::map<uint64_t, struct EntryReference> _references;
uint32_t _max_field_section_size = 0;
uint16_t _max_table_size = 0;
uint16_t _max_blocking_streams = 0;
Continuation *_event_handler = nullptr;
void _resume_decode();
void _abort_decode();
bool _invalid = false;
swoc::IntrusiveDList<DecodeRequest::Linkage> _blocked_list;
bool _add_to_blocked_list(DecodeRequest *decode_request);
uint16_t _largest_known_received_index = 0;
void _update_largest_known_received_index_by_insert_count(uint16_t insert_count);
void _update_largest_known_received_index_by_stream_id(uint64_t stream_id);
void _update_reference_counts(uint64_t stream_id);
// Encoder Stream
int _read_insert_with_name_ref(IOBufferReader &reader, bool &is_static, uint16_t &index, Arena &arena, char **value,
size_t &value_len);
int _read_insert_without_name_ref(IOBufferReader &reader, Arena &arena, char **name, size_t &name_len, char **value,
size_t &value_len);
int _read_duplicate(IOBufferReader &reader, uint16_t &index);
int _read_dynamic_table_size_update(IOBufferReader &reader, uint16_t &max_size);
int _write_insert_with_name_ref(uint16_t index, bool dynamic, const char *value, uint16_t value_len);
int _write_insert_without_name_ref(const char *name, int name_len, const char *value, uint16_t value_len);
int _write_duplicate(uint16_t index);
int _write_dynamic_table_size_update(uint16_t max_size);
// Decoder Stream
int _read_table_state_synchronize(IOBufferReader &reader, uint16_t &insert_count);
int _read_header_acknowledgement(IOBufferReader &reader, uint64_t &stream_id);
int _read_stream_cancellation(IOBufferReader &reader, uint64_t &stream_id);
int _write_table_state_synchronize(uint16_t insert_count);
int _write_header_acknowledgement(uint64_t stream_id);
int _write_stream_cancellation(uint64_t stream_id);
// Request and Push Streams
int _encode_prefix(uint16_t largest_reference, uint16_t base_index, IOBufferBlock *prefix);
int _encode_header(const MIMEField &field, uint16_t base_index, IOBufferBlock *compressed_header, uint16_t &referred_index);
int _encode_indexed_header_field(uint16_t index, uint16_t base_index, bool dynamic_table, IOBufferBlock *compressed_header);
int _encode_indexed_header_field_with_postbase_index(uint16_t index, uint16_t base_index, bool never_index,
IOBufferBlock *compressed_header);
int _encode_literal_header_field_with_name_ref(uint16_t index, bool dynamic_table, uint16_t base_index, const char *value,
int value_len, bool never_index, IOBufferBlock *compressed_header);
int _encode_literal_header_field_without_name_ref(const char *name, int name_len, const char *value, int value_len,
bool never_index, IOBufferBlock *compressed_header);
int _encode_literal_header_field_with_postbase_name_ref(uint16_t index, uint16_t base_index, const char *value, int value_len,
bool never_index, IOBufferBlock *compressed_header);
void _decode(EThread *ethread, Continuation *cont, uint64_t stream_id, const uint8_t *header_block, size_t header_block_len,
HTTPHdr &hdr);
int _decode_header(const uint8_t *header_block, size_t header_block_len, HTTPHdr &hdr);
int _decode_indexed_header_field(int16_t base_index, const uint8_t *buf, size_t buf_len, HTTPHdr &hdr, uint32_t &header_len);
int _decode_indexed_header_field_with_postbase_index(int16_t base_index, const uint8_t *buf, size_t buf_len, HTTPHdr &hdr,
uint32_t &header_len);
int _decode_literal_header_field_with_name_ref(int16_t base_index, const uint8_t *buf, size_t buf_len, HTTPHdr &hdr,
uint32_t &header_len);
int _decode_literal_header_field_without_name_ref(const uint8_t *buf, size_t buf_len, HTTPHdr &hdr, uint32_t &header_len);
int _decode_literal_header_field_with_postbase_name_ref(int16_t base_index, const uint8_t *buf, size_t buf_len, HTTPHdr &hdr,
uint32_t &header_len);
// Utilities
uint16_t _calc_absolute_index_from_relative_index(uint16_t base_index, uint16_t relative_index);
uint16_t _calc_absolute_index_from_postbase_index(uint16_t base_index, uint16_t postbase_index);
uint16_t _calc_relative_index_from_absolute_index(uint16_t base_index, uint16_t absolute_index);
uint16_t _calc_postbase_index_from_absolute_index(uint16_t base_index, uint16_t absolute_index);
void _attach_header(HTTPHdr &hdr, const char *name, int name_len, const char *value, int value_len, bool never_index);
int _on_read_ready(VIO *vio);
int _on_decoder_stream_read_ready(IOBufferReader &reader);
int _on_encoder_stream_read_ready(IOBufferReader &reader);
int _on_write_ready(VIO *vio);
int _on_decoder_write_ready(MIOBuffer &writer);
int _on_encoder_write_ready(MIOBuffer &writer);
// Stream numbers
// FIXME How are these stream ids negotiated? In interop, encoder stream id have to be 0 and decoder stream id must not be used.
uint64_t _encoder_stream_id = 0;
uint64_t _decoder_stream_id = 9999;
// Chain of sending instructions
MIOBuffer *_encoder_stream_sending_instructions;
MIOBuffer *_decoder_stream_sending_instructions;
IOBufferReader *_encoder_stream_sending_instructions_reader;
IOBufferReader *_decoder_stream_sending_instructions_reader;
// Temporal buffer
Arena _arena;
};