|
@@ -89,9 +89,46 @@ static void sendSoapException(SafeSocket &client, IException *E, const char *que
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+static void sendJsonException(SafeSocket &client, IException *E, const char *queryName)
|
|
|
+{
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (!queryName)
|
|
|
+ queryName = "Unknown"; // Exceptions when parsing query XML can leave queryName unset/unknowable....
|
|
|
+
|
|
|
+ StringBuffer response("{");
|
|
|
+ appendfJSONName(response, "%sResponse", queryName).append(" {");
|
|
|
+ appendJSONName(response, "Results").append(" {");
|
|
|
+ appendJSONName(response, "Exception").append(" [{");
|
|
|
+ appendJSONValue(response, "Source", "Roxie");
|
|
|
+ appendJSONValue(response, "Code", E->errorCode());
|
|
|
+ StringBuffer s;
|
|
|
+ appendJSONValue(response, "Message", E->errorMessage(s).str());
|
|
|
+ response.append("}]}}}");
|
|
|
+ client.write(response.str(), response.length());
|
|
|
+ }
|
|
|
+ catch(IException *EE)
|
|
|
+ {
|
|
|
+ StringBuffer error("While reporting exception: ");
|
|
|
+ DBGLOG("%s", EE->errorMessage(error).str());
|
|
|
+ EE->Release();
|
|
|
+ }
|
|
|
+#ifndef _DEBUG
|
|
|
+ catch(...) {}
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static void sendHttpException(SafeSocket &client, TextMarkupFormat fmt, IException *E, const char *queryName)
|
|
|
+{
|
|
|
+ if (fmt==MarkupFmt_JSON)
|
|
|
+ sendJsonException(client, E, queryName);
|
|
|
+ else
|
|
|
+ sendSoapException(client, E, queryName);
|
|
|
+}
|
|
|
+
|
|
|
//================================================================================================================
|
|
|
|
|
|
-class CSoapRequestAsyncFor : public CInterface, public CAsyncFor
|
|
|
+class CHttpRequestAsyncFor : public CInterface, public CAsyncFor
|
|
|
{
|
|
|
private:
|
|
|
const char *queryName, *queryText;
|
|
@@ -106,7 +143,7 @@ private:
|
|
|
CriticalSection crit;
|
|
|
|
|
|
public:
|
|
|
- CSoapRequestAsyncFor(const char *_queryName, IQueryFactory *_f, IArrayOf<IPropertyTree> &_requestArray, SafeSocket &_client, HttpHelper &_httpHelper, unsigned &_memused, unsigned &_slaveReplyLen, const char *_queryText, const IRoxieContextLogger &_logctx, PTreeReaderOptions _xmlReadFlags) :
|
|
|
+ CHttpRequestAsyncFor(const char *_queryName, IQueryFactory *_f, IArrayOf<IPropertyTree> &_requestArray, SafeSocket &_client, HttpHelper &_httpHelper, unsigned &_memused, unsigned &_slaveReplyLen, const char *_queryText, const IRoxieContextLogger &_logctx, PTreeReaderOptions _xmlReadFlags) :
|
|
|
f(_f), requestArray(_requestArray), client(_client), httpHelper(_httpHelper), memused(_memused), slaveReplyLen(_slaveReplyLen), logctx(_logctx), xmlReadFlags(_xmlReadFlags)
|
|
|
{
|
|
|
queryName = _queryName;
|
|
@@ -122,7 +159,7 @@ public:
|
|
|
StringBuffer error("EXCEPTION: ");
|
|
|
E->errorMessage(error);
|
|
|
DBGLOG("%s", error.str());
|
|
|
- sendSoapException(client, E, queryName);
|
|
|
+ sendHttpException(client, httpHelper.queryContentFormat(), E, queryName);
|
|
|
E->Release();
|
|
|
}
|
|
|
|
|
@@ -131,7 +168,7 @@ public:
|
|
|
try
|
|
|
{
|
|
|
IPropertyTree &request = requestArray.item(idx);
|
|
|
- Owned<IRoxieServerContext> ctx = f->createContext(&request, client, true, false, false, httpHelper, true, logctx, xmlReadFlags);
|
|
|
+ Owned<IRoxieServerContext> ctx = f->createContext(&request, client, httpHelper.queryContentFormat(), false, false, httpHelper, true, logctx, xmlReadFlags);
|
|
|
ctx->process();
|
|
|
ctx->flush(idx);
|
|
|
CriticalBlock b(crit);
|
|
@@ -1246,16 +1283,31 @@ private:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void sanitizeQuery(Owned<IPropertyTree> &queryXML, StringAttr &queryName, StringBuffer &saniText, bool isHTTP, const char *&uid, bool &isRequest, bool &isRequestArray, bool &isBlind, bool &isDebug)
|
|
|
+ void sanitizeQuery(Owned<IPropertyTree> &queryXML, StringAttr &queryName, StringBuffer &saniText, HttpHelper &httpHelper, const char *&uid, bool &isRequest, bool &isRequestArray, bool &isBlind, bool &isDebug)
|
|
|
{
|
|
|
if (queryXML)
|
|
|
{
|
|
|
queryName.set(queryXML->queryName());
|
|
|
isRequest = false;
|
|
|
isRequestArray = false;
|
|
|
- if (isHTTP)
|
|
|
+ if (httpHelper.isHttp())
|
|
|
{
|
|
|
- if (stricmp(queryName, "envelope") == 0)
|
|
|
+ if (httpHelper.queryContentFormat()==MarkupFmt_JSON)
|
|
|
+ {
|
|
|
+ if (strieq(queryName, "__object__"))
|
|
|
+ {
|
|
|
+ queryXML.setown(queryXML->getPropTree("*[1]"));
|
|
|
+ queryName.set(queryXML->queryName());
|
|
|
+ isRequest = true;
|
|
|
+ if (!queryXML)
|
|
|
+ throw MakeStringException(ROXIE_DATA_ERROR, "Malformed JSON request (missing Body)");
|
|
|
+ }
|
|
|
+ else if (strieq(queryName, "__array__"))
|
|
|
+ throw MakeStringException(ROXIE_DATA_ERROR, "JSON request array not implemented");
|
|
|
+ else
|
|
|
+ throw MakeStringException(ROXIE_DATA_ERROR, "Malformed JSON request");
|
|
|
+ }
|
|
|
+ else if (strieq(queryName, "envelope"))
|
|
|
{
|
|
|
queryXML.setown(queryXML->getPropTree("Body/*"));
|
|
|
if (!queryXML)
|
|
@@ -1297,7 +1349,13 @@ private:
|
|
|
else
|
|
|
throw MakeStringException(ROXIE_DATA_ERROR, "Malformed request");
|
|
|
}
|
|
|
-
|
|
|
+ void parseQueryPTFromString(Owned<IPropertyTree> &queryPT, HttpHelper &httpHelper, const char *text, PTreeReaderOptions options)
|
|
|
+ {
|
|
|
+ if (strieq(httpHelper.queryContentType(), "application/json"))
|
|
|
+ queryPT.setown(createPTreeFromJSONString(text, ipt_caseInsensitive, options));
|
|
|
+ else
|
|
|
+ queryPT.setown(createPTreeFromXMLString(text, ipt_caseInsensitive, options));
|
|
|
+ }
|
|
|
void doMain(const char *runQuery)
|
|
|
{
|
|
|
StringBuffer rawText(runQuery);
|
|
@@ -1347,7 +1405,7 @@ readAnother:
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- bool isXml = true;
|
|
|
+ TextMarkupFormat mlFmt = MarkupFmt_XML;
|
|
|
bool isRaw = false;
|
|
|
bool isHTTP = httpHelper.isHttp();
|
|
|
bool isBlocked = false;
|
|
@@ -1371,7 +1429,7 @@ readAnother:
|
|
|
{
|
|
|
if (logctx.queryTraceLevel() > 8)
|
|
|
logctx.CTXLOG("Got lock request %s", rawText.str());
|
|
|
- FlushingStringBuffer response(client, false, true, false, false, logctx);
|
|
|
+ FlushingStringBuffer response(client, false, MarkupFmt_XML, false, false, logctx);
|
|
|
response.startDataset("Control", NULL, (unsigned) -1);
|
|
|
if (!cascade)
|
|
|
cascade.setown(new CascadeManager(logctx));
|
|
@@ -1391,7 +1449,7 @@ readAnother:
|
|
|
{
|
|
|
if (logctx.queryTraceLevel() > 8)
|
|
|
logctx.CTXLOG("Got childlock request %s", rawText.str());
|
|
|
- FlushingStringBuffer response(client, false, true, false, false, logctx);
|
|
|
+ FlushingStringBuffer response(client, false, MarkupFmt_XML, false, false, logctx);
|
|
|
response.startDataset("Control", NULL, (unsigned) -1);
|
|
|
if (!cascade)
|
|
|
cascade.setown(new CascadeManager(logctx));
|
|
@@ -1413,7 +1471,7 @@ readAnother:
|
|
|
queryXML.clear();
|
|
|
bool doControlQuery = true;
|
|
|
|
|
|
- FlushingStringBuffer response(client, false, true, false, isHTTP, logctx);
|
|
|
+ FlushingStringBuffer response(client, false, MarkupFmt_XML, false, isHTTP, logctx);
|
|
|
response.startDataset("Control", NULL, (unsigned) -1);
|
|
|
|
|
|
if (strnicmp(rawText.str(), "<control:aclupdate", 18)==0 && !isalpha(rawText.charAt(18)))
|
|
@@ -1472,7 +1530,7 @@ readAnother:
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
- queryXml.setown(createPTreeFromXMLString(rawText.str(), ipt_caseInsensitive, (PTreeReaderOptions)(defaultXmlReadFlags | ptr_ignoreNameSpaces)));
|
|
|
+ parseQueryPTFromString(queryXml, httpHelper, rawText.str(), (PTreeReaderOptions)(defaultXmlReadFlags | ptr_ignoreNameSpaces));
|
|
|
}
|
|
|
catch (IException *E)
|
|
|
{
|
|
@@ -1485,7 +1543,7 @@ readAnother:
|
|
|
bool isBlind = false;
|
|
|
bool isDebug = false;
|
|
|
|
|
|
- sanitizeQuery(queryXml, queryName, sanitizedText, isHTTP, uid, isRequest, isRequestArray, isBlind, isDebug);
|
|
|
+ sanitizeQuery(queryXml, queryName, sanitizedText, httpHelper, uid, isRequest, isRequestArray, isBlind, isDebug);
|
|
|
pool->checkAccess(peer, queryName, sanitizedText, isBlind);
|
|
|
if (isDebug)
|
|
|
{
|
|
@@ -1518,7 +1576,7 @@ readAnother:
|
|
|
if (!debugCmdHandler.get())
|
|
|
debugCmdHandler.setown(new CDebugCommandHandler);
|
|
|
}
|
|
|
- FlushingStringBuffer response(client, false, true, false, isHTTP, logctx);
|
|
|
+ FlushingStringBuffer response(client, false, MarkupFmt_XML, false, isHTTP, logctx);
|
|
|
response.startDataset("Debug", NULL, (unsigned) -1);
|
|
|
debugCmdHandler->doDebugCommand(queryXml, debuggerContext, response);
|
|
|
}
|
|
@@ -1545,7 +1603,7 @@ readAnother:
|
|
|
{
|
|
|
queryFactory.setown(globalPackageSetManager->getQuery(queryName, logctx));
|
|
|
if (isHTTP)
|
|
|
- client->setHttpMode(queryName, isRequestArray);
|
|
|
+ client->setHttpMode(queryName, isRequestArray, httpHelper.queryContentFormat());
|
|
|
if (queryFactory)
|
|
|
{
|
|
|
bool stripWhitespace = queryFactory->getDebugValueBool("stripWhitespaceFromStoredDataset", 0 != (ptr_ignoreWhiteSpace & defaultXmlReadFlags));
|
|
@@ -1555,8 +1613,8 @@ readAnother:
|
|
|
if (xmlReadFlags != defaultXmlReadFlags)
|
|
|
{
|
|
|
// we need to reparse input xml, as global whitespace setting has been overridden
|
|
|
- queryXml.setown(createPTreeFromXMLString(rawText.str(), ipt_caseInsensitive, (PTreeReaderOptions)(xmlReadFlags|ptr_ignoreNameSpaces)));
|
|
|
- sanitizeQuery(queryXml, queryName, sanitizedText, isHTTP, uid, isRequest, isRequestArray, isBlind, isDebug);
|
|
|
+ parseQueryPTFromString(queryXml, httpHelper, rawText.str(), (PTreeReaderOptions)(xmlReadFlags | ptr_ignoreNameSpaces));
|
|
|
+ sanitizeQuery(queryXml, queryName, sanitizedText, httpHelper, uid, isRequest, isRequestArray, isBlind, isDebug);
|
|
|
}
|
|
|
IArrayOf<IPropertyTree> requestArray;
|
|
|
if (isHTTP)
|
|
@@ -1598,7 +1656,7 @@ readAnother:
|
|
|
if (stricmp(format, "raw") == 0)
|
|
|
{
|
|
|
isRaw = true;
|
|
|
- isXml = false;
|
|
|
+ mlFmt = MarkupFmt_Unknown;
|
|
|
isBlocked = (client != NULL);
|
|
|
}
|
|
|
else if (stricmp(format, "bxml") == 0)
|
|
@@ -1608,7 +1666,7 @@ readAnother:
|
|
|
else if (stricmp(format, "ascii") == 0)
|
|
|
{
|
|
|
isRaw = false;
|
|
|
- isXml = false;
|
|
|
+ mlFmt = MarkupFmt_Unknown;
|
|
|
}
|
|
|
else if (stricmp(format, "xml") != 0) // xml is the default
|
|
|
throw MakeStringException(ROXIE_INVALID_INPUT, "Unsupported format specified: %s", format);
|
|
@@ -1629,12 +1687,12 @@ readAnother:
|
|
|
combinedQueryStats.noteActive();
|
|
|
if (isHTTP)
|
|
|
{
|
|
|
- CSoapRequestAsyncFor af(queryName, queryFactory, requestArray, *client, httpHelper, memused, slavesReplyLen, sanitizedText, logctx, xmlReadFlags);
|
|
|
+ CHttpRequestAsyncFor af(queryName, queryFactory, requestArray, *client, httpHelper, memused, slavesReplyLen, sanitizedText, logctx, xmlReadFlags);
|
|
|
af.For(requestArray.length(), numRequestArrayThreads);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- Owned<IRoxieServerContext> ctx = queryFactory->createContext(queryXml, *client, isXml, isRaw, isBlocked, httpHelper, trim, logctx, xmlReadFlags);
|
|
|
+ Owned<IRoxieServerContext> ctx = queryFactory->createContext(queryXml, *client, mlFmt, isRaw, isBlocked, httpHelper, trim, logctx, xmlReadFlags);
|
|
|
if (client && !ctx->outputResultsToSocket())
|
|
|
{
|
|
|
unsigned replyLen = 0;
|
|
@@ -1706,7 +1764,7 @@ readAnother:
|
|
|
if (client)
|
|
|
{
|
|
|
if (isHTTP)
|
|
|
- sendSoapException(*client, E, queryName);
|
|
|
+ sendHttpException(*client, mlFmt, E, queryName);
|
|
|
else
|
|
|
client->sendException("Roxie", code, error.str(), isBlocked, logctx);
|
|
|
}
|
|
@@ -1782,7 +1840,7 @@ readAnother:
|
|
|
{
|
|
|
if (logctx.intercept)
|
|
|
{
|
|
|
- FlushingStringBuffer response(client, isBlocked, isXml, isRaw, false, logctx);
|
|
|
+ FlushingStringBuffer response(client, isBlocked, mlFmt, isRaw, false, logctx);
|
|
|
response.startDataset("Tracing", NULL, (unsigned) -1);
|
|
|
logctx.outputXML(response);
|
|
|
}
|