예제 #1
0
int mtev_json_object_to_fd(int fd, struct mtev_json_object *obj)
{
  const char *mtev_json_str;
  int ret;
  unsigned int wpos, wsize;

  if(!obj) {
    MC_ERROR("mtev_json_object_to_fd: object is null%s\n", "");
    return -1;
  }

  if(!(mtev_json_str = mtev_json_object_to_json_string(obj))) {
    return -1;
  }

  wsize = (unsigned int)(strlen(mtev_json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */
  wpos = 0;
  while(wpos < wsize) {
    if((ret = write(fd, mtev_json_str + wpos, wsize-wpos)) < 0) {
      MC_ERROR("mtev_json_object_to_fd: error writing fd %d: %s\n",
	     fd, strerror(errno));
      return -1;
    }

    /* because of the above check for ret < 0, we can safely cast and add */
    wpos += (unsigned int)ret;
  }

  return 0;

}
/**
 * The "noit_livestream" protocol requires a well formed request to come
 * in via websocket that resembles:
 *
 * {
 *   "period_ms": <milliseconds>,
 *   "check_uuid": "<uuid_string>",
 *   "metrics" : ["<metric_name1>", "<metric_name2>"]
 * }
 *
 * There can be only one active livestream request for a single websocket at one time.
 * However, you can send down a new livestream request at any time to alter the
 * stream based on new requirements.
 *
 * If the "metrics" member exists in the incoming request, then the metrics that are sent will be
 * filtered to those that match the strings in the array by exact match.  If you want to get
 * all metrics in the check, omit the "metrics" member from the request object.
 */
int
noit_websocket_msg_handler(mtev_http_rest_closure_t *restc, int opcode,
                           const unsigned char *msg, size_t msg_len)
{
#ifdef HAVE_WSLAY
  const char *error = NULL;
  noit_websocket_closure_t *handler_data = restc->call_closure;

  /* for now this message is JSON, that might change in the future */

  struct mtev_json_tokener *tok = mtev_json_tokener_new();
  struct mtev_json_object *request = mtev_json_tokener_parse_ex(tok, (const char *)msg, msg_len);
  enum mtev_json_tokener_error err = tok->err;
  mtev_json_tokener_free(tok);
  if (err != mtev_json_tokener_success) {
    error = "Unable to parse incoming json";
    goto websocket_handler_error;
  }

  uint64_t period_ms = mtev_json_object_get_uint64(mtev_json_object_object_get(request, "period_ms"));
  const char *check_uuid = mtev_json_object_get_string(mtev_json_object_object_get(request, "check_uuid"));
  struct mtev_json_object *metrics = mtev_json_object_object_get(request, "metrics");

  if (handler_data != NULL) {
    /* destroy old subscription */
    noit_websocket_closure_free(handler_data);
    handler_data = NULL;
  }

  handler_data = restc->call_closure = noit_websocket_closure_alloc();
  handler_data->use_filter = mtev_false;
  restc->call_closure_free = noit_websocket_closure_free;
  handler_data->period = period_ms;
  if (metrics != NULL) {
    handler_data->use_filter = mtev_true;
    handler_data->filter_count = mtev_json_object_array_length(metrics);
    handler_data->filters = calloc(sizeof(char *), handler_data->filter_count);
    jl_array_list* a = mtev_json_object_get_array(metrics);
    for (int i = 0; i < handler_data->filter_count; i++) {
      struct mtev_json_object *o = jl_array_list_get_idx(a, i);
      if (o != NULL) {
        handler_data->filters[i] = strdup(mtev_json_object_get_string(o));
      }
    }
  }

  handler_data->restc = restc;

  strncpy(handler_data->uuid_str, check_uuid, UUID_STR_LEN);
  handler_data->uuid_str[36] = '\0';
  if(uuid_parse(handler_data->uuid_str, handler_data->uuid)) {
    mtevL(noit_error, "bad uuid received in livestream websocket_handler '%s'\n", handler_data->uuid_str);
    error = "bad uuid received in livestream subscription request";
    goto websocket_handler_error;
  }
  mtev_json_object_put(request);

  /* setup subscription to noit_livestream */
  asprintf(&handler_data->feed, "websocket_livestream/%d", mtev_atomic_inc32(&ls_counter));
  handler_data->log_stream = mtev_log_stream_new(handler_data->feed, "noit_websocket_livestream", handler_data->feed,
                                                 handler_data, NULL);

  handler_data->check = noit_check_watch(handler_data->uuid, handler_data->period);
  if(!handler_data->check) {
    error = "Cannot locate check";
    goto websocket_handler_error;
  }

  /* This check must be watched from the livestream */
  noit_check_transient_add_feed(handler_data->check, handler_data->feed);

  /* Note the check */
  noit_check_log_check(handler_data->check);

  /* kick it off, if it isn't running already */
  if(!NOIT_CHECK_LIVE(handler_data->check)) noit_check_activate(handler_data->check);

  return 0;

 websocket_handler_error:
  {
    struct mtev_json_object *e = mtev_json_object_new_object();
    mtev_json_object_object_add(e, "success", mtev_json_object_new_int(0));
    struct mtev_json_object *errors = mtev_json_object_new_array();
    struct mtev_json_object *error_o = mtev_json_object_new_object();
    mtev_json_object_object_add(error_o, "error", mtev_json_object_new_string(error));
    mtev_json_object_array_add(errors, error_o);
    mtev_json_object_object_add(e, "errors", errors);

    const char *json_error = mtev_json_object_to_json_string(e);
    mtev_http_websocket_queue_msg(restc->http_ctx,
                                  WSLAY_TEXT_FRAME | WSLAY_CONNECTION_CLOSE,
                                  (const unsigned char *)json_error, strlen(json_error));
    mtev_json_object_put(e);
  }
#endif
  return -1;
}