From 12dddfd794b6df1694855f03e533c921a3b4402d Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Mon, 14 Feb 2022 20:59:10 +0100 Subject: [PATCH] * RESTCONF, * Refactored http1 code * fixed some http1/http2 only compile issues --- apps/restconf/restconf_http1.c | 4 + apps/restconf/restconf_native.c | 589 +++++++++++++++++++------------- apps/restconf/restconf_native.h | 3 +- test/lib.sh | 6 +- test/test_perf_restconf.sh | 39 +-- test/test_perf_restconf_ssl.sh | 43 ++- 6 files changed, 408 insertions(+), 276 deletions(-) diff --git a/apps/restconf/restconf_http1.c b/apps/restconf/restconf_http1.c index b69e72c9..2a950d3b 100644 --- a/apps/restconf/restconf_http1.c +++ b/apps/restconf/restconf_http1.c @@ -350,7 +350,9 @@ restconf_http1_path_root(clicon_handle h, char *subject = NULL; cxobj *xerr = NULL; int pretty; +#ifdef HAVE_LIBNGHTTP2 int ret; +#endif clicon_debug(1, "------------"); pretty = restconf_pretty_get(h); @@ -417,7 +419,9 @@ restconf_http1_path_root(clicon_handle h, fail: if (restconf_param_del_all(h) < 0) goto done; +#ifdef HAVE_LIBNGHTTP2 upgrade: +#endif if (sd->sd_code) if (restconf_http1_reply(rc, sd) < 0) goto done; diff --git a/apps/restconf/restconf_native.c b/apps/restconf/restconf_native.c index 7c5e287d..e067e856 100644 --- a/apps/restconf/restconf_native.c +++ b/apps/restconf/restconf_native.c @@ -79,6 +79,7 @@ #endif /*! + * @param[in] rc Restconf connection handle * @see restconf_stream_free */ restconf_stream_data * @@ -94,6 +95,10 @@ restconf_stream_data_new(restconf_conn *rc, memset(sd, 0, sizeof(restconf_stream_data)); sd->sd_stream_id = stream_id; sd->sd_fd = -1; + if ((sd->sd_inbuf = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + return NULL; + } if ((sd->sd_indata = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); return NULL; @@ -111,6 +116,9 @@ restconf_stream_data_new(restconf_conn *rc, return sd; } +/*! + * @param[in] rc Restconf connection handle + */ restconf_stream_data * restconf_stream_find(restconf_conn *rc, int32_t id) @@ -127,12 +135,18 @@ restconf_stream_find(restconf_conn *rc, return NULL; } + +/* + * @param[in] sd Restconf data stream + */ int restconf_stream_free(restconf_stream_data *sd) { if (sd->sd_fd != -1) { close(sd->sd_fd); } + if (sd->sd_inbuf) + cbuf_free(sd->sd_inbuf); if (sd->sd_indata) cbuf_free(sd->sd_indata); if (sd->sd_outp_hdrs) @@ -467,6 +481,329 @@ native_clear_input(clicon_handle h, } #endif +/*! Read HTTP from SSL socket + * + * @param[in] rc Restconf connection handle + * @param[in] buf Input buffer + * @param[in] sz Size of input buffer + * @param[out] np Bytes read + * @param[out] again If set, read data again, do not continue processing + * @retval -1 Error + * @retval 0 OK + */ +static int +read_ssl(restconf_conn *rc, + char *buf, + size_t sz, + ssize_t *np, + int *again) +{ + int retval = -1; + int sslerr; + + if ((*np = SSL_read(rc->rc_ssl, buf, sz)) < 0){ + sslerr = SSL_get_error(rc->rc_ssl, *np); + clicon_debug(1, "%s SSL_read() n:%zd errno:%d sslerr:%d", __FUNCTION__, *np, errno, sslerr); + switch (sslerr){ + case SSL_ERROR_WANT_READ: /* 2 */ + /* SSL_ERROR_WANT_READ is returned when the last operation was a read operation + * from a nonblocking BIO. + * That is, it can happen if restconf_socket_init() below is called + * with SOCK_NONBLOCK + */ + clicon_debug(1, "%s SSL_read SSL_ERROR_WANT_READ", __FUNCTION__); + usleep(1000); + *again = 1; + break; + default: + clicon_err(OE_XML, errno, "SSL_read"); + goto done; + } /* switch */ + } + retval = 0; + done: + return retval; +} + +/*! Read HTTP from regular socket + * + * @param[in] rc Restconf connection handle + * @param[in] buf Input buffer + * @param[in] sz Size of input buffer + * @param[out] np Bytes read + * @param[out] again If set, read data again, do not continue processing + * @retval -1 Error + * @retval 0 Socket closed, quit + * @retval 1 OK + * XXX: + * readmore/continue + * goto ok + */ +static int +read_regular(restconf_conn *rc, + char *buf, + size_t sz, + ssize_t *np, + int *again) +{ + int retval = -1; + + if ((*np = read(rc->rc_s, buf, sz)) < 0){ /* XXX atomicio ? */ + switch(errno){ + case ECONNRESET:/* Connection reset by peer */ + clicon_debug(1, "%s %d Connection reset by peer", __FUNCTION__, rc->rc_s); + clixon_event_unreg_fd(rc->rc_s, restconf_connection); + close(rc->rc_s); + restconf_conn_free(rc); + retval = 0; /* Close socket and ssl */ + goto done; + break; + case EAGAIN: + clicon_debug(1, "%s read EAGAIN", __FUNCTION__); + usleep(1000); + *again = 1; + break; + default:; + clicon_err(OE_XML, errno, "read"); + goto done; + break; + } + } + retval = 1; + done: + return retval; +} + +#ifdef HAVE_HTTP1 +/*! RESTCONF HTTP/1 processing after chunk of bytes read + * + * @param[in] rc Restconf connection handle + * @param[in] buf Input buffer + * @param[in] n Length of data in input buffer + * @param[out] readmore If set, read data again, do not continue processing + * @retval -1 Error + * @retval 0 Socket closed, quit + * @retval 1 OK + */ +static int +restconf_http1(restconf_conn *rc, + char *buf, + size_t n, + int *readmore) +{ + int retval = -1; + restconf_stream_data *sd; + clicon_handle h; + int ret; + int status; + + h = rc->rc_h; + if ((sd = restconf_stream_find(rc, 0)) == NULL){ + clicon_err(OE_RESTCONF, EINVAL, "restconf stream not found"); + goto done; + } + /* Two states for reading: + * 1) Initial reading of headers, parse from start + * 2) Headers are read / body started, dont parse, just append body + */ + /* Check whole message is read. + * Only way this could happen is that body is read + * 0: No Content-Length or 0 + * header does not contain Content-Length or is 0 + * (OR: message header not fully read SHOULDNT HAPPEN IF BODY READ) + * 1: Content-Length found but body has fewer bytes, ie remaining bytes to read + * 2: Content-Length found and matches body length. No more bytes to read + */ + if ((ret = http1_check_content_length(h, sd, &status)) < 0) + goto done; + if (status == 1){ /* Next read: keep header state and only append inbody */ + if (cbuf_append_buf(sd->sd_indata, buf, n) < 0){ + clicon_err(OE_UNIX, errno, "cbuf_append"); + goto done; + } + } + else { + /* multi-buffer for multiple reads + * This is different from sd_indata that it is before and includes headers + */ + if (cbuf_append_buf(sd->sd_inbuf, buf, n) < 0){ + clicon_err(OE_UNIX, errno, "cbuf_append"); + goto done; + } + if (clixon_http1_parse_string(h, rc, cbuf_get(sd->sd_inbuf)) < 0){ + /* XXX This does not work for SSL */ + if (rc->rc_ssl){ + ret = SSL_pending(rc->rc_ssl); + } + else if ((ret = clixon_event_poll(rc->rc_s)) < 0) + goto done; + if (ret > 0){ + if (native_clear_input(h, sd) < 0) + goto done; + (*readmore)++; + goto ok; + } + /* Return error. Honsetly, the sender could just e slow, it should really be a + * timeout here. + */ + if (native_send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml", + "protocolmalformed-messageThe requested URL or a header is in some way badly formed") < 0) + goto done; + } + /* Check for Continue and if so reply with 100 Continue + * ret == 1: send reply + */ + if ((ret = http1_check_expect(h, rc, sd)) < 0) + goto done; + if (ret == 1){ + if (native_buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf), + rc->rc_s, rc->rc_ssl) < 0) + goto done; + cvec_reset(sd->sd_outp_hdrs); + cbuf_reset(sd->sd_outp_buf); + } + } + /* Check whole message is read. + * Only way this could happen is that body is read + * 0: No Content-Length or 0 + * header does not contain Content-Length or is 0 + * (OR: message header not fully read SHOULDNT HAPPEN IF BODY READ OK in parse above) + * 1: Content-Length found but body has fewer bytes, ie remaining bytes to read + * 2: Content-Length found and matches body length. No more bytes to read + */ + if ((ret = http1_check_content_length(h, sd, &status)) < 0) + goto done; + if (status == 1){ + (*readmore)++; + goto ok; + } + /* main restconf processing */ + if (restconf_http1_path_root(h, rc) < 0) + goto done; + if (native_buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf), + rc->rc_s, rc->rc_ssl) < 0) + goto done; + cvec_reset(sd->sd_outp_hdrs); /* Can be done in native_send_reply */ + cbuf_reset(sd->sd_outp_buf); + if (rc->rc_exit){ /* Server-initiated exit */ + SSL_free(rc->rc_ssl); + rc->rc_ssl = NULL; + if (close(rc->rc_s) < 0){ + clicon_err(OE_UNIX, errno, "close"); + goto done; + } + clixon_event_unreg_fd(rc->rc_s, restconf_connection); + restconf_conn_free(rc); + retval = 0; + goto done; + } + ok: + retval = 1; + done: + return retval; +} +#endif + +static int +restconf_http2_upgrade(restconf_conn *rc) +{ + int retval = -1; + restconf_stream_data *sd; + + if ((sd = restconf_stream_find(rc, 0)) == NULL){ + clicon_err(OE_RESTCONF, EINVAL, "restconf stream not found"); + goto done; + } + if (sd->sd_upgrade2){ + nghttp2_error ngerr; + + /* Switch to http/2 according to RFC 7540 Sec 3.2 and RFC 7230 Sec 6.7 */ + rc->rc_proto = HTTP_2; + if (http2_session_init(rc) < 0){ + restconf_close_ssl_socket(rc, 1); + goto done; + } + /* The HTTP/1.1 request that is sent prior to upgrade is assigned a + * stream identifier of 1 (see Section 5.1.1) with default priority + */ + sd->sd_stream_id = 1; + /* The first HTTP/2 frame sent by the server MUST be a server connection + * preface (Section 3.5) consisting of a SETTINGS frame (Section 6.5). + */ + if ((ngerr = nghttp2_session_upgrade2(rc->rc_ngsession, + sd->sd_settings2, + sd->sd_settings2?strlen((const char*)sd->sd_settings2):0, + 0, /* XXX: 1 if HEAD */ + NULL)) < 0){ + clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_upgrade2"); + goto done; + } + if (http2_send_server_connection(rc) < 0){ + restconf_close_ssl_socket(rc, 1); + goto done; + } + /* Use params from original http/1 session to http/2 stream */ + if (http2_exec(rc, sd, rc->rc_ngsession, 1) < 0) + goto done; + /* + * Very special case for http/1->http/2 upgrade and restconf "restart" + * That is, the restconf daemon is restarted under the hood, and the session + * is closed in mid-step: it needs a couple of extra rounds to complete the http/2 + * settings before it completes. + * Maybe a more precise way would be to encode that semantics using recieved http/2 + * frames instead of just postponing nrof events? + */ + if (clixon_exit_get() == 1){ + clixon_exit_set(3); + } + } + + retval = 0; + done: + return retval; +} + +/*! + * @param[in] buf Input buffer + * @param[in] n Size of input buffer + * @retval -1 Error + * @retval 0 Socket closed, quit + * @retval 1 OK + */ +static int +restconf_http2(restconf_conn *rc, + char *buf, + size_t n, + int *readmore) +{ + int retval = -1; + int ret; + nghttp2_error ngerr; + + if (rc->rc_exit){ /* Server-initiated exit for http/2 */ + if ((ngerr = nghttp2_session_terminate_session(rc->rc_ngsession, 0)) < 0){ + clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_terminate_session %d", ngerr); + goto done; // XXX not here in original? + } + } + else { + if ((ret = http2_recv(rc, (unsigned char *)buf, n)) < 0) + goto done; + if (ret == 0){ + restconf_close_ssl_socket(rc, 1); + if (restconf_conn_free(rc) < 0) + goto done; + retval = 0; + goto done; + } + /* There may be more data frames */ + (*readmore)++; + } + retval = 1; + done: + return retval; +} + /*! New data connection after accept, receive and reply on data socket * * @param[in] s Socket where message arrived. read from this. @@ -487,21 +824,10 @@ restconf_connection(int s, int retval = -1; restconf_conn *rc = NULL; ssize_t n; - char buf[1024]; /* Alter BUFSIZ (8K) from stdio.h 8K. 256 fails some tests */ - char *totbuf = NULL; - size_t totlen = 0; + // char buf[1024]; /* Alter BUFSIZ (8K) from stdio.h 8K. 256 fails some tests */ + char buf[32]; /* Alter BUFSIZ (8K) from stdio.h 8K. 256 fails some tests */ int readmore = 1; - int sslerr; - int contnr = 0; /* Continue sent */ -#ifdef HAVE_LIBNGHTTP2 int ret; -#endif -#ifdef HAVE_HTTP1 - clicon_handle h; - restconf_stream_data *sd; - int status; - int http1_headers_read = 0; /* Dont re-parse headers, just append body */ -#endif clicon_debug(1, "%s %d", __FUNCTION__, s); if ((rc = (restconf_conn*)arg) == NULL){ @@ -514,54 +840,18 @@ restconf_connection(int s, readmore = 0; /* Example: curl -Ssik -u wilma:bar -X GET https://localhost/restconf/data/example:x */ if (rc->rc_ssl){ - /* Non-ssl gets n == 0 here! - curl -Ssik --key /var/tmp/./test_restconf_ssl_certs.sh/certs/limited.key --cert /var/tmp/./test_restconf_ssl_certs.sh/certs/limited.crt -X GET https://localhost/restconf/data/example:x - */ - if ((n = SSL_read(rc->rc_ssl, buf, sizeof(buf))) < 0){ - sslerr = SSL_get_error(rc->rc_ssl, n); - clicon_debug(1, "%s SSL_read() n:%zd errno:%d sslerr:%d", __FUNCTION__, n, errno, sslerr); - switch (sslerr){ - case SSL_ERROR_WANT_READ: /* 2 */ - /* SSL_ERROR_WANT_READ is returned when the last operation was a read operation - * from a nonblocking BIO. - * That is, it can happen if restconf_socket_init() below is called - * with SOCK_NONBLOCK - */ - clicon_debug(1, "%s SSL_read SSL_ERROR_WANT_READ", __FUNCTION__); - usleep(1000); - readmore = 1; - break; - default: - clicon_err(OE_XML, errno, "SSL_read"); - goto done; - } /* switch */ - continue; /* readmore */ - } + if (read_ssl(rc, buf, sizeof(buf), &n, &readmore) < 0) + goto done; } - else{ - if ((n = read(rc->rc_s, buf, sizeof(buf))) < 0){ /* XXX atomicio ? */ - switch(errno){ - case ECONNRESET:/* Connection reset by peer */ - clicon_debug(1, "%s %d Connection reset by peer", __FUNCTION__, rc->rc_s); - clixon_event_unreg_fd(rc->rc_s, restconf_connection); - close(rc->rc_s); - restconf_conn_free(rc); - goto ok; /* Close socket and ssl */ - break; - case EAGAIN: - clicon_debug(1, "%s read EAGAIN", __FUNCTION__); - usleep(1000); - readmore = 1; - break; - default:; - clicon_err(OE_XML, errno, "read"); - goto done; - break; - } - continue; - } + else{ /* Not SSL */ + if ((ret = read_regular(rc, buf, sizeof(buf), &n, &readmore)) < 0) + goto done; + if (ret == 0) + goto ok; /* abort here */ } clicon_debug(1, "%s read:%zd", __FUNCTION__, n); + if (readmore) + continue; if (n == 0){ clicon_debug(1, "%s n=0 closing socket", __FUNCTION__); if (restconf_close_ssl_socket(rc, 0) < 0) @@ -574,181 +864,26 @@ restconf_connection(int s, #ifdef HAVE_HTTP1 case HTTP_10: case HTTP_11: - h = rc->rc_h; - /* default stream */ - if ((sd = restconf_stream_find(rc, 0)) == NULL){ - clicon_err(OE_RESTCONF, EINVAL, "restconf stream not found"); + if ((ret = restconf_http1(rc, buf, n, &readmore)) < 0) goto done; - } - if (http1_headers_read){ - if (cbuf_append_buf(sd->sd_indata, buf, n) < 0){ - clicon_err(OE_UNIX, errno, "cbuf_append"); - goto done; - } - } - else { - /* multi-buffer for multiple reads */ - totlen += n; - if ((totbuf = realloc(totbuf, totlen+1)) == NULL){ - clicon_err(OE_UNIX, errno, "realloc"); - goto done; - } - memcpy(&totbuf[totlen-n], buf, n); - totbuf[totlen] = '\0'; - } - /* - * The following cases are handled after parsing - * - Parse error - * - More data to read? - * - clear and read more - * - No more data - * - send error - * - Parse OK - * - - */ - if (!http1_headers_read && - clixon_http1_parse_string(h, rc, totbuf) < 0){ - /* Maybe only for non-ssl ? */ - if ((ret = clixon_event_poll(rc->rc_s)) < 0) - goto done; - if (ret == 1){ - if (native_clear_input(h, sd) < 0) - goto done; - readmore++; - continue; - } - if (native_send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml", - "protocolmalformed-messageThe requested URL or a header is in some way badly formed") < 0) - goto done; - } - else{ - /* Check for Continue and if so reply with 100 Continue - * ret == 1: send reply - */ - if (!contnr){ - if ((ret = http1_check_expect(h, rc, sd)) < 0) - goto done; - if (ret == 1){ - if (native_buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf), - rc->rc_s, rc->rc_ssl) < 0) - goto done; - cvec_reset(sd->sd_outp_hdrs); - cbuf_reset(sd->sd_outp_buf); - contnr++; - } - } - /* Check whole message is read. - * Only way this could happen is that body is read - * 0: No Content-Length or 0 - * header does not contain Content-Length or is 0 - * (OR: message header not fully read SHOULDNT HAPPEN IF BODY READ) - * 1: Content-Length found but body has fewer bytes, ie remaining bytes to read - * 2: Content-Length found and matches body length. No more bytes to read - */ - if ((ret = http1_check_content_length(h, sd, &status)) < 0) - goto done; - switch (status){ - case 1: - /* Next read: keep header state and only append inbody */ - http1_headers_read++; - readmore++; - continue; - break; - - break; - case 0: - case 2: - default: - break; - } - if (status == 0){ - - } - if (restconf_http1_path_root(h, rc) < 0) - goto done; - if (native_buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf), - rc->rc_s, rc->rc_ssl) < 0) - goto done; - cvec_reset(sd->sd_outp_hdrs); /* Can be done in native_send_reply */ - cbuf_reset(sd->sd_outp_buf); - } - if (rc->rc_exit){ /* Server-initiated exit for http/2 */ - SSL_free(rc->rc_ssl); - rc->rc_ssl = NULL; - if (close(rc->rc_s) < 0){ - clicon_err(OE_UNIX, errno, "close"); - goto done; - } - clixon_event_unreg_fd(rc->rc_s, restconf_connection); - restconf_conn_free(rc); + if (ret == 0) goto ok; - } + if (readmore) + continue; #ifdef HAVE_LIBNGHTTP2 - if (sd->sd_upgrade2){ - nghttp2_error ngerr; - - /* Switch to http/2 according to RFC 7540 Sec 3.2 and RFC 7230 Sec 6.7 */ - rc->rc_proto = HTTP_2; - if (http2_session_init(rc) < 0){ - restconf_close_ssl_socket(rc, 1); - goto done; - } - /* The HTTP/1.1 request that is sent prior to upgrade is assigned a - * stream identifier of 1 (see Section 5.1.1) with default priority - */ - sd->sd_stream_id = 1; - /* The first HTTP/2 frame sent by the server MUST be a server connection - * preface (Section 3.5) consisting of a SETTINGS frame (Section 6.5). - */ - if ((ngerr = nghttp2_session_upgrade2(rc->rc_ngsession, - sd->sd_settings2, - sd->sd_settings2?strlen((const char*)sd->sd_settings2):0, - 0, /* XXX: 1 if HEAD */ - NULL)) < 0){ - clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_upgrade2"); - goto done; - } - if (http2_send_server_connection(rc) < 0){ - restconf_close_ssl_socket(rc, 1); - goto done; - } - /* Use params from original http/1 session to http/2 stream */ - if (http2_exec(rc, sd, rc->rc_ngsession, 1) < 0) - goto done; - /* - * Very special case for http/1->http/2 upgrade and restconf "restart" - * That is, the restconf daemon is restarted under the hood, and the session - * is closed in mid-step: it needs a couple of extra rounds to complete the http/2 - * settings before it completes. - * Maybe a more precise way would be to encode that semantics using recieved http/2 - * frames instead of just postponing nrof events? - */ - if (clixon_exit_get() == 1){ - clixon_exit_set(3); - } - } -#endif + if (restconf_http2_upgrade(rc) < 0) + goto done; +#endif /* HAVE_LIBNGHTTP2 */ break; #endif /* HAVE_HTTP1 */ #ifdef HAVE_LIBNGHTTP2 case HTTP_2: - if (rc->rc_exit){ /* Server-initiated exit for http/2 */ - nghttp2_error ngerr; - if ((ngerr = nghttp2_session_terminate_session(rc->rc_ngsession, 0)) < 0) - clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_terminate_session %d", ngerr); - } - else { - if ((ret = http2_recv(rc, (unsigned char *)buf, n)) < 0) - goto done; - if (ret == 0){ - restconf_close_ssl_socket(rc, 1); - if (restconf_conn_free(rc) < 0) - goto done; - goto ok; - } - /* There may be more data frames */ - readmore++; - } + if ((ret = restconf_http2(rc, buf, n, &readmore)) < 0) + goto done; + if (ret == 0) + goto ok; + if (readmore) + continue; break; #endif /* HAVE_LIBNGHTTP2 */ default: @@ -758,8 +893,6 @@ restconf_connection(int s, ok: retval = 0; done: - if (totbuf) - free(totbuf); clicon_debug(1, "%s retval %d", __FUNCTION__, retval); return retval; } /* restconf_connection */ diff --git a/apps/restconf/restconf_native.h b/apps/restconf/restconf_native.h index 5a1db990..b84740fc 100644 --- a/apps/restconf/restconf_native.h +++ b/apps/restconf/restconf_native.h @@ -73,7 +73,8 @@ typedef struct { cbuf *sd_body; /* http output body as cbuf terminated with \r\n */ size_t sd_body_len; /* Content-Length, note for HEAD body body can be NULL and this non-zero */ size_t sd_body_offset; /* Offset into body */ - cbuf *sd_indata; /* Receive/input data */ + cbuf *sd_inbuf; /* Receive/input buf (whole message) */ + cbuf *sd_indata; /* Receive/input data body */ char *sd_path; /* Uri path, uri-encoded, without args (eg ?) */ uint16_t sd_code; /* If != 0 send a reply XXX: need reply flag? */ struct restconf_conn *sd_conn; /* Backpointer to connection this stream is part of */ diff --git a/test/lib.sh b/test/lib.sh index afca104b..e81679b3 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -439,8 +439,8 @@ function wait_restconf(){ else myproto=${RCPROTO} fi -# echo "curl $CURLOPTS $myproto://localhost/restconf" - hdr=$(curl $CURLOPTS $myproto://localhost/restconf 2> /dev/null) +# echo "curl $CURLOPTS -X GET $myproto://localhost/restconf" + hdr=$(curl $CURLOPTS -X GET $myproto://localhost/restconf 2> /dev/null) # echo "hdr:\"$hdr\"" let i=0; while [[ "$hdr" != *"200"* ]]; do @@ -449,7 +449,7 @@ function wait_restconf(){ err1 "restconf timeout $DEMWAIT seconds" fi sleep $DEMSLEEP - hdr=$(curl $CURLOPTS $* $myproto://localhost/restconf 2> /dev/null) + hdr=$(curl $CURLOPTS -X GET $myproto://localhost/restconf 2> /dev/null) # echo "hdr:\"$hdr\"" let i++; done diff --git a/test/test_perf_restconf.sh b/test/test_perf_restconf.sh index 2ad291e4..15a95043 100755 --- a/test/test_perf_restconf.sh +++ b/test/test_perf_restconf.sh @@ -30,10 +30,9 @@ APPNAME=example cfg=$dir/scaling-conf.xml fyang=$dir/scaling.yang -fdataxml=$dir/config.xml # dataxml +fdataxml=$dir/large.xml # dataxml ftest=$dir/test.xml -fconfig=$dir/large.xml -fconfig2=$dir/large2.xml # leaf-list +fdataxml2=$dir/large2.xml # leaf-list XXX use restconf foutput=$dir/output.xml foutput2=$dir/output2.xml @@ -66,6 +65,7 @@ cat < $cfg $cfg clixon-restconf:allow-auth-none + ietf-netconf:startup $dir ${YANG_INSTALLDIR} $fyang @@ -78,7 +78,7 @@ cat < $cfg /usr/local/lib/example/cli /usr/local/lib/example/clispec 0 - ietf-netconf:startup + 128 $RESTCONFIG EOF @@ -91,6 +91,9 @@ if [ $BE -ne 0 ]; then err fi +# sudo pkill clixon_backend # extra +# sleep 1 + new "start backend -s init -f $cfg -- -s" start_backend -s init -f $cfg -- -s fi @@ -110,23 +113,21 @@ new "wait restconf" wait_restconf # Check this later with committed data -new "generate config with $perfnr list entries" +new "generate config with $perfnr list entries to $fdataxml" echo -n "" > $fdataxml for (( i=0; i<$perfnr; i++ )); do echo -n "$i$i" >> $fdataxml done echo -n "" >> $fdataxml # No CR -echo -n "$DEFAULTHELLO" > $fconfig -cat $fdataxml >> $fconfig -echo "]]>]]>" >> $fconfig - # Now take large config file and write it via netconf to candidate new "test time exists" expectpart "$(time -p ls)" 0 -new "restconf write large config" -expectpart "$(time -p curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data -d @$fdataxml )" 0 "HTTP/$HVER 201" +# Use PUT so it can be repeated +new "restconf PUT large initial config" +echo "curl $CURLOPTS -X PUT -H \"Content-Type: application/yang-data+xml\" $RCPROTO://localhost/restconf/data/scaling:x -d @$fdataxml" +expectpart "$(time -p curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data/scaling:x -d @$fdataxml)" 0 "HTTP/$HVER 20" new "Check running-db contents" curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=config > $foutput @@ -213,19 +214,15 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO /dev/null; } 2>&1 | awk '/real/ {print $2}' # Now do leaf-lists istead of leafs - -#new "generate leaf-list config" -echo -n "$DEFAULTHELLOreplace" > $fconfig2 +new "generate config with $perfnr leaf-lists to $fdataxml2" +echo -n "" > $fdataxml2 for (( i=0; i<$perfnr; i++ )); do - echo -n "$i" >> $fconfig2 + echo -n "$i" >> $fdataxml2 done -echo "]]>]]>" >> $fconfig2 +echo -n "" >> $fdataxml2 # No CR -new "netconf replace large list-leaf config" -expecteof_file "time -p $clixon_netconf -qf $cfg" 0 "$fconfig2" "^]]>]]>$" 2>&1 | awk '/real/ {print $2}' - -new "netconf commit large leaf-list config" -expecteof "time -p $clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$" 2>&1 | awk '/real/ {print $2}' +new "restconf replace large list-leaf config" +expectpart "$(time -p curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data/scaling:x -d @$fdataxml2)" 0 "HTTP/$HVER 20" new "netconf add $perfreq small leaf-list config" { time -p for (( i=0; i<$perfreq; i++ )); do diff --git a/test/test_perf_restconf_ssl.sh b/test/test_perf_restconf_ssl.sh index ed8e23c3..9b7d5a7d 100755 --- a/test/test_perf_restconf_ssl.sh +++ b/test/test_perf_restconf_ssl.sh @@ -4,8 +4,8 @@ # Add, get and delete entries # Override default to use http/1.1, comment to use https/2 -#HAVE_LIBNGHTTP2=false -#RCPROTO=http +HAVE_LIBNGHTTP2=false +RCPROTO=http # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi @@ -35,10 +35,9 @@ APPNAME=example cfg=$dir/scaling-conf.xml fyang=$dir/scaling.yang -fdataxml=$dir/config.xml # dataxml +fdataxml=$dir/large.xml # dataxml ftest=$dir/test.xml -fconfig=$dir/large.xml -fconfig2=$dir/large2.xml # leaf-list +fdataxml2=$dir/large2.xml # leaf-list XXX use restconf foutput=$dir/output.xml foutput2=$dir/output2.xml @@ -93,6 +92,7 @@ cat < $cfg $cfg clixon-restconf:allow-auth-none + ietf-netconf:startup $dir ${YANG_INSTALLDIR} $fyang @@ -105,7 +105,7 @@ cat < $cfg /usr/local/lib/example/cli /usr/local/lib/example/clispec 0 - ietf-netconf:startup + 128 $RESTCONFIG EOF @@ -118,6 +118,9 @@ if [ $BE -ne 0 ]; then err fi +# sudo pkill clixon_backend # extra +# sleep 1 + new "start backend -s init -f $cfg -- -s" start_backend -s init -f $cfg -- -s fi @@ -137,23 +140,21 @@ new "wait restconf" wait_restconf # Check this later with committed data -new "generate config with $perfnr list entries" +new "generate config with $perfnr list entries to $fdataxml" echo -n "" > $fdataxml for (( i=0; i<$perfnr; i++ )); do echo -n "$i$i" >> $fdataxml done echo -n "" >> $fdataxml # No CR -echo -n "$DEFAULTHELLO" > $fconfig -cat $fdataxml >> $fconfig -echo "]]>]]>" >> $fconfig - # Now take large config file and write it via netconf to candidate new "test time exists" expectpart "$(time -p ls)" 0 -new "restconf write large config" -expectpart "$(time -p curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data -d @$fdataxml )" 0 "HTTP/$HVER 201" +# Use PUT so it can be repeated +new "restconf PUT large initial config" +echo "curl $CURLOPTS -X PUT -H \"Content-Type: application/yang-data+xml\" $RCPROTO://localhost/restconf/data/scaling:x -d @$fdataxml" +expectpart "$(time -p curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data/scaling:x -d @$fdataxml)" 0 "HTTP/$HVER 20" new "Check running-db contents" curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=config > $foutput @@ -240,19 +241,15 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO /dev/null; } 2>&1 | awk '/real/ {print $2}' # Now do leaf-lists istead of leafs - -#new "generate leaf-list config" -echo -n "$DEFAULTHELLOreplace" > $fconfig2 +new "generate config with $perfnr leaf-lists to $fdataxml2" +echo -n "" > $fdataxml2 for (( i=0; i<$perfnr; i++ )); do - echo -n "$i" >> $fconfig2 + echo -n "$i" >> $fdataxml2 done -echo "]]>]]>" >> $fconfig2 +echo -n "" >> $fdataxml2 # No CR -new "netconf replace large list-leaf config" -expecteof_file "time -p $clixon_netconf -qf $cfg" 0 "$fconfig2" "^]]>]]>$" 2>&1 | awk '/real/ {print $2}' - -new "netconf commit large leaf-list config" -expecteof "time -p $clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$" 2>&1 | awk '/real/ {print $2}' +new "restconf replace large list-leaf config" +expectpart "$(time -p curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data/scaling:x -d @$fdataxml2)" 0 "HTTP/$HVER 20" new "netconf add $perfreq small leaf-list config" { time -p for (( i=0; i<$perfreq; i++ )); do