blob: e81b4ab0673ccad69112ab3bd158d532eaea2258 [file] [log] [blame]
/** @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 "I_EventSystem.h"
#include "I_Event.h"
#include "I_IOBuffer.h"
#include "tscore/Arena.h"
#include "tscpp/util/IntrusiveDList.h"
#include "MIME.h"
#include "HTTP.h"
#include "QUICApplication.h"
#include "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_header_list_size, uint16_t max_table_size, uint16_t max_blocking_streams);
virtual ~QPACK();
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(QUICStreamIO *stream_io);
void set_decoder_stream(QUICStreamIO *stream_io);
void update_max_header_list_size(uint32_t max_header_list_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 LookupResult {
uint16_t index = 0;
enum MatchType { NONE, NAME, EXACT } match_type = MatchType::NONE;
};
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 int name_len;
const int value_len;
};
class StaticTable
{
public:
static const LookupResult lookup(uint16_t index, const char **name, int *name_len, const char **value, int *value_len);
static const LookupResult lookup(const char *name, int name_len, const char *value, int value_len);
private:
static const Header STATIC_HEADER_FIELDS[];
};
struct DynamicTableEntry {
uint16_t index = 0;
uint16_t offset = 0;
uint16_t name_len = 0;
uint16_t value_len = 0;
uint16_t ref_count = 0;
};
class DynamicTableStorage
{
public:
DynamicTableStorage(uint16_t size);
~DynamicTableStorage();
void read(uint16_t offset, const char **name, uint16_t name_len, const char **value, uint16_t value_len) const;
uint16_t write(const char *name, uint16_t name_len, const char *value, uint16_t value_len);
void erase(uint16_t name_len, uint16_t value_len);
private:
uint16_t _overwrite_threshold = 0;
uint8_t *_data = nullptr;
uint16_t _data_size = 0;
uint16_t _head = 0;
uint16_t _tail = 0;
};
class DynamicTable
{
public:
DynamicTable(uint16_t size);
~DynamicTable();
const LookupResult lookup(uint16_t index, const char **name, int *name_len, const char **value, int *value_len);
const LookupResult lookup(const char *name, int name_len, const char *value, int value_len);
const LookupResult insert_entry(bool is_static, uint16_t index, const char *value, uint16_t value_len);
const LookupResult insert_entry(const char *name, uint16_t name_len, const char *value, uint16_t value_len);
const LookupResult duplicate_entry(uint16_t current_index);
bool should_duplicate(uint16_t index);
void update_size(uint16_t max_size);
void ref_entry(uint16_t index);
void unref_entry(uint16_t index);
uint16_t largest_index() const;
private:
uint16_t _available = 0;
uint16_t _entries_inserted = 0;
// FIXME It may be better to split this array into small arrays to reduce memory footprint
struct DynamicTableEntry *_entries = nullptr;
uint16_t _max_entries = 0;
uint16_t _entries_head = 0;
uint16_t _entries_tail = 0;
DynamicTableStorage *_storage = nullptr;
};
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;
};
DynamicTable _dynamic_table;
std::map<uint64_t, struct EntryReference> _references;
uint32_t _max_header_list_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;
ts::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(QUICStreamIO &stream_io, bool &is_static, uint16_t &index, Arena &arena, char **value,
uint16_t &value_len);
int _read_insert_without_name_ref(QUICStreamIO &stream_io, Arena &arena, char **name, uint16_t &name_len, char **value,
uint16_t &value_len);
int _read_duplicate(QUICStreamIO &stream_io, uint16_t &index);
int _read_dynamic_table_size_update(QUICStreamIO &stream_io, 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(QUICStreamIO &stream_io, uint16_t &insert_count);
int _read_header_acknowledgement(QUICStreamIO &stream_io, uint64_t &stream_id);
int _read_stream_cancellation(QUICStreamIO &stream_io, 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(QUICStreamIO &stream_io);
int _on_decoder_stream_read_ready(QUICStreamIO &stream_io);
int _on_encoder_stream_read_ready(QUICStreamIO &stream_io);
int _on_write_ready(QUICStreamIO &stream_io);
int _on_decoder_write_ready(QUICStreamIO &stream_io);
int _on_encoder_write_ready(QUICStreamIO &stream_io);
// 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;
};