- Added Restconf-style xml/json message bodies everywhere
- Added api_return_err0() and changed many calls to that instead of api_return_err - Added netconf_operation_not_supported_xml()
This commit is contained in:
parent
c50f9f8056
commit
ad08200931
17 changed files with 496 additions and 660 deletions
|
|
@ -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 <ctype.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
|
|
@ -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, "<error-tag>access-denied</error-tag>\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</h1>\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: <rpc-error>... 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 <xxx><rpc-error>...
|
||||
* 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 <rpc-error>
|
||||
*/
|
||||
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 <rpc-error>..");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, req, xe, pretty, media, code) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue