1 #include "internal.h"
2 #include "errmap.h"
3 #include "contrib/lcb-jsoncpp/lcb-jsoncpp.h"
4
5 using namespace lcb::errmap;
6
ErrorMap()7 ErrorMap::ErrorMap() : revision(0), version(0) {
8 }
9
getAttribute(const std::string & s)10 static ErrorAttribute getAttribute(const std::string& s) {
11 #define X(c, s_) if (s == s_) { return c; }
12 LCB_XERRMAP_ATTRIBUTES(X)
13 #undef X
14 return INVALID_ATTRIBUTE;
15 }
16
getRetrySpec() const17 RetrySpec *Error::getRetrySpec() const {
18 return retry.specptr;
19 }
20
parse(const Json::Value & retryJson,std::string & emsg)21 RetrySpec* RetrySpec::parse(const Json::Value& retryJson, std::string& emsg) {
22
23 RetrySpec *spec = new RetrySpec();
24 spec->refcount = 1;
25
26 #define FAIL_RETRY(s) \
27 emsg = s; \
28 delete spec; \
29 return NULL;
30
31 if (!retryJson.isObject()) {
32 FAIL_RETRY("Missing retry specification");
33 }
34
35 const Json::Value& strategyJson = retryJson["strategy"];
36 if (!strategyJson.isString()) {
37 FAIL_RETRY("Missing `strategy`");
38 }
39 const char* strategy = strategyJson.asCString();
40 if (!strcasecmp(strategy, "constant")) {
41 spec->strategy = CONSTANT;
42 } else if (!strcasecmp(strategy, "linear")) {
43 spec->strategy = LINEAR;
44 } else if (!strcasecmp(strategy, "exponential")) {
45 spec->strategy = EXPONENTIAL;
46 } else {
47 FAIL_RETRY("Unknown strategy");
48 }
49
50 #define GET_TIMEFLD(srcname, dstname, required) { \
51 Json::Value dstname##Json = retryJson[srcname]; \
52 if (dstname##Json.isNumeric()) { \
53 spec->dstname = (dstname##Json).asUInt() * 1000; \
54 } else if (required) { \
55 FAIL_RETRY("Missing " # srcname); \
56 } else { \
57 spec->dstname = 0; \
58 } \
59 }
60
61 GET_TIMEFLD("interval", interval, true);
62 GET_TIMEFLD("after", after, true);
63 GET_TIMEFLD("ceil", ceil, false);
64 GET_TIMEFLD("max-duration", max_duration, false);
65
66 return spec;
67
68 #undef FAIL_RETRY
69 #undef GET_TIMEFLD
70
71 }
72
73 const uint32_t ErrorMap::MAX_VERSION = 1;
74
75 ErrorMap::ParseStatus
parse(const char * s,size_t n,std::string & errmsg)76 ErrorMap::parse(const char *s, size_t n, std::string& errmsg) {
77 Json::Value root_nonconst;
78 Json::Reader reader;
79 if (!reader.parse(s, s + n, root_nonconst)) {
80 errmsg = "Invalid JSON";
81 return PARSE_ERROR;
82 }
83
84 const Json::Value& root = root_nonconst;
85 const Json::Value& verJson = root["version"];
86 if (!verJson.isNumeric()) {
87 errmsg = "'version' is not a number";
88 return PARSE_ERROR;
89 }
90
91 if (verJson.asUInt() > MAX_VERSION) {
92 errmsg = "'version' is unreasonably high";
93 return UNKNOWN_VERSION;
94 }
95
96 const Json::Value& revJson = root["revision"];
97 if (!revJson.isNumeric()) {
98 errmsg = "'revision' is not a number";
99 return PARSE_ERROR;
100 }
101
102 if (revJson.asUInt() <= revision) {
103 return NOT_UPDATED;
104 }
105
106 const Json::Value& errsJson = root["errors"];
107 if (!errsJson.isObject()) {
108 errmsg = "'errors' is not an object";
109 return PARSE_ERROR;
110 }
111
112 Json::Value::const_iterator ii = errsJson.begin();
113 for (; ii != errsJson.end(); ++ii) {
114 // Key is the version in hex
115 unsigned ec = 0;
116 if (sscanf(ii.key().asCString(), "%x", &ec) != 1) {
117 errmsg = "key " + ii.key().asString() + " is not a hex number";
118 return PARSE_ERROR;
119 }
120
121 const Json::Value& errorJson = *ii;
122
123 // Descend into the error attributes
124 Error error;
125 error.code = static_cast<uint16_t>(ec);
126
127 error.shortname = errorJson["name"].asString();
128 error.description = errorJson["desc"].asString();
129
130 const Json::Value& attrs = errorJson["attrs"];
131 if (!attrs.isArray()) {
132 errmsg = "'attrs' is not an array";
133 return PARSE_ERROR;
134 }
135
136 Json::Value::const_iterator jj = attrs.begin();
137 for (; jj != attrs.end(); ++jj) {
138 ErrorAttribute attr = getAttribute(jj->asString());
139 if (attr == INVALID_ATTRIBUTE) {
140 errmsg = "unknown attribute received";
141 return UNKNOWN_VERSION;
142 }
143 error.attributes.insert(attr);
144 }
145 if (error.hasAttribute(AUTO_RETRY)) {
146 const Json::Value& retryJson = errorJson["retry"];
147 if (!retryJson.isObject()) {
148 errmsg = "Need `retry` specification for `auto-retry` attribute";
149 return PARSE_ERROR;
150 }
151 if ((error.retry.specptr = RetrySpec::parse(retryJson, errmsg)) == NULL) {
152 return PARSE_ERROR;
153 }
154 }
155 errors.insert(MapType::value_type(ec, error));
156 }
157
158 return UPDATED;
159 }
160
getError(uint16_t code) const161 const Error& ErrorMap::getError(uint16_t code) const {
162 static const Error invalid;
163 MapType::const_iterator it = errors.find(code);
164
165 if (it != errors.end()) {
166 return it->second;
167 } else {
168 return invalid;
169 }
170 }
171
lcb_errmap_new()172 ErrorMap *lcb_errmap_new() { return new ErrorMap(); }
lcb_errmap_free(ErrorMap * m)173 void lcb_errmap_free(ErrorMap* m) { delete m; }
174