blob: 567fc3c13d1d4297cb30042906756caf42a0c0a3 [file] [log] [blame]
/** @file
*
* Fundamental HTTP/2 protocol definitions and parsers.
*
* @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.
*/
#include "HTTP2.h"
#include "ink_assert.h"
const char * const HTTP2_CONNECTION_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
union byte_pointer {
byte_pointer(void * p) : ptr(p) {}
void * ptr;
uint8_t * u8;
uint16_t * u16;
uint32_t * u32;
};
template <typename T>
union byte_addressable_value
{
uint8_t bytes[sizeof(T)];
T value;
};
static void
write_and_advance(byte_pointer& dst, uint32_t src)
{
byte_addressable_value<uint32_t> pval;
pval.value = htonl(src);
memcpy(dst.u8, pval.bytes, sizeof(pval.bytes));
dst.u8 += sizeof(pval.bytes);
}
static void
write_and_advance(byte_pointer& dst, uint16_t src)
{
byte_addressable_value<uint16_t> pval;
pval.value = htons(src);
memcpy(dst.u8, pval.bytes, sizeof(pval.bytes));
dst.u8 += sizeof(pval.bytes);
}
static void
write_and_advance(byte_pointer& dst, uint8_t src)
{
*dst.u8 = src;
dst.u8++;
}
template<unsigned N> static void
memcpy_and_advance(uint8_t (&dst)[N], byte_pointer& src)
{
memcpy(dst, src.u8, N);
src.u8 += N;
}
void
memcpy_and_advance(uint8_t (&dst), byte_pointer& src)
{
dst = *src.u8;
++src.u8;
}
static bool
http2_are_frame_flags_valid(uint8_t ftype, uint8_t fflags)
{
static const uint8_t mask[HTTP2_FRAME_TYPE_MAX] = {
HTTP2_FLAGS_DATA_MASK,
HTTP2_FLAGS_HEADERS_MASK,
HTTP2_FLAGS_PRIORITY_MASK,
HTTP2_FLAGS_RST_STREAM_MASK,
HTTP2_FLAGS_SETTINGS_MASK,
HTTP2_FLAGS_PUSH_PROMISE_MASK,
HTTP2_FLAGS_PING_MASK,
HTTP2_FLAGS_GOAWAY_MASK,
HTTP2_FLAGS_WINDOW_UPDATE_MASK,
HTTP2_FLAGS_CONTINUATION_MASK,
HTTP2_FLAGS_ALTSVC_MASK,
HTTP2_FLAGS_BLOCKED_MASK,
};
// The frame flags are valid for this frame if nothing outside the defined bits is set.
return (fflags & ~mask[ftype]) == 0;
}
bool
http2_frame_header_is_valid(const Http2FrameHeader& hdr)
{
if (hdr.type >= HTTP2_FRAME_TYPE_MAX) {
return false;
}
if (hdr.length > HTTP2_MAX_FRAME_PAYLOAD) {
return false;
}
if (!http2_are_frame_flags_valid(hdr.type, hdr.flags)) {
return false;
}
return true;
}
bool
http2_settings_parameter_is_valid(const Http2SettingsParameter& param)
{
// Static maximum values for Settings parameters.
static const unsigned settings_max[HTTP2_SETTINGS_MAX] = {
0,
UINT_MAX, // HTTP2_SETTINGS_HEADER_TABLE_SIZE
1, // HTTP2_SETTINGS_ENABLE_PUSH
UINT_MAX, // HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
HTTP2_MAX_WINDOW_SIZE, // HTTP2_SETTINGS_INITIAL_WINDOW_SIZE
1, // HTTP2_SETTINGS_COMPRESS_DATA
};
if (param.id == 0 || param.id >= HTTP2_SETTINGS_MAX) {
return false;
}
if (param.value > settings_max[param.id]) {
return false;
}
return true;
}
// 4.1. Frame Format
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | R | Length (14) | Type (8) | Flags (8) |
// +-+-+-----------+---------------+-------------------------------+
// |R| Stream Identifier (31) |
// +-+-------------------------------------------------------------+
// | Frame Payload (0...) ...
// +---------------------------------------------------------------+
bool
http2_parse_frame_header(IOVec iov, Http2FrameHeader& hdr)
{
byte_pointer ptr(iov.iov_base);
byte_addressable_value<uint16_t> length;
byte_addressable_value<uint32_t> streamid;
if (unlikely(iov.iov_len < HTTP2_FRAME_HEADER_LEN)) {
return false;
}
memcpy_and_advance(length.bytes, ptr);
memcpy_and_advance(hdr.type, ptr);
memcpy_and_advance(hdr.flags, ptr);
memcpy_and_advance(streamid.bytes, ptr);
length.bytes[0] &= 0x3F; // Clear the 2 reserved high bits
streamid.bytes[0] &= 0x7f;// Clear the high reserved bit
hdr.length = ntohs(length.value);
hdr.streamid = ntohl(streamid.value);
return true;
}
bool
http2_write_frame_header(const Http2FrameHeader& hdr, IOVec iov)
{
byte_pointer ptr(iov.iov_base);
if (unlikely(iov.iov_len < HTTP2_FRAME_HEADER_LEN)) {
return false;
}
write_and_advance(ptr, hdr.length);
write_and_advance(ptr, hdr.type);
write_and_advance(ptr, hdr.flags);
write_and_advance(ptr, hdr.streamid);
return true;
}
// 6.8. GOAWAY
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |R| Last-Stream-ID (31) |
// +-+-------------------------------------------------------------+
// | Error Code (32) |
// +---------------------------------------------------------------+
// | Additional Debug Data (*) |
// +---------------------------------------------------------------+
bool
http2_write_goaway(const Http2Goaway& goaway, IOVec iov)
{
byte_pointer ptr(iov.iov_base);
if (unlikely(iov.iov_len < HTTP2_GOAWAY_LEN)) {
return false;
}
write_and_advance(ptr, goaway.last_streamid);
write_and_advance(ptr, goaway.error_code);
return true;
}
// 6.5.1. SETTINGS Format
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Identifier (8)| Value (32) |
// +---------------+-----------------------------------------------+
// | |
// +---------------+
bool
http2_parse_settings_parameter(IOVec iov, Http2SettingsParameter& param)
{
byte_pointer ptr(iov.iov_base);
byte_addressable_value<uint32_t> pval;
if (unlikely(iov.iov_len < HTTP2_SETTINGS_PARAMETER_LEN)) {
return false;
}
memcpy_and_advance(param.id, ptr);
memcpy_and_advance(pval.bytes, ptr);
param.value = ntohl(pval.value);
return true;
}