static lcb_error_t schedule_next_request(cccp_provider *cccp, lcb_error_t err, int can_rollover) { lcb_server_t *server = NULL; lcb_size_t ii; lcb_host_t *next_host = hostlist_shift_next(cccp->nodes, can_rollover); if (!next_host) { lcb_timer_disarm(cccp->timer); lcb_confmon_provider_failed(&cccp->base, err); cccp->server_active = 0; return err; } /** See if we can find a server */ for (ii = 0; ii < cccp->instance->nservers; ii++) { lcb_server_t *cur = cccp->instance->servers + ii; if (lcb_host_equals(&cur->curhost, next_host)) { server = cur; break; } } if (server) { protocol_binary_request_get_cluster_config req; cccp_cookie *cookie = calloc(1, sizeof(*cookie)); lcb_log(LOGARGS(cccp, INFO), "Re-Issuing CCCP Command on server struct %p", server); cookie->parent = cccp; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = CMD_GET_CLUSTER_CONFIG; req.message.header.request.opaque = ++cccp->instance->seqno; lcb_server_start_packet(server, cookie, &req, sizeof(req.bytes)); lcb_server_end_packet(server); lcb_server_send_packets(server); lcb_timer_rearm(cccp->timer, PROVIDER_SETTING(&cccp->base, config_node_timeout)); } else { cccp->cur_connreq = calloc(1, sizeof(*cccp->cur_connreq)); connmgr_req_init(cccp->cur_connreq, next_host->host, next_host->port, socket_connected); cccp->cur_connreq->data = cccp; connmgr_get(cccp->instance->memd_sockpool, cccp->cur_connreq, PROVIDER_SETTING(&cccp->base, config_node_timeout)); } cccp->server_active = 1; return LCB_SUCCESS; }
static void request_config(cccp_provider *cccp) { protocol_binary_request_set_cluster_config req; lcb_connection_t conn = &cccp->connection; ringbuffer_t *buf = conn->output; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = CMD_GET_CLUSTER_CONFIG; req.message.header.request.opaque = 0xF00D; if (!buf) { if ((buf = calloc(1, sizeof(*buf))) == NULL) { mcio_error(cccp, LCB_CLIENT_ENOMEM); return; } conn->output = buf; } if (!ringbuffer_ensure_capacity(buf, sizeof(req.bytes))) { mcio_error(cccp, LCB_CLIENT_ENOMEM); } ringbuffer_write(buf, req.bytes, sizeof(req.bytes)); lcb_sockrw_set_want(conn, LCB_WRITE_EVENT, 1); lcb_sockrw_apply_want(conn); lcb_timer_rearm(cccp->timer, PROVIDER_SETTING(&cccp->base, config_node_timeout)); }
static void refresh_nodes(clconfig_provider *pb, const hostlist_t newnodes, VBUCKET_CONFIG_HANDLE newconfig) { unsigned int ii; http_provider *http = (http_provider *)pb; hostlist_clear(http->nodes); if (!newconfig) { for (ii = 0; ii < newnodes->nentries; ii++) { hostlist_add_host(http->nodes, newnodes->entries + ii); } goto GT_DONE; } for (ii = 0; (int)ii < vbucket_config_get_num_servers(newconfig); ii++) { lcb_error_t status; const char *ss = vbucket_config_get_rest_api_server(newconfig, ii); lcb_assert(ss != NULL); status = hostlist_add_stringz(http->nodes, ss, LCB_CONFIG_HTTP_PORT); lcb_assert(status == LCB_SUCCESS); } GT_DONE: if (PROVIDER_SETTING(pb, randomize_bootstrap_nodes)) { hostlist_randomize(http->nodes); } }
static lcb_error_t get_refresh(clconfig_provider *provider) { http_provider *http = (http_provider *)provider; /** * We want a grace interval here because we might already be fetching a * connection. HOWEVER we don't want to indefinitely wait on a socket * so we issue a timer indicating how long we expect to wait for a * streaming update until we get something. */ /** If we need a new socket, we do connect_next. */ if (http->connection.state == LCB_CONNSTATE_UNINIT) { lcb_error_t rc = connect_next(http); if (rc != LCB_SUCCESS) { http->as_errcode = rc; lcb_async_signal(http->as_schederr); } return rc; } lcb_timer_disarm(http->disconn_timer); if (http->connection.state == LCB_CONNSTATE_CONNECTED) { lcb_timer_rearm(http->io_timer, PROVIDER_SETTING(provider, config_node_timeout)); } return LCB_SUCCESS; }
static lcb_error_t connect_next(http_provider *http) { char *errinfo = NULL; lcb_error_t err; lcb_conn_params params; lcb_connection_t conn = &http->connection; close_current(http); reset_stream_state(http); params.handler = connect_done_handler; params.timeout = PROVIDER_SETTING(&http->base, config_node_timeout); lcb_log(LOGARGS(http, TRACE), "Starting HTTP Configuration Provider %p", http); err = lcb_connection_cycle_nodes(conn, http->nodes, ¶ms, &errinfo); if (err == LCB_SUCCESS) { err = setup_request_header(http); } else { lcb_log(LOGARGS(http, ERROR), "%p: Couldn't schedule connection (0x%x)", http, err); } return err; }
static void connect_done_handler(lcb_connection_t conn, lcb_error_t err) { http_provider *http = (http_provider *)conn->data; const lcb_host_t *host = lcb_connection_get_host(conn); if (err != LCB_SUCCESS) { lcb_log(LOGARGS(http, ERR), "Connection to REST API @%s:%s failed with code=0x%x", host->host, host->port, err); io_error(http, err); return; } lcb_log(LOGARGS(http, DEBUG), "Successfuly connected to REST API %s:%s", host->host, host->port); lcb_connection_reset_buffers(conn); ringbuffer_strcat(conn->output, http->request_buf); lcb_assert(conn->output->nbytes > 0); lcb_sockrw_set_want(conn, LCB_RW_EVENT, 0); lcb_sockrw_apply_want(conn); lcb_timer_rearm(http->io_timer, PROVIDER_SETTING(&http->base, config_node_timeout)); }
/** * Common function to handle parsing the HTTP stream for both v0 and v1 io * implementations. */ static void read_common(http_provider *http) { lcb_error_t err; lcb_connection_t conn = &http->connection; int old_generation = http->stream.generation; lcb_log(LOGARGS(http, TRACE), "Received %d bytes on HTTP stream", conn->input->nbytes); lcb_timer_rearm(http->io_timer, PROVIDER_SETTING(&http->base, config_node_timeout)); lcb_string_rbappend(&http->stream.chunk, conn->input, 1); err = htvb_parse(&http->stream, http->base.parent->settings->conntype); if (http->stream.generation != old_generation) { lcb_log(LOGARGS(http, DEBUG), "Generation %d -> %d", old_generation, http->stream.generation); set_new_config(http); } else { lcb_log(LOGARGS(http, TRACE), "HTTP not yet done. Err=0x%x", err); } if (err != LCB_BUSY && err != LCB_SUCCESS) { io_error(http, err); return; } lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); }
/** * Determine if we're in compatibility mode with the previous versions of the * library - where the idle timeout is disabled and a perpetual streaming * connection will always remain open (regardless of whether it was triggered * by start_refresh/get_refresh). */ static int is_v220_compat(http_provider *http) { lcb_uint32_t setting = PROVIDER_SETTING(&http->base, bc_http_stream_time); if (setting == (lcb_uint32_t)-1) { return 1; } return 0; }
static void socket_connected(connmgr_request *req) { cccp_provider *cccp = req->data; lcb_connection_t conn = req->conn; struct lcb_nibufs_st nistrs; struct lcb_io_use_st use; struct negotiation_context *ctx; lcb_error_t err; free(req); cccp->cur_connreq = NULL; LOG(cccp, DEBUG, "CCCP Socket connected"); if (!conn) { mcio_error(cccp, LCB_CONNECT_ERROR); return; } lcb_connuse_easy(&use, cccp, io_read_handler, io_error_handler); lcb_connection_transfer_socket(conn, &cccp->connection, &use); conn = NULL; if (cccp->connection.protoctx) { /** Already have SASL */ if ((err = lcb_connection_reset_buffers(&cccp->connection)) != LCB_SUCCESS) { mcio_error(cccp, err); return; } request_config(cccp); return; } if (!lcb_get_nameinfo(&cccp->connection, &nistrs)) { mcio_error(cccp, LCB_EINTERNAL); return; } ctx = lcb_negotiation_create(&cccp->connection, cccp->base.parent->settings, PROVIDER_SETTING(&cccp->base, config_node_timeout), nistrs.remote, nistrs.local, &err); if (!ctx) { mcio_error(cccp, err); } ctx->complete = negotiation_done; ctx->data = cccp; cccp->connection.protoctx = ctx; cccp->connection.protoctx_dtor = (protoctx_dtor_t)lcb_negotiation_destroy; }
static lcb_error_t pause_http(clconfig_provider *pb) { http_provider *http = (http_provider *)pb; if (is_v220_compat(http)) { return LCB_SUCCESS; } if (!lcb_timer_armed(http->disconn_timer)) { lcb_timer_rearm(http->disconn_timer, PROVIDER_SETTING(pb, bc_http_stream_time)); } return LCB_SUCCESS; }
void lcb_clconfig_cccp_set_nodes(clconfig_provider *pb, const hostlist_t nodes) { unsigned ii; cccp_provider *cccp = (cccp_provider *)pb; hostlist_clear(cccp->nodes); for (ii = 0; ii < nodes->nentries; ii++) { hostlist_add_host(cccp->nodes, nodes->entries + ii); } if (PROVIDER_SETTING(pb, randomize_bootstrap_nodes)) { hostlist_randomize(cccp->nodes); } }
static lcb_error_t pause_http(clconfig_provider *pb) { http_provider *http = (http_provider *)pb; if (http->always_on) { lcb_timer_disarm(http->io_timer); return LCB_SUCCESS; } if (!lcb_timer_armed(http->disconn_timer)) { lcb_timer_rearm(http->disconn_timer, PROVIDER_SETTING(pb, bc_http_stream_time)); } return LCB_SUCCESS; }
/** * Call when there is an error in I/O. This includes read, write, connect * and timeouts. */ static lcb_error_t io_error(http_provider *http) { lcb_error_t err; lcb_conn_params params; char *errinfo; close_current(http); params.timeout = PROVIDER_SETTING(&http->base, config_node_timeout); params.handler = connect_done_handler; err = lcb_connection_next_node(&http->connection, http->nodes, ¶ms, &errinfo); if (err != LCB_SUCCESS) { lcb_confmon_provider_failed(&http->base, err); lcb_timer_disarm(http->io_timer); return err; } else { setup_request_header(http); } return LCB_SUCCESS; }
/** * Call when there is an error in I/O. This includes read, write, connect * and timeouts. */ static lcb_error_t io_error(http_provider *http, lcb_error_t origerr) { lcb_error_t err; lcb_conn_params params; char *errinfo; int can_retry = 0; close_current(http); params.timeout = PROVIDER_SETTING(&http->base, config_node_timeout); params.handler = connect_done_handler; if (http->base.parent->config) { can_retry = 1; } else if (origerr != LCB_AUTH_ERROR && origerr != LCB_BUCKET_ENOENT) { can_retry = 1; } if (can_retry) { err = lcb_connection_next_node( &http->connection, http->nodes, ¶ms, &errinfo); } else { err = origerr; } if (err != LCB_SUCCESS) { lcb_confmon_provider_failed(&http->base, origerr); lcb_timer_disarm(http->io_timer); if (is_v220_compat(http)) { lcb_log(LOGARGS(http, INFO), "HTTP node list finished. Looping again (disconn_tmo=-1)"); connect_next(http); } return origerr; } else { setup_request_header(http); } return LCB_SUCCESS; }
static void nodes_updated(clconfig_provider *provider, hostlist_t nodes, VBUCKET_CONFIG_HANDLE vbc) { int ii; cccp_provider *cccp = (cccp_provider *)provider; if (!vbc) { return; } if (vbucket_config_get_num_servers(vbc) < 1) { return; } hostlist_clear(cccp->nodes); for (ii = 0; ii < vbucket_config_get_num_servers(vbc); ii++) { const char *mcaddr = vbucket_config_get_server(vbc, ii); hostlist_add_stringz(cccp->nodes, mcaddr, LCB_CONFIG_MCD_PORT); } if (PROVIDER_SETTING(provider, randomize_bootstrap_nodes)) { hostlist_randomize(cccp->nodes); } (void)nodes; }
static int load_cache(file_provider *provider) { lcb_string str; char line[1024]; lcb_ssize_t nr; int fail; FILE *fp = NULL; lcbvb_CONFIG *config = NULL; char *end; struct stat st; int status = -1; lcb_string_init(&str); if (provider->filename == NULL) { return -1; } fp = fopen(provider->filename, "r"); if (fp == NULL) { int save_errno = errno; lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't open for reading: %s", LOGID(provider), strerror(save_errno)); return -1; } if (fstat(fileno(fp), &st)) { provider->last_errno = errno; goto GT_DONE; } if (provider->last_mtime == st.st_mtime) { lcb_log(LOGARGS(provider, WARN), LOGFMT "Modification time too old", LOGID(provider)); goto GT_DONE; } config = lcbvb_create(); if (config == NULL) { goto GT_DONE; } lcb_string_init(&str); while ((nr = fread(line, 1, sizeof(line), fp)) > 0) { if (lcb_string_append(&str, line, nr)) { goto GT_DONE; } } if (ferror(fp)) { goto GT_DONE; } fclose(fp); fp = NULL; if (!str.nused) { status = -1; goto GT_DONE; } end = strstr(str.base, CONFIG_CACHE_MAGIC); if (end == NULL) { lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't find magic", LOGID(provider)); if (!provider->ro_mode) { remove(provider->filename); } status = -1; goto GT_DONE; } fail = lcbvb_load_json(config, str.base); if (fail) { status = -1; lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't parse configuration", LOGID(provider)); lcb_log_badconfig(LOGARGS(provider, ERROR), config, str.base); if (!provider->ro_mode) { remove(provider->filename); } goto GT_DONE; } if (lcbvb_get_distmode(config) != LCBVB_DIST_VBUCKET) { status = -1; lcb_log(LOGARGS(provider, ERROR), LOGFMT "Not applying cached memcached config", LOGID(provider)); goto GT_DONE; } if (strcmp(config->bname, PROVIDER_SETTING(&provider->base, bucket)) != 0) { status = -1; lcb_log(LOGARGS(provider, ERROR), LOGFMT "Bucket name in file is different from the one requested", LOGID(provider)); } if (provider->config) { lcb_clconfig_decref(provider->config); } provider->config = lcb_clconfig_create(config, LCB_CLCONFIG_FILE); provider->config->cmpclock = gethrtime(); provider->config->origin = provider->base.type; provider->last_mtime = st.st_mtime; status = 0; config = NULL; GT_DONE: if (fp != NULL) { fclose(fp); } if (config != NULL) { lcbvb_destroy(config); } lcb_string_release(&str); return status; }