Flesh our packing/unpacking

This commit is contained in:
Lucas Pardue 2017-04-24 18:44:52 +01:00
parent 2a0cf7c8d8
commit fbc1e17f7b
4 changed files with 252 additions and 1 deletions

View File

@ -763,6 +763,71 @@ int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,
return 0;
}
int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) {
int rv;
nghttp2_buf *buf;
nghttp2_ext_origin *origin_frame;
origin_frame = frame->payload;
buf = &bufs->head->buf;
assert(nghttp2_buf_avail(buf) >= 2 + origin_frame->origin_len);
buf->pos -= NGHTTP2_FRAME_HDLEN;
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
nghttp2_put_uint16be(buf->last, (uint16_t)origin_frame->origin_len);
buf->last += 2;
rv = nghttp2_bufs_add(bufs, origin_frame->origin, origin_frame->origin_len);
assert(rv == 0);
return 0;
}
void nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
size_t origin_len, uint8_t *payload,
size_t payloadlen) {
nghttp2_ext_origin *origin_frame;
uint8_t *p;
origin_frame = frame->payload;
p = payload;
origin_frame->origin = p;
p += origin_len;
origin_frame->origin_len = origin_len;
}
int nghttp2_frame_unpack_origin_payload2(nghttp2_extension *frame,
const uint8_t *payload,
size_t payloadlen, nghttp2_mem *mem) {
uint8_t *buf;
size_t origin_len;
if (payloadlen < 2) {
return NGHTTP2_FRAME_SIZE_ERROR;
}
origin_len = nghttp2_get_uint16(payload);
buf = nghttp2_mem_malloc(mem, payloadlen - 2);
if (!buf) {
return NGHTTP2_ERR_NOMEM;
}
nghttp2_cpymem(buf, payload + 2, payloadlen - 2);
nghttp2_frame_unpack_origin_payload(frame, origin_len, buf, payloadlen - 2);
return 0;
}
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
size_t niv, nghttp2_mem *mem) {
nghttp2_settings_entry *iv_copy;

View File

@ -393,6 +393,45 @@ int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,
const uint8_t *payload,
size_t payloadlen, nghttp2_mem *mem);
/*
* Packs ORIGIN frame |frame| in wire frame format and store it in
* |bufs|.
*
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
* before calling this function.
*
* This function always succeeds and returns 0.
*/
int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *ext);
/*
* Unpacks ORIGIN wire format into |frame|. The |payload| of
* |payloadlen| bytes contains frame payload. This function assumes
* that frame->payload points to the nghttp2_ext_origin object.
*
* This function always succeeds and returns 0.
*/
void nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
size_t origin_len, uint8_t *payload,
size_t payloadlen);
/*
* Unpacks ORIGIN wire format into |frame|. This function only exists
* for unit test. After allocating buffer for fields, this function
* internally calls nghttp2_frame_unpack_origin_payload().
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
* NGHTTP2_ERR_FRAME_SIZE_ERROR
* The payload is too small.
*/
int nghttp2_frame_unpack_origin_payload2(nghttp2_extension *frame,
const uint8_t *payload,
size_t payloadlen, nghttp2_mem *mem);
/*
* Initializes HEADERS frame |frame| with given values. |frame| takes
* ownership of |nva|, so caller must not free it. If |stream_id| is

View File

@ -1745,6 +1745,29 @@ static int session_predicate_altsvc_send(nghttp2_session *session,
return 0;
}
static int session_predicate_origin_send(nghttp2_session *session,
int32_t stream_id) {
nghttp2_stream *stream;
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
if (stream_id != 0) {
return 0;
}
stream = nghttp2_session_get_stream(session, stream_id);
if (stream == NULL) {
return NGHTTP2_ERR_STREAM_CLOSED;
}
if (stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING;
}
return 0;
}
/* Take into account settings max frame size and both connection-level
flow control here */
static ssize_t
@ -2275,6 +2298,15 @@ static int session_prep_frame(nghttp2_session *session,
nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext);
return 0;
case NGHTTP2_ORIGIN:
rv = session_predicate_origin_send(session, frame->hd.stream_id);
if (rv != 0) {
return rv;
}
nghttp2_frame_pack_origin(&session->aob.framebufs, &frame->ext);
return 0;
default:
/* Unreachable here */
@ -4833,6 +4865,46 @@ static int session_process_altsvc_frame(nghttp2_session *session) {
return nghttp2_session_on_altsvc_received(session, frame);
}
int nghttp2_session_on_origin_received(nghttp2_session *session,
nghttp2_frame *frame) {
nghttp2_ext_origin *origin_frame;
nghttp2_stream *stream;
origin_frame = frame->ext.payload;
/* session->server case has been excluded */
if (frame->hd.stream_id == 0) {
return 0;
} else {
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if (!stream) {
return 0;
}
if (stream->state == NGHTTP2_STREAM_CLOSING) {
return 0;
}
}
return session_call_on_frame_received(session, frame);
}
static int session_process_origin_frame(nghttp2_session *session) {
nghttp2_inbound_frame *iframe = &session->iframe;
nghttp2_frame *frame = &iframe->frame;
nghttp2_frame_unpack_origin_payload(
&frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos,
nghttp2_buf_len(&iframe->lbuf));
/* nghttp2_frame_unpack_altsvc_payload steals buffer from
iframe->lbuf */
nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
return nghttp2_session_on_origin_received(session, frame);
}
static int session_process_extension_frame(nghttp2_session *session) {
int rv;
nghttp2_inbound_frame *iframe = &session->iframe;
@ -6031,6 +6103,37 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
break;
}
case NGHTTP2_ORIGIN: {
size_t origin_len;
origin_len = nghttp2_get_uint16(iframe->sbuf.pos);
DEBUGF("recv: origin_len=%zu\n", origin_len);
if (2 + origin_len > iframe->payloadleft) {
busy = 1;
iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
break;
}
if (iframe->frame.hd.length > 2) {
iframe->raw_lbuf =
nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2);
if (iframe->raw_lbuf == NULL) {
return NGHTTP2_ERR_NOMEM;
}
nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
iframe->frame.hd.length);
}
busy = 1;
iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD;
break;
}
default:
/* This is unknown frame */
session_inbound_frame_reset(session);
@ -6584,6 +6687,36 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
session_inbound_frame_reset(session);
break;
case NGHTTP2_IB_READ_ORIGIN_PAYLOAD:
DEBUGF("recv: [IB_READ_ORIGIN_PAYLOAD]\n");
readlen = inbound_frame_payload_readlen(iframe, in, last);
if (readlen > 0) {
iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
iframe->payloadleft -= readlen;
in += readlen;
}
DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
iframe->payloadleft);
if (iframe->payloadleft) {
assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
break;
}
rv = session_process_origin_frame(session);
if (nghttp2_is_fatal(rv)) {
return rv;
}
session_inbound_frame_reset(session);
break;
}

View File

@ -122,6 +122,7 @@ typedef enum {
NGHTTP2_IB_IGN_DATA,
NGHTTP2_IB_IGN_ALL,
NGHTTP2_IB_READ_ALTSVC_PAYLOAD,
NGHTTP2_IB_READ_ORIGIN_PAYLOAD,
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
} nghttp2_inbound_state;
@ -738,7 +739,7 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
nghttp2_frame *frame);
/*
* Called when ALTSVC is recieved, assuming |frame| is properly
* Called when ALTSVC is received, assuming |frame| is properly
* initialized.
*
* This function returns 0 if it succeeds, or one of the following
@ -750,6 +751,19 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
int nghttp2_session_on_altsvc_received(nghttp2_session *session,
nghttp2_frame *frame);
/*
* Called when ORIGIN is received, assuming |frame| is properly
* initialized.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_CALLBACK_FAILURE
* The callback function failed.
*/
int nghttp2_session_on_origin_received(nghttp2_session *session,
nghttp2_frame *frame);
/*
* Called when DATA is received, assuming |frame| is properly
* initialized.