clconfig_provider * lcb_clconfig_create_http(lcb_confmon *parent) { lcb_error_t status; struct lcb_io_use_st use; http_provider *http = calloc(1, sizeof(*http)); if (!http) { return NULL; } status = lcb_connection_init(&http->connection, parent->settings->io, parent->settings); if (status != LCB_SUCCESS) { free(http); return NULL; } if (! (http->nodes = hostlist_create())) { lcb_connection_cleanup(&http->connection); free(http); return NULL; } http->base.type = LCB_CLCONFIG_HTTP; http->base.refresh = get_refresh; http->base.pause = pause_http; http->base.get_cached = http_get_cached; http->base.shutdown = shutdown_http; http->base.nodes_updated = refresh_nodes; http->base.enabled = 0; http->io_timer = lcb_timer_create_simple(parent->settings->io, http, parent->settings->config_node_timeout, timeout_handler); lcb_timer_disarm(http->io_timer); http->disconn_timer = lcb_timer_create_simple(parent->settings->io, http, parent->settings->bc_http_stream_time, delayed_disconn); lcb_timer_disarm(http->disconn_timer); lcb_connuse_easy(&use, http, io_read_handler, io_error_handler); lcb_connection_use(&http->connection, &use); lcb_string_init(&http->stream.chunk); lcb_string_init(&http->stream.header); lcb_string_init(&http->stream.input); return &http->base; }
static void timer_callback(lcb_socket_t sock, short which, void *arg) { lcb_timer_t timer = arg; lcb_t instance = timer->instance; lcb_assert(TMR_IS_ARMED(timer)); lcb_assert(!TMR_IS_DESTROYED(timer)); timer->state |= LCB_TIMER_S_ENTERED; lcb_timer_disarm(timer); timer->callback(timer, instance, timer->cookie); if (TMR_IS_DESTROYED(timer) == 0 && TMR_IS_PERIODIC(timer) != 0) { lcb_timer_rearm(timer, timer->usec_); return; } if (! TMR_IS_STANDALONE(timer)) { lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_TIMER, timer); lcb_maybe_breakout(instance); } if (TMR_IS_DESTROYED(timer)) { destroy_timer(timer); } else { timer->state &= ~LCB_TIMER_S_ENTERED; } (void)sock; (void)which; }
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; }
clconfig_provider * lcb_clconfig_create_cccp(lcb_confmon *mon) { cccp_provider *cccp = calloc(1, sizeof(*cccp)); cccp->nodes = hostlist_create(); cccp->base.type = LCB_CLCONFIG_CCCP; cccp->base.refresh = cccp_get; cccp->base.get_cached = cccp_get_cached; cccp->base.pause = cccp_pause; cccp->base.shutdown = cccp_cleanup; cccp->base.nodes_updated = nodes_updated; cccp->base.parent = mon; cccp->base.enabled = 0; cccp->timer = lcb_timer_create_simple(mon->settings->io, cccp, mon->settings->config_timeout, socket_timeout); lcb_timer_disarm(cccp->timer); if (!cccp->nodes) { free(cccp); return NULL; } if (lcb_connection_init(&cccp->connection, cccp->base.parent->settings->io, cccp->base.parent->settings) != LCB_SUCCESS) { free(cccp); return NULL; } return &cccp->base; }
void lcb_clconfig_set_http_always_on(clconfig_provider *pb) { http_provider *http = (http_provider *)pb; if (!pb->enabled) { return; } lcb_timer_disarm(http->disconn_timer); http->always_on = 1; }
/** * Call this if the configuration generation has changed. */ static void set_new_config(http_provider *http) { if (http->current_config) { lcb_clconfig_decref(http->current_config); } http->current_config = http->stream.config; lcb_clconfig_incref(http->current_config); lcb_confmon_provider_success(&http->base, http->current_config); lcb_timer_disarm(http->io_timer); }
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 lcb_error_t cccp_pause(clconfig_provider *pb) { cccp_provider *cccp = (cccp_provider *)pb; if (!cccp->server_active) { return LCB_SUCCESS; } cccp->server_active = 0; release_socket(cccp, 0); lcb_timer_disarm(cccp->timer); return LCB_SUCCESS; }
LCB_INTERNAL_API void lcb_timer_rearm(lcb_timer_t timer, lcb_uint32_t usec) { if (TMR_IS_ARMED(timer)) { lcb_timer_disarm(timer); } timer->usec_ = usec; timer->io->timer.schedule(timer->io->p, timer->event, usec, timer, timer_callback); timer->state |= LCB_TIMER_S_ARMED; }
static void delayed_disconn(lcb_timer_t tm, lcb_t instance, const void *cookie) { http_provider *http = (http_provider *)cookie; lcb_log(LOGARGS(http, DEBUG), "Stopping HTTP provider %p", http); /** closes the connection and cleans up the timer */ close_current(http); lcb_timer_disarm(http->io_timer); reset_stream_state(http); (void)tm; (void)instance; }
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; }
LIBCOUCHBASE_API lcb_error_t lcb_timer_destroy(lcb_t instance, lcb_timer_t timer) { int standalone = timer->options & LCB_TIMER_STANDALONE; if (!standalone) { lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_TIMER, timer); } lcb_timer_disarm(timer); if (timer->state & LCB_TIMER_S_ENTERED) { timer->state |= LCB_TIMER_S_DESTROYED; lcb_assert(TMR_IS_DESTROYED(timer)); } else { destroy_timer(timer); } 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; }
lcb_error_t lcb_server_initialize(lcb_server_t *server, int servernum) { /* Initialize all members */ lcb_error_t err; char *p; const char *n = vbucket_config_get_server(server->instance->vbucket_config, servernum); err = lcb_connection_init(&server->connection, server->instance->settings.io, &server->instance->settings); if (err != LCB_SUCCESS) { return err; } server->connection.data = server; server->index = servernum; server->authority = strdup(n); strcpy(server->curhost.host, n); p = strchr(server->curhost.host, ':'); *p = '\0'; strcpy(server->curhost.port, p + 1); n = vbucket_config_get_couch_api_base(server->instance->vbucket_config, servernum); server->couch_api_base = (n != NULL) ? strdup(n) : NULL; n = vbucket_config_get_rest_api_server(server->instance->vbucket_config, servernum); server->rest_api_server = strdup(n); server->io_timer = lcb_timer_create_simple(server->instance->settings.io, server, MCSERVER_TIMEOUT(server), tmo_thunk); lcb_timer_disarm(server->io_timer); return LCB_SUCCESS; }
static void io_read_handler(lcb_connection_t conn) { packet_info pi; cccp_provider *cccp = conn->data; lcb_string jsonstr; lcb_error_t err; int rv; lcb_host_t curhost; memset(&pi, 0, sizeof(pi)); rv = lcb_packet_read_ringbuffer(&pi, conn->input); if (rv < 0) { LOG(cccp, ERR, "Couldn't parse packet!?"); mcio_error(cccp, LCB_EINTERNAL); return; } else if (rv == 0) { lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); return; } if (PACKET_STATUS(&pi) != PROTOCOL_BINARY_RESPONSE_SUCCESS) { lcb_log(LOGARGS(cccp, ERR), "CCCP Packet responded with 0x%x; nkey=%d, nbytes=%lu, cmd=0x%x, seq=0x%x", PACKET_STATUS(&pi), PACKET_NKEY(&pi), PACKET_NBODY(&pi), PACKET_OPCODE(&pi), PACKET_OPAQUE(&pi)); switch (PACKET_STATUS(&pi)) { case PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED: case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND: mcio_error(cccp, LCB_NOT_SUPPORTED); break; default: mcio_error(cccp, LCB_PROTOCOL_ERROR); break; } return; } if (!PACKET_NBODY(&pi)) { mcio_error(cccp, LCB_PROTOCOL_ERROR); return; } if (lcb_string_init(&jsonstr)) { mcio_error(cccp, LCB_CLIENT_ENOMEM); return; } if (lcb_string_append(&jsonstr, PACKET_BODY(&pi), PACKET_NBODY(&pi))) { mcio_error(cccp, LCB_CLIENT_ENOMEM); return; } curhost = *lcb_connection_get_host(&cccp->connection); lcb_packet_release_ringbuffer(&pi, conn->input); release_socket(cccp, 1); err = lcb_cccp_update(&cccp->base, curhost.host, &jsonstr); lcb_string_release(&jsonstr); if (err == LCB_SUCCESS) { lcb_timer_disarm(cccp->timer); cccp->server_active = 0; } else { schedule_next_request(cccp, LCB_PROTOCOL_ERROR, 0); } }
/** * Closes the current connection and removes the disconn timer along with it */ static void close_current(http_provider *http) { lcb_timer_disarm(http->disconn_timer); lcb_connection_close(&http->connection); }