17 std::map<std::string, RESTHTTPCall*>::iterator i =
httpCalls.begin(), e =
httpCalls.end();
30 mainNode=XMLNode::parseString(restDefinition,
"restapi", &xmlResults);
50 std::map<std::string, RESTParam> paramMap;
53 uint32 i, count =
mainNode.nChildNode(
"paramset");
54 for (i = 0; i < count; i++) {
55 if (!(node =
mainNode.getChildNode(
"paramset", i)).isEmpty()) {
56 if (!(name = node.
getAttribute(
"name")) || !strlen(name))
66 count =
mainNode.nChildNode(
"httpcall");
67 for (i = 0; i < count; i++) {
68 if (!(node =
mainNode.getChildNode(
"httpcall", i)).isEmpty()) {
69 if (!(name = node.
getAttribute(
"name")) || !strlen(name))
72 if (!httpCall->
init(node)) {
86 std::map<std::string, RESTParam> paramMap;
87 const char* optional, *type, *name, *objects, *allowEmpty;
90 uint32 i, count = objectNode.
nChildNode(
"param");
91 for (i = 0; i < count; i++) {
92 if (!(node = objectNode.
getChildNode(
"param", i)).isEmpty()) {
99 if (!type || !name || !strlen(name)) {
117 restParam.
optional =
false | (optional && strcmp(optional,
"yes") == 0);
118 restParam.
allowEmpty =
false | (allowEmpty && strcmp(allowEmpty,
"yes") == 0);
119 if (objects && strlen(objects)) restParam.
objects = objects;
else restParam.
objects.clear();
120 restParam.
name = name;
121 paramMap[restParam.
name] = restParam;
129 std::map<std::string, RESTParam> paramSet;
130 std::string signature;
139 if (!(auth = req->
getHeaderEntry(
"Authorization")) || !strlen(auth)) {
144 restReq->
setString(
"Authentication", auth);
147 const char* httpCallName = node.
getAttribute(
"httpcall");
148 if (httpCallName && strlen(httpCallName)) {
149 std::map<std::string, RESTHTTPCall*>::iterator iHC =
httpCalls.find(httpCallName);
211 std::map<std::string, RESTParam> paramSet;
212 std::string signature;
219 const char* httpCallName = node.
getAttribute(
"httpcall");
220 if (httpCallName && strlen(httpCallName)) {
221 std::map<std::string, RESTHTTPCall*>::iterator iHC =
httpCalls.find(httpCallName);
227 int64 httpOperation = 0;
228 msg->
getInt(
"HTTP_OPERATION", httpOperation);
232 if (!(auth = msg->
getString(
"Authorization")) || !strlen(auth)) {
237 restReq->
setString(
"Authentication", auth);
300 XMLNode emptyNode = XMLNode::emptyNode();
308 if (!requestString || !strlen(requestString)) {
314 std::string filterString;
315 std::string requestPart;
316 const char* filterDiv = strstr(requestString,
"?");
318 filterString = filterDiv+1;
322 requestPart = requestString;
324 std::vector<std::string> reqParts =
utils::TextListSplit(requestPart.c_str(),
"/",
false,
true);
333 if (filterString.length()) {
335 std::multimap<std::string, std::string>::iterator ip = paramMap.begin(), ep = paramMap.end();
343 const char* tag, *type, *name, *subName, *set, *auth, *subapi;
344 XMLNode node = topNode, subNode;
346 int subNodeCount = 0, i;
350 while (level < reqParts.size()) {
352 if (!(name = node.
getAttribute(
"name")) || !strlen(name)) {
373 if (!(tag = node.
getName()) || !strlen(tag)) {
379 if (strcmp(tag,
"id") == 0) {
381 if (strcmp(type,
"integer") == 0) {
385 else if (strcmp(type,
"text") == 0) {
386 restReq->
setString(name, reqParts.at(level).c_str());
391 else if (strcmp(tag,
"api") == 0) {
397 if ((subapi = node.
getAttribute(
"subapi")) && strlen(subapi)) {
399 std::string newRequest;
400 std::string oldSignature = signature;
401 for (uint32 n=level+1; n<reqParts.size(); n++) {
403 newRequest = reqParts.at(n);
411 signature = oldSignature;
416 if (level == reqParts.size() - 1)
421 if (!(type = subNode.getAttribute(
"type"))) {
426 else if (strcmp(type,
"integer") == 0) {
437 else if (strcmp(type,
"text") == 0) {
439 if (!reqParts.at(level+1).length()) {
460 else if ( (subNodeCount = node.
nChildNode(
"api")) > 0) {
462 for (i = 0; i < subNodeCount; i++) {
463 if (!(subNode=node.
getChildNode(
"api", i)).isEmpty() && (subName = subNode.getAttribute(
"name")) && (
stricmp(reqParts.at(level+1).c_str(), subName) == 0)) {
469 if (i == subNodeCount) {
504 const char* val, *data, *json = NULL, *xml = NULL;
521 if (!(json = req->
getPostData(
"POST", size)) || !size) {
526 if ( (tokenCount =
jsmn_parse(&parser, json, size, tokens, 256)) <= 0) {
532 else if ((json = req->
getPostData(
"JSON", size)) && size) {
534 if ( (tokenCount =
jsmn_parse(&parser, json, size, tokens, 256)) <= 0) {
541 if (!(xml = req->
getPostData(
"POST", size)) || !size) {
545 paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
552 else if ((xml = req->
getPostData(
"XML", size)) && size) {
553 paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
560 else if ((val = req->
getPostData(
"POST", size)) && size) {
563 if ( (tokenCount =
jsmn_parse(&parser, val, size, tokens, 256)) > 0) {
569 paramNode=XMLNode::parseString(val, NULL, &xmlResults);
581 uint32 errorCount = 0;
583 std::map<std::string, RESTParam>::iterator i = paramSet.begin(), e = paramSet.end();
588 if (!i->second.optional) {
595 else if (!valSize && !i->second.allowEmpty) {
602 value = std::string(val, valSize);
613 if (!(data = req->
getPostData(i->second.name.c_str(), size)) || !size) {
626 restReq->
setBinary(i->second.name.c_str(), data, size, val);
631 if (!(val = req->
getPostData(i->second.name.c_str(), size)) || !size) {
653 std::map<std::string, RESTParam> arrayParamSet =
paramSets[i->second.objects];
654 if (!arrayParamSet.size()) {
662 std::vector<int>::iterator ia = jsonArrayIdx.begin(), ea = jsonArrayIdx.end();
664 value = std::string(json+tokens[*ia].start, tokens[*ia].end - tokens[*ia].start);
685 else if (value.length()) {
691 return (errorCount == 0);
703 const char* postType = msg->
getString(
"POST_CONTENT_TYPE_");
704 const char* val, *data, *json = NULL, *xml = NULL;
715 if (!(json = msg->
getString(
"POST", size)) || !size) {
720 if ( (tokenCount =
jsmn_parse(&parser, json, size, tokens, 256)) <= 0) {
726 else if ((json = msg->
getString(
"JSON", size)) && size) {
728 if ( (tokenCount =
jsmn_parse(&parser, json, size, tokens, 256)) <= 0) {
735 if (!(xml = msg->
getString(
"POST", size)) || !size) {
739 paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
746 else if ((xml = msg->
getString(
"XML", size)) && size) {
747 paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
754 else if ((val = msg->
getString(
"POST", size)) && size) {
757 if ( (tokenCount =
jsmn_parse(&parser, val, size, tokens, 256)) > 0) {
763 paramNode=XMLNode::parseString(val, NULL, &xmlResults);
775 uint32 errorCount = 0;
777 std::map<std::string, RESTParam>::iterator i = paramSet.begin(), e = paramSet.end();
782 if (!i->second.optional) {
789 else if (!valSize && !i->second.allowEmpty) {
796 value = std::string(val, valSize);
807 if (!(data = msg->
getData(i->second.name.c_str(), size)) || !size) {
814 if (!(val = msg->
getString((i->second.name +
"_CONTENT_TYPE_").c_str(), size2)) || !size2) {
820 restReq->
setBinary(i->second.name.c_str(), data, size, val);
825 if (!(val = msg->
getString(i->second.name.c_str(), size)) || !size) {
848 std::map<std::string, RESTParam> arrayParamSet =
paramSets[i->second.objects];
849 if (!arrayParamSet.size()) {
857 std::vector<int>::iterator ia = jsonArrayIdx.begin(), ea = jsonArrayIdx.end();
859 value = std::string(json+tokens[*ia].start, tokens[*ia].end - tokens[*ia].start);
880 else if (value.length()) {
890 return (errorCount == 0);
905 const char* val, *data, *json = NULL, *xml = NULL;
915 if (!(json = content) || !contentSize) {
920 if ( (tokenCount =
jsmn_parse(&parser, json, contentSize, tokens, 256)) <= 0) {
926 if (!(xml = content) || !contentSize) {
930 paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
937 std::string paddedParamName;
939 std::map<std::string, RESTParam>::iterator i = paramSet.begin(), e = paramSet.end();
948 if (!i->second.optional) {
955 if (!valSize && !i->second.allowEmpty) {
959 value = std::string(val, valSize);
963 if (!data || !size) {
964 restReq->
addToErrorString(
utils::StringFormat(
"Array parameter extraction error: Parameter %s (from %s) not found in form", value.c_str(), paddedParamName.c_str()).c_str());
972 restReq->
setBinary(paddedParamName.c_str(), data, size, val);
987 if (!(data = req->
getPostData(paddedParamName.c_str(), size)) || !size) {
988 if (!i->second.optional) {
1002 restReq->
setBinary(paddedParamName.c_str(), data, size, val);
1019 else if (value.length()) {
1036 const char* val, *data, *json = NULL, *xml = NULL;
1046 if (!(json = content) || !contentSize) {
1051 if ( (tokenCount =
jsmn_parse(&parser, json, contentSize, tokens, 256)) <= 0) {
1057 if (!(xml = content) || !contentSize) {
1061 paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
1068 std::string paddedParamName;
1070 std::map<std::string, RESTParam>::iterator i = paramSet.begin(), e = paramSet.end();
1079 if (!i->second.optional) {
1086 if (!valSize && !i->second.allowEmpty) {
1090 value = std::string(val, valSize);
1093 data = msg->
getData(value.c_str(), size);
1094 if (!data || !size) {
1095 restReq->
addToErrorString(
utils::StringFormat(
"Array parameter extraction error: Parameter %s (from %s) not found in form", value.c_str(), paddedParamName.c_str()).c_str());
1099 if (!(val = msg->
getString((value +
"_CONTENT_TYPE_").c_str(), size2)) || !size2) {
1103 restReq->
setBinary(paddedParamName.c_str(), data, size, val);
1119 if (!(data = msg->
getData(paddedParamName.c_str(), size)) || !size) {
1120 if (!i->second.optional) {
1130 if (!(val = msg->
getString((paddedParamName +
"_CONTENT_TYPE_").c_str(), size2)) || !size2) {
1134 restReq->
setBinary(paddedParamName.c_str(), data, size, val);
1139 if (!(val = msg->
getString(paddedParamName.c_str(), size)) || !size) {
1140 if (!i->second.optional) {
1160 else if (value.length()) {
1206 const char* data, *val;
1209 std::string paddedParamName;
1213 paddedParamName = param.
name;
1215 switch(param.
type) {
1231 restReq->
setString(paddedParamName.c_str(), value.c_str());
1237 if (!(data = req->
getPostData(value.c_str(), size))) {
1242 restReq->
addToErrorString(
utils::StringFormat(
"Parameter error: cannot find post type for entry called %s for parameter %s", value.c_str(), paddedParamName.c_str()).c_str());
1245 restReq->
setBinary(paddedParamName.c_str(), data, size, val);
1267 const char* data, *val;
1270 std::string paddedParamName;
1274 paddedParamName = param.
name;
1276 switch(param.
type) {
1292 restReq->
setString(paddedParamName.c_str(), value.c_str());
1298 if (!(data = msg->
getData(value.c_str(), size))) {
1303 if (!(val = msg->
getString((value +
"_CONTENT_TYPE_").c_str(), size2)) || !size2) {
1304 restReq->
addToErrorString(
utils::StringFormat(
"Parameter error: cannot find post type for entry called %s for parameter %s", value.c_str(), paddedParamName.c_str()).c_str());
1307 restReq->
setBinary(paddedParamName.c_str(), data, size, val);
1342 uint32 i, count =
mainNode.nChildNode(
"api");
1343 for(i=0; i<count; i++) {
1344 if (!(node =
mainNode.getChildNode(
"api", i)).isEmpty()) {
1346 if (strcmp(topNodeString, name) == 0)
1350 return XMLNode::emptyXMLNode;
1373 std::map<std::string, std::string>::iterator i = filterMapTemp.begin(), e = filterMapTemp.end();
1394 msg->
setInt(
"BUILTIN_REQUIRE_AUTHENTICATION", 1);
1395 std::string filterString;
1396 std::multimap<std::string, std::string>::iterator i =
filterMap.begin(), e =
filterMap.end();
1398 if (filterString.length())
1404 msg->
setString(
"BUILTIN_FILTERMAP", filterString.c_str());
1413 return httpCall->generateHTTPRequest(
this);
1430 const char* restDesc =
1432 " <api name=\"users\" ops=\"get,post\">"
1433 " <id name=\"userid\" type=\"integer\" ops=\"get,put,delete\" />"
1434 " <api name=\"profile\" ops=\"get\" />"
1441 if (!parser.
init(restDesc)) {
1451 if (bad.
init(
"<notrestapi></notrestapi>")) {
1452 unittest::fail(
"RESTParser test: init accepted definition without <restapi> tag");
1456 unittest::fail(
"RESTParser test: missing-tag init did not set an error string");
1462 if (malformed.
init(
"<restapi><api name=")) {
1471 bool isInvalid =
false;
1473 "GET /users HTTP/1.1\r\n"
1474 "Host: localhost\r\n"
1477 if (!httpReq.
processHeader(raw, (uint32)strlen(raw), isInvalid) || isInvalid) {
1478 unittest::fail(
"RESTParser test: processHeader on GET failed (isInvalid=%d)", (
int)isInvalid);
1482 unittest::fail(
"RESTParser test: parsed request type != HTTP_GET (got %u)", (
unsigned)httpReq.
type);
1487 unittest::fail(
"RESTParser test: parseRequest(HTTPRequest) returned NULL");
1491 unittest::fail(
"RESTParser test: GET /users status %d != SUCCESS (err='%s')",
1503 bool isInvalid =
false;
1505 "GET /doesnotexist HTTP/1.1\r\n"
1506 "Host: localhost\r\n"
1509 if (!httpReq.
processHeader(raw, (uint32)strlen(raw), isInvalid) || isInvalid) {
1510 unittest::fail(
"RESTParser test: processHeader on unknown GET failed");
1515 unittest::fail(
"RESTParser test: parseRequest(unknown) returned NULL");
1519 unittest::fail(
"RESTParser test: unknown request unexpectedly reported SUCCESS");
1536 unittest::fail(
"RESTParser test: parseRequest(DataMessage) returned NULL");
1549 "GET /users HTTP/1.1\r\n"
1550 "Host: localhost\r\n"
1552 uint32 rawLen = (uint32)strlen(raw);
1554 for (
int n = 0; n < N; n++) {
1555 bool isInvalid =
false;
1558 unittest::fail(
"RESTParser test: processHeader failed during throughput loop at %d", n);
1563 unittest::fail(
"RESTParser test: parseRequest returned NULL during throughput loop at %d", n);
1581 "REST API definition parsing, request matching and parameter extraction",
"network");
1612 const char* opsString = objectNode.
getAttribute(
"ops");
1614 if (!opsString || !strlen(opsString))
1616 else if (
stricmp(opsString,
"post") == 0)
1618 else if (
stricmp(opsString,
"put") == 0)
1620 else if (
stricmp(opsString,
"delete") == 0)
1625 if (!
name.length() || !
url.length())
1634 const char* entryName, *entryValue, *entryParam, *entryContentType;
1637 uint32 i, count = objectNode.
nChildNode(
"header");
1638 for (i = 0; i < count; i++) {
1639 if (!(node = objectNode.
getChildNode(
"header", i)).isEmpty()) {
1643 if (entryName && strlen(entryName)) {
1644 if (entryValue && strlen(entryValue)) {
1647 else if (entryParam && strlen(entryParam)) {
1655 for (i = 0; i < count; i++) {
1656 if (!(node = objectNode.
getChildNode(
"body", i)).isEmpty()) {
1661 if (entryName && strlen(entryName)) {
1662 if (entryValue && strlen(entryValue)) {
1665 else if (entryParam && strlen(entryParam)) {
1669 if (entryContentType && strlen(entryContentType)) {
1688 std::map<std::string, std::string> allHeaderEntries;
1699 if ((str = restReq->
getString(hI->second.c_str(), success)) && success)
1700 allHeaderEntries[hI->first] = str;
1704 std::map<std::string, HTTPPostEntry*> allBodyEntries;
1707 std::map<std::string, std::string>::iterator sI, sE =
bodyContentTypes.end();
1712 postEntry->
name = bI->first;
1714 postEntry->
type = sI->second;
1716 postEntry->
type =
"text/plain";
1717 postEntry->
contentSize = (uint32)bI->second.length();
1720 allBodyEntries[postEntry->
name] = postEntry;
1728 postEntry->
name = bI->first;
1730 postEntry->
type = sI->second;
1732 postEntry->
type =
"text/plain";
1740 if ((str = restReq->
getString(bI->second.c_str(), success)) && success) {
1747 allBodyEntries[postEntry->
name] = postEntry;
1753 std::map<std::string, HTTPPostEntry*>::iterator i = allBodyEntries.begin(), e = allBodyEntries.end();
#define HTTP_OPTIONS
OPTIONS.
#define HTTP_DELETE
DELETE.
XML-defined REST API parsing: turn HTTP requests (or DataMessages) into validated RESTRequest objects...
Small, dependency-free unit test harness used by all CMSDK object tests.
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool setTime(const char *key, uint64 value)
setTime(const char* key, uint64 value)
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
bool getInt(const char *key, int64 &value)
getInt(const char* key, int64& value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
const char * getData(const char *key, uint32 &size)
getData(const char* key, uint32& size)
uint64 getTime(const char *key)
getTime(const char* key)
const char * getString(const char *key)
getString(const char* key)
One named entry of an HTTP POST body (form field or uploaded file part).
std::string type
MIME Content-Type of this part.
uint32 contentSize
Size of content in bytes.
std::string name
Form field name (Content-Disposition name attribute).
char * content
Owned content bytes (NUL-terminated for convenience).
A parsed or generated HTTP request (also used for WebSocket upgrade handshakes).
const char * getHeaderEntry(const char *entry)
Look up a header field (case-insensitive).
const char * getPostData(const char *entry, uint32 &size, const char **type)
Get a multipart POST part's content, size and MIME type.
uint8 type
HTTP_* method id.
bool processHeader(const char *buffer, uint32 size, bool &isInvalid)
Parse the HTTP header block from raw bytes.
bool createMultipartRequest(uint8 type, const char *host, const char *uri, std::map< std::string, std::string > &headerEntries, std::map< std::string, HTTPPostEntry * > &bodyEntries, bool keepAlive, uint64 ifModifiedSince)
Build a multipart/form-data request from several named parts.
const char * getPostDataType(const char *entry)
Definition of an outbound HTTP call template from the REST API XML.
std::map< std::string, std::string > headerParams
Headers filled from request parameters.
~RESTHTTPCall()
Destructor.
HTTPRequest * generateHTTPRequest(RESTRequest *restReq)
Build the outbound HTTP request for a parsed REST request, substituting parameters into URL,...
uint8 ops
Allowed HTTP operations bitmask (GET/POST/...).
std::string name
Call name from the API definition.
std::map< std::string, std::string > headerEntries
Fixed header key/values.
bool init(XMLNode objectNode)
Populate this call definition from its XML node in the API definition.
std::string contentType
Content-Type for the request body.
std::map< std::string, std::string > bodyContentTypes
Per-body-part content types (e.g.
std::string cacheControl
Cache-Control header value, if any.
RESTHTTPCall()
Create an empty call definition; populate with init().
std::map< std::string, std::string > bodyParams
Body fields filled from request parameters.
std::map< std::string, std::string > bodyEntries
Fixed body key/values.
std::string url
URL template (with parameter placeholders).
RESTParser()
Create an uninitialised parser; call init() before use.
std::map< std::string, RESTHTTPCall * > httpCalls
virtual ~RESTParser()
Destructor; deletes the owned RESTHTTPCall objects.
bool init(const char *restDefinition)
Load and validate the XML REST API definition.
const char * getLastErrorString()
XMLNode findTopNodeForRequest(const char *requestString)
bool extractParameters(HTTPRequest *req, std::map< std::string, RESTParam > ¶mSet, RESTRequest *restReq)
std::map< std::string, std::map< std::string, RESTParam > > paramSets
bool extractArrayParameters(HTTPRequest *req, std::map< std::string, RESTParam > ¶mSet, RESTRequest *restReq, int entryNum, const char *content, uint32 contentSize, const char *contentType)
std::string lastErrorString
bool setParameterValue(HTTPRequest *req, RESTParam ¶m, std::string value, RESTRequest *restReq, uint32 entryNum=0)
RESTRequest * parseRequest(HTTPRequest *req)
Parse an incoming HTTP request against the API definition.
static bool UnitTest()
Run the RESTParser unit tests.
std::map< std::string, RESTParam > parseParamSet(XMLNode objectNode)
A parsed and validated REST request with typed access to its parameters.
virtual ~RESTRequest()
Destructor; deletes the internal storage message.
bool addToErrorString(const char *errorLine)
bool setRequestSignature(const char *signature)
RESTRequest()
Create an empty request (status FAILED_EMPTY) with fresh internal storage.
bool setBinary(const char *key, const char *data, uint32 size, const char *contentType)
bool setDouble(const char *key, double val)
bool requireAuthentication
enum cmlabs::RESTRequest::Status status
bool setString(const char *key, const char *val)
char * getDataCopy(const char *key, uint32 &size, bool &success)
bool setInt(const char *key, int64 val)
std::string getHTTPCallURL()
bool setStatus(Status status)
bool setFilter(const char *key, const char *val)
std::map< std::string, std::string > filterMap
const char * getString(const char *key, bool &success)
bool setRequireAuthentication(bool auth)
HTTPRequest * generateHTTPCallRequest()
Generate the outbound HTTPRequest for an HTTP-call request.
bool getRequireAuthentication()
const char * getErrorString()
DataMessage * getStorageMessageCopy()
Status
Parse/validation outcome of the request.
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
std::vector< std::string > TextListSplit(const char *text, const char *split, bool keepEmpty=true, bool autoTrim=false)
Split text on a separator.
bool IsTextNumeric(const char *ascii, uint32 start=0, uint32 end=0)
Test whether a (sub)string is a valid number.
std::map< std::string, std::string > TextMapSplit(const char *text, const char *outersplit, const char *innersplit)
Split "k=v<sep>k=v..." text into a map (later duplicates overwrite earlier ones).
uint32 StringSingleReplace(std::string &text, std::string key, std::string value, bool onlyFirst)
Replace occurrences of key with value in text.
std::multimap< std::string, std::string > TextMultiMapSplit(const char *text, const char *outersplit, const char *innersplit)
Split "k=v<sep>k=v..." text into a multimap (duplicate keys preserved).
std::string TextTrimQuotes(const char *text)
Strip a single pair of surrounding quotes if present.
const char * stristr(const char *str, const char *substr, uint32 len=0)
Case-insensitive strstr.
std::string StringFormat(const char *format,...)
printf into a std::string.
int64 Ascii2Int64(const char *ascii, uint32 start=0, uint32 end=0)
Parse a signed 64-bit decimal integer from a substring.
float64 Ascii2Float64(const char *ascii, uint32 start=0, uint32 end=0)
Parse a 64-bit float from a substring (decimal point, not locale dependent).
const char * GetJSONValue(jsmntok_t *tokens, int tokenCount, const char *json, int token, int &valLength)
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens)
Run JSON parser.
std::vector< int > GetJSONArrayIndexes(jsmntok_t *tokens, int tokenCount, const char *json, const char *key, jsmntype_t type=JSMN_UNDEFINED, int level=0, int instance=0)
void jsmn_init(jsmn_parser *parser)
Create JSON parser over an array of tokens.
std::string GetURIFromURL(std::string url)
Extract the URI (path plus query) from a URL.
bool IsMimeBinary(const char *type)
Determine whether a MIME type denotes binary content.
std::string EncodeHTML(std::string str)
Encode a plain string for safe embedding in HTML (e.g.
std::string GetHostFromURL(std::string url)
Extract the host name (or IP literal) from a URL.
std::string DecodeHTML(std::string str)
Decode HTML entities in a string (e.g.
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
@ eXMLErrorFirstTagNotFound
static char HTTP_Type[][8]
HTTP method name strings, indexed by the HTTP_* method ids (index 0 unused).
void Register_RESTParser_Tests()
static struct PsyType CTRL_TEST
Definition of one parameter of a REST request, as declared in the API XML.
enum cmlabs::RESTParam::Type type
Expected data type of the parameter.
bool allowEmpty
true if an empty value is acceptable.
std::string name
Parameter name.
std::string objects
For ARRAY parameters: the nested object/param-set name.
bool optional
true if the parameter may be omitted.
XMLAttribute getAttribute(int i=0) const
XMLNode getChildNode(int i=0) const
int nChildNode(XMLCSTR name) const