web_client.c 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "web_client.h"
  3. // this is an async I/O implementation of the web server request parser
  4. // it is used by all netdata web servers
  5. int respect_web_browser_do_not_track_policy = 0;
  6. char *web_x_frame_options = NULL;
  7. int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRATEGY;
  8. void web_client_set_conn_tcp(struct web_client *w) {
  9. web_client_flags_clear_conn(w);
  10. web_client_flag_set(w, WEB_CLIENT_FLAG_CONN_TCP);
  11. }
  12. void web_client_set_conn_unix(struct web_client *w) {
  13. web_client_flags_clear_conn(w);
  14. web_client_flag_set(w, WEB_CLIENT_FLAG_CONN_UNIX);
  15. }
  16. void web_client_set_conn_cloud(struct web_client *w) {
  17. web_client_flags_clear_conn(w);
  18. web_client_flag_set(w, WEB_CLIENT_FLAG_CONN_CLOUD);
  19. }
  20. void web_client_set_conn_webrtc(struct web_client *w) {
  21. web_client_flags_clear_conn(w);
  22. web_client_flag_set(w, WEB_CLIENT_FLAG_CONN_WEBRTC);
  23. }
  24. inline int web_client_permission_denied(struct web_client *w) {
  25. w->response.data->content_type = CT_TEXT_PLAIN;
  26. buffer_flush(w->response.data);
  27. buffer_strcat(w->response.data, "You are not allowed to access this resource.");
  28. w->response.code = HTTP_RESP_FORBIDDEN;
  29. return HTTP_RESP_FORBIDDEN;
  30. }
  31. inline int web_client_bearer_required(struct web_client *w) {
  32. w->response.data->content_type = CT_TEXT_PLAIN;
  33. buffer_flush(w->response.data);
  34. buffer_strcat(w->response.data, "An authorization bearer is required to access the resource.");
  35. w->response.code = HTTP_RESP_PRECOND_FAIL;
  36. return HTTP_RESP_PRECOND_FAIL;
  37. }
  38. static inline int bad_request_multiple_dashboard_versions(struct web_client *w) {
  39. w->response.data->content_type = CT_TEXT_PLAIN;
  40. buffer_flush(w->response.data);
  41. buffer_strcat(w->response.data, "Multiple dashboard versions given at the URL.");
  42. w->response.code = HTTP_RESP_BAD_REQUEST;
  43. return HTTP_RESP_BAD_REQUEST;
  44. }
  45. static inline int web_client_cork_socket(struct web_client *w __maybe_unused) {
  46. #ifdef TCP_CORK
  47. if(likely(web_client_check_conn_tcp(w) && !w->tcp_cork && w->ofd != -1)) {
  48. w->tcp_cork = true;
  49. if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
  50. netdata_log_error("%llu: failed to enable TCP_CORK on socket.", w->id);
  51. w->tcp_cork = false;
  52. return -1;
  53. }
  54. }
  55. #endif /* TCP_CORK */
  56. return 0;
  57. }
  58. #ifdef ENABLE_HTTPS
  59. static inline void web_client_enable_wait_from_ssl(struct web_client *w) {
  60. if (w->ssl.ssl_errno == SSL_ERROR_WANT_READ)
  61. web_client_enable_ssl_wait_receive(w);
  62. else if (w->ssl.ssl_errno == SSL_ERROR_WANT_WRITE)
  63. web_client_enable_ssl_wait_send(w);
  64. else {
  65. web_client_disable_ssl_wait_receive(w);
  66. web_client_disable_ssl_wait_send(w);
  67. }
  68. }
  69. #endif
  70. static inline int web_client_uncork_socket(struct web_client *w __maybe_unused) {
  71. #ifdef TCP_CORK
  72. if(likely(w->tcp_cork && w->ofd != -1)) {
  73. w->tcp_cork = false;
  74. if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
  75. netdata_log_error("%llu: failed to disable TCP_CORK on socket.", w->id);
  76. w->tcp_cork = true;
  77. return -1;
  78. }
  79. }
  80. #endif /* TCP_CORK */
  81. w->tcp_cork = false;
  82. return 0;
  83. }
  84. static inline char *strip_control_characters(char *url) {
  85. if(!url) return "";
  86. for(char *s = url; *s ;s++)
  87. if(iscntrl(*s)) *s = ' ';
  88. return url;
  89. }
  90. static void web_client_reset_allocations(struct web_client *w, bool free_all) {
  91. if(free_all) {
  92. // the web client is to be destroyed
  93. buffer_free(w->url_as_received);
  94. w->url_as_received = NULL;
  95. buffer_free(w->url_path_decoded);
  96. w->url_path_decoded = NULL;
  97. buffer_free(w->url_query_string_decoded);
  98. w->url_query_string_decoded = NULL;
  99. buffer_free(w->response.header_output);
  100. w->response.header_output = NULL;
  101. buffer_free(w->response.header);
  102. w->response.header = NULL;
  103. buffer_free(w->response.data);
  104. w->response.data = NULL;
  105. buffer_free(w->payload);
  106. w->payload = NULL;
  107. }
  108. else {
  109. // the web client is to be re-used
  110. buffer_reset(w->url_as_received);
  111. buffer_reset(w->url_path_decoded);
  112. buffer_reset(w->url_query_string_decoded);
  113. buffer_reset(w->response.header_output);
  114. buffer_reset(w->response.header);
  115. buffer_reset(w->response.data);
  116. if(w->payload)
  117. buffer_reset(w->payload);
  118. // to add more items here,
  119. // web_client_reuse_from_cache() needs to be adjusted to maintain them
  120. }
  121. freez(w->server_host);
  122. w->server_host = NULL;
  123. freez(w->forwarded_host);
  124. w->forwarded_host = NULL;
  125. freez(w->forwarded_for);
  126. w->forwarded_for = NULL;
  127. freez(w->origin);
  128. w->origin = NULL;
  129. freez(w->user_agent);
  130. w->user_agent = NULL;
  131. freez(w->auth_bearer_token);
  132. w->auth_bearer_token = NULL;
  133. // if we had enabled compression, release it
  134. if(w->response.zinitialized) {
  135. deflateEnd(&w->response.zstream);
  136. w->response.zsent = 0;
  137. w->response.zhave = 0;
  138. w->response.zstream.avail_in = 0;
  139. w->response.zstream.avail_out = 0;
  140. w->response.zstream.total_in = 0;
  141. w->response.zstream.total_out = 0;
  142. w->response.zinitialized = false;
  143. web_client_flag_clear(w, WEB_CLIENT_CHUNKED_TRANSFER);
  144. }
  145. memset(w->transaction, 0, sizeof(w->transaction));
  146. web_client_flags_clear_auth(w);
  147. web_client_flag_clear(w, WEB_CLIENT_ENCODING_GZIP|WEB_CLIENT_ENCODING_DEFLATE);
  148. web_client_reset_path_flags(w);
  149. }
  150. void web_client_log_completed_request(struct web_client *w, bool update_web_stats) {
  151. struct timeval tv;
  152. now_monotonic_high_precision_timeval(&tv);
  153. size_t size = (w->mode == HTTP_REQUEST_MODE_FILECOPY) ? w->response.rlen : w->response.data->len;
  154. size_t sent = w->response.zoutput ? (size_t)w->response.zstream.total_out : size;
  155. if(update_web_stats)
  156. global_statistics_web_request_completed(dt_usec(&tv, &w->timings.tv_in),
  157. w->statistics.received_bytes,
  158. w->statistics.sent_bytes,
  159. size,
  160. sent);
  161. usec_t prep_ut = w->timings.tv_ready.tv_sec ? dt_usec(&w->timings.tv_ready, &w->timings.tv_in) : 0;
  162. usec_t sent_ut = w->timings.tv_ready.tv_sec ? dt_usec(&tv, &w->timings.tv_ready) : 0;
  163. usec_t total_ut = dt_usec(&tv, &w->timings.tv_in);
  164. strip_control_characters((char *)buffer_tostring(w->url_as_received));
  165. ND_LOG_STACK lgs[] = {
  166. ND_LOG_FIELD_U64(NDF_CONNECTION_ID, w->id),
  167. ND_LOG_FIELD_UUID(NDF_TRANSACTION_ID, &w->transaction),
  168. ND_LOG_FIELD_TXT(NDF_NIDL_NODE, w->client_host),
  169. ND_LOG_FIELD_TXT(NDF_REQUEST_METHOD, http_request_method2string(w->mode)),
  170. ND_LOG_FIELD_BFR(NDF_REQUEST, w->url_as_received),
  171. ND_LOG_FIELD_U64(NDF_RESPONSE_CODE, w->response.code),
  172. ND_LOG_FIELD_U64(NDF_RESPONSE_SENT_BYTES, sent),
  173. ND_LOG_FIELD_U64(NDF_RESPONSE_SIZE_BYTES, size),
  174. ND_LOG_FIELD_U64(NDF_RESPONSE_PREPARATION_TIME_USEC, prep_ut),
  175. ND_LOG_FIELD_U64(NDF_RESPONSE_SENT_TIME_USEC, sent_ut),
  176. ND_LOG_FIELD_U64(NDF_RESPONSE_TOTAL_TIME_USEC, total_ut),
  177. ND_LOG_FIELD_TXT(NDF_SRC_IP, w->client_ip),
  178. ND_LOG_FIELD_TXT(NDF_SRC_PORT, w->client_port),
  179. ND_LOG_FIELD_TXT(NDF_SRC_FORWARDED_FOR, w->forwarded_for),
  180. ND_LOG_FIELD_UUID(NDF_ACCOUNT_ID, &w->auth.cloud_account_id),
  181. ND_LOG_FIELD_TXT(NDF_USER_NAME, w->auth.client_name),
  182. ND_LOG_FIELD_TXT(NDF_USER_ROLE, http_id2access(w->access)),
  183. ND_LOG_FIELD_END(),
  184. };
  185. ND_LOG_STACK_PUSH(lgs);
  186. ND_LOG_FIELD_PRIORITY prio = NDLP_INFO;
  187. if(w->response.code >= 500)
  188. prio = NDLP_EMERG;
  189. else if(w->response.code >= 400)
  190. prio = NDLP_WARNING;
  191. else if(w->response.code >= 300)
  192. prio = NDLP_NOTICE;
  193. // cleanup progress
  194. if(web_client_flag_check(w, WEB_CLIENT_FLAG_PROGRESS_TRACKING)) {
  195. web_client_flag_clear(w, WEB_CLIENT_FLAG_PROGRESS_TRACKING);
  196. query_progress_finished(&w->transaction, 0, w->response.code, total_ut, size, sent);
  197. }
  198. // access log
  199. if(likely(buffer_strlen(w->url_as_received)))
  200. nd_log(NDLS_ACCESS, prio, NULL);
  201. }
  202. void web_client_request_done(struct web_client *w) {
  203. web_client_uncork_socket(w);
  204. netdata_log_debug(D_WEB_CLIENT, "%llu: Resetting client.", w->id);
  205. web_client_log_completed_request(w, true);
  206. if(unlikely(w->mode == HTTP_REQUEST_MODE_FILECOPY)) {
  207. if(w->ifd != w->ofd) {
  208. netdata_log_debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
  209. if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
  210. if (w->ifd != -1){
  211. close(w->ifd);
  212. }
  213. }
  214. w->ifd = w->ofd;
  215. }
  216. }
  217. web_client_reset_allocations(w, false);
  218. w->mode = HTTP_REQUEST_MODE_GET;
  219. web_client_disable_donottrack(w);
  220. web_client_disable_tracking_required(w);
  221. web_client_disable_keepalive(w);
  222. w->header_parse_tries = 0;
  223. w->header_parse_last_size = 0;
  224. web_client_enable_wait_receive(w);
  225. web_client_disable_wait_send(w);
  226. w->response.has_cookies = false;
  227. w->response.rlen = 0;
  228. w->response.sent = 0;
  229. w->response.code = 0;
  230. w->response.zoutput = false;
  231. w->statistics.received_bytes = 0;
  232. w->statistics.sent_bytes = 0;
  233. }
  234. static int append_slash_to_url_and_redirect(struct web_client *w) {
  235. // this function returns a relative redirect
  236. // it finds the last path component on the URL and just appends / to it
  237. //
  238. // So, if the URL is:
  239. //
  240. // /path/to/file?query_string
  241. //
  242. // It adds a Location header like this:
  243. //
  244. // Location: file/?query_string\r\n
  245. //
  246. // The web browser already knows that it is inside /path/to/
  247. // so it converts the path to /path/to/file/ and executes the
  248. // request again.
  249. buffer_strcat(w->response.header, "Location: ");
  250. const char *b = buffer_tostring(w->url_as_received);
  251. const char *q = strchr(b, '?');
  252. if(q && q > b) {
  253. const char *e = q - 1;
  254. while(e > b && *e != '/') e--;
  255. if(*e == '/') e++;
  256. size_t len = q - e;
  257. buffer_strncat(w->response.header, e, len);
  258. buffer_strncat(w->response.header, "/", 1);
  259. buffer_strcat(w->response.header, q);
  260. }
  261. else {
  262. const char *e = &b[buffer_strlen(w->url_as_received) - 1];
  263. while(e > b && *e != '/') e--;
  264. if(*e == '/') e++;
  265. buffer_strcat(w->response.header, e);
  266. buffer_strncat(w->response.header, "/", 1);
  267. }
  268. buffer_strncat(w->response.header, "\r\n", 2);
  269. w->response.data->content_type = CT_TEXT_HTML;
  270. buffer_flush(w->response.data);
  271. buffer_strcat(w->response.data,
  272. "<!DOCTYPE html><html>"
  273. "<body onload=\"window.location.href = window.location.origin + window.location.pathname + '/' + window.location.search + window.location.hash\">"
  274. "Redirecting. In case your browser does not support redirection, please click "
  275. "<a onclick=\"window.location.href = window.location.origin + window.location.pathname + '/' + window.location.search + window.location.hash\">here</a>."
  276. "</body></html>");
  277. return HTTP_RESP_MOVED_PERM;
  278. }
  279. // Work around a bug in the CMocka library by removing this function during testing.
  280. #ifndef REMOVE_MYSENDFILE
  281. static inline int dashboard_version(struct web_client *w) {
  282. if(!web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
  283. return -1;
  284. if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_IS_V0))
  285. return 0;
  286. if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_IS_V1))
  287. return 1;
  288. if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_IS_V2))
  289. return 2;
  290. return -1;
  291. }
  292. static bool find_filename_to_serve(const char *filename, char *dst, size_t dst_len, struct stat *statbuf, struct web_client *w, bool *is_dir) {
  293. int d_version = dashboard_version(w);
  294. bool has_extension = web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION);
  295. int fallback = 0;
  296. if(has_extension) {
  297. if(d_version == -1)
  298. snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
  299. else {
  300. // check if the filename or directory exists
  301. // fallback to the same path without the dashboard version otherwise
  302. snprintfz(dst, dst_len, "%s/v%d/%s", netdata_configured_web_dir, d_version, filename);
  303. fallback = 1;
  304. }
  305. }
  306. else if(d_version != -1) {
  307. if(filename && *filename) {
  308. // check if the filename exists
  309. // fallback to /vN/index.html otherwise
  310. snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
  311. fallback = 2;
  312. }
  313. else {
  314. if(filename && *filename)
  315. web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
  316. snprintfz(dst, dst_len, "%s/v%d", netdata_configured_web_dir, d_version);
  317. }
  318. }
  319. else {
  320. // check if filename exists
  321. // this is needed to serve {filename}/index.html, in case a user puts a html file into a directory
  322. // fallback to /index.html otherwise
  323. snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
  324. fallback = 3;
  325. }
  326. if (stat(dst, statbuf) != 0) {
  327. if(fallback == 1) {
  328. snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
  329. if (stat(dst, statbuf) != 0)
  330. return false;
  331. }
  332. else if(fallback == 2) {
  333. if(filename && *filename)
  334. web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
  335. snprintfz(dst, dst_len, "%s/v%d", netdata_configured_web_dir, d_version);
  336. if (stat(dst, statbuf) != 0)
  337. return false;
  338. }
  339. else if(fallback == 3) {
  340. if(filename && *filename)
  341. web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
  342. snprintfz(dst, dst_len, "%s", netdata_configured_web_dir);
  343. if (stat(dst, statbuf) != 0)
  344. return false;
  345. }
  346. else
  347. return false;
  348. }
  349. if((statbuf->st_mode & S_IFMT) == S_IFDIR) {
  350. size_t len = strlen(dst);
  351. if(len > dst_len - 11)
  352. return false;
  353. strncpyz(&dst[len], "/index.html", dst_len - len);
  354. if (stat(dst, statbuf) != 0)
  355. return false;
  356. *is_dir = true;
  357. }
  358. return true;
  359. }
  360. static int mysendfile(struct web_client *w, char *filename) {
  361. netdata_log_debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
  362. if(!http_can_access_dashboard(w))
  363. return web_client_permission_denied(w);
  364. // skip leading slashes
  365. while (*filename == '/') filename++;
  366. // if the filename contains "strange" characters, refuse to serve it
  367. char *s;
  368. for(s = filename; *s ;s++) {
  369. if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
  370. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
  371. w->response.data->content_type = CT_TEXT_HTML;
  372. buffer_sprintf(w->response.data, "Filename contains invalid characters: ");
  373. buffer_strcat_htmlescape(w->response.data, filename);
  374. return HTTP_RESP_BAD_REQUEST;
  375. }
  376. }
  377. // if the filename contains a double dot refuse to serve it
  378. if(strstr(filename, "..") != 0) {
  379. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
  380. w->response.data->content_type = CT_TEXT_HTML;
  381. buffer_strcat(w->response.data, "Relative filenames are not supported: ");
  382. buffer_strcat_htmlescape(w->response.data, filename);
  383. return HTTP_RESP_BAD_REQUEST;
  384. }
  385. // find the physical file on disk
  386. bool is_dir = false;
  387. char web_filename[FILENAME_MAX + 1];
  388. struct stat statbuf;
  389. if(!find_filename_to_serve(filename, web_filename, FILENAME_MAX, &statbuf, w, &is_dir)) {
  390. w->response.data->content_type = CT_TEXT_HTML;
  391. buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
  392. buffer_strcat_htmlescape(w->response.data, web_filename);
  393. return HTTP_RESP_NOT_FOUND;
  394. }
  395. if(is_dir && !web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH))
  396. return append_slash_to_url_and_redirect(w);
  397. // open the file
  398. w->ifd = open(web_filename, O_NONBLOCK, O_RDONLY);
  399. if(w->ifd == -1) {
  400. w->ifd = w->ofd;
  401. if(errno == EBUSY || errno == EAGAIN) {
  402. netdata_log_error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, web_filename);
  403. w->response.data->content_type = CT_TEXT_HTML;
  404. buffer_sprintf(w->response.header, "Location: /%s\r\n", filename);
  405. buffer_strcat(w->response.data, "File is currently busy, please try again later: ");
  406. buffer_strcat_htmlescape(w->response.data, web_filename);
  407. return HTTP_RESP_REDIR_TEMP;
  408. }
  409. else {
  410. netdata_log_error("%llu: Cannot open file '%s'.", w->id, web_filename);
  411. w->response.data->content_type = CT_TEXT_HTML;
  412. buffer_strcat(w->response.data, "Cannot open file: ");
  413. buffer_strcat_htmlescape(w->response.data, web_filename);
  414. return HTTP_RESP_NOT_FOUND;
  415. }
  416. }
  417. sock_setnonblock(w->ifd);
  418. w->response.data->content_type = contenttype_for_filename(web_filename);
  419. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, web_filename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
  420. w->mode = HTTP_REQUEST_MODE_FILECOPY;
  421. web_client_enable_wait_receive(w);
  422. web_client_disable_wait_send(w);
  423. buffer_flush(w->response.data);
  424. buffer_need_bytes(w->response.data, (size_t)statbuf.st_size);
  425. w->response.rlen = (size_t)statbuf.st_size;
  426. #ifdef __APPLE__
  427. w->response.data->date = statbuf.st_mtimespec.tv_sec;
  428. #else
  429. w->response.data->date = statbuf.st_mtim.tv_sec;
  430. #endif
  431. buffer_cacheable(w->response.data);
  432. return HTTP_RESP_OK;
  433. }
  434. #endif
  435. void buffer_data_options2string(BUFFER *wb, uint32_t options) {
  436. int count = 0;
  437. if(options & RRDR_OPTION_NONZERO) {
  438. if(count++) buffer_strcat(wb, " ");
  439. buffer_strcat(wb, "nonzero");
  440. }
  441. if(options & RRDR_OPTION_REVERSED) {
  442. if(count++) buffer_strcat(wb, " ");
  443. buffer_strcat(wb, "flip");
  444. }
  445. if(options & RRDR_OPTION_JSON_WRAP) {
  446. if(count++) buffer_strcat(wb, " ");
  447. buffer_strcat(wb, "jsonwrap");
  448. }
  449. if(options & RRDR_OPTION_MIN2MAX) {
  450. if(count++) buffer_strcat(wb, " ");
  451. buffer_strcat(wb, "min2max");
  452. }
  453. if(options & RRDR_OPTION_MILLISECONDS) {
  454. if(count++) buffer_strcat(wb, " ");
  455. buffer_strcat(wb, "ms");
  456. }
  457. if(options & RRDR_OPTION_ABSOLUTE) {
  458. if(count++) buffer_strcat(wb, " ");
  459. buffer_strcat(wb, "absolute");
  460. }
  461. if(options & RRDR_OPTION_SECONDS) {
  462. if(count++) buffer_strcat(wb, " ");
  463. buffer_strcat(wb, "seconds");
  464. }
  465. if(options & RRDR_OPTION_NULL2ZERO) {
  466. if(count++) buffer_strcat(wb, " ");
  467. buffer_strcat(wb, "null2zero");
  468. }
  469. if(options & RRDR_OPTION_OBJECTSROWS) {
  470. if(count++) buffer_strcat(wb, " ");
  471. buffer_strcat(wb, "objectrows");
  472. }
  473. if(options & RRDR_OPTION_GOOGLE_JSON) {
  474. if(count++) buffer_strcat(wb, " ");
  475. buffer_strcat(wb, "google_json");
  476. }
  477. if(options & RRDR_OPTION_PERCENTAGE) {
  478. if(count++) buffer_strcat(wb, " ");
  479. buffer_strcat(wb, "percentage");
  480. }
  481. if(options & RRDR_OPTION_NOT_ALIGNED) {
  482. if(count++) buffer_strcat(wb, " ");
  483. buffer_strcat(wb, "unaligned");
  484. }
  485. if(options & RRDR_OPTION_ANOMALY_BIT) {
  486. if(count++) buffer_strcat(wb, " ");
  487. buffer_strcat(wb, "anomaly-bit");
  488. }
  489. }
  490. static inline int check_host_and_call(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) {
  491. //if(unlikely(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) {
  492. // buffer_flush(w->response.data);
  493. // buffer_strcat(w->response.data, "This host does not maintain a database");
  494. // return HTTP_RESP_BAD_REQUEST;
  495. //}
  496. return func(host, w, url);
  497. }
  498. static inline int UNUSED_FUNCTION(check_host_and_dashboard_acl_and_call)(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) {
  499. if(!http_can_access_dashboard(w))
  500. return web_client_permission_denied(w);
  501. return check_host_and_call(host, w, url, func);
  502. }
  503. static inline int UNUSED_FUNCTION(check_host_and_mgmt_acl_and_call)(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) {
  504. if(!http_can_access_mgmt(w))
  505. return web_client_permission_denied(w);
  506. return check_host_and_call(host, w, url, func);
  507. }
  508. int web_client_api_request(RRDHOST *host, struct web_client *w, char *url_path_fragment) {
  509. ND_LOG_STACK lgs[] = {
  510. ND_LOG_FIELD_TXT(NDF_SRC_IP, w->client_ip),
  511. ND_LOG_FIELD_TXT(NDF_SRC_PORT, w->client_port),
  512. ND_LOG_FIELD_TXT(NDF_SRC_FORWARDED_HOST, w->forwarded_host),
  513. ND_LOG_FIELD_TXT(NDF_SRC_FORWARDED_FOR, w->forwarded_for),
  514. ND_LOG_FIELD_TXT(NDF_NIDL_NODE, w->client_host),
  515. ND_LOG_FIELD_TXT(NDF_REQUEST_METHOD, http_request_method2string(w->mode)),
  516. ND_LOG_FIELD_BFR(NDF_REQUEST, w->url_as_received),
  517. ND_LOG_FIELD_U64(NDF_CONNECTION_ID, w->id),
  518. ND_LOG_FIELD_UUID(NDF_TRANSACTION_ID, &w->transaction),
  519. ND_LOG_FIELD_UUID(NDF_ACCOUNT_ID, &w->auth.cloud_account_id),
  520. ND_LOG_FIELD_TXT(NDF_USER_NAME, w->auth.client_name),
  521. ND_LOG_FIELD_TXT(NDF_USER_ROLE, http_id2access(w->access)),
  522. ND_LOG_FIELD_END(),
  523. };
  524. ND_LOG_STACK_PUSH(lgs);
  525. if(!web_client_flag_check(w, WEB_CLIENT_FLAG_PROGRESS_TRACKING)) {
  526. web_client_flag_set(w, WEB_CLIENT_FLAG_PROGRESS_TRACKING);
  527. query_progress_start_or_update(&w->transaction, 0, w->mode, w->acl,
  528. buffer_tostring(w->url_as_received),
  529. w->payload,
  530. w->forwarded_for ? w->forwarded_for : w->client_ip);
  531. }
  532. // get the api version
  533. char *tok = strsep_skip_consecutive_separators(&url_path_fragment, "/");
  534. if(tok && *tok) {
  535. if(strcmp(tok, "v2") == 0)
  536. return web_client_api_request_v2(host, w, url_path_fragment);
  537. else if(strcmp(tok, "v1") == 0)
  538. return web_client_api_request_v1(host, w, url_path_fragment);
  539. else {
  540. buffer_flush(w->response.data);
  541. w->response.data->content_type = CT_TEXT_HTML;
  542. buffer_strcat(w->response.data, "Unsupported API version: ");
  543. buffer_strcat_htmlescape(w->response.data, tok);
  544. return HTTP_RESP_NOT_FOUND;
  545. }
  546. }
  547. else {
  548. buffer_flush(w->response.data);
  549. buffer_sprintf(w->response.data, "Which API version?");
  550. return HTTP_RESP_BAD_REQUEST;
  551. }
  552. }
  553. /**
  554. * Valid Method
  555. *
  556. * Netdata accepts only three methods, including one of these three(STREAM) is an internal method.
  557. *
  558. * @param w is the structure with the client request
  559. * @param s is the start string to parse
  560. *
  561. * @return it returns the next address to parse case the method is valid and NULL otherwise.
  562. */
  563. static inline char *web_client_valid_method(struct web_client *w, char *s) {
  564. // is is a valid request?
  565. if(!strncmp(s, "GET ", 4)) {
  566. s = &s[4];
  567. w->mode = HTTP_REQUEST_MODE_GET;
  568. }
  569. else if(!strncmp(s, "OPTIONS ", 8)) {
  570. s = &s[8];
  571. w->mode = HTTP_REQUEST_MODE_OPTIONS;
  572. }
  573. else if(!strncmp(s, "POST ", 5)) {
  574. s = &s[5];
  575. w->mode = HTTP_REQUEST_MODE_POST;
  576. }
  577. else if(!strncmp(s, "PUT ", 4)) {
  578. s = &s[4];
  579. w->mode = HTTP_REQUEST_MODE_PUT;
  580. }
  581. else if(!strncmp(s, "DELETE ", 7)) {
  582. s = &s[7];
  583. w->mode = HTTP_REQUEST_MODE_DELETE;
  584. }
  585. else if(!strncmp(s, "STREAM ", 7)) {
  586. s = &s[7];
  587. #ifdef ENABLE_HTTPS
  588. if (!SSL_connection(&w->ssl) && http_is_using_ssl_force(w)) {
  589. w->header_parse_tries = 0;
  590. w->header_parse_last_size = 0;
  591. web_client_disable_wait_receive(w);
  592. char hostname[256];
  593. char *copyme = strstr(s,"hostname=");
  594. if ( copyme ){
  595. copyme += 9;
  596. char *end = strchr(copyme,'&');
  597. if(end){
  598. size_t length = MIN(255, end - copyme);
  599. memcpy(hostname,copyme,length);
  600. hostname[length] = 0X00;
  601. }
  602. else{
  603. memcpy(hostname,"not available",13);
  604. hostname[13] = 0x00;
  605. }
  606. }
  607. else{
  608. memcpy(hostname,"not available",13);
  609. hostname[13] = 0x00;
  610. }
  611. netdata_log_error("The server is configured to always use encrypted connections, please enable the SSL on child with hostname '%s'.",hostname);
  612. s = NULL;
  613. }
  614. #endif
  615. w->mode = HTTP_REQUEST_MODE_STREAM;
  616. }
  617. else {
  618. s = NULL;
  619. }
  620. return s;
  621. }
  622. /**
  623. * Request validate
  624. *
  625. * @param w is the structure with the client request
  626. *
  627. * @return It returns HTTP_VALIDATION_OK on success and another code present
  628. * in the enum HTTP_VALIDATION otherwise.
  629. */
  630. HTTP_VALIDATION http_request_validate(struct web_client *w) {
  631. char *s = (char *)buffer_tostring(w->response.data), *encoded_url = NULL;
  632. size_t last_pos = w->header_parse_last_size;
  633. w->header_parse_tries++;
  634. w->header_parse_last_size = buffer_strlen(w->response.data);
  635. int is_it_valid;
  636. if(w->header_parse_tries > 1) {
  637. if(last_pos > 4) last_pos -= 4; // allow searching for \r\n\r\n
  638. else last_pos = 0;
  639. if(w->header_parse_last_size < last_pos)
  640. last_pos = 0;
  641. is_it_valid =
  642. url_is_request_complete_and_extract_payload(s, &s[last_pos], w->header_parse_last_size, &w->payload);
  643. if(!is_it_valid) {
  644. if(w->header_parse_tries > HTTP_REQ_MAX_HEADER_FETCH_TRIES) {
  645. netdata_log_info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
  646. w->header_parse_tries = 0;
  647. w->header_parse_last_size = 0;
  648. web_client_disable_wait_receive(w);
  649. return HTTP_VALIDATION_TOO_MANY_READ_RETRIES;
  650. }
  651. return HTTP_VALIDATION_INCOMPLETE;
  652. }
  653. is_it_valid = 1;
  654. } else {
  655. last_pos = w->header_parse_last_size;
  656. is_it_valid =
  657. url_is_request_complete_and_extract_payload(s, &s[last_pos], w->header_parse_last_size, &w->payload);
  658. }
  659. s = web_client_valid_method(w, s);
  660. if (!s) {
  661. w->header_parse_tries = 0;
  662. w->header_parse_last_size = 0;
  663. web_client_disable_wait_receive(w);
  664. return HTTP_VALIDATION_NOT_SUPPORTED;
  665. } else if (!is_it_valid) {
  666. //Invalid request, we have more data after the end of message
  667. char *check = strstr((char *)buffer_tostring(w->response.data), "\r\n\r\n");
  668. if(check) {
  669. check += 4;
  670. if (*check) {
  671. w->header_parse_tries = 0;
  672. w->header_parse_last_size = 0;
  673. web_client_disable_wait_receive(w);
  674. return HTTP_VALIDATION_EXCESS_REQUEST_DATA;
  675. }
  676. }
  677. web_client_enable_wait_receive(w);
  678. return HTTP_VALIDATION_INCOMPLETE;
  679. }
  680. //After the method we have the path and query string together
  681. encoded_url = s;
  682. //we search for the position where we have " HTTP/", because it finishes the user request
  683. s = url_find_protocol(s);
  684. // incomplete requests
  685. if(unlikely(!*s)) {
  686. web_client_enable_wait_receive(w);
  687. return HTTP_VALIDATION_INCOMPLETE;
  688. }
  689. // we have the end of encoded_url - remember it
  690. char *ue = s;
  691. // make sure we have complete request
  692. // complete requests contain: \r\n\r\n
  693. while(*s) {
  694. // find a line feed
  695. while(*s && *s++ != '\r');
  696. // did we reach the end?
  697. if(unlikely(!*s)) break;
  698. // is it \r\n ?
  699. if(likely(*s++ == '\n')) {
  700. // is it again \r\n ? (header end)
  701. if(unlikely(*s == '\r' && s[1] == '\n')) {
  702. // a valid complete HTTP request found
  703. char c = *ue;
  704. *ue = '\0';
  705. web_client_decode_path_and_query_string(w, encoded_url);
  706. *ue = c;
  707. #ifdef ENABLE_HTTPS
  708. if ( (web_client_check_conn_tcp(w)) && (netdata_ssl_web_server_ctx) ) {
  709. if (!w->ssl.conn && (http_is_using_ssl_force(w) || http_is_using_ssl_default(w)) && (w->mode != HTTP_REQUEST_MODE_STREAM)) {
  710. w->header_parse_tries = 0;
  711. w->header_parse_last_size = 0;
  712. web_client_disable_wait_receive(w);
  713. return HTTP_VALIDATION_REDIRECT;
  714. }
  715. }
  716. #endif
  717. w->header_parse_tries = 0;
  718. w->header_parse_last_size = 0;
  719. web_client_disable_wait_receive(w);
  720. return HTTP_VALIDATION_OK;
  721. }
  722. // another header line
  723. s = http_header_parse_line(w, s);
  724. }
  725. }
  726. // incomplete request
  727. web_client_enable_wait_receive(w);
  728. return HTTP_VALIDATION_INCOMPLETE;
  729. }
  730. static inline ssize_t web_client_send_data(struct web_client *w,const void *buf,size_t len, int flags)
  731. {
  732. ssize_t bytes;
  733. #ifdef ENABLE_HTTPS
  734. if ((web_client_check_conn_tcp(w)) && (netdata_ssl_web_server_ctx)) {
  735. if (SSL_connection(&w->ssl)) {
  736. bytes = netdata_ssl_write(&w->ssl, buf, len) ;
  737. web_client_enable_wait_from_ssl(w);
  738. }
  739. else
  740. bytes = send(w->ofd,buf, len , flags);
  741. }
  742. else if(web_client_check_conn_tcp(w) || web_client_check_conn_unix(w))
  743. bytes = send(w->ofd,buf, len , flags);
  744. else
  745. bytes = -999;
  746. #else
  747. if(web_client_check_conn_tcp(w) || web_client_check_conn_unix(w))
  748. bytes = send(w->ofd, buf, len, flags);
  749. else
  750. bytes = -999;
  751. #endif
  752. return bytes;
  753. }
  754. void web_client_build_http_header(struct web_client *w) {
  755. if(unlikely(w->response.code != HTTP_RESP_OK))
  756. buffer_no_cacheable(w->response.data);
  757. if(unlikely(!w->response.data->date))
  758. w->response.data->date = now_realtime_sec();
  759. // set a proper expiration date, if not already set
  760. if(unlikely(!w->response.data->expires))
  761. w->response.data->expires = w->response.data->date +
  762. ((w->response.data->options & WB_CONTENT_NO_CACHEABLE) ? 0 : 86400);
  763. // prepare the HTTP response header
  764. netdata_log_debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, w->response.code);
  765. const char *code_msg = http_response_code2string(w->response.code);
  766. // prepare the last modified and expiration dates
  767. char rfc7231_date[RFC7231_MAX_LENGTH], rfc7231_expires[RFC7231_MAX_LENGTH];
  768. rfc7231_datetime(rfc7231_date, sizeof(rfc7231_date), w->response.data->date);
  769. rfc7231_datetime(rfc7231_expires, sizeof(rfc7231_expires), w->response.data->expires);
  770. if (w->response.code == HTTP_RESP_HTTPS_UPGRADE) {
  771. buffer_sprintf(w->response.header_output,
  772. "HTTP/1.1 %d %s\r\n"
  773. "Location: https://%s%s\r\n",
  774. w->response.code, code_msg,
  775. w->server_host ? w->server_host : "",
  776. buffer_tostring(w->url_as_received));
  777. w->response.code = HTTP_RESP_MOVED_PERM;
  778. }
  779. else {
  780. buffer_sprintf(w->response.header_output,
  781. "HTTP/1.1 %d %s\r\n"
  782. "Connection: %s\r\n"
  783. "Server: Netdata Embedded HTTP Server %s\r\n"
  784. "Access-Control-Allow-Origin: %s\r\n"
  785. "Access-Control-Allow-Credentials: true\r\n"
  786. "Date: %s\r\n",
  787. w->response.code,
  788. code_msg,
  789. web_client_has_keepalive(w)?"keep-alive":"close",
  790. VERSION,
  791. w->origin ? w->origin : "*",
  792. rfc7231_date);
  793. http_header_content_type(w->response.header_output, w->response.data->content_type);
  794. }
  795. if(unlikely(web_x_frame_options))
  796. buffer_sprintf(w->response.header_output, "X-Frame-Options: %s\r\n", web_x_frame_options);
  797. if(w->response.has_cookies) {
  798. if(respect_web_browser_do_not_track_policy)
  799. buffer_sprintf(w->response.header_output,
  800. "Tk: T;cookies\r\n");
  801. }
  802. else {
  803. if(respect_web_browser_do_not_track_policy) {
  804. if(web_client_has_tracking_required(w))
  805. buffer_sprintf(w->response.header_output,
  806. "Tk: T;cookies\r\n");
  807. else
  808. buffer_sprintf(w->response.header_output,
  809. "Tk: N\r\n");
  810. }
  811. }
  812. if(w->mode == HTTP_REQUEST_MODE_OPTIONS) {
  813. buffer_strcat(w->response.header_output,
  814. "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
  815. "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control, x-auth-token\r\n"
  816. "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
  817. );
  818. }
  819. else {
  820. buffer_sprintf(w->response.header_output,
  821. "Cache-Control: %s\r\n"
  822. "Expires: %s\r\n",
  823. (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache, no-store, must-revalidate\r\nPragma: no-cache":"public",
  824. rfc7231_expires);
  825. }
  826. // copy a possibly available custom header
  827. if(unlikely(buffer_strlen(w->response.header)))
  828. buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
  829. // headers related to the transfer method
  830. if(likely(w->response.zoutput))
  831. buffer_strcat(w->response.header_output, "Content-Encoding: gzip\r\n");
  832. if(likely(w->flags & WEB_CLIENT_CHUNKED_TRANSFER))
  833. buffer_strcat(w->response.header_output, "Transfer-Encoding: chunked\r\n");
  834. else {
  835. if(likely((w->response.data->len || w->response.rlen))) {
  836. // we know the content length, put it
  837. buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
  838. }
  839. else {
  840. // we don't know the content length, disable keep-alive
  841. web_client_disable_keepalive(w);
  842. }
  843. }
  844. char uuid[UUID_COMPACT_STR_LEN];
  845. uuid_unparse_lower_compact(w->transaction, uuid);
  846. buffer_sprintf(w->response.header_output,
  847. "X-Transaction-ID: %s\r\n", uuid);
  848. // end of HTTP header
  849. buffer_strcat(w->response.header_output, "\r\n");
  850. }
  851. static inline void web_client_send_http_header(struct web_client *w) {
  852. web_client_build_http_header(w);
  853. // sent the HTTP header
  854. netdata_log_debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
  855. , w->id
  856. , buffer_strlen(w->response.header_output)
  857. , buffer_tostring(w->response.header_output)
  858. );
  859. web_client_cork_socket(w);
  860. size_t count = 0;
  861. ssize_t bytes;
  862. #ifdef ENABLE_HTTPS
  863. if ( (web_client_check_conn_tcp(w)) && (netdata_ssl_web_server_ctx) ) {
  864. if (SSL_connection(&w->ssl)) {
  865. bytes = netdata_ssl_write(&w->ssl, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output));
  866. web_client_enable_wait_from_ssl(w);
  867. }
  868. else {
  869. while((bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
  870. count++;
  871. if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
  872. netdata_log_error("Cannot send HTTP headers to web client.");
  873. break;
  874. }
  875. }
  876. }
  877. }
  878. else if(web_client_check_conn_tcp(w) || web_client_check_conn_unix(w)) {
  879. while((bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
  880. count++;
  881. if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
  882. netdata_log_error("Cannot send HTTP headers to web client.");
  883. break;
  884. }
  885. }
  886. }
  887. else
  888. bytes = -999;
  889. #else
  890. if(web_client_check_conn_tcp(w) || web_client_check_conn_unix(w)) {
  891. while ((bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
  892. count++;
  893. if (count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
  894. netdata_log_error("Cannot send HTTP headers to web client.");
  895. break;
  896. }
  897. }
  898. }
  899. else
  900. bytes = -999;
  901. #endif
  902. if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
  903. if(bytes > 0)
  904. w->statistics.sent_bytes += bytes;
  905. if (bytes < 0) {
  906. netdata_log_error("HTTP headers failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
  907. , buffer_strlen(w->response.header_output)
  908. , bytes);
  909. WEB_CLIENT_IS_DEAD(w);
  910. return;
  911. }
  912. }
  913. else
  914. w->statistics.sent_bytes += bytes;
  915. }
  916. static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, char *url, bool nodeid, int (*func)(RRDHOST *, struct web_client *, char *)) {
  917. static uint32_t hash_localhost = 0;
  918. if(unlikely(!hash_localhost)) {
  919. hash_localhost = simple_hash("localhost");
  920. }
  921. if(host != localhost) {
  922. buffer_flush(w->response.data);
  923. buffer_strcat(w->response.data, "Nesting of hosts is not allowed.");
  924. return HTTP_RESP_BAD_REQUEST;
  925. }
  926. char *tok = strsep_skip_consecutive_separators(&url, "/");
  927. if(tok && *tok) {
  928. netdata_log_debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok);
  929. if(nodeid) {
  930. host = find_host_by_node_id(tok);
  931. if(!host) {
  932. host = rrdhost_find_by_hostname(tok);
  933. if (!host)
  934. host = rrdhost_find_by_guid(tok);
  935. }
  936. }
  937. else {
  938. host = rrdhost_find_by_hostname(tok);
  939. if(!host) {
  940. host = rrdhost_find_by_guid(tok);
  941. if (!host)
  942. host = find_host_by_node_id(tok);
  943. }
  944. }
  945. if(!host) {
  946. // we didn't find it, but it may be a uuid case mismatch for MACHINE_GUID
  947. // so, recreate the machine guid in lower-case.
  948. uuid_t uuid;
  949. char txt[UUID_STR_LEN];
  950. if (uuid_parse(tok, uuid) == 0) {
  951. uuid_unparse_lower(uuid, txt);
  952. host = rrdhost_find_by_guid(txt);
  953. }
  954. }
  955. if (host) {
  956. if(!url)
  957. //no delim found
  958. return append_slash_to_url_and_redirect(w);
  959. size_t len = strlen(url) + 2;
  960. char buf[len];
  961. buf[0] = '/';
  962. strcpy(&buf[1], url);
  963. buf[len - 1] = '\0';
  964. buffer_flush(w->url_path_decoded);
  965. buffer_strcat(w->url_path_decoded, buf);
  966. return func(host, w, buf);
  967. }
  968. }
  969. buffer_flush(w->response.data);
  970. w->response.data->content_type = CT_TEXT_HTML;
  971. buffer_strcat(w->response.data, "This netdata does not maintain a database for host: ");
  972. buffer_strcat_htmlescape(w->response.data, tok?tok:"");
  973. return HTTP_RESP_NOT_FOUND;
  974. }
  975. int web_client_api_request_with_node_selection(RRDHOST *host, struct web_client *w, char *decoded_url_path) {
  976. // entry point for all API requests
  977. ND_LOG_STACK lgs[] = {
  978. ND_LOG_FIELD_TXT(NDF_REQUEST_METHOD, http_request_method2string(w->mode)),
  979. ND_LOG_FIELD_BFR(NDF_REQUEST, w->url_as_received),
  980. ND_LOG_FIELD_U64(NDF_CONNECTION_ID, w->id),
  981. ND_LOG_FIELD_UUID(NDF_TRANSACTION_ID, &w->transaction),
  982. ND_LOG_FIELD_UUID(NDF_ACCOUNT_ID, &w->auth.cloud_account_id),
  983. ND_LOG_FIELD_TXT(NDF_USER_NAME, w->auth.client_name),
  984. ND_LOG_FIELD_TXT(NDF_USER_ROLE, http_id2access(w->access)),
  985. ND_LOG_FIELD_END(),
  986. };
  987. ND_LOG_STACK_PUSH(lgs);
  988. // give a new transaction id to the request
  989. if(uuid_is_null(w->transaction))
  990. uuid_generate_random(w->transaction);
  991. static uint32_t
  992. hash_api = 0,
  993. hash_host = 0,
  994. hash_node = 0;
  995. if(unlikely(!hash_api)) {
  996. hash_api = simple_hash("api");
  997. hash_host = simple_hash("host");
  998. hash_node = simple_hash("node");
  999. }
  1000. char *tok = strsep_skip_consecutive_separators(&decoded_url_path, "/?");
  1001. if(likely(tok && *tok)) {
  1002. uint32_t hash = simple_hash(tok);
  1003. if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) {
  1004. // current API
  1005. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
  1006. return check_host_and_call(host, w, decoded_url_path, web_client_api_request);
  1007. }
  1008. else if(unlikely((hash == hash_host && strcmp(tok, "host") == 0) || (hash == hash_node && strcmp(tok, "node") == 0))) {
  1009. // host switching
  1010. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
  1011. return web_client_switch_host(host, w, decoded_url_path, hash == hash_node, web_client_api_request_with_node_selection);
  1012. }
  1013. }
  1014. buffer_flush(w->response.data);
  1015. buffer_strcat(w->response.data, "Unknown API endpoint.");
  1016. w->response.data->content_type = CT_TEXT_HTML;
  1017. return HTTP_RESP_NOT_FOUND;
  1018. }
  1019. static inline int web_client_process_url(RRDHOST *host, struct web_client *w, char *decoded_url_path) {
  1020. if(unlikely(!service_running(ABILITY_WEB_REQUESTS)))
  1021. return web_client_permission_denied(w);
  1022. static uint32_t
  1023. hash_api = 0,
  1024. hash_netdata_conf = 0,
  1025. hash_host = 0,
  1026. hash_node = 0,
  1027. hash_v0 = 0,
  1028. hash_v1 = 0,
  1029. hash_v2 = 0;
  1030. #ifdef NETDATA_INTERNAL_CHECKS
  1031. static uint32_t hash_exit = 0, hash_debug = 0, hash_mirror = 0;
  1032. #endif
  1033. if(unlikely(!hash_api)) {
  1034. hash_api = simple_hash("api");
  1035. hash_netdata_conf = simple_hash("netdata.conf");
  1036. hash_host = simple_hash("host");
  1037. hash_node = simple_hash("node");
  1038. hash_v0 = simple_hash("v0");
  1039. hash_v1 = simple_hash("v1");
  1040. hash_v2 = simple_hash("v2");
  1041. #ifdef NETDATA_INTERNAL_CHECKS
  1042. hash_exit = simple_hash("exit");
  1043. hash_debug = simple_hash("debug");
  1044. hash_mirror = simple_hash("mirror");
  1045. #endif
  1046. }
  1047. // keep a copy of the decoded path, in case we need to serve it as a filename
  1048. char filename[FILENAME_MAX + 1];
  1049. strncpyz(filename, decoded_url_path ? decoded_url_path : "", FILENAME_MAX);
  1050. char *tok = strsep_skip_consecutive_separators(&decoded_url_path, "/?");
  1051. if(likely(tok && *tok)) {
  1052. uint32_t hash = simple_hash(tok);
  1053. netdata_log_debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
  1054. if(likely(hash == hash_api && strcmp(tok, "api") == 0)) { // current API
  1055. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
  1056. return check_host_and_call(host, w, decoded_url_path, web_client_api_request);
  1057. }
  1058. else if(unlikely((hash == hash_host && strcmp(tok, "host") == 0) || (hash == hash_node && strcmp(tok, "node") == 0))) { // host switching
  1059. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
  1060. return web_client_switch_host(host, w, decoded_url_path, hash == hash_node, web_client_process_url);
  1061. }
  1062. else if(unlikely(hash == hash_v2 && strcmp(tok, "v2") == 0)) {
  1063. if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
  1064. return bad_request_multiple_dashboard_versions(w);
  1065. web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_IS_V2);
  1066. return web_client_process_url(host, w, decoded_url_path);
  1067. }
  1068. else if(unlikely(hash == hash_v1 && strcmp(tok, "v1") == 0)) {
  1069. if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
  1070. return bad_request_multiple_dashboard_versions(w);
  1071. web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_IS_V1);
  1072. return web_client_process_url(host, w, decoded_url_path);
  1073. }
  1074. else if(unlikely(hash == hash_v0 && strcmp(tok, "v0") == 0)) {
  1075. if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
  1076. return bad_request_multiple_dashboard_versions(w);
  1077. web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_IS_V0);
  1078. return web_client_process_url(host, w, decoded_url_path);
  1079. }
  1080. else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) { // netdata.conf
  1081. if(unlikely(!http_can_access_netdataconf(w)))
  1082. return web_client_permission_denied(w);
  1083. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
  1084. w->response.data->content_type = CT_TEXT_PLAIN;
  1085. buffer_flush(w->response.data);
  1086. config_generate(w->response.data, 0);
  1087. return HTTP_RESP_OK;
  1088. }
  1089. #ifdef NETDATA_INTERNAL_CHECKS
  1090. else if(unlikely(hash == hash_exit && strcmp(tok, "exit") == 0)) {
  1091. if(unlikely(!http_can_access_netdataconf(w)))
  1092. return web_client_permission_denied(w);
  1093. w->response.data->content_type = CT_TEXT_PLAIN;
  1094. buffer_flush(w->response.data);
  1095. if(!netdata_exit)
  1096. buffer_strcat(w->response.data, "ok, will do...");
  1097. else
  1098. buffer_strcat(w->response.data, "I am doing it already");
  1099. netdata_log_error("web request to exit received.");
  1100. netdata_cleanup_and_exit(0, NULL, NULL, NULL);
  1101. return HTTP_RESP_OK;
  1102. }
  1103. else if(unlikely(hash == hash_debug && strcmp(tok, "debug") == 0)) {
  1104. if(unlikely(!http_can_access_netdataconf(w)))
  1105. return web_client_permission_denied(w);
  1106. buffer_flush(w->response.data);
  1107. // get the name of the data to show
  1108. tok = strsep_skip_consecutive_separators(&decoded_url_path, "&");
  1109. if(tok && *tok) {
  1110. netdata_log_debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
  1111. // do we have such a data set?
  1112. RRDSET *st = rrdset_find_byname(host, tok);
  1113. if(!st) st = rrdset_find(host, tok);
  1114. if(!st) {
  1115. w->response.data->content_type = CT_TEXT_HTML;
  1116. buffer_strcat(w->response.data, "Chart is not found: ");
  1117. buffer_strcat_htmlescape(w->response.data, tok);
  1118. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
  1119. return HTTP_RESP_NOT_FOUND;
  1120. }
  1121. debug_flags |= D_RRD_STATS;
  1122. if(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))
  1123. rrdset_flag_clear(st, RRDSET_FLAG_DEBUG);
  1124. else
  1125. rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
  1126. w->response.data->content_type = CT_TEXT_HTML;
  1127. buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
  1128. buffer_strcat_htmlescape(w->response.data, tok);
  1129. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
  1130. return HTTP_RESP_OK;
  1131. }
  1132. buffer_flush(w->response.data);
  1133. buffer_strcat(w->response.data, "debug which chart?\r\n");
  1134. return HTTP_RESP_BAD_REQUEST;
  1135. }
  1136. else if(unlikely(hash == hash_mirror && strcmp(tok, "mirror") == 0)) {
  1137. if(unlikely(!http_can_access_netdataconf(w)))
  1138. return web_client_permission_denied(w);
  1139. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
  1140. // replace the zero bytes with spaces
  1141. buffer_char_replace(w->response.data, '\0', ' ');
  1142. // just leave the buffer as-is
  1143. // it will be copied back to the client
  1144. return HTTP_RESP_OK;
  1145. }
  1146. #endif /* NETDATA_INTERNAL_CHECKS */
  1147. }
  1148. buffer_flush(w->response.data);
  1149. return mysendfile(w, filename);
  1150. }
  1151. static bool web_server_log_transport(BUFFER *wb, void *ptr) {
  1152. struct web_client *w = ptr;
  1153. if(!w)
  1154. return false;
  1155. #ifdef ENABLE_HTTPS
  1156. buffer_strcat(wb, SSL_connection(&w->ssl) ? "https" : "http");
  1157. #else
  1158. buffer_strcat(wb, "http");
  1159. #endif
  1160. return true;
  1161. }
  1162. void web_client_process_request_from_web_server(struct web_client *w) {
  1163. // entry point for web server requests
  1164. ND_LOG_STACK lgs[] = {
  1165. ND_LOG_FIELD_CB(NDF_SRC_TRANSPORT, web_server_log_transport, w),
  1166. ND_LOG_FIELD_TXT(NDF_SRC_IP, w->client_ip),
  1167. ND_LOG_FIELD_TXT(NDF_SRC_PORT, w->client_port),
  1168. ND_LOG_FIELD_TXT(NDF_SRC_FORWARDED_HOST, w->forwarded_host),
  1169. ND_LOG_FIELD_TXT(NDF_SRC_FORWARDED_FOR, w->forwarded_for),
  1170. ND_LOG_FIELD_TXT(NDF_NIDL_NODE, w->client_host),
  1171. ND_LOG_FIELD_TXT(NDF_REQUEST_METHOD, http_request_method2string(w->mode)),
  1172. ND_LOG_FIELD_BFR(NDF_REQUEST, w->url_as_received),
  1173. ND_LOG_FIELD_U64(NDF_CONNECTION_ID, w->id),
  1174. ND_LOG_FIELD_UUID(NDF_TRANSACTION_ID, &w->transaction),
  1175. ND_LOG_FIELD_UUID(NDF_ACCOUNT_ID, &w->auth.cloud_account_id),
  1176. ND_LOG_FIELD_TXT(NDF_USER_NAME, w->auth.client_name),
  1177. ND_LOG_FIELD_TXT(NDF_USER_ROLE, http_id2access(w->access)),
  1178. ND_LOG_FIELD_END(),
  1179. };
  1180. ND_LOG_STACK_PUSH(lgs);
  1181. // give a new transaction id to the request
  1182. if(uuid_is_null(w->transaction))
  1183. uuid_generate_random(w->transaction);
  1184. // start timing us
  1185. web_client_timeout_checkpoint_init(w);
  1186. switch(http_request_validate(w)) {
  1187. case HTTP_VALIDATION_OK:
  1188. if(!web_client_flag_check(w, WEB_CLIENT_FLAG_PROGRESS_TRACKING)) {
  1189. web_client_flag_set(w, WEB_CLIENT_FLAG_PROGRESS_TRACKING);
  1190. query_progress_start_or_update(&w->transaction, 0, w->mode, w->acl,
  1191. buffer_tostring(w->url_as_received),
  1192. w->payload,
  1193. w->forwarded_for ? w->forwarded_for : w->client_ip);
  1194. }
  1195. switch(w->mode) {
  1196. case HTTP_REQUEST_MODE_STREAM:
  1197. if(unlikely(!http_can_access_stream(w))) {
  1198. web_client_permission_denied(w);
  1199. return;
  1200. }
  1201. w->response.code = rrdpush_receiver_thread_spawn(w, (char *)buffer_tostring(w->url_query_string_decoded), NULL);
  1202. return;
  1203. case HTTP_REQUEST_MODE_OPTIONS:
  1204. if(unlikely(
  1205. !http_can_access_dashboard(w) &&
  1206. !http_can_access_registry(w) &&
  1207. !http_can_access_badges(w) &&
  1208. !http_can_access_mgmt(w) &&
  1209. !http_can_access_netdataconf(w)
  1210. )) {
  1211. web_client_permission_denied(w);
  1212. break;
  1213. }
  1214. w->response.data->content_type = CT_TEXT_PLAIN;
  1215. buffer_flush(w->response.data);
  1216. buffer_strcat(w->response.data, "OK");
  1217. w->response.code = HTTP_RESP_OK;
  1218. break;
  1219. case HTTP_REQUEST_MODE_FILECOPY:
  1220. case HTTP_REQUEST_MODE_POST:
  1221. case HTTP_REQUEST_MODE_GET:
  1222. case HTTP_REQUEST_MODE_PUT:
  1223. case HTTP_REQUEST_MODE_DELETE:
  1224. if(unlikely(
  1225. !http_can_access_dashboard(w) &&
  1226. !http_can_access_registry(w) &&
  1227. !http_can_access_badges(w) &&
  1228. !http_can_access_mgmt(w) &&
  1229. !http_can_access_netdataconf(w)
  1230. )) {
  1231. web_client_permission_denied(w);
  1232. break;
  1233. }
  1234. web_client_reset_path_flags(w);
  1235. // find if the URL path has a filename extension
  1236. char path[FILENAME_MAX + 1];
  1237. strncpyz(path, buffer_tostring(w->url_path_decoded), FILENAME_MAX);
  1238. char *s = path, *e = path;
  1239. // remove the query string and find the last char
  1240. for (; *e ; e++) {
  1241. if (*e == '?')
  1242. break;
  1243. }
  1244. if(e == s || (*(e - 1) == '/'))
  1245. web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
  1246. // check if there is a filename extension
  1247. while (--e > s) {
  1248. if (*e == '/')
  1249. break;
  1250. if(*e == '.') {
  1251. web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION);
  1252. break;
  1253. }
  1254. }
  1255. w->response.code = (short)web_client_process_url(localhost, w, path);
  1256. break;
  1257. default:
  1258. web_client_permission_denied(w);
  1259. return;
  1260. }
  1261. break;
  1262. case HTTP_VALIDATION_INCOMPLETE:
  1263. if(w->response.data->len > NETDATA_WEB_REQUEST_MAX_SIZE) {
  1264. buffer_flush(w->url_as_received);
  1265. buffer_strcat(w->url_as_received, "too big request");
  1266. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
  1267. size_t len = w->response.data->len;
  1268. buffer_flush(w->response.data);
  1269. buffer_sprintf(w->response.data, "Received request is too big (received %zu bytes, max is %zu bytes).\r\n", len, (size_t)NETDATA_WEB_REQUEST_MAX_SIZE);
  1270. w->response.code = HTTP_RESP_BAD_REQUEST;
  1271. }
  1272. else {
  1273. // wait for more data
  1274. // set to normal to prevent web_server_rcv_callback
  1275. // from going into stream mode
  1276. if (w->mode == HTTP_REQUEST_MODE_STREAM)
  1277. w->mode = HTTP_REQUEST_MODE_GET;
  1278. return;
  1279. }
  1280. break;
  1281. #ifdef ENABLE_HTTPS
  1282. case HTTP_VALIDATION_REDIRECT:
  1283. {
  1284. buffer_flush(w->response.data);
  1285. w->response.data->content_type = CT_TEXT_HTML;
  1286. buffer_strcat(w->response.data,
  1287. "<!DOCTYPE html><!-- SPDX-License-Identifier: GPL-3.0-or-later --><html>"
  1288. "<body onload=\"window.location.href ='https://'+ window.location.hostname +"
  1289. " ':' + window.location.port + window.location.pathname + window.location.search\">"
  1290. "Redirecting to safety connection, case your browser does not support redirection, please"
  1291. " click <a onclick=\"window.location.href ='https://'+ window.location.hostname + ':' "
  1292. " + window.location.port + window.location.pathname + window.location.search\">here</a>."
  1293. "</body></html>");
  1294. w->response.code = HTTP_RESP_HTTPS_UPGRADE;
  1295. break;
  1296. }
  1297. #endif
  1298. case HTTP_VALIDATION_MALFORMED_URL:
  1299. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Malformed URL '%s'.", w->id, w->response.data->buffer);
  1300. buffer_flush(w->response.data);
  1301. buffer_strcat(w->response.data, "Malformed URL...\r\n");
  1302. w->response.code = HTTP_RESP_BAD_REQUEST;
  1303. break;
  1304. case HTTP_VALIDATION_EXCESS_REQUEST_DATA:
  1305. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Excess data in request '%s'.", w->id, w->response.data->buffer);
  1306. buffer_flush(w->response.data);
  1307. buffer_strcat(w->response.data, "Excess data in request.\r\n");
  1308. w->response.code = HTTP_RESP_BAD_REQUEST;
  1309. break;
  1310. case HTTP_VALIDATION_TOO_MANY_READ_RETRIES:
  1311. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Too many retries to read request '%s'.", w->id, w->response.data->buffer);
  1312. buffer_flush(w->response.data);
  1313. buffer_strcat(w->response.data, "Too many retries to read request.\r\n");
  1314. w->response.code = HTTP_RESP_BAD_REQUEST;
  1315. break;
  1316. case HTTP_VALIDATION_NOT_SUPPORTED:
  1317. netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: HTTP method requested is not supported '%s'.", w->id, w->response.data->buffer);
  1318. buffer_flush(w->response.data);
  1319. buffer_strcat(w->response.data, "HTTP method requested is not supported...\r\n");
  1320. w->response.code = HTTP_RESP_BAD_REQUEST;
  1321. break;
  1322. }
  1323. // keep track of the processing time
  1324. web_client_timeout_checkpoint_response_ready(w, NULL);
  1325. w->response.sent = 0;
  1326. web_client_send_http_header(w);
  1327. // enable sending immediately if we have data
  1328. if(w->response.data->len) web_client_enable_wait_send(w);
  1329. else web_client_disable_wait_send(w);
  1330. switch(w->mode) {
  1331. case HTTP_REQUEST_MODE_STREAM:
  1332. netdata_log_debug(D_WEB_CLIENT, "%llu: STREAM done.", w->id);
  1333. break;
  1334. case HTTP_REQUEST_MODE_OPTIONS:
  1335. netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
  1336. break;
  1337. case HTTP_REQUEST_MODE_POST:
  1338. case HTTP_REQUEST_MODE_GET:
  1339. case HTTP_REQUEST_MODE_PUT:
  1340. case HTTP_REQUEST_MODE_DELETE:
  1341. netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
  1342. break;
  1343. case HTTP_REQUEST_MODE_FILECOPY:
  1344. if(w->response.rlen) {
  1345. netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %zu bytes to client.", w->id, w->response.rlen);
  1346. web_client_enable_wait_receive(w);
  1347. /*
  1348. // utilize the kernel sendfile() for copying the file to the socket.
  1349. // this block of code can be commented, without anything missing.
  1350. // when it is commented, the program will copy the data using async I/O.
  1351. {
  1352. long len = sendfile(w->ofd, w->ifd, NULL, w->response.data->rbytes);
  1353. if(len != w->response.data->rbytes)
  1354. netdata_log_error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
  1355. else
  1356. web_client_request_done(w);
  1357. }
  1358. */
  1359. }
  1360. else
  1361. netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
  1362. break;
  1363. default:
  1364. fatal("%llu: Unknown client mode %u.", w->id, w->mode);
  1365. break;
  1366. }
  1367. }
  1368. ssize_t web_client_send_chunk_header(struct web_client *w, size_t len)
  1369. {
  1370. netdata_log_debug(D_DEFLATE, "%llu: OPEN CHUNK of %zu bytes (hex: %zx).", w->id, len, len);
  1371. char buf[24];
  1372. ssize_t bytes;
  1373. bytes = (ssize_t)sprintf(buf, "%zX\r\n", len);
  1374. buf[bytes] = 0x00;
  1375. bytes = web_client_send_data(w,buf,strlen(buf),0);
  1376. if(bytes > 0) {
  1377. netdata_log_debug(D_DEFLATE, "%llu: Sent chunk header %zd bytes.", w->id, bytes);
  1378. w->statistics.sent_bytes += bytes;
  1379. }
  1380. else if(bytes == 0) {
  1381. netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send chunk header to the client.", w->id);
  1382. }
  1383. else {
  1384. netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send chunk header to client.", w->id);
  1385. WEB_CLIENT_IS_DEAD(w);
  1386. }
  1387. return bytes;
  1388. }
  1389. ssize_t web_client_send_chunk_close(struct web_client *w)
  1390. {
  1391. //debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id);
  1392. ssize_t bytes;
  1393. bytes = web_client_send_data(w,"\r\n",2,0);
  1394. if(bytes > 0) {
  1395. netdata_log_debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
  1396. w->statistics.sent_bytes += bytes;
  1397. }
  1398. else if(bytes == 0) {
  1399. netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send chunk suffix to the client.", w->id);
  1400. }
  1401. else {
  1402. netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send chunk suffix to client.", w->id);
  1403. WEB_CLIENT_IS_DEAD(w);
  1404. }
  1405. return bytes;
  1406. }
  1407. ssize_t web_client_send_chunk_finalize(struct web_client *w)
  1408. {
  1409. //debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id);
  1410. ssize_t bytes;
  1411. bytes = web_client_send_data(w,"\r\n0\r\n\r\n",7,0);
  1412. if(bytes > 0) {
  1413. netdata_log_debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
  1414. w->statistics.sent_bytes += bytes;
  1415. }
  1416. else if(bytes == 0) {
  1417. netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send chunk finalize suffix to the client.", w->id);
  1418. }
  1419. else {
  1420. netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send chunk finalize suffix to client.", w->id);
  1421. WEB_CLIENT_IS_DEAD(w);
  1422. }
  1423. return bytes;
  1424. }
  1425. ssize_t web_client_send_deflate(struct web_client *w)
  1426. {
  1427. ssize_t len = 0, t = 0;
  1428. // when using compression,
  1429. // w->response.sent is the amount of bytes passed through compression
  1430. netdata_log_debug(D_DEFLATE, "%llu: web_client_send_deflate(): w->response.data->len = %zu, w->response.sent = %zu, w->response.zhave = %zu, w->response.zsent = %zu, w->response.zstream.avail_in = %u, w->response.zstream.avail_out = %u, w->response.zstream.total_in = %lu, w->response.zstream.total_out = %lu.",
  1431. w->id, w->response.data->len, w->response.sent, w->response.zhave, w->response.zsent, w->response.zstream.avail_in, w->response.zstream.avail_out, w->response.zstream.total_in, w->response.zstream.total_out);
  1432. if(w->response.data->len - w->response.sent == 0 && w->response.zstream.avail_in == 0 && w->response.zhave == w->response.zsent && w->response.zstream.avail_out != 0) {
  1433. // there is nothing to send
  1434. netdata_log_debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
  1435. // finalize the chunk
  1436. if(w->response.sent != 0) {
  1437. t = web_client_send_chunk_finalize(w);
  1438. if(t < 0) return t;
  1439. }
  1440. if(w->mode == HTTP_REQUEST_MODE_FILECOPY && web_client_has_wait_receive(w) && w->response.rlen && w->response.rlen > w->response.data->len) {
  1441. // we have to wait, more data will come
  1442. netdata_log_debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
  1443. web_client_disable_wait_send(w);
  1444. return t;
  1445. }
  1446. if(unlikely(!web_client_has_keepalive(w))) {
  1447. netdata_log_debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
  1448. WEB_CLIENT_IS_DEAD(w);
  1449. return t;
  1450. }
  1451. // reset the client
  1452. web_client_request_done(w);
  1453. netdata_log_debug(D_WEB_CLIENT, "%llu: Done sending all data on socket.", w->id);
  1454. return t;
  1455. }
  1456. if(w->response.zhave == w->response.zsent) {
  1457. // compress more input data
  1458. // close the previous open chunk
  1459. if(w->response.sent != 0) {
  1460. t = web_client_send_chunk_close(w);
  1461. if(t < 0) return t;
  1462. }
  1463. netdata_log_debug(D_DEFLATE, "%llu: Compressing %zu new bytes starting from %zu (and %u left behind).", w->id, (w->response.data->len - w->response.sent), w->response.sent, w->response.zstream.avail_in);
  1464. // give the compressor all the data not passed through the compressor yet
  1465. if(w->response.data->len > w->response.sent) {
  1466. w->response.zstream.next_in = (Bytef *)&w->response.data->buffer[w->response.sent - w->response.zstream.avail_in];
  1467. w->response.zstream.avail_in += (uInt) (w->response.data->len - w->response.sent);
  1468. }
  1469. // reset the compressor output buffer
  1470. w->response.zstream.next_out = w->response.zbuffer;
  1471. w->response.zstream.avail_out = NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE;
  1472. // ask for FINISH if we have all the input
  1473. int flush = Z_SYNC_FLUSH;
  1474. if((w->mode == HTTP_REQUEST_MODE_GET || w->mode == HTTP_REQUEST_MODE_POST || w->mode == HTTP_REQUEST_MODE_PUT || w->mode == HTTP_REQUEST_MODE_DELETE)
  1475. || (w->mode == HTTP_REQUEST_MODE_FILECOPY && !web_client_has_wait_receive(w) && w->response.data->len == w->response.rlen)) {
  1476. flush = Z_FINISH;
  1477. netdata_log_debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
  1478. }
  1479. else {
  1480. netdata_log_debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
  1481. }
  1482. // compress
  1483. if(deflate(&w->response.zstream, flush) == Z_STREAM_ERROR) {
  1484. netdata_log_error("%llu: Compression failed. Closing down client.", w->id);
  1485. web_client_request_done(w);
  1486. return(-1);
  1487. }
  1488. w->response.zhave = NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE - w->response.zstream.avail_out;
  1489. w->response.zsent = 0;
  1490. // keep track of the bytes passed through the compressor
  1491. w->response.sent = w->response.data->len;
  1492. netdata_log_debug(D_DEFLATE, "%llu: Compression produced %zu bytes.", w->id, w->response.zhave);
  1493. // open a new chunk
  1494. ssize_t t2 = web_client_send_chunk_header(w, w->response.zhave);
  1495. if(t2 < 0) return t2;
  1496. t += t2;
  1497. }
  1498. netdata_log_debug(D_WEB_CLIENT, "%llu: Sending %zu bytes of data (+%zd of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
  1499. len = web_client_send_data(w,&w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
  1500. if(len > 0) {
  1501. w->statistics.sent_bytes += len;
  1502. w->response.zsent += len;
  1503. len += t;
  1504. netdata_log_debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, len);
  1505. }
  1506. else if(len == 0) {
  1507. netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %zu, zsent = %zu, need to send = %zu).",
  1508. w->id, w->response.zhave, w->response.zsent, w->response.zhave - w->response.zsent);
  1509. }
  1510. else {
  1511. netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
  1512. WEB_CLIENT_IS_DEAD(w);
  1513. }
  1514. return(len);
  1515. }
  1516. ssize_t web_client_send(struct web_client *w) {
  1517. if(likely(w->response.zoutput)) return web_client_send_deflate(w);
  1518. ssize_t bytes;
  1519. if(unlikely(w->response.data->len - w->response.sent == 0)) {
  1520. // there is nothing to send
  1521. netdata_log_debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
  1522. // there can be two cases for this
  1523. // A. we have done everything
  1524. // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
  1525. if(w->mode == HTTP_REQUEST_MODE_FILECOPY && web_client_has_wait_receive(w) && w->response.rlen && w->response.rlen > w->response.data->len) {
  1526. // we have to wait, more data will come
  1527. netdata_log_debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
  1528. web_client_disable_wait_send(w);
  1529. return 0;
  1530. }
  1531. if(unlikely(!web_client_has_keepalive(w))) {
  1532. netdata_log_debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
  1533. WEB_CLIENT_IS_DEAD(w);
  1534. return 0;
  1535. }
  1536. web_client_request_done(w);
  1537. netdata_log_debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
  1538. return 0;
  1539. }
  1540. bytes = web_client_send_data(w,&w->response.data->buffer[w->response.sent], w->response.data->len - w->response.sent, MSG_DONTWAIT);
  1541. if(likely(bytes > 0)) {
  1542. w->statistics.sent_bytes += bytes;
  1543. w->response.sent += bytes;
  1544. netdata_log_debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, bytes);
  1545. }
  1546. else if(likely(bytes == 0)) {
  1547. netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
  1548. }
  1549. else {
  1550. netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
  1551. WEB_CLIENT_IS_DEAD(w);
  1552. }
  1553. return(bytes);
  1554. }
  1555. ssize_t web_client_read_file(struct web_client *w)
  1556. {
  1557. if(unlikely(w->response.rlen > w->response.data->size))
  1558. buffer_need_bytes(w->response.data, w->response.rlen - w->response.data->size);
  1559. if(unlikely(w->response.rlen <= w->response.data->len))
  1560. return 0;
  1561. ssize_t left = (ssize_t)(w->response.rlen - w->response.data->len);
  1562. ssize_t bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t)left);
  1563. if(likely(bytes > 0)) {
  1564. size_t old = w->response.data->len;
  1565. (void)old;
  1566. w->response.data->len += bytes;
  1567. w->response.data->buffer[w->response.data->len] = '\0';
  1568. netdata_log_debug(D_WEB_CLIENT, "%llu: Read %zd bytes.", w->id, bytes);
  1569. netdata_log_debug(D_WEB_DATA, "%llu: Read data: '%s'.", w->id, &w->response.data->buffer[old]);
  1570. web_client_enable_wait_send(w);
  1571. if(w->response.rlen && w->response.data->len >= w->response.rlen)
  1572. web_client_disable_wait_receive(w);
  1573. }
  1574. else if(likely(bytes == 0)) {
  1575. netdata_log_debug(D_WEB_CLIENT, "%llu: Out of input file data.", w->id);
  1576. // if we cannot read, it means we have an error on input.
  1577. // if however, we are copying a file from ifd to ofd, we should not return an error.
  1578. // in this case, the error should be generated when the file has been sent to the client.
  1579. // we are copying data from ifd to ofd
  1580. // let it finish copying...
  1581. web_client_disable_wait_receive(w);
  1582. netdata_log_debug(D_WEB_CLIENT, "%llu: Read the whole file.", w->id);
  1583. if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
  1584. if (w->ifd != w->ofd) close(w->ifd);
  1585. }
  1586. w->ifd = w->ofd;
  1587. }
  1588. else {
  1589. netdata_log_debug(D_WEB_CLIENT, "%llu: read data failed.", w->id);
  1590. WEB_CLIENT_IS_DEAD(w);
  1591. }
  1592. return(bytes);
  1593. }
  1594. ssize_t web_client_receive(struct web_client *w)
  1595. {
  1596. if(unlikely(w->mode == HTTP_REQUEST_MODE_FILECOPY))
  1597. return web_client_read_file(w);
  1598. ssize_t bytes;
  1599. ssize_t left = (ssize_t)(w->response.data->size - w->response.data->len);
  1600. // do we have any space for more data?
  1601. buffer_need_bytes(w->response.data, NETDATA_WEB_REQUEST_INITIAL_SIZE);
  1602. errno = 0;
  1603. #ifdef ENABLE_HTTPS
  1604. if ( (web_client_check_conn_tcp(w)) && (netdata_ssl_web_server_ctx) ) {
  1605. if (SSL_connection(&w->ssl)) {
  1606. bytes = netdata_ssl_read(&w->ssl, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1));
  1607. web_client_enable_wait_from_ssl(w);
  1608. }
  1609. else {
  1610. bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
  1611. }
  1612. }
  1613. else if(web_client_check_conn_tcp(w) || web_client_check_conn_unix(w)) {
  1614. bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
  1615. }
  1616. else // other connection methods
  1617. bytes = -1;
  1618. #else
  1619. if(web_client_check_conn_tcp(w) || web_client_check_conn_unix(w))
  1620. bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
  1621. else
  1622. bytes = -1;
  1623. #endif
  1624. if(likely(bytes > 0)) {
  1625. w->statistics.received_bytes += bytes;
  1626. size_t old = w->response.data->len;
  1627. (void)old;
  1628. w->response.data->len += bytes;
  1629. w->response.data->buffer[w->response.data->len] = '\0';
  1630. netdata_log_debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
  1631. netdata_log_debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]);
  1632. }
  1633. else if(unlikely(bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))) {
  1634. web_client_enable_wait_receive(w);
  1635. return 0;
  1636. }
  1637. else if (bytes < 0) {
  1638. netdata_log_debug(D_WEB_CLIENT, "%llu: receive data failed.", w->id);
  1639. WEB_CLIENT_IS_DEAD(w);
  1640. } else
  1641. netdata_log_debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
  1642. return(bytes);
  1643. }
  1644. void web_client_decode_path_and_query_string(struct web_client *w, const char *path_and_query_string) {
  1645. char buffer[NETDATA_WEB_REQUEST_URL_SIZE + 2];
  1646. buffer[0] = '\0';
  1647. buffer_flush(w->url_path_decoded);
  1648. buffer_flush(w->url_query_string_decoded);
  1649. if(buffer_strlen(w->url_as_received) == 0)
  1650. // do not overwrite this if it is already filled
  1651. buffer_strcat(w->url_as_received, path_and_query_string);
  1652. if(w->mode == HTTP_REQUEST_MODE_STREAM) {
  1653. // in stream mode, there is no path
  1654. url_decode_r(buffer, path_and_query_string, NETDATA_WEB_REQUEST_URL_SIZE + 1);
  1655. buffer[NETDATA_WEB_REQUEST_URL_SIZE + 1] = '\0';
  1656. buffer_strcat(w->url_query_string_decoded, buffer);
  1657. }
  1658. else {
  1659. // in non-stream mode, there is a path
  1660. // FIXME - the way this is implemented, query string params never accept the symbol &, not even encoded as %26
  1661. // To support the symbol & in query string params, we need to turn the url_query_string_decoded into a
  1662. // dictionary and decode each of the parameters individually.
  1663. // OR: in url_query_string_decoded use as separator a control character that cannot appear in the URL.
  1664. url_decode_r(buffer, path_and_query_string, NETDATA_WEB_REQUEST_URL_SIZE + 1);
  1665. char *question_mark_start = strchr(buffer, '?');
  1666. if (question_mark_start) {
  1667. buffer_strcat(w->url_query_string_decoded, question_mark_start);
  1668. char c = *question_mark_start;
  1669. *question_mark_start = '\0';
  1670. buffer_strcat(w->url_path_decoded, buffer);
  1671. *question_mark_start = c;
  1672. } else {
  1673. buffer_strcat(w->url_query_string_decoded, "");
  1674. buffer_strcat(w->url_path_decoded, buffer);
  1675. }
  1676. }
  1677. }
  1678. void web_client_reuse_from_cache(struct web_client *w) {
  1679. // zero everything about it - but keep the buffers
  1680. web_client_reset_allocations(w, false);
  1681. // remember the pointers to the buffers
  1682. BUFFER *b1 = w->response.data;
  1683. BUFFER *b2 = w->response.header;
  1684. BUFFER *b3 = w->response.header_output;
  1685. BUFFER *b4 = w->url_path_decoded;
  1686. BUFFER *b5 = w->url_as_received;
  1687. BUFFER *b6 = w->url_query_string_decoded;
  1688. BUFFER *b7 = w->payload;
  1689. #ifdef ENABLE_HTTPS
  1690. NETDATA_SSL ssl = w->ssl;
  1691. #endif
  1692. size_t use_count = w->use_count;
  1693. size_t *statistics_memory_accounting = w->statistics.memory_accounting;
  1694. // zero everything
  1695. memset(w, 0, sizeof(struct web_client));
  1696. w->ifd = w->ofd = -1;
  1697. w->statistics.memory_accounting = statistics_memory_accounting;
  1698. w->use_count = use_count;
  1699. #ifdef ENABLE_HTTPS
  1700. w->ssl = ssl;
  1701. #endif
  1702. // restore the pointers of the buffers
  1703. w->response.data = b1;
  1704. w->response.header = b2;
  1705. w->response.header_output = b3;
  1706. w->url_path_decoded = b4;
  1707. w->url_as_received = b5;
  1708. w->url_query_string_decoded = b6;
  1709. w->payload = b7;
  1710. }
  1711. struct web_client *web_client_create(size_t *statistics_memory_accounting) {
  1712. struct web_client *w = (struct web_client *)callocz(1, sizeof(struct web_client));
  1713. #ifdef ENABLE_HTTPS
  1714. w->ssl = NETDATA_SSL_UNSET_CONNECTION;
  1715. #endif
  1716. w->use_count = 1;
  1717. w->statistics.memory_accounting = statistics_memory_accounting;
  1718. w->url_as_received = buffer_create(NETDATA_WEB_DECODED_URL_INITIAL_SIZE, w->statistics.memory_accounting);
  1719. w->url_path_decoded = buffer_create(NETDATA_WEB_DECODED_URL_INITIAL_SIZE, w->statistics.memory_accounting);
  1720. w->url_query_string_decoded = buffer_create(NETDATA_WEB_DECODED_URL_INITIAL_SIZE, w->statistics.memory_accounting);
  1721. w->response.data = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE, w->statistics.memory_accounting);
  1722. w->response.header = buffer_create(NETDATA_WEB_RESPONSE_HEADER_INITIAL_SIZE, w->statistics.memory_accounting);
  1723. w->response.header_output = buffer_create(NETDATA_WEB_RESPONSE_HEADER_INITIAL_SIZE, w->statistics.memory_accounting);
  1724. __atomic_add_fetch(w->statistics.memory_accounting, sizeof(struct web_client), __ATOMIC_RELAXED);
  1725. return w;
  1726. }
  1727. void web_client_free(struct web_client *w) {
  1728. #ifdef ENABLE_HTTPS
  1729. netdata_ssl_close(&w->ssl);
  1730. #endif
  1731. web_client_reset_allocations(w, true);
  1732. __atomic_sub_fetch(w->statistics.memory_accounting, sizeof(struct web_client), __ATOMIC_RELAXED);
  1733. freez(w);
  1734. }
  1735. inline void web_client_timeout_checkpoint_init(struct web_client *w) {
  1736. now_monotonic_high_precision_timeval(&w->timings.tv_in);
  1737. }
  1738. inline void web_client_timeout_checkpoint_set(struct web_client *w, int timeout_ms) {
  1739. w->timings.timeout_ut = timeout_ms * USEC_PER_MS;
  1740. if(!w->timings.tv_in.tv_sec)
  1741. web_client_timeout_checkpoint_init(w);
  1742. if(!w->timings.tv_timeout_last_checkpoint.tv_sec)
  1743. w->timings.tv_timeout_last_checkpoint = w->timings.tv_in;
  1744. }
  1745. inline usec_t web_client_timeout_checkpoint(struct web_client *w) {
  1746. struct timeval now;
  1747. now_monotonic_high_precision_timeval(&now);
  1748. if (!w->timings.tv_timeout_last_checkpoint.tv_sec)
  1749. w->timings.tv_timeout_last_checkpoint = w->timings.tv_in;
  1750. usec_t since_last_check_ut = dt_usec(&w->timings.tv_timeout_last_checkpoint, &now);
  1751. w->timings.tv_timeout_last_checkpoint = now;
  1752. return since_last_check_ut;
  1753. }
  1754. inline usec_t web_client_timeout_checkpoint_response_ready(struct web_client *w, usec_t *usec_since_last_checkpoint) {
  1755. usec_t since_last_check_ut = web_client_timeout_checkpoint(w);
  1756. if(usec_since_last_checkpoint)
  1757. *usec_since_last_checkpoint = since_last_check_ut;
  1758. w->timings.tv_ready = w->timings.tv_timeout_last_checkpoint;
  1759. // return the total time of the query
  1760. return dt_usec(&w->timings.tv_in, &w->timings.tv_ready);
  1761. }
  1762. inline bool web_client_timeout_checkpoint_and_check(struct web_client *w, usec_t *usec_since_last_checkpoint) {
  1763. usec_t since_last_check_ut = web_client_timeout_checkpoint(w);
  1764. if(usec_since_last_checkpoint)
  1765. *usec_since_last_checkpoint = since_last_check_ut;
  1766. if(!w->timings.timeout_ut)
  1767. return false;
  1768. usec_t since_reception_ut = dt_usec(&w->timings.tv_in, &w->timings.tv_timeout_last_checkpoint);
  1769. if (since_reception_ut >= w->timings.timeout_ut) {
  1770. buffer_flush(w->response.data);
  1771. buffer_strcat(w->response.data, "Query timeout exceeded");
  1772. w->response.code = HTTP_RESP_GATEWAY_TIMEOUT;
  1773. return true;
  1774. }
  1775. return false;
  1776. }