* Most tests work with HTTP/2 support using nghttp2

* except non-ssl http/1->/2 upgrade
* Restconf: ensure HEAD method works everywhere GET does.
This commit is contained in:
Olof hagsand 2021-06-08 20:53:43 +02:00
parent b680e3c5ac
commit 84f5762ab5
59 changed files with 1683 additions and 1107 deletions

View file

@ -32,6 +32,18 @@
***** END LICENSE BLOCK *****
* nghttp2 callback mechanism
*
* nghttp2_session_mem_recv()
* on_begin_headers_callback()
* create sd
* on_header_callback() NGHTTP2_HEADERS
* translate all headers
* on_data_chunk_recv_callback
* get indata
* on_frame_recv_callback NGHTTP2_FLAG_END_STREAM
* get method and call handler
* create rr
*/
#ifdef HAVE_CONFIG_H
@ -93,53 +105,23 @@
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
static restconf_stream_data *
restconf_stream_data_new(restconf_conn_h *rc,
int32_t stream_id)
{
restconf_stream_data *sd;
sd = malloc(sizeof(restconf_stream_data));
memset(sd, 0, sizeof(restconf_stream_data));
sd->sd_stream_id = stream_id;
sd->sd_fd = -1;
INSQ(sd, rc->rc_streams);
return sd;
}
#ifdef NOTUSED
static void
delete_http2_stream_data(restconf_stream_data *sd)
{
if (sd->fd != -1) {
close(sd->fd);
}
free(sd->request_path);
free(sd);
}
#endif
#ifdef NOTUSED
static int
send_client_connection_header(nghttp2_session *session)
{
int retval = -1;
nghttp2_settings_entry iv[1] = {
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
int rv;
clicon_debug(1, "%s", __FUNCTION__);
/* client 24 bytes magic string will be sent by nghttp2 library */
rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, ARRLEN(iv));
if (rv != 0) {
clicon_err(OE_XML, 0, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
goto done;
}
retval = 0;
done:
return retval;
}
#endif /* NOTUSED */
/*! Map http2 frame types in nghttp2
* I had expected it in in libnghttp2 but havent found it
*/
static const map_str2int nghttp2_frame_type_map[] = {
{"DATA", NGHTTP2_DATA},
{"HEADERS", NGHTTP2_HEADERS},
{"PRIORITY", NGHTTP2_PRIORITY},
{"RST_STREAM", NGHTTP2_RST_STREAM},
{"SETTINGS", NGHTTP2_SETTINGS},
{"PUSH_PROMISE", NGHTTP2_PUSH_PROMISE},
{"PING", NGHTTP2_PING},
{"GOAWAY", NGHTTP2_GOAWAY},
{"WINDOW_UPDATE", NGHTTP2_WINDOW_UPDATE},
{"CONTINUATION", NGHTTP2_CONTINUATION},
{"ALTSVC", NGHTTP2_ALTSVC},
{NULL, -1}
};
/* Clixon error category specialized log callback for nghttp2
* @param[in] handle Application-specific handle
@ -156,7 +138,6 @@ clixon_nghttp2_log_cb(void *handle,
return 0;
}
#ifdef NOTUSED
static void
nghttp2_print_header(const uint8_t *name,
@ -181,35 +162,94 @@ nghttp2_print_headers(nghttp2_nv *nva,
}
#endif /* NOTUSED */
/*! Transmit the |data|, |length| bytes, to the network.
/*! Send data to remote peer, Send at most the |length| bytes of |data|.
* This callback is required if the application uses
* `nghttp2_session_send()` to send data to the remote endpoint. If
* the application uses solely `nghttp2_session_mem_send()` instead,
* this callback function is unnecessary.
* XXX see buf_write
*/
static ssize_t
send_callback(nghttp2_session *session,
const uint8_t *data,
size_t length,
int flags,
void *user_data)
session_send_callback(nghttp2_session *session,
const uint8_t *buf,
size_t buflen,
int flags,
void *user_data)
{
restconf_conn_h *rc = (restconf_conn_h *)user_data;
int ret;
int retval = -1;
restconf_conn *rc = (restconf_conn *)user_data;
int er;
ssize_t len;
ssize_t totlen = 0;
int s;
SSL *ssl;
clicon_debug(1, "%s %zu:", __FUNCTION__, length);
#if 0
{
int i;
for (i=0; i<length; i++)
fprintf(stderr, "%02x", data[i]&255);
fprintf(stderr, "\n");
}
clicon_debug(1, "%s buflen:%lu", __FUNCTION__, buflen);
s = rc->rc_s;
ssl = rc->rc_ssl;
while (totlen < buflen){
if (ssl){
if ((len = SSL_write(ssl, buf+totlen, buflen-totlen)) <= 0){
er = errno;
switch (SSL_get_error(ssl, len)){
case SSL_ERROR_SYSCALL: /* 5 */
if (er == ECONNRESET) {/* Connection reset by peer */
if (ssl)
SSL_free(ssl);
close(s);
// XXX clixon_event_unreg_fd(s, restconf_connection);
goto ok; /* Close socket and ssl */
}
else if (er == EAGAIN){
clicon_debug(1, "%s write EAGAIN", __FUNCTION__);
usleep(10000);
continue;
}
else{
clicon_err(OE_RESTCONF, er, "SSL_write %d", er);
goto done;
}
break;
default:
clicon_err(OE_SSL, 0, "SSL_write");
goto done;
break;
}
goto done;
}
}
else{
if ((len = write(s, buf+totlen, buflen-totlen)) < 0){
if (errno == EAGAIN){
clicon_debug(1, "%s write EAGAIN", __FUNCTION__);
usleep(10000);
continue;
}
#if 1
else if (errno == ECONNRESET) {/* Connection reset by peer */
close(s);
// XXX clixon_event_unreg_fd(s, restconf_connection);
goto ok; /* Close socket and ssl */
}
#endif
/* encrypt & send message */
if ((ret = SSL_write(rc->rc_ssl, data, length)) < 0)
return ret;
return ret;
else{
clicon_err(OE_UNIX, errno, "write");
goto done;
}
}
assert(len != 0);
}
totlen += len;
} /* while */
ok:
retval = 0;
done:
if (retval < 0){
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;
}
clicon_debug(1, "%s retval:%lu", __FUNCTION__, totlen);
return totlen;
}
/*! Invoked when |session| wants to receive data from the remote peer.
@ -221,7 +261,7 @@ recv_callback(nghttp2_session *session,
int flags,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
@ -239,31 +279,57 @@ recv_callback(nghttp2_session *session,
* 2) terminating the process?
*/
static int
restconf_nghttp2_root(restconf_conn_h *rc)
restconf_nghttp2_path(restconf_stream_data *sd)
{
int retval = -1;
clicon_handle h;
// int ret;
cvec *qvec = NULL;
int retval = -1;
clicon_handle h;
cvec *qvec = NULL;
char *query = NULL;
restconf_conn *rc;
char *oneline = NULL;
cvec *cvv = NULL;
char *cn;
clicon_debug(1, "------------");
rc = sd->sd_conn;
if ((h = rc->rc_h) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
goto done;
}
/* get accepted connection */
/* Query vector, ie the ?a=x&b=y stuff */
if ((qvec = cvec_new(0)) ==NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;
query = restconf_param_get(h, "REQUEST_URI");
if ((query = index(query, '?')) != NULL){
query++;
if (strlen(query) &&
uri_str2cvec(query, '&', '=', 1, &qvec) < 0)
goto done;
}
/* Slightly awkward way of taking SSL cert subject and CN and add it to restconf parameters
* instead of accessing it directly */
if (rc->rc_ssl != NULL){
/* SSL subject fields, eg CN (Common Name) , can add more here? */
if (ssl_x509_name_oneline(rc->rc_ssl, &oneline) < 0)
goto done;
if (oneline != NULL) {
if (uri_str2cvec(oneline, '/', '=', 1, &cvv) < 0)
goto done;
if ((cn = cvec_find_str(cvv, "CN")) != NULL){
if (restconf_param_set(h, "SSL_CN", cn) < 0)
goto done;
}
}
}
/* call generic function */
if (api_root_restconf(h, rc, qvec) < 0)
if (strcmp(sd->sd_path, RESTCONF_WELL_KNOWN) == 0){
if (api_well_known(h, sd) < 0)
goto done;
}
else if (api_root_restconf(h, sd, qvec) < 0)
goto done;
// /* Clear (fcgi) paramaters from this request */
// if (restconf_param_del_all(h) < 0)
// goto done;
/* Clear (fcgi) paramaters from this request */
if (restconf_param_del_all(h) < 0)
goto done;
retval = 0;
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
@ -271,12 +337,123 @@ restconf_nghttp2_root(restconf_conn_h *rc)
// if (retval < 0){
// evhtp_send_reply(req, EVHTP_RES_ERROR);
// }
if (cvv)
cvec_free(cvv);
if (oneline)
free(oneline);
if (qvec)
cvec_free(qvec);
return retval; /* void */
}
/*!
/*! data callback, just pass pointer to cbuf
* XXX handle several chunks with cbuf
*/
static ssize_t
restconf_sd_read(nghttp2_session *session,
int32_t stream_id,
uint8_t *buf,
size_t length,
uint32_t *data_flags,
nghttp2_data_source *source,
void *user_data)
{
restconf_stream_data *sd = (restconf_stream_data *)source->ptr;
cbuf *cb;
size_t len = 0;
size_t remain;
if ((cb = sd->sd_body) == NULL){ /* shouldnt happen */
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
return 0;
}
#if 0
if (cbuf_len(cb) <= length){
len = remain;
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
else{
len = length;
}
memcpy(buf, cbuf_get(cb) + sd->sd_body_offset, len);
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
return len;
#endif
assert(cbuf_len(cb) > sd->sd_body_offset);
remain = cbuf_len(cb) - sd->sd_body_offset;
clicon_debug(1, "%s length:%lu totlen:%d, offset:%lu remain:%lu",
__FUNCTION__,
length,
cbuf_len(cb),
sd->sd_body_offset,
remain);
if (remain <= length){
len = remain;
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
else{
len = length;
}
memcpy(buf, cbuf_get(cb) + sd->sd_body_offset, len);
sd->sd_body_offset += len;
clicon_debug(1, "%s retval:%lu", __FUNCTION__, len);
return len;
}
static int
restconf_submit_response(nghttp2_session *session,
restconf_conn *rc,
int stream_id,
restconf_stream_data *sd)
{
int retval = -1;
nghttp2_data_provider data_prd;
nghttp2_error ngerr;
cg_var *cv;
nghttp2_nv *hdrs;
nghttp2_nv *hdr;
int i = 0;
char valstr[16];
clicon_debug(1, "%s", __FUNCTION__);
data_prd.source.ptr = sd;
data_prd.read_callback = restconf_sd_read;
if ((hdrs = (nghttp2_nv*)calloc(1+cvec_len(sd->sd_outp_hdrs), sizeof(nghttp2_nv))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
hdr = &hdrs[i++];
hdr->name = (uint8_t*)":status";
snprintf(valstr, 15, "%u", sd->sd_code);
hdr->value = (uint8_t*)valstr;
hdr->namelen = strlen(":status");
hdr->valuelen = strlen(valstr);
clicon_debug(1, "%s val:'%s' valuelen:%lu", __FUNCTION__, hdr->value, hdr->valuelen);
hdr->flags = 0;
cv = NULL;
while ((cv = cvec_each(sd->sd_outp_hdrs, cv)) != NULL){
hdr = &hdrs[i++];
hdr->name = (uint8_t*)cv_name_get(cv);
hdr->value = (uint8_t*)cv_string_get(cv);
hdr->namelen = strlen(cv_name_get(cv));
hdr->valuelen = strlen(cv_string_get(cv));
hdr->flags = 0;
}
if ((ngerr = nghttp2_submit_response(session,
stream_id,
hdrs, i,
(data_prd.source.ptr != NULL)?&data_prd:NULL)) < 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_submit_response");
goto done;
}
retval = 0;
done:
return retval;
}
/*! A frame is received
*/
static int
on_frame_recv_callback(nghttp2_session *session,
@ -284,30 +461,39 @@ on_frame_recv_callback(nghttp2_session *session,
void *user_data)
{
int retval = -1;
restconf_conn_h *rc = (restconf_conn_h *)user_data;
restconf_stream_data *sd;
char *path;
restconf_conn *rc = (restconf_conn *)user_data;
restconf_stream_data *sd = NULL;
clicon_debug(1, "%s %d", __FUNCTION__, frame->hd.stream_id);
clicon_debug(1, "%s %s %d", __FUNCTION__,
clicon_int2str(nghttp2_frame_type_map, frame->hd.type),
frame->hd.stream_id);
switch (frame->hd.type) {
case NGHTTP2_DATA:
case NGHTTP2_HEADERS:
/* Check that the client request has finished */
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
/* For DATA and HEADERS frame, this callback may be called after
on_stream_close_callback. Check that stream still alive. */
on_stream_close_callback. Check that stream still alive.
*/
if ((sd = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) == NULL)
return 0;
if ((path = restconf_uripath(rc->rc_h)) == NULL)
if ((sd->sd_path = restconf_uripath(rc->rc_h)) == NULL)
goto ok;
if (strcmp(path, "/" RESTCONF_API) == 0){
if (restconf_nghttp2_root(rc) < 0)
sd->sd_proto = HTTP_2; /* XXX is this necessary? */
if (strncmp(sd->sd_path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0 ||
strcmp(sd->sd_path, RESTCONF_WELL_KNOWN) == 0){
if (restconf_nghttp2_path(sd) < 0)
goto done;
}
else if (strcmp(path, RESTCONF_WELL_KNOWN) == 0){
}
else
; /* ignore */
if (sd->sd_code){
if (restconf_submit_response(session, rc, frame->hd.stream_id, sd) < 0)
goto done;
}
else {
/* 500 Internal server error ? */
}
}
break;
default:
@ -319,7 +505,7 @@ on_frame_recv_callback(nghttp2_session *session,
return retval;
}
/*!
/*! An invalid non-DATA frame is received.
*/
static int
on_invalid_frame_recv_callback(nghttp2_session *session,
@ -327,12 +513,12 @@ on_invalid_frame_recv_callback(nghttp2_session *session,
int lib_error_code,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
/*!
/*! A chunk of data in DATA frame is received
*/
static int
on_data_chunk_recv_callback(nghttp2_session *session,
@ -342,41 +528,41 @@ on_data_chunk_recv_callback(nghttp2_session *session,
size_t len,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
restconf_conn *rc = (restconf_conn *)user_data;
restconf_stream_data *sd;
clicon_debug(1, "%s %d", __FUNCTION__, stream_id);
// if (sd->sd_session == session &&
// sd->sd_stream_id == stream_id)
// fwrite(data, 1, len, stdout); /* This is where data is printed */
if ((sd = restconf_stream_find(rc, stream_id)) != NULL){
cbuf_append_buf(sd->sd_indata, (void*)data, len);
}
return 0;
}
/*!
/*! Just before the non-DATA frame |frame| is sent
*/
static int
before_frame_send_callback(nghttp2_session *session,
const nghttp2_frame *frame,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
/*!
/*! After the frame |frame| is sent
*/
static int
on_frame_send_callback(nghttp2_session *session,
const nghttp2_frame *frame,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
/*!
/*! After the non-DATA frame |frame| is not sent because of error
*/
static int
on_frame_not_send_callback(nghttp2_session *session,
@ -384,12 +570,12 @@ on_frame_not_send_callback(nghttp2_session *session,
int lib_error_code,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
/*!
/*! Stream |stream_id| is closed.
*/
static int
on_stream_close_callback(nghttp2_session *session,
@ -397,7 +583,7 @@ on_stream_close_callback(nghttp2_session *session,
nghttp2_error_code error_code,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
//session_data *sd = (session_data*)user_data;
return 0;
@ -410,10 +596,10 @@ on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
void *user_data)
{
restconf_conn_h *rc = (restconf_conn_h *)user_data;
restconf_conn *rc = (restconf_conn *)user_data;
restconf_stream_data *sd;
clicon_debug(1, "%s", __FUNCTION__);
clicon_debug(1, "%s %s", __FUNCTION__, clicon_int2str(nghttp2_frame_type_map, frame->hd.type));
if (frame->hd.type == NGHTTP2_HEADERS &&
frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
sd = restconf_stream_data_new(rc, frame->hd.stream_id);
@ -422,45 +608,6 @@ on_begin_headers_callback(nghttp2_session *session,
return 0;
}
#ifdef XXX
/*! Translate http header by capitalizing, prepend w HTTP_ and - -> _
* Example: Host -> HTTP_HOST
*/
static int
evhtp_convert_fcgi(evhtp_header_t *hdr,
void *arg)
{
int retval = -1;
clicon_handle h = (clicon_handle)arg;
cbuf *cb = NULL;
int i;
char c;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* convert key name */
cprintf(cb, "HTTP_");
for (i=0; i<strlen(hdr->key); i++){
c = hdr->key[i] & 0xff;
if (islower(c))
cprintf(cb, "%c", toupper(c));
else if (c == '-')
cprintf(cb, "_");
else
cprintf(cb, "%c", c);
}
if (restconf_param_set(h, cbuf_get(cb), hdr->val) < 0)
goto done;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
#endif
/*! Map from nghttp2 headers to "fcgi" type parameters used in clixon code
* Both |name| and |value| are guaranteed to be NULL-terminated.
*/
@ -472,8 +619,8 @@ nghttp2_hdr2clixon(clicon_handle h,
int retval = -1;
if (strcmp(name, ":path") == 0){
/* XXX "/restconf" Is PATH really REQUEST_URI? */
if (restconf_param_set(h, "REQUEST_URI", value) < 0) /* XXX string? */
/* Including ?args, call restconf_uripath() to get only path */
if (restconf_param_set(h, "REQUEST_URI", value) < 0)
goto done;
}
else if (strcmp(name, ":method") == 0){
@ -513,20 +660,17 @@ on_header_callback(nghttp2_session *session,
void *user_data)
{
int retval = -1;
restconf_conn_h *rc = (restconf_conn_h *)user_data;
restconf_stream_data *sd;
restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s %d:", __FUNCTION__, frame->hd.stream_id);
switch (frame->hd.type){
case NGHTTP2_HEADERS:
assert (frame->headers.cat == NGHTTP2_HCAT_REQUEST);
clicon_debug(1, "%s %s %s", __FUNCTION__, name, value);
if ((sd = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) == NULL)
break;
clicon_debug(1, "%s HEADERS %s %s", __FUNCTION__, name, value);
if (nghttp2_hdr2clixon(rc->rc_h, (char*)name, (char*)value) < 0)
goto done;
break;
default:
clicon_debug(1, "%s %s %s", __FUNCTION__, clicon_int2str(nghttp2_frame_type_map, frame->hd.type), name);
break;
}
retval = 0;
@ -534,7 +678,8 @@ on_header_callback(nghttp2_session *session,
return retval;
}
/*!
#ifdef NOTUSED
/*! How many padding bytes are required for the transmission of the |frame|?
*/
static ssize_t
select_padding_callback(nghttp2_session *session,
@ -542,12 +687,12 @@ select_padding_callback(nghttp2_session *session,
size_t max_payloadlen,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
return frame->hd.length;
}
/*!
/*! Get max length of data to send data to the remote peer
*/
static ssize_t
data_source_read_length_callback(nghttp2_session *session,
@ -558,10 +703,11 @@ data_source_read_length_callback(nghttp2_session *session,
uint32_t remote_max_frame_size,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
#endif /* NOTUSED */
/*! Invoked when a frame header is received.
* Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will
@ -572,15 +718,17 @@ on_begin_frame_callback(nghttp2_session *session,
const nghttp2_frame_hd *hd,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
clicon_debug(1, "%s type:%d", __FUNCTION__, hd->type);
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s %s", __FUNCTION__, clicon_int2str(nghttp2_frame_type_map, hd->type));
if (hd->type == NGHTTP2_CONTINUATION)
assert(0);
return 0;
}
/*!
/*! Send complete DATA frame for no-copy
* Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is
* used in :type:`nghttp2_data_source_read_callback` to send complete
* DATA frame.
*/
static int
send_data_callback(nghttp2_session *session,
@ -589,12 +737,13 @@ send_data_callback(nghttp2_session *session,
nghttp2_data_source *source,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
/*!
#ifdef NOTUSED
/*! Pack extension payload in its wire format
*/
static ssize_t
pack_extension_callback(nghttp2_session *session,
@ -602,12 +751,12 @@ pack_extension_callback(nghttp2_session *session,
const nghttp2_frame *frame,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
/*!
/*! Unpack extension payload from its wire format.
*/
static int
unpack_extension_callback(nghttp2_session *session,
@ -615,12 +764,13 @@ unpack_extension_callback(nghttp2_session *session,
const nghttp2_frame_hd *hd,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
#endif /* NOTUSED */
/*!
/*! Chunk of extension frame payload is received
*/
static int
on_extension_chunk_recv_callback(nghttp2_session *session,
@ -629,25 +779,12 @@ on_extension_chunk_recv_callback(nghttp2_session *session,
size_t len,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
/*!
*/
static int
error_callback(nghttp2_session *session,
const char *msg,
size_t len,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
/*!
/*! Library provides the error code, and message for debugging purpose.
*/
static int
error_callback2(nghttp2_session *session,
@ -656,8 +793,9 @@ error_callback2(nghttp2_session *session,
size_t len,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
// restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
clicon_err(OE_NGHTTP2, lib_error_code, "%s", msg);
return 0;
}
@ -665,22 +803,57 @@ error_callback2(nghttp2_session *session,
* XXX see session_recv
*/
int
http2_recv(restconf_conn_h *rc,
http2_recv(restconf_conn *rc,
const unsigned char *buf,
size_t n)
{
int retval = -1;
nghttp2_error ngerr;
int retval = -1;
nghttp2_error ngerr;
clicon_debug(1, "%s", __FUNCTION__);
if (rc->rc_ngsession == NULL){
clicon_err(OE_RESTCONF, EINVAL, "No nghttp2 session");
/* http2_session_init not called */
clicon_err(OE_RESTCONF, EINVAL, "No nghttp2 session");
goto done;
}
/* may make additional pending frames */
if ((ngerr = nghttp2_session_mem_recv(rc->rc_ngsession, buf, n)) < 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_mem_recv");
goto done;
}
/* sends highest prio frame from outbound queue to remote peer. It does this as
* many as possible until user callback :type:`nghttp2_send_callback` returns
* * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty.
*/
if ((ngerr = nghttp2_session_send(rc->rc_ngsession)) != 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_send");
goto done;
}
retval = 0;
done:
return retval;
}
/* Send HTTP/2 client connection header, which includes 24 bytes
magic octets and SETTINGS frame */
int
http2_send_server_connection(restconf_conn *rc)
{
int retval = -1;
nghttp2_settings_entry iv[1] = {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
nghttp2_error ngerr;
if ((ngerr = nghttp2_submit_settings(rc->rc_ngsession,
NGHTTP2_FLAG_NONE,
iv,
ARRLEN(iv))) != 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_submit_settings");
goto done;
}
if ((ngerr = nghttp2_session_send(rc->rc_ngsession)) != 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_send");
goto done;
}
retval = 0;
done:
return retval;
@ -689,13 +862,15 @@ http2_recv(restconf_conn_h *rc,
/*! Initialize callbacks
*/
int
http2_session_init(restconf_conn_h *rc)
http2_session_init(restconf_conn *rc)
{
int retval = -1;
nghttp2_session_callbacks *callbacks = NULL;
nghttp2_session *session = NULL;
nghttp2_error ngerr;
nghttp2_session_callbacks_new(&callbacks);
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
nghttp2_session_callbacks_set_send_callback(callbacks, session_send_callback);
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback);
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(callbacks, on_invalid_frame_recv_callback);
@ -706,22 +881,31 @@ http2_session_init(restconf_conn_h *rc)
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);
nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, on_begin_headers_callback);
nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback);
#ifdef NOTUSED
nghttp2_session_callbacks_set_select_padding_callback(callbacks, select_padding_callback);
nghttp2_session_callbacks_set_data_source_read_length_callback(callbacks, data_source_read_length_callback);
#endif
nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks, on_begin_frame_callback);
nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_callback);
#ifdef NOTUSED
nghttp2_session_callbacks_set_pack_extension_callback(callbacks, pack_extension_callback);
nghttp2_session_callbacks_set_unpack_extension_callback(callbacks, unpack_extension_callback);
#endif
nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(callbacks, on_extension_chunk_recv_callback);
nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
nghttp2_session_callbacks_set_error_callback2(callbacks, error_callback2);
/* Register callbacks with nghttp2 */
nghttp2_session_server_new(&session, callbacks, rc);
/* Create session for server use, register callbacks */
if ((ngerr = nghttp2_session_server_new(&session, callbacks, rc)) < 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_server_new");
goto done;
}
nghttp2_session_callbacks_del(callbacks);
rc->rc_ngsession = session;
return 0;
retval = 0;
done:
return retval;
}
#endif /* HAVE_LIBNGHTTP2 */