Commit ff0d137f authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

Reference counted HPACK name/value pair

parent 8da20975
...@@ -162,7 +162,7 @@ def gen_enum(): ...@@ -162,7 +162,7 @@ def gen_enum():
def gen_index_header(): def gen_index_header():
print '''\ print '''\
static inline int lookup_token(const uint8_t *name, size_t namelen) { static inline int32_t lookup_token(const uint8_t *name, size_t namelen) {
switch (namelen) {''' switch (namelen) {'''
b = build_header(HEADERS) b = build_header(HEADERS)
for size in sorted(b.keys()): for size in sorted(b.keys()):
......
...@@ -47,7 +47,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ ...@@ -47,7 +47,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
nghttp2_option.c \ nghttp2_option.c \
nghttp2_callbacks.c \ nghttp2_callbacks.c \
nghttp2_mem.c \ nghttp2_mem.c \
nghttp2_http.c nghttp2_http.c \
nghttp2_rcbuf.c
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_frame.h \ nghttp2_frame.h \
...@@ -61,7 +62,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ ...@@ -61,7 +62,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_option.h \ nghttp2_option.h \
nghttp2_callbacks.h \ nghttp2_callbacks.h \
nghttp2_mem.h \ nghttp2_mem.h \
nghttp2_http.h nghttp2_http.h \
nghttp2_rcbuf.h
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
libnghttp2_la_LDFLAGS = -no-undefined \ libnghttp2_la_LDFLAGS = -no-undefined \
......
...@@ -419,6 +419,53 @@ typedef enum { ...@@ -419,6 +419,53 @@ typedef enum {
NGHTTP2_ERR_FLOODED = -904 NGHTTP2_ERR_FLOODED = -904
} nghttp2_error; } nghttp2_error;
/**
* @struct
*
* The object representing single contagious buffer.
*/
typedef struct {
/**
* The pointer to the buffer.
*/
uint8_t *base;
/**
* The length of the buffer.
*/
size_t len;
} nghttp2_vec;
struct nghttp2_rcbuf;
/**
* @struct
*
* The object representing reference counted buffer. The details of
* this structure are intentionally hidden from the public API.
*/
typedef struct nghttp2_rcbuf nghttp2_rcbuf;
/**
* @function
*
* Increments the reference count of |rcbuf| by 1.
*/
void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf);
/**
* @function
*
* Decrements the reference count of |rcbuf| by 1. If the reference
* count becomes zero, the object pointed by |rcbuf| will be freed.
* In this case, application must not use |rcbuf| again.
*/
void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf);
/**
* Returns the underlying buffer managed by |rcbuf|.
*/
nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf);
/** /**
* @enum * @enum
* *
......
This diff is collapsed.
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "nghttp2_hd_huffman.h" #include "nghttp2_hd_huffman.h"
#include "nghttp2_buf.h" #include "nghttp2_buf.h"
#include "nghttp2_mem.h" #include "nghttp2_mem.h"
#include "nghttp2_rcbuf.h"
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE #define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
#define NGHTTP2_HD_ENTRY_OVERHEAD 32 #define NGHTTP2_HD_ENTRY_OVERHEAD 32
...@@ -168,25 +169,29 @@ typedef enum { ...@@ -168,25 +169,29 @@ typedef enum {
NGHTTP2_TOKEN_X_XSS_PROTECTION, NGHTTP2_TOKEN_X_XSS_PROTECTION,
} nghttp2_token; } nghttp2_token;
typedef enum {
NGHTTP2_HD_FLAG_NONE = 0,
/* Indicates name was dynamically allocated and must be freed */
NGHTTP2_HD_FLAG_NAME_ALLOC = 1,
/* Indicates value was dynamically allocated and must be freed */
NGHTTP2_HD_FLAG_VALUE_ALLOC = 1 << 1,
/* Indicates that the name was gifted to the entry and no copying
necessary. */
NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 2,
/* Indicates that the value was gifted to the entry and no copying
necessary. */
NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 3
} nghttp2_hd_flags;
struct nghttp2_hd_entry; struct nghttp2_hd_entry;
typedef struct nghttp2_hd_entry nghttp2_hd_entry; typedef struct nghttp2_hd_entry nghttp2_hd_entry;
typedef struct {
/* The buffer containing header field name. NULL-termination is
guaranteed. */
nghttp2_rcbuf *name;
/* The buffer containing header field value. NULL-termination is
guaranteed. */
nghttp2_rcbuf *value;
/* nghttp2_token value for name. It could be -1 if we have no token
for that header field name. */
int32_t token;
/* Bitwise OR of one or more of nghttp2_nv_flag. */
uint8_t flags;
} nghttp2_hd_nv;
struct nghttp2_hd_entry { struct nghttp2_hd_entry {
nghttp2_nv nv; /* The header field name/value pair */
nghttp2_hd_nv nv;
/* This is solely for nghttp2_hd_{deflate,inflate}_get_table_entry
APIs to keep backward compatibility. */
nghttp2_nv cnv;
/* The next entry which shares same bucket in hash table. */ /* The next entry which shares same bucket in hash table. */
nghttp2_hd_entry *next; nghttp2_hd_entry *next;
/* The sequence number. We will increment it by one whenever we /* The sequence number. We will increment it by one whenever we
...@@ -194,14 +199,17 @@ struct nghttp2_hd_entry { ...@@ -194,14 +199,17 @@ struct nghttp2_hd_entry {
uint32_t seq; uint32_t seq;
/* The hash value for header name (nv.name). */ /* The hash value for header name (nv.name). */
uint32_t hash; uint32_t hash;
/* nghttp2_token value for nv.name. It could be -1 if we have no
token for that header field name. */
int token;
/* Reference count */
uint8_t ref;
uint8_t flags;
}; };
/* The entry used for static header table. */
typedef struct {
nghttp2_rcbuf name;
nghttp2_rcbuf value;
nghttp2_nv cnv;
int32_t token;
uint32_t hash;
} nghttp2_hd_static_entry;
typedef struct { typedef struct {
nghttp2_hd_entry **buffer; nghttp2_hd_entry **buffer;
size_t mask; size_t mask;
...@@ -275,17 +283,14 @@ struct nghttp2_hd_deflater { ...@@ -275,17 +283,14 @@ struct nghttp2_hd_deflater {
struct nghttp2_hd_inflater { struct nghttp2_hd_inflater {
nghttp2_hd_context ctx; nghttp2_hd_context ctx;
/* header buffer */
nghttp2_buf namebuf, valuebuf;
/* Stores current state of huffman decoding */ /* Stores current state of huffman decoding */
nghttp2_hd_huff_decode_context huff_decode_ctx; nghttp2_hd_huff_decode_context huff_decode_ctx;
/* Pointer to the nghttp2_hd_entry which is used current header /* header buffer */
emission. This is required because in some cases the nghttp2_buf namebuf, valuebuf;
ent_keep->ref == 0 and we have to keep track of it. */ nghttp2_rcbuf *namercbuf, *valuercbuf;
nghttp2_hd_entry *ent_keep; /* Pointer to the name/value pair which are used in the current
/* Pointer to the name/value pair buffer which is used in the header emission. */
current header emission. */ nghttp2_rcbuf *nv_name_keep, *nv_value_keep;
uint8_t *nv_name_keep, *nv_value_keep;
/* The number of bytes to read */ /* The number of bytes to read */
size_t left; size_t left;
/* The index in indexed repr or indexed name */ /* The index in indexed repr or indexed name */
...@@ -309,24 +314,16 @@ struct nghttp2_hd_inflater { ...@@ -309,24 +314,16 @@ struct nghttp2_hd_inflater {
}; };
/* /*
* Initializes the |ent| members. If NGHTTP2_HD_FLAG_NAME_ALLOC bit * Initializes the |ent| members. The reference counts of nv->name
* set in the |flags|, the content pointed by the |name| with length * and nv->value are increased by one for each.
* |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit
* set in the |flags|, the content pointed by the |value| with length
* |valuelen| is copied. The |token| is enum number looked up by
* |name|. It could be -1 if we don't have that enum value.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/ */
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv);
size_t namelen, uint8_t *value, size_t valuelen,
int token, nghttp2_mem *mem);
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem); /*
* This function decreases the reference counts of nv->name and
* nv->value.
*/
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
/* /*
* Initializes |deflater| for deflating name/values pairs. * Initializes |deflater| for deflating name/values pairs.
...@@ -407,16 +404,14 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); ...@@ -407,16 +404,14 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem);
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater);
/* /*
* Similar to nghttp2_hd_inflate_hd(), but this takes additional * Similar to nghttp2_hd_inflate_hd(), but this takes nghttp2_hd_nv
* output parameter |token|. On successful header emission, it * instead of nghttp2_nv as output parameter |nv_out|. Other than
* contains nghttp2_token value for nv_out->name. It could be -1 if * that return values and semantics are the same as
* we don't have enum value for the name. Other than that return * nghttp2_hd_inflate_hd().
* values and semantics are the same as nghttp2_hd_inflate_hd().
*/ */
ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
nghttp2_nv *nv_out, int *inflate_flags, nghttp2_hd_nv *nv_out, int *inflate_flags,
int *token, uint8_t *in, size_t inlen, uint8_t *in, size_t inlen, int in_final);
int in_final);
/* For unittesting purpose */ /* For unittesting purpose */
int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index,
...@@ -430,8 +425,7 @@ int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, ...@@ -430,8 +425,7 @@ int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);
/* For unittesting purpose */ /* For unittesting purpose */
nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context, nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index);
size_t index);
/* For unittesting purpose */ /* For unittesting purpose */
ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final, ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final,
......
...@@ -82,12 +82,12 @@ static int lws(const uint8_t *s, size_t n) { ...@@ -82,12 +82,12 @@ static int lws(const uint8_t *s, size_t n) {
return 1; return 1;
} }
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_nv *nv, static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
int flag) { int flag) {
if (stream->http_flags & flag) { if (stream->http_flags & flag) {
return 0; return 0;
} }
if (lws(nv->value, nv->valuelen)) { if (lws(nv->value->base, nv->value->len)) {
return 0; return 0;
} }
stream->http_flags = (uint16_t)(stream->http_flags | flag); stream->http_flags = (uint16_t)(stream->http_flags | flag);
...@@ -112,16 +112,16 @@ static int check_path(nghttp2_stream *stream) { ...@@ -112,16 +112,16 @@ static int check_path(nghttp2_stream *stream) {
(stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK))); (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
} }
static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
int token, int trailer) { int trailer) {
if (nv->name[0] == ':') { if (nv->name->base[0] == ':') {
if (trailer || if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
} }
switch (token) { switch (nv->token) {
case NGHTTP2_TOKEN__AUTHORITY: case NGHTTP2_TOKEN__AUTHORITY:
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
...@@ -131,16 +131,16 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, ...@@ -131,16 +131,16 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
switch (nv->valuelen) { switch (nv->value->len) {
case 4: case 4:
if (lstreq("HEAD", nv->value, nv->valuelen)) { if (lstreq("HEAD", nv->value->base, nv->value->len)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
} }
break; break;
case 7: case 7:
switch (nv->value[6]) { switch (nv->value->base[6]) {
case 'T': case 'T':
if (lstreq("CONNECT", nv->value, nv->valuelen)) { if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
if (stream->stream_id % 2 == 0) { if (stream->stream_id % 2 == 0) {
/* we won't allow CONNECT for push */ /* we won't allow CONNECT for push */
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
...@@ -153,7 +153,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, ...@@ -153,7 +153,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
} }
break; break;
case 'S': case 'S':
if (lstreq("OPTIONS", nv->value, nv->valuelen)) { if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS; stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
} }
break; break;
...@@ -168,9 +168,9 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, ...@@ -168,9 +168,9 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
if (nv->value[0] == '/') { if (nv->value->base[0] == '/') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR; stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
} else if (nv->valuelen == 1 && nv->value[0] == '*') { } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK; stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
} }
break; break;
...@@ -181,8 +181,8 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, ...@@ -181,8 +181,8 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
if ((nv->valuelen == 4 && memieq("http", nv->value, 4)) || if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
(nv->valuelen == 5 && memieq("https", nv->value, 5))) { (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP; stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
} }
break; break;
...@@ -195,7 +195,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, ...@@ -195,7 +195,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (stream->content_length != -1) { if (stream->content_length != -1) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
stream->content_length = parse_uint(nv->value, nv->valuelen); stream->content_length = parse_uint(nv->value->base, nv->value->len);
if (stream->content_length == -1) { if (stream->content_length == -1) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
...@@ -209,41 +209,41 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, ...@@ -209,41 +209,41 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
case NGHTTP2_TOKEN_UPGRADE: case NGHTTP2_TOKEN_UPGRADE:
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
case NGHTTP2_TOKEN_TE: case NGHTTP2_TOKEN_TE:
if (!lstrieq("trailers", nv->value, nv->valuelen)) { if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
break; break;
default: default:
if (nv->name[0] == ':') { if (nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
} }
if (nv->name[0] != ':') { if (nv->name->base[0] != ':') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
} }
return 0; return 0;
} }
static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
int token, int trailer) { int trailer) {
if (nv->name[0] == ':') { if (nv->name->base[0] == ':') {
if (trailer || if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
} }
switch (token) { switch (nv->token) {
case NGHTTP2_TOKEN__STATUS: { case NGHTTP2_TOKEN__STATUS: {
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
if (nv->valuelen != 3) { if (nv->value->len != 3) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
stream->status_code = (int16_t)parse_uint(nv->value, nv->valuelen); stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
if (stream->status_code == -1) { if (stream->status_code == -1) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
...@@ -253,7 +253,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, ...@@ -253,7 +253,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (stream->content_length != -1) { if (stream->content_length != -1) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
stream->content_length = parse_uint(nv->value, nv->valuelen); stream->content_length = parse_uint(nv->value->base, nv->value->len);
if (stream->content_length == -1) { if (stream->content_length == -1) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
...@@ -267,17 +267,17 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, ...@@ -267,17 +267,17 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
case NGHTTP2_TOKEN_UPGRADE: case NGHTTP2_TOKEN_UPGRADE:
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
case NGHTTP2_TOKEN_TE: case NGHTTP2_TOKEN_TE:
if (!lstrieq("trailers", nv->value, nv->valuelen)) { if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
break; break;
default: default:
if (nv->name[0] == ':') { if (nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
} }
if (nv->name[0] != ':') { if (nv->name->base[0] != ':') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
} }
...@@ -375,7 +375,7 @@ static int check_scheme(const uint8_t *value, size_t len) { ...@@ -375,7 +375,7 @@ static int check_scheme(const uint8_t *value, size_t len) {
} }
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
nghttp2_frame *frame, nghttp2_nv *nv, int token, nghttp2_frame *frame, nghttp2_hd_nv *nv,
int trailer) { int trailer) {
int rv; int rv;
...@@ -386,14 +386,14 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, ...@@ -386,14 +386,14 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
this, we may disrupt many web sites and/or libraries. So we this, we may disrupt many web sites and/or libraries. So we
become conservative here, and just ignore those illegal regular become conservative here, and just ignore those illegal regular
headers. */ headers. */
if (!nghttp2_check_header_name(nv->name, nv->namelen)) { if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
size_t i; size_t i;
if (nv->namelen > 0 && nv->name[0] == ':') { if (nv->name->len > 0 && nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
/* header field name must be lower-cased without exception */ /* header field name must be lower-cased without exception */
for (i = 0; i < nv->namelen; ++i) { for (i = 0; i < nv->name->len; ++i) {
uint8_t c = nv->name[i]; uint8_t c = nv->name->base[i];
if ('A' <= c && c <= 'Z') { if ('A' <= c && c <= 'Z') {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
...@@ -405,17 +405,18 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, ...@@ -405,17 +405,18 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
return NGHTTP2_ERR_IGN_HTTP_HEADER; return NGHTTP2_ERR_IGN_HTTP_HEADER;
} }
if (token == NGHTTP2_TOKEN__AUTHORITY || token == NGHTTP2_TOKEN_HOST) { if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
rv = check_authority(nv->value, nv->valuelen); nv->token == NGHTTP2_TOKEN_HOST) {
} else if (token == NGHTTP2_TOKEN__SCHEME) { rv = check_authority(nv->value->base, nv->value->len);
rv = check_scheme(nv->value, nv->valuelen); } else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
rv = check_scheme(nv->value->base, nv->value->len);
} else { } else {
rv = nghttp2_check_header_value(nv->value, nv->valuelen); rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
} }
if (rv == 0) { if (rv == 0) {
assert(nv->namelen > 0); assert(nv->name->len > 0);
if (nv->name[0] == ':') { if (nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
} }
/* When ignoring regular headers, we set this flag so that we /* When ignoring regular headers, we set this flag so that we
...@@ -426,10 +427,10 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, ...@@ -426,10 +427,10 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
} }
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
return http_request_on_header(stream, nv, token, trailer); return http_request_on_header(stream, nv, trailer);
} }
return http_response_on_header(stream, nv, token, trailer); return http_response_on_header(stream, nv, trailer);
} }
int nghttp2_http_on_request_headers(nghttp2_stream *stream, int nghttp2_http_on_request_headers(nghttp2_stream *stream,
......
...@@ -36,8 +36,7 @@ ...@@ -36,8 +36,7 @@
/* /*
* This function is called when HTTP header field |nv| in |frame| is * This function is called when HTTP header field |nv| in |frame| is
* received for |stream|. This function will validate |nv| against * received for |stream|. This function will validate |nv| against
* the current state of stream. The |token| is nghttp2_token value * the current state of stream.
* for nv->name, or -1 if we don't have enum value for the name.
* *
* 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:
...@@ -49,7 +48,7 @@ ...@@ -49,7 +48,7 @@
* if it was not received because of compatibility reasons. * if it was not received because of compatibility reasons.
*/ */
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
nghttp2_frame *frame, nghttp2_nv *nv, int token, nghttp2_frame *frame, nghttp2_hd_nv *nv,
int trailer); int trailer);
/* /*
......
...@@ -52,6 +52,10 @@ void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) { ...@@ -52,6 +52,10 @@ void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) {
mem->free(ptr, mem->mem_user_data); mem->free(ptr, mem->mem_user_data);
} }
void nghttp2_mem_free2(nghttp2_free free, void *ptr, void *mem_user_data) {
free(ptr, mem_user_data);
}
void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) { void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) {
return mem->calloc(nmemb, size, mem->mem_user_data); return mem->calloc(nmemb, size, mem->mem_user_data);
} }
......
...@@ -38,6 +38,7 @@ nghttp2_mem *nghttp2_mem_default(void); ...@@ -38,6 +38,7 @@ nghttp2_mem *nghttp2_mem_default(void);
|mem|. */ |mem|. */
void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size); void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size);
void nghttp2_mem_free(nghttp2_mem *mem, void *ptr); void nghttp2_mem_free(nghttp2_mem *mem, void *ptr);
void nghttp2_mem_free2(nghttp2_free free, void *ptr, void *mem_user_data);
void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size); void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size);
void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size); void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size);
......
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_rcbuf.h"
#include <string.h>
#include <assert.h>
#include "nghttp2_mem.h"
int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size,
nghttp2_mem *mem) {
uint8_t *p;
p = nghttp2_mem_malloc(mem, sizeof(nghttp2_rcbuf) + size);
if (p == NULL) {
return NGHTTP2_ERR_NOMEM;
}
*rcbuf_ptr = (void *)p;
(*rcbuf_ptr)->mem_user_data = mem->mem_user_data;
(*rcbuf_ptr)->free = mem->free;
(*rcbuf_ptr)->base = p + sizeof(nghttp2_rcbuf);
(*rcbuf_ptr)->len = size;
(*rcbuf_ptr)->ref = 1;
return 0;
}
int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,
size_t srclen, nghttp2_mem *mem) {
int rv;
rv = nghttp2_rcbuf_new(rcbuf_ptr, srclen + 1, mem);
if (rv != 0) {
return rv;
}
memcpy((*rcbuf_ptr)->base, src, srclen);
(*rcbuf_ptr)->len = srclen;
(*rcbuf_ptr)->base[srclen] = '\0';
return 0;
}
/*
* Frees |rcbuf| itself, regardless of its reference cout.
*/
void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf) {
nghttp2_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data);
}
void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf) {
if (rcbuf->ref == -1) {
return;
}
++rcbuf->ref;
}
void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf) {
if (rcbuf == NULL || rcbuf->ref == -1) {
return;
}
assert(rcbuf->ref > 0);
if (--rcbuf->ref == 0) {
nghttp2_rcbuf_del(rcbuf);
}
}
nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) {
return (nghttp2_vec){rcbuf->base, rcbuf->len};
}
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef NGHTTP2_RCBUF_H
#define NGHTTP2_RCBUF_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
struct nghttp2_rcbuf {
/* custom memory allocator belongs to the mem parameter when
creating this object. */
void *mem_user_data;
nghttp2_free free;
/* The pointer to the underlying buffer */
uint8_t *base;
/* Size of buffer pointed by |base|. */
size_t len;
/* Reference count */
int32_t ref;
};
/*
* Allocates nghttp2_rcbuf object with |size| as initial buffer size.
* When the function succeeds, the reference count becomes 1.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM:
* Out of memory.
*/
int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, nghttp2_mem *mem);
/*
* Like nghttp2_rcbuf_new(), but initializes the buffer with |src| of
* length |srclen|. This function allocates additional byte at the
* end and puts '\0' into it, so that the resulting buffer could be
* used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to
* |srclen|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM:
* Out of memory.
*/
int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,
size_t srclen, nghttp2_mem *mem);
/*
* Frees |rcbuf| itself, regardless of its reference cout.
*/
void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf);
#endif /* NGHTTP2_RCBUF_H */
...@@ -3122,12 +3122,12 @@ static int session_call_on_begin_headers(nghttp2_session *session, ...@@ -3122,12 +3122,12 @@ static int session_call_on_begin_headers(nghttp2_session *session,
static int session_call_on_header(nghttp2_session *session, static int session_call_on_header(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,
const nghttp2_nv *nv) { const nghttp2_hd_nv *nv) {
int rv; int rv;
if (session->callbacks.on_header_callback) { if (session->callbacks.on_header_callback) {
rv = session->callbacks.on_header_callback( rv = session->callbacks.on_header_callback(
session, frame, nv->name, nv->namelen, nv->value, nv->valuelen, session, frame, nv->name->base, nv->name->len, nv->value->base,
nv->flags, session->user_data); nv->value->len, nv->flags, session->user_data);
if (rv == NGHTTP2_ERR_PAUSE || if (rv == NGHTTP2_ERR_PAUSE ||
rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
return rv; return rv;
...@@ -3317,11 +3317,10 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, ...@@ -3317,11 +3317,10 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
ssize_t proclen; ssize_t proclen;
int rv; int rv;
int inflate_flags; int inflate_flags;
nghttp2_nv nv; nghttp2_hd_nv nv;
nghttp2_stream *stream; nghttp2_stream *stream;
nghttp2_stream *subject_stream; nghttp2_stream *subject_stream;
int trailer = 0; int trailer = 0;
int token;
*readlen_ptr = 0; *readlen_ptr = 0;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id); stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
...@@ -3338,7 +3337,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, ...@@ -3338,7 +3337,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
for (;;) { for (;;) {
inflate_flags = 0; inflate_flags = 0;
proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags, proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags,
&token, in, inlen, final); in, inlen, final);
if (nghttp2_is_fatal((int)proclen)) { if (nghttp2_is_fatal((int)proclen)) {
return (int)proclen; return (int)proclen;
} }
...@@ -3373,13 +3372,13 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, ...@@ -3373,13 +3372,13 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
rv = 0; rv = 0;
if (subject_stream && session_enforce_http_messaging(session)) { if (subject_stream && session_enforce_http_messaging(session)) {
rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, token, rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
trailer); trailer);
if (rv == NGHTTP2_ERR_HTTP_HEADER) { if (rv == NGHTTP2_ERR_HTTP_HEADER) {
DEBUGF(fprintf( DEBUGF(fprintf(
stderr, "recv: HTTP error: type=%d, id=%d, header %.*s: %.*s\n", stderr, "recv: HTTP error: type=%d, id=%d, header %.*s: %.*s\n",
frame->hd.type, subject_stream->stream_id, (int)nv.namelen, frame->hd.type, subject_stream->stream_id, (int)nv.name->len,
nv.name, (int)nv.valuelen, nv.value)); nv.name->base, (int)nv.value->len, nv.value->base));
rv = rv =
session_handle_invalid_stream2(session, subject_stream->stream_id, session_handle_invalid_stream2(session, subject_stream->stream_id,
...@@ -3394,8 +3393,8 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, ...@@ -3394,8 +3393,8 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
/* header is ignored */ /* header is ignored */
DEBUGF(fprintf( DEBUGF(fprintf(
stderr, "recv: HTTP ignored: type=%d, id=%d, header %.*s: %.*s\n", stderr, "recv: HTTP ignored: type=%d, id=%d, header %.*s: %.*s\n",
frame->hd.type, subject_stream->stream_id, (int)nv.namelen, frame->hd.type, subject_stream->stream_id, (int)nv.name->len,
nv.name, (int)nv.valuelen, nv.value)); nv.name->base, (int)nv.value->len, nv.value->base));
} }
} }
if (rv == 0) { if (rv == 0) {
......
...@@ -295,11 +295,6 @@ void test_nghttp2_hd_inflate_indname_inc(void) { ...@@ -295,11 +295,6 @@ void test_nghttp2_hd_inflate_indname_inc(void) {
assert_nv_equal(&nv, out.nva, 1, mem); assert_nv_equal(&nv, out.nva, 1, mem);
CU_ASSERT(1 == inflater.ctx.hd_table.len); CU_ASSERT(1 == inflater.ctx.hd_table.len);
CU_ASSERT(62 == nghttp2_hd_inflate_get_num_table_entries(&inflater)); CU_ASSERT(62 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
assert_nv_equal(&nv,
&nghttp2_hd_table_get(&inflater.ctx,
NGHTTP2_STATIC_TABLE_LENGTH +
inflater.ctx.hd_table.len - 1)->nv,
1, mem);
assert_nv_equal(&nv, nghttp2_hd_inflate_get_table_entry( assert_nv_equal(&nv, nghttp2_hd_inflate_get_table_entry(
&inflater, NGHTTP2_STATIC_TABLE_LENGTH + &inflater, NGHTTP2_STATIC_TABLE_LENGTH +
inflater.ctx.hd_table.len), inflater.ctx.hd_table.len),
...@@ -429,10 +424,9 @@ void test_nghttp2_hd_inflate_newname_inc(void) { ...@@ -429,10 +424,9 @@ void test_nghttp2_hd_inflate_newname_inc(void) {
CU_ASSERT(1 == out.nvlen); CU_ASSERT(1 == out.nvlen);
assert_nv_equal(&nv, out.nva, 1, mem); assert_nv_equal(&nv, out.nva, 1, mem);
CU_ASSERT(1 == inflater.ctx.hd_table.len); CU_ASSERT(1 == inflater.ctx.hd_table.len);
assert_nv_equal(&nv, assert_nv_equal(&nv, nghttp2_hd_inflate_get_table_entry(
&nghttp2_hd_table_get(&inflater.ctx, &inflater, NGHTTP2_STATIC_TABLE_LENGTH +
NGHTTP2_STATIC_TABLE_LENGTH + inflater.ctx.hd_table.len),
inflater.ctx.hd_table.len - 1)->nv,
1, mem); 1, mem);
nva_out_reset(&out, mem); nva_out_reset(&out, mem);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment