Skip to content

Commit

Permalink
nghttp2_hd: Implement stream header inflater
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tatsuhiro-t committed Jan 25, 2014
1 parent f8a446f commit 8317559
Show file tree
Hide file tree
Showing 13 changed files with 741 additions and 712 deletions.
221 changes: 36 additions & 185 deletions lib/nghttp2_buffer.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* 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
* a copy of this software and associated documentation files (the
Expand All @@ -27,217 +27,68 @@
#include <assert.h>
#include <string.h>

#include "nghttp2_net.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->root.next = NULL;
buffer->current = &buffer->root;
buffer->capacity = chunk_capacity;
buffer->buf = NULL;
buffer->len = 0;
/*
* Set last_offset to maximum so that first append adds new buffer
* buffer.
*/
buffer->last_offset = buffer->capacity;
buffer->capacity = 0;
buffer->max_capacity = max_capacity;
}

void nghttp2_buffer_free(nghttp2_buffer *buffer)
{
nghttp2_buffer_chunk *p = buffer->root.next;
while(p) {
nghttp2_buffer_chunk *next = p->next;
free(p->data);
free(p);
p = next;
}
free(buffer->buf);
}

int nghttp2_buffer_alloc(nghttp2_buffer *buffer)
int nghttp2_buffer_reserve(nghttp2_buffer *buffer, size_t len)
{
if(buffer->current->next == NULL) {
nghttp2_buffer_chunk *chunk;
uint8_t *buf;
chunk = malloc(sizeof(nghttp2_buffer_chunk));
if(chunk == NULL) {
return NGHTTP2_ERR_NOMEM;
}
buf = malloc(buffer->capacity);
if(buf == NULL) {
free(chunk);
if(len > buffer->max_capacity) {
return NGHTTP2_ERR_BUFFER_ERROR;
}
if(buffer->capacity < len) {
uint8_t *new_buf;
size_t new_cap = buffer->capacity == 0 ? 32 : buffer->capacity * 3 / 2;
new_cap = nghttp2_min(buffer->max_capacity, nghttp2_max(new_cap, len));
new_buf = realloc(buffer->buf, new_cap);
if(new_buf == NULL) {
return NGHTTP2_ERR_NOMEM;
}
chunk->data = buf;
chunk->next = NULL;
buffer->current->next = chunk;
buffer->current = chunk;
} else {
buffer->current = buffer->current->next;
buffer->buf = new_buf;
buffer->capacity = new_cap;
}
buffer->len += buffer->capacity-buffer->last_offset;
buffer->last_offset = 0;
return 0;
}

uint8_t* nghttp2_buffer_get(nghttp2_buffer *buffer)
{
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 nghttp2_buffer_add(nghttp2_buffer *buffer,
const uint8_t *data, size_t len)
{
int rv;
while(len) {
size_t writelen;
if(nghttp2_buffer_avail(buffer) == 0) {
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);
rv = nghttp2_buffer_reserve(buffer, buffer->len + len);
if(rv != 0) {
return rv;
}
memcpy(buffer->buf + buffer->len, data, len);
buffer->len += len;
return 0;
}

size_t nghttp2_buffer_length(nghttp2_buffer *buffer)
int nghttp2_buffer_add_byte(nghttp2_buffer *buffer, uint8_t b)
{
return buffer->len;
}

size_t nghttp2_buffer_capacity(nghttp2_buffer *buffer)
{
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;
int rv;
rv = nghttp2_buffer_reserve(buffer, buffer->len + 1);
if(rv != 0) {
return rv;
}
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->last_offset = buffer->capacity;
}

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;
}
}
buffer->capacity = 0;
}
Loading

0 comments on commit 8317559

Please sign in to comment.