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