Fixed: [JSON backslash string decoding/encoding not correct](https://github.com/clicon/clixon/issues/453)
Added unicode BMP support for JSON strings Test: encoding/decoding tests for UTF-8
This commit is contained in:
parent
1a43a32770
commit
45f41e3e4d
12 changed files with 254 additions and 35 deletions
|
|
@ -73,6 +73,7 @@ Developers may need to change their code
|
||||||
|
|
||||||
### Minor features
|
### Minor features
|
||||||
|
|
||||||
|
* JSON: Added unicode BMP support for unicode strings as part of fixing (https://github.com/clicon/clixon/issues/453)
|
||||||
* Example cli pipe grep command quotes vertical bar for OR function
|
* Example cli pipe grep command quotes vertical bar for OR function
|
||||||
* Added: [Feature request: node's alias for CLI](https://github.com/clicon/clixon/issues/434)
|
* Added: [Feature request: node's alias for CLI](https://github.com/clicon/clixon/issues/434)
|
||||||
* Note: "Skip" is for all nodes, but "Alias" is only for leafs
|
* Note: "Skip" is for all nodes, but "Alias" is only for leafs
|
||||||
|
|
@ -85,7 +86,9 @@ Developers may need to change their code
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
|
||||||
|
* Fixed: [JSON backslash string decoding/encoding not correct](https://github.com/clicon/clixon/issues/453)
|
||||||
* Fixed: [CLI show config | display <format> exits over mountpoints with large YANGs](https://github.com/clicon/clixon-controller/issues/39)
|
* Fixed: [CLI show config | display <format> exits over mountpoints with large YANGs](https://github.com/clicon/clixon-controller/issues/39)
|
||||||
|
* JSON string fixed according to RFC 8259: encoding/decoding of escape as defined in Section 8
|
||||||
* No need to bind for xml and json, only cli and text
|
* No need to bind for xml and json, only cli and text
|
||||||
* Fixed several issues with extra-config files, including overwriting of structured sub-configs
|
* Fixed several issues with extra-config files, including overwriting of structured sub-configs
|
||||||
* including `<restconf>`and m̀ <autoconf>`
|
* including `<restconf>`and m̀ <autoconf>`
|
||||||
|
|
|
||||||
|
|
@ -297,6 +297,7 @@ pipe_showas_fn(clicon_handle h,
|
||||||
switch (format){
|
switch (format){
|
||||||
case FORMAT_CLI:
|
case FORMAT_CLI:
|
||||||
case FORMAT_TEXT:
|
case FORMAT_TEXT:
|
||||||
|
case FORMAT_JSON:
|
||||||
/* Requires binding. Note binding over mountpoints can cause rpc: extra latency */
|
/* Requires binding. Note binding over mountpoints can cause rpc: extra latency */
|
||||||
if ((ret = xml_bind_yang(h, xt, YB_MODULE, yspec, &xerr)) < 0)
|
if ((ret = xml_bind_yang(h, xt, YB_MODULE, yspec, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
* JSON support functions.
|
* JSON support functions.
|
||||||
* JSON syntax is according to:
|
* @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||||
* http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
* and RFC 7951 JSON Encoding of Data Modeled with YANG
|
||||||
* RFC 7951 JSON Encoding of Data Modeled with YANG
|
* and RFC 8259 The JavaScript Object Notation (JSON) Data Interchange Format
|
||||||
*/
|
*/
|
||||||
#ifndef _CLIXON_JSON_H
|
#ifndef _CLIXON_JSON_H
|
||||||
#define _CLIXON_JSON_H
|
#define _CLIXON_JSON_H
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ int nodeid_split(char *nodeid, char **prefix, char **id);
|
||||||
char *clixon_trim(char *str);
|
char *clixon_trim(char *str);
|
||||||
char *clixon_trim2(char *str, char *trims);
|
char *clixon_trim2(char *str, char *trims);
|
||||||
int clicon_strcmp(char *s1, char *s2);
|
int clicon_strcmp(char *s1, char *s2);
|
||||||
|
int clixon_unicode2utf8(char *ucstr, char *utfstr, size_t utflen);
|
||||||
|
|
||||||
#ifndef HAVE_STRNDUP
|
#ifndef HAVE_STRNDUP
|
||||||
char *clicon_strndup (const char *, size_t);
|
char *clicon_strndup (const char *, size_t);
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
* JSON support functions.
|
* JSON support functions.
|
||||||
* JSON syntax is according to:
|
* @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||||
* http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
* and RFC 7951 JSON Encoding of Data Modeled with YANG
|
||||||
* RFC 7951 JSON Encoding of Data Modeled with YANG
|
* and RFC 8259 The JavaScript Object Notation (JSON) Data Interchange Format
|
||||||
* XXX: The complexity of xml2json1_cbuf() mapping from internal cxobj structure to JSON output
|
* XXX: The complexity of xml2json1_cbuf() mapping from internal cxobj structure to JSON output
|
||||||
* needs a rewrite due to complexity of lists/leaf-lists/null-values, etc.
|
* needs a rewrite due to complexity of lists/leaf-lists/null-values, etc.
|
||||||
*/
|
*/
|
||||||
|
|
@ -107,6 +107,7 @@ enum childtype{
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! x is element and has exactly one child which in turn has none
|
/*! x is element and has exactly one child which in turn has none
|
||||||
|
*
|
||||||
* remove attributes from x
|
* remove attributes from x
|
||||||
* @see tleaf in clixon_xml_map.c
|
* @see tleaf in clixon_xml_map.c
|
||||||
*/
|
*/
|
||||||
|
|
@ -241,6 +242,7 @@ array_eval(cxobj *xprev,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Escape a json string as well as decode xml cdata
|
/*! Escape a json string as well as decode xml cdata
|
||||||
|
*
|
||||||
* @param[out] cb cbuf (encoded)
|
* @param[out] cb cbuf (encoded)
|
||||||
* @param[in] str string (unencoded)
|
* @param[in] str string (unencoded)
|
||||||
*/
|
*/
|
||||||
|
|
@ -255,15 +257,27 @@ json_str_escape_cdata(cbuf *cb,
|
||||||
len = strlen(str);
|
len = strlen(str);
|
||||||
for (i=0; i<len; i++)
|
for (i=0; i<len; i++)
|
||||||
switch (str[i]){
|
switch (str[i]){
|
||||||
case '\n':
|
|
||||||
cprintf(cb, "\\n");
|
|
||||||
break;
|
|
||||||
case '\"':
|
case '\"':
|
||||||
cprintf(cb, "\\\"");
|
cprintf(cb, "\\\"");
|
||||||
break;
|
break;
|
||||||
case '\\':
|
case '\\':
|
||||||
cprintf(cb, "\\\\");
|
cprintf(cb, "\\\\");
|
||||||
break;
|
break;
|
||||||
|
case '\b':
|
||||||
|
cprintf(cb, "\\b");
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
cprintf(cb, "\\f");
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
cprintf(cb, "\\n");
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
cprintf(cb, "\\r");
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
cprintf(cb, "\\t");
|
||||||
|
break;
|
||||||
default: /* fall thru */
|
default: /* fall thru */
|
||||||
cprintf(cb, "%c", str[i]);
|
cprintf(cb, "%c", str[i]);
|
||||||
break;
|
break;
|
||||||
|
|
@ -274,6 +288,7 @@ json_str_escape_cdata(cbuf *cb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Decode types from JSON to XML identityrefs
|
/*! Decode types from JSON to XML identityrefs
|
||||||
|
*
|
||||||
* Assume an xml tree where prefix:name have been split into "module":"name"
|
* Assume an xml tree where prefix:name have been split into "module":"name"
|
||||||
* In other words, from JSON RFC7951 to XML namespace trees
|
* In other words, from JSON RFC7951 to XML namespace trees
|
||||||
* @param[in] x XML tree. Must be yang populated.
|
* @param[in] x XML tree. Must be yang populated.
|
||||||
|
|
@ -433,6 +448,7 @@ json2xml_decode(cxobj *x,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Encode leaf/leaf_list identityref type from XML to JSON
|
/*! Encode leaf/leaf_list identityref type from XML to JSON
|
||||||
|
*
|
||||||
* @param[in] x XML body node
|
* @param[in] x XML body node
|
||||||
* @param[in] body body string
|
* @param[in] body body string
|
||||||
* @param[in] ys Yang spec of parent
|
* @param[in] ys Yang spec of parent
|
||||||
|
|
@ -497,6 +513,7 @@ xml2json_encode_identityref(cxobj *xb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Encode leaf/leaf_list types from XML to JSON
|
/*! Encode leaf/leaf_list types from XML to JSON
|
||||||
|
*
|
||||||
* @param[in] xb XML body
|
* @param[in] xb XML body
|
||||||
* @param[in] xp XML parent
|
* @param[in] xp XML parent
|
||||||
* @param[in] yp Yang spec of parent
|
* @param[in] yp Yang spec of parent
|
||||||
|
|
@ -663,6 +680,7 @@ nullchild(cbuf *cb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Encode json metadata
|
/*! Encode json metadata
|
||||||
|
*
|
||||||
* This function could be more general, code based on two examples:
|
* This function could be more general, code based on two examples:
|
||||||
* 1) ietf-list-pagination:remaining
|
* 1) ietf-list-pagination:remaining
|
||||||
* {
|
* {
|
||||||
|
|
@ -773,6 +791,7 @@ xml2json_encode_attr(cxobj *xa,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Do the actual work of translating XML to JSON
|
/*! Do the actual work of translating XML to JSON
|
||||||
|
*
|
||||||
* @param[out] cb Cligen text buffer containing json on exit
|
* @param[out] cb Cligen text buffer containing json on exit
|
||||||
* @param[in] x XML tree structure containing XML to translate
|
* @param[in] x XML tree structure containing XML to translate
|
||||||
* @param[in] arraytype Does x occur in a array (of its parent) and how?
|
* @param[in] arraytype Does x occur in a array (of its parent) and how?
|
||||||
|
|
@ -1142,6 +1161,7 @@ clixon_json2cbuf(cbuf *cb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Translate a vector of xml objects to JSON Cligen buffer.
|
/*! Translate a vector of xml objects to JSON Cligen buffer.
|
||||||
|
*
|
||||||
* This is done by adding a top pseudo-object, and add the vector as subs,
|
* This is done by adding a top pseudo-object, and add the vector as subs,
|
||||||
* and then not printing the top pseudo-object using the 'flat' option.
|
* and then not printing the top pseudo-object using the 'flat' option.
|
||||||
* @param[out] cb Cligen buffer to write to
|
* @param[out] cb Cligen buffer to write to
|
||||||
|
|
@ -1224,6 +1244,7 @@ xml2json_cbuf_vec(cbuf *cb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Translate from xml tree to JSON and print to file using a callback
|
/*! Translate from xml tree to JSON and print to file using a callback
|
||||||
|
*
|
||||||
* @param[in] f File to print to
|
* @param[in] f File to print to
|
||||||
* @param[in] xn XML tree to translate from
|
* @param[in] xn XML tree to translate from
|
||||||
* @param[in] pretty Set if output is pretty-printed
|
* @param[in] pretty Set if output is pretty-printed
|
||||||
|
|
@ -1280,6 +1301,7 @@ json_print(FILE *f,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Translate a vector of xml objects to JSON File.
|
/*! Translate a vector of xml objects to JSON File.
|
||||||
|
*
|
||||||
* This is done by adding a top pseudo-object, and add the vector as subs,
|
* This is done by adding a top pseudo-object, and add the vector as subs,
|
||||||
* and then not pritning the top pseudo-.object using the 'flat' option.
|
* and then not pritning the top pseudo-.object using the 'flat' option.
|
||||||
* @param[out] cb Cligen buffer to write to
|
* @param[out] cb Cligen buffer to write to
|
||||||
|
|
@ -1320,6 +1342,7 @@ xml2json_vec(FILE *f,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Translate from JSON module:name to XML default ns: xmlns="uri" recursively
|
/*! Translate from JSON module:name to XML default ns: xmlns="uri" recursively
|
||||||
|
*
|
||||||
* Assume an xml tree where prefix:name have been split into "module":"name"
|
* Assume an xml tree where prefix:name have been split into "module":"name"
|
||||||
* In other words, from JSON to XML namespace trees
|
* In other words, from JSON to XML namespace trees
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,9 @@
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
|
* @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||||
|
* and RFC 7951 JSON Encoding of Data Modeled with YANG
|
||||||
|
* and RFC 8259 The JavaScript Object Notation (JSON) Data Interchange Format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
|
@ -51,6 +54,7 @@
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_hash.h"
|
#include "clixon_hash.h"
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
|
#include "clixon_string.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
|
|
@ -78,10 +82,12 @@ digit [0-9]
|
||||||
integer {digit}+
|
integer {digit}+
|
||||||
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
|
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
|
||||||
exp ({integer}|{real})[eE][+-]{integer}
|
exp ({integer}|{real})[eE][+-]{integer}
|
||||||
|
hex [A-Fa-f0-9]
|
||||||
|
|
||||||
%x START
|
%x START
|
||||||
%s STRING
|
%s STRING
|
||||||
%s ESCAPE
|
%s ESCAPE
|
||||||
|
%s HEXDIG
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
|
@ -103,14 +109,31 @@ exp ({integer}|{real})[eE][+-]{integer}
|
||||||
<START>. { return -1; }
|
<START>. { return -1; }
|
||||||
<STRING>\" { BEGIN(START); return J_DQ; }
|
<STRING>\" { BEGIN(START); return J_DQ; }
|
||||||
<STRING>\\ { BEGIN(ESCAPE); }
|
<STRING>\\ { BEGIN(ESCAPE); }
|
||||||
<STRING>\n { _JY->jy_linenum++;
|
<STRING>[^\"\\\b\f\n\r\t]+ { BEGIN(STRING); clixon_json_parselval.string = yytext; return J_STRING; }
|
||||||
clixon_json_parselval.string = strdup(yytext);
|
<STRING>\n { return -1; }
|
||||||
return J_CHAR;}
|
<STRING>. { return -1; }
|
||||||
<STRING>. { clixon_json_parselval.string = strdup(yytext);
|
<ESCAPE>\" { BEGIN(STRING); clixon_json_parselval.string = yytext; return J_STRING; }
|
||||||
return J_CHAR;}
|
<ESCAPE>\\ { BEGIN(STRING); clixon_json_parselval.string = yytext; return J_STRING; }
|
||||||
<ESCAPE>. { BEGIN(STRING);
|
<ESCAPE>\/ { BEGIN(STRING); clixon_json_parselval.string = yytext; return J_STRING; }
|
||||||
clixon_json_parselval.string = strdup(yytext);
|
<ESCAPE>b { BEGIN(STRING); clixon_json_parselval.string = "\b"; return J_STRING; }
|
||||||
return J_CHAR; }
|
<ESCAPE>f { BEGIN(STRING); clixon_json_parselval.string = "\f"; return J_STRING; }
|
||||||
|
<ESCAPE>n { BEGIN(STRING); clixon_json_parselval.string = "\n"; return J_STRING; }
|
||||||
|
<ESCAPE>r { BEGIN(STRING); clixon_json_parselval.string = "\r"; return J_STRING; }
|
||||||
|
<ESCAPE>t { BEGIN(STRING); clixon_json_parselval.string = "\t"; return J_STRING; }
|
||||||
|
<ESCAPE>u { BEGIN(HEXDIG); }
|
||||||
|
<ESCAPE>\n { return -1; }
|
||||||
|
<ESCAPE>. { return -1; }
|
||||||
|
<HEXDIG>{hex}{hex}{hex}{hex} {
|
||||||
|
char buf[5] = {0, };
|
||||||
|
BEGIN(STRING);
|
||||||
|
if (clixon_unicode2utf8(yytext, buf, 5) < 0)
|
||||||
|
return -1;
|
||||||
|
strncpy(yytext, buf, 4);
|
||||||
|
clixon_json_parselval.string = yytext;
|
||||||
|
return J_STRING;
|
||||||
|
}
|
||||||
|
<HEXDIG>\n { return -1;}
|
||||||
|
<HEXDIG>. { return -1; }
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,9 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
* JSON Parser
|
* JSON Parser
|
||||||
* From http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
* @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||||
* And RFC7951 JSON Encoding of Data Modeled with YANG
|
* and RFC 7951 JSON Encoding of Data Modeled with YANG
|
||||||
|
* and RFC 8259 The JavaScript Object Notation (JSON) Data Interchange Format
|
||||||
|
|
||||||
Structural tokens:
|
Structural tokens:
|
||||||
[ left square bracket
|
[ left square bracket
|
||||||
|
|
@ -87,7 +88,7 @@ object.
|
||||||
%token <string> J_NULL
|
%token <string> J_NULL
|
||||||
%token <string> J_EOF
|
%token <string> J_EOF
|
||||||
%token <string> J_DQ
|
%token <string> J_DQ
|
||||||
%token <string> J_CHAR
|
%token <string> J_STRING
|
||||||
%token <string> J_NUMBER
|
%token <string> J_NUMBER
|
||||||
|
|
||||||
%type <cbuf> string
|
%type <cbuf> string
|
||||||
|
|
@ -151,7 +152,7 @@ void
|
||||||
clixon_json_parseerror(void *_jy,
|
clixon_json_parseerror(void *_jy,
|
||||||
char *s)
|
char *s)
|
||||||
{
|
{
|
||||||
clicon_err(OE_JSON, XMLPARSE_ERRNO, "json_parse: line %d: %s at or before: '%s'",
|
clicon_err(OE_JSON, 0, "json_parse: line %d: %s at or before: '%s'",
|
||||||
_JY->jy_linenum ,
|
_JY->jy_linenum ,
|
||||||
s,
|
s,
|
||||||
clixon_json_parsetext);
|
clixon_json_parsetext);
|
||||||
|
|
@ -312,12 +313,16 @@ string : J_DQ ustring J_DQ { _PARSE_DEBUG("string->\" ustring \"");$$=$2;
|
||||||
;
|
;
|
||||||
|
|
||||||
/* unquoted string: can be optimized by reading whole string in lex */
|
/* unquoted string: can be optimized by reading whole string in lex */
|
||||||
ustring : ustring J_CHAR
|
ustring : ustring J_STRING
|
||||||
{
|
{
|
||||||
cbuf_append_str($1,$2); $$=$1; free($2);
|
cbuf_append_str($1,$2); $$=$1;
|
||||||
|
}
|
||||||
|
| J_STRING
|
||||||
|
{
|
||||||
|
cbuf *cb = cbuf_new();
|
||||||
|
cbuf_append_str(cb,$1);
|
||||||
|
$$=cb;
|
||||||
}
|
}
|
||||||
| J_CHAR
|
|
||||||
{ cbuf *cb = cbuf_new(); cbuf_append_str(cb,$1); $$=cb; free($1);}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
number : J_NUMBER { $$ = $1; }
|
number : J_NUMBER { $$ = $1; }
|
||||||
|
|
|
||||||
|
|
@ -1051,6 +1051,105 @@ clicon_strcmp(char *s1,
|
||||||
return strcmp(s1, s2);
|
return strcmp(s1, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Translate from unicode in hex form to utf-8
|
||||||
|
*
|
||||||
|
* @param[in] uc16 Unicode as 2-byte hex int
|
||||||
|
* @param[out] utf8str UTF-8 string
|
||||||
|
* @param[out] utflen Length utf string
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
clixon_unicode2utf8_one(uint16_t uc16,
|
||||||
|
char *utfstr,
|
||||||
|
size_t utflen)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
|
if (utflen < 5){
|
||||||
|
clicon_err(OE_UNIX, EINVAL, "Length of utfstr is not >=4");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (uc16<0x80)
|
||||||
|
*utfstr++=uc16;
|
||||||
|
else if (uc16<0x800){
|
||||||
|
*utfstr++=192+uc16/64;
|
||||||
|
*utfstr++=128+uc16%64;
|
||||||
|
}
|
||||||
|
else if (uc16-0xd800u<0x800){
|
||||||
|
clicon_err(OE_UNIX, EINVAL, "unicode2utf error");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else if (uc16<0x10000) {
|
||||||
|
*utfstr++=224+uc16/4096;
|
||||||
|
*utfstr++=128+uc16/64%64;
|
||||||
|
*utfstr++=128+uc16%64;
|
||||||
|
}
|
||||||
|
else if (uc16<0x110000) {
|
||||||
|
*utfstr++=240+uc16/262144;
|
||||||
|
*utfstr++=128+uc16/4096%64;
|
||||||
|
*utfstr++=128+uc16/64%64;
|
||||||
|
*utfstr++=128+uc16%64;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
clicon_err(OE_UNIX, EINVAL, "unicode2utf error");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
*utfstr++=0;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Translate unicode hexstring on the form "ABCD" to UTF-8 in string form
|
||||||
|
*
|
||||||
|
* @param[in] unicode Unicode as string of 2-byte hex codes
|
||||||
|
* @param[out] utf8 UTF-8 character string must be length >=5
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clixon_unicode2utf8(char *ucstr,
|
||||||
|
char *utfstr,
|
||||||
|
size_t utflen)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
size_t len;
|
||||||
|
int i;
|
||||||
|
char c;
|
||||||
|
int j;
|
||||||
|
uint16_t uc16 = 0;
|
||||||
|
|
||||||
|
if (ucstr == NULL || utfstr == NULL){
|
||||||
|
clicon_err(OE_UNIX, EINVAL, "input param is NULL");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((len = strlen(ucstr)) != 4){
|
||||||
|
clicon_err(OE_UNIX, EINVAL, "Length of ucstr is not 4");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
for (i=0; i<len; i++){
|
||||||
|
c = ucstr[i]&0xFF;
|
||||||
|
if ('0' <= c && c <= '9')
|
||||||
|
j = c-'0';
|
||||||
|
else if ('A' <= c && c <= 'F')
|
||||||
|
j = c+10-'A';
|
||||||
|
else if ('a' <= c && c <= 'f')
|
||||||
|
j = c+10-'a';
|
||||||
|
else{
|
||||||
|
clicon_err(OE_UNIX, 0, "no match");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (uc16 != 0)
|
||||||
|
uc16 <<= 4;
|
||||||
|
uc16 |= j;
|
||||||
|
}
|
||||||
|
if (clixon_unicode2utf8_one(uc16, utfstr, utflen) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! strndup() for systems without it, such as xBSD
|
/*! strndup() for systems without it, such as xBSD
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
# Test: JSON parser tests. See RFC7951
|
# Test: JSON parser tests. See RFC7951
|
||||||
# - Multi-line + pretty-print
|
# - Multi-line + pretty-print
|
||||||
# - Empty values
|
# - Empty values
|
||||||
|
# - JSON string encode/decode
|
||||||
# Note that members should not be quoted. See test_restconf2.sh for typed
|
# Note that members should not be quoted. See test_restconf2.sh for typed
|
||||||
#PROG="valgrind --leak-check=full --show-leak-kinds=all ../util/clixon_util_json"
|
#PROG="valgrind --leak-check=full --show-leak-kinds=all ../util/clixon_util_json"
|
||||||
# Magic line must be first in script (see README.md)
|
# Magic line must be first in script (see README.md)
|
||||||
|
|
@ -138,6 +139,41 @@ JSON='{"json:c":{"s":"<![CDATA[ z > x & x < y ]]>"}}'
|
||||||
new "json parse cdata xml"
|
new "json parse cdata xml"
|
||||||
expecteofx "$clixon_util_json -j -y $fyang" 0 "$JSON" "$JSON"
|
expecteofx "$clixon_util_json -j -y $fyang" 0 "$JSON" "$JSON"
|
||||||
|
|
||||||
|
JSON='{"text":"ruled over the shores of the Hreiðsea"}'
|
||||||
|
new "json utf-8"
|
||||||
|
expecteofx "$clixon_util_json -j" 0 "$JSON" "$JSON"
|
||||||
|
|
||||||
|
new "json utf-8 xml out"
|
||||||
|
expecteofx "$clixon_util_json" 0 "$JSON" "<text>ruled over the shores of the Hreiðsea</text>"
|
||||||
|
|
||||||
|
# see https://github.com/clicon/clixon/issues/453
|
||||||
|
JSON="{\"text\":\"one
|
||||||
|
two\"}"
|
||||||
|
new "json not escaped \n expect fail"
|
||||||
|
expecteofx "$clixon_util_json -j" 255 "$JSON" 2> /dev/null
|
||||||
|
|
||||||
|
new "json not escaped \" expect fail"
|
||||||
|
expecteofx "$clixon_util_json -j" 255 "quote:\"" 2> /dev/null
|
||||||
|
|
||||||
|
JSON='{"text":"cr:\nquote:\"tab:\tend"}'
|
||||||
|
new "json escaping \n\"\t to json"
|
||||||
|
expecteofx "$clixon_util_json -j" 0 "$JSON" "$JSON"
|
||||||
|
|
||||||
|
new "json escaping \n\"\t to xml"
|
||||||
|
expecteofx "$clixon_util_json" 0 "$JSON" "quote:\"tab: end</text>"
|
||||||
|
|
||||||
|
JSON='{"text":"bmp:\u005E"}'
|
||||||
|
new "json escaping \u BMP circumflex"
|
||||||
|
#expecteofx "$clixon_util_json -j -D $DBG" 0 "$JSON" '{"text":"bmp:^"}'
|
||||||
|
|
||||||
|
JSON='{"text":"bmp:\u00F0"}'
|
||||||
|
new "json escaping \u BMP latin eth"
|
||||||
|
expecteofx "$clixon_util_json -j -D $DBG" 0 "$JSON" '{"text":"bmp:ð"}'
|
||||||
|
|
||||||
|
JSON='{"text":"bmp:\uFAIL"}'
|
||||||
|
new "json escaping unicode BMP fail"
|
||||||
|
expecteofx "$clixon_util_json -j -D $DBG" 255 "$JSON" 2> /dev/null
|
||||||
|
|
||||||
rm -rf $dir
|
rm -rf $dir
|
||||||
|
|
||||||
new "endtest"
|
new "endtest"
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,7 @@ function testrun()
|
||||||
new "start restconf daemon"
|
new "start restconf daemon"
|
||||||
# inline of start_restconf, cant make quotes to work
|
# inline of start_restconf, cant make quotes to work
|
||||||
echo "sudo -u $wwwstartuser -s $clixon_restconf $RCLOG -D $DBG -f $cfg -R $RESTCONFIG1"
|
echo "sudo -u $wwwstartuser -s $clixon_restconf $RCLOG -D $DBG -f $cfg -R $RESTCONFIG1"
|
||||||
|
STTYSETTINGS=$(stty -g) # reset in wait_restconf
|
||||||
sudo -u $wwwstartuser -s $clixon_restconf $RCLOG -D $DBG -f $cfg -R "$RESTCONFIG1" </dev/null &>/dev/null &
|
sudo -u $wwwstartuser -s $clixon_restconf $RCLOG -D $DBG -f $cfg -R "$RESTCONFIG1" </dev/null &>/dev/null &
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
err1 "expected 0" "$?"
|
err1 "expected 0" "$?"
|
||||||
|
|
@ -308,14 +309,13 @@ function testrun()
|
||||||
else
|
else
|
||||||
HVER=2
|
HVER=2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "wait restconf"
|
new "wait restconf"
|
||||||
wait_restconf
|
wait_restconf
|
||||||
|
|
||||||
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
||||||
echo "curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta"
|
|
||||||
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta)" 0 "HTTP/$HVER 200" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta)" 0 "HTTP/$HVER 200" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
||||||
|
|
||||||
echo "fcgi or native+http/1 or native+http/1+http/2"
|
|
||||||
new "restconf GET http/1.1"
|
new "restconf GET http/1.1"
|
||||||
expectpart "$(curl $CURLOPTS --http1.1 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
expectpart "$(curl $CURLOPTS --http1.1 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
||||||
|
|
||||||
|
|
@ -354,10 +354,8 @@ function testrun()
|
||||||
wait_restconf
|
wait_restconf
|
||||||
|
|
||||||
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
||||||
echo "curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta"
|
|
||||||
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta)" 0 "HTTP/$HVER 200" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta)" 0 "HTTP/$HVER 200" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
||||||
|
|
||||||
echo "native + http/2 only"
|
|
||||||
# Important here is robustness of restconf daemon, not a meaningful reply
|
# Important here is robustness of restconf daemon, not a meaningful reply
|
||||||
if [ $proto = http ]; then # see (2) https to http port in restconf_main_native.c
|
if [ $proto = http ]; then # see (2) https to http port in restconf_main_native.c
|
||||||
# http protocol mismatch can just close the socket if assumed its http/2
|
# http protocol mismatch can just close the socket if assumed its http/2
|
||||||
|
|
@ -405,10 +403,8 @@ function testrun()
|
||||||
wait_restconf
|
wait_restconf
|
||||||
|
|
||||||
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
||||||
echo "curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta"
|
|
||||||
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta)" 0 "HTTP/$HVER 200" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta)" 0 "HTTP/$HVER 200" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
||||||
|
|
||||||
echo "fcgi or native+http/1 or native+http/1+http/2"
|
|
||||||
if [ "${WITH_RESTCONF}" = "native" ]; then # XXX does not work with nginx
|
if [ "${WITH_RESTCONF}" = "native" ]; then # XXX does not work with nginx
|
||||||
new "restconf GET http/1.0 - returns 1.0"
|
new "restconf GET http/1.0 - returns 1.0"
|
||||||
expectpart "$(curl $CURLOPTS --http1.0 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.0 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
expectpart "$(curl $CURLOPTS --http1.0 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.0 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
|
||||||
|
|
@ -439,7 +435,6 @@ function testrun()
|
||||||
expectpart "$(curl $CURLOPTS -X GET http://$addr:443/.well-known/host-meta)" 0 "HTTP/" "400"
|
expectpart "$(curl $CURLOPTS -X GET http://$addr:443/.well-known/host-meta)" 0 "HTTP/" "400"
|
||||||
fi
|
fi
|
||||||
fi # HTTP/2
|
fi # HTTP/2
|
||||||
|
|
||||||
# Exact match
|
# Exact match
|
||||||
new "restconf get restconf resource. RFC 8040 3.3 (json)"
|
new "restconf get restconf resource. RFC 8040 3.3 (json)"
|
||||||
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $proto://$addr/restconf)" 0 "HTTP/$HVER 200" '{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2019-01-04"}}'
|
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $proto://$addr/restconf)" 0 "HTTP/$HVER 200" '{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2019-01-04"}}'
|
||||||
|
|
@ -653,6 +648,24 @@ function testrun()
|
||||||
new "restconf Add subtree with too many keys (expected error)"
|
new "restconf Add subtree with too many keys (expected error)"
|
||||||
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=a,b)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key interface length mismatch"}}}'
|
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=a,b)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key interface length mismatch"}}}'
|
||||||
|
|
||||||
|
cat <<EOF > $dir/input.json
|
||||||
|
{"clixon-example:parameter":{"name":"x","value":"foo
|
||||||
|
bar"}}
|
||||||
|
EOF
|
||||||
|
new "restconf JSON escape encoding with explicit \n expect fail"
|
||||||
|
expectpart "$(curl $CURLOPTS -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" --data-binary @$dir/input.json $proto://$addr/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 400" "malformed-message"
|
||||||
|
|
||||||
|
JSON='{"clixon-example:parameter":[{"name":"x","value":"foo\nbar"}]}'
|
||||||
|
|
||||||
|
new "restconf JSON escape encoding"
|
||||||
|
expectpart "$(curl $CURLOPTS -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" --data-binary "$JSON" $proto://$addr/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 201" "Location: $proto://$addr/restconf/data/clixon-example:table/parameter=x"
|
||||||
|
|
||||||
|
new "Check restconf JSON escape encoding"
|
||||||
|
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $proto://$addr/restconf/data/clixon-example:table/parameter=x)" 0 "HTTP/$HVER 200" '{"clixon-example:parameter":\[{"name":"x","value":"foo\\nbar"}\]}'
|
||||||
|
|
||||||
|
new "Check restconf JSON escape encoding in XML"
|
||||||
|
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $proto://$addr/restconf/data/clixon-example:table/parameter=x)" 0 "HTTP/$HVER 200" "<parameter xmlns=\"urn:example:clixon\"><name>x</name><value>foo" "bar</value></parameter>"
|
||||||
|
|
||||||
if [ $RC -ne 0 ]; then
|
if [ $RC -ne 0 ]; then
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
stop_restconf
|
stop_restconf
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,19 @@ EOF
|
||||||
)
|
)
|
||||||
expecteof "$clixon_util_xml -o" 0 "$XML" '^<bk:book xmlns:bk="urn:loc.gov:books" xmlns:isbn="urn:ISBN:0-395-36341-6"><bk:title>Cheaper by the Dozen</bk:title><isbn:number>1568491379</isbn:number></bk:book>$'
|
expecteof "$clixon_util_xml -o" 0 "$XML" '^<bk:book xmlns:bk="urn:loc.gov:books" xmlns:isbn="urn:ISBN:0-395-36341-6"><bk:title>Cheaper by the Dozen</bk:title><isbn:number>1568491379</isbn:number></bk:book>$'
|
||||||
|
|
||||||
|
XML=$(cat <<EOF
|
||||||
|
<?xml version="1.0" encoding='utf-8'?>
|
||||||
|
<text>
|
||||||
|
Theodoric the bold
|
||||||
|
chief of sea-warriors
|
||||||
|
ruled over the shores of the Hreiðsea
|
||||||
|
</text>
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
new "utf-8 string"
|
||||||
|
expecteof "$clixon_util_xml -o" 0 "$XML" "^ruled over the shores of the Hreiðsea$"
|
||||||
|
|
||||||
rm -rf $dir
|
rm -rf $dir
|
||||||
|
|
||||||
new "endtest"
|
new "endtest"
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,9 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
* JSON utility command
|
* JSON utility command
|
||||||
* http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
* @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||||
|
* and RFC 7951 JSON Encoding of Data Modeled with YANG
|
||||||
|
* and RFC 8259 The JavaScript Object Notation (JSON) Data Interchange Format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue