nghttp2_hd: Implement stream header inflater
This stream inflater can inflate incoming header block in streaming fashion. Currently, we buffer up single name/value pair, but we chose far more smaller buffer size than HTTP/2 frame size.
This commit is contained in:
parent
f8a446fbeb
commit
8317559090
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* nghttp2 - HTTP/2.0 C Library
|
* nghttp2 - HTTP/2.0 C Library
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
@ -27,217 +27,68 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "nghttp2_net.h"
|
|
||||||
#include "nghttp2_helper.h"
|
#include "nghttp2_helper.h"
|
||||||
|
|
||||||
void nghttp2_buffer_init(nghttp2_buffer *buffer, size_t chunk_capacity)
|
void nghttp2_buffer_init(nghttp2_buffer *buffer, size_t max_capacity)
|
||||||
{
|
{
|
||||||
buffer->root.data = NULL;
|
buffer->buf = NULL;
|
||||||
buffer->root.next = NULL;
|
|
||||||
buffer->current = &buffer->root;
|
|
||||||
buffer->capacity = chunk_capacity;
|
|
||||||
buffer->len = 0;
|
buffer->len = 0;
|
||||||
/*
|
buffer->capacity = 0;
|
||||||
* Set last_offset to maximum so that first append adds new buffer
|
buffer->max_capacity = max_capacity;
|
||||||
* buffer.
|
|
||||||
*/
|
|
||||||
buffer->last_offset = buffer->capacity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_buffer_free(nghttp2_buffer *buffer)
|
void nghttp2_buffer_free(nghttp2_buffer *buffer)
|
||||||
{
|
{
|
||||||
nghttp2_buffer_chunk *p = buffer->root.next;
|
free(buffer->buf);
|
||||||
while(p) {
|
|
||||||
nghttp2_buffer_chunk *next = p->next;
|
|
||||||
free(p->data);
|
|
||||||
free(p);
|
|
||||||
p = next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_buffer_alloc(nghttp2_buffer *buffer)
|
int nghttp2_buffer_reserve(nghttp2_buffer *buffer, size_t len)
|
||||||
{
|
{
|
||||||
if(buffer->current->next == NULL) {
|
if(len > buffer->max_capacity) {
|
||||||
nghttp2_buffer_chunk *chunk;
|
return NGHTTP2_ERR_BUFFER_ERROR;
|
||||||
uint8_t *buf;
|
}
|
||||||
chunk = malloc(sizeof(nghttp2_buffer_chunk));
|
if(buffer->capacity < len) {
|
||||||
if(chunk == NULL) {
|
uint8_t *new_buf;
|
||||||
return NGHTTP2_ERR_NOMEM;
|
size_t new_cap = buffer->capacity == 0 ? 32 : buffer->capacity * 3 / 2;
|
||||||
}
|
new_cap = nghttp2_min(buffer->max_capacity, nghttp2_max(new_cap, len));
|
||||||
buf = malloc(buffer->capacity);
|
new_buf = realloc(buffer->buf, new_cap);
|
||||||
if(buf == NULL) {
|
if(new_buf == NULL) {
|
||||||
free(chunk);
|
return NGHTTP2_ERR_NOMEM;
|
||||||
return NGHTTP2_ERR_NOMEM;
|
}
|
||||||
}
|
buffer->buf = new_buf;
|
||||||
chunk->data = buf;
|
buffer->capacity = new_cap;
|
||||||
chunk->next = NULL;
|
|
||||||
buffer->current->next = chunk;
|
|
||||||
buffer->current = chunk;
|
|
||||||
} else {
|
|
||||||
buffer->current = buffer->current->next;
|
|
||||||
}
|
}
|
||||||
buffer->len += buffer->capacity-buffer->last_offset;
|
|
||||||
buffer->last_offset = 0;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* nghttp2_buffer_get(nghttp2_buffer *buffer)
|
int nghttp2_buffer_add(nghttp2_buffer *buffer,
|
||||||
{
|
const uint8_t *data, size_t len)
|
||||||
if(buffer->current->data == NULL) {
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
return buffer->current->data+buffer->last_offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t nghttp2_buffer_avail(nghttp2_buffer *buffer)
|
|
||||||
{
|
|
||||||
return buffer->capacity-buffer->last_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_buffer_advance(nghttp2_buffer *buffer, size_t amount)
|
|
||||||
{
|
|
||||||
buffer->last_offset += amount;
|
|
||||||
buffer->len += amount;
|
|
||||||
assert(buffer->last_offset <= buffer->capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
int nghttp2_buffer_write(nghttp2_buffer *buffer, const uint8_t *data,
|
|
||||||
size_t len)
|
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
while(len) {
|
rv = nghttp2_buffer_reserve(buffer, buffer->len + len);
|
||||||
size_t writelen;
|
if(rv != 0) {
|
||||||
if(nghttp2_buffer_avail(buffer) == 0) {
|
return rv;
|
||||||
if((rv = nghttp2_buffer_alloc(buffer)) != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writelen = nghttp2_min(nghttp2_buffer_avail(buffer), len);
|
|
||||||
memcpy(nghttp2_buffer_get(buffer), data, writelen);
|
|
||||||
data += writelen;
|
|
||||||
len -= writelen;
|
|
||||||
nghttp2_buffer_advance(buffer, writelen);
|
|
||||||
}
|
}
|
||||||
|
memcpy(buffer->buf + buffer->len, data, len);
|
||||||
|
buffer->len += len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nghttp2_buffer_length(nghttp2_buffer *buffer)
|
int nghttp2_buffer_add_byte(nghttp2_buffer *buffer, uint8_t b)
|
||||||
{
|
{
|
||||||
return buffer->len;
|
int rv;
|
||||||
}
|
rv = nghttp2_buffer_reserve(buffer, buffer->len + 1);
|
||||||
|
if(rv != 0) {
|
||||||
size_t nghttp2_buffer_capacity(nghttp2_buffer *buffer)
|
return rv;
|
||||||
{
|
|
||||||
return buffer->capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_buffer_serialize(nghttp2_buffer *buffer, uint8_t *buf)
|
|
||||||
{
|
|
||||||
nghttp2_buffer_chunk *p = buffer->root.next;
|
|
||||||
for(; p; p = p->next) {
|
|
||||||
size_t len;
|
|
||||||
if(p == buffer->current) {
|
|
||||||
len = buffer->last_offset;
|
|
||||||
} else {
|
|
||||||
len = buffer->capacity;
|
|
||||||
}
|
|
||||||
memcpy(buf, p->data, len);
|
|
||||||
buf += len;
|
|
||||||
}
|
}
|
||||||
|
buffer->buf[buffer->len] = b;
|
||||||
|
++buffer->len;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_buffer_reset(nghttp2_buffer *buffer)
|
void nghttp2_buffer_release(nghttp2_buffer *buffer)
|
||||||
{
|
{
|
||||||
buffer->current = &buffer->root;
|
buffer->buf = NULL;
|
||||||
buffer->len = 0;
|
buffer->len = 0;
|
||||||
buffer->last_offset = buffer->capacity;
|
buffer->capacity = 0;
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_buffer_reader_init(nghttp2_buffer_reader *reader,
|
|
||||||
nghttp2_buffer *buffer)
|
|
||||||
{
|
|
||||||
reader->buffer = buffer;
|
|
||||||
reader->current = buffer->root.next;
|
|
||||||
reader->offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t nghttp2_buffer_reader_uint8(nghttp2_buffer_reader *reader)
|
|
||||||
{
|
|
||||||
uint8_t out;
|
|
||||||
nghttp2_buffer_reader_data(reader, &out, sizeof(uint8_t));
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t nghttp2_buffer_reader_uint16(nghttp2_buffer_reader *reader)
|
|
||||||
{
|
|
||||||
uint16_t out;
|
|
||||||
nghttp2_buffer_reader_data(reader, (uint8_t*)&out, sizeof(uint16_t));
|
|
||||||
return ntohs(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t nghttp2_buffer_reader_uint32(nghttp2_buffer_reader *reader)
|
|
||||||
{
|
|
||||||
uint32_t out;
|
|
||||||
nghttp2_buffer_reader_data(reader, (uint8_t*)&out, sizeof(uint32_t));
|
|
||||||
return ntohl(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_buffer_reader_data(nghttp2_buffer_reader *reader,
|
|
||||||
uint8_t *out, size_t len)
|
|
||||||
{
|
|
||||||
while(len) {
|
|
||||||
size_t remlen, readlen;
|
|
||||||
remlen = reader->buffer->capacity - reader->offset;
|
|
||||||
readlen = nghttp2_min(remlen, len);
|
|
||||||
memcpy(out, reader->current->data + reader->offset, readlen);
|
|
||||||
out += readlen;
|
|
||||||
len -= readlen;
|
|
||||||
reader->offset += readlen;
|
|
||||||
if(reader->buffer->capacity == reader->offset) {
|
|
||||||
reader->current = reader->current->next;
|
|
||||||
reader->offset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int nghttp2_buffer_reader_count(nghttp2_buffer_reader *reader,
|
|
||||||
size_t len, uint8_t c)
|
|
||||||
{
|
|
||||||
int res = 0;
|
|
||||||
while(len) {
|
|
||||||
size_t remlen, readlen, i;
|
|
||||||
uint8_t *p;
|
|
||||||
remlen = reader->buffer->capacity - reader->offset;
|
|
||||||
readlen = nghttp2_min(remlen, len);
|
|
||||||
p = reader->current->data + reader->offset;
|
|
||||||
for(i = 0; i < readlen; ++i) {
|
|
||||||
if(p[i] == c) {
|
|
||||||
++res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
len -= readlen;
|
|
||||||
reader->offset += readlen;
|
|
||||||
if(reader->buffer->capacity == reader->offset) {
|
|
||||||
reader->current = reader->current->next;
|
|
||||||
reader->offset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nghttp2_buffer_reader_advance(nghttp2_buffer_reader *reader,
|
|
||||||
size_t amount)
|
|
||||||
{
|
|
||||||
while(amount) {
|
|
||||||
size_t remlen, skiplen;
|
|
||||||
remlen = reader->buffer->capacity - reader->offset;
|
|
||||||
skiplen = nghttp2_min(remlen, amount);
|
|
||||||
amount -= skiplen;
|
|
||||||
reader->offset += skiplen;
|
|
||||||
if(reader->buffer->capacity == reader->offset) {
|
|
||||||
reader->current = reader->current->next;
|
|
||||||
reader->offset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* nghttp2 - HTTP/2.0 C Library
|
* nghttp2 - HTTP/2.0 C Library
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
@ -31,140 +31,73 @@
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
typedef struct nghttp2_buffer_chunk {
|
#include "nghttp2_int.h"
|
||||||
uint8_t *data;
|
|
||||||
struct nghttp2_buffer_chunk *next;
|
|
||||||
} nghttp2_buffer_chunk;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List of fixed sized chunks
|
* Byte array buffer
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* Capacity of each chunk buffer */
|
uint8_t *buf;
|
||||||
|
/* Capacity of this buffer */
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
/* Root of list of chunk buffers. The root is dummy and its data
|
/* How many bytes are written to buf. len <= capacity must hold. */
|
||||||
member is always NULL. */
|
|
||||||
nghttp2_buffer_chunk root;
|
|
||||||
/* Points to the current chunk to write */
|
|
||||||
nghttp2_buffer_chunk *current;
|
|
||||||
/* Total length of this buffer */
|
|
||||||
size_t len;
|
size_t len;
|
||||||
/* Offset of last chunk buffer */
|
/* Maximum capacity this buffer can grow up */
|
||||||
size_t last_offset;
|
size_t max_capacity;
|
||||||
} nghttp2_buffer;
|
} nghttp2_buffer;
|
||||||
|
|
||||||
/*
|
void nghttp2_buffer_init(nghttp2_buffer *buffer, size_t max_capacity);
|
||||||
* Initializes buffer with fixed chunk size chunk_capacity.
|
|
||||||
*/
|
|
||||||
void nghttp2_buffer_init(nghttp2_buffer *buffer, size_t chunk_capacity);
|
|
||||||
/* Releases allocated memory for buffer */
|
|
||||||
void nghttp2_buffer_free(nghttp2_buffer *buffer);
|
void nghttp2_buffer_free(nghttp2_buffer *buffer);
|
||||||
/* Returns buffer pointer */
|
|
||||||
uint8_t* nghttp2_buffer_get(nghttp2_buffer *buffer);
|
|
||||||
/* Returns available buffer length */
|
|
||||||
size_t nghttp2_buffer_avail(nghttp2_buffer *buffer);
|
|
||||||
/* Advances buffer pointer by amount. This reduces available buffer
|
|
||||||
length. */
|
|
||||||
void nghttp2_buffer_advance(nghttp2_buffer *buffer, size_t amount);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Writes the |data| with the |len| bytes starting at the current
|
* Expands capacity so that it can contain at least |len| bytes of
|
||||||
* position of the |buffer|. The new chunk buffer will be allocated on
|
* data. If buffer->capacity >= len, no action is taken. If len >
|
||||||
* the course of the write and the current position is updated. If
|
* buffer->max_capacity, NGHTTP2_ERR_BUFFER_ERROR is returned.
|
||||||
* this function succeeds, the total length of the |buffer| will be
|
|
||||||
* increased by |len|.
|
|
||||||
*
|
*
|
||||||
* This function returns 0 if it succeeds, or one of the following
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
* negative error codes:
|
* negative error codes:
|
||||||
*
|
*
|
||||||
|
* NGHTTP2_ERR_BUFFER_ERROR
|
||||||
|
* The |len| is strictly larger than buffer->max_capacity
|
||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory
|
||||||
*/
|
*/
|
||||||
int nghttp2_buffer_write(nghttp2_buffer *buffer, const uint8_t *data,
|
int nghttp2_buffer_reserve(nghttp2_buffer *buffer, size_t len);
|
||||||
size_t len);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate new chunk buffer. This will increase total length of
|
* Appends the |data| with |len| bytes to the buffer. The data is
|
||||||
* buffer (returned by nghttp2_buffer_length) by capacity-last_offset.
|
* copied. The |buffer| will be expanded as needed.
|
||||||
* It means untouched buffer is assumued to be written.
|
|
||||||
*
|
*
|
||||||
* This function returns 0 if it succeeds, or one of the following
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
* negative eror codes:
|
* negative error codes:
|
||||||
*
|
*
|
||||||
|
* NGHTTP2_ERR_BUFFER_ERROR
|
||||||
|
* The |len| is strictly larger than buffer->max_capacity
|
||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory
|
||||||
*/
|
*/
|
||||||
int nghttp2_buffer_alloc(nghttp2_buffer *buffer);
|
int nghttp2_buffer_add(nghttp2_buffer *buffer,
|
||||||
|
const uint8_t *data, size_t len);
|
||||||
/* Returns total length of buffer */
|
|
||||||
size_t nghttp2_buffer_length(nghttp2_buffer *buffer);
|
|
||||||
|
|
||||||
/* Returns capacity of each fixed chunk buffer */
|
|
||||||
size_t nghttp2_buffer_capacity(nghttp2_buffer *buffer);
|
|
||||||
|
|
||||||
/* Stores the contents of buffer into |buf|. |buf| must be at least
|
|
||||||
nghttp2_buffer_length(buffer) bytes long. */
|
|
||||||
void nghttp2_buffer_serialize(nghttp2_buffer *buffer, uint8_t *buf);
|
|
||||||
|
|
||||||
/* Reset |buffer| for reuse. Set the total length of buffer to 0.
|
|
||||||
Next nghttp2_buffer_avail() returns 0. This function does not free
|
|
||||||
allocated memory space; they are reused. */
|
|
||||||
void nghttp2_buffer_reset(nghttp2_buffer *buffer);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reader interface to read data from nghttp2_buffer sequentially.
|
* Appends the a single byte|b| to the buffer. The data is copied. The
|
||||||
|
* |buffer| will be expanded as needed.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
|
* negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_BUFFER_ERROR
|
||||||
|
* The |len| is strictly larger than buffer->max_capacity
|
||||||
|
* NGHTTP2_ERR_NOMEM
|
||||||
|
* Out of memory
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
int nghttp2_buffer_add_byte(nghttp2_buffer *buffer, uint8_t b);
|
||||||
/* The buffer to read */
|
|
||||||
nghttp2_buffer *buffer;
|
|
||||||
/* Pointer to the current chunk to read. */
|
|
||||||
nghttp2_buffer_chunk *current;
|
|
||||||
/* Offset to the current chunk data to read. */
|
|
||||||
size_t offset;
|
|
||||||
} nghttp2_buffer_reader;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes the |reader| with the |buffer|.
|
* Releases the buffer without freeing it. The data members in buffer
|
||||||
|
* is initialized.
|
||||||
*/
|
*/
|
||||||
void nghttp2_buffer_reader_init(nghttp2_buffer_reader *reader,
|
void nghttp2_buffer_release(nghttp2_buffer *buffer);
|
||||||
nghttp2_buffer *buffer);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads 1 byte and return it. This function will advance the current
|
|
||||||
* position by 1.
|
|
||||||
*/
|
|
||||||
uint8_t nghttp2_buffer_reader_uint8(nghttp2_buffer_reader *reader);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads 2 bytes integer in network byte order and returns it in host
|
|
||||||
* byte order. This function will advance the current position by 2.
|
|
||||||
*/
|
|
||||||
uint16_t nghttp2_buffer_reader_uint16(nghttp2_buffer_reader *reader);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads 4 bytes integer in network byte order and returns it in host
|
|
||||||
* byte order. This function will advance the current position by 4.
|
|
||||||
*/
|
|
||||||
uint32_t nghttp2_buffer_reader_uint32(nghttp2_buffer_reader *reader);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads |len| bytes and store them in the |out|. This function will
|
|
||||||
* advance the current position by |len|.
|
|
||||||
*/
|
|
||||||
void nghttp2_buffer_reader_data(nghttp2_buffer_reader *reader,
|
|
||||||
uint8_t *out, size_t len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads |len| bytes and count the occurrence of |c| there and return
|
|
||||||
* it. This function will advance the current position by |len|.
|
|
||||||
*/
|
|
||||||
int nghttp2_buffer_reader_count(nghttp2_buffer_reader *reader,
|
|
||||||
size_t len, uint8_t c);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Advances the current position by |amount|.
|
|
||||||
*/
|
|
||||||
void nghttp2_buffer_reader_advance(nghttp2_buffer_reader *reader,
|
|
||||||
size_t amount);
|
|
||||||
|
|
||||||
#endif /* NGHTTP2_BUFFER_H */
|
#endif /* NGHTTP2_BUFFER_H */
|
||||||
|
|
821
lib/nghttp2_hd.c
821
lib/nghttp2_hd.c
|
@ -331,8 +331,22 @@ int nghttp2_hd_deflate_init2(nghttp2_hd_context *deflater,
|
||||||
|
|
||||||
int nghttp2_hd_inflate_init(nghttp2_hd_context *inflater, nghttp2_hd_side side)
|
int nghttp2_hd_inflate_init(nghttp2_hd_context *inflater, nghttp2_hd_side side)
|
||||||
{
|
{
|
||||||
return nghttp2_hd_context_init(inflater, NGHTTP2_HD_ROLE_INFLATE, side,
|
int rv;
|
||||||
NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE);
|
rv = nghttp2_hd_context_init(inflater, NGHTTP2_HD_ROLE_INFLATE, side,
|
||||||
|
NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE);
|
||||||
|
if(rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
inflater->opcode = NGHTTP2_HD_OPCODE_NONE;
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||||
|
nghttp2_buffer_init(&inflater->namebuf, NGHTTP2_HD_MAX_NAME);
|
||||||
|
nghttp2_buffer_init(&inflater->valuebuf, NGHTTP2_HD_MAX_VALUE);
|
||||||
|
inflater->huffman_encoded = 0;
|
||||||
|
inflater->index = 0;
|
||||||
|
inflater->left = 0;
|
||||||
|
inflater->index_required = 0;
|
||||||
|
inflater->ent_name = NULL;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hd_inflate_keep_free(nghttp2_hd_context *inflater)
|
static void hd_inflate_keep_free(nghttp2_hd_context *inflater)
|
||||||
|
@ -363,6 +377,8 @@ void nghttp2_hd_deflate_free(nghttp2_hd_context *deflater)
|
||||||
void nghttp2_hd_inflate_free(nghttp2_hd_context *inflater)
|
void nghttp2_hd_inflate_free(nghttp2_hd_context *inflater)
|
||||||
{
|
{
|
||||||
hd_inflate_keep_free(inflater);
|
hd_inflate_keep_free(inflater);
|
||||||
|
nghttp2_buffer_free(&inflater->namebuf);
|
||||||
|
nghttp2_buffer_free(&inflater->valuebuf);
|
||||||
nghttp2_hd_context_free(inflater);
|
nghttp2_hd_context_free(inflater);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,28 +510,39 @@ static size_t encode_length(uint8_t *buf, size_t n, int prefix)
|
||||||
* region from |in|. The decoded integer must be strictly less than 1
|
* region from |in|. The decoded integer must be strictly less than 1
|
||||||
* << 16.
|
* << 16.
|
||||||
*
|
*
|
||||||
|
* If the |initial| is nonzero, it is used as a initial value, this
|
||||||
|
* function assumes the |in| starts with intermediate data.
|
||||||
|
*
|
||||||
|
* An entire integer is decoded successfully, decoded, the |*final| is
|
||||||
|
* set to nonzero.
|
||||||
|
*
|
||||||
* This function returns the next byte of read byte. This function
|
* This function returns the next byte of read byte. This function
|
||||||
* stores the decoded integer in |*res| if it succeeds, or stores -1
|
* stores the decoded integer in |*res| if it succeed, including
|
||||||
* in |*res|, indicating decoding error.
|
* partial decoding, or stores -1 in |*res|, indicating decoding
|
||||||
|
* error.
|
||||||
*/
|
*/
|
||||||
static uint8_t* decode_length(ssize_t *res, uint8_t *in, uint8_t *last,
|
static uint8_t* decode_length(ssize_t *res, int *final, ssize_t initial,
|
||||||
int prefix)
|
uint8_t *in, uint8_t *last, int prefix)
|
||||||
{
|
{
|
||||||
int k = (1 << prefix) - 1, r;
|
int k = (1 << prefix) - 1, r;
|
||||||
if(in == last) {
|
ssize_t n = initial;
|
||||||
*res = -1;
|
*final = 0;
|
||||||
|
if(n == 0) {
|
||||||
|
if((*in & k) == k) {
|
||||||
|
n = k;
|
||||||
|
} else {
|
||||||
|
*res = (*in) & k;
|
||||||
|
*final = 1;
|
||||||
|
return in + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(++in == last) {
|
||||||
|
*res = n;
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
if((*in & k) == k) {
|
|
||||||
*res = k;
|
|
||||||
} else {
|
|
||||||
*res = (*in) & k;
|
|
||||||
return in + 1;
|
|
||||||
}
|
|
||||||
++in;
|
|
||||||
for(r = 0; in != last; ++in, r += 7) {
|
for(r = 0; in != last; ++in, r += 7) {
|
||||||
*res += (*in & 0x7f) << r;
|
n += (*in & 0x7f) << r;
|
||||||
if(*res >= (1 << 16)) {
|
if(n >= (1 << 16)) {
|
||||||
*res = -1;
|
*res = -1;
|
||||||
return in + 1;
|
return in + 1;
|
||||||
}
|
}
|
||||||
|
@ -523,12 +550,17 @@ static uint8_t* decode_length(ssize_t *res, uint8_t *in, uint8_t *last,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(in == last || *in & (1 << 7)) {
|
if(in == last) {
|
||||||
|
*res = n;
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
if(*in & (1 << 7)) {
|
||||||
*res = -1;
|
*res = -1;
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
return in + 1;
|
return in + 1;
|
||||||
}
|
}
|
||||||
|
*res = n;
|
||||||
|
*final = 1;
|
||||||
|
return in + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int emit_indexed0(uint8_t **buf_ptr, size_t *buflen_ptr,
|
static int emit_indexed0(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||||
|
@ -911,6 +943,11 @@ static int check_index_range(nghttp2_hd_context *context, size_t index)
|
||||||
return index < context->hd_table.len + STATIC_TABLE_LENGTH;
|
return index < context->hd_table.len + STATIC_TABLE_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int get_max_index(nghttp2_hd_context *context)
|
||||||
|
{
|
||||||
|
return context->hd_table.len + STATIC_TABLE_LENGTH - 1;
|
||||||
|
}
|
||||||
|
|
||||||
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||||
size_t index)
|
size_t index)
|
||||||
{
|
{
|
||||||
|
@ -1128,274 +1165,502 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
|
static void hd_inflate_set_huffman_encoded(nghttp2_hd_context *inflater,
|
||||||
nghttp2_nv *nv_out, int *final,
|
const uint8_t *in)
|
||||||
uint8_t *in, size_t inlen)
|
|
||||||
{
|
{
|
||||||
int rv = 0;
|
inflater->huffman_encoded = (*in & (1 << 7)) != 0;
|
||||||
uint8_t *first = in;
|
}
|
||||||
uint8_t *last = in + inlen;
|
|
||||||
|
|
||||||
DEBUGF(fprintf(stderr, "infalte_hd start\n"));
|
/*
|
||||||
if(inflater->bad) {
|
* Decodes the integer from the range [in, last). The result is
|
||||||
|
* assigned to |inflater->left|. If the |inflater->left| is 0, then
|
||||||
|
* it performs variable integer decoding from scratch. Otherwise, it
|
||||||
|
* uses the |inflater->left| as the initial value and continues to
|
||||||
|
* decode assuming that [in, last) begins with intermediary sequence.
|
||||||
|
*
|
||||||
|
* This function returns the number of bytes read if it succeeds, or
|
||||||
|
* one of the following negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_HEADER_COMP
|
||||||
|
* Integer decoding failed
|
||||||
|
*/
|
||||||
|
static ssize_t hd_inflate_read_len(nghttp2_hd_context *inflater,
|
||||||
|
int *rfin,
|
||||||
|
uint8_t *in, uint8_t *last,
|
||||||
|
int prefix, size_t maxlen)
|
||||||
|
{
|
||||||
|
uint8_t *nin;
|
||||||
|
*rfin = 0;
|
||||||
|
nin = decode_length(&inflater->left, rfin, inflater->left, in, last, prefix);
|
||||||
|
if(inflater->left == -1) {
|
||||||
|
DEBUGF(fprintf(stderr, "invalid integer\n"));
|
||||||
return NGHTTP2_ERR_HEADER_COMP;
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
}
|
}
|
||||||
|
if((size_t)inflater->left > maxlen) {
|
||||||
*final = 0;
|
DEBUGF(fprintf(stderr, "integer exceeds the maximum value %zu\n", maxlen));
|
||||||
hd_inflate_keep_free(inflater);
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
|
|
||||||
for(; in != last;) {
|
|
||||||
uint8_t c = *in;
|
|
||||||
if(c & 0x80u) {
|
|
||||||
/* Indexed Header Repr */
|
|
||||||
ssize_t index;
|
|
||||||
nghttp2_hd_entry *ent;
|
|
||||||
in = decode_length(&index, in, last, 7);
|
|
||||||
DEBUGF(fprintf(stderr, "Indexed repr index=%zd\n", index));
|
|
||||||
if(index < 0) {
|
|
||||||
DEBUGF(fprintf(stderr, "Index out of range index=%zd\n", index));
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if(index == 0) {
|
|
||||||
DEBUGF(fprintf(stderr, "Clearing reference set\n"));
|
|
||||||
clear_refset(inflater);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
--index;
|
|
||||||
if(!check_index_range(inflater, index)) {
|
|
||||||
DEBUGF(fprintf(stderr, "Index out of range index=%zd\n", index));
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ent = nghttp2_hd_table_get(inflater, index);
|
|
||||||
if(index >= (ssize_t)inflater->hd_table.len) {
|
|
||||||
nghttp2_hd_entry *new_ent;
|
|
||||||
new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL,
|
|
||||||
&ent->nv, NGHTTP2_HD_FLAG_NONE);
|
|
||||||
if(!new_ent) {
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
/* new_ent->ref == 0 may be hold but emit_indexed_header
|
|
||||||
tracks new_ent, so there is no leak. */
|
|
||||||
emit_indexed_header(inflater, nv_out, new_ent);
|
|
||||||
inflater->ent_keep = new_ent;
|
|
||||||
return in - first;
|
|
||||||
} else {
|
|
||||||
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
|
|
||||||
if(ent->flags & NGHTTP2_HD_FLAG_REFSET) {
|
|
||||||
emit_indexed_header(inflater, nv_out, ent);
|
|
||||||
return in - first;
|
|
||||||
} else {
|
|
||||||
DEBUGF(fprintf(stderr, "Toggle off item:\n"));
|
|
||||||
DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr));
|
|
||||||
DEBUGF(fprintf(stderr, ": "));
|
|
||||||
DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr));
|
|
||||||
DEBUGF(fprintf(stderr, "\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rv != 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
} else if(c == 0x40u || c == 0) {
|
|
||||||
/* Literal Header Repr - New Name */
|
|
||||||
nghttp2_nv nv;
|
|
||||||
ssize_t namelen, valuelen;
|
|
||||||
int name_huffman, value_huffman;
|
|
||||||
uint8_t *decoded_huffman_name = NULL, *decoded_huffman_value = NULL;
|
|
||||||
DEBUGF(fprintf(stderr, "Literal header repr - new name\n"));
|
|
||||||
if(++in == last) {
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
name_huffman = *in & (1 << 7);
|
|
||||||
in = decode_length(&namelen, in, last, 7);
|
|
||||||
if(namelen < 0 || in + namelen > last) {
|
|
||||||
DEBUGF(fprintf(stderr, "Invalid namelen=%zd\n", namelen));
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if(name_huffman) {
|
|
||||||
rv = nghttp2_hd_huff_decode(&nv.name, in, namelen, inflater->side);
|
|
||||||
if(rv < 0) {
|
|
||||||
DEBUGF(fprintf(stderr, "Name huffman decoding failed\n"));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
decoded_huffman_name = nv.name;
|
|
||||||
nv.namelen = rv;
|
|
||||||
} else {
|
|
||||||
nv.name = in;
|
|
||||||
nv.namelen = namelen;
|
|
||||||
}
|
|
||||||
in += namelen;
|
|
||||||
|
|
||||||
if(in == last) {
|
|
||||||
DEBUGF(fprintf(stderr, "No value found\n"));
|
|
||||||
free(decoded_huffman_name);
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
value_huffman = *in & (1 << 7);
|
|
||||||
in = decode_length(&valuelen, in, last, 7);
|
|
||||||
if(valuelen < 0 || in + valuelen > last) {
|
|
||||||
DEBUGF(fprintf(stderr, "Invalid valuelen=%zd\n", valuelen));
|
|
||||||
free(decoded_huffman_name);
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if(value_huffman) {
|
|
||||||
rv = nghttp2_hd_huff_decode(&nv.value, in, valuelen, inflater->side);
|
|
||||||
if(rv < 0) {
|
|
||||||
DEBUGF(fprintf(stderr, "Value huffman decoding failed\n"));
|
|
||||||
free(decoded_huffman_name);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
decoded_huffman_value = nv.value;
|
|
||||||
nv.valuelen = rv;
|
|
||||||
} else {
|
|
||||||
nv.value = in;
|
|
||||||
nv.valuelen = valuelen;
|
|
||||||
}
|
|
||||||
in += valuelen;
|
|
||||||
|
|
||||||
if(c == 0x40u) {
|
|
||||||
int flags = NGHTTP2_HD_FLAG_NONE;
|
|
||||||
if(name_huffman) {
|
|
||||||
flags |= NGHTTP2_HD_FLAG_NAME_GIFT;
|
|
||||||
}
|
|
||||||
if(value_huffman) {
|
|
||||||
flags |= NGHTTP2_HD_FLAG_VALUE_GIFT;
|
|
||||||
}
|
|
||||||
emit_newname_header(inflater, nv_out, &nv);
|
|
||||||
inflater->name_keep = decoded_huffman_name;
|
|
||||||
inflater->value_keep = decoded_huffman_value;
|
|
||||||
return in - first;
|
|
||||||
} else {
|
|
||||||
nghttp2_hd_entry *new_ent;
|
|
||||||
uint8_t ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC |
|
|
||||||
NGHTTP2_HD_FLAG_VALUE_ALLOC;
|
|
||||||
if(name_huffman) {
|
|
||||||
ent_flags |= NGHTTP2_HD_FLAG_NAME_GIFT;
|
|
||||||
}
|
|
||||||
if(value_huffman) {
|
|
||||||
ent_flags |= NGHTTP2_HD_FLAG_VALUE_GIFT;
|
|
||||||
}
|
|
||||||
new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv,
|
|
||||||
ent_flags);
|
|
||||||
if(new_ent) {
|
|
||||||
emit_indexed_header(inflater, nv_out, new_ent);
|
|
||||||
inflater->ent_keep = new_ent;
|
|
||||||
return in - first;
|
|
||||||
} else {
|
|
||||||
free(decoded_huffman_name);
|
|
||||||
free(decoded_huffman_value);
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rv != 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Literal Header Repr - Indexed Name */
|
|
||||||
nghttp2_hd_entry *ent;
|
|
||||||
uint8_t *value;
|
|
||||||
ssize_t valuelen, index;
|
|
||||||
int value_huffman;
|
|
||||||
uint8_t *decoded_huffman_value = NULL;
|
|
||||||
DEBUGF(fprintf(stderr, "Literal header repr - indexed name\n"));
|
|
||||||
in = decode_length(&index, in, last, 6);
|
|
||||||
if(index <= 0) {
|
|
||||||
DEBUGF(fprintf(stderr, "Index out of range index=%zd\n", index));
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
--index;
|
|
||||||
if(!check_index_range(inflater, index)) {
|
|
||||||
DEBUGF(fprintf(stderr, "Index out of range index=%zd\n", index));
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ent = nghttp2_hd_table_get(inflater, index);
|
|
||||||
if(in == last) {
|
|
||||||
DEBUGF(fprintf(stderr, "No value found\n"));
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
value_huffman = *in & (1 << 7);
|
|
||||||
in = decode_length(&valuelen, in , last, 7);
|
|
||||||
if(valuelen < 0 || in + valuelen > last) {
|
|
||||||
DEBUGF(fprintf(stderr, "Invalid valuelen=%zd\n", valuelen));
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if(value_huffman) {
|
|
||||||
rv = nghttp2_hd_huff_decode(&value, in, valuelen, inflater->side);
|
|
||||||
if(rv < 0) {
|
|
||||||
DEBUGF(fprintf(stderr, "Value huffman decoding failed\n"));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
decoded_huffman_value = value;
|
|
||||||
in += valuelen;
|
|
||||||
valuelen = rv;
|
|
||||||
} else {
|
|
||||||
value = in;
|
|
||||||
in += valuelen;
|
|
||||||
}
|
|
||||||
if((c & 0x40u) == 0x40u) {
|
|
||||||
emit_indname_header(inflater, nv_out, ent, value, valuelen);
|
|
||||||
inflater->value_keep = decoded_huffman_value;
|
|
||||||
return in - first;
|
|
||||||
} else {
|
|
||||||
nghttp2_nv nv;
|
|
||||||
nghttp2_hd_entry *new_ent;
|
|
||||||
uint8_t ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC;
|
|
||||||
if(value_huffman) {
|
|
||||||
ent_flags |= NGHTTP2_HD_FLAG_VALUE_GIFT;
|
|
||||||
}
|
|
||||||
++ent->ref;
|
|
||||||
nv.name = ent->nv.name;
|
|
||||||
if((size_t)index < inflater->hd_table.len) {
|
|
||||||
ent_flags |= NGHTTP2_HD_FLAG_NAME_ALLOC;
|
|
||||||
}
|
|
||||||
nv.namelen = ent->nv.namelen;
|
|
||||||
nv.value = value;
|
|
||||||
nv.valuelen = valuelen;
|
|
||||||
new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv,
|
|
||||||
ent_flags);
|
|
||||||
if(--ent->ref == 0) {
|
|
||||||
nghttp2_hd_entry_free(ent);
|
|
||||||
free(ent);
|
|
||||||
}
|
|
||||||
if(new_ent) {
|
|
||||||
emit_indexed_header(inflater, nv_out, new_ent);
|
|
||||||
inflater->ent_keep = new_ent;
|
|
||||||
return in - first;
|
|
||||||
} else {
|
|
||||||
free(decoded_huffman_value);
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rv != 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return nin - in;
|
||||||
|
}
|
||||||
|
|
||||||
for(; inflater->end_headers_index < inflater->hd_table.len;
|
/*
|
||||||
++inflater->end_headers_index) {
|
* Reads |inflater->left| bytes from the range [in, last) and performs
|
||||||
nghttp2_hd_entry *ent;
|
* huffman decoding against them and pushes the result into the
|
||||||
ent = nghttp2_hd_ringbuf_get(&inflater->hd_table,
|
* |buffer|.
|
||||||
inflater->end_headers_index);
|
*
|
||||||
|
* This function returns the number of bytes read if it succeeds, or
|
||||||
|
* one of the following negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_NOMEM
|
||||||
|
* Out of memory
|
||||||
|
* NGHTTP2_ERR_HEADER_COMP
|
||||||
|
* Huffman decoding failed
|
||||||
|
*/
|
||||||
|
static ssize_t hd_inflate_read_huff(nghttp2_hd_context *inflater,
|
||||||
|
nghttp2_buffer *buffer,
|
||||||
|
uint8_t *in, uint8_t *last)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
int final = 0;
|
||||||
|
if(last - in >= inflater->left) {
|
||||||
|
last = in + inflater->left;
|
||||||
|
final = 1;
|
||||||
|
}
|
||||||
|
rv = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, buffer,
|
||||||
|
in, last - in, final);
|
||||||
|
if(rv == NGHTTP2_ERR_BUFFER_ERROR) {
|
||||||
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
|
}
|
||||||
|
if(rv < 0) {
|
||||||
|
DEBUGF(fprintf(stderr, "huffman decoding failed\n"));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
inflater->left -= rv;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
|
/*
|
||||||
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
|
* Reads |inflater->left| bytes from the range [in, last) and copies
|
||||||
emit_indexed_header(inflater, nv_out, ent);
|
* them into the |buffer|.
|
||||||
|
*
|
||||||
|
* This function returns the number of bytes read if it succeeds, or
|
||||||
|
* one of the following negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_NOMEM
|
||||||
|
* Out of memory
|
||||||
|
* NGHTTP2_ERR_HEADER_COMP
|
||||||
|
* Header decompression failed
|
||||||
|
*/
|
||||||
|
static ssize_t hd_inflate_read(nghttp2_hd_context *inflater,
|
||||||
|
nghttp2_buffer *buffer,
|
||||||
|
uint8_t *in, uint8_t *last)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
size_t len = nghttp2_min(last - in, inflater->left);
|
||||||
|
rv = nghttp2_buffer_add(buffer, in, len);
|
||||||
|
if(rv == NGHTTP2_ERR_BUFFER_ERROR) {
|
||||||
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
|
}
|
||||||
|
if(rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
inflater->left -= len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finalize indexed header representation reception. If header is
|
||||||
|
* emitted, |*nv_out| is filled with that value and 0 is returned. If
|
||||||
|
* no header is emitted, 1 is returned.
|
||||||
|
*
|
||||||
|
* This function returns either 0 or 1 if it succeeds, or one of the
|
||||||
|
* following negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_NOMEM
|
||||||
|
* Out of memory
|
||||||
|
*/
|
||||||
|
static int hd_inflate_commit_indexed(nghttp2_hd_context *inflater,
|
||||||
|
nghttp2_nv *nv_out)
|
||||||
|
{
|
||||||
|
nghttp2_hd_entry *ent = nghttp2_hd_table_get(inflater, inflater->index);
|
||||||
|
if(inflater->index >= inflater->hd_table.len) {
|
||||||
|
nghttp2_hd_entry *new_ent;
|
||||||
|
new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL,
|
||||||
|
&ent->nv, NGHTTP2_HD_FLAG_NONE);
|
||||||
|
if(!new_ent) {
|
||||||
|
return NGHTTP2_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
/* new_ent->ref == 0 may be hold */
|
||||||
|
emit_indexed_header(inflater, nv_out, new_ent);
|
||||||
|
inflater->ent_keep = new_ent;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
|
||||||
|
if(ent->flags & NGHTTP2_HD_FLAG_REFSET) {
|
||||||
|
emit_indexed_header(inflater, nv_out, ent);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEBUGF(fprintf(stderr, "Toggle off item:\n"));
|
||||||
|
DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr));
|
||||||
|
DEBUGF(fprintf(stderr, ": "));
|
||||||
|
DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr));
|
||||||
|
DEBUGF(fprintf(stderr, "\n"));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finalize literal header representation - new name- reception. If
|
||||||
|
* header is emitted, |*nv_out| is filled with that value and 0 is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
|
* negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_NOMEM
|
||||||
|
* Out of memory
|
||||||
|
*/
|
||||||
|
static int hd_inflate_commit_newname(nghttp2_hd_context *inflater,
|
||||||
|
nghttp2_nv *nv_out)
|
||||||
|
{
|
||||||
|
nghttp2_nv nv = {
|
||||||
|
inflater->namebuf.buf,
|
||||||
|
inflater->valuebuf.buf,
|
||||||
|
inflater->namebuf.len,
|
||||||
|
inflater->valuebuf.len
|
||||||
|
};
|
||||||
|
if(inflater->index_required) {
|
||||||
|
nghttp2_hd_entry *new_ent;
|
||||||
|
uint8_t ent_flags =
|
||||||
|
NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC |
|
||||||
|
NGHTTP2_HD_FLAG_NAME_GIFT | NGHTTP2_HD_FLAG_VALUE_GIFT;
|
||||||
|
new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv,
|
||||||
|
ent_flags);
|
||||||
|
if(new_ent) {
|
||||||
|
nghttp2_buffer_release(&inflater->namebuf);
|
||||||
|
nghttp2_buffer_release(&inflater->valuebuf);
|
||||||
|
emit_indexed_header(inflater, nv_out, new_ent);
|
||||||
|
inflater->ent_keep = new_ent;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return NGHTTP2_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
emit_newname_header(inflater, nv_out, &nv);
|
||||||
|
inflater->name_keep = inflater->namebuf.buf;
|
||||||
|
nghttp2_buffer_release(&inflater->namebuf);
|
||||||
|
inflater->value_keep = inflater->valuebuf.buf;
|
||||||
|
nghttp2_buffer_release(&inflater->valuebuf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finalize literal header representation - indexed name-
|
||||||
|
* reception. If header is emitted, |*nv_out| is filled with that
|
||||||
|
* value and 0 is returned.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
|
* negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_NOMEM
|
||||||
|
* Out of memory
|
||||||
|
*/
|
||||||
|
static int hd_inflate_commit_indname(nghttp2_hd_context *inflater,
|
||||||
|
nghttp2_nv *nv_out)
|
||||||
|
{
|
||||||
|
if(inflater->index_required) {
|
||||||
|
nghttp2_nv nv;
|
||||||
|
nghttp2_hd_entry *new_ent;
|
||||||
|
uint8_t ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC |
|
||||||
|
NGHTTP2_HD_FLAG_VALUE_GIFT;
|
||||||
|
|
||||||
|
if(inflater->index < inflater->hd_table.len) {
|
||||||
|
ent_flags |= NGHTTP2_HD_FLAG_NAME_ALLOC;
|
||||||
|
}
|
||||||
|
++inflater->ent_name->ref;
|
||||||
|
nv.name = inflater->ent_name->nv.name;
|
||||||
|
nv.namelen = inflater->ent_name->nv.namelen;
|
||||||
|
nv.value = inflater->valuebuf.buf;
|
||||||
|
nv.valuelen = inflater->valuebuf.len;
|
||||||
|
new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv,
|
||||||
|
ent_flags);
|
||||||
|
if(--inflater->ent_name->ref == 0) {
|
||||||
|
nghttp2_hd_entry_free(inflater->ent_name);
|
||||||
|
free(inflater->ent_name);
|
||||||
|
}
|
||||||
|
inflater->ent_name = NULL;
|
||||||
|
if(new_ent) {
|
||||||
|
nghttp2_buffer_release(&inflater->valuebuf);
|
||||||
|
emit_indexed_header(inflater, nv_out, new_ent);
|
||||||
|
inflater->ent_keep = new_ent;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return NGHTTP2_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
emit_indname_header(inflater, nv_out, inflater->ent_name,
|
||||||
|
inflater->valuebuf.buf, inflater->valuebuf.len);
|
||||||
|
inflater->value_keep = inflater->valuebuf.buf;
|
||||||
|
nghttp2_buffer_release(&inflater->valuebuf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t guess_huff_decode_len(size_t encode_len)
|
||||||
|
{
|
||||||
|
return encode_len * 3 / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
|
||||||
|
nghttp2_nv *nv_out, int *inflate_flags,
|
||||||
|
uint8_t *in, size_t inlen, int in_final)
|
||||||
|
{
|
||||||
|
ssize_t rv = 0;
|
||||||
|
uint8_t *first = in;
|
||||||
|
uint8_t *last = in + inlen;
|
||||||
|
int rfin = 0;
|
||||||
|
|
||||||
|
DEBUGF(fprintf(stderr, "nghtp2_hd_infalte_hd start state=%d\n",
|
||||||
|
inflater->state));
|
||||||
|
*inflate_flags = NGHTTP2_HD_INFLATE_NONE;
|
||||||
|
for(; in != last;) {
|
||||||
|
switch(inflater->state) {
|
||||||
|
case NGHTTP2_HD_STATE_OPCODE:
|
||||||
|
if(*in & 0x80u) {
|
||||||
|
DEBUGF(fprintf(stderr, "Indexed repr\n"));
|
||||||
|
inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_READ_INDEX;
|
||||||
|
} else {
|
||||||
|
if(*in == 0x40 || *in == 0) {
|
||||||
|
DEBUGF(fprintf(stderr, "Literal header repr - new name\n"));
|
||||||
|
inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME;
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN;
|
||||||
|
} else {
|
||||||
|
DEBUGF(fprintf(stderr, "Literal header repr - indexed name\n"));
|
||||||
|
inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME;
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_READ_INDEX;
|
||||||
|
}
|
||||||
|
inflater->index_required = (*in & 0x40) == 0;
|
||||||
|
DEBUGF(fprintf(stderr, "indexing required=%d\n",
|
||||||
|
inflater->index_required != 0));
|
||||||
|
if(inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
|
||||||
|
++in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inflater->left = 0;
|
||||||
|
break;
|
||||||
|
case NGHTTP2_HD_STATE_READ_INDEX:
|
||||||
|
rfin = 0;
|
||||||
|
rv = hd_inflate_read_len(inflater, &rfin, in, last,
|
||||||
|
inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED ?
|
||||||
|
7 : 6,
|
||||||
|
get_max_index(inflater) + 1);
|
||||||
|
if(rv < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
in += rv;
|
||||||
|
if(!rfin) {
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
DEBUGF(fprintf(stderr, "index=%zd\n", inflater->left));
|
||||||
|
if(inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) {
|
||||||
|
inflater->index = inflater->left;
|
||||||
|
if(inflater->index == 0) {
|
||||||
|
DEBUGF(fprintf(stderr, "Clearing reference set\n"));
|
||||||
|
clear_refset(inflater);
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--inflater->index;
|
||||||
|
rv = hd_inflate_commit_indexed(inflater, nv_out);
|
||||||
|
if(rv < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||||
|
/* If rv == 1, no header was emitted */
|
||||||
|
if(rv == 0) {
|
||||||
|
*inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inflater->index = inflater->left;
|
||||||
|
--inflater->index;
|
||||||
|
inflater->ent_name = nghttp2_hd_table_get(inflater, inflater->index);
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN:
|
||||||
|
hd_inflate_set_huffman_encoded(inflater, in);
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN;
|
||||||
|
inflater->left = 0;
|
||||||
|
DEBUGF(fprintf(stderr, "huffman encoded=%d\n",
|
||||||
|
inflater->huffman_encoded != 0));
|
||||||
|
/* Fall through */
|
||||||
|
case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN:
|
||||||
|
rfin = 0;
|
||||||
|
rv = hd_inflate_read_len(inflater, &rfin, in, last, 7,
|
||||||
|
NGHTTP2_HD_MAX_NAME);
|
||||||
|
if(rv < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
in += rv;
|
||||||
|
if(!rfin) {
|
||||||
|
DEBUGF(fprintf(stderr, "integer not fully decoded. current=%zd\n",
|
||||||
|
inflater->left));
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
rv = 0;
|
||||||
|
if(inflater->huffman_encoded) {
|
||||||
|
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx,
|
||||||
|
inflater->side);
|
||||||
|
rv = nghttp2_buffer_reserve(&inflater->namebuf,
|
||||||
|
guess_huff_decode_len(inflater->left));
|
||||||
|
if(rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF;
|
||||||
|
} else {
|
||||||
|
rv = nghttp2_buffer_reserve(&inflater->namebuf, inflater->left);
|
||||||
|
if(rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF:
|
||||||
|
rv = hd_inflate_read_huff(inflater, &inflater->namebuf, in, last);
|
||||||
|
if(rv < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
in += rv;
|
||||||
|
DEBUGF(fprintf(stderr, "%zd bytes read\n", rv));
|
||||||
|
if(inflater->left) {
|
||||||
|
DEBUGF(fprintf(stderr, "still %zd bytes to go\n", inflater->left));
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
||||||
|
break;
|
||||||
|
case NGHTTP2_HD_STATE_NEWNAME_READ_NAME:
|
||||||
|
rv = hd_inflate_read(inflater, &inflater->namebuf, in, last);
|
||||||
|
if(rv < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
in += rv;
|
||||||
|
DEBUGF(fprintf(stderr, "%zd bytes read\n", rv));
|
||||||
|
if(inflater->left) {
|
||||||
|
DEBUGF(fprintf(stderr, "still %zd bytes to go\n", inflater->left));
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
||||||
|
break;
|
||||||
|
case NGHTTP2_HD_STATE_CHECK_VALUELEN:
|
||||||
|
hd_inflate_set_huffman_encoded(inflater, in);
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN;
|
||||||
|
inflater->left = 0;
|
||||||
|
DEBUGF(fprintf(stderr, "huffman encoded=%d\n",
|
||||||
|
inflater->huffman_encoded != 0));
|
||||||
|
/* Fall through */
|
||||||
|
case NGHTTP2_HD_STATE_READ_VALUELEN:
|
||||||
|
rfin = 0;
|
||||||
|
rv = hd_inflate_read_len(inflater, &rfin, in, last, 7,
|
||||||
|
NGHTTP2_HD_MAX_VALUE);
|
||||||
|
if(rv < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
in += rv;
|
||||||
|
if(!rfin) {
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
DEBUGF(fprintf(stderr, "valuelen=%zd\n", inflater->left));
|
||||||
|
if(inflater->left == 0) {
|
||||||
|
if(inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
|
||||||
|
rv = hd_inflate_commit_newname(inflater, nv_out);
|
||||||
|
} else {
|
||||||
|
rv = hd_inflate_commit_indname(inflater, nv_out);
|
||||||
|
}
|
||||||
|
if(rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||||
|
*inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
if(inflater->huffman_encoded) {
|
||||||
|
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx,
|
||||||
|
inflater->side);
|
||||||
|
rv = nghttp2_buffer_reserve(&inflater->valuebuf,
|
||||||
|
guess_huff_decode_len(inflater->left));
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF;
|
||||||
|
} else {
|
||||||
|
rv = nghttp2_buffer_reserve(&inflater->valuebuf, inflater->left);
|
||||||
|
if(rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_READ_VALUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NGHTTP2_HD_STATE_READ_VALUEHUFF:
|
||||||
|
rv = hd_inflate_read_huff(inflater, &inflater->valuebuf, in, last);
|
||||||
|
if(rv < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
in += rv;
|
||||||
|
DEBUGF(fprintf(stderr, "%zd bytes read\n", rv));
|
||||||
|
if(inflater->left) {
|
||||||
|
DEBUGF(fprintf(stderr, "still %zd bytes to go\n", inflater->left));
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
if(inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
|
||||||
|
rv = hd_inflate_commit_newname(inflater, nv_out);
|
||||||
|
} else {
|
||||||
|
rv = hd_inflate_commit_indname(inflater, nv_out);
|
||||||
|
}
|
||||||
|
if(rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||||
|
*inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
|
||||||
|
return in - first;
|
||||||
|
case NGHTTP2_HD_STATE_READ_VALUE:
|
||||||
|
rv = hd_inflate_read(inflater, &inflater->valuebuf, in, last);
|
||||||
|
if(rv < 0) {
|
||||||
|
DEBUGF(fprintf(stderr, "value read failure %zd: %s\n",
|
||||||
|
rv, nghttp2_strerror(rv)));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
in += rv;
|
||||||
|
DEBUGF(fprintf(stderr, "%zd bytes read\n", rv));
|
||||||
|
if(inflater->left) {
|
||||||
|
DEBUGF(fprintf(stderr, "still %zd bytes to go\n", inflater->left));
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
if(inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
|
||||||
|
rv = hd_inflate_commit_newname(inflater, nv_out);
|
||||||
|
} else {
|
||||||
|
rv = hd_inflate_commit_indname(inflater, nv_out);
|
||||||
|
}
|
||||||
|
if(rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||||
|
*inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
|
||||||
return in - first;
|
return in - first;
|
||||||
}
|
}
|
||||||
ent->flags &= ~NGHTTP2_HD_FLAG_EMIT;
|
|
||||||
}
|
}
|
||||||
*final = 1;
|
assert(in == last);
|
||||||
|
if(in_final) {
|
||||||
|
for(; inflater->end_headers_index < inflater->hd_table.len;
|
||||||
|
++inflater->end_headers_index) {
|
||||||
|
nghttp2_hd_entry *ent;
|
||||||
|
ent = nghttp2_hd_ringbuf_get(&inflater->hd_table,
|
||||||
|
inflater->end_headers_index);
|
||||||
|
|
||||||
|
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
|
||||||
|
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
|
||||||
|
emit_indexed_header(inflater, nv_out, ent);
|
||||||
|
*inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
|
||||||
|
return in - first;
|
||||||
|
}
|
||||||
|
ent->flags &= ~NGHTTP2_HD_FLAG_EMIT;
|
||||||
|
}
|
||||||
|
*inflate_flags |= NGHTTP2_HD_INFLATE_FINAL;
|
||||||
|
}
|
||||||
return in - first;
|
return in - first;
|
||||||
fail:
|
fail:
|
||||||
inflater->bad = 1;
|
inflater->bad = 1;
|
||||||
|
|
114
lib/nghttp2_hd.h
114
lib/nghttp2_hd.h
|
@ -31,9 +31,15 @@
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
|
#include "nghttp2_hd_huffman.h"
|
||||||
|
#include "nghttp2_buffer.h"
|
||||||
|
|
||||||
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE (1 << 12)
|
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE (1 << 12)
|
||||||
#define NGHTTP2_HD_ENTRY_OVERHEAD 32
|
#define NGHTTP2_HD_ENTRY_OVERHEAD 32
|
||||||
|
|
||||||
|
#define NGHTTP2_HD_MAX_NAME 256
|
||||||
|
#define NGHTTP2_HD_MAX_VALUE 4096
|
||||||
|
|
||||||
/* Default size of maximum table buffer size for encoder. Even if
|
/* Default size of maximum table buffer size for encoder. Even if
|
||||||
remote decoder notifies larger buffer size for its decoding,
|
remote decoder notifies larger buffer size for its decoding,
|
||||||
encoder only uses the memory up to this value. */
|
encoder only uses the memory up to this value. */
|
||||||
|
@ -90,6 +96,26 @@ typedef struct {
|
||||||
size_t len;
|
size_t len;
|
||||||
} nghttp2_hd_ringbuf;
|
} nghttp2_hd_ringbuf;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NGHTTP2_HD_OPCODE_NONE,
|
||||||
|
NGHTTP2_HD_OPCODE_INDEXED,
|
||||||
|
NGHTTP2_HD_OPCODE_NEWNAME,
|
||||||
|
NGHTTP2_HD_OPCODE_INDNAME
|
||||||
|
} nghttp2_hd_opcode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NGHTTP2_HD_STATE_OPCODE,
|
||||||
|
NGHTTP2_HD_STATE_READ_INDEX,
|
||||||
|
NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN,
|
||||||
|
NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN,
|
||||||
|
NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF,
|
||||||
|
NGHTTP2_HD_STATE_NEWNAME_READ_NAME,
|
||||||
|
NGHTTP2_HD_STATE_CHECK_VALUELEN,
|
||||||
|
NGHTTP2_HD_STATE_READ_VALUELEN,
|
||||||
|
NGHTTP2_HD_STATE_READ_VALUEHUFF,
|
||||||
|
NGHTTP2_HD_STATE_READ_VALUE,
|
||||||
|
} nghttp2_hd_inflate_state;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* dynamic header table */
|
/* dynamic header table */
|
||||||
nghttp2_hd_ringbuf hd_table;
|
nghttp2_hd_ringbuf hd_table;
|
||||||
|
@ -139,6 +165,17 @@ typedef struct {
|
||||||
/* Set to this nonzero to clear reference set on each deflation each
|
/* Set to this nonzero to clear reference set on each deflation each
|
||||||
time. */
|
time. */
|
||||||
uint8_t no_refset;
|
uint8_t no_refset;
|
||||||
|
/* Decoder specific members */
|
||||||
|
nghttp2_buffer namebuf;
|
||||||
|
nghttp2_buffer valuebuf;
|
||||||
|
nghttp2_hd_huff_decode_context huff_decode_ctx;
|
||||||
|
int state;
|
||||||
|
nghttp2_hd_opcode opcode;
|
||||||
|
uint8_t huffman_encoded;
|
||||||
|
uint8_t index_required;
|
||||||
|
ssize_t left;
|
||||||
|
size_t index;
|
||||||
|
nghttp2_hd_entry *ent_name;
|
||||||
} nghttp2_hd_context;
|
} nghttp2_hd_context;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -270,26 +307,34 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
|
||||||
size_t nv_offset,
|
size_t nv_offset,
|
||||||
nghttp2_nv *nva, size_t nvlen);
|
nghttp2_nv *nva, size_t nvlen);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NGHTTP2_HD_INFLATE_NONE = 0,
|
||||||
|
NGHTTP2_HD_INFLATE_FINAL = 1,
|
||||||
|
NGHTTP2_HD_INFLATE_EMIT = (1 << 1)
|
||||||
|
} nghttp2_hd_inflate_flag;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inflates name/value block stored in |in| with length |inlen|. This
|
* Inflates name/value block stored in |in| with length |inlen|. This
|
||||||
* function performs decompression. For each successful emission of
|
* function performs decompression. For each successful emission of
|
||||||
* header name/value pair, name/value pair is assigned to the
|
* header name/value pair, NGHTTP2_HD_INFLATE_EMIT is set in
|
||||||
* |nv_out| and the function returns. The caller must not free
|
* |*inflate_flags| and name/value pair is assigned to the |nv_out|
|
||||||
* the members of |nv_out|.
|
* and the function returns. The caller must not free the members of
|
||||||
|
* |nv_out|.
|
||||||
*
|
*
|
||||||
* The |nv_out| includes pointers to the memory region in the
|
* The |nv_out| may include pointers to the memory region in the
|
||||||
* |in|. The caller must retain the |in| while the |nv_out| is used.
|
* |in|. The caller must retain the |in| while the |nv_out| is used.
|
||||||
*
|
*
|
||||||
* The application should call this function repeatedly until the
|
* The application should call this function repeatedly until the
|
||||||
* |*final| is nonzero and return value is non-negative. This means
|
* |(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL| is nonzero and return
|
||||||
* the all input values are processed successfully. If |*final| is
|
* value is non-negative. This means the all input values are
|
||||||
* nonzero, no header name/value is emitted. Then the application must
|
* processed successfully. Then the application must call
|
||||||
* call `nghttp2_hd_inflate_end_headers()` to prepare for the next
|
* `nghttp2_hd_inflate_end_headers()` to prepare for the next header
|
||||||
* header block input.
|
* block input.
|
||||||
*
|
*
|
||||||
* Currently, the whole compressed header block must be given in the
|
* The caller can feed complete compressed header block. It also can
|
||||||
* |in| and |inlen|. Otherwise, it may lead to NGHTTP2_ERR_HEADER_COMP
|
* feed it in several chunks. The caller must set |in_final| to
|
||||||
* error.
|
* nonzero if the given input is the last block of the compressed
|
||||||
|
* header.
|
||||||
*
|
*
|
||||||
* This function returns the number of bytes processed if it succeeds,
|
* This function returns the number of bytes processed if it succeeds,
|
||||||
* or one of the following negative error codes:
|
* or one of the following negative error codes:
|
||||||
|
@ -300,8 +345,8 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
|
||||||
* Inflation process has failed.
|
* Inflation process has failed.
|
||||||
*/
|
*/
|
||||||
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
|
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
|
||||||
nghttp2_nv *nv_out, int *final,
|
nghttp2_nv *nv_out, int *inflate_flags,
|
||||||
uint8_t *in, size_t inlen);
|
uint8_t *in, size_t inlen, int in_final);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Signals the end of decompression for one header block.
|
* Signals the end of decompression for one header block.
|
||||||
|
@ -324,17 +369,6 @@ int nghttp2_hd_emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||||
int inc_indexing,
|
int inc_indexing,
|
||||||
nghttp2_hd_side side);
|
nghttp2_hd_side side);
|
||||||
|
|
||||||
/* For unittesting purpose */
|
|
||||||
int nghttp2_hd_emit_subst_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
|
|
||||||
size_t *offset_ptr, size_t index,
|
|
||||||
const uint8_t *value, size_t valuelen,
|
|
||||||
size_t subindex);
|
|
||||||
|
|
||||||
/* For unittesting purpose */
|
|
||||||
int nghttp2_hd_emit_subst_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
|
|
||||||
size_t *offset_ptr, nghttp2_nv *nv,
|
|
||||||
size_t subindex);
|
|
||||||
|
|
||||||
/* For unittesting purpose */
|
/* For unittesting purpose */
|
||||||
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||||
size_t index);
|
size_t index);
|
||||||
|
@ -371,29 +405,33 @@ ssize_t nghttp2_hd_huff_encode(uint8_t *dest, size_t destlen,
|
||||||
const uint8_t *src, size_t srclen,
|
const uint8_t *src, size_t srclen,
|
||||||
nghttp2_hd_side side);
|
nghttp2_hd_side side);
|
||||||
|
|
||||||
|
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx,
|
||||||
|
nghttp2_hd_side side);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decodes the given data |src| with length |srclen|. This function
|
* Decodes the given data |src| with length |srclen|. The |ctx| must
|
||||||
* allocates memory to store the result and assigns the its pointer to
|
* be initialized by nghttp2_hd_huff_decode_context_init(). The result
|
||||||
* |*dest_ptr| on success. The caller is responsible to release the
|
* will be added to |dest|. This function may expand |dest| as
|
||||||
* memory pointed by |*dest_ptr| if this function succeeds. If |side|
|
* needed. The caller is responsible to release the memory of |dest|
|
||||||
* is NGHTTP2_HD_SIDE_REQUEST, the request huffman code table is
|
* by calling nghttp2_buffer_free().
|
||||||
* used. Otherwise, the response code table is used.
|
|
||||||
*
|
*
|
||||||
* This function returns the number of written bytes. This return
|
* The caller must set the |final| to nonzero if the given input is
|
||||||
* value is exactly the same with the return value of
|
* the final block.
|
||||||
* nghttp2_hd_huff_decode_count() if it is given with the same |src|,
|
*
|
||||||
* |srclen|, and |side|.
|
* This function returns the number of read bytes from the |in|.
|
||||||
*
|
*
|
||||||
* If this function fails, it returns one of the following negative
|
* If this function fails, it returns one of the following negative
|
||||||
* return codes:
|
* return codes:
|
||||||
*
|
*
|
||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
|
* NGHTTP2_ERR_BUFFER_ERROR
|
||||||
|
* Maximum buffer capacity size exceeded.
|
||||||
* NGHTTP2_ERR_HEADER_COMP
|
* NGHTTP2_ERR_HEADER_COMP
|
||||||
* Decoding process has failed.
|
* Decoding process has failed.
|
||||||
*/
|
*/
|
||||||
ssize_t nghttp2_hd_huff_decode(uint8_t **dest_ptr,
|
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||||
const uint8_t *src, size_t srclen,
|
nghttp2_buffer *dest,
|
||||||
nghttp2_hd_side side);
|
const uint8_t *src, size_t srclen, int final);
|
||||||
|
|
||||||
#endif /* NGHTTP2_HD_COMP_H */
|
#endif /* NGHTTP2_HD_COMP_H */
|
||||||
|
|
|
@ -114,58 +114,46 @@ ssize_t nghttp2_hd_huff_encode(uint8_t *dest, size_t destlen,
|
||||||
return dest - dest_first;
|
return dest - dest_first;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t nghttp2_hd_huff_decode(uint8_t **dest_ptr,
|
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx,
|
||||||
const uint8_t *src, size_t srclen,
|
nghttp2_hd_side side)
|
||||||
nghttp2_hd_side side)
|
|
||||||
{
|
{
|
||||||
size_t i, j, k;
|
if(side == NGHTTP2_HD_SIDE_REQUEST) {
|
||||||
const huff_decode_table_type *huff_decode_table;
|
ctx->huff_decode_table = req_huff_decode_table;
|
||||||
uint8_t *dest = NULL;
|
} else {
|
||||||
size_t destlen = 0;
|
ctx->huff_decode_table = res_huff_decode_table;
|
||||||
int rv;
|
}
|
||||||
int16_t state = 0;
|
ctx->state = 0;
|
||||||
const nghttp2_huff_decode *t = NULL;
|
ctx->accept = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||||
|
nghttp2_buffer *dest,
|
||||||
|
const uint8_t *src, size_t srclen, int final)
|
||||||
|
{
|
||||||
|
size_t i, j;
|
||||||
|
int rv;
|
||||||
/* We use the decoding algorithm described in
|
/* We use the decoding algorithm described in
|
||||||
http://graphics.ics.uci.edu/pub/Prefix.pdf */
|
http://graphics.ics.uci.edu/pub/Prefix.pdf */
|
||||||
if(side == NGHTTP2_HD_SIDE_REQUEST) {
|
|
||||||
huff_decode_table = req_huff_decode_table;
|
|
||||||
} else {
|
|
||||||
huff_decode_table = res_huff_decode_table;
|
|
||||||
}
|
|
||||||
j = 0;
|
|
||||||
for(i = 0; i < srclen; ++i) {
|
for(i = 0; i < srclen; ++i) {
|
||||||
uint8_t in = src[i] >> 4;
|
uint8_t in = src[i] >> 4;
|
||||||
for(k = 0; k < 2; ++k) {
|
for(j = 0; j < 2; ++j) {
|
||||||
t = &huff_decode_table[state][in];
|
const nghttp2_huff_decode *t = &ctx->huff_decode_table[ctx->state][in];
|
||||||
if(t->state == -1) {
|
if(t->state == -1) {
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
if(t->flags & NGHTTP2_HUFF_SYM) {
|
if(t->flags & NGHTTP2_HUFF_SYM) {
|
||||||
if(destlen == j) {
|
rv = nghttp2_buffer_add_byte(dest, t->sym);
|
||||||
size_t new_len = j == 0 ? 32 : j * 2;
|
if(rv != 0) {
|
||||||
uint8_t *new_dest = realloc(dest, new_len);
|
return rv;
|
||||||
if(new_dest == NULL) {
|
|
||||||
rv = NGHTTP2_ERR_NOMEM;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
dest = new_dest;
|
|
||||||
destlen = new_len;
|
|
||||||
}
|
}
|
||||||
dest[j++] = t->sym;
|
|
||||||
}
|
}
|
||||||
state = t->state;
|
ctx->state = t->state;
|
||||||
|
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
|
||||||
in = src[i] & 0xf;
|
in = src[i] & 0xf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(srclen && (t->flags & NGHTTP2_HUFF_ACCEPTED) == 0) {
|
if(final && !ctx->accept) {
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
*dest_ptr = dest;
|
return i;
|
||||||
return j;
|
|
||||||
fail:
|
|
||||||
free(dest);
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,13 +40,27 @@ enum {
|
||||||
} nghttp2_huff_decode_flag;
|
} nghttp2_huff_decode_flag;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
/* huffman decoding state, which is actually the node ID of internal
|
||||||
|
huffman tree */
|
||||||
int16_t state;
|
int16_t state;
|
||||||
|
/* bitwise OR of zero or more of the nghttp2_huff_decode_flag */
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
|
/* symbol if NGHTTP2_HUFF_SYM flag set */
|
||||||
uint8_t sym;
|
uint8_t sym;
|
||||||
} nghttp2_huff_decode;
|
} nghttp2_huff_decode;
|
||||||
|
|
||||||
typedef nghttp2_huff_decode huff_decode_table_type[16];
|
typedef nghttp2_huff_decode huff_decode_table_type[16];
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const huff_decode_table_type *huff_decode_table;
|
||||||
|
/* Current huffman decoding state. We stripped leaf nodes, so the
|
||||||
|
value range is [0..255], inclusive. */
|
||||||
|
uint8_t state;
|
||||||
|
/* nonzero if we can say that the decoding process succeeds at this
|
||||||
|
state */
|
||||||
|
uint8_t accept;
|
||||||
|
} nghttp2_hd_huff_decode_context;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* The number of bits in this code */
|
/* The number of bits in this code */
|
||||||
uint32_t nbits;
|
uint32_t nbits;
|
||||||
|
|
|
@ -44,7 +44,8 @@ typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
|
||||||
/* Internal error code. They must be in the range [-499, -100],
|
/* Internal error code. They must be in the range [-499, -100],
|
||||||
inclusive. */
|
inclusive. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NGHTTP2_ERR_CREDENTIAL_PENDING = -101
|
NGHTTP2_ERR_CREDENTIAL_PENDING = -101,
|
||||||
|
NGHTTP2_ERR_BUFFER_ERROR = - 102
|
||||||
} nghttp2_internal_error;
|
} nghttp2_internal_error;
|
||||||
|
|
||||||
#endif /* NGHTTP2_INT_H */
|
#endif /* NGHTTP2_INT_H */
|
||||||
|
|
|
@ -1915,14 +1915,15 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
||||||
int call_header_cb)
|
int call_header_cb)
|
||||||
{
|
{
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
int final;
|
int inflate_flags;
|
||||||
nghttp2_nv nv;
|
nghttp2_nv nv;
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
|
inflate_flags = 0;
|
||||||
rv = nghttp2_hd_inflate_hd
|
rv = nghttp2_hd_inflate_hd
|
||||||
(&session->hd_inflater, &nv, &final,
|
(&session->hd_inflater, &nv, &inflate_flags,
|
||||||
session->iframe.buf + session->iframe.inflate_offset,
|
session->iframe.buf + session->iframe.inflate_offset,
|
||||||
session->iframe.buflen - session->iframe.inflate_offset);
|
session->iframe.buflen - session->iframe.inflate_offset, 1);
|
||||||
if(nghttp2_is_fatal(rv)) {
|
if(nghttp2_is_fatal(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -1942,16 +1943,16 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
||||||
return NGHTTP2_ERR_HEADER_COMP;
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
}
|
}
|
||||||
session->iframe.inflate_offset += rv;
|
session->iframe.inflate_offset += rv;
|
||||||
if(final) {
|
if(call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(call_header_cb) {
|
|
||||||
rv = session_call_on_header(session, frame, &nv);
|
rv = session_call_on_header(session, frame, &nv);
|
||||||
/* This handles NGHTTP2_ERR_PAUSE as well */
|
/* This handles NGHTTP2_ERR_PAUSE as well */
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nghttp2_hd_inflate_end_headers(&session->hd_inflater);
|
nghttp2_hd_inflate_end_headers(&session->hd_inflater);
|
||||||
if(call_header_cb) {
|
if(call_header_cb) {
|
||||||
|
|
|
@ -94,7 +94,7 @@ static int inflate_hd(json_t *obj, nghttp2_hd_context *inflater, int seq)
|
||||||
size_t buflen;
|
size_t buflen;
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
nghttp2_nv nv;
|
nghttp2_nv nv;
|
||||||
int final;
|
int inflate_flags;
|
||||||
|
|
||||||
wire = json_object_get(obj, "wire");
|
wire = json_object_get(obj, "wire");
|
||||||
if(wire == NULL) {
|
if(wire == NULL) {
|
||||||
|
@ -131,18 +131,21 @@ static int inflate_hd(json_t *obj, nghttp2_hd_context *inflater, int seq)
|
||||||
|
|
||||||
p = buf;
|
p = buf;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &final, p, buflen);
|
inflate_flags = 0;
|
||||||
|
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, p, buflen, 1);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq);
|
fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
p += rv;
|
p += rv;
|
||||||
buflen -= rv;
|
buflen -= rv;
|
||||||
if(final) {
|
if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
||||||
|
json_array_append_new(headers, dump_header(nv.name, nv.namelen,
|
||||||
|
nv.value, nv.valuelen));
|
||||||
|
}
|
||||||
|
if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
json_array_append_new(headers, dump_header(nv.name, nv.namelen,
|
|
||||||
nv.value, nv.valuelen));
|
|
||||||
}
|
}
|
||||||
assert(buflen == 0);
|
assert(buflen == 0);
|
||||||
nghttp2_hd_inflate_end_headers(inflater);
|
nghttp2_hd_inflate_end_headers(inflater);
|
||||||
|
|
|
@ -73,7 +73,6 @@ int main(int argc, char* argv[])
|
||||||
!CU_add_test(pSuite, "map_each_free", test_nghttp2_map_each_free) ||
|
!CU_add_test(pSuite, "map_each_free", test_nghttp2_map_each_free) ||
|
||||||
!CU_add_test(pSuite, "queue", test_nghttp2_queue) ||
|
!CU_add_test(pSuite, "queue", test_nghttp2_queue) ||
|
||||||
!CU_add_test(pSuite, "buffer", test_nghttp2_buffer) ||
|
!CU_add_test(pSuite, "buffer", test_nghttp2_buffer) ||
|
||||||
!CU_add_test(pSuite, "buffer_reader", test_nghttp2_buffer_reader) ||
|
|
||||||
!CU_add_test(pSuite, "npn", test_nghttp2_npn) ||
|
!CU_add_test(pSuite, "npn", test_nghttp2_npn) ||
|
||||||
!CU_add_test(pSuite, "session_recv", test_nghttp2_session_recv) ||
|
!CU_add_test(pSuite, "session_recv", test_nghttp2_session_recv) ||
|
||||||
!CU_add_test(pSuite, "session_recv_invalid_stream_id",
|
!CU_add_test(pSuite, "session_recv_invalid_stream_id",
|
||||||
|
|
|
@ -29,96 +29,28 @@
|
||||||
#include <CUnit/CUnit.h>
|
#include <CUnit/CUnit.h>
|
||||||
|
|
||||||
#include "nghttp2_buffer.h"
|
#include "nghttp2_buffer.h"
|
||||||
#include "nghttp2_net.h"
|
|
||||||
|
|
||||||
void test_nghttp2_buffer(void)
|
void test_nghttp2_buffer(void)
|
||||||
{
|
{
|
||||||
nghttp2_buffer buffer;
|
nghttp2_buffer buffer;
|
||||||
uint8_t out[1024];
|
|
||||||
nghttp2_buffer_init(&buffer, 8);
|
|
||||||
CU_ASSERT(0 == nghttp2_buffer_length(&buffer));
|
|
||||||
CU_ASSERT(0 == nghttp2_buffer_avail(&buffer));
|
|
||||||
CU_ASSERT(NULL == nghttp2_buffer_get(&buffer));
|
|
||||||
CU_ASSERT(0 == nghttp2_buffer_alloc(&buffer));
|
|
||||||
|
|
||||||
CU_ASSERT(8 == nghttp2_buffer_avail(&buffer));
|
nghttp2_buffer_init(&buffer, 16);
|
||||||
CU_ASSERT(NULL != nghttp2_buffer_get(&buffer));
|
|
||||||
memcpy(nghttp2_buffer_get(&buffer), "012", 3);
|
|
||||||
nghttp2_buffer_advance(&buffer, 3);
|
|
||||||
CU_ASSERT(3 == nghttp2_buffer_length(&buffer));
|
|
||||||
|
|
||||||
CU_ASSERT(5 == nghttp2_buffer_avail(&buffer));
|
CU_ASSERT(0 == buffer.len);
|
||||||
memcpy(nghttp2_buffer_get(&buffer), "34567", 5);
|
|
||||||
nghttp2_buffer_advance(&buffer, 5);
|
|
||||||
CU_ASSERT(8 == nghttp2_buffer_length(&buffer));
|
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_buffer_avail(&buffer));
|
CU_ASSERT(0 == nghttp2_buffer_add(&buffer, (const uint8_t*)"foo", 3));
|
||||||
CU_ASSERT(0 == nghttp2_buffer_alloc(&buffer));
|
CU_ASSERT(3 == buffer.len);
|
||||||
memcpy(nghttp2_buffer_get(&buffer), "89ABCDE", 7);
|
|
||||||
nghttp2_buffer_advance(&buffer, 7);
|
|
||||||
CU_ASSERT(15 == nghttp2_buffer_length(&buffer));
|
|
||||||
|
|
||||||
CU_ASSERT(1 == nghttp2_buffer_avail(&buffer));
|
CU_ASSERT(0 == nghttp2_buffer_add_byte(&buffer, '.'));
|
||||||
|
CU_ASSERT(4 == buffer.len);
|
||||||
|
|
||||||
nghttp2_buffer_serialize(&buffer, out);
|
CU_ASSERT(0 == nghttp2_buffer_add(&buffer,
|
||||||
CU_ASSERT(0 == memcmp("0123456789ABCDE", out, 15));
|
(const uint8_t*)"012345678901", 12));
|
||||||
|
CU_ASSERT(16 == buffer.len);
|
||||||
|
|
||||||
nghttp2_buffer_reset(&buffer);
|
CU_ASSERT(NGHTTP2_ERR_BUFFER_ERROR == nghttp2_buffer_add_byte(&buffer, '.'));
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_BUFFER_ERROR ==
|
||||||
CU_ASSERT(0 == nghttp2_buffer_length(&buffer));
|
nghttp2_buffer_add(&buffer, (const uint8_t*)".", 1));
|
||||||
CU_ASSERT(0 == nghttp2_buffer_avail(&buffer));
|
|
||||||
CU_ASSERT(NULL == nghttp2_buffer_get(&buffer));
|
|
||||||
CU_ASSERT(0 == nghttp2_buffer_alloc(&buffer));
|
|
||||||
|
|
||||||
CU_ASSERT(8 == nghttp2_buffer_avail(&buffer));
|
|
||||||
memcpy(nghttp2_buffer_get(&buffer), "Hello", 5);
|
|
||||||
nghttp2_buffer_advance(&buffer, 5);
|
|
||||||
CU_ASSERT(5 == nghttp2_buffer_length(&buffer));
|
|
||||||
|
|
||||||
nghttp2_buffer_serialize(&buffer, out);
|
|
||||||
CU_ASSERT(0 == memcmp("Hello", out, 5));
|
|
||||||
|
|
||||||
nghttp2_buffer_free(&buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_nghttp2_buffer_reader(void)
|
|
||||||
{
|
|
||||||
nghttp2_buffer buffer;
|
|
||||||
nghttp2_buffer_reader reader;
|
|
||||||
uint16_t val16;
|
|
||||||
uint32_t val32;
|
|
||||||
uint8_t temp[256];
|
|
||||||
|
|
||||||
nghttp2_buffer_init(&buffer, 3);
|
|
||||||
nghttp2_buffer_write(&buffer, (const uint8_t*)"hello", 5);
|
|
||||||
val16 = htons(678);
|
|
||||||
nghttp2_buffer_write(&buffer, (const uint8_t*)&val16, sizeof(uint16_t));
|
|
||||||
val32 = htonl(1000000007);
|
|
||||||
nghttp2_buffer_write(&buffer, (const uint8_t*)&val32, sizeof(uint32_t));
|
|
||||||
nghttp2_buffer_write(&buffer, (const uint8_t*)"world", 5);
|
|
||||||
|
|
||||||
CU_ASSERT(5+2+4+5 == nghttp2_buffer_length(&buffer));
|
|
||||||
|
|
||||||
nghttp2_buffer_reader_init(&reader, &buffer);
|
|
||||||
|
|
||||||
nghttp2_buffer_reader_data(&reader, temp, 5);
|
|
||||||
CU_ASSERT(memcmp(temp, "hello", 5) == 0);
|
|
||||||
CU_ASSERT(678 == nghttp2_buffer_reader_uint16(&reader));
|
|
||||||
CU_ASSERT(1000000007 == nghttp2_buffer_reader_uint32(&reader));
|
|
||||||
CU_ASSERT('w' == nghttp2_buffer_reader_uint8(&reader));
|
|
||||||
CU_ASSERT('o' == nghttp2_buffer_reader_uint8(&reader));
|
|
||||||
CU_ASSERT('r' == nghttp2_buffer_reader_uint8(&reader));
|
|
||||||
CU_ASSERT('l' == nghttp2_buffer_reader_uint8(&reader));
|
|
||||||
CU_ASSERT('d' == nghttp2_buffer_reader_uint8(&reader));
|
|
||||||
|
|
||||||
nghttp2_buffer_reader_init(&reader, &buffer);
|
|
||||||
nghttp2_buffer_reader_advance(&reader, 5);
|
|
||||||
CU_ASSERT(678 == nghttp2_buffer_reader_uint16(&reader));
|
|
||||||
nghttp2_buffer_reader_advance(&reader, 1);
|
|
||||||
nghttp2_buffer_reader_advance(&reader, 1);
|
|
||||||
nghttp2_buffer_reader_advance(&reader, 1);
|
|
||||||
nghttp2_buffer_reader_advance(&reader, 1);
|
|
||||||
CU_ASSERT('w' == nghttp2_buffer_reader_uint8(&reader));
|
|
||||||
|
|
||||||
nghttp2_buffer_free(&buffer);
|
nghttp2_buffer_free(&buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,28 +185,29 @@ void test_nghttp2_hd_deflate_same_indexed_repr(void)
|
||||||
void test_nghttp2_hd_deflate_common_header_eviction(void)
|
void test_nghttp2_hd_deflate_common_header_eviction(void)
|
||||||
{
|
{
|
||||||
nghttp2_hd_context deflater, inflater;
|
nghttp2_hd_context deflater, inflater;
|
||||||
nghttp2_nv nva[] = {MAKE_NV(":scheme", "http"),
|
nghttp2_nv nva[] = {MAKE_NV("h1", ""),
|
||||||
MAKE_NV("", "")};
|
MAKE_NV("h2", "")};
|
||||||
uint8_t *buf = NULL;
|
uint8_t *buf = NULL;
|
||||||
size_t buflen = 0;
|
size_t buflen = 0;
|
||||||
ssize_t blocklen;
|
ssize_t blocklen;
|
||||||
/* Default header table capacity is 4096. Adding 2 byte header name
|
/* Default header table capacity is 4096. Adding 2 byte header name
|
||||||
and 4060 byte value, which is 4094 bytes including overhead, to
|
and 4060 byte value, which is 4094 bytes including overhead, to
|
||||||
the table evicts first entry. */
|
the table evicts first entry. */
|
||||||
uint8_t value[4060];
|
uint8_t value[3038];
|
||||||
nva_out out;
|
nva_out out;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
nva_out_init(&out);
|
nva_out_init(&out);
|
||||||
memset(value, '0', sizeof(value));
|
memset(value, '0', sizeof(value));
|
||||||
nva[1].name = (uint8_t*)"hd";
|
for(i = 0; i < 2; ++i) {
|
||||||
nva[1].namelen = strlen((const char*)nva[1].name);
|
nva[i].value = value;
|
||||||
nva[1].value = value;
|
nva[i].valuelen = sizeof(value);
|
||||||
nva[1].valuelen = sizeof(value);
|
}
|
||||||
|
|
||||||
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
|
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
|
||||||
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
|
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
|
||||||
|
|
||||||
/* First emit ":scheme: http" to put it in the reference set (index
|
/* First emit "h1: ..." to put it in the reference set (index
|
||||||
= 0). */
|
= 0). */
|
||||||
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 1);
|
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 1);
|
||||||
CU_ASSERT(blocklen > 0);
|
CU_ASSERT(blocklen > 0);
|
||||||
|
@ -218,11 +219,11 @@ void test_nghttp2_hd_deflate_common_header_eviction(void)
|
||||||
|
|
||||||
nva_out_reset(&out);
|
nva_out_reset(&out);
|
||||||
|
|
||||||
/* Encode with large header */
|
/* Encode with second header */
|
||||||
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 2);
|
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 2);
|
||||||
CU_ASSERT(blocklen > 0);
|
CU_ASSERT(blocklen > 0);
|
||||||
|
|
||||||
/* Check common header :scheme: http, which is removed from the
|
/* Check common header "h1: ...:, which is removed from the
|
||||||
header table because of eviction, is still emitted by the
|
header table because of eviction, is still emitted by the
|
||||||
inflater */
|
inflater */
|
||||||
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf, blocklen));
|
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf, blocklen));
|
||||||
|
|
|
@ -120,20 +120,23 @@ ssize_t inflate_hd(nghttp2_hd_context *inflater, nva_out *out,
|
||||||
{
|
{
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
nghttp2_nv nv;
|
nghttp2_nv nv;
|
||||||
int final;
|
int inflate_flags;
|
||||||
size_t initial = buflen;
|
size_t initial = buflen;
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &final, buf, buflen);
|
inflate_flags = 0;
|
||||||
|
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, buf, buflen, 1);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
buf += rv;
|
buf += rv;
|
||||||
buflen -= rv;
|
buflen -= rv;
|
||||||
if(final) {
|
if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
||||||
|
add_out(out, &nv);
|
||||||
|
}
|
||||||
|
if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
add_out(out, &nv);
|
|
||||||
}
|
}
|
||||||
nghttp2_hd_inflate_end_headers(inflater);
|
nghttp2_hd_inflate_end_headers(inflater);
|
||||||
return initial - buflen;
|
return initial - buflen;
|
||||||
|
|
Loading…
Reference in New Issue