RESTCONF notification for HTTP/2 native mode
This commit is contained in:
parent
3579d98243
commit
25e1bade8f
8 changed files with 286 additions and 207 deletions
|
|
@ -66,12 +66,13 @@
|
||||||
|
|
||||||
/*! Add HTTP header field name and value to reply
|
/*! Add HTTP header field name and value to reply
|
||||||
*
|
*
|
||||||
|
* Generic code, add to restconf/native struct. Specific http/1 or /2 code actually sends
|
||||||
* @param[in] req request handle
|
* @param[in] req request handle
|
||||||
* @param[in] name HTTP header field name
|
* @param[in] name HTTP header field name
|
||||||
* @param[in] vfmt HTTP header field value format string w variable parameter
|
* @param[in] vfmt HTTP header field value format string w variable parameter
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @see eg RFC 7230
|
* @see restconf_submit_response http/2 actual send function
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
restconf_reply_header(void *req0,
|
restconf_reply_header(void *req0,
|
||||||
|
|
@ -86,7 +87,6 @@ restconf_reply_header(void *req0,
|
||||||
char *value = NULL;
|
char *value = NULL;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "%s", name);
|
|
||||||
if (sd == NULL || name == NULL || vfmt == NULL){
|
if (sd == NULL || name == NULL || vfmt == NULL){
|
||||||
clixon_err(OE_CFG, EINVAL, "sd, name or value is NULL");
|
clixon_err(OE_CFG, EINVAL, "sd, name or value is NULL");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -95,6 +95,17 @@ restconf_reply_header(void *req0,
|
||||||
clixon_err(OE_CFG, EINVAL, "rc is NULL");
|
clixon_err(OE_CFG, EINVAL, "rc is NULL");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* If HTTP/2, filter some headers
|
||||||
|
* The following header fields must not appear: "Connection", "Keep-Alive", "Proxy-Connection",
|
||||||
|
* "Transfer-Encoding" and "Upgrade".
|
||||||
|
*/
|
||||||
|
if (rc->rc_proto == HTTP_2){ // NO http/2
|
||||||
|
if (strcmp(name, "Connection") == 0){
|
||||||
|
clixon_debug(CLIXON_DBG_RESTCONF, "Skip: %s: %s", name, value);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* First round: compute vlen and allocate value */
|
/* First round: compute vlen and allocate value */
|
||||||
va_start(ap, vfmt);
|
va_start(ap, vfmt);
|
||||||
vlen = vsnprintf(NULL, 0, vfmt, ap);
|
vlen = vsnprintf(NULL, 0, vfmt, ap);
|
||||||
|
|
@ -112,10 +123,12 @@ restconf_reply_header(void *req0,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
clixon_debug(CLIXON_DBG_RESTCONF, "%s: %s", name, value);
|
||||||
if (cvec_add_string(sd->sd_outp_hdrs, (char*)name, value) < 0){
|
if (cvec_add_string(sd->sd_outp_hdrs, (char*)name, value) < 0){
|
||||||
clixon_err(OE_RESTCONF, errno, "cvec_add_string");
|
clixon_err(OE_RESTCONF, errno, "cvec_add_string");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (value)
|
if (value)
|
||||||
|
|
@ -123,8 +136,9 @@ restconf_reply_header(void *req0,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Send HTTP reply with potential message body
|
/*! Assign values to HTTP reply with potential message body
|
||||||
*
|
*
|
||||||
|
* Generic code, add to restconf/native struct. Specific http/1 or /2 code actually sends
|
||||||
* @param[in] req http request handle
|
* @param[in] req http request handle
|
||||||
* @param[in] code Status code
|
* @param[in] code Status code
|
||||||
* @param[in] cb Body as a cbuf if non-NULL. Note: is consumed
|
* @param[in] cb Body as a cbuf if non-NULL. Note: is consumed
|
||||||
|
|
|
||||||
|
|
@ -387,7 +387,7 @@ restconf_connection_sanity(clixon_handle h,
|
||||||
|
|
||||||
/* Write buf to socket
|
/* Write buf to socket
|
||||||
*
|
*
|
||||||
* see also this function in restconf_api_openssl.c
|
* Only (at least mostly?) for HTTP/1
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] buf Buffer to write
|
* @param[in] buf Buffer to write
|
||||||
* @param[in] buflen Length of buffer
|
* @param[in] buflen Length of buffer
|
||||||
|
|
@ -419,7 +419,7 @@ native_buf_write(clixon_handle h,
|
||||||
* 1. they are not "strings" in the sense they are not NULL-terminated
|
* 1. they are not "strings" in the sense they are not NULL-terminated
|
||||||
* 2. they are often very long
|
* 2. they are often very long
|
||||||
*/
|
*/
|
||||||
if (clixon_debug_get()) {
|
if ((clixon_debug_get() & CLIXON_DBG_RESTCONF) != 0) {
|
||||||
char *dbgstr = NULL;
|
char *dbgstr = NULL;
|
||||||
size_t sz;
|
size_t sz;
|
||||||
sz = buflen>256?256:buflen; /* Truncate to 256 */
|
sz = buflen>256?256:buflen; /* Truncate to 256 */
|
||||||
|
|
@ -1071,7 +1071,7 @@ restconf_connection_close1(restconf_conn *rc)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
rsock = rc->rc_socket;
|
rsock = rc->rc_socket;
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "\"%s\"", rsock->rs_description);
|
clixon_debug(CLIXON_DBG_RESTCONF, "%s", rsock->rs_description?rsock->rs_description:"");
|
||||||
if (close(rc->rc_s) < 0){
|
if (close(rc->rc_s) < 0){
|
||||||
clixon_err(OE_UNIX, errno, "close");
|
clixon_err(OE_UNIX, errno, "close");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1390,34 +1390,6 @@ restconf_ssl_accept_client(clixon_handle h,
|
||||||
}
|
}
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "proto:%s", restconf_proto2str(proto));
|
clixon_debug(CLIXON_DBG_RESTCONF, "proto:%s", restconf_proto2str(proto));
|
||||||
|
|
||||||
#if 0 /* Seems too early to fail here, instead let authentication callback deal with this */
|
|
||||||
/* For client-cert authentication, check if any certs are present,
|
|
||||||
* if not, send bad request
|
|
||||||
* Alt: set SSL_CTX_set_verify(ctx, SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
|
|
||||||
* but then SSL_accept fails.
|
|
||||||
*/
|
|
||||||
if (restconf_auth_type_get(h) == CLIXON_AUTH_CLIENT_CERTIFICATE){
|
|
||||||
X509 *peercert;
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
|
||||||
if ((peercert = SSL_get_peer_certificate(rc->rc_ssl)) != NULL){
|
|
||||||
X509_free(peercert);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if ((peercert = SSL_get1_peer_certificate(rc->rc_ssl)) != NULL){
|
|
||||||
X509_free(peercert);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else { /* Get certificates (if available) */
|
|
||||||
if (proto != HTTP_2 &&
|
|
||||||
native_send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml",
|
|
||||||
"<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>protocol</error-type><error-tag>malformed-message</error-tag><error-message>Peer certificate required</error-message></error></errors>", rc->rc_socket, rc) < 0)
|
|
||||||
goto done;
|
|
||||||
if (restconf_close_ssl_socket(rc, __FUNCTION__, 0) < 0)
|
|
||||||
goto done;
|
|
||||||
goto closed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* Get the actual peer, XXX this maybe could be done in ca-auth client-cert code ?
|
/* Get the actual peer, XXX this maybe could be done in ca-auth client-cert code ?
|
||||||
* Note this _only_ works if SSL_set1_host() was set previously,...
|
* Note this _only_ works if SSL_set1_host() was set previously,...
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
|
* @see RFC9113
|
||||||
* nghttp2 callback mechanism
|
* nghttp2 callback mechanism
|
||||||
*
|
*
|
||||||
* nghttp2_session_mem_recv()
|
* nghttp2_session_mem_recv()
|
||||||
|
|
@ -112,9 +113,11 @@ static const map_str2int nghttp2_frame_type_map[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Clixon error category specialized log callback for nghttp2
|
/* Clixon error category specialized log callback for nghttp2
|
||||||
* @param[in] handle Application-specific handle
|
*
|
||||||
* @param[in] suberr Application-specific handle
|
* @param[in] handle Application-specific handle
|
||||||
* @param[out] cb Read log/error string into this buffer
|
* @param[in] suberr Application-specific handle
|
||||||
|
* @param[out] cb Read log/error string into this buffer
|
||||||
|
* @retval 0 OK
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clixon_nghttp2_log_cb(void *handle,
|
clixon_nghttp2_log_cb(void *handle,
|
||||||
|
|
@ -140,7 +143,7 @@ nghttp2_print_header(const uint8_t *name,
|
||||||
*
|
*
|
||||||
* Please note that this function does not
|
* Please note that this function does not
|
||||||
* take into account that header name and value are sequence of
|
* take into account that header name and value are sequence of
|
||||||
* octets, therefore they may contain non-printable characters.
|
* octets, therefore they may contain non-printable characters.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
nghttp2_print_headers(nghttp2_nv *nva,
|
nghttp2_print_headers(nghttp2_nv *nva,
|
||||||
|
|
@ -163,6 +166,8 @@ nghttp2_print_headers(nghttp2_nv *nva,
|
||||||
* If it cannot send any single byte without blocking,
|
* If it cannot send any single byte without blocking,
|
||||||
* it must return :enum:`NGHTTP2_ERR_WOULDBLOCK`.
|
* it must return :enum:`NGHTTP2_ERR_WOULDBLOCK`.
|
||||||
* For other errors, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
* For other errors, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static ssize_t
|
static ssize_t
|
||||||
session_send_callback(nghttp2_session *session,
|
session_send_callback(nghttp2_session *session,
|
||||||
|
|
@ -261,6 +266,9 @@ session_send_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Invoked when |session| wants to receive data from the remote peer.
|
/*! Invoked when |session| wants to receive data from the remote peer.
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static ssize_t
|
static ssize_t
|
||||||
recv_callback(nghttp2_session *session,
|
recv_callback(nghttp2_session *session,
|
||||||
|
|
@ -278,7 +286,7 @@ recv_callback(nghttp2_session *session,
|
||||||
*
|
*
|
||||||
* This are all messages except /.well-known,
|
* This are all messages except /.well-known,
|
||||||
*
|
*
|
||||||
* @param[in] sd session stream struct (http/1 has a single)
|
* @param[in] sd Restconf native stream struct
|
||||||
* @retval void
|
* @retval void
|
||||||
* Discussion: problematic if fatal error -1 is returned from clixon routines
|
* Discussion: problematic if fatal error -1 is returned from clixon routines
|
||||||
* without actually terminating. Consider:
|
* without actually terminating. Consider:
|
||||||
|
|
@ -360,11 +368,27 @@ restconf_nghttp2_path(restconf_stream_data *sd)
|
||||||
return retval; /* void */
|
return retval; /* void */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! data callback, just pass pointer to cbuf
|
/*! Data callback, just pass pointer to cbuf
|
||||||
*
|
*
|
||||||
* XXX handle several chunks with cbuf
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] stream_id Nghttp2 stream id
|
||||||
|
* @param[in] buf
|
||||||
|
* @param[in] length
|
||||||
|
* @param[in] data_flags
|
||||||
|
* @param[in] source
|
||||||
|
* @param[in] user_data User data, in effect Restconf stream data
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*
|
||||||
|
* If the application wants to postpone DATA frames (e.g.,
|
||||||
|
* asynchronous I/O, or reading data blocks for long time), it is
|
||||||
|
* achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED`
|
||||||
|
* without reading any data in this invocation. The library removes
|
||||||
|
* DATA frame from the outgoing queue temporarily. To move back
|
||||||
|
* deferred DATA frame to outgoing queue, call
|
||||||
|
* `nghttp2_session_resume_data()`.
|
||||||
*/
|
*/
|
||||||
static ssize_t
|
ssize_t
|
||||||
restconf_sd_read(nghttp2_session *session,
|
restconf_sd_read(nghttp2_session *session,
|
||||||
int32_t stream_id,
|
int32_t stream_id,
|
||||||
uint8_t *buf,
|
uint8_t *buf,
|
||||||
|
|
@ -374,27 +398,19 @@ restconf_sd_read(nghttp2_session *session,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
restconf_stream_data *sd = (restconf_stream_data *)source->ptr;
|
restconf_stream_data *sd = (restconf_stream_data *)source->ptr;
|
||||||
|
restconf_conn *rc = sd->sd_conn;
|
||||||
cbuf *cb;
|
cbuf *cb;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
size_t remain;
|
size_t remain;
|
||||||
|
|
||||||
|
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
||||||
if ((cb = sd->sd_body) == NULL){ /* shouldnt happen */
|
if ((cb = sd->sd_body) == NULL){ /* shouldnt happen */
|
||||||
|
if (rc->rc_event_stream && rc->rc_exit == 0) {
|
||||||
|
return NGHTTP2_ERR_DEFERRED;
|
||||||
|
}
|
||||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||||
return 0;
|
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;
|
remain = cbuf_len(cb) - sd->sd_body_offset;
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "length:%zu totlen:%zu, offset:%zu remain:%zu",
|
clixon_debug(CLIXON_DBG_RESTCONF, "length:%zu totlen:%zu, offset:%zu remain:%zu",
|
||||||
length,
|
length,
|
||||||
|
|
@ -415,6 +431,18 @@ restconf_sd_read(nghttp2_session *session,
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Create nghttp2 response message
|
||||||
|
*
|
||||||
|
* Create nghttp2 name/value pairs from previously assigned sd_outp_hdrs
|
||||||
|
* Call nghttp2 submit response function
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] rc Restconf connection
|
||||||
|
* @param[in] stream_id Nghttp2 stream id
|
||||||
|
* @param[in] sd Restconf native stream struct
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @see restconf_reply_header
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
restconf_submit_response(nghttp2_session *session,
|
restconf_submit_response(nghttp2_session *session,
|
||||||
restconf_conn *rc,
|
restconf_conn *rc,
|
||||||
|
|
@ -439,28 +467,41 @@ restconf_submit_response(nghttp2_session *session,
|
||||||
hdr = &hdrs[i++];
|
hdr = &hdrs[i++];
|
||||||
hdr->name = (uint8_t*)":status";
|
hdr->name = (uint8_t*)":status";
|
||||||
snprintf(valstr, 15, "%u", sd->sd_code);
|
snprintf(valstr, 15, "%u", sd->sd_code);
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "status %d", sd->sd_code);
|
clixon_debug(CLIXON_DBG_RESTCONF, "Status: %d", sd->sd_code);
|
||||||
hdr->value = (uint8_t*)valstr;
|
hdr->value = (uint8_t*)valstr;
|
||||||
hdr->namelen = strlen(":status");
|
hdr->namelen = strlen(":status");
|
||||||
hdr->valuelen = strlen(valstr);
|
hdr->valuelen = strlen(valstr);
|
||||||
hdr->flags = 0;
|
hdr->flags = 0;
|
||||||
|
|
||||||
cv = NULL;
|
cv = NULL;
|
||||||
while ((cv = cvec_each(sd->sd_outp_hdrs, cv)) != NULL){
|
while ((cv = cvec_each(sd->sd_outp_hdrs, cv)) != NULL){
|
||||||
hdr = &hdrs[i++];
|
hdr = &hdrs[i++];
|
||||||
hdr->name = (uint8_t*)cv_name_get(cv);
|
hdr->name = (uint8_t*)cv_name_get(cv);
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "hdr: %s", hdr->name);
|
|
||||||
hdr->value = (uint8_t*)cv_string_get(cv);
|
hdr->value = (uint8_t*)cv_string_get(cv);
|
||||||
hdr->namelen = strlen(cv_name_get(cv));
|
hdr->namelen = strlen(cv_name_get(cv));
|
||||||
hdr->valuelen = strlen(cv_string_get(cv));
|
hdr->valuelen = strlen(cv_string_get(cv));
|
||||||
|
clixon_debug(CLIXON_DBG_RESTCONF, "%s: %s", hdr->name, hdr->value);
|
||||||
hdr->flags = 0;
|
hdr->flags = 0;
|
||||||
}
|
}
|
||||||
if ((ngerr = nghttp2_submit_response(session,
|
if (rc->rc_event_stream){
|
||||||
stream_id,
|
if ((ngerr = nghttp2_submit_headers(session,
|
||||||
hdrs, i,
|
0, // flags
|
||||||
(data_prd.source.ptr != NULL)?&data_prd:NULL)) < 0){
|
stream_id,
|
||||||
clixon_err(OE_NGHTTP2, ngerr, "nghttp2_submit_response");
|
NULL, // pri_spec
|
||||||
goto done;
|
hdrs, i,
|
||||||
|
NULL // stream_user_data
|
||||||
|
)) < 0){
|
||||||
|
clixon_err(OE_NGHTTP2, ngerr, "nghttp2_submit_response");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((ngerr = nghttp2_submit_response(session,
|
||||||
|
stream_id,
|
||||||
|
hdrs, i,
|
||||||
|
(data_prd.source.ptr != NULL)?&data_prd:NULL)) < 0){
|
||||||
|
clixon_err(OE_NGHTTP2, ngerr, "nghttp2_submit_response");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -471,12 +512,19 @@ restconf_submit_response(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Simulate a received request in an upgrade scenario by talking the http/1 parameters
|
/*! Simulate a received request in an upgrade scenario by talking the http/1 parameters
|
||||||
|
*
|
||||||
|
* @param[in] rc Restconf connection
|
||||||
|
* @param[in] sd Restconf native stream struct
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] stream_id Nghttp2 stream id
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
http2_exec(restconf_conn *rc,
|
http2_exec(restconf_conn *rc,
|
||||||
restconf_stream_data *sd,
|
restconf_stream_data *sd,
|
||||||
nghttp2_session *session,
|
nghttp2_session *session,
|
||||||
int32_t stream_id)
|
int32_t stream_id)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
|
|
@ -530,6 +578,12 @@ http2_exec(restconf_conn *rc,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! A frame is received
|
/*! A frame is received
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] frame Nghttp2 frame
|
||||||
|
* @param[in] user_data User-data, in effect Restconf connection
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_frame_recv_callback(nghttp2_session *session,
|
on_frame_recv_callback(nghttp2_session *session,
|
||||||
|
|
@ -575,12 +629,16 @@ on_frame_recv_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! An invalid non-DATA frame is received.
|
/*! An invalid non-DATA frame is received.
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] frame Nghttp2 frame
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_invalid_frame_recv_callback(nghttp2_session *session,
|
on_invalid_frame_recv_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
int lib_error_code,
|
int lib_error_code,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
// restconf_conn *rc = (restconf_conn *)user_data;
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
||||||
|
|
@ -593,6 +651,8 @@ on_invalid_frame_recv_callback(nghttp2_session *session,
|
||||||
* necessarily mean this chunk of data is the last one in the stream.
|
* necessarily mean this chunk of data is the last one in the stream.
|
||||||
* You should use :type:`nghttp2_on_frame_recv_callback` to know all
|
* You should use :type:`nghttp2_on_frame_recv_callback` to know all
|
||||||
* data frames are received.
|
* data frames are received.
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_data_chunk_recv_callback(nghttp2_session *session,
|
on_data_chunk_recv_callback(nghttp2_session *session,
|
||||||
|
|
@ -613,6 +673,10 @@ on_data_chunk_recv_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Just before the non-DATA frame |frame| is sent
|
/*! Just before the non-DATA frame |frame| is sent
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] frame Nghttp2 frame
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
before_frame_send_callback(nghttp2_session *session,
|
before_frame_send_callback(nghttp2_session *session,
|
||||||
|
|
@ -625,6 +689,10 @@ before_frame_send_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! After the frame |frame| is sent
|
/*! After the frame |frame| is sent
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] frame Nghttp2 frame
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_frame_send_callback(nghttp2_session *session,
|
on_frame_send_callback(nghttp2_session *session,
|
||||||
|
|
@ -637,12 +705,16 @@ on_frame_send_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! After the non-DATA frame |frame| is not sent because of error
|
/*! After the non-DATA frame |frame| is not sent because of error
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] frame Nghttp2 frame
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_frame_not_send_callback(nghttp2_session *session,
|
on_frame_not_send_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
int lib_error_code,
|
int lib_error_code,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
// restconf_conn *rc = (restconf_conn *)user_data;
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
||||||
|
|
@ -650,6 +722,9 @@ on_frame_not_send_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Stream |stream_id| is closed.
|
/*! Stream |stream_id| is closed.
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_stream_close_callback(nghttp2_session *session,
|
on_stream_close_callback(nghttp2_session *session,
|
||||||
|
|
@ -670,13 +745,17 @@ on_stream_close_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Reception of header block in HEADERS or PUSH_PROMISE is started.
|
/*! Reception of header block in HEADERS or PUSH_PROMISE is started.
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] frame Nghttp2 frame
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_begin_headers_callback(nghttp2_session *session,
|
on_begin_headers_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
restconf_conn *rc = (restconf_conn *)user_data;
|
restconf_conn *rc = (restconf_conn *)user_data;
|
||||||
restconf_stream_data *sd;
|
restconf_stream_data *sd;
|
||||||
|
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "%s", clicon_int2str(nghttp2_frame_type_map, frame->hd.type));
|
clixon_debug(CLIXON_DBG_RESTCONF, "%s", clicon_int2str(nghttp2_frame_type_map, frame->hd.type));
|
||||||
|
|
@ -730,6 +809,9 @@ nghttp2_hdr2clixon(clixon_handle h,
|
||||||
* If the application uses `nghttp2_session_mem_recv()`, it can return
|
* If the application uses `nghttp2_session_mem_recv()`, it can return
|
||||||
* :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
|
* :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
|
||||||
* return without processing further input bytes.
|
* return without processing further input bytes.
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] frame Nghttp2 frame
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_header_callback(nghttp2_session *session,
|
on_header_callback(nghttp2_session *session,
|
||||||
|
|
@ -760,46 +842,18 @@ on_header_callback(nghttp2_session *session,
|
||||||
return retval;
|
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,
|
|
||||||
const nghttp2_frame *frame,
|
|
||||||
size_t max_payloadlen,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
|
||||||
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,
|
|
||||||
uint8_t frame_type,
|
|
||||||
int32_t stream_id,
|
|
||||||
int32_t session_remote_window_size,
|
|
||||||
int32_t stream_remote_window_size,
|
|
||||||
uint32_t remote_max_frame_size,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif /* NOTUSED */
|
|
||||||
|
|
||||||
/*! Invoked when a frame header is received.
|
/*! Invoked when a frame header is received.
|
||||||
*
|
*
|
||||||
* Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will
|
* Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will
|
||||||
* also be called when frame header of CONTINUATION frame is received.
|
* also be called when frame header of CONTINUATION frame is received.
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] hd Nghttp2 frame header
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_begin_frame_callback(nghttp2_session *session,
|
on_begin_frame_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame_hd *hd,
|
const nghttp2_frame_hd *hd,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
// restconf_conn *rc = (restconf_conn *)user_data;
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "%s", clicon_int2str(nghttp2_frame_type_map, hd->type));
|
clixon_debug(CLIXON_DBG_RESTCONF, "%s", clicon_int2str(nghttp2_frame_type_map, hd->type));
|
||||||
|
|
@ -813,55 +867,35 @@ on_begin_frame_callback(nghttp2_session *session,
|
||||||
* Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is
|
* Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is
|
||||||
* used in :type:`nghttp2_data_source_read_callback` to send complete
|
* used in :type:`nghttp2_data_source_read_callback` to send complete
|
||||||
* DATA frame.
|
* DATA frame.
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] frame Nghttp2 frame
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
send_data_callback(nghttp2_session *session,
|
send_data_callback(nghttp2_session *session,
|
||||||
nghttp2_frame *frame,
|
nghttp2_frame *frame,
|
||||||
const uint8_t *framehd, size_t length,
|
const uint8_t *framehd,
|
||||||
|
size_t length,
|
||||||
nghttp2_data_source *source,
|
nghttp2_data_source *source,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
// restconf_conn *rc = (restconf_conn *)user_data;
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NOTUSED
|
|
||||||
/*! Pack extension payload in its wire format
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
pack_extension_callback(nghttp2_session *session,
|
|
||||||
uint8_t *buf, size_t len,
|
|
||||||
const nghttp2_frame *frame,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Unpack extension payload from its wire format.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
unpack_extension_callback(nghttp2_session *session,
|
|
||||||
void **payload,
|
|
||||||
const nghttp2_frame_hd *hd,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif /* NOTUSED */
|
|
||||||
|
|
||||||
/*! Chunk of extension frame payload is received
|
/*! Chunk of extension frame payload is received
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] hd Nghttp2 frame header
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
on_extension_chunk_recv_callback(nghttp2_session *session,
|
on_extension_chunk_recv_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame_hd *hd,
|
const nghttp2_frame_hd *hd,
|
||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
size_t len,
|
size_t len,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
// restconf_conn *rc = (restconf_conn *)user_data;
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
||||||
|
|
@ -869,12 +903,15 @@ on_extension_chunk_recv_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Library provides the error code, and message for debugging purpose.
|
/*! Library provides the error code, and message for debugging purpose.
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
error_callback(nghttp2_session *session,
|
error_callback(nghttp2_session *session,
|
||||||
const char *msg,
|
const char *msg,
|
||||||
size_t len,
|
size_t len,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
// restconf_conn *rc = (restconf_conn *)user_data;
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
||||||
|
|
@ -883,13 +920,16 @@ error_callback(nghttp2_session *session,
|
||||||
|
|
||||||
#if (NGHTTP2_VERSION_NUM > 0x011201) /* Unsure of version number */
|
#if (NGHTTP2_VERSION_NUM > 0x011201) /* Unsure of version number */
|
||||||
/*! Library provides the error code, and message for debugging purpose.
|
/*! Library provides the error code, and message for debugging purpose.
|
||||||
|
*
|
||||||
|
* @param[in] session Nghttp2 session struct
|
||||||
|
* @param[in] user_data User data, in effect Restconf connection
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
error_callback2(nghttp2_session *session,
|
error_callback2(nghttp2_session *session,
|
||||||
int lib_error_code,
|
int lib_error_code,
|
||||||
const char *msg,
|
const char *msg,
|
||||||
size_t len,
|
size_t len,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
// restconf_conn *rc = (restconf_conn *)user_data;
|
// restconf_conn *rc = (restconf_conn *)user_data;
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
||||||
|
|
@ -988,6 +1028,7 @@ http2_send_server_connection(restconf_conn *rc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Initialize callbacks
|
/*! Initialize callbacks
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
http2_session_init(restconf_conn *rc)
|
http2_session_init(restconf_conn *rc)
|
||||||
|
|
@ -1009,18 +1050,10 @@ http2_session_init(restconf_conn *rc)
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);
|
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_begin_headers_callback(callbacks, on_begin_headers_callback);
|
||||||
nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_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_on_begin_frame_callback(callbacks, on_begin_frame_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_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_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_callback(callbacks, error_callback);
|
||||||
#if (NGHTTP2_VERSION_NUM > 0x011201) /* Unsure of version number */
|
#if (NGHTTP2_VERSION_NUM > 0x011201) /* Unsure of version number */
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*
|
*
|
||||||
* Virtual clixon restconf API functions.
|
* Virtual clixon restconf API functions.
|
||||||
|
* @see RFC9113
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _RESTCONF_NGHTTP2_H_
|
#ifndef _RESTCONF_NGHTTP2_H_
|
||||||
|
|
@ -42,6 +43,7 @@
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int clixon_nghttp2_log_cb(void *handle, int suberr, cbuf *cb);
|
int clixon_nghttp2_log_cb(void *handle, int suberr, cbuf *cb);
|
||||||
|
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);
|
||||||
int http2_exec(restconf_conn *rc, restconf_stream_data *sd, nghttp2_session *session, int32_t stream_id);
|
int http2_exec(restconf_conn *rc, restconf_stream_data *sd, nghttp2_session *session, int32_t stream_id);
|
||||||
int http2_recv(restconf_conn *rc, const unsigned char *buf, size_t n);
|
int http2_recv(restconf_conn *rc, const unsigned char *buf, size_t n);
|
||||||
int http2_send_server_connection(restconf_conn *rc);
|
int http2_send_server_connection(restconf_conn *rc);
|
||||||
|
|
|
||||||
|
|
@ -398,8 +398,8 @@ api_data(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
retval = api_return_err0(h, req, xerr, pretty, media_out, 0);
|
retval = api_return_err0(h, req, xerr, pretty, media_out, 0);
|
||||||
}
|
}
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval);
|
|
||||||
done:
|
done:
|
||||||
|
clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval);
|
||||||
if (xerr)
|
if (xerr)
|
||||||
xml_free(xerr);
|
xml_free(xerr);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -119,24 +119,24 @@ api_path_is_stream(clixon_handle h)
|
||||||
|
|
||||||
/*! Send subscription to backend
|
/*! Send subscription to backend
|
||||||
*
|
*
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle (can be part of clixon handle)
|
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||||
* @param[in] name Stream name
|
* @param[in] name Stream name
|
||||||
* @param[in] qvec
|
* @param[in] qvec
|
||||||
* @param[in] pretty Pretty-print json/xml reply
|
* @param[in] pretty Pretty-print json/xml reply
|
||||||
* @param[in] media_out Restconf output media
|
* @param[in] media_out Restconf output media
|
||||||
* @param[out] sp Socket -1 if not set
|
* @param[out] sp Socket -1 if not set (only fcgi)
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
restconf_subscription(clixon_handle h,
|
restconf_subscription(clixon_handle h,
|
||||||
void *req,
|
void *req,
|
||||||
char *name,
|
char *name,
|
||||||
cvec *qvec,
|
cvec *qvec,
|
||||||
int pretty,
|
int pretty,
|
||||||
restconf_media media_out,
|
restconf_media media_out,
|
||||||
int *sp)
|
int *sp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
|
|
@ -179,14 +179,15 @@ restconf_subscription(clixon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* Setting up stream */
|
/* Setting up stream */
|
||||||
if (restconf_reply_header(req, "Server", "clixon") < 0)
|
|
||||||
goto done;
|
|
||||||
if (restconf_reply_header(req, "Server", "clixon") < 0)
|
if (restconf_reply_header(req, "Server", "clixon") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (restconf_reply_header(req, "Content-Type", "text/event-stream") < 0)
|
if (restconf_reply_header(req, "Content-Type", "text/event-stream") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* HTTP/2 does not use the Connection header field RFC 9113
|
||||||
|
* filtered in restconf_reply_header
|
||||||
|
*/
|
||||||
if (restconf_reply_header(req, "Connection", "keep-alive") < 0)
|
if (restconf_reply_header(req, "Connection", "keep-alive") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Must be there for FCGI caching */
|
/* Must be there for FCGI caching */
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
@ -86,6 +85,39 @@
|
||||||
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
|
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
|
||||||
#include "restconf_stream.h"
|
#include "restconf_stream.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBNGHTTP2
|
||||||
|
#include "restconf_nghttp2.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
restconf_http2_send_notification(clixon_handle h,
|
||||||
|
restconf_stream_data *sd,
|
||||||
|
restconf_conn *rc)
|
||||||
|
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_error ngerr;
|
||||||
|
nghttp2_data_provider data_prd;
|
||||||
|
int32_t stream_id;
|
||||||
|
|
||||||
|
data_prd.source.ptr = sd;
|
||||||
|
data_prd.read_callback = restconf_sd_read;
|
||||||
|
session = rc->rc_ngsession;
|
||||||
|
stream_id = 1;
|
||||||
|
if ((ngerr = nghttp2_submit_data(session,
|
||||||
|
0, // flags
|
||||||
|
stream_id,
|
||||||
|
(data_prd.source.ptr != NULL)?&data_prd:NULL
|
||||||
|
)) < 0){
|
||||||
|
clixon_err(OE_NGHTTP2, ngerr, "nghttp2_submit_response");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
#endif // NGHTTP2
|
||||||
|
|
||||||
/*! Callback when stream notifications arrive from backend
|
/*! Callback when stream notifications arrive from backend
|
||||||
*
|
*
|
||||||
* @param[in] s Socket
|
* @param[in] s Socket
|
||||||
|
|
@ -98,18 +130,21 @@ static int
|
||||||
stream_native_backend_cb(int s,
|
stream_native_backend_cb(int s,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
restconf_stream_data *sd = (restconf_stream_data *)arg;
|
restconf_stream_data *sd = (restconf_stream_data *)arg;
|
||||||
int eof;
|
int eof;
|
||||||
cxobj *xtop = NULL; /* top xml */
|
cxobj *xtop = NULL; /* top xml */
|
||||||
cxobj *xn; /* notification xml */
|
cxobj *xn; /* notification xml */
|
||||||
cbuf *cbx = NULL;
|
cbuf *cbx = NULL;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
cbuf *cbmsg = NULL;
|
cbuf *cbmsg = NULL;
|
||||||
int pretty = 0;
|
int pretty = 0;
|
||||||
int ret;
|
int ret;
|
||||||
restconf_conn *rc = sd->sd_conn;
|
restconf_conn *rc = sd->sd_conn;
|
||||||
clixon_handle h = rc->rc_h;
|
clixon_handle h = rc->rc_h;
|
||||||
|
#ifdef HAVE_LIBNGHTTP2
|
||||||
|
nghttp2_error ngerr;
|
||||||
|
#endif
|
||||||
|
|
||||||
clixon_debug(CLIXON_DBG_STREAM|CLIXON_DBG_DETAIL, "");
|
clixon_debug(CLIXON_DBG_STREAM|CLIXON_DBG_DETAIL, "");
|
||||||
pretty = restconf_pretty_get(h);
|
pretty = restconf_pretty_get(h);
|
||||||
|
|
@ -139,31 +174,28 @@ stream_native_backend_cb(int s,
|
||||||
}
|
}
|
||||||
if ((xn = xpath_first(xtop, NULL, "notification")) == NULL)
|
if ((xn = xpath_first(xtop, NULL, "notification")) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
#if 0 // Cant get CHUNKED to work
|
|
||||||
{
|
|
||||||
size_t len;
|
|
||||||
cprintf(cbx, "data: ");
|
|
||||||
if (clixon_xml2cbuf(cbx, xn, 0, pretty, NULL, -1, 0) < 0)
|
|
||||||
goto done;
|
|
||||||
len = cbuf_len(cbx);
|
|
||||||
len +=2;
|
|
||||||
cprintf(cb, "%x", (int16_t)len&0xffff);
|
|
||||||
cprintf(cb, "\r\n");
|
|
||||||
cprintf(cb, "%s", cbuf_get(cbx));
|
|
||||||
cprintf(cb, "\r\n");
|
|
||||||
cprintf(cb, "\r\n");
|
|
||||||
cprintf(cb, "0\r\n");
|
|
||||||
cprintf(cb, "\r\n");
|
|
||||||
// XXX This terminates stream, but want it to continue / hang
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
cprintf(cb, "data: ");
|
cprintf(cb, "data: ");
|
||||||
if (clixon_xml2cbuf(cb, xn, 0, pretty, NULL, -1, 0) < 0)
|
if (clixon_xml2cbuf(cb, xn, 0, pretty, NULL, -1, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cb, "\r\n");
|
cprintf(cb, "\r\n");
|
||||||
cprintf(cb, "\r\n");
|
cprintf(cb, "\r\n");
|
||||||
#endif
|
#ifdef HAVE_LIBNGHTTP2
|
||||||
if ((ret = native_buf_write(h, cbuf_get(cb), cbuf_len(cb), rc, "native stream")) < 0)
|
if (rc->rc_proto == HTTP_2){
|
||||||
|
if (restconf_reply_send(sd, 200, cb, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
cb = NULL;
|
||||||
|
if (restconf_http2_send_notification(h, sd, rc) < 0)
|
||||||
|
goto done;
|
||||||
|
if ((ngerr = nghttp2_session_send(rc->rc_ngsession)) != 0)
|
||||||
|
goto done;
|
||||||
|
if (sd->sd_body){
|
||||||
|
cbuf_free(sd->sd_body);
|
||||||
|
sd->sd_body = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif // HAVE_LIBNGHTTP2
|
||||||
|
if ((ret = native_buf_write(h, cbuf_get(cb), cbuf_len(cb), rc, "native stream")) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -181,15 +213,39 @@ stream_native_backend_cb(int s,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Timeout of notification stream, limit lifetime, for debug
|
/*! Timeout of notification stream, limit lifetime, for debug
|
||||||
|
*
|
||||||
|
* XXX HTTP/2 not closed cleanly
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
stream_timeout_end(int s,
|
stream_timeout_end(int s,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
|
int retval = -1;
|
||||||
restconf_conn *rc = (restconf_conn *)arg;
|
restconf_conn *rc = (restconf_conn *)arg;
|
||||||
|
|
||||||
clixon_debug(CLIXON_DBG_STREAM, "");
|
clixon_debug(CLIXON_DBG_STREAM, "");
|
||||||
return restconf_close_ssl_socket(rc, __FUNCTION__, 0);
|
rc->rc_exit = 1;
|
||||||
|
#if 0 // Termination is not clean
|
||||||
|
{
|
||||||
|
int ngerr;
|
||||||
|
int ret;
|
||||||
|
if ((ngerr = nghttp2_session_terminate_session(rc->rc_ngsession, 0)) < 0){
|
||||||
|
clixon_err(OE_NGHTTP2, ngerr, "nghttp2_session_terminate_session %d", ngerr);
|
||||||
|
goto done; // XXX not here in original?
|
||||||
|
}
|
||||||
|
if ((ngerr = nghttp2_session_send(rc->rc_ngsession)) != 0){
|
||||||
|
clixon_err(OE_NGHTTP2, ngerr, "nghttp2_session_send %d", ngerr);
|
||||||
|
goto done; // XXX not here in original?
|
||||||
|
}
|
||||||
|
ret = SSL_pending(rc->rc_ssl);
|
||||||
|
clixon_debug(CLIXON_DBG_STREAM, "SSL pending: %d", ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (restconf_close_ssl_socket(rc, __FUNCTION__, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Close notification stream
|
/*! Close notification stream
|
||||||
|
|
@ -203,6 +259,7 @@ stream_close(clixon_handle h,
|
||||||
{
|
{
|
||||||
restconf_conn *rc = (restconf_conn *)req;
|
restconf_conn *rc = (restconf_conn *)req;
|
||||||
|
|
||||||
|
clixon_debug(CLIXON_DBG_STREAM, "");
|
||||||
clicon_rpc_close_session(h);
|
clicon_rpc_close_session(h);
|
||||||
clixon_event_unreg_fd(rc->rc_event_stream, stream_native_backend_cb);
|
clixon_event_unreg_fd(rc->rc_event_stream, stream_native_backend_cb);
|
||||||
clixon_event_unreg_timeout(stream_timeout_end, req);
|
clixon_event_unreg_timeout(stream_timeout_end, req);
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ fi
|
||||||
: ${SLEEP2:=1}
|
: ${SLEEP2:=1}
|
||||||
SLEEP5=.5
|
SLEEP5=.5
|
||||||
APPNAME=example
|
APPNAME=example
|
||||||
: ${clixon_util_stream:=clixon_util_stream}
|
|
||||||
|
|
||||||
: ${TIMEOUT:=10}
|
: ${TIMEOUT:=10}
|
||||||
: ${PERIOD:=2}
|
: ${PERIOD:=2}
|
||||||
|
|
@ -231,13 +230,14 @@ if [ "${WITH_RESTCONF}" = "fcgi" ]; then
|
||||||
runtest ""
|
runtest ""
|
||||||
fi
|
fi
|
||||||
if [ "${WITH_RESTCONF}" = "native" ]; then
|
if [ "${WITH_RESTCONF}" = "native" ]; then
|
||||||
if false; then # XXX native + http/2 dont work yet
|
if ${HAVE_LIBNGHTTP2}; then
|
||||||
# if ${HAVE_LIBNGHTTP2}; then
|
|
||||||
runtest --http2
|
runtest --http2
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if false; then # NYI
|
if false; then # NYI
|
||||||
|
: ${clixon_util_stream:=clixon_util_stream}
|
||||||
|
|
||||||
# 2c
|
# 2c
|
||||||
new "2c) start sub 8s - replay from start -8s - expect 3-4 notifications"
|
new "2c) start sub 8s - replay from start -8s - expect 3-4 notifications"
|
||||||
ret=$($clixon_util_stream -u $RCPROTO://localhost/streams/EXAMPLE -t 10 -s -8)
|
ret=$($clixon_util_stream -u $RCPROTO://localhost/streams/EXAMPLE -t 10 -s -8)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue