diff --git a/CHANGELOG.md b/CHANGELOG.md index b7438c64..851dba14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,10 @@ Expected: June 2021 Users may have to change how they access the system +* RESTCONF error replies have changed + * Added Restconf-style xml/json message bodies everywhere + * Clixon removed the message body from many errors in the 4.6 version since they used html encoding. + * However, the RFC Section 7.1 mandates to use RESTCONF-style message bodies. * RESTCONF in Clixon used empty key as "wildchar". But according to RFC 8040 it should mean the "empty string". * Example: `GET restconf/data/x:a=` * Previous meaning (wrong): Return all `a` elements. diff --git a/apps/restconf/restconf_err.c b/apps/restconf/restconf_err.c index e2841c3f..efe467f4 100644 --- a/apps/restconf/restconf_err.c +++ b/apps/restconf/restconf_err.c @@ -34,7 +34,10 @@ * * Return errors * @see RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content - */ + + + * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3 +*/ #ifdef HAVE_CONFIG_H #include "clixon_config.h" /* generated by config & autoconf */ @@ -50,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -65,122 +69,84 @@ #include "restconf_api.h" #include "restconf_err.h" -/* - * Constants - */ -/* In the fcgi implementations some errors had body, it would be cleaner to skip them - * None seem mandatory according to RFC 7231 - */ -#define SKIP_BODY - -/* - * NOTE, fcgi seems not enough with a status code (libevhtp is) but must also have a status - * header. - */ - -/*! HTTP error 400 - * @param[in] h Clicon handle - * @param[in] req Generic Www handle +/*! HTTP error 405 Not Allowed + * @param[in] req Generic http handle + * @param[in] allow Which methods are allowed + * @param[in] pretty Pretty-print of reply + * @param[in] media_out Restconf output media */ int -restconf_badrequest(clicon_handle h, - void *req) +restconf_method_notallowed(clicon_handle h, + void *req, + char *allow, + int pretty, + restconf_media media) { - int retval = -1; + int retval = -1; + cxobj *xerr = NULL; -#ifdef SKIP_BODY /* Remove the body - should it really be there? */ - if (restconf_reply_send(req, 400, NULL) < 0) + if (netconf_operation_not_supported_xml(&xerr, "protocol", "Method not allowed") < 0) goto done; - retval = 0; - done: -#else - char *path; - cbuf *cb = NULL; - - /* Create body */ - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - path = restconf_param_get("REQUEST_URI", r->envp); - if (restconf_reply_header(req, "Content-Type", "text/html") < 0) - goto done; - cprintf(cb, "The requested URL %s or data is in some way badly formed.\n", path); - if (restconf_reply_send(req, 400, cb) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); -#endif - return retval; -} - -/*! HTTP error 404 - * @param[in] h Clicon handle - * @param[in] req Generic Www handle - * XXX skip body? - */ -int -restconf_notfound(clicon_handle h, - void *req) -{ - int retval = -1; -#ifdef SKIP_BODY /* Remove the body - should it really be there? */ - if (restconf_reply_send(req, 404, NULL) < 0) - goto done; - retval = 0; - done: -#else - char *path; - cbuf *cb = NULL; - - /* Create body */ - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - path = restconf_param_get("REQUEST_URI", r->envp); - if (restconf_reply_header(req, "Content-Type", "text/html") < 0) - goto done; - cprintf(cb, "The requested URL %s was not found on this server.\n", path); - if (restconf_reply_send(req, 404, cb) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); -#endif - return retval; -} - -/*! HTTP error 405 - * @param[in] req Generic Www handle - * @param[in] allow Which methods are allowed - */ -int -restconf_method_notallowed(void *req, - char *allow) -{ - int retval = -1; - + /* Assume not-supported mapped to Not Allowed with allow header */ if (restconf_reply_header(req, "Allow", "%s", allow) < 0) goto done; - if (restconf_reply_send(req, 405, NULL) < 0) + if (api_return_err0(h, req, xerr, pretty, YANG_DATA_JSON, 0) < 0) goto done; retval = 0; done: + if (xerr) + xml_free(xerr); return retval; } -/*! HTTP error 409 Unsupporte dmedia - * @param[in] req Generic Www handle +/*! HTTP error 415 Unsupported media + * @param[in] req Generic http handle + * RFC8040, section 5.2: + * If the server does not support the requested input encoding for a request, then it MUST + * return an error response with a "415 Unsupported Media Type" status-line */ int -restconf_unsupported_media(void *req) +restconf_unsupported_media(clicon_handle h, + void *req, + int pretty, + restconf_media media) { - int retval = -1; + int retval = -1; + cxobj *xerr = NULL; + if (netconf_operation_not_supported_xml(&xerr, "protocol", "Unsupported Media Type") < 0) + goto done; + /* override with 415 netconf->restoconf translation which gives a 405 */ + if (api_return_err0(h, req, xerr, pretty, media, 415) < 0) + goto done; + retval = 0; + done: + if (xerr) + xml_free(xerr); + return retval; +} + +/*! HTTP error 406 Not acceptable + * + * @param[in] req Generic http handle + * RFC8040, section 5.2: + * If the server does not support any of the requested output encodings for a request, then it MUST + * return an error response with a "406 Not Acceptable" status-line. + */ +int +restconf_not_acceptable(clicon_handle h, + void *req, + int pretty, + restconf_media media) +{ + int retval = -1; + cxobj *xerr = NULL; + + if (netconf_operation_not_supported_xml(&xerr, "protocol", "Unacceptable output encoding") < 0) + goto done; + /* Override with 415 netconf->restoconf translation which gives a 405 */ + if (api_return_err0(h, req, xerr, pretty, media, 415) < 0) + goto done; if (restconf_reply_send(req, 415, NULL) < 0) goto done; retval = 0; @@ -189,198 +155,38 @@ restconf_unsupported_media(void *req) } /*! HTTP error 501 Not implemented - * @param[in] req Generic Www handle + * @param[in] req Generic http handle */ int -restconf_notimplemented(void *req) +restconf_notimplemented(clicon_handle h, + void *req, + int pretty, + restconf_media media) { int retval = -1; + cxobj *xerr = NULL; - if (restconf_reply_send(req, 501, NULL) < 0) + if (netconf_operation_not_supported_xml(&xerr, "protocol", "Not Implemented") < 0) + goto done; + /* Override with 501 Not Implemented netconf->restoconf translation which gives a 405 */ + if (api_return_err0(h, req, xerr, pretty, YANG_DATA_JSON, 501) < 0) goto done; retval = 0; done: + if (xerr) + xml_free(xerr); return retval; } -#ifdef NOTUSED -/*! HTTP error 401 - * @param[in] h Clicon handle - * @param[in] req Generic Www handle - */ -int -restconf_unauthorized(clicon_handle h, - void *req) - -{ - int retval = -1; - -#ifdef SKIP_BODY /* Remove the body - should it really be there? */ - if (restconf_reply_send(req, 400, NULL) < 0) - goto done; - retval = 0; - done: -#else - char *path; - cbuf *cb = NULL; - - /* Create body */ - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - path = restconf_param_get("REQUEST_URI", r->envp); - if (restconf_reply_header(req, "Content-Type", "text/html") < 0) - goto done; - cprintf(cb, "access-denied\n"); - cprintf(cb, "The requested URL %s was unauthorized.\n", path); - if (restconf_reply_send(req, 400, cb) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); -#endif - return retval; -} - -/*! HTTP error 403 - * @param[in] h Clicon handle - * @param[in] req Generic Www handle - */ -int -restconf_forbidden(clicon_handle h, - void *req) -{ - int retval = -1; -#ifdef SKIP_BODY /* Remove the body - should it really be there? */ - if (restconf_reply_send(req, 403, NULL) < 0) - goto done; - retval = 0; - done: -#else - char *path; - cbuf *cb = NULL; - - /* Create body */ - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - path = restconf_param_get("REQUEST_URI", r->envp); - if (restconf_reply_header(req, "Content-Type", "text/html") < 0) - goto done; - cprintf(cb, "The requested URL %s was forbidden.\n", path); - if (restconf_reply_send(req, 403, cb) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); -#endif - return retval; -} - -/*! HTTP error 406 Not acceptable - * @param[in] h Clicon handle - * @param[in] req Generic Www handle - */ -int -restconf_notacceptable(clicon_handle h, - void *req) -{ - int retval = -1; - -#ifdef SKIP_BODY /* Remove the body - should it really be there? */ - if (restconf_reply_send(req, 406, NULL) < 0) - goto done; - retval = 0; - done: -#else - char *path; - cbuf *cb = NULL; - - /* Create body */ - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - path = restconf_param_get("REQUEST_URI", r->envp); - if (restconf_reply_header(req, "Content-Type", "text/html") < 0) - goto done; - cprintf(cb, "The target resource does not have a current representation that would be acceptable to the user agent.\n", path); - if (restconf_reply_send(req, 406, cb) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); -#endif - return retval; -} - -/*! HTTP error 409 - * @param[in] req Generic Www handle - */ -int -restconf_conflict(void *req) - -{ - int retval = -1; - - if (restconf_reply_send(req, 409, NULL) < 0) - goto done; - retval = 0; - done: - return retval; -} - -/*! HTTP error 500 Internal server error - * @param[in] h Clicon handle - * @param[in] req Generic Www handle - */ -int -restconf_internal_server_error(clicon_handle h, - void *req) -{ - int retval = -1; -#ifdef SKIP_BODY /* Remove the body - should it really be there? */ - if (restconf_reply_send(req, 500, NULL) < 0) - goto done; - retval = 0; - done: -#else - char *path; - cbuf *cb = NULL; - - /* Create body */ - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - path = restconf_param_get("REQUEST_URI", r->envp); - if (restconf_reply_header(req, "Content-Type", "text/html") < 0) - goto done; - cprintf(cb, "Internal server error when accessing %s\n", path); - if (restconf_reply_send(req, 500, cb) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); -#endif - return retval; -} -#endif /* NOTUSED */ - /*! Generic restconf error function on get/head request * @param[in] h Clixon handle - * @param[in] req Generic Www handle - * @param[in] xerr XML error message from backend + * @param[in] req Generic http handle + * @param[in] xerr XML error message (eg from backend, or from a clixon_netconf_lib function) * @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] media Output media * @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping * otherwise use this code + * xerr should be on the form: ... otherwise an internal error is generated */ int api_return_err(clicon_handle h, @@ -419,7 +225,11 @@ api_return_err(clicon_handle h, if (netconf_operation_failed_xml(&xerr2, "application", cbuf_get(cberr)) < 0) goto done; - if ((xerr = xpath_first(xerr2, NULL, "//rpc-error")) == NULL){ + if ((xerr = xpath_first(xerr2, NULL, "rpc-error")) == NULL){ + clicon_err(OE_XML, 0, "Internal error, shouldnt happen"); + goto done; + } + if ((xtag = xpath_first(xerr, NULL, "error-tag")) == NULL){ clicon_err(OE_XML, 0, "Internal error, shouldnt happen"); goto done; } @@ -490,6 +300,7 @@ api_return_err(clicon_handle h, goto done; break; } /* switch media */ + assert(cbuf_len(cb)); if (restconf_reply_send(req, code, cb) < 0) goto done; // ok: @@ -502,3 +313,38 @@ api_return_err(clicon_handle h, cbuf_free(cberr); return retval; } + +/*! Generic restconf error function on get/head request + * + * Variant of api_return_err on the form ... + * This is the form most functions in clixon_netconf_lib returns errors. + * @param[in] h Clixon handle + * @param[in] req Generic http handle + * @param[in] xerr XML error message (eg from backend, or from a clixon_netconf_lib function) + * @param[in] pretty Set to 1 for pretty-printed xml/json output + * @param[in] media Output media + * @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping + * otherwise use this code + * @see api_return_err where top level is expected to be + */ +int +api_return_err0(clicon_handle h, + void *req, + cxobj *xerr, + int pretty, + restconf_media media, + int code) +{ + int retval = -1; + cxobj *xe; + + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + clicon_err(OE_XML, EINVAL, "Expected xml on the form .."); + goto done; + } + if (api_return_err(h, req, xe, pretty, media, code) < 0) + goto done; + retval = 0; + done: + return retval; +} diff --git a/apps/restconf/restconf_err.h b/apps/restconf/restconf_err.h index a78e53b7..063fe61e 100644 --- a/apps/restconf/restconf_err.h +++ b/apps/restconf/restconf_err.h @@ -43,20 +43,13 @@ * Prototypes */ -int restconf_badrequest(clicon_handle h, void *req); -int restconf_notfound(clicon_handle h, void *req); -int restconf_method_notallowed(void *req, char *allow); -int restconf_unsupported_media(void *req); -int restconf_notimplemented(void *req); -#ifdef NOTUSED -int restconf_unauthorized(clicon_handle h, void *req); -int restconf_forbidden(clicon_handle h, void *req); -int restconf_notacceptable(clicon_handle h, void *req); -int restconf_conflict(void *req); -int restconf_internal_server_error(clicon_handle h, void *req); -#endif /* NOTUSED */ +int restconf_method_notallowed(clicon_handle h, void *req, char *allow, int pretty, restconf_media media); +int restconf_unsupported_media(clicon_handle h, void *req, int pretty, restconf_media media); +int restconf_not_acceptable(clicon_handle h, void *req, int pretty, restconf_media media); +int restconf_notimplemented(clicon_handle h, void *req, int pretty, restconf_media media); int api_return_err(clicon_handle h, void *req, cxobj *xerr, int pretty, restconf_media media, int code0); +int api_return_err0(clicon_handle h, void *req, cxobj *xerr, int pretty, restconf_media media, int code0); #endif /* _RESTCONF_ERR_H_ */ diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 46abe635..14358a2e 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -69,7 +69,57 @@ #include "restconf_err.h" #include "restconf_handle.h" -/* See RFC 8040 Section 7: Mapping from NETCONF to Status Code +/* + +----------------------------+--------------------------------------+ + | 100 Continue | POST accepted, 201 should follow | + | 200 OK | Success with response message-body | + | 201 Created | POST to create a resource success | + | 204 No Content | Success without response message- | + | | body | + | 304 Not Modified | Conditional operation not done | + | 400 Bad Request | Invalid request message | + | 401 Unauthorized | Client cannot be authenticated | + | 403 Forbidden | Access to resource denied | + | 404 Not Found | Resource target or resource node not | + | | found | + | 405 Method Not Allowed | Method not allowed for target | + | | resource | + | 409 Conflict | Resource or lock in use | + | 412 Precondition Failed | Conditional method is false | + | 413 Request Entity Too | too-big error | + | Large | | + | 414 Request-URI Too Large | too-big error | + | 415 Unsupported Media Type | non RESTCONF media type | + | 500 Internal Server Error | operation-failed | + | 501 Not Implemented | unknown-operation | + | 503 Service Unavailable | Recoverable server error | + +----------------------------+--------------------------------------+ +Mapping netconf error-tag -> status code + +-------------------------+-------------+ + | | status code | + +-------------------------+-------------+ + | in-use | 409 | + | invalid-value | 400 | + | too-big | 413 | + | missing-attribute | 400 | + | bad-attribute | 400 | + | unknown-attribute | 400 | + | bad-element | 400 | + | unknown-element | 400 | + | unknown-namespace | 400 | + | access-denied | 403 | + | lock-denied | 409 | + | resource-denied | 409 | + | rollback-failed | 500 | + | data-exists | 409 | + | data-missing | 409 | + | operation-not-supported | 405 or 501 | + | operation-failed | 500 | + | partial-operation | 500 | + | malformed-message | 400 | + +-------------------------+-------------+ + + * See RFC 8040 Section 7: Mapping from NETCONF to Status Code * and RFC 6241 Appendix A. NETCONF Error list */ static const map_str2int netconf_restconf_map[] = { @@ -505,11 +555,13 @@ restconf_drop_privileges(clicon_handle h, } /*! - * @param[in] h Clicon handle - * @param[in] req Generic Www handle (can be part of clixon handle) - * @retval -1 Error - * @retval 0 Not authenticated - * @retval 1 Authenticated + * @param[in] h Clicon handle + * @param[in] req Generic Www handle (can be part of clixon handle) + * @param[in] pretty Pretty-print + * @param[in] media_out Restconf output media + * @retval -1 Error + * @retval 0 Not authenticated + * @retval 1 Authenticated */ int restconf_authentication_cb(clicon_handle h, diff --git a/apps/restconf/restconf_main_fcgi.c b/apps/restconf/restconf_main_fcgi.c index d1f49e0c..f10c55e5 100644 --- a/apps/restconf/restconf_main_fcgi.c +++ b/apps/restconf/restconf_main_fcgi.c @@ -581,7 +581,14 @@ main(int argc, } else{ clicon_debug(1, "top-level %s not found", path); - restconf_notfound(h, req); + if (netconf_invalid_value_xml(&xerr, "protocol", "Top-level path not found") < 0) + goto done; + if (api_return_err0(h, req, xerr, 1, YANG_DATA_JSON, 0) < 0) + goto done; + if (xerr){ + xml_free(xerr); + xerr = NULL; + } } } else diff --git a/apps/restconf/restconf_main_native.c b/apps/restconf/restconf_main_native.c index 52dd9edf..8d7dd46e 100644 --- a/apps/restconf/restconf_main_native.c +++ b/apps/restconf/restconf_main_native.c @@ -485,6 +485,8 @@ evhtp_params_set(clicon_handle h, char *subject = NULL; cvec *cvv = NULL; char *cn; + cxobj *xerr = NULL; + int pretty; if ((uri = req->uri) == NULL){ clicon_err(OE_DAEMON, EFAULT, "No uri"); @@ -510,10 +512,14 @@ evhtp_params_set(clicon_handle h, if (restconf_param_set(h, "REQUEST_URI", path->full) < 0) goto done; clicon_debug(1, "%s proto:%d", __FUNCTION__, req->proto); + pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY"); /* XXX: Any two http numbers seem accepted by evhtp, like 1.99, 99.3 as http/1.1*/ if (req->proto != EVHTP_PROTO_10 && req->proto != EVHTP_PROTO_11){ - if (restconf_badrequest(h, req) < 0) + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid HTTP version number") < 0) + goto done; + /* Select json as default since content-type header may not be accessible yet */ + if (api_return_err0(h, req, xerr, pretty, YANG_DATA_JSON, 0) < 0) goto done; goto fail; } @@ -542,6 +548,8 @@ evhtp_params_set(clicon_handle h, clicon_debug(1, "%s %d", __FUNCTION__, retval); if (subject) free(subject); + if (xerr) + xml_free(xerr); if (cvv) cvec_free(cvv); return retval; @@ -878,11 +886,12 @@ close_ssl_evhtp_socket(int s, return retval; } -/*! Send early bad request reply before actual packet received, just after accept +/*! Send early handcoded bad request reply before actual packet received, just after accept * @param[in] h Clixon handle * @param[in] s Socket * @param[in] ssl If set, it will be freed * @param[in] body If given add message body using media + * @see restconf_badrequest which can only be called in a request context */ static int send_badrequest(clicon_handle h, @@ -941,7 +950,8 @@ restconf_connection(int s, char buf[BUFSIZ]; /* from stdio.h, typically 8K */ clicon_handle h; int readmore = 1; - restconf_conn_h *rc; + restconf_conn_h *rc; + cbuf *cberr = NULL; clicon_debug(1, "%s", __FUNCTION__); if ((conn = (evhtp_connection_t*)arg) == NULL){ @@ -984,10 +994,12 @@ restconf_connection(int s, * signature: */ if (connection_parse_nobev(buf, n, conn) < 0){ - /* One error is: (2) https to http port*/ clicon_debug(1, "%s connection_parse error", __FUNCTION__); + /* XXX To get more nuanced evhtp error check + * htparser_get_error(conn->parser) + */ if (send_badrequest(h, s, conn->ssl, "application/yang-data+xml", - "protocolmalformed-messageError from evhtp") < 0) + "protocolmalformed-messageThe requested URL or a header is in some way badly formed") < 0) goto done; SSL_free(conn->ssl); if (close(s) < 0){ @@ -1036,6 +1048,8 @@ restconf_connection(int s, retval = 0; done: clicon_debug(1, "%s retval %d", __FUNCTION__, retval); + if (cberr) + cbuf_free(cberr); return retval; } diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index ed14a95e..adea6960 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -45,56 +45,6 @@ * This is the interface: * api/data/profile=/metric= PUT data:enable= * api/test - +----------------------------+--------------------------------------+ - | 100 Continue | POST accepted, 201 should follow | - | 200 OK | Success with response message-body | - | 201 Created | POST to create a resource success | - | 204 No Content | Success without response message- | - | | body | - | 304 Not Modified | Conditional operation not done | - | 400 Bad Request | Invalid request message | - | 401 Unauthorized | Client cannot be authenticated | - | 403 Forbidden | Access to resource denied | - | 404 Not Found | Resource target or resource node not | - | | found | - | 405 Method Not Allowed | Method not allowed for target | - | | resource | - | 409 Conflict | Resource or lock in use | - | 412 Precondition Failed | Conditional method is false | - | 413 Request Entity Too | too-big error | - | Large | | - | 414 Request-URI Too Large | too-big error | - | 415 Unsupported Media Type | non RESTCONF media type | - | 500 Internal Server Error | operation-failed | - | 501 Not Implemented | unknown-operation | - | 503 Service Unavailable | Recoverable server error | - +----------------------------+--------------------------------------+ -Mapping netconf error-tag -> status code - +-------------------------+-------------+ - | | status code | - +-------------------------+-------------+ - | in-use | 409 | - | invalid-value | 400 | - | too-big | 413 | - | missing-attribute | 400 | - | bad-attribute | 400 | - | unknown-attribute | 400 | - | bad-element | 400 | - | unknown-element | 400 | - | unknown-namespace | 400 | - | access-denied | 403 | - | lock-denied | 409 | - | resource-denied | 409 | - | rollback-failed | 500 | - | data-exists | 409 | - | data-missing | 409 | - | operation-not-supported | 501 | - | operation-failed | 500 | - | partial-operation | 500 | - | malformed-message | 400 | - +-------------------------+-------------+ - - * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3 */ #ifdef HAVE_CONFIG_H @@ -231,6 +181,9 @@ match_list_keys(yang_stmt *y, * PATCH: If it does not, fail, otherwise replace/merge * @param[in] plain_patch fail if object does not exists AND merge (not replace) * @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource + * @param[in] pretty Pretty-print + * @param[in] media_in Restconf input media + * @param[in] media_out Restconf output media */ static int api_data_write(clicon_handle h, @@ -291,11 +244,7 @@ api_data_write(clicon_handle h, if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -313,11 +262,7 @@ api_data_write(clicon_handle h, if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -330,11 +275,7 @@ api_data_write(clicon_handle h, if (data == NULL || strlen(data) == 0){ if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -377,20 +318,12 @@ api_data_write(clicon_handle h, if ((ret = clixon_xml_parse_string(data, yb, yspec, &xdata0, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } if (ret == 0){ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -399,26 +332,18 @@ api_data_write(clicon_handle h, if ((ret = clixon_json_parse_string(data, yb, yspec, &xdata0, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } if (ret == 0){ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } break; default: - restconf_unsupported_media(req); + restconf_unsupported_media(h, req, pretty, media_out); goto ok; break; } /* switch media_in */ @@ -429,11 +354,7 @@ api_data_write(clicon_handle h, if (xml_child_nr_type(xdata0, CX_ELMNT) != 1){ if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -448,11 +369,7 @@ api_data_write(clicon_handle h, if (ymoddata != ymodapi){ if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -488,11 +405,7 @@ api_data_write(clicon_handle h, if (netconf_bad_element_xml(&xerr, "application", dname, "Data element does not match top-level data") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -521,11 +434,7 @@ api_data_write(clicon_handle h, if (netconf_bad_element_xml(&xerr, "application", dname, "Data element does not match api-path") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -546,11 +455,7 @@ api_data_write(clicon_handle h, if (match_list_keys(ybot, xdata, xbot) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -570,11 +475,7 @@ api_data_write(clicon_handle h, if (parbod == NULL || strcmp(parbod, xml_body(xdata))){ if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -766,10 +667,10 @@ api_data_patch(clicon_handle h, break; case YANG_PATCH_XML: case YANG_PATCH_JSON: /* RFC 8072 patch */ - ret = restconf_notimplemented(req); + ret = restconf_notimplemented(h, req, pretty, media_out); break; default: - ret = restconf_unsupported_media(req); + ret = restconf_unsupported_media(h, req, pretty, media_out); break; } return ret; @@ -829,11 +730,7 @@ api_data_delete(clicon_handle h, if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index dc21ff8d..4c6a254b 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -144,11 +144,7 @@ api_data_get2(clicon_handle h, (ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -161,11 +157,7 @@ api_data_get2(clicon_handle h, if (netconf_bad_attribute_xml(&xerr, "application", "content", "Unrecognized value of content attribute") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -183,11 +175,7 @@ api_data_get2(clicon_handle h, if (netconf_bad_attribute_xml(&xerr, "application", "depth", "Unrecognized value of depth attribute") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -209,11 +197,7 @@ api_data_get2(clicon_handle h, if (ret < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -261,11 +245,7 @@ api_data_get2(clicon_handle h, if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){ if (netconf_operation_failed_xml(&xerr, "application", clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -278,10 +258,8 @@ api_data_get2(clicon_handle h, if (netconf_invalid_value_xml(&xerr, "application", "Instance does not exist") < 0) goto done; /* override invalid-value default 400 with 404 */ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) != NULL){ - if (api_return_err(h, req, xe, pretty, media_out, 404) < 0) - goto done; - } + if (api_return_err0(h, req, xerr, pretty, media_out, 404) < 0) + goto done; goto ok; } switch (media_out){ diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c index cbac6e80..248e0d11 100644 --- a/apps/restconf/restconf_methods_post.c +++ b/apps/restconf/restconf_methods_post.c @@ -197,11 +197,7 @@ api_data_post(clicon_handle h, if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -212,11 +208,7 @@ api_data_post(clicon_handle h, if (data == NULL || strlen(data) == 0){ if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -242,20 +234,12 @@ api_data_post(clicon_handle h, if ((ret = clixon_xml_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } if (ret == 0){ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -264,26 +248,18 @@ api_data_post(clicon_handle h, if ((ret = clixon_json_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } if (ret == 0){ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } break; default: - restconf_unsupported_media(req); + restconf_unsupported_media(h, req, pretty, media_out); goto ok; break; } /* switch media_in */ @@ -295,11 +271,7 @@ api_data_post(clicon_handle h, if (xml_child_nr_type(xbot, CX_ELMNT) - nrchildren0 != 1){ if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -331,27 +303,18 @@ api_data_post(clicon_handle h, if (ymod != ymoddata){ if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) - goto done; - goto ok; - + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } /* If URI points out an object, then data's parent should be that object */ if (ybot && yang_parent_get(ydata) != ybot){ if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) - goto done; - goto ok; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } } /* If restconf insert/point attributes are present, translate to netconf */ @@ -456,7 +419,6 @@ api_operations_post_input(clicon_handle h, int retval = -1; cxobj *xdata = NULL; cxobj *xerr = NULL; /* malloced must be freed */ - cxobj *xe; cxobj *xinput; cxobj *x; cbuf *cbret = NULL; @@ -477,20 +439,12 @@ api_operations_post_input(clicon_handle h, if ((ret = clixon_xml_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto fail; } if (ret == 0){ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto fail; } @@ -501,26 +455,18 @@ api_operations_post_input(clicon_handle h, if ((ret = clixon_json_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto fail; } if (ret == 0){ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto fail; } break; default: - restconf_unsupported_media(req); + restconf_unsupported_media(h, req, pretty, media_out); goto fail; break; } /* switch media_in */ @@ -544,11 +490,7 @@ api_operations_post_input(clicon_handle h, else if (netconf_malformed_message_xml(&xerr, "restconf RPC has malformed input statement (multiple or not called input)") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto fail; } @@ -605,7 +547,6 @@ api_operations_post_output(clicon_handle h, int retval = -1; cxobj *xoutput = NULL; cxobj *xerr = NULL; /* assumed malloced, will be freed */ - cxobj *xe; /* just pointer */ cxobj *xa; /* xml attribute (xmlns) */ cxobj *x; cxobj *xok; @@ -623,11 +564,7 @@ api_operations_post_output(clicon_handle h, ){ if (netconf_malformed_message_xml(&xerr, "restconf RPC does not have single input") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto fail; } @@ -660,11 +597,7 @@ api_operations_post_output(clicon_handle h, (ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, NULL, "rpc-reply/rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto fail; } @@ -786,11 +719,7 @@ api_operations_post(clicon_handle h, if (oppath == NULL || strcmp(oppath,"/")==0){ if (netconf_operation_failed_xml(&xerr, "protocol", "Operation name expected") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -805,22 +734,14 @@ api_operations_post(clicon_handle h, if ((ys = yang_find(yspec, Y_MODULE, prefix)) == NULL){ if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } if ((yrpc = yang_find(ys, Y_RPC, id)) == NULL){ if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -842,11 +763,7 @@ api_operations_post(clicon_handle h, if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } @@ -872,26 +789,17 @@ api_operations_post(clicon_handle h, clicon_log_xml(LOG_DEBUG, xtop, "%s 5. Translate input args:", __FUNCTION__); #endif /* 6. Validate outgoing RPC and fill in defaults */ - if ((ret = xml_bind_yang_rpc(xtop, yspec, &xret)) < 0) /* */ + if ((ret = xml_bind_yang_rpc(xtop, yspec, &xerr)) < 0) /* */ goto done; if (ret == 0){ - if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto ok; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } - if ((ret = xml_yang_validate_rpc(h, xtop, &xret)) < 0) + if ((ret = xml_yang_validate_rpc(h, xtop, &xerr)) < 0) goto done; - if (ret == 0){ - if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto ok; - } - if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) goto done; goto ok; } diff --git a/apps/restconf/restconf_root.c b/apps/restconf/restconf_root.c index 6f67752a..9238372a 100644 --- a/apps/restconf/restconf_root.c +++ b/apps/restconf/restconf_root.c @@ -84,6 +84,7 @@ api_well_known(clicon_handle h, int retval = -1; char *request_method; cbuf *cb = NULL; + int pretty; clicon_debug(1, "%s", __FUNCTION__); if (req == NULL){ @@ -92,7 +93,9 @@ api_well_known(clicon_handle h, } request_method = restconf_param_get(h, "REQUEST_METHOD"); if (strcmp(request_method, "GET") != 0){ - restconf_method_notallowed(req, "GET"); + pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY"); + if (restconf_method_notallowed(h, req, "GET", pretty, YANG_DATA_JSON) < 0) + goto done; goto ok; } if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0) @@ -119,8 +122,12 @@ api_well_known(clicon_handle h, } /*! Retrieve the Top-Level API Resource /restconf/ (exact) - * @param[in] h Clicon handle - * @param[in] r Fastcgi request handle + * @param[in] h Clicon handle + * @param[in] req Generic request handle + * @param[in] method Http method + * @param[in] pretty Pretty print + * @param[in] media_out Restconf output media + * @note Only returns null for operations and data,... * See RFC8040 3.3 * @see api_root_restconf for accessing /restconf/ * @@ -140,7 +147,8 @@ api_root_restconf_exact(clicon_handle h, clicon_debug(1, "%s", __FUNCTION__); if (strcmp(request_method, "GET") != 0){ - restconf_method_notallowed(req, "GET"); + if (restconf_method_notallowed(h, req, "GET", pretty, media_out) < 0) + goto done; goto ok; } if ((yspec = clicon_dbspec_yang(h)) == NULL){ @@ -190,6 +198,10 @@ api_root_restconf_exact(clicon_handle h, /** A stub implementation of the operational state datastore. The full * implementation is required by https://tools.ietf.org/html/rfc8527#section-3.1 + * @param[in] h Clicon handle + * @param[in] req Generic http handle + * @param[in] pretty Pretty-print + * @param[in] media_out Restconf output media */ static int api_operational_state(clicon_handle h, @@ -203,11 +215,13 @@ api_operational_state(clicon_handle h, /* We are not implementing this method at this time, 20201105 despite it * being mandatory https://tools.ietf.org/html/rfc8527#section-3.1 */ - return restconf_notimplemented(req); + return restconf_notimplemented(h, req, pretty, media_out); } /*! * See https://tools.ietf.org/html/rfc7895 + * @param[in] pretty Pretty-print + * @param[in] media_out Restconf output media */ static int api_yang_library_version(clicon_handle h, @@ -259,15 +273,14 @@ api_yang_library_version(clicon_handle h, } /*! Generic REST method, GET, PUT, DELETE, etc - * @param[in] h CLIXON handle - * @param[in] r Fastcgi request handle - * @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft]) - * @param[in] pcvec Vector of path ie DOCUMENT_URI element - * @param[in] pi Offset, where to start pcvec - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] pretty Set to 1 for pretty-printed xml/json output - * @param[in] media_in Input media - * @param[in] media_out Output media + * @param[in] h CLIXON handle + * @param[in] r Fastcgi request handle + * @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft]) + * @param[in] pcvec Vector of path ie DOCUMENT_URI element + * @param[in] pi Offset, where to start pcvec + * @param[in] qvec Vector of query string (QUERY_STRING) + * @param[in] pretty Set to 1 for pretty-printed xml/json output + * @param[in] media_out Restconf output media * @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource */ static int @@ -285,6 +298,7 @@ api_data(clicon_handle h, int retval = -1; int read_only = 0, dynamic = 0; char *request_method; + cxobj *xerr = NULL; clicon_debug(1, "%s", __FUNCTION__); request_method = restconf_param_get(h, "REQUEST_METHOD"); @@ -302,10 +316,10 @@ api_data(clicon_handle h, if (strcmp(request_method, "OPTIONS")==0) retval = api_data_options(h, req); else if (strcmp(request_method, "HEAD")==0) { - if (dynamic) { - retval = restconf_method_notallowed(req, "GET,POST"); - } - retval = api_data_head(h, req, api_path, pcvec, pi, qvec, pretty, media_out, ds); + if (dynamic) + retval = restconf_method_notallowed(h, req, "GET,POST", pretty, media_out); + else + retval = api_data_head(h, req, api_path, pcvec, pi, qvec, pretty, media_out, ds); } else if (strcmp(request_method, "GET")==0) { retval = api_data_get(h, req, api_path, pcvec, pi, qvec, pretty, media_out, ds); @@ -314,26 +328,32 @@ api_data(clicon_handle h, retval = api_data_post(h, req, api_path, pi, qvec, data, pretty, media_out, ds); } else if (strcmp(request_method, "PUT")==0) { - if (read_only) { - retval = restconf_method_notallowed(req, "GET,POST"); - } - retval = api_data_put(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out, ds); + if (read_only) + retval = restconf_method_notallowed(h, req, "GET,POST", pretty, media_out); + else + retval = api_data_put(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out, ds); } else if (strcmp(request_method, "PATCH")==0) { if (read_only) { - retval = restconf_method_notallowed(req, "GET,POST"); + retval = restconf_method_notallowed(h, req, "GET,POST", pretty, media_out); } retval = api_data_patch(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out, ds); } else if (strcmp(request_method, "DELETE")==0) { - if (read_only) { - retval = restconf_method_notallowed(req, "GET,POST"); - } - retval = api_data_delete(h, req, api_path, pi, pretty, media_out, ds); + if (read_only) + retval = restconf_method_notallowed(h, req, "GET,POST", pretty, media_out); + else + retval = api_data_delete(h, req, api_path, pi, pretty, media_out, ds); + } + else{ + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid HTTP data method") < 0) + goto done; + retval = api_return_err0(h, req, xerr, pretty, media_out, 0); } - else - retval = restconf_notfound(h, req); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + done: + if (xerr) + xml_free(xerr); return retval; } @@ -360,7 +380,8 @@ api_operations(clicon_handle h, int pretty, restconf_media media_out) { - int retval; + int retval; + cxobj *xerr = NULL; clicon_debug(1, "%s", __FUNCTION__); if (strcmp(request_method, "GET")==0) @@ -368,8 +389,14 @@ api_operations(clicon_handle h, else if (strcmp(request_method, "POST")==0) retval = api_operations_post(h, req, path, pi, qvec, data, pretty, media_out); - else - retval = restconf_notfound(h, req); + else{ + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid HTTP operations method") < 0) + goto done; + retval = api_return_err0(h, req, xerr, pretty, media_out, 0); + } + done: + if (xerr) + xml_free(xerr); return retval; } @@ -398,6 +425,7 @@ api_root_restconf(clicon_handle h, char *indata = NULL; char *username = NULL; int ret; + cxobj *xerr = NULL; clicon_debug(1, "%s", __FUNCTION__); if (req == NULL){ @@ -414,15 +442,19 @@ api_root_restconf(clicon_handle h, * If accept is * default is yang-json */ if ((media_str = restconf_param_get(h, "HTTP_ACCEPT")) == NULL){ - // retval = restconf_unsupported_media(r); - // goto done; +#if 0 /* Use default +json */ + if (restconf_not_acceptable(h, r, pretty, media_out) < 0) + goto done; + goto ok; +#endif } - else if ((int)(media_out = restconf_media_str2int(media_str)) == -1){ + else if ((int)(media_out = restconf_media_str2int(media_str)) == -1){ if (strcmp(media_str, "*/*") == 0) /* catch-all */ media_out = YANG_DATA_JSON; else{ - retval = restconf_unsupported_media(req); - goto done; + if (restconf_not_acceptable(h, req, pretty, YANG_DATA_JSON) < 0) + goto done; + goto ok; } } @@ -432,24 +464,36 @@ api_root_restconf(clicon_handle h, goto done; /* Sanity check of path. Should be /restconf/ */ if (pn < 2){ - restconf_notfound(h, req); + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; goto ok; } if (strlen(pvec[0]) != 0){ - retval = restconf_notfound(h, req); - goto done; + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } if (strcmp(pvec[1], RESTCONF_API)){ - retval = restconf_notfound(h, req); - goto done; + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } if (pn == 2){ retval = api_root_restconf_exact(h, req, request_method, pretty, media_out); goto done; } if ((api_resource = pvec[2]) == NULL){ - retval = restconf_notfound(h, req); - goto done; + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } clicon_debug(1, "%s: api_resource=%s", __FUNCTION__, api_resource); if (uri_str2cvec(path, '/', '=', 1, &pcvec) < 0) /* rest url eg /album=ricky/foo */ @@ -487,8 +531,11 @@ api_root_restconf(clicon_handle h, ietf_ds_t ds = IETF_DS_NONE; if (4 > pn) { /* Malformed request, no "ietf-datastores:" component */ - restconf_notfound(h, req); - goto done; + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, No ietf-datastores: component") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } /* Assign ds; See https://tools.ietf.org/html/rfc8342#section-7 */ @@ -507,8 +554,11 @@ api_root_restconf(clicon_handle h, goto ok; } else { /* Malformed request, unsupported datastore type */ - restconf_notfound(h, req); - goto done; + if (netconf_invalid_value_xml(&xerr, "protocol", "Unsupported datastore type") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } /* ds is assigned at this point */ if (0 > api_data(h, req, path, pcvec, 3, qvec, indata, pretty, media_out, ds)) @@ -519,12 +569,20 @@ api_root_restconf(clicon_handle h, pretty, media_out) < 0) goto done; } - else - restconf_notfound(h, req); + else{ + if (netconf_invalid_value_xml(&xerr, "protocol", "API-resource type") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; + } + ok: retval = 0; done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (xerr) + xml_free(xerr); if (username) free(username); if (pcvec) diff --git a/apps/restconf/restconf_stream_fcgi.c b/apps/restconf/restconf_stream_fcgi.c index 96461f22..a00f2b65 100644 --- a/apps/restconf/restconf_stream_fcgi.c +++ b/apps/restconf/restconf_stream_fcgi.c @@ -240,6 +240,9 @@ restconf_stream_cb(int s, * @param[in] h Clicon handle * @param[in] req Generic Www handle (can be part of clixon handle) * @param[in] name Stream name + * @param[in] qvec + * @param[in] pretty Pretty-print json/xml reply + * @param[in] media_out Restconf output media * @param[out] sp Socket -1 if not set */ static int @@ -387,6 +390,7 @@ api_stream(clicon_handle h, cbuf *cbret = NULL; int s = -1; int ret; + cxobj *xerr = NULL; #ifdef STREAM_FORK int pid; struct stream_child *sc; @@ -400,21 +404,32 @@ api_stream(clicon_handle h, goto done; /* Sanity check of path. Should be /stream/ */ if (pn != 3){ - restconf_notfound(h, req); + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/ expected") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; goto ok; } if (strlen(pvec[0]) != 0){ - retval = restconf_notfound(h, req); - goto done; + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/ expected") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } if (strcmp(pvec[1], streampath)){ - retval = restconf_notfound(h, req); - goto done; + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/ expected") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } - if ((method = pvec[2]) == NULL){ - retval = restconf_notfound(h, req); - goto done; + if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/ expected") < 0) + goto done; + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; } clicon_debug(1, "%s: method=%s", __FUNCTION__, method); @@ -496,6 +511,8 @@ api_stream(clicon_handle h, retval = 0; done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (xerr) + xml_free(xerr); if (pvec) free(pvec); if (pcvec) diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h index 64b5937a..deac778a 100644 --- a/lib/clixon/clixon_netconf_lib.h +++ b/lib/clixon/clixon_netconf_lib.h @@ -116,12 +116,14 @@ int netconf_rollback_failed(cbuf *cb, char *type, char *message); int netconf_data_exists(cbuf *cb, char *message); int netconf_data_missing(cbuf *cb, char *missing_choice, char *message); int netconf_data_missing_xml(cxobj **xret, char *missing_choice, char *message); +int netconf_operation_not_supported_xml(cxobj **xret, char *type, char *message); int netconf_operation_not_supported(cbuf *cb, char *type, char *message); int netconf_operation_failed(cbuf *cb, char *type, char *message); int netconf_operation_failed_xml(cxobj **xret, char *type, char *message); int netconf_malformed_message(cbuf *cb, char *message); int netconf_malformed_message_xml(cxobj **xret, char *message); int netconf_data_not_unique_xml(cxobj **xret, cxobj *x, cvec *cvk); + int netconf_minmax_elements_xml(cxobj **xret, cxobj *xp, char *name, int max); int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret); int netconf_module_features(clicon_handle h); diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index b93adf93..d9be082a 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -941,6 +941,62 @@ netconf_data_missing_xml(cxobj **xret, return retval; } +/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A + * + * Request could not be completed because the requested operation is not + * supported by this implementation. + * @param[out] xret Error XML tree + * @param[in] type Error type: "application" or "protocol" + * @param[in] message Error message + * @code + * cxobj *xret = NULL; + * if (netconf_operation_not_supported_xml(&xret, "protocol", "Unauthorized") < 0) + * err; + * xml_free(xret); + * @endcode + * @see netconf_operation_not_supported Same but returns cligen buffer + */ +int +netconf_operation_not_supported_xml(cxobj **xret, + char *type, + char *message) +{ + int retval =-1; + cxobj *xerr; + char *encstr = NULL; + cxobj *xa; + + if (*xret == NULL){ + if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL) + goto done; + } + else if (xml_name_set(*xret, "rpc-reply") < 0) + goto done; + if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) + goto done; + if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0) + goto done; + if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) + goto done; + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s" + "operation-not-supported" + "error", + type) < 0) + goto done; + if (message){ + if (xml_chardata_encode(&encstr, "%s", message) < 0) + goto done; + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s", + encstr) < 0) + goto done; + } + retval = 0; + done: + if (encstr) + free(encstr); + return retval; +} + /*! Create Netconf operation-not-supported error XML according to RFC 6241 App A * * Request could not be completed because the requested operation is not @@ -955,30 +1011,17 @@ netconf_operation_not_supported(cbuf *cb, char *message) { int retval = -1; - char *encstr = NULL; + cxobj *xret = NULL; - if (cprintf(cb, "" - "%s" - "operation-not-supported" - "error", - NETCONF_BASE_NAMESPACE, type) <0) - goto err; - if (message){ - if (xml_chardata_encode(&encstr, "%s", message) < 0) - goto done; - if (cprintf(cb, "%s", encstr) < 0) - goto err; - } - if (cprintf(cb, "") <0) - goto err; + if (netconf_operation_not_supported_xml(&xret, type, message) < 0) + goto done; + if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0) + goto done; retval = 0; done: - if (encstr) - free(encstr); + if (xret) + xml_free(xret); return retval; - err: - clicon_err(OE_XML, errno, "cprintf"); - goto done; } /*! Create Netconf operation-failed error XML tree according to RFC 6241 App A @@ -1028,7 +1071,6 @@ int netconf_operation_failed_xml(cxobj **xret, char *type, char *message) - { int retval =-1; cxobj *xerr; diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 38575a89..45bc6eb4 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -2968,6 +2968,8 @@ yang_desc_schema_nodeid(yang_stmt *yn, /* If p0 is NULL an entry will be: [i0] which needs to be transformed to [NULL:i0] */ cv = NULL; while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){ + if (cv_type_get(cv) != CGV_STRING) + cv_type_set(cv, CGV_STRING); if ((str = cv_string_get(cv)) == NULL || !strlen(str)){ if (cv_string_set(cv, cv_name_get(cv)) < 0){ clicon_err(OE_UNIX, errno, "cv_string_set"); diff --git a/test/test_restconf_err.sh b/test/test_restconf_err.sh index 27e6a2e9..dbe6019b 100755 --- a/test/test_restconf_err.sh +++ b/test/test_restconf_err.sh @@ -172,7 +172,7 @@ if [ $BE -ne 0 ]; then start_backend -s init -f $cfg -- -sS $fstate -v /table/parameter[name="4242"] fi -new "waiting" +new "wait backend" wait_backend if [ $RC -ne 0 ]; then @@ -181,16 +181,15 @@ if [ $RC -ne 0 ]; then new "start restconf daemon" start_restconf -f $cfg - - new "waiting" - wait_restconf fi +new "wait restconf" +wait_restconf + new "restconf POST initial tree" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 201 Created' new "restconf GET initial datastore" -#echo "curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 'HTTP/1.1 200 OK' "$XML" # XXX cannot get this to work for all combinations of nc/netcat fcgi/native @@ -233,7 +232,18 @@ Accept: application/yang-data+xml EOF )" 0 "HTTP/1.1 405" "Not Allowed" # nginx uses "method not allowed" evhtp "not allowed" -fi # Cannot get to work + + # XXX error from evhtp parsing, should pick up error code + new "restconf GET wrong http version raw" + expectpart "$(${netcat} 127.0.0.1 80 <malformed-messageThe requested URL or a header is in some way badly formed' + +fi # netcat Cannot get to work on all platforms new "restconf XYZ not found" expectpart "$(curl $CURLOPTS -X XYS -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 'HTTP/1.1 404 Not Found' @@ -241,7 +251,7 @@ expectpart "$(curl $CURLOPTS -X XYS -H 'Accept: application/yang-data+xml' $RCPR # XXX dont work w fcgi if [ "${WITH_RESTCONF}" = "native" ]; then new "restconf HEAD not allowed" - expectpart "$(curl $CURLOPTS -X HEAD -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 405" "Not Allowed" + expectpart "$(curl $CURLOPTS -X HEAD -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 405" "Not Allowed" "Allow: GET" fi new "restconf GET non-qualified list" diff --git a/test/test_restconf_op.sh b/test/test_restconf_op.sh index be981c36..f01a0ecc 100755 --- a/test/test_restconf_op.sh +++ b/test/test_restconf_op.sh @@ -114,6 +114,9 @@ expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:c new "restconf GET interface subtree" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0)" 0 "HTTP/1.1 200 OK" '{"example:interface":\[{"name":"local0","type":"regular"}\]}' +new "restconf PUT interface without media" +expectpart "$(curl $CURLOPTS -X PUT $RCPROTO://localhost/restconf/data/example:cont1/interface=local0 -d '{"example:interface":{"name":"local0","type":"foo"}}')" 0 "HTTP/1.1 415 Unsupported Media Type" + new "restconf GET interface subtree xml" ret=$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0) expect='local0regular' diff --git a/test/test_restconf_ssl_certs.sh b/test/test_restconf_ssl_certs.sh index a907f036..f1ae0eba 100755 --- a/test/test_restconf_ssl_certs.sh +++ b/test/test_restconf_ssl_certs.sh @@ -266,6 +266,9 @@ EOF new "admin set x 42" expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:x":42}' $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 204 No Content" + new "admin set x 42 without media" + expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X PUT -d '{"example:x":42}' $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 415 Unsupported Media Type" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-not-supported","error-severity":"error","error-message":"Unsupported Media Type"}}}' + new "admin get x 42" expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X GET $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 200 OK" '{"example:x":42}'