diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b4cbc11..4d86ee98 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,9 +49,9 @@ Expected: May 2022
* path: Local static files within `CLICON_WWW_DATA_ROOT`
* operation GET, HEAD, or OPTIONS
* query parameters not supported
- 5. indata should be NULL (no write operations)
- 6. Limited media: text/html, JavaScript, image, and css
- 7. Authentication as restconf
+ * no indata
+ * media: html, css, js, fonts, image,
+ 7. Authentication, TLS, http/2 as restconf
Generic changes:
* Uniform path selection across fcgi, native http/1 + http/2
diff --git a/apps/restconf/clixon_http_data.c b/apps/restconf/clixon_http_data.c
index b2b8f348..72249aed 100644
--- a/apps/restconf/clixon_http_data.c
+++ b/apps/restconf/clixon_http_data.c
@@ -87,7 +87,7 @@ static const map_str2str mime_map[] = {
/*! Check if uri path denotes a data path
*
* @param[out] data Pointer to string where data starts if retval = 1
- * @retval 0 No, not a data path
+ * @retval 0 No, not a data path, or not enabled
* @retval 1 Yes, a data path and "data" points to www-data if given
*/
int
@@ -97,19 +97,21 @@ api_path_is_data(clicon_handle h,
char *path;
char *http_data_path;
- if ((path = restconf_uripath(h)) == NULL)
- return 0;
- if ((http_data_path = clicon_option_str(h, "CLICON_HTTP_DATA_PATH")) == NULL)
- return 0;
- if (strlen(path) < strlen(http_data_path))
- return 0;
- if (path[0] != '/')
- return 0;
- if (strncmp(path, http_data_path, strlen(http_data_path)) != 0)
- return 0;
- if (data)
- *data = path + strlen(http_data_path);
- return 1;
+ if (restconf_http_data_get(h) == 0)
+ return 0;
+ if ((path = restconf_uripath(h)) == NULL)
+ return 0;
+ if ((http_data_path = clicon_option_str(h, "CLICON_HTTP_DATA_PATH")) == NULL)
+ return 0;
+ if (strlen(path) < strlen(http_data_path))
+ return 0;
+ if (path[0] != '/')
+ return 0;
+ if (strncmp(path, http_data_path, strlen(http_data_path)) != 0)
+ return 0;
+ if (data)
+ *data = path + strlen(http_data_path);
+ return 1;
}
/*! Generic restconf error function on get/head request
diff --git a/apps/restconf/restconf_handle.c b/apps/restconf/restconf_handle.c
index 21c9843f..65d6ca53 100644
--- a/apps/restconf/restconf_handle.c
+++ b/apps/restconf/restconf_handle.c
@@ -90,9 +90,10 @@ struct restconf_handle {
event_stream_t *rh_stream; /* notification streams, see clixon_stream.[ch] */
/* ------ end of common handle ------ */
- clicon_hash_t *rh_params; /* restconf parameters, including http headers */
- clixon_auth_type_t rh_auth_type; /* authentication type */
- int rh_pretty; /* pretty-print for http replies */
+ clicon_hash_t *rh_params; /* restconf parameters, including http headers */
+ clixon_auth_type_t rh_auth_type; /* authentication type */
+ int rh_pretty; /* pretty-print for http replies */
+ int rh_http_data; /* enable-http-data (and if-feature http-data) */
char *rh_fcgi_socket; /* if-feature fcgi, XXX: use WITH_RESTCONF_FCGI ? */
};
@@ -228,12 +229,10 @@ restconf_pretty_get(clicon_handle h)
}
/*! Set restconf pretty-print
- * @param[in] h Clicon handle
- * @param[in] name Data name
- * @param[in] val Data value as null-terminated string
- * @retval 0 OK
- * @retval -1 Error
- * Currently using clixon runtime data but there is risk for colliding names
+ * @param[in] h Clicon handle
+ * @param[in] pretty 0 or 1
+ * @retval 0 OK
+ * @retval -1 Error
*/
int
restconf_pretty_set(clicon_handle h,
@@ -245,6 +244,34 @@ restconf_pretty_set(clicon_handle h,
return 0;
}
+/*! Get restconf http-data
+ * @param[in] h Clixon handle
+ * @retval 0 Yes, http-data enabled
+ * @retval 1 No, http-data disabled
+ */
+int
+restconf_http_data_get(clicon_handle h)
+{
+ struct restconf_handle *rh = handle(h);
+
+ return rh->rh_http_data;
+}
+
+/*! Set restconf http-data
+ * @param[in] h Clixon handle
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+int
+restconf_http_data_set(clicon_handle h,
+ int http_data)
+{
+ struct restconf_handle *rh = handle(h);
+
+ rh->rh_http_data = http_data;
+ return 0;
+}
+
/*! Get restconf fcgi socket path
* @param[in] h Clicon handle
* @retval socketpath
diff --git a/apps/restconf/restconf_handle.h b/apps/restconf/restconf_handle.h
index 52fc9fc1..9dee70be 100644
--- a/apps/restconf/restconf_handle.h
+++ b/apps/restconf/restconf_handle.h
@@ -51,6 +51,8 @@ clixon_auth_type_t restconf_auth_type_get(clicon_handle h);
int restconf_auth_type_set(clicon_handle h, clixon_auth_type_t type);
int restconf_pretty_get(clicon_handle h);
int restconf_pretty_set(clicon_handle h, int pretty);
+int restconf_http_data_get(clicon_handle h);
+int restconf_http_data_set(clicon_handle h, int http_data);
char *restconf_fcgi_socket_get(clicon_handle h);
int restconf_fcgi_socket_set(clicon_handle h, char *socketpath);
diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c
index 1eaf49d8..2980261d 100644
--- a/apps/restconf/restconf_lib.c
+++ b/apps/restconf/restconf_lib.c
@@ -786,6 +786,13 @@ restconf_config_init(clicon_handle h,
else if (strcmp(bstr, "false") == 0)
restconf_pretty_set(h, 0);
}
+ if ((x = xpath_first(xrestconf, nsc, "enable-http-data")) != NULL &&
+ (bstr = xml_body(x)) != NULL){
+ if (strcmp(bstr, "true") == 0)
+ restconf_http_data_set(h, 1);
+ else if (strcmp(bstr, "false") == 0)
+ restconf_http_data_set(h, 0);
+ }
if ((x = xpath_first(xrestconf, nsc, "fcgi-socket")) != NULL &&
(bstr = xml_body(x)) != NULL){
if (restconf_fcgi_socket_set(h, bstr) < 0)
diff --git a/apps/restconf/restconf_nghttp2.c b/apps/restconf/restconf_nghttp2.c
index 008482d0..059c50b2 100644
--- a/apps/restconf/restconf_nghttp2.c
+++ b/apps/restconf/restconf_nghttp2.c
@@ -479,8 +479,10 @@ http2_exec(restconf_conn *rc,
if (restconf_nghttp2_path(sd) < 0)
goto done;
}
- else
+ else{
+ sd->sd_code = 400;
; /* ignore */
+ }
/* If body, add a content-length header
* A server MUST NOT send a Content-Length header field in any response
* with a status code of 1xx (Informational) or 204 (No Content). A
diff --git a/test/config.sh.in b/test/config.sh.in
index 368610fe..6823eff3 100755
--- a/test/config.sh.in
+++ b/test/config.sh.in
@@ -71,8 +71,8 @@ DATASTORE_TOP="config"
# clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in)
CLIXON_AUTOCLI_REV="2022-02-11"
CLIXON_LIB_REV="2021-12-05"
-CLIXON_CONFIG_REV="2022-02-11"
-CLIXON_RESTCONF_REV="2021-05-20"
+CLIXON_CONFIG_REV="2022-03-21"
+CLIXON_RESTCONF_REV="2022-03-21"
CLIXON_EXAMPLE_REV="2020-12-01"
# Length of TSL RSA key
diff --git a/test/lib.sh b/test/lib.sh
index 4e21bfbe..c4a2a771 100755
--- a/test/lib.sh
+++ b/test/lib.sh
@@ -228,6 +228,8 @@ fi
# Args:
# 1: auth-type (one of none, client-cert, user)
# 2: pretty (if true pretty-print restconf return values)
+# [3: proto: http or https]
+# [4: http_data: true or false] # Note feature http-data must be enabled
# Note, if AUTH=none then FEATURE clixon-restconf:allow-auth-none must be enabled
# Note if https, check if server cert/key exists, if not generate them
function restconf_config()
@@ -235,26 +237,42 @@ function restconf_config()
AUTH=$1
PRETTY=$2
- if [ false -a ${WITH_RESTCONF} = "fcgi" ]; then
- echo "clixon-restconf:fcgitrue$AUTH$PRETTY$DBG"
+ # Change this to fixed parameters
+ if [ $# -gt 2 ]; then
+ proto=$3
else
- FEATURES="clixon-restconf:fcgi"
- if [ $RCPROTO = http ]; then
- echo "${FEATURES}true$AUTH$PRETTY$DBGdefault0.0.0.080false"
- else
- certdir=$dir/certs
- if [ ! -f ${dir}/clixon-server-crt.pem ]; then
- certdir=$dir/certs
- test -d $certdir || mkdir $certdir
- srvcert=${certdir}/clixon-server-crt.pem
- srvkey=${certdir}/clixon-server-key.pem
- cacert=${certdir}/clixon-ca-crt.pem
- cakey=${certdir}/clixon-ca-key.pem
- cacerts $cakey $cacert
- servercerts $cakey $cacert $srvkey $srvcert
- fi
- echo "${FEATURES}true$AUTH$PRETTY${certdir}/clixon-server-crt.pem${certdir}/clixon-server-key.pem${certdir}/clixon-ca-crt.pem$DBGdefault0.0.0.0443true"
+ proto=$RCPROTO
+ fi
+ if [ $# -gt 3 ]; then
+ http_data=$4
+ else
+ http_data=false
+ fi
+
+ echo -n "clixon-restconf:fcgi"
+ if [ $proto = http ]; then
+ echo -n "true"
+ if ${http_data}; then
+ echo -n "true"
fi
+ echo "$AUTH$PRETTY$DBGdefault0.0.0.080false"
+ else
+ certdir=$dir/certs
+ if [ ! -f ${dir}/clixon-server-crt.pem ]; then
+ certdir=$dir/certs
+ test -d $certdir || mkdir $certdir
+ srvcert=${certdir}/clixon-server-crt.pem
+ srvkey=${certdir}/clixon-server-key.pem
+ cacert=${certdir}/clixon-ca-crt.pem
+ cakey=${certdir}/clixon-ca-key.pem
+ cacerts $cakey $cacert
+ servercerts $cakey $cacert $srvkey $srvcert
+ fi
+ echo -n "true"
+ if ${http_data}; then
+ echo -n "true"
+ fi
+ echo "$AUTH$PRETTY${certdir}/clixon-server-crt.pem${certdir}/clixon-server-key.pem${certdir}/clixon-ca-crt.pem$DBGdefault0.0.0.0443true"
fi
}
diff --git a/test/test_http_data.sh b/test/test_http_data.sh
index 717bc9e5..84612b13 100755
--- a/test/test_http_data.sh
+++ b/test/test_http_data.sh
@@ -1,5 +1,10 @@
#!/usr/bin/env bash
-# Simple web data
+# Simple http data test
+# Create an html and css file
+# Get them via http and https
+# Send options and head request
+# Errors: not found, post,
+# XXX: feature disabled
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@@ -7,9 +12,8 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
cfg=$dir/conf.xml
-wdir=$dir/www
-rm -rf $wdir
-mkdir $wdir
+rm -rf $dir/www
+mkdir $dir/www
# Does not work with fcgi
if [ "${WITH_RESTCONF}" = "fcgi" ]; then
@@ -17,40 +21,8 @@ if [ "${WITH_RESTCONF}" = "fcgi" ]; then
if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi
-RESTCONFIG=$(restconf_config none false)
-
-
-# Clixon config
-cat < $cfg
-
- $cfg
- clixon-restconf:allow-auth-none
- clixon-restconf:http-data
- ${YANG_INSTALLDIR}
- $IETFRFC
- $dir
- /usr/local/lib/$APPNAME/clispec
- /usr/local/lib/$APPNAME/backend
- example_backend.so$
- /usr/local/lib/$APPNAME/restconf
- /data
- $wdir
- /usr/local/lib/$APPNAME/cli
- $APPNAME
- /usr/local/var/$APPNAME/$APPNAME.sock
- /usr/local/var/$APPNAME/$APPNAME.pidfile
- /usr/local/var/$APPNAME
- true
- $RESTCONFIG
-
-EOF
-
-# Host setup:
-# /
-# /var/www/html
-
# Data file
-cat < $wdir/index.html
+cat < $dir/www/index.html
@@ -71,7 +43,7 @@ working. Further configuration is required.
EOF
-cat < $wdir/example.css
+cat < $dir/www/example.css
img {
display: inline;
border:
@@ -95,62 +67,138 @@ h1,h2,h3,h4,h5,h6 {
}
EOF
-
-new "test params: -f $cfg"
-if [ $BE -ne 0 ]; then
- new "kill old backend"
- sudo clixon_backend -zf $cfg
- if [ $? -ne 0 ]; then
- err
- fi
- sudo pkill -f clixon_backend # to be sure
+# Http test routine with arguments:
+# 1. proto:http/https
+function testrun()
+{
+ proto=$1 # http/https
+ enable=$2 # true/false
- new "start backend -s init -f $cfg"
- start_backend -s init -f $cfg
-fi
+ RESTCONFIG=$(restconf_config none false $proto $enable)
-new "wait backend"
-wait_backend
+ datapath=/data
+ wdir=$dir/www
+# Host setup:
+# datapath=/
+# wdir=/var/www/html
-if [ $RC -ne 0 ]; then
- new "kill old restconf daemon"
- stop_restconf_pre
+ # Clixon config
+ cat < $cfg
+
+ $cfg
+ clixon-restconf:allow-auth-none
+ clixon-restconf:http-data
+ ${YANG_INSTALLDIR}
+ $IETFRFC
+ $dir
+ /usr/local/lib/$APPNAME/clispec
+ /usr/local/lib/$APPNAME/backend
+ example_backend.so$
+ /usr/local/lib/$APPNAME/restconf
+ $datapath
+ $wdir
+ /usr/local/lib/$APPNAME/cli
+ $APPNAME
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ /usr/local/var/$APPNAME
+ true
+ $RESTCONFIG
+
+EOF
- new "start restconf daemon"
- start_restconf -f $cfg
-fi
-
-new "wait restconf"
-wait_restconf
-
-new "WWW get html"
-expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $RCPROTO://localhost/data/index.html)" 0 "HTTP/$HVER 200" "Content-Type: text/html" "Welcome to Clixon!"
-
-new "WWW get css"
-expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $RCPROTO://localhost/data/example.css)" 0 "HTTP/$HVER 200" "Content-Type: text/css" "display: inline;" --not-- "Content-Type: text/html"
-
-new "WWW head"
-expectpart "$(curl $CURLOPTS --head -H 'Accept: text/html' $RCPROTO://localhost/data/index.html)" 0 "HTTP/$HVER 200" "Content-Type: text/html" --not-- "Welcome to Clixon!"
-
-new "WWW options"
-expectpart "$(curl $CURLOPTS -X OPTIONS $RCPROTO://localhost/data/index.html)" 0 "HTTP/$HVER 200" "allow: OPTIONS,HEAD,GET"
-
-new "WWW get http not found"
-expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $RCPROTO://localhost/data/notfound.html)" 0 "HTTP/$HVER 404" "Content-Type: text/html" "404 Not Found"
-
-new "WWW post not allowed"
-expectpart "$(curl $CURLOPTS -X POST -H 'Accept: text/html' -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $RCPROTO://localhost/data/notfound.html)" 0 "HTTP/$HVER 405" "Content-Type: text/html" "405 Method Not Allowed"
-
-if [ $BE -ne 0 ]; then
- new "Kill backend"
- # Check if premature kill
- pid=$(pgrep -u root -f clixon_backend)
- if [ -z "$pid" ]; then
- err "backend already dead"
+ new "test params: -f $cfg"
+ if [ $BE -ne 0 ]; then
+ new "kill old backend"
+ sudo clixon_backend -zf $cfg
+ if [ $? -ne 0 ]; then
+ err
+ fi
+ sudo pkill -f clixon_backend # to be sure
+
+ new "start backend -s init -f $cfg"
+ start_backend -s init -f $cfg
fi
- # kill backend
- stop_backend -f $cfg
+
+ new "wait backend"
+ wait_backend
+
+ if [ $RC -ne 0 ]; then
+ new "kill old restconf daemon"
+ stop_restconf_pre
+
+ new "start restconf daemon"
+ start_restconf -f $cfg
+ fi
+
+ new "wait restconf"
+ wait_restconf $proto
+
+# echo "curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html"
+ if $enable; then
+ new "WWW get html"
+ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html)" 0 "HTTP/$HVER 200" "Content-Type: text/html" "Welcome to Clixon!"
+ else
+ new "WWW get html, not enabled, expect bad request"
+ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html)" 0 "HTTP/$HVER 400"
+ return
+ fi
+
+ new "WWW get css"
+ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/example.css)" 0 "HTTP/$HVER 200" "Content-Type: text/css" "display: inline;" --not-- "Content-Type: text/html"
+
+ new "WWW head"
+ expectpart "$(curl $CURLOPTS --head -H 'Accept: text/html' $proto://localhost/data/index.html)" 0 "HTTP/$HVER 200" "Content-Type: text/html" --not-- "Welcome to Clixon!"
+
+ new "WWW options"
+ expectpart "$(curl $CURLOPTS -X OPTIONS $proto://localhost/data/index.html)" 0 "HTTP/$HVER 200" "allow: OPTIONS,HEAD,GET"
+
+ # negative errors
+ new "WWW get http not found"
+ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/notfound.html)" 0 "HTTP/$HVER 404" "Content-Type: text/html" "404 Not Found"
+
+ new "WWW post not allowed"
+ expectpart "$(curl $CURLOPTS -X POST -H 'Accept: text/html' -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $proto://localhost/data/notfound.html)" 0 "HTTP/$HVER 405" "Content-Type: text/html" "405 Method Not Allowed"
+
+ if [ $RC -ne 0 ]; then
+ new "Kill restconf daemon"
+ stop_restconf
+ fi
+
+ if [ $BE -ne 0 ]; then
+ new "Kill backend"
+ # Check if premature kill
+ pid=$(pgrep -u root -f clixon_backend)
+ if [ -z "$pid" ]; then
+ err "backend already dead"
+ fi
+ # kill backend
+ stop_backend -f $cfg
+ fi
+}
+
+protos=
+# Go thru all combinations of IPv4/IPv6, http/https, local/backend config
+if [ "${WITH_RESTCONF}" = "fcgi" ]; then
+ protos="http"
+elif ${HAVE_HTTP1}; then
+ protos="http" # No plain http for http/2 only
fi
+if [ "${WITH_RESTCONF}" = "native" ]; then
+ # https only relevant for internal (for fcgi: need nginx config)
+ protos="$protos https"
+fi
+
+for proto in $protos; do
+ for enable in true false; do
+ new "http-data proto:$proto enabled:$enable"
+ testrun $proto $enable
+ done
+done
+
+# unset conditional parameters
+unset RCPROTO
+unset RESTCONFIG
rm -rf $dir
diff --git a/test/test_restconf.sh b/test/test_restconf.sh
index d550b07d..38b79478 100755
--- a/test/test_restconf.sh
+++ b/test/test_restconf.sh
@@ -534,6 +534,7 @@ function testrun()
fi
}
+protos=
# Go thru all combinations of IPv4/IPv6, http/https, local/backend config
if [ "${WITH_RESTCONF}" = "fcgi" ]; then
protos="http"
@@ -541,7 +542,7 @@ elif ${HAVE_HTTP1}; then
protos="http" # No plain http for http/2 only
fi
if [ "${WITH_RESTCONF}" = "native" ]; then
- # http only relevant for internal (for fcgi: need nginx config)
+ # https only relevant for internal (for fcgi: need nginx config)
protos="$protos https"
fi
for proto in $protos; do