PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/bloomd/conn_handler.c

http://github.com/armon/bloomd
C | 720 lines | 461 code | 97 blank | 162 comment | 94 complexity | 6ced65343c2c07da57cb3f4e58c6e0ed MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, BSD-2-Clause
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <regex.h>
  5. #include <assert.h>
  6. #include "conn_handler.h"
  7. #include "handler_constants.c"
  8. /**
  9. * Defines the number of keys we set/check in a single
  10. * iteration for our multi commands. We do not do all the
  11. * keys at one time to prevent a client from holding locks
  12. * for too long. This is especially critical for set
  13. * operations which serialize access.
  14. */
  15. #define MULTI_OP_SIZE 32
  16. /**
  17. * Invoked in any context with a bloom_conn_handler
  18. * to send out an INTERNAL_ERROR message to the client.
  19. */
  20. #define INTERNAL_ERROR() (handle_client_resp(handle->conn, (char*)INTERNAL_ERR, INTERNAL_ERR_LEN))
  21. /* Static method declarations */
  22. static void handle_check_cmd(bloom_conn_handler *handle, char *args, int args_len);
  23. static void handle_check_multi_cmd(bloom_conn_handler *handle, char *args, int args_len);
  24. static void handle_set_cmd(bloom_conn_handler *handle, char *args, int args_len);
  25. static void handle_set_multi_cmd(bloom_conn_handler *handle, char *args, int args_len);
  26. static void handle_create_cmd(bloom_conn_handler *handle, char *args, int args_len);
  27. static void handle_drop_cmd(bloom_conn_handler *handle, char *args, int args_len);
  28. static void handle_close_cmd(bloom_conn_handler *handle, char *args, int args_len);
  29. static void handle_clear_cmd(bloom_conn_handler *handle, char *args, int args_len);
  30. static void handle_list_cmd(bloom_conn_handler *handle, char *args, int args_len);
  31. static void handle_info_cmd(bloom_conn_handler *handle, char *args, int args_len);
  32. static void handle_flush_cmd(bloom_conn_handler *handle, char *args, int args_len);
  33. static int handle_multi_response(bloom_conn_handler *handle, int cmd_res, int num_keys, char *res_buf, int end_of_input);
  34. static inline void handle_client_resp(bloom_conn_info *conn, char* resp_mesg, int resp_len);
  35. static void handle_client_err(bloom_conn_info *conn, char* err_msg, int msg_len);
  36. static conn_cmd_type determine_client_command(char *cmd_buf, int buf_len, char **arg_buf, int *arg_len);
  37. static int buffer_after_terminator(char *buf, int buf_len, char terminator, char **after_term, int *after_len);
  38. /**
  39. * Invoked to initialize the conn handler layer.
  40. */
  41. void init_conn_handler() {
  42. // Compile our regexes
  43. int res;
  44. res = regcomp(&VALID_FILTER_NAMES_RE, VALID_FILTER_NAMES_PATTERN, REG_EXTENDED|REG_NOSUB);
  45. assert(res == 0);
  46. }
  47. /**
  48. * Invoked by the networking layer when there is new
  49. * data to be handled. The connection handler should
  50. * consume all the input possible, and generate responses
  51. * to all requests.
  52. * @arg handle The connection related information
  53. * @return 0 on success.
  54. */
  55. int handle_client_connect(bloom_conn_handler *handle) {
  56. // Look for the next command line
  57. char *buf, *arg_buf;
  58. int buf_len, arg_buf_len, should_free;
  59. int status;
  60. while (1) {
  61. status = extract_to_terminator(handle->conn, '\n', &buf, &buf_len, &should_free);
  62. if (status == -1) break; // Return if no command is available
  63. // Determine the command type
  64. conn_cmd_type type = determine_client_command(buf, buf_len, &arg_buf, &arg_buf_len);
  65. // Handle an error or unknown response
  66. switch(type) {
  67. case CHECK:
  68. handle_check_cmd(handle, arg_buf, arg_buf_len);
  69. break;
  70. case CHECK_MULTI:
  71. handle_check_multi_cmd(handle, arg_buf, arg_buf_len);
  72. break;
  73. case SET:
  74. handle_set_cmd(handle, arg_buf, arg_buf_len);
  75. break;
  76. case SET_MULTI:
  77. handle_set_multi_cmd(handle, arg_buf, arg_buf_len);
  78. break;
  79. case CREATE:
  80. handle_create_cmd(handle, arg_buf, arg_buf_len);
  81. break;
  82. case DROP:
  83. handle_drop_cmd(handle, arg_buf, arg_buf_len);
  84. break;
  85. case CLOSE:
  86. handle_close_cmd(handle, arg_buf, arg_buf_len);
  87. break;
  88. case CLEAR:
  89. handle_clear_cmd(handle, arg_buf, arg_buf_len);
  90. break;
  91. case LIST:
  92. handle_list_cmd(handle, arg_buf, arg_buf_len);
  93. break;
  94. case INFO:
  95. handle_info_cmd(handle, arg_buf, arg_buf_len);
  96. break;
  97. case FLUSH:
  98. handle_flush_cmd(handle, arg_buf, arg_buf_len);
  99. break;
  100. default:
  101. handle_client_err(handle->conn, (char*)&CMD_NOT_SUP, CMD_NOT_SUP_LEN);
  102. break;
  103. }
  104. // Make sure to free the command buffer if we need to
  105. if (should_free) free(buf);
  106. }
  107. return 0;
  108. }
  109. /**
  110. * Periodic update is used to update our checkpoint with
  111. * the filter manager, so that vacuum progress can be made.
  112. */
  113. void periodic_update(bloom_conn_handler *handle) {
  114. filtmgr_client_checkpoint(handle->mgr);
  115. }
  116. /**
  117. * Internal method to handle a command that relies
  118. * on a filter name and a single key, responses are handled using
  119. * handle_multi_response.
  120. */
  121. static void handle_filt_key_cmd(bloom_conn_handler *handle, char *args, int args_len,
  122. int(*filtmgr_func)(bloom_filtmgr *, char*, char **, int, char*)) {
  123. #define CHECK_ARG_ERR() { \
  124. handle_client_err(handle->conn, (char*)&FILT_KEY_NEEDED, FILT_KEY_NEEDED_LEN); \
  125. return; \
  126. }
  127. // If we have no args, complain.
  128. if (!args) CHECK_ARG_ERR();
  129. // Scan past the filter name
  130. char *key;
  131. int key_len;
  132. int err = buffer_after_terminator(args, args_len, ' ', &key, &key_len);
  133. if (err || key_len <= 1) CHECK_ARG_ERR();
  134. // Setup the buffers
  135. char *key_buf[] = {key};
  136. char result_buf[1];
  137. // Call into the filter manager
  138. int res = filtmgr_func(handle->mgr, args, (char**)&key_buf, 1, (char*)&result_buf);
  139. handle_multi_response(handle, res, 1, (char*)&result_buf, 1);
  140. }
  141. static void handle_check_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  142. handle_filt_key_cmd(handle, args, args_len, filtmgr_check_keys);
  143. }
  144. static void handle_set_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  145. handle_filt_key_cmd(handle, args, args_len, filtmgr_set_keys);
  146. }
  147. /**
  148. * Internal method to handle a command that relies
  149. * on a filter name and multiple keys, responses are handled using
  150. * handle_multi_response.
  151. */
  152. static void handle_filt_multi_key_cmd(bloom_conn_handler *handle, char *args, int args_len,
  153. int(*filtmgr_func)(bloom_filtmgr *, char*, char **, int, char*)) {
  154. #define CHECK_ARG_ERR() { \
  155. handle_client_err(handle->conn, (char*)&FILT_KEY_NEEDED, FILT_KEY_NEEDED_LEN); \
  156. return; \
  157. }
  158. // If we have no args, complain.
  159. if (!args) CHECK_ARG_ERR();
  160. // Setup the buffers
  161. char *key_buf[MULTI_OP_SIZE];
  162. char result_buf[MULTI_OP_SIZE];
  163. // Scan all the keys
  164. char *key;
  165. int key_len;
  166. int err = buffer_after_terminator(args, args_len, ' ', &key, &key_len);
  167. if (err || key_len <= 1) CHECK_ARG_ERR();
  168. // Parse any options
  169. char *curr_key = key;
  170. int index = 0;
  171. #define HAS_ANOTHER_KEY() (curr_key && *curr_key != '\0')
  172. while (HAS_ANOTHER_KEY()) {
  173. // Adds a zero terminator to the current key, scans forward
  174. buffer_after_terminator(key, key_len, ' ', &key, &key_len);
  175. // Set the key
  176. key_buf[index] = curr_key;
  177. // Advance to the next key
  178. curr_key = key;
  179. index++;
  180. // If we have filled the buffer, check now
  181. if (index == MULTI_OP_SIZE) {
  182. // Handle the keys now
  183. int res = filtmgr_func(handle->mgr, args, (char**)&key_buf, index, (char*)&result_buf);
  184. res = handle_multi_response(handle, res, index, (char*)&result_buf, !HAS_ANOTHER_KEY());
  185. if (res) return;
  186. // Reset the index
  187. index = 0;
  188. }
  189. }
  190. // Handle any remaining keys
  191. if (index) {
  192. int res = filtmgr_func(handle->mgr, args, key_buf, index, result_buf);
  193. handle_multi_response(handle, res, index, (char*)&result_buf, 1);
  194. }
  195. }
  196. static void handle_check_multi_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  197. handle_filt_multi_key_cmd(handle, args, args_len, filtmgr_check_keys);
  198. }
  199. static void handle_set_multi_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  200. handle_filt_multi_key_cmd(handle, args, args_len, filtmgr_set_keys);
  201. }
  202. /**
  203. * Internal command used to handle filter creation.
  204. */
  205. static void handle_create_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  206. // If we have no args, complain.
  207. if (!args) {
  208. handle_client_err(handle->conn, (char*)&FILT_NEEDED, FILT_NEEDED_LEN);
  209. return;
  210. }
  211. // Scan for options after the filter name
  212. char *options;
  213. int options_len;
  214. int res = buffer_after_terminator(args, args_len, ' ', &options, &options_len);
  215. // Verify the filter name is valid
  216. char *filter_name = args;
  217. if (regexec(&VALID_FILTER_NAMES_RE, filter_name, 0, NULL, 0) != 0) {
  218. handle_client_err(handle->conn, (char*)&BAD_FILT_NAME, BAD_FILT_NAME_LEN);
  219. return;
  220. }
  221. // Parse the options
  222. bloom_config *config = NULL;
  223. int err = 0;
  224. if (res == 0) {
  225. // Make a new config store, copy the current
  226. config = malloc(sizeof(bloom_config));
  227. memcpy(config, handle->config, sizeof(bloom_config));
  228. // Parse any options
  229. char *param = options;
  230. while (param) {
  231. // Adds a zero terminator to the current param, scans forward
  232. buffer_after_terminator(options, options_len, ' ', &options, &options_len);
  233. // Check for the custom params
  234. int match = 0;
  235. match |= sscanf(param, "capacity=%llu", (unsigned long long*)&config->initial_capacity);
  236. match |= sscanf(param, "prob=%lf", &config->default_probability);
  237. match |= sscanf(param, "in_memory=%d", &config->in_memory);
  238. // Check if there was no match
  239. if (!match) {
  240. err = 1;
  241. handle_client_err(handle->conn, (char*)&BAD_ARGS, BAD_ARGS_LEN);
  242. break;
  243. }
  244. // Advance to the next param
  245. param = options;
  246. }
  247. // Validate the params
  248. int invalid_config = 0;
  249. invalid_config |= sane_initial_capacity(config->initial_capacity);
  250. invalid_config |= sane_default_probability(config->default_probability);
  251. invalid_config |= sane_in_memory(config->in_memory);
  252. // Barf if the configs are bad
  253. if (invalid_config) {
  254. err = 1;
  255. handle_client_err(handle->conn, (char*)&BAD_ARGS, BAD_ARGS_LEN);
  256. }
  257. }
  258. // Clean up an leave on errors
  259. if (err) {
  260. if (config) free(config);
  261. return;
  262. }
  263. // Create a new filter
  264. res = filtmgr_create_filter(handle->mgr, filter_name, config);
  265. switch (res) {
  266. case 0:
  267. handle_client_resp(handle->conn, (char*)DONE_RESP, DONE_RESP_LEN);
  268. break;
  269. case -1:
  270. handle_client_resp(handle->conn, (char*)EXISTS_RESP, EXISTS_RESP_LEN);
  271. if (config) free(config);
  272. break;
  273. case -3:
  274. handle_client_resp(handle->conn, (char*)DELETE_IN_PROGRESS, DELETE_IN_PROGRESS_LEN);
  275. if (config) free(config);
  276. break;
  277. default:
  278. INTERNAL_ERROR();
  279. if (config) free(config);
  280. break;
  281. }
  282. }
  283. /**
  284. * Internal method to handle a command that relies
  285. * on a filter name and a single key, responses are handled using
  286. * handle_multi_response.
  287. */
  288. static void handle_filt_cmd(bloom_conn_handler *handle, char *args, int args_len,
  289. int(*filtmgr_func)(bloom_filtmgr *, char*)) {
  290. // If we have no args, complain.
  291. if (!args) {
  292. handle_client_err(handle->conn, (char*)&FILT_NEEDED, FILT_NEEDED_LEN);
  293. return;
  294. }
  295. // Scan past the filter name
  296. char *key;
  297. int key_len;
  298. int after = buffer_after_terminator(args, args_len, ' ', &key, &key_len);
  299. if (after == 0) {
  300. handle_client_err(handle->conn, (char*)&UNEXPECTED_ARGS, UNEXPECTED_ARGS_LEN);
  301. return;
  302. }
  303. // Call into the filter manager
  304. int res = filtmgr_func(handle->mgr, args);
  305. switch (res) {
  306. case 0:
  307. handle_client_resp(handle->conn, (char*)DONE_RESP, DONE_RESP_LEN);
  308. break;
  309. case -1:
  310. handle_client_resp(handle->conn, (char*)FILT_NOT_EXIST, FILT_NOT_EXIST_LEN);
  311. break;
  312. case -2:
  313. handle_client_resp(handle->conn, (char*)FILT_NOT_PROXIED, FILT_NOT_PROXIED_LEN);
  314. break;
  315. default:
  316. INTERNAL_ERROR();
  317. break;
  318. }
  319. }
  320. static void handle_drop_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  321. handle_filt_cmd(handle, args, args_len, filtmgr_drop_filter);
  322. }
  323. static void handle_close_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  324. handle_filt_cmd(handle, args, args_len, filtmgr_unmap_filter);
  325. }
  326. static void handle_clear_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  327. handle_filt_cmd(handle, args, args_len, filtmgr_clear_filter);
  328. }
  329. // Callback invoked by list command to create an output
  330. // line for each filter. We hold a filter handle which we
  331. // can use to get some info about it
  332. static void list_filter_cb(void *data, char *filter_name, bloom_filter *filter) {
  333. char **out = data;
  334. int res;
  335. res = asprintf(out, "%s %f %llu %llu %llu\n",
  336. filter_name,
  337. filter->filter_config.default_probability,
  338. (unsigned long long)bloomf_byte_size(filter),
  339. (unsigned long long)bloomf_capacity(filter),
  340. (unsigned long long)bloomf_size(filter));
  341. assert(res != -1);
  342. }
  343. static void handle_list_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  344. (void)args_len;
  345. // List all the filters
  346. bloom_filter_list_head *head;
  347. int res = filtmgr_list_filters(handle->mgr, args, &head);
  348. if (res != 0) {
  349. INTERNAL_ERROR();
  350. return;
  351. }
  352. // Allocate buffers for the responses
  353. int num_out = (head->size+2);
  354. char** output_bufs = malloc(num_out * sizeof(char*));
  355. int* output_bufs_len = malloc(num_out * sizeof(int));
  356. // Setup the START/END lines
  357. output_bufs[0] = (char*)&START_RESP;
  358. output_bufs_len[0] = START_RESP_LEN;
  359. output_bufs[head->size+1] = (char*)&END_RESP;
  360. output_bufs_len[head->size+1] = END_RESP_LEN;
  361. // Generate the responses
  362. char *resp;
  363. bloom_filter_list *node = head->head;
  364. for (int i=0; i < head->size; i++) {
  365. res = filtmgr_filter_cb(handle->mgr, node->filter_name, list_filter_cb, &resp);
  366. if (res == 0) {
  367. output_bufs[i+1] = resp;
  368. output_bufs_len[i+1] = strlen(resp);
  369. } else { // Skip this output
  370. output_bufs[i+1] = NULL;
  371. output_bufs_len[i+1] = 0;
  372. }
  373. node = node->next;
  374. }
  375. // Write the response
  376. send_client_response(handle->conn, output_bufs, output_bufs_len, num_out);
  377. // Cleanup
  378. for (int i=1; i <= head->size; i++) if(output_bufs[i]) free(output_bufs[i]);
  379. free(output_bufs);
  380. free(output_bufs_len);
  381. filtmgr_cleanup_list(head);
  382. }
  383. // Callback invoked by list command to create an output
  384. // line for each filter. We hold a filter handle which we
  385. // can use to get some info about it
  386. static void info_filter_cb(void *data, char *filter_name, bloom_filter *filter) {
  387. (void)filter_name;
  388. // Cast the intput
  389. char **out = data;
  390. // Get some metrics
  391. filter_counters *counters = bloomf_counters(filter);
  392. uint64_t capacity = bloomf_capacity(filter);
  393. uint64_t storage = bloomf_byte_size(filter);
  394. uint64_t size = bloomf_size(filter);
  395. uint64_t checks = counters->check_hits + counters->check_misses;
  396. uint64_t sets = counters->set_hits + counters->set_misses;
  397. // Generate a formatted string output
  398. int res;
  399. res = asprintf(out, "capacity %llu\n\
  400. checks %llu\n\
  401. check_hits %llu\n\
  402. check_misses %llu\n\
  403. in_memory %d\n\
  404. page_ins %llu\n\
  405. page_outs %llu\n\
  406. probability %f\n\
  407. sets %llu\n\
  408. set_hits %llu\n\
  409. set_misses %llu\n\
  410. size %llu\n\
  411. storage %llu\n",
  412. (unsigned long long)capacity, (unsigned long long)checks,
  413. (unsigned long long)counters->check_hits, (unsigned long long)counters->check_misses,
  414. ((bloomf_is_proxied(filter)) ? 0 : 1),
  415. (unsigned long long)counters->page_ins, (unsigned long long)counters->page_outs,
  416. filter->filter_config.default_probability,
  417. (unsigned long long)sets, (unsigned long long)counters->set_hits,
  418. (unsigned long long)counters->set_misses, (unsigned long long)size, (unsigned long long)storage);
  419. assert(res != -1);
  420. }
  421. static void handle_info_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  422. // If we have no args, complain.
  423. if (!args) {
  424. handle_client_err(handle->conn, (char*)&FILT_NEEDED, FILT_NEEDED_LEN);
  425. return;
  426. }
  427. // Scan past the filter name
  428. char *key;
  429. int key_len;
  430. int after = buffer_after_terminator(args, args_len, ' ', &key, &key_len);
  431. if (after == 0) {
  432. handle_client_err(handle->conn, (char*)&UNEXPECTED_ARGS, UNEXPECTED_ARGS_LEN);
  433. return;
  434. }
  435. // Create output buffers
  436. char *output[] = {(char*)&START_RESP, NULL, (char*)&END_RESP};
  437. int lens[] = {START_RESP_LEN, 0, END_RESP_LEN};
  438. // Invoke the callback to get the filter stats
  439. int res = filtmgr_filter_cb(handle->mgr, args, info_filter_cb, &output[1]);
  440. // Check for no filter
  441. if (res != 0) {
  442. switch (res) {
  443. case -1:
  444. handle_client_resp(handle->conn, (char*)FILT_NOT_EXIST, FILT_NOT_EXIST_LEN);
  445. break;
  446. default:
  447. INTERNAL_ERROR();
  448. break;
  449. }
  450. return;
  451. }
  452. // Adjust the buffer size
  453. lens[1] = strlen(output[1]);
  454. // Write out the bufs
  455. send_client_response(handle->conn, (char**)&output, (int*)&lens, 3);
  456. free(output[1]);
  457. }
  458. static void handle_flush_cmd(bloom_conn_handler *handle, char *args, int args_len) {
  459. // If we have a specfic filter, use filt_cmd
  460. if (args) {
  461. handle_filt_cmd(handle, args, args_len, filtmgr_flush_filter);
  462. return;
  463. }
  464. // List all the filters
  465. bloom_filter_list_head *head;
  466. int res = filtmgr_list_filters(handle->mgr, NULL, &head);
  467. if (res != 0) {
  468. INTERNAL_ERROR();
  469. return;
  470. }
  471. // Flush all, ignore errors since
  472. // filters might get deleted in the process
  473. bloom_filter_list *node = head->head;
  474. while (node) {
  475. filtmgr_flush_filter(handle->mgr, node->filter_name);
  476. node = node->next;
  477. }
  478. // Respond
  479. handle_client_resp(handle->conn, (char*)DONE_RESP, DONE_RESP_LEN);
  480. // Cleanup
  481. filtmgr_cleanup_list(head);
  482. }
  483. /**
  484. * Helper to handle sending the response to the multi commands,
  485. * either multi or bulk.
  486. * @arg handle The conn handle
  487. * @arg cmd_res The result of the command
  488. * @arg num_keys The number of keys in the result buffer. This should NOT be
  489. * more than MULTI_OP_SIZE.
  490. * @arg res_buf The result buffer
  491. * @arg end_of_input Should the last result include a new line
  492. * @return 0 on success, 1 if we should stop.
  493. */
  494. static int handle_multi_response(bloom_conn_handler *handle, int cmd_res, int num_keys, char *res_buf, int end_of_input) {
  495. // Do nothing if we get too many keys
  496. if (num_keys > MULTI_OP_SIZE || num_keys <= 0) return 1;
  497. if (cmd_res != 0) {
  498. switch (cmd_res) {
  499. case -1:
  500. handle_client_resp(handle->conn, (char*)FILT_NOT_EXIST, FILT_NOT_EXIST_LEN);
  501. break;
  502. default:
  503. INTERNAL_ERROR();
  504. break;
  505. }
  506. return 1;
  507. }
  508. // Allocate buffers for our response, plus a newline
  509. char *resp_bufs[MULTI_OP_SIZE];
  510. int resp_buf_lens[MULTI_OP_SIZE];
  511. // Set the response buffers according to the results
  512. int last_key = 1;
  513. for (int i=0; i < num_keys; i++) {
  514. last_key = end_of_input && (i == (num_keys - 1));
  515. switch (res_buf[i]) {
  516. case 0:
  517. resp_bufs[i] = (char*)((last_key) ? NO_RESP : NO_SPACE);
  518. resp_buf_lens[i] = (last_key) ? NO_RESP_LEN: NO_SPACE_LEN;
  519. break;
  520. case 1:
  521. resp_bufs[i] = (char*)((last_key) ? YES_RESP : YES_SPACE);
  522. resp_buf_lens[i] = (last_key) ? YES_RESP_LEN: YES_SPACE_LEN;
  523. break;
  524. default:
  525. INTERNAL_ERROR();
  526. return 1;
  527. }
  528. }
  529. // Write out!
  530. send_client_response(handle->conn, (char**)&resp_bufs, (int*)&resp_buf_lens, num_keys);
  531. return 0;
  532. }
  533. /**
  534. * Sends a client response message back. Simple convenience wrapper
  535. * around handle_client_resp.
  536. */
  537. static inline void handle_client_resp(bloom_conn_info *conn, char* resp_mesg, int resp_len) {
  538. char *buffers[] = {resp_mesg};
  539. int sizes[] = {resp_len};
  540. send_client_response(conn, (char**)&buffers, (int*)&sizes, 1);
  541. }
  542. /**
  543. * Sends a client error message back. Optimizes to use multiple
  544. * output buffers so we can collapse this into a single write without
  545. * needing to move our buffers around.
  546. */
  547. static void handle_client_err(bloom_conn_info *conn, char* err_msg, int msg_len) {
  548. char *buffers[] = {(char*)&CLIENT_ERR, err_msg, (char*)&NEW_LINE};
  549. int sizes[] = {CLIENT_ERR_LEN, msg_len, NEW_LINE_LEN};
  550. send_client_response(conn, (char**)&buffers, (int*)&sizes, 3);
  551. }
  552. /**
  553. * Determines the client command.
  554. * @arg cmd_buf A command buffer
  555. * @arg buf_len The length of the buffer
  556. * @arg arg_buf Output. Sets the start address of the command arguments.
  557. * @arg arg_len Output. Sets the length of arg_buf.
  558. * @return The conn_cmd_type enum value.
  559. * UNKNOWN if it doesn't match anything supported, or a proper command.
  560. */
  561. static conn_cmd_type determine_client_command(char *cmd_buf, int buf_len, char **arg_buf, int *arg_len) {
  562. // Check if we are ending with \r, and remove it.
  563. if (cmd_buf[buf_len-2] == '\r') {
  564. cmd_buf[buf_len-2] = '\0';
  565. buf_len -= 1;
  566. }
  567. // Scan for a space. This will setup the arg_buf and arg_len
  568. // if we do find the terminator. It will also insert a null terminator
  569. // at the space, so we can compare the cmd_buf to the commands.
  570. buffer_after_terminator(cmd_buf, buf_len, ' ', arg_buf, arg_len);
  571. // Search for the command
  572. conn_cmd_type type = UNKNOWN;
  573. #define CMD_MATCH(name) (strcmp(name, cmd_buf) == 0)
  574. if (CMD_MATCH("c") || CMD_MATCH("check")) {
  575. type = CHECK;
  576. } else if (CMD_MATCH("m") || CMD_MATCH("multi")) {
  577. type = CHECK_MULTI;
  578. } else if (CMD_MATCH("s") || CMD_MATCH("set")) {
  579. type = SET;
  580. } else if (CMD_MATCH("b") || CMD_MATCH("bulk")) {
  581. type = SET_MULTI;
  582. } else if (CMD_MATCH("list")) {
  583. type = LIST;
  584. } else if (CMD_MATCH("info")) {
  585. type = INFO;
  586. } else if (CMD_MATCH("create")) {
  587. type = CREATE;
  588. } else if (CMD_MATCH("drop")) {
  589. type = DROP;
  590. } else if (CMD_MATCH("close")) {
  591. type = CLOSE;
  592. } else if (CMD_MATCH("clear")) {
  593. type = CLEAR;
  594. } else if (CMD_MATCH("flush")) {
  595. type = FLUSH;
  596. }
  597. return type;
  598. }
  599. /**
  600. * Scans the input buffer of a given length up to a terminator.
  601. * Then sets the start of the buffer after the terminator including
  602. * the length of the after buffer.
  603. * @arg buf The input buffer
  604. * @arg buf_len The length of the input buffer
  605. * @arg terminator The terminator to scan to. Replaced with the null terminator.
  606. * @arg after_term Output. Set to the byte after the terminator.
  607. * @arg after_len Output. Set to the length of the output buffer.
  608. * @return 0 if terminator found. -1 otherwise.
  609. */
  610. static int buffer_after_terminator(char *buf, int buf_len, char terminator, char **after_term, int *after_len) {
  611. // Scan for a space
  612. char *term_addr = memchr(buf, terminator, buf_len);
  613. if (!term_addr) {
  614. *after_term = NULL;
  615. return -1;
  616. }
  617. // Convert the space to a null-seperator
  618. *term_addr = '\0';
  619. // Provide the arg buffer, and arg_len
  620. *after_term = term_addr+1;
  621. *after_len = buf_len - (term_addr - buf + 1);
  622. return 0;
  623. }