searchdhttp.cpp 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  1. //
  2. // $Id$
  3. //
  4. //
  5. // Copyright (c) 2001-2016, Andrew Aksyonoff
  6. // Copyright (c) 2008-2016, Sphinx Technologies Inc
  7. // All rights reserved
  8. //
  9. // This program is free software; you can redistribute it and/or modify
  10. // it under the terms of the GNU General Public License. You should have
  11. // received a copy of the GPL license along with this program; if you
  12. // did not, you can find it at http://www.gnu.org/
  13. //
  14. #include "sphinx.h"
  15. #include "sphinxint.h"
  16. #include "sphinxjson.h"
  17. #include "sphinxjsonquery.h"
  18. #include "http/http_parser.h"
  19. #include "json/cJSON.h"
  20. #include "searchdaemon.h"
  21. #include "searchdha.h"
  22. struct EscapeJsonString_t
  23. {
  24. static bool IsEscapeChar ( char c )
  25. {
  26. return ( c=='"' || c=='\\' || c=='/' || c=='\b' || c=='\n' || c=='\r' || c=='\t'|| c=='\f' );
  27. }
  28. static char GetEscapedChar ( char c )
  29. {
  30. switch ( c )
  31. {
  32. case '\b': return 'b';
  33. case '\n': return 'n';
  34. case '\r': return 'r';
  35. case '\t': return 't';
  36. case '\f': return 'f';
  37. default: return c;
  38. }
  39. }
  40. };
  41. using JsonEscapedBuilder = EscapedStringBuilder_T <EscapeJsonString_t>;
  42. static void AppendJsonKey ( const char * sName, JsonEscapedBuilder & tOut )
  43. {
  44. tOut += "\"";
  45. tOut += sName;
  46. tOut += "\":";
  47. }
  48. static void EncodeResultJson ( const AggrResult_t & tRes, JsonEscapedBuilder & tOut )
  49. {
  50. const ISphSchema & tSchema = tRes.m_tSchema;
  51. CSphVector<BYTE> dTmp;
  52. int iAttrsCount = sphSendGetAttrCount(tSchema);
  53. tOut += "{";
  54. // column names
  55. AppendJsonKey ( "attrs", tOut );
  56. tOut += "[";
  57. for ( int i=0; i<iAttrsCount; i++ )
  58. tOut.Appendf ( "%s\"%s\"", ( i==0 ? "" : "," ), tSchema.GetAttr ( i ).m_sName.cstr() );
  59. tOut += "],";
  60. // attribute values
  61. AppendJsonKey ( "matches", tOut );
  62. tOut += "[";
  63. for ( int iMatch=tRes.m_iOffset; iMatch<tRes.m_iOffset+tRes.m_iCount; iMatch++ )
  64. {
  65. tOut += ( iMatch==tRes.m_iOffset ? "[" : ",[" );
  66. const CSphMatch & tMatch = tRes.m_dMatches [ iMatch ];
  67. const char * sSep = "";
  68. for ( int iAttr=0; iAttr<iAttrsCount; iAttr++ )
  69. {
  70. tOut += sSep;
  71. CSphAttrLocator tLoc = tSchema.GetAttr(iAttr).m_tLocator;
  72. ESphAttr eAttrType = tSchema.GetAttr(iAttr).m_eAttrType;
  73. assert ( sphPlainAttrToPtrAttr(eAttrType)==eAttrType );
  74. switch ( eAttrType )
  75. {
  76. case SPH_ATTR_FLOAT:
  77. tOut.Appendf ( "%f", tMatch.GetAttrFloat(tLoc) );
  78. break;
  79. case SPH_ATTR_UINT32SET_PTR:
  80. case SPH_ATTR_INT64SET_PTR:
  81. {
  82. tOut += "[";
  83. CSphVector<char> dStr;
  84. sphPackedMVA2Str ( (const BYTE *)tMatch.GetAttr ( tLoc ), eAttrType==SPH_ATTR_INT64SET_PTR, dStr );
  85. dStr.Add('\0');
  86. tOut += dStr.Begin();
  87. tOut += "]";
  88. }
  89. break;
  90. case SPH_ATTR_STRINGPTR:
  91. {
  92. const BYTE * pString = (const BYTE *)tMatch.GetAttr ( tLoc );
  93. int iLen = sphUnpackPtrAttr ( pString, &pString );
  94. dTmp.Resize ( iLen+1 );
  95. memcpy ( dTmp.Begin(), pString, iLen );
  96. dTmp[iLen] = '\0';
  97. tOut += "\"";
  98. tOut.AppendEscaped ( ( const char *)dTmp.Begin(), true, false );
  99. tOut += "\"";
  100. }
  101. break;
  102. case SPH_ATTR_JSON_PTR:
  103. {
  104. const BYTE * pJSON = (const BYTE *)tMatch.GetAttr ( tLoc );
  105. sphUnpackPtrAttr ( pJSON, &pJSON );
  106. // no object at all? return NULL
  107. if ( !pJSON )
  108. {
  109. tOut += "null";
  110. break;
  111. }
  112. dTmp.Resize ( 0 );
  113. sphJsonFormat ( dTmp, pJSON );
  114. if ( !dTmp.GetLength() )
  115. {
  116. // empty string (no objects) - return NULL
  117. // (canonical "{}" and "[]" are handled by sphJsonFormat)
  118. tOut += "null";
  119. break;
  120. }
  121. dTmp.Add(0);
  122. tOut += (const char *)dTmp.Begin();
  123. }
  124. break;
  125. case SPH_ATTR_FACTORS:
  126. case SPH_ATTR_FACTORS_JSON:
  127. {
  128. const BYTE * pFactors = (const BYTE *)tMatch.GetAttr ( tLoc );
  129. sphUnpackPtrAttr ( pFactors, &pFactors );
  130. if ( pFactors )
  131. {
  132. bool bStr = ( eAttrType==SPH_ATTR_FACTORS );
  133. dTmp.Resize ( 0 );
  134. sphFormatFactors ( dTmp, (unsigned int *)pFactors, !bStr );
  135. dTmp.Add ( '\0' );
  136. if ( bStr )
  137. {
  138. tOut += "\"";
  139. tOut.AppendEscaped ( (const char *)dTmp.Begin(), true, false );
  140. tOut += "\"";
  141. } else
  142. tOut += (const char *)dTmp.Begin();
  143. } else
  144. tOut += "null";
  145. }
  146. break;
  147. case SPH_ATTR_JSON_FIELD_PTR:
  148. {
  149. const BYTE * pField = (const BYTE *)tMatch.GetAttr ( tLoc );
  150. sphUnpackPtrAttr ( pField, &pField );
  151. if ( !pField )
  152. {
  153. tOut += "null";
  154. break;
  155. }
  156. ESphJsonType eJson = ESphJsonType ( *pField++ );
  157. if ( eJson==JSON_NULL )
  158. {
  159. // no key found - NULL value
  160. tOut += "null";
  161. } else
  162. {
  163. // send string to client
  164. dTmp.Resize ( 0 );
  165. sphJsonFieldFormat ( dTmp, pField, eJson, true );
  166. dTmp.Add ( '\0' );
  167. tOut += (const char *)dTmp.Begin();
  168. }
  169. }
  170. break;
  171. case SPH_ATTR_INTEGER:
  172. case SPH_ATTR_TIMESTAMP:
  173. case SPH_ATTR_BOOL:
  174. case SPH_ATTR_TOKENCOUNT:
  175. case SPH_ATTR_BIGINT:
  176. default:
  177. tOut.Appendf ( INT64_FMT, tMatch.GetAttr(tLoc) );
  178. }
  179. sSep = ",";
  180. }
  181. tOut += "]";
  182. }
  183. tOut += "],";
  184. // meta information
  185. AppendJsonKey ( "meta", tOut );
  186. tOut += "{";
  187. tOut.Appendf ( "\"total\":%d, \"total_found\":" INT64_FMT ", \"time\":%d.%03d,", tRes.m_iMatches, tRes.m_iTotalMatches, tRes.m_iQueryTime/1000, tRes.m_iQueryTime%1000 );
  188. // word statistics
  189. AppendJsonKey ( "words", tOut );
  190. tOut += "[";
  191. tRes.m_hWordStats.IterateStart();
  192. const char * sSep = "";
  193. while ( tRes.m_hWordStats.IterateNext() )
  194. {
  195. const CSphQueryResultMeta::WordStat_t & tStat = tRes.m_hWordStats.IterateGet();
  196. tOut.Appendf ( "%s{\"word\":\"%s\", \"docs\":" INT64_FMT ", \"hits\":" INT64_FMT "}", sSep, tRes.m_hWordStats.IterateGetKey().cstr(), tStat.m_iDocs, tStat.m_iHits );
  197. sSep = ",";
  198. }
  199. tOut += "]}";
  200. if ( !tRes.m_sWarning.IsEmpty() )
  201. {
  202. tOut.Appendf ( ",\"warning\":\"" );
  203. tOut.AppendEscaped ( tRes.m_sWarning.cstr() );
  204. tOut.Appendf ( "\"" );
  205. }
  206. tOut += "}";
  207. }
  208. const char * g_dHttpStatus[] = { "200 OK", "206 Partial Content", "400 Bad Request", "500 Internal Server Error", "501 Not Implemented", "503 Service Unavailable" };
  209. STATIC_ASSERT ( sizeof(g_dHttpStatus)/sizeof(g_dHttpStatus[0])==SPH_HTTP_STATUS_TOTAL, SPH_HTTP_STATUS_SHOULD_BE_SAME_AS_SPH_HTTP_STATUS_TOTAL );
  210. static void HttpBuildReply ( CSphVector<BYTE> & dData, ESphHttpStatus eCode, const char * sBody, int iBodyLen, bool bHtml )
  211. {
  212. assert ( sBody && iBodyLen );
  213. const char * sContent = ( bHtml ? "text/html" : "application/json" );
  214. CSphString sHttp;
  215. sHttp.SetSprintf ( "HTTP/1.1 %s\r\nServer: %s\r\nContent-Type: %s; charset=UTF-8\r\nContent-Length:%d\r\n\r\n", g_dHttpStatus[eCode], SPHINX_VERSION, sContent, iBodyLen );
  216. int iHeaderLen = sHttp.Length();
  217. dData.Resize ( iHeaderLen + iBodyLen );
  218. memcpy ( dData.Begin(), sHttp.cstr(), iHeaderLen );
  219. memcpy ( dData.Begin() + iHeaderLen, sBody, iBodyLen );
  220. }
  221. static void HttpErrorReply ( CSphVector<BYTE> & dData, ESphHttpStatus eCode, const char * szError )
  222. {
  223. cJSON * pError = cJSON_CreateObject();
  224. assert ( pError );
  225. cJSON_AddStringToObject ( pError, "error", szError );
  226. CSphString sJsonError = sphJsonToString ( pError );
  227. cJSON_Delete ( pError );
  228. HttpBuildReply ( dData, eCode, sJsonError.cstr(), sJsonError.Length(), false );
  229. }
  230. using OptionsHash_t = SmallStringHash_T<CSphString>;
  231. class HttpRequestParser_c : public ISphNoncopyable
  232. {
  233. public:
  234. bool Parse ( const BYTE * pData, int iDataLen );
  235. bool ParseList ( const char * sAt, int iLen );
  236. const CSphString & GetBody() const { return m_sRawBody; }
  237. ESphHttpEndpoint GetEndpoint() const { return m_eEndpoint; }
  238. const OptionsHash_t & GetOptions() const { return m_hOptions; }
  239. const CSphString & GetInvalidEndpoint() const { return m_sInvalidEndpoint; }
  240. const char * GetError() const { return m_szError; }
  241. bool GetKeepAlive() const { return m_bKeepAlive; }
  242. static int ParserUrl ( http_parser * pParser, const char * sAt, size_t iLen );
  243. static int ParserHeaderField ( http_parser * pParser, const char * sAt, size_t iLen );
  244. static int ParserHeaderValue ( http_parser * pParser, const char * sAt, size_t iLen );
  245. static int ParserBody ( http_parser * pParser, const char * sAt, size_t iLen );
  246. private:
  247. bool m_bKeepAlive {false};
  248. const char * m_szError {nullptr};
  249. ESphHttpEndpoint m_eEndpoint {SPH_HTTP_ENDPOINT_TOTAL};
  250. CSphString m_sInvalidEndpoint;
  251. CSphString m_sRawBody;
  252. CSphString m_sCurField;
  253. OptionsHash_t m_hOptions;
  254. };
  255. bool HttpRequestParser_c::Parse ( const BYTE * pData, int iDataLen )
  256. {
  257. http_parser_settings tParserSettings;
  258. http_parser_settings_init ( &tParserSettings );
  259. tParserSettings.on_url = HttpRequestParser_c::ParserUrl;
  260. tParserSettings.on_header_field = HttpRequestParser_c::ParserHeaderField;
  261. tParserSettings.on_header_value = HttpRequestParser_c::ParserHeaderValue;
  262. tParserSettings.on_body = HttpRequestParser_c::ParserBody;
  263. http_parser tParser;
  264. tParser.data = this;
  265. http_parser_init ( &tParser, HTTP_REQUEST );
  266. int iParsed = http_parser_execute ( &tParser, &tParserSettings, (const char *)pData, iDataLen );
  267. if ( iParsed!=iDataLen )
  268. {
  269. m_szError = http_errno_description ( (http_errno)tParser.http_errno );
  270. return false;
  271. }
  272. // connection wide http options
  273. m_bKeepAlive = ( http_should_keep_alive ( &tParser )!=0 );
  274. return true;
  275. }
  276. static BYTE Char2Hex ( BYTE uChar )
  277. {
  278. if ( uChar>=0x41 && uChar<=0x46 )
  279. return ( uChar - 'A' ) + 10;
  280. else if ( uChar>=0x61 && uChar<=0x66 )
  281. return ( uChar - 'a' ) + 10;
  282. else
  283. return uChar - '0';
  284. }
  285. static void UriPercentReplace ( CSphString & sEntity )
  286. {
  287. if ( sEntity.IsEmpty() )
  288. return;
  289. char * pDst = (char *)sEntity.cstr();
  290. const char * pSrc = pDst;
  291. while ( *pSrc )
  292. {
  293. if ( *pSrc=='%' && *(pSrc+1) && *(pSrc+2) )
  294. {
  295. BYTE uCode = Char2Hex ( *(pSrc+1) ) * 16 + Char2Hex ( *(pSrc+2) );
  296. pSrc += 3;
  297. *pDst++ = uCode;
  298. } else
  299. {
  300. *pDst++ = ( *pSrc=='+' ? ' ' : *pSrc );
  301. pSrc++;
  302. }
  303. }
  304. *pDst = '\0';
  305. }
  306. bool HttpRequestParser_c::ParseList ( const char * sAt, int iLen )
  307. {
  308. const char * sCur = sAt;
  309. const char * sEnd = sAt + iLen;
  310. const char * sLast = sCur;
  311. CSphString sName;
  312. CSphString sVal;
  313. for ( ; sCur<sEnd; sCur++ )
  314. {
  315. if ( *sCur!='&' && *sCur!='=' )
  316. continue;
  317. int iValueLen = sCur - sLast;
  318. if ( *sCur=='&' )
  319. {
  320. sVal.SetBinary ( sLast, iValueLen );
  321. UriPercentReplace ( sName );
  322. UriPercentReplace ( sVal );
  323. m_hOptions.Add ( sVal, sName );
  324. sName = "";
  325. sVal = "";
  326. } else
  327. {
  328. sName.SetBinary ( sLast, iValueLen );
  329. }
  330. sLast = sCur+1;
  331. }
  332. if ( !sName.IsEmpty() )
  333. {
  334. sVal.SetBinary ( sLast, sCur - sLast );
  335. UriPercentReplace ( sName );
  336. UriPercentReplace ( sVal );
  337. m_hOptions.Add ( sVal, sName );
  338. }
  339. return true;
  340. }
  341. int HttpRequestParser_c::ParserUrl ( http_parser * pParser, const char * sAt, size_t iLen )
  342. {
  343. http_parser_url tUri;
  344. if ( http_parser_parse_url ( sAt, iLen, 0, &tUri ) )
  345. return 0;
  346. DWORD uPath = ( 1UL<<UF_PATH );
  347. DWORD uQuery = ( 1UL<<UF_QUERY );
  348. if ( ( tUri.field_set & uPath )!=0 )
  349. {
  350. const char * sPath = sAt + tUri.field_data[UF_PATH].off;
  351. int iPathLen = tUri.field_data[UF_PATH].len;
  352. if ( *sPath=='/' )
  353. {
  354. sPath++;
  355. iPathLen--;
  356. }
  357. CSphString sEndpoint ( sPath, iPathLen );
  358. ESphHttpEndpoint eEndpoint = sphStrToHttpEndpoint ( sEndpoint );
  359. ( (HttpRequestParser_c *)pParser->data )->m_eEndpoint = eEndpoint;
  360. if ( eEndpoint==SPH_HTTP_ENDPOINT_TOTAL )
  361. ( (HttpRequestParser_c *)pParser->data )->m_sInvalidEndpoint.SetBinary ( sPath, iPathLen );
  362. }
  363. if ( ( tUri.field_set & uQuery )!=0 )
  364. ( (HttpRequestParser_c *)pParser->data )->ParseList ( sAt + tUri.field_data[UF_QUERY].off, tUri.field_data[UF_QUERY].len );
  365. return 0;
  366. }
  367. int HttpRequestParser_c::ParserHeaderField ( http_parser * pParser, const char * sAt, size_t iLen )
  368. {
  369. assert ( pParser->data );
  370. ( (HttpRequestParser_c *)pParser->data )->m_sCurField.SetBinary ( sAt, iLen );
  371. return 0;
  372. }
  373. int HttpRequestParser_c::ParserHeaderValue ( http_parser * pParser, const char * sAt, size_t iLen )
  374. {
  375. assert ( pParser->data );
  376. CSphString sVal;
  377. sVal.SetBinary ( sAt, iLen );
  378. HttpRequestParser_c * pHttpParser = (HttpRequestParser_c *)pParser->data;
  379. pHttpParser->m_hOptions.Add ( sVal, pHttpParser->m_sCurField );
  380. pHttpParser->m_sCurField = "";
  381. return 0;
  382. }
  383. int HttpRequestParser_c::ParserBody ( http_parser * pParser, const char * sAt, size_t iLen )
  384. {
  385. assert ( pParser->data );
  386. HttpRequestParser_c * pHttpParser = (HttpRequestParser_c *)pParser->data;
  387. pHttpParser->ParseList ( sAt, iLen );
  388. pHttpParser->m_sRawBody.SetBinary ( sAt, iLen );
  389. return 0;
  390. }
  391. static const CSphString * GetAnyValue ( const SmallStringHash_T<CSphString> & hOptions, const char * sKey1, const char * sKey2 )
  392. {
  393. const CSphString * pVal1 = hOptions ( sKey1 );
  394. return pVal1 ? pVal1 : hOptions ( sKey2 );
  395. }
  396. static void ParseSearchOptions ( const SmallStringHash_T<CSphString> & hOptions, CSphQuery & tQuery )
  397. {
  398. const CSphString * pMatch = hOptions ( "match" );
  399. if ( pMatch )
  400. tQuery.m_sQuery = *pMatch;
  401. const CSphString * pIdx = GetAnyValue ( hOptions, "index", "indexes" );
  402. if ( pIdx )
  403. tQuery.m_sIndexes = *pIdx;
  404. const CSphString * pSel = GetAnyValue ( hOptions, "select", "select_list" );
  405. if ( pSel )
  406. tQuery.m_sSelect = *pSel;
  407. const CSphString * pGroup = GetAnyValue ( hOptions, "group", "group_by" );
  408. if ( pGroup )
  409. tQuery.m_sGroupBy = *pGroup;
  410. const CSphString * pOrder = GetAnyValue ( hOptions, "order", "order_by" );
  411. if ( pOrder )
  412. tQuery.m_sSortBy = *pOrder;
  413. const CSphString * pLimit = hOptions ( "limit" );
  414. if ( pLimit )
  415. tQuery.m_iLimit = atoi ( pLimit->cstr() );
  416. }
  417. static const char * g_sIndexPage =
  418. "<!DOCTYPE html>"
  419. "<html>"
  420. "<head>"
  421. "<title>Manticore</title>"
  422. "</head>"
  423. "<body>"
  424. "<h1>Manticore daemon</h1>"
  425. "<p>%s</p>"
  426. "</body>"
  427. "</html>";
  428. static void HttpHandlerIndexPage ( CSphVector<BYTE> & dData )
  429. {
  430. StringBuilder_c sIndexPage;
  431. sIndexPage.Appendf ( g_sIndexPage, SPHINX_VERSION );
  432. HttpBuildReply ( dData, SPH_HTTP_STATUS_200, sIndexPage.cstr(), sIndexPage.Length(), true );
  433. }
  434. class CSphQueryProfileJson : public CSphQueryProfile
  435. {
  436. public:
  437. virtual ~CSphQueryProfileJson();
  438. virtual void BuildResult ( XQNode_t * pRoot, const CSphSchema & tSchema, const CSphVector<CSphString> & dZones );
  439. virtual cJSON * LeakResultAsJson();
  440. virtual const char * GetResultAsStr() const;
  441. private:
  442. cJSON * m_pResult {nullptr};
  443. };
  444. CSphQueryProfileJson::~CSphQueryProfileJson()
  445. {
  446. cJSON_Delete(m_pResult);
  447. }
  448. void CSphQueryProfileJson::BuildResult ( XQNode_t * pRoot, const CSphSchema & tSchema, const CSphVector<CSphString> & /*dZones*/ )
  449. {
  450. assert ( !m_pResult );
  451. m_pResult = sphBuildProfileJson ( pRoot, tSchema );
  452. }
  453. cJSON * CSphQueryProfileJson::LeakResultAsJson()
  454. {
  455. cJSON * pTmp = m_pResult;
  456. m_pResult = nullptr;
  457. return pTmp;
  458. }
  459. const char * CSphQueryProfileJson::GetResultAsStr() const
  460. {
  461. assert ( 0 && "Not implemented" );
  462. return nullptr;
  463. }
  464. //////////////////////////////////////////////////////////////////////////
  465. class JsonRequestBuilder_c : public IRequestBuilder_t
  466. {
  467. public:
  468. JsonRequestBuilder_c ( const CSphString & sQuery, const SqlStmt_t & /*tStmt*/, ESphHttpEndpoint eEndpoint )
  469. : m_eEndpoint ( eEndpoint )
  470. {
  471. // fixme: we can implement replacing indexes in a string (without parsing) if it becomes a performance issue
  472. m_pQuery = cJSON_Parse ( sQuery.cstr() );
  473. assert ( m_pQuery );
  474. }
  475. ~JsonRequestBuilder_c()
  476. {
  477. cJSON_Delete ( m_pQuery );
  478. }
  479. void BuildRequest ( const AgentConn_t & tAgent, ISphOutputBuffer & tOut ) const override
  480. {
  481. // replace "index" value in the json query
  482. cJSON_DeleteItemFromObject ( m_pQuery, "index" );
  483. cJSON_AddStringToObject ( m_pQuery, "index", tAgent.m_tDesc.m_sIndexes.cstr() );
  484. CSphString sRequest = sphJsonToString ( m_pQuery );
  485. CSphString sEndpoint = sphHttpEndpointToStr ( m_eEndpoint );
  486. tOut.SendWord ( SEARCHD_COMMAND_JSON );
  487. tOut.SendWord ( VER_COMMAND_JSON );
  488. tOut.SendInt ( sEndpoint.Length() + sRequest.Length() + 8 );
  489. tOut.SendString ( sEndpoint.cstr() );
  490. tOut.SendString ( sRequest.cstr() );
  491. }
  492. private:
  493. cJSON * m_pQuery {nullptr};
  494. ESphHttpEndpoint m_eEndpoint {SPH_HTTP_ENDPOINT_TOTAL};
  495. };
  496. class JsonReplyParser_c : public IReplyParser_t
  497. {
  498. public:
  499. JsonReplyParser_c ( int & iAffected, int & iWarnings )
  500. : m_iAffected ( iAffected )
  501. , m_iWarnings ( iWarnings )
  502. {}
  503. virtual bool ParseReply ( MemInputBuffer_c & tReq, AgentConn_t & ) const
  504. {
  505. CSphString sEndpoint = tReq.GetString();
  506. ESphHttpEndpoint eEndpoint = sphStrToHttpEndpoint ( sEndpoint );
  507. if ( eEndpoint!=SPH_HTTP_ENDPOINT_JSON_UPDATE && eEndpoint!=SPH_HTTP_ENDPOINT_JSON_DELETE )
  508. return false;
  509. DWORD uLength = tReq.GetDword();
  510. CSphFixedVector<BYTE> dResult ( uLength );
  511. tReq.GetBytes ( dResult.Begin(), uLength );
  512. return sphGetResultStats ( (const char *)dResult.Begin(), m_iAffected, m_iWarnings, eEndpoint==SPH_HTTP_ENDPOINT_JSON_UPDATE );
  513. }
  514. protected:
  515. int & m_iAffected;
  516. int & m_iWarnings;
  517. };
  518. class JsonParserFactory_c : public QueryParserFactory_i
  519. {
  520. public:
  521. JsonParserFactory_c ( ESphHttpEndpoint eEndpoint )
  522. : m_eEndpoint ( eEndpoint )
  523. {}
  524. virtual QueryParser_i * CreateQueryParser() const override
  525. {
  526. return sphCreateJsonQueryParser();
  527. }
  528. IRequestBuilder_t * CreateRequestBuilder ( const CSphString & sQuery, const SqlStmt_t & tStmt ) const override
  529. {
  530. return new JsonRequestBuilder_c ( sQuery, tStmt, m_eEndpoint );
  531. }
  532. IReplyParser_t * CreateReplyParser ( int & iUpdated, int & iWarnings ) const override
  533. {
  534. return new JsonReplyParser_c ( iUpdated, iWarnings );
  535. }
  536. private:
  537. ESphHttpEndpoint m_eEndpoint {SPH_HTTP_ENDPOINT_TOTAL};
  538. };
  539. //////////////////////////////////////////////////////////////////////////
  540. class HttpErrorReporter_c : public StmtErrorReporter_i
  541. {
  542. public:
  543. virtual void Ok ( int iAffectedRows, const CSphString & /*sWarning*/ ) { m_iAffected = iAffectedRows; }
  544. virtual void Ok ( int iAffectedRows, int /*nWarnings*/ ) { m_iAffected = iAffectedRows; }
  545. virtual void Error ( const char * sStmt, const char * sError, MysqlErrors_e iErr );
  546. virtual SqlRowBuffer_c * GetBuffer() { return NULL; }
  547. bool IsError() const { return m_bError; }
  548. const char * GetError() const { return m_sError.cstr(); }
  549. int GetAffectedRows() const { return m_iAffected; }
  550. private:
  551. bool m_bError {false};
  552. CSphString m_sError;
  553. int m_iAffected {0};
  554. };
  555. void HttpErrorReporter_c::Error ( const char * /*sStmt*/, const char * sError, MysqlErrors_e /*iErr*/ )
  556. {
  557. m_bError = true;
  558. m_sError = sError;
  559. }
  560. //////////////////////////////////////////////////////////////////////////
  561. // all the handlers for http queries
  562. class HttpHandler_c
  563. {
  564. public:
  565. HttpHandler_c ( const CSphString & sQuery, int iCID, bool bNeedHttpResponse )
  566. : m_sQuery ( sQuery )
  567. , m_bNeedHttpResponse ( bNeedHttpResponse )
  568. {}
  569. virtual bool Process () = 0;
  570. CSphVector<BYTE> & GetResult()
  571. {
  572. return m_dData;
  573. }
  574. protected:
  575. const CSphString & m_sQuery;
  576. int m_iCID {0};
  577. bool m_bNeedHttpResponse {false};
  578. CSphVector<BYTE> m_dData;
  579. void ReportError ( const char * szError, ESphHttpStatus eStatus )
  580. {
  581. if ( m_bNeedHttpResponse )
  582. HttpErrorReply ( m_dData, eStatus, szError );
  583. else
  584. {
  585. m_dData.Resize ( strlen(szError)+1 );
  586. memcpy ( m_dData.Begin(), szError, m_dData.GetLength() );
  587. }
  588. }
  589. void BuildReply ( const CSphString & sResult, ESphHttpStatus eStatus )
  590. {
  591. if ( m_bNeedHttpResponse )
  592. HttpBuildReply ( m_dData, eStatus, sResult.cstr(), sResult.Length(), false );
  593. else
  594. {
  595. m_dData.Resize ( sResult.Length()+1 );
  596. memcpy ( m_dData.Begin(), sResult.cstr(), m_dData.GetLength() );
  597. }
  598. }
  599. };
  600. class HttpOptionsTraits_c
  601. {
  602. protected:
  603. const OptionsHash_t & m_tOptions;
  604. HttpOptionsTraits_c ( const OptionsHash_t & tOptions )
  605. : m_tOptions ( tOptions )
  606. {}
  607. };
  608. class HttpSearchHandler_c : public HttpHandler_c, public HttpOptionsTraits_c
  609. {
  610. public:
  611. HttpSearchHandler_c ( const CSphString & sQuery, const OptionsHash_t & tOptions, int iCID, bool bNeedHttpResponse )
  612. : HttpHandler_c ( sQuery, iCID, bNeedHttpResponse )
  613. , HttpOptionsTraits_c ( tOptions )
  614. {}
  615. virtual bool Process () override
  616. {
  617. CSphQuery tQuery;
  618. CSphString sWarning;
  619. QueryParser_i * pQueryParser = PreParseQuery();
  620. if ( !pQueryParser )
  621. return false;
  622. m_tQuery.m_pQueryParser = pQueryParser;
  623. CSphScopedPtr<ISphSearchHandler> tHandler ( sphCreateSearchHandler ( 1, pQueryParser, m_eQueryType, true, m_iCID ) );
  624. CSphQueryProfileJson tProfile;
  625. if ( m_bProfile )
  626. tHandler->SetProfile ( &tProfile );
  627. tHandler->SetQuery ( 0, m_tQuery );
  628. // search
  629. tHandler->RunQueries();
  630. if ( m_bProfile )
  631. tProfile.Stop();
  632. AggrResult_t * pRes = tHandler->GetResult ( 0 );
  633. if ( !pRes->m_sError.IsEmpty() )
  634. {
  635. ReportError ( pRes->m_sError.cstr(), SPH_HTTP_STATUS_500 );
  636. return false;
  637. }
  638. // fixme: handle more than one warning at once?
  639. if ( pRes->m_sWarning.IsEmpty() )
  640. pRes->m_sWarning = m_sWarning;
  641. CSphString sResult = EncodeResult ( *pRes, m_bProfile ? &tProfile : NULL );
  642. BuildReply ( sResult, SPH_HTTP_STATUS_200 );
  643. return true;
  644. }
  645. protected:
  646. bool m_bProfile {false};
  647. bool m_bAttrHighlight {false};
  648. QueryType_e m_eQueryType {QUERY_SQL};
  649. CSphQuery m_tQuery;
  650. CSphString m_sWarning;
  651. virtual QueryParser_i * PreParseQuery() = 0;
  652. virtual CSphString EncodeResult ( const AggrResult_t & tRes, CSphQueryProfile * pProfile ) = 0;
  653. };
  654. class HttpSearchHandler_Plain_c : public HttpSearchHandler_c
  655. {
  656. public:
  657. HttpSearchHandler_Plain_c ( const CSphString & sQuery, const OptionsHash_t & tOptions, int iCID, bool bNeedHttpResponse )
  658. : HttpSearchHandler_c ( sQuery, tOptions, iCID, bNeedHttpResponse )
  659. {}
  660. protected:
  661. QueryParser_i * PreParseQuery() override
  662. {
  663. CSphString sError;
  664. ParseSearchOptions ( m_tOptions, m_tQuery );
  665. if ( !m_tQuery.ParseSelectList ( sError ) )
  666. {
  667. ReportError ( sError.cstr(), SPH_HTTP_STATUS_400 );
  668. return NULL;
  669. }
  670. if ( !m_tQuery.m_sSortBy.IsEmpty() )
  671. m_tQuery.m_eSort = SPH_SORT_EXTENDED;
  672. m_eQueryType = QUERY_SQL;
  673. return sphCreatePlainQueryParser();
  674. }
  675. CSphString EncodeResult ( const AggrResult_t & tRes, CSphQueryProfile * /*pProfile*/ ) override
  676. {
  677. JsonEscapedBuilder tResBuilder;
  678. EncodeResultJson ( tRes, tResBuilder );
  679. CSphString sResult;
  680. sResult.Adopt ( tResBuilder.Leak() );
  681. return sResult;
  682. }
  683. };
  684. class HttpSearchHandler_SQL_c : public HttpSearchHandler_Plain_c
  685. {
  686. public:
  687. HttpSearchHandler_SQL_c ( const CSphString & sQuery, const OptionsHash_t & tOptions, int iCID, bool bNeedHttpResponse )
  688. : HttpSearchHandler_Plain_c ( sQuery, tOptions, iCID, bNeedHttpResponse )
  689. {}
  690. protected:
  691. QueryParser_i * PreParseQuery() override
  692. {
  693. const CSphString * pRawQl = m_tOptions ( "query" );
  694. if ( !pRawQl || pRawQl->IsEmpty() )
  695. {
  696. ReportError ( "query missing", SPH_HTTP_STATUS_400 );
  697. return NULL;
  698. }
  699. CSphString sError;
  700. CSphVector<SqlStmt_t> dStmt;
  701. if ( !sphParseSqlQuery ( pRawQl->cstr(), pRawQl->Length(), dStmt, sError, SPH_COLLATION_DEFAULT ) )
  702. {
  703. ReportError ( sError.cstr(), SPH_HTTP_STATUS_400 );
  704. return NULL;
  705. }
  706. m_tQuery = dStmt[0].m_tQuery;
  707. if ( dStmt.GetLength()>1 )
  708. {
  709. ReportError ( "multiple queries not supported", SPH_HTTP_STATUS_501 );
  710. return NULL;
  711. } else if ( dStmt[0].m_eStmt!=STMT_SELECT )
  712. {
  713. ReportError ( "only SELECT queries are supported", SPH_HTTP_STATUS_501 );
  714. return NULL;
  715. }
  716. m_eQueryType = QUERY_SQL;
  717. return sphCreatePlainQueryParser();
  718. }
  719. };
  720. class HttpHandler_JsonSearch_c : public HttpSearchHandler_c
  721. {
  722. public:
  723. HttpHandler_JsonSearch_c ( const CSphString & sQuery, const OptionsHash_t & tOptions, int iCID, bool bNeedHttpResponse )
  724. : HttpSearchHandler_c ( sQuery, tOptions, iCID, bNeedHttpResponse )
  725. {}
  726. QueryParser_i * PreParseQuery() override
  727. {
  728. CSphString sError;
  729. if ( !sphParseJsonQuery ( m_sQuery.cstr(), m_tQuery, m_bProfile, m_bAttrHighlight, sError, m_sWarning ) )
  730. {
  731. sError.SetSprintf( "Error parsing json query: %s", sError.cstr() );
  732. ReportError ( sError.cstr(), SPH_HTTP_STATUS_400 );
  733. return NULL;
  734. }
  735. m_eQueryType = QUERY_JSON;
  736. return sphCreateJsonQueryParser();
  737. }
  738. protected:
  739. CSphString EncodeResult ( const AggrResult_t & tRes, CSphQueryProfile * pProfile ) override
  740. {
  741. return sphEncodeResultJson ( tRes, m_tQuery, pProfile, m_bAttrHighlight );
  742. }
  743. };
  744. class HttpJsonInsertTraits_c
  745. {
  746. protected:
  747. bool ProcessInsert ( SqlStmt_t & tStmt, SphDocID_t tDocId, bool bReplace, cJSON * & pResult )
  748. {
  749. CSphSessionAccum tAcc ( false );
  750. CSphString sWarning;
  751. HttpErrorReporter_c tReporter;
  752. sphHandleMysqlInsert ( tReporter, tStmt, bReplace, true, sWarning, tAcc, SPH_COLLATION_DEFAULT );
  753. if ( tReporter.IsError() )
  754. pResult = sphEncodeInsertErrorJson ( tStmt.m_sIndex.cstr(), tReporter.GetError() );
  755. else
  756. pResult = sphEncodeInsertResultJson ( tStmt.m_sIndex.cstr(), bReplace, tDocId );
  757. return !tReporter.IsError();
  758. }
  759. };
  760. class HttpHandler_JsonInsert_c : public HttpHandler_c, public HttpJsonInsertTraits_c
  761. {
  762. public:
  763. HttpHandler_JsonInsert_c ( const CSphString & sQuery, bool bReplace, bool bNeedHttpResponse )
  764. : HttpHandler_c ( sQuery, 0, bNeedHttpResponse )
  765. , m_bReplace ( bReplace )
  766. {}
  767. bool Process () override
  768. {
  769. SqlStmt_t tStmt;
  770. SphDocID_t tDocId = DOCID_MAX;
  771. CSphString sError;
  772. if ( !sphParseJsonInsert ( m_sQuery.cstr(), tStmt, tDocId, m_bReplace, sError ) )
  773. {
  774. sError.SetSprintf( "Error parsing json query: %s", sError.cstr() );
  775. ReportError ( sError.cstr(), SPH_HTTP_STATUS_400 );
  776. return false;
  777. }
  778. cJSON * pResult = NULL;
  779. bool bResult = ProcessInsert ( tStmt, tDocId, m_bReplace, pResult );
  780. CSphString sResult = sphJsonToString ( pResult );
  781. BuildReply( sResult, bResult ? SPH_HTTP_STATUS_200 : SPH_HTTP_STATUS_500 );
  782. cJSON_Delete ( pResult );
  783. return bResult;
  784. }
  785. private:
  786. bool m_bReplace {false};
  787. };
  788. class HttpJsonUpdateTraits_c
  789. {
  790. protected:
  791. bool ProcessUpdate ( const char * szRawRequest, const SqlStmt_t & tStmt, SphDocID_t tDocId, int iCID, cJSON * & pResult )
  792. {
  793. HttpErrorReporter_c tReporter;
  794. CSphString sWarning;
  795. JsonParserFactory_c tFactory ( SPH_HTTP_ENDPOINT_JSON_UPDATE );
  796. sphHandleMysqlUpdate ( tReporter, tFactory, tStmt, szRawRequest, sWarning, iCID );
  797. if ( tReporter.IsError() )
  798. pResult = sphEncodeInsertErrorJson ( tStmt.m_sIndex.cstr(), tReporter.GetError() );
  799. else
  800. pResult = sphEncodeUpdateResultJson ( tStmt.m_sIndex.cstr(), tDocId, tReporter.GetAffectedRows() );
  801. return !tReporter.IsError();
  802. }
  803. };
  804. class HttpHandler_JsonUpdate_c : public HttpHandler_c, HttpJsonUpdateTraits_c
  805. {
  806. public:
  807. HttpHandler_JsonUpdate_c ( const CSphString & sQuery, int iCID, bool bNeedHttpResponse )
  808. : HttpHandler_c ( sQuery, iCID, bNeedHttpResponse )
  809. {}
  810. bool Process () override
  811. {
  812. SqlStmt_t tStmt;
  813. SphDocID_t tDocId = DOCID_MAX;
  814. CSphString sError;
  815. if ( !ParseQuery ( tStmt, tDocId, sError ) )
  816. {
  817. sError.SetSprintf( "Error parsing json query: %s", sError.cstr() );
  818. ReportError ( sError.cstr(), SPH_HTTP_STATUS_400 );
  819. return false;
  820. }
  821. cJSON * pResult = NULL;
  822. bool bResult = ProcessQuery ( tStmt, tDocId, pResult );
  823. CSphString sResult = sphJsonToString ( pResult );
  824. BuildReply( sResult, bResult ? SPH_HTTP_STATUS_200 : SPH_HTTP_STATUS_500 );
  825. cJSON_Delete ( pResult );
  826. return bResult;
  827. }
  828. protected:
  829. virtual bool ParseQuery ( SqlStmt_t & tStmt, SphDocID_t & tDocId, CSphString & sError )
  830. {
  831. return sphParseJsonUpdate ( m_sQuery.cstr(), tStmt, tDocId, sError );
  832. }
  833. virtual bool ProcessQuery ( const SqlStmt_t & tStmt, SphDocID_t tDocId, cJSON * & pResult )
  834. {
  835. return ProcessUpdate ( m_sQuery.cstr(), tStmt, tDocId, m_iCID, pResult );
  836. }
  837. };
  838. class HttpJsonDeleteTraits_c
  839. {
  840. protected:
  841. bool ProcessDelete ( const char * szRawRequest, const SqlStmt_t & tStmt, SphDocID_t tDocId, int iCID, cJSON * & pResult )
  842. {
  843. CSphSessionAccum tAcc ( false );
  844. HttpErrorReporter_c tReporter;
  845. CSphString sWarning;
  846. JsonParserFactory_c tFactory ( SPH_HTTP_ENDPOINT_JSON_DELETE );
  847. sphHandleMysqlDelete ( tReporter, tFactory, tStmt, szRawRequest, true, tAcc, iCID );
  848. if ( tReporter.IsError() )
  849. pResult = sphEncodeInsertErrorJson ( tStmt.m_sIndex.cstr(), tReporter.GetError() );
  850. else
  851. pResult = sphEncodeDeleteResultJson ( tStmt.m_sIndex.cstr(), tDocId, tReporter.GetAffectedRows() );
  852. return !tReporter.IsError();
  853. }
  854. };
  855. class HttpHandler_JsonDelete_c : public HttpHandler_JsonUpdate_c, public HttpJsonDeleteTraits_c
  856. {
  857. public:
  858. HttpHandler_JsonDelete_c ( const CSphString & sQuery, int iCID, bool bNeedHttpResponse )
  859. : HttpHandler_JsonUpdate_c ( sQuery, iCID, bNeedHttpResponse )
  860. {}
  861. protected:
  862. bool ParseQuery ( SqlStmt_t & tStmt, SphDocID_t & tDocId, CSphString & sError ) override
  863. {
  864. return sphParseJsonDelete ( m_sQuery.cstr(), tStmt, tDocId, sError );
  865. }
  866. bool ProcessQuery ( const SqlStmt_t & tStmt, SphDocID_t tDocId, cJSON * & pResult ) override
  867. {
  868. return ProcessDelete ( m_sQuery.cstr(), tStmt, tDocId, m_iCID, pResult );
  869. }
  870. };
  871. class HttpHandler_JsonBulk_c : public HttpHandler_c, public HttpOptionsTraits_c, public HttpJsonInsertTraits_c, public HttpJsonUpdateTraits_c, public HttpJsonDeleteTraits_c
  872. {
  873. public:
  874. HttpHandler_JsonBulk_c ( const CSphString & sQuery, const OptionsHash_t & tOptions, int iCID, bool bNeedHttpResponse )
  875. : HttpHandler_c ( sQuery, iCID, bNeedHttpResponse )
  876. , HttpOptionsTraits_c ( tOptions )
  877. {}
  878. bool Process () override
  879. {
  880. if ( !m_tOptions.Exists ("Content-Type") )
  881. {
  882. ReportError ( "Content-Type must be set", SPH_HTTP_STATUS_400 );
  883. return false;
  884. }
  885. if ( m_tOptions["Content-Type"].ToLower() != "application/x-ndjson" )
  886. {
  887. ReportError ( "Content-Type must be application/x-ndjson", SPH_HTTP_STATUS_400 );
  888. return false;
  889. }
  890. cJSON * pRoot = cJSON_CreateObject();
  891. cJSON * pItems = cJSON_CreateArray();
  892. assert ( pRoot && pItems );
  893. cJSON_AddItemToObject ( pRoot, "items", pItems );
  894. // fixme: we're modifying the original query at this point
  895. char * p = const_cast<char*>(m_sQuery.cstr());
  896. bool bResult = false;
  897. while ( *p )
  898. {
  899. while ( sphIsSpace(*p) )
  900. p++;
  901. char * szStmt = p;
  902. while ( *p && *p!='\r' && *p!='\n' )
  903. p++;
  904. if ( p-szStmt==0 )
  905. break;
  906. *p++ = '\0';
  907. SqlStmt_t tStmt;
  908. SphDocID_t tDocId = DOCID_MAX;
  909. CSphString sStmt;
  910. CSphString sError;
  911. CSphString sQuery;
  912. if ( !sphParseJsonStatement ( szStmt, tStmt, sStmt, sQuery, tDocId, sError ) )
  913. {
  914. sError.SetSprintf( "Error parsing json query: %s", sError.cstr() );
  915. ReportError ( sError.cstr(), SPH_HTTP_STATUS_400 );
  916. cJSON_Delete ( pRoot );
  917. return false;
  918. }
  919. cJSON * pResult = NULL;
  920. bResult = false;
  921. switch ( tStmt.m_eStmt )
  922. {
  923. case STMT_INSERT:
  924. case STMT_REPLACE:
  925. bResult = ProcessInsert ( tStmt, tDocId, tStmt.m_eStmt==STMT_REPLACE, pResult );
  926. break;
  927. case STMT_UPDATE:
  928. bResult = ProcessUpdate ( sQuery.cstr(), tStmt, tDocId, m_iCID, pResult );
  929. break;
  930. case STMT_DELETE:
  931. bResult = ProcessDelete ( sQuery.cstr(), tStmt, tDocId, m_iCID, pResult );
  932. break;
  933. default:
  934. ReportError ( "Unknown statement", SPH_HTTP_STATUS_400 );
  935. cJSON_Delete ( pRoot );
  936. return false;
  937. }
  938. AddResult ( pItems, sStmt, pResult );
  939. // no further than the first error
  940. if ( !bResult )
  941. break;
  942. while ( sphIsSpace(*p) )
  943. p++;
  944. }
  945. cJSON_AddBoolToObject ( pRoot, "errors", bResult ? 0 : 1 );
  946. CSphString sResult = sphJsonToString ( pRoot );
  947. BuildReply ( sResult, bResult ? SPH_HTTP_STATUS_200 : SPH_HTTP_STATUS_500 );
  948. cJSON_Delete ( pRoot );
  949. return true;
  950. }
  951. private:
  952. void AddResult ( cJSON * pRoot, CSphString & sStmt, cJSON * pResult )
  953. {
  954. assert ( pRoot && pResult );
  955. cJSON * pItem = cJSON_CreateObject();
  956. assert ( pItem );
  957. cJSON_AddItemToArray ( pRoot, pItem );
  958. cJSON_AddItemToObject( pItem, sStmt.cstr(), pResult );
  959. }
  960. };
  961. static HttpHandler_c * CreateHttpHandler ( ESphHttpEndpoint eEndpoint, const CSphString & sQuery, const OptionsHash_t & tOptions, int iCID, bool bNeedHttpResonse )
  962. {
  963. switch ( eEndpoint )
  964. {
  965. case SPH_HTTP_ENDPOINT_SEARCH:
  966. return new HttpSearchHandler_Plain_c ( sQuery, tOptions, iCID, bNeedHttpResonse );
  967. case SPH_HTTP_ENDPOINT_SQL:
  968. return new HttpSearchHandler_SQL_c ( sQuery, tOptions, iCID, bNeedHttpResonse );
  969. case SPH_HTTP_ENDPOINT_JSON_SEARCH:
  970. return new HttpHandler_JsonSearch_c ( sQuery, tOptions, iCID, bNeedHttpResonse );
  971. case SPH_HTTP_ENDPOINT_JSON_INDEX:
  972. case SPH_HTTP_ENDPOINT_JSON_CREATE:
  973. case SPH_HTTP_ENDPOINT_JSON_INSERT:
  974. case SPH_HTTP_ENDPOINT_JSON_REPLACE:
  975. return new HttpHandler_JsonInsert_c ( sQuery, eEndpoint==SPH_HTTP_ENDPOINT_JSON_INDEX || eEndpoint==SPH_HTTP_ENDPOINT_JSON_REPLACE, bNeedHttpResonse );
  976. case SPH_HTTP_ENDPOINT_JSON_UPDATE:
  977. return new HttpHandler_JsonUpdate_c ( sQuery, iCID, bNeedHttpResonse );
  978. case SPH_HTTP_ENDPOINT_JSON_DELETE:
  979. return new HttpHandler_JsonDelete_c ( sQuery, iCID, bNeedHttpResonse );
  980. case SPH_HTTP_ENDPOINT_JSON_BULK:
  981. return new HttpHandler_JsonBulk_c ( sQuery, tOptions, iCID, bNeedHttpResonse );
  982. default:
  983. break;
  984. }
  985. return nullptr;
  986. }
  987. bool sphProcessHttpQuery ( ESphHttpEndpoint eEndpoint, const CSphString & sQuery, const SmallStringHash_T<CSphString> & tOptions, int iCID, CSphVector<BYTE> & dResult, bool bNeedHttpResponse )
  988. {
  989. CSphScopedPtr<HttpHandler_c> pHandler ( CreateHttpHandler ( eEndpoint, sQuery, tOptions, iCID, bNeedHttpResponse ) );
  990. if ( !pHandler.Ptr() )
  991. return false;
  992. pHandler->Process();
  993. dResult = std::move ( pHandler->GetResult() );
  994. return true;
  995. }
  996. bool sphLoopClientHttp ( const BYTE * pRequest, int iRequestLen, CSphVector<BYTE> & dResult, int iCID )
  997. {
  998. HttpRequestParser_c tParser;
  999. if ( !tParser.Parse ( pRequest, iRequestLen ) )
  1000. {
  1001. HttpErrorReply ( dResult, SPH_HTTP_STATUS_400, tParser.GetError() );
  1002. return tParser.GetKeepAlive();
  1003. }
  1004. ESphHttpEndpoint eEndpoint = tParser.GetEndpoint();
  1005. if ( !sphProcessHttpQuery ( eEndpoint, tParser.GetBody(), tParser.GetOptions(), iCID, dResult, true ) )
  1006. {
  1007. if ( eEndpoint==SPH_HTTP_ENDPOINT_INDEX )
  1008. HttpHandlerIndexPage ( dResult );
  1009. else
  1010. {
  1011. CSphString sError;
  1012. sError.SetSprintf ( "/%s - unsupported endpoint", tParser.GetInvalidEndpoint().cstr() );
  1013. HttpErrorReply ( dResult, SPH_HTTP_STATUS_501, sError.cstr() );
  1014. }
  1015. }
  1016. return tParser.GetKeepAlive();
  1017. }
  1018. void sphHttpErrorReply ( CSphVector<BYTE> & dData, ESphHttpStatus eCode, const char * szError )
  1019. {
  1020. HttpErrorReply ( dData, eCode, szError );
  1021. }
  1022. const char * g_dEndpoints[] = { "index.html", "search", "sql", "json/search", "json/index", "json/create", "json/insert", "json/replace", "json/update", "json/delete", "json/bulk" };
  1023. STATIC_ASSERT ( sizeof(g_dEndpoints)/sizeof(g_dEndpoints[0])==SPH_HTTP_ENDPOINT_TOTAL, SPH_HTTP_ENDPOINT_SHOULD_BE_SAME_AS_SPH_HTTP_ENDPOINT_TOTAL );
  1024. ESphHttpEndpoint sphStrToHttpEndpoint ( const CSphString & sEndpoint )
  1025. {
  1026. for ( int i = 0; i < SPH_HTTP_ENDPOINT_TOTAL; i++ )
  1027. if ( sEndpoint==g_dEndpoints[i] )
  1028. return ESphHttpEndpoint(i);
  1029. return SPH_HTTP_ENDPOINT_TOTAL;
  1030. }
  1031. CSphString sphHttpEndpointToStr ( ESphHttpEndpoint eEndpoint )
  1032. {
  1033. assert ( eEndpoint>=SPH_HTTP_ENDPOINT_INDEX && eEndpoint<SPH_HTTP_ENDPOINT_TOTAL );
  1034. return g_dEndpoints[eEndpoint];
  1035. }