Restconf native+http/1 + tls
Added command-line timeout -t <sec> to restconf Example: Added programmable timeout to backend example Test: updated for fcgi and native using internal timeouts
This commit is contained in:
parent
2b2a2ec1ad
commit
62a4b5feff
17 changed files with 577 additions and 318 deletions
|
|
@ -78,7 +78,7 @@
|
|||
* XXX may be unecessary (or body start or something)
|
||||
*/
|
||||
FCGX_Request *
|
||||
restconf_reply_body_start(void *req0)
|
||||
restconf_reply_body_start(void *req0)
|
||||
{
|
||||
FCGX_Request *req = (FCGX_Request *)req0;
|
||||
|
||||
|
|
@ -102,10 +102,10 @@ restconf_reply_header(void *req0,
|
|||
...)
|
||||
{
|
||||
FCGX_Request *req = (FCGX_Request *)req0;
|
||||
int retval = -1;
|
||||
size_t vlen;
|
||||
char *value = NULL;
|
||||
va_list ap;
|
||||
int retval = -1;
|
||||
size_t vlen;
|
||||
char *value = NULL;
|
||||
va_list ap;
|
||||
|
||||
if (req == NULL || name == NULL || vfmt == NULL){
|
||||
clixon_err(OE_CFG, EINVAL, "req, name or value is NULL");
|
||||
|
|
@ -151,11 +151,11 @@ restconf_reply_body_add(void *req0,
|
|||
...)
|
||||
{
|
||||
FCGX_Request *req = (FCGX_Request *)req0;
|
||||
int retval = -1;
|
||||
size_t sz;
|
||||
size_t blen;
|
||||
char *body = NULL;
|
||||
va_list ap;
|
||||
int retval = -1;
|
||||
size_t sz;
|
||||
size_t blen;
|
||||
char *body = NULL;
|
||||
va_list ap;
|
||||
|
||||
if (req == NULL || bfmt == NULL){
|
||||
clixon_err(OE_CFG, EINVAL, "req or body is NULL");
|
||||
|
|
@ -208,7 +208,7 @@ restconf_reply_send(void *req0,
|
|||
{
|
||||
FCGX_Request *req = (FCGX_Request *)req0;
|
||||
int retval = -1;
|
||||
const char *reason_phrase;
|
||||
const char *reason_phrase;
|
||||
|
||||
FCGX_SetExitStatus(code, req->out);
|
||||
if ((reason_phrase = restconf_code2reason(code)) == NULL)
|
||||
|
|
@ -241,8 +241,8 @@ cbuf *
|
|||
restconf_get_indata(void *req0)
|
||||
{
|
||||
FCGX_Request *req = (FCGX_Request *)req0;
|
||||
int c;
|
||||
cbuf *cb = NULL;
|
||||
int c;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@
|
|||
#include "clixon_http1_parse.h"
|
||||
#include "restconf_http1.h"
|
||||
#include "clixon_http_data.h"
|
||||
#ifdef RESTCONF_NATIVE_STREAM
|
||||
#include "restconf_stream.h"
|
||||
#endif
|
||||
|
||||
/* Size of xml read buffer */
|
||||
#define BUFLEN 1024
|
||||
|
|
@ -310,7 +313,7 @@ restconf_http1_reply(restconf_conn *rc,
|
|||
* (Successful) response to a CONNECT request (Section 4.3.6 of
|
||||
* [RFC7231]).
|
||||
*/
|
||||
if (sd->sd_code != 204 && sd->sd_code > 199)
|
||||
if (sd->sd_code != 204 && sd->sd_code > 199 && !rc->rc_event_stream)
|
||||
if (restconf_reply_header(sd, "Content-Length", "%zu", sd->sd_body_len) < 0)
|
||||
goto done;
|
||||
/* Create reply and write headers */
|
||||
|
|
@ -450,6 +453,13 @@ restconf_http1_path_root(clixon_handle h,
|
|||
if (api_http_data(h, sd, sd->sd_qvec) < 0)
|
||||
goto done;
|
||||
}
|
||||
#ifdef RESTCONF_NATIVE_STREAM
|
||||
else if (api_path_is_stream(h)){
|
||||
restconf_socket *rs = rc->rc_socket;
|
||||
if (api_stream(h, sd, sd->sd_qvec, rs->rs_stream_timeout, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
sd->sd_code = 404; /* catch all without body/media */
|
||||
fail:
|
||||
|
|
|
|||
|
|
@ -88,12 +88,12 @@
|
|||
#include "restconf_stream.h"
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define RESTCONF_OPTS "hVD:f:E:l:C:p:d:y:a:u:rW:R:o:"
|
||||
#define RESTCONF_OPTS "hVD:f:E:l:C:p:d:y:a:u:rW:R:t:o:"
|
||||
|
||||
/*! Convert FCGI parameters to clixon runtime data
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] envp Fastcgi request handle parameter array on the format "<param>=<value>"
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] envp Fastcgi request handle parameter array on the format "<param>=<value>"
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
|
||||
|
|
@ -285,6 +285,7 @@ usage(clixon_handle h,
|
|||
"\t-r \t\t Do not drop privileges if run as root\n"
|
||||
"\t-W <user>\t Run restconf daemon as this user, drop according to CLICON_RESTCONF_PRIVILEGES\n"
|
||||
"\t-R <xml> \t Restconf configuration in-line overriding config file\n"
|
||||
"\t-t <sec>\t Notification stream timeout in: quit after <sec>. For debug\n"
|
||||
"\t-o \"<option>=<value>\" Give configuration option overriding config file (see clixon-config.yang)\n",
|
||||
argv0
|
||||
);
|
||||
|
|
@ -299,35 +300,36 @@ int
|
|||
main(int argc,
|
||||
char **argv)
|
||||
{
|
||||
int retval = -1;
|
||||
int sock;
|
||||
char *argv0 = argv[0];
|
||||
FCGX_Request request;
|
||||
FCGX_Request *req = &request;
|
||||
int c;
|
||||
char *sockpath = NULL;
|
||||
char *path;
|
||||
clixon_handle h;
|
||||
char *dir;
|
||||
int logdst = CLIXON_LOG_SYSLOG;
|
||||
yang_stmt *yspec = NULL;
|
||||
char *query;
|
||||
cvec *qvec;
|
||||
int finish = 0;
|
||||
char *str;
|
||||
int retval = -1;
|
||||
int sock;
|
||||
char *argv0 = argv[0];
|
||||
FCGX_Request request;
|
||||
FCGX_Request *req = &request;
|
||||
int c;
|
||||
char *sockpath = NULL;
|
||||
char *path;
|
||||
clixon_handle h;
|
||||
char *dir;
|
||||
int logdst = CLIXON_LOG_SYSLOG;
|
||||
yang_stmt *yspec = NULL;
|
||||
char *query;
|
||||
cvec *qvec;
|
||||
int finish = 0;
|
||||
char *str;
|
||||
clixon_plugin_t *cp = NULL;
|
||||
cvec *nsctx_global = NULL; /* Global namespace context */
|
||||
size_t cligen_buflen;
|
||||
size_t cligen_bufthreshold;
|
||||
int dbg = 0;
|
||||
cxobj *xerr = NULL;
|
||||
char *wwwuser;
|
||||
char *inline_config = NULL;
|
||||
size_t sz;
|
||||
int config_dump = 0;
|
||||
cvec *nsctx_global = NULL; /* Global namespace context */
|
||||
size_t cligen_buflen;
|
||||
size_t cligen_bufthreshold;
|
||||
int dbg = 0;
|
||||
cxobj *xerr = NULL;
|
||||
char *wwwuser;
|
||||
char *inline_config = NULL;
|
||||
size_t sz;
|
||||
int config_dump = 0;
|
||||
enum format_enum config_dump_format = FORMAT_XML;
|
||||
int print_version = 0;
|
||||
|
||||
int stream_timeout = 0;
|
||||
|
||||
/* Create handle */
|
||||
if ((h = restconf_handle_init()) == NULL)
|
||||
goto done;
|
||||
|
|
@ -445,6 +447,9 @@ main(int argc,
|
|||
case 'R': /* Restconf on-line config */
|
||||
inline_config = optarg;
|
||||
break;
|
||||
case 't': /* Stream timeout */
|
||||
stream_timeout = atoi(optarg);
|
||||
break;
|
||||
case 'o':{ /* Configuration option */
|
||||
char *val;
|
||||
if ((val = index(optarg, '=')) == NULL)
|
||||
|
|
@ -675,7 +680,7 @@ main(int argc,
|
|||
if (uri_str2cvec(query, '&', '=', 1, &qvec) < 0)
|
||||
goto done;
|
||||
/* XXX doing goto done on error causes test errors */
|
||||
(void)api_stream(h, req, qvec, &finish);
|
||||
(void)api_stream(h, req, qvec, stream_timeout, &finish);
|
||||
}
|
||||
else{
|
||||
clixon_debug(CLIXON_DBG_RESTCONF, "top-level %s not found", path);
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@
|
|||
#endif
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define RESTCONF_OPTS "hVD:f:E:l:C:p:y:a:u:rW:R:o:"
|
||||
#define RESTCONF_OPTS "hVD:f:E:l:C:p:y:a:u:rW:R:t:o:"
|
||||
|
||||
/* If set, open outwards socket non-blocking, as opposed to blocking
|
||||
* Should work both ways, but in the ninblocking case,
|
||||
|
|
@ -713,13 +713,15 @@ restconf_clixon_backend(clixon_handle h,
|
|||
* @param[in] h Clixon handle
|
||||
* @param[in] xs XML config of single restconf socket
|
||||
* @param[in] nsc Namespace context
|
||||
* @param[in] timeout Terminate notification event stream after number of seconds
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
openssl_init_socket(clixon_handle h,
|
||||
cxobj *xs,
|
||||
cvec *nsc)
|
||||
cvec *nsc,
|
||||
int timeout)
|
||||
{
|
||||
int retval = -1;
|
||||
char *netns = NULL;
|
||||
|
|
@ -742,6 +744,7 @@ openssl_init_socket(clixon_handle h,
|
|||
}
|
||||
memset(rsock, 0, sizeof *rsock);
|
||||
rsock->rs_h = h;
|
||||
rsock->rs_stream_timeout = timeout;
|
||||
gettimeofday(&now, NULL);
|
||||
rsock->rs_start = now.tv_sec;
|
||||
/* Extract socket parameters from single socket config: ns, addr, port, ssl */
|
||||
|
|
@ -808,13 +811,15 @@ openssl_init_socket(clixon_handle h,
|
|||
* @param[in] h Clixon handle
|
||||
* @param[in] dbg0 Manually set debug flag, if set overrides configuration setting
|
||||
* @param[in] xrestconf XML tree containing restconf config
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @param[in] timeout Terminate notification stream after number of seconds
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
restconf_openssl_init(clixon_handle h,
|
||||
int dbg0,
|
||||
cxobj *xrestconf)
|
||||
cxobj *xrestconf,
|
||||
int timeout)
|
||||
{
|
||||
int retval = -1;
|
||||
SSL_CTX *ctx; /* SSL context */
|
||||
|
|
@ -851,6 +856,8 @@ restconf_openssl_init(clixon_handle h,
|
|||
if ((x = xpath_first(xrestconf, nsc, "enable-core-dump")) != NULL) {
|
||||
/* core dump is enabled on RESTCONF process */
|
||||
struct rlimit rlp;
|
||||
int status;
|
||||
|
||||
if (strcmp(xml_body(x), "true") == 0) {
|
||||
rlp.rlim_cur = RLIM_INFINITY;
|
||||
rlp.rlim_max = RLIM_INFINITY;
|
||||
|
|
@ -858,12 +865,11 @@ restconf_openssl_init(clixon_handle h,
|
|||
rlp.rlim_cur = 0;
|
||||
rlp.rlim_max = 0;
|
||||
}
|
||||
int status = setrlimit(RLIMIT_CORE, &rlp);
|
||||
status = setrlimit(RLIMIT_CORE, &rlp);
|
||||
if (status != 0) {
|
||||
clixon_log(h, LOG_INFO, "%s: setrlimit() failed, %s", __FUNCTION__, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (init_openssl() < 0)
|
||||
goto done;
|
||||
if ((ctx = restconf_ssl_context_create(h)) == NULL)
|
||||
|
|
@ -886,7 +892,7 @@ restconf_openssl_init(clixon_handle h,
|
|||
if (xpath_vec(xrestconf, nsc, "socket", &vec, &veclen) < 0)
|
||||
goto done;
|
||||
for (i=0; i<veclen; i++){
|
||||
if (openssl_init_socket(h, vec[i], nsc) < 0){
|
||||
if (openssl_init_socket(h, vec[i], nsc, timeout) < 0){
|
||||
/* Bind errors are ignored, proceed with next after log */
|
||||
if (clixon_err_category() == OE_UNIX && clixon_err_subnr() == EADDRNOTAVAIL)
|
||||
clixon_err_reset();
|
||||
|
|
@ -1118,20 +1124,21 @@ usage(clixon_handle h,
|
|||
fprintf(stderr, "usage:%s [options]\n"
|
||||
"where options are\n"
|
||||
"\t-h \t\t Help\n"
|
||||
"\t-V \t\tPrint version and exit\n"
|
||||
"\t-D <level>\tDebug level (see available levels below)\n"
|
||||
"\t-V \t\t Print version and exit\n"
|
||||
"\t-D <level>\t Debug level (see available levels below)\n"
|
||||
"\t-f <file>\t Configuration file (mandatory)\n"
|
||||
"\t-E <dir> \t Extra configuration file directory\n"
|
||||
"\t-l <s|e|o|n|f<file>> \tLog on (s)yslog, std(e)rr, std(o)ut, (n)one or (f)ile (syslog is default)\n"
|
||||
"\t-C <format>\tDump configuration options on stdout after loading and exit. Format is xml|json|text\n"
|
||||
"\t-C <format>\t Dump configuration options on stdout after loading and exit. Format is xml|json|text\n"
|
||||
"\t-p <dir>\t Yang directory path (see CLICON_YANG_DIR)\n"
|
||||
"\t-y <file>\t Load yang spec file (override yang main module)\n"
|
||||
"\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
|
||||
"\t-u <path|addr>\t Internal socket domain path or IP addr (see -a)\n"
|
||||
"\t-r \t\t Do not drop privileges if run as root\n"
|
||||
"\t-W <user>\t Run restconf daemon as this user, drop according to CLICON_RESTCONF_PRIVILEGES\n"
|
||||
"\t-R <xml> \t Restconf configuration in-line overriding config file\n"
|
||||
"\t-o <option>=<value> Set configuration option overriding config file (see clixon-config.yang)\n"
|
||||
"\t-R <xml>\t Restconf configuration in-line overriding config file\n"
|
||||
"\t-t <sec>\t Notification stream timeout in: quit after <sec>. For debug\n"
|
||||
"\t-o <op>=<value>\t Set configuration option overriding config file (see clixon-config.yang)\n"
|
||||
,
|
||||
argv0
|
||||
);
|
||||
|
|
@ -1147,19 +1154,20 @@ int
|
|||
main(int argc,
|
||||
char **argv)
|
||||
{
|
||||
int retval = -1;
|
||||
char *argv0 = argv[0];
|
||||
int c;
|
||||
clixon_handle h;
|
||||
int dbg = 0;
|
||||
int logdst = CLIXON_LOG_SYSLOG;
|
||||
int retval = -1;
|
||||
char *argv0 = argv[0];
|
||||
int c;
|
||||
clixon_handle h;
|
||||
int dbg = 0;
|
||||
int logdst = CLIXON_LOG_SYSLOG;
|
||||
restconf_native_handle *rn = NULL;
|
||||
int ret;
|
||||
cxobj *xrestconf = NULL;
|
||||
char *inline_config = NULL;
|
||||
int config_dump = 0;
|
||||
enum format_enum config_dump_format = FORMAT_XML;
|
||||
int print_version = 0;
|
||||
int ret;
|
||||
cxobj *xrestconf = NULL;
|
||||
char *inline_config = NULL;
|
||||
int config_dump = 0;
|
||||
enum format_enum config_dump_format = FORMAT_XML;
|
||||
int print_version = 0;
|
||||
int stream_timeout = 0;
|
||||
|
||||
/* Create handle */
|
||||
if ((h = restconf_handle_init()) == NULL)
|
||||
|
|
@ -1297,6 +1305,9 @@ main(int argc,
|
|||
case 'R': /* Restconf on-line config */
|
||||
inline_config = optarg;
|
||||
break;
|
||||
case 't': /* Stream timeout */
|
||||
stream_timeout = atoi(optarg);
|
||||
break;
|
||||
case 'o':{ /* Configuration option */
|
||||
char *val;
|
||||
if ((val = index(optarg, '=')) == NULL)
|
||||
|
|
@ -1352,7 +1363,7 @@ main(int argc,
|
|||
if (restconf_native_handle_set(h, rn) < 0)
|
||||
goto done;
|
||||
/* Openssl inits */
|
||||
if (restconf_openssl_init(h, dbg, xrestconf) < 0)
|
||||
if (restconf_openssl_init(h, dbg, xrestconf, stream_timeout) < 0)
|
||||
goto done;
|
||||
/* Drop privileges if started as root to CLICON_RESTCONF_USER
|
||||
* and use drop mode: CLICON_RESTCONF_PRIVILEGES
|
||||
|
|
|
|||
|
|
@ -76,6 +76,9 @@
|
|||
#ifdef HAVE_HTTP1
|
||||
#include "restconf_http1.h"
|
||||
#endif
|
||||
#ifdef RESTCONF_NATIVE_STREAM
|
||||
#include "restconf_stream.h"
|
||||
#endif
|
||||
|
||||
/* Forward */
|
||||
static int restconf_idle_cb(int fd, void *arg);
|
||||
|
|
@ -1041,6 +1044,12 @@ restconf_connection(int s,
|
|||
|
||||
/*----------------------------- Close socket ------------------------------*/
|
||||
|
||||
static int
|
||||
restconf_idle_timer_unreg(restconf_conn *rc)
|
||||
{
|
||||
return clixon_event_unreg_timeout(restconf_idle_cb, rc);
|
||||
}
|
||||
|
||||
/*! Close Restconf native connection socket and unregister callback
|
||||
*
|
||||
* For callhome also start reconnect timer
|
||||
|
|
@ -1072,6 +1081,11 @@ restconf_connection_close1(restconf_conn *rc)
|
|||
if (restconf_callhome_timer(rsock, 1) < 0)
|
||||
goto done;
|
||||
}
|
||||
#ifdef RESTCONF_NATIVE_STREAM
|
||||
if (rc->rc_event_stream){
|
||||
stream_close(rc->rc_h, rc);
|
||||
}
|
||||
#endif
|
||||
retval = 0;
|
||||
done:
|
||||
clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval);
|
||||
|
|
@ -1556,12 +1570,6 @@ restconf_idle_cb(int fd,
|
|||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
restconf_idle_timer_unreg(restconf_conn *rc)
|
||||
{
|
||||
return clixon_event_unreg_timeout(restconf_idle_cb, rc);
|
||||
}
|
||||
|
||||
/*! Set callhome periodic idle-timeout
|
||||
*
|
||||
* 1) If callhome and periodic, set timer for t0+idle-timeout(ti)
|
||||
|
|
@ -1573,7 +1581,7 @@ restconf_idle_timer_unreg(restconf_conn *rc)
|
|||
* XXX: now just timeout dont keep track of data (td)
|
||||
* @see restconf_idle_timer_unreg
|
||||
*/
|
||||
int
|
||||
static int
|
||||
restconf_idle_timer(restconf_conn *rc)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
***** END LICENSE BLOCK *****
|
||||
*
|
||||
* Data structures:
|
||||
* Data structures: (NOT UPDATED)
|
||||
* 1 1
|
||||
* +--------------------+ restconf_handle_get +--------------------+
|
||||
* | rn restconf_native | <--------------------- | h clixon_handle |
|
||||
|
|
@ -71,7 +71,7 @@ extern "C" {
|
|||
/* Forward */
|
||||
struct restconf_conn;
|
||||
|
||||
/* session stream struct, mainly for http/2 but http/1 has a single pseudo-stream with id=0
|
||||
/* Session stream struct, mainly for http/2 but http/1 has a single pseudo-stream with id=0
|
||||
*/
|
||||
typedef struct {
|
||||
qelem_t sd_qelem; /* List header */
|
||||
|
|
@ -111,8 +111,8 @@ typedef struct restconf_conn {
|
|||
int rc_s; /* Connection socket */
|
||||
clixon_handle rc_h; /* Clixon handle */
|
||||
SSL *rc_ssl; /* Structure for SSL connection */
|
||||
restconf_stream_data *rc_streams; /* List of http/2 session streams */
|
||||
int rc_exit; /* Set to close socket server-side */
|
||||
restconf_stream_data *rc_streams; /* List of http/2 session streams */
|
||||
int rc_exit; /* Set to close socket server-side */
|
||||
/* Decision to keep lib-specific data here, otherwise new struct necessary
|
||||
* drawback is specific includes need to go everywhere */
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
|
|
@ -121,6 +121,9 @@ typedef struct restconf_conn {
|
|||
restconf_socket *rc_socket; /* Backpointer to restconf_socket needed for callhome */
|
||||
struct timeval rc_t; /* Timestamp of last read/write activity, used by callhome
|
||||
idle-timeout algorithm */
|
||||
#ifdef RESTCONF_NATIVE_STREAM
|
||||
int rc_event_stream; /* Event notification stream socket (maybe in sd?) */
|
||||
#endif
|
||||
} restconf_conn;
|
||||
|
||||
/* Restconf per socket handle
|
||||
|
|
@ -156,7 +159,7 @@ typedef struct restconf_socket{
|
|||
*/
|
||||
restconf_conn *rs_conns; /* List of transient connect sockets */
|
||||
char *rs_from_addr; /* From IP address as seen by accept (mv to rc?) */
|
||||
|
||||
int rs_stream_timeout; /* Close stream after <s> (debug) */
|
||||
} restconf_socket;
|
||||
|
||||
/* Restconf handle
|
||||
|
|
@ -182,8 +185,6 @@ int restconf_connection_sanity(clixon_handle h, restconf_conn *rc,
|
|||
restconf_native_handle *restconf_native_handle_get(clixon_handle h);
|
||||
int restconf_connection(int s, void *arg);
|
||||
int restconf_ssl_accept_client(clixon_handle h, int s, restconf_socket *rsock, restconf_conn **rcp);
|
||||
int restconf_idle_timer_unreg(restconf_conn *rc);
|
||||
int restconf_idle_timer(restconf_conn *rc);
|
||||
int restconf_callhome_timer_unreg(restconf_socket *rsock);
|
||||
int restconf_callhome_timer(restconf_socket *rsock, int status);
|
||||
int restconf_socket_extract(clixon_handle h, cxobj *xs, cvec *nsc, restconf_socket *rsock,
|
||||
|
|
|
|||
|
|
@ -343,7 +343,8 @@ restconf_nghttp2_path(restconf_stream_data *sd)
|
|||
}
|
||||
#ifdef RESTCONF_NATIVE_STREAM
|
||||
else if (api_path_is_stream(h)){
|
||||
if (api_stream(h, sd, sd->sd_qvec, NULL) < 0)
|
||||
restconf_socket *rs = rc->rc_socket;
|
||||
if (api_stream(h, sd, sd->sd_qvec, rs->rs_stream_timeout, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -179,14 +179,18 @@ restconf_subscription(clixon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
/* Setting up stream */
|
||||
if (restconf_reply_header(req, "Server", "clixon") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Content-Type", "text/event-stream") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Connection", "keep-alive") < 0)
|
||||
goto done;
|
||||
#ifndef RESTCONF_NATIVE_STREAM
|
||||
if (restconf_reply_header(req, "X-Accel-Buffering", "no") < 0)
|
||||
goto done;
|
||||
#endif
|
||||
if (restconf_reply_send(req, 201, NULL, 0) < 0)
|
||||
goto done;
|
||||
*sp = s;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ int api_path_is_stream(clixon_handle h);
|
|||
int restconf_subscription(clixon_handle h, void *req, char *name, cvec *qvec, int pretty, restconf_media media_out, int *sp);
|
||||
int stream_child_free(clixon_handle h, int pid);
|
||||
int stream_child_freeall(clixon_handle h);
|
||||
int api_stream(clixon_handle h, void *req, cvec *qvec, int *finish);
|
||||
int stream_close(clixon_handle h, void *req);
|
||||
int api_stream(clixon_handle h, void *req, cvec *qvec, int timeout, int *finish);
|
||||
|
||||
#endif /* _RESTCONF_STREAM_H_ */
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@
|
|||
#include "restconf_api.h"
|
||||
#include "restconf_err.h"
|
||||
#include "restconf_stream.h"
|
||||
#include "restconf_lib.h"
|
||||
#include "restconf_stream.h"
|
||||
|
||||
/*
|
||||
* Constants
|
||||
|
|
@ -185,9 +187,10 @@ restconf_stream_cb(int s,
|
|||
int pretty = 0; /* XXX should be via arg */
|
||||
int ret;
|
||||
|
||||
clixon_debug(CLIXON_DBG_STREAM, "");
|
||||
clixon_debug(CLIXON_DBG_STREAM|CLIXON_DBG_DETAIL, "");
|
||||
if (clixon_msg_rcv11(s, NULL, 0, &cbmsg, &eof) < 0)
|
||||
goto done;
|
||||
clixon_debug(CLIXON_DBG_STREAM, "%s", cbuf_get(cbmsg)); // Also MSG
|
||||
/* handle close from remote end: this will exit the client */
|
||||
if (eof){
|
||||
clixon_debug(CLIXON_DBG_STREAM, "eof");
|
||||
|
|
@ -232,7 +235,7 @@ restconf_stream_cb(int s,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
clixon_debug(CLIXON_DBG_STREAM, "retval: %d", retval);
|
||||
clixon_debug(CLIXON_DBG_STREAM|CLIXON_DBG_DETAIL, "retval: %d", retval);
|
||||
if (xtop != NULL)
|
||||
xml_free(xtop);
|
||||
if (cbmsg)
|
||||
|
|
@ -242,10 +245,6 @@ restconf_stream_cb(int s,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* restconf */
|
||||
#include "restconf_lib.h"
|
||||
#include "restconf_stream.h"
|
||||
|
||||
/*! Listen sock callback (from proxy?)
|
||||
*
|
||||
* @param[in] s Socket
|
||||
|
|
@ -265,33 +264,45 @@ stream_checkuplink(int s,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
stream_timeout(int s,
|
||||
void *arg)
|
||||
/*! Timeout of notification stream, check fcgi socket
|
||||
*/
|
||||
static int
|
||||
fcgi_stream_timeout(int s,
|
||||
void *arg)
|
||||
{
|
||||
struct timeval t;
|
||||
struct timeval t1;
|
||||
FCGX_Request *r = (FCGX_Request *)arg;
|
||||
FCGX_Request *r = (FCGX_Request *)arg;
|
||||
|
||||
clixon_debug(CLIXON_DBG_STREAM, "");
|
||||
clixon_debug(CLIXON_DBG_STREAM|CLIXON_DBG_DETAIL, "");
|
||||
if (FCGX_GetError(r->out) != 0){ /* break loop */
|
||||
clixon_debug(CLIXON_DBG_STREAM, "FCGX_GetError upstream");
|
||||
clixon_exit_set(1);
|
||||
}
|
||||
else{
|
||||
gettimeofday(&t, NULL);
|
||||
t1.tv_sec = 1; t1.tv_usec = 0;
|
||||
timeradd(&t, &t1, &t);
|
||||
clixon_event_reg_timeout(t, stream_timeout, arg, "Stream timeout");
|
||||
t.tv_sec++;
|
||||
clixon_event_reg_timeout(t, fcgi_stream_timeout, arg, "Stream timeout");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Timeout of notification stream, limit lifetime, for debug
|
||||
*/
|
||||
static int
|
||||
fcgi_stream_timeout2(int s,
|
||||
void *arg)
|
||||
{
|
||||
clixon_debug(CLIXON_DBG_STREAM, "Terminate stream");
|
||||
clixon_exit_set(1); // XXX This is local eventloop see below, not global
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Process a stream request
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||
* @param[in] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
|
||||
* @param[in] timeout Stream timeout
|
||||
* @param[out] finish Set to zero, if request should not be finnished by upper layer
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
|
|
@ -300,6 +311,7 @@ int
|
|||
api_stream(clixon_handle h,
|
||||
void *req,
|
||||
cvec *qvec,
|
||||
int timeout,
|
||||
int *finish)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -403,9 +415,15 @@ api_stream(clixon_handle h,
|
|||
req,
|
||||
"stream socket") < 0)
|
||||
goto done;
|
||||
clixon_debug(CLIXON_DBG_STREAM, "before loop");
|
||||
/* Timeout of notification stream, close after limited lifetime, for debug */
|
||||
if (timeout){
|
||||
struct timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
t.tv_sec += timeout;
|
||||
clixon_event_reg_timeout(t, fcgi_stream_timeout2, req, "Stream timeout");
|
||||
}
|
||||
/* Poll upstream errors */
|
||||
stream_timeout(0, req);
|
||||
fcgi_stream_timeout(0, req);
|
||||
/* Start loop */
|
||||
clixon_event_loop(h);
|
||||
clixon_debug(CLIXON_DBG_STREAM, "after loop");
|
||||
|
|
@ -414,7 +432,8 @@ api_stream(clixon_handle h,
|
|||
close(s);
|
||||
clixon_event_unreg_fd(rfcgi->listen_sock,
|
||||
restconf_stream_cb);
|
||||
clixon_event_unreg_timeout(stream_timeout, (void*)req);
|
||||
clixon_event_unreg_timeout(fcgi_stream_timeout, (void*)req);
|
||||
clixon_event_unreg_timeout(fcgi_stream_timeout2, (void*)req);
|
||||
clixon_exit_set(0); /* reset */
|
||||
#ifdef STREAM_FORK
|
||||
#if 0 /* Seems to be a global resource, but there is till some timing error here */
|
||||
|
|
|
|||
|
|
@ -86,8 +86,100 @@
|
|||
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
|
||||
#include "restconf_stream.h"
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2 /* Ends at end-of-file */
|
||||
#include "restconf_nghttp2.h" /* Restconf-openssl mode specific headers*/
|
||||
// XXX: copy from restconf_native.c
|
||||
static int
|
||||
native_buf_write_xxx(clixon_handle h,
|
||||
char *buf,
|
||||
size_t buflen,
|
||||
restconf_conn *rc,
|
||||
const char *callfn)
|
||||
{
|
||||
int retval = -1;
|
||||
ssize_t len;
|
||||
ssize_t totlen = 0;
|
||||
int er;
|
||||
SSL *ssl;
|
||||
|
||||
if (rc == NULL){
|
||||
clixon_err(OE_RESTCONF, EINVAL, "rc is NULL");
|
||||
goto done;
|
||||
}
|
||||
ssl = rc->rc_ssl;
|
||||
/* Two problems with debugging buffers that this fixes:
|
||||
* 1. they are not "strings" in the sense they are not NULL-terminated
|
||||
* 2. they are often very long
|
||||
*/
|
||||
if (clixon_debug_get()) {
|
||||
char *dbgstr = NULL;
|
||||
size_t sz;
|
||||
sz = buflen>256?256:buflen; /* Truncate to 256 */
|
||||
if ((dbgstr = malloc(sz+1)) == NULL){
|
||||
clixon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memcpy(dbgstr, buf, sz);
|
||||
dbgstr[sz] = '\0';
|
||||
clixon_debug(CLIXON_DBG_RESTCONF, "%s buflen:%zu buf:\n%s", callfn, buflen, dbgstr);
|
||||
free(dbgstr);
|
||||
}
|
||||
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 */
|
||||
er == EPIPE) { /* Reading end of socket is closed */
|
||||
goto closed; /* Close socket and ssl */
|
||||
}
|
||||
else if (er == EAGAIN){
|
||||
clixon_debug(CLIXON_DBG_RESTCONF, "write EAGAIN");
|
||||
usleep(10000);
|
||||
continue;
|
||||
}
|
||||
else{
|
||||
clixon_err(OE_RESTCONF, er, "SSL_write %d", er);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
clixon_err(OE_SSL, 0, "SSL_write");
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if ((len = write(rc->rc_s, buf+totlen, buflen-totlen)) < 0){
|
||||
switch (errno){
|
||||
case EAGAIN: /* Operation would block */
|
||||
clixon_debug(CLIXON_DBG_RESTCONF, "write EAGAIN");
|
||||
usleep(10000);
|
||||
continue;
|
||||
break;
|
||||
// case EBADF: // XXX if this happens there is some larger error
|
||||
case ECONNRESET: /* Connection reset by peer */
|
||||
case EPIPE: /* Broken pipe */
|
||||
goto closed; /* Close socket and ssl */
|
||||
break;
|
||||
default:
|
||||
clixon_err(OE_UNIX, errno, "write %d", errno);
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
totlen += len;
|
||||
} /* while */
|
||||
retval = 1;
|
||||
done:
|
||||
clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval);
|
||||
return retval;
|
||||
closed:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Callback when stream notifications arrive from backend
|
||||
*
|
||||
|
|
@ -101,26 +193,29 @@ static int
|
|||
restconf_native_stream_cb(int s,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
int eof;
|
||||
cxobj *xtop = NULL; /* top xml */
|
||||
cxobj *xn; /* notification xml */
|
||||
cbuf *cb = NULL;
|
||||
cbuf *cbmsg = NULL;
|
||||
int pretty = 0; /* XXX should be via arg */
|
||||
int ret;
|
||||
int retval = -1;
|
||||
restconf_stream_data *sd = (restconf_stream_data *)arg;
|
||||
int eof;
|
||||
cxobj *xtop = NULL; /* top xml */
|
||||
cxobj *xn; /* notification xml */
|
||||
cbuf *cbx = NULL;
|
||||
cbuf *cb = NULL;
|
||||
cbuf *cbmsg = NULL;
|
||||
int pretty = 0;
|
||||
int ret;
|
||||
restconf_conn *rc = sd->sd_conn;
|
||||
clixon_handle h = rc->rc_h;
|
||||
|
||||
clixon_debug(CLIXON_DBG_STREAM, "");
|
||||
clixon_debug(CLIXON_DBG_STREAM|CLIXON_DBG_DETAIL, "");
|
||||
pretty = restconf_pretty_get(h);
|
||||
if (clixon_msg_rcv11(s, NULL, 0, &cbmsg, &eof) < 0)
|
||||
goto done;
|
||||
clixon_debug(CLIXON_DBG_STREAM, "%s", cbuf_get(cbmsg));
|
||||
/* handle close from remote end: this will exit the client */
|
||||
if (eof){
|
||||
clixon_debug(CLIXON_DBG_STREAM, "eof");
|
||||
clixon_err(OE_PROTO, ESHUTDOWN, "Socket unexpected close");
|
||||
errno = ESHUTDOWN;
|
||||
clixon_exit_set(1);
|
||||
goto done;
|
||||
restconf_close_ssl_socket(rc, __FUNCTION__, 0);
|
||||
goto ok;
|
||||
}
|
||||
if ((ret = clixon_xml_parse_string(cbuf_get(cbmsg), YB_NONE, NULL, &xtop, NULL)) < 0)
|
||||
goto done;
|
||||
|
|
@ -133,20 +228,220 @@ restconf_native_stream_cb(int s,
|
|||
clixon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((cbx = cbuf_new()) == NULL){
|
||||
clixon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((xn = xpath_first(xtop, NULL, "notification")) == NULL)
|
||||
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: ");
|
||||
if (clixon_xml2cbuf(cb, xn, 0, pretty, NULL, -1, 0) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "\r\n");
|
||||
cprintf(cb, "\r\n");
|
||||
#endif
|
||||
if ((ret = native_buf_write_xxx(h, cbuf_get(cb), cbuf_len(cb), rc, "native stream")) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
clixon_debug(CLIXON_DBG_STREAM, "retval: %d", retval);
|
||||
clixon_debug(CLIXON_DBG_STREAM|CLIXON_DBG_DETAIL, "retval: %d", retval);
|
||||
if (xtop != NULL)
|
||||
xml_free(xtop);
|
||||
if (cbmsg)
|
||||
cbuf_free(cbmsg);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Timeout of notification stream, limit lifetime, for debug
|
||||
*/
|
||||
static int
|
||||
native_stream_timeout(int s,
|
||||
void *arg)
|
||||
{
|
||||
restconf_conn *rc = (restconf_conn *)arg;
|
||||
|
||||
clixon_debug(CLIXON_DBG_STREAM, "");
|
||||
return restconf_close_ssl_socket(rc, __FUNCTION__, 0);
|
||||
}
|
||||
|
||||
/*! Close notification stream
|
||||
*
|
||||
* Only stream aspects, to close full socket, call eg restconf_close_ssl_socket
|
||||
*/
|
||||
int
|
||||
stream_close(clixon_handle h,
|
||||
void *req)
|
||||
|
||||
{
|
||||
restconf_conn *rc = (restconf_conn *)req;
|
||||
|
||||
clicon_rpc_close_session(h);
|
||||
clixon_event_unreg_fd(rc->rc_event_stream, restconf_native_stream_cb);
|
||||
clixon_event_unreg_timeout(native_stream_timeout, req);
|
||||
close(rc->rc_event_stream);
|
||||
rc->rc_event_stream = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Process a stream request, native variant
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||
* @param[in] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
|
||||
* @param[in] timeout Stream timeout
|
||||
* @param[out] finish Not used in native?
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see api_stream fcgi implementation
|
||||
* @note According to RFC8040 Sec 6 accept-stream is text/event-stream, but stream data
|
||||
* is XML according to RFC5277. But what is error return? assume XML here
|
||||
*/
|
||||
static int
|
||||
api_native_stream(clixon_handle h,
|
||||
void *req,
|
||||
cvec *qvec,
|
||||
int timeout,
|
||||
int *finish)
|
||||
{
|
||||
int retval = -1;
|
||||
restconf_stream_data *sd = (restconf_stream_data *)req;
|
||||
restconf_conn *rc;
|
||||
char *path = NULL;
|
||||
char *request_method = NULL; /* GET,.. */
|
||||
char *streampath;
|
||||
int pretty;
|
||||
char **pvec = NULL;
|
||||
int pn;
|
||||
cvec *pcvec = NULL; /* for rest api */
|
||||
cxobj *xerr = NULL;
|
||||
char *media_str = NULL;
|
||||
char *stream_name;
|
||||
restconf_media media_reply = YANG_DATA_XML;
|
||||
int ret;
|
||||
int backend_socket = -1;
|
||||
|
||||
clixon_debug(CLIXON_DBG_STREAM, "");
|
||||
if (req == NULL){
|
||||
clixon_err(OE_RESTCONF, EINVAL, "req is NULL");
|
||||
goto done;
|
||||
}
|
||||
rc = sd->sd_conn;
|
||||
streampath = clicon_option_str(h, "CLICON_STREAM_PATH");
|
||||
if ((path = restconf_uripath(h)) == NULL)
|
||||
goto done;
|
||||
clixon_debug(CLIXON_DBG_STREAM, "path:%s", path);
|
||||
request_method = restconf_param_get(h, "REQUEST_METHOD");
|
||||
clixon_debug(CLIXON_DBG_STREAM, "method:%s", request_method);
|
||||
pretty = restconf_pretty_get(h);
|
||||
clixon_debug(CLIXON_DBG_STREAM, "pretty:%d", pretty);
|
||||
/* Get media for output (proactive negotiation) RFC7231 by using
|
||||
* Accept:. This is for methods that have output, such as GET,
|
||||
* operation POST, etc
|
||||
* If accept is * default is yang-json
|
||||
*/
|
||||
media_str = restconf_param_get(h, "HTTP_ACCEPT");
|
||||
clixon_debug(CLIXON_DBG_STREAM, "accept(media):%s", media_str);
|
||||
if (media_str == NULL){
|
||||
if (restconf_not_acceptable(h, sd, pretty, media_reply) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* Accept only text_event-stream or */
|
||||
if (strcmp(media_str, "*/*") != 0 &&
|
||||
strcmp(media_str, "text/event-stream") != 0){
|
||||
if (restconf_not_acceptable(h, req, pretty, media_reply) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||
goto done;
|
||||
if (strlen(pvec[0]) != 0){
|
||||
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||
goto done;
|
||||
if (api_return_err0(h, req, xerr, pretty, media_reply, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
else if (strcmp(pvec[1], streampath)){
|
||||
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||
goto done;
|
||||
if (api_return_err0(h, req, xerr, pretty, media_reply, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
else if ((stream_name = pvec[2]) == NULL ||
|
||||
strlen(stream_name) == 0){
|
||||
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||
goto done;
|
||||
if (api_return_err0(h, req, xerr, pretty, media_reply, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
clixon_debug(CLIXON_DBG_STREAM, "stream-name: %s", stream_name);
|
||||
if (uri_str2cvec(path, '/', '=', 1, &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
||||
goto done;
|
||||
/* If present, check credentials. See "plugin_credentials" in plugin
|
||||
* See RFC 8040 section 2.5
|
||||
*/
|
||||
if ((ret = restconf_authentication_cb(h, req, pretty, media_reply)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto ok;
|
||||
clixon_debug(CLIXON_DBG_STREAM, "passed auth");
|
||||
if (restconf_subscription(h, req, stream_name, qvec, pretty, media_reply, &backend_socket) < 0)
|
||||
goto done;
|
||||
if (backend_socket != -1){
|
||||
// XXX Could add forking here eventurally
|
||||
/* Listen to backend socket */
|
||||
if (clixon_event_reg_fd(backend_socket,
|
||||
restconf_native_stream_cb,
|
||||
sd,
|
||||
"stream socket") < 0)
|
||||
goto done;
|
||||
rc->rc_event_stream = backend_socket;
|
||||
/* Timeout of notification stream, close after limited lifetime, for debug */
|
||||
if (timeout){
|
||||
struct timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
t.tv_sec += timeout;
|
||||
clixon_event_reg_timeout(t, native_stream_timeout, rc, "Stream timeout");
|
||||
}
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
clixon_debug(CLIXON_DBG_STREAM, "retval:%d", retval);
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (path)
|
||||
free(path);
|
||||
if (pvec)
|
||||
free(pvec);
|
||||
if (pcvec)
|
||||
cvec_free(pcvec);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -166,131 +461,8 @@ int
|
|||
api_stream(clixon_handle h,
|
||||
void *req,
|
||||
cvec *qvec,
|
||||
int timeout,
|
||||
int *finish)
|
||||
{
|
||||
int retval = -1;
|
||||
char *path = NULL;
|
||||
char *request_method = NULL; /* GET,.. */
|
||||
char *streampath;
|
||||
int pretty;
|
||||
char **pvec = NULL;
|
||||
int pn;
|
||||
cvec *pcvec = NULL; /* for rest api */
|
||||
cxobj *xerr = NULL;
|
||||
char *media_str = NULL;
|
||||
char *stream_name;
|
||||
restconf_media media_stream = TEXT_EVENT_STREAM; /* text/event-stream, see RFC8040 sec 6 */
|
||||
restconf_media media_out = YANG_DATA_XML;
|
||||
int ret;
|
||||
int backend_socket = -1;
|
||||
|
||||
fprintf(stderr, "%s\n", __FUNCTION__);
|
||||
clixon_debug(CLIXON_DBG_STREAM, "");
|
||||
if (req == NULL){
|
||||
errno = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
streampath = clicon_option_str(h, "CLICON_STREAM_PATH");
|
||||
if ((path = restconf_uripath(h)) == NULL)
|
||||
goto done;
|
||||
clixon_debug(CLIXON_DBG_STREAM, "path:%s", path);
|
||||
request_method = restconf_param_get(h, "REQUEST_METHOD");
|
||||
clixon_debug(CLIXON_DBG_STREAM, "method:%s", request_method);
|
||||
pretty = restconf_pretty_get(h);
|
||||
clixon_debug(CLIXON_DBG_STREAM, "pretty:%d", pretty);
|
||||
/* Get media for output (proactive negotiation) RFC7231 by using
|
||||
* Accept:. This is for methods that have output, such as GET,
|
||||
* operation POST, etc
|
||||
* If accept is * default is yang-json
|
||||
*/
|
||||
media_str = restconf_param_get(h, "HTTP_ACCEPT");
|
||||
clixon_debug(CLIXON_DBG_STREAM, "accept(media):%s", media_str);
|
||||
if (media_str == NULL){
|
||||
if (restconf_not_acceptable(h, req, pretty, media_out) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
media_stream = restconf_media_str2int(media_str);
|
||||
clixon_debug(CLIXON_DBG_STREAM, "media_out:%s", restconf_media_int2str(media_stream));
|
||||
switch ((int)media_stream){
|
||||
case -1:
|
||||
if (strcmp(media_str, "*/*") == 0){ /* catch-all */
|
||||
media_out = TEXT_EVENT_STREAM;
|
||||
}
|
||||
else{
|
||||
if (restconf_not_acceptable(h, req, pretty, media_out) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
break;
|
||||
case TEXT_EVENT_STREAM:
|
||||
break;
|
||||
default:
|
||||
if (restconf_not_acceptable(h, req, pretty, media_out) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
}
|
||||
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||
goto done;
|
||||
if (strlen(pvec[0]) != 0){
|
||||
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||
goto done;
|
||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
else if (strcmp(pvec[1], streampath)){
|
||||
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||
goto done;
|
||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
else if ((stream_name = pvec[2]) == NULL ||
|
||||
strlen(stream_name) == 0){
|
||||
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||
goto done;
|
||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
clixon_debug(CLIXON_DBG_STREAM, "stream-name: %s", stream_name);
|
||||
if (uri_str2cvec(path, '/', '=', 1, &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
||||
goto done;
|
||||
/* If present, check credentials. See "plugin_credentials" in plugin
|
||||
* See RFC 8040 section 2.5
|
||||
*/
|
||||
if ((ret = restconf_authentication_cb(h, req, pretty, media_out)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto ok;
|
||||
clixon_debug(CLIXON_DBG_STREAM, "passed auth");
|
||||
if (restconf_subscription(h, req, stream_name, qvec, pretty, media_out, &backend_socket) < 0)
|
||||
goto done;
|
||||
if (backend_socket != -1){
|
||||
// XXX Could add forking here eventurally
|
||||
/* Listen to backend socket */
|
||||
if (clixon_event_reg_fd(backend_socket,
|
||||
restconf_native_stream_cb,
|
||||
req,
|
||||
"stream socket") < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
fprintf(stderr, "%s retval %d\n", __FUNCTION__, retval);
|
||||
clixon_debug(CLIXON_DBG_STREAM, "retval:%d", retval);
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (path)
|
||||
free(path);
|
||||
if (pvec)
|
||||
free(pvec);
|
||||
if (pcvec)
|
||||
cvec_free(pcvec);
|
||||
return retval;
|
||||
return api_native_stream(h, req, qvec, timeout, finish);
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBNGHTTP2 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue