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 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)); }
void lcb_server_v1_read_handler(lcb_sockdata_t *sockptr, lcb_ssize_t nr) { lcb_server_t *c; int rv; hrtime_t stop; if (!lcb_sockrw_v1_cb_common(sockptr, NULL, (void **)&c)) { return; } lcb_sockrw_v1_onread_common(sockptr, &c->connection.input, nr); c->inside_handler = 1; if (nr < 1) { event_complete_common(c, LCB_NETWORK_ERROR); return; } lcb_connection_delay_timer(&c->connection); stop = gethrtime(); while ((rv = lcb_proto_parse_single(c, stop)) > 0) { /* do nothing */ } if (rv >= 0) { /* Schedule the read request again */ lcb_sockrw_set_want(&c->connection, LCB_READ_EVENT, 0); } event_complete_common(c, LCB_SUCCESS); }
/** * 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); }
static void v0_generic_handler(lcb_socket_t sock, short which, void *arg) { lcb_connection_t conn = arg; lcb_sockrw_status_t status; lcb_size_t oldnr, newnr; lcb_assert(sock != INVALID_SOCKET); if (which & LCB_WRITE_EVENT) { status = lcb_sockrw_v0_write(conn, conn->output); if (status == LCB_SOCKRW_WROTE) { if ((which & LCB_READ_EVENT) == 0) { lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); } } else if (status == LCB_SOCKRW_WOULDBLOCK) { lcb_sockrw_set_want(conn, LCB_WRITE_EVENT, 0); lcb_sockrw_apply_want(conn); } else { conn->easy.error(conn); return; } } if ( (which & LCB_READ_EVENT) == 0) { return; } oldnr = conn->input->nbytes; status = lcb_sockrw_v0_slurp(conn, conn->input); newnr = conn->input->nbytes; if (status != LCB_SOCKRW_READ && status != LCB_SOCKRW_WOULDBLOCK && oldnr == newnr) { conn->easy.error(conn); } else { conn->easy.read(conn); } }
static void sasl_auth_response_handler(lcb_server_t *server, struct lcb_command_data_st *command_data, protocol_binary_response_header *res) { lcb_uint16_t ret = ntohs(res->response.status); if (ret == PROTOCOL_BINARY_RESPONSE_SUCCESS) { if (server->sasl_conn) { cbsasl_dispose(&server->sasl_conn); } server->sasl_conn = NULL; lcb_server_connected(server); } else if (ret == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE) { protocol_binary_request_no_extras req; lcb_connection_t conn = &server->connection; const char *out, *bytes = (const char *)res + sizeof(res->bytes); unsigned int nout, nbytes = ntohl(res->response.bodylen); if (cbsasl_client_step(server->sasl_conn, bytes, nbytes, NULL, &out, &nout) != SASL_CONTINUE) { lcb_error_handler(server->instance, LCB_AUTH_ERROR, "Unable to perform SASL STEP"); return; } memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SASL_STEP; req.message.header.request.keylen = ntohs((lcb_uint16_t)server->sasl_nmech); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.bodylen = ntohl((lcb_uint32_t)(server->sasl_nmech + nout)); lcb_server_buffer_start_packet(server, command_data->cookie, conn->output, &server->output_cookies, req.bytes, sizeof(req.bytes)); lcb_server_buffer_write_packet(server, conn->output, server->sasl_mech, server->sasl_nmech); lcb_server_buffer_write_packet(server, conn->output, out, nout); lcb_server_buffer_end_packet(server, conn->output); lcb_sockrw_set_want(conn, LCB_WRITE_EVENT, 0); } else { lcb_error_handler(server->instance, LCB_AUTH_ERROR, "SASL authentication failed"); } /* Make it known that this was a success. */ lcb_error_handler(server->instance, LCB_SUCCESS, NULL); (void)command_data; }
static void start_sasl_auth_server(lcb_server_t *server) { protocol_binary_request_no_extras req; lcb_connection_t conn = &server->connection; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SASL_LIST_MECHS; req.message.header.request.keylen = 0; req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.bodylen = 0; lcb_server_buffer_complete_packet(server, NULL, conn->output, &server->output_cookies, req.bytes, sizeof(req.bytes)); lcb_sockrw_set_want(conn, LCB_WRITE_EVENT, 0); }
void lcb_server_send_packets(lcb_server_t *server) { if (server->pending.nbytes > 0 || server->connection.output->nbytes > 0) { if (server->connection_ready) { lcb_sockrw_set_want(&server->connection, LCB_WRITE_EVENT, 0); if (!server->inside_handler) { lcb_sockrw_apply_want(&server->connection); if (!lcb_timer_armed(server->io_timer)) { lcb_timer_rearm(server->io_timer, MCSERVER_TIMEOUT(server)); } } } else if (server->connection.state == LCB_CONNSTATE_UNINIT) { lcb_server_connect(server); } } }
static void config_v1_write_handler(lcb_sockdata_t *sockptr, lcb_io_writebuf_t *wbuf, int status) { lcb_t instance; lcb_connection_t conn = sockptr->lcbconn; if (!lcb_sockrw_v1_cb_common(sockptr, wbuf, (void **)&instance)) { return; } lcb_sockrw_v1_onwrite_common(sockptr, wbuf, &conn->output); if (status) { v1_error_common(instance); } lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); }
static void config_v1_read_handler(lcb_sockdata_t *sockptr, lcb_ssize_t nr) { lcb_t instance; lcb_connection_t conn = sockptr->lcbconn; if (!lcb_sockrw_v1_cb_common(sockptr, NULL, (void **)&instance)) { return; } lcb_sockrw_v1_onread_common(sockptr, &conn->input, nr); if (nr < 1) { v1_error_common(instance); return; } lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); /* automatically does apply_want */ handle_vbstream_read(instance); }
static void v1_generic_write_handler(lcb_sockdata_t *sd, lcb_io_writebuf_t *wbuf, int status) { lcb_t instance; lcb_connection_t conn = sd->lcbconn; if (!lcb_sockrw_v1_cb_common(sd, wbuf, (void **)&instance)) { return; } lcb_sockrw_v1_onwrite_common(sd, wbuf, &sd->lcbconn->output); if (status) { conn->easy.error(conn); } else { lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); } }
void lcb_server_v1_write_handler(lcb_sockdata_t *sockptr, lcb_io_writebuf_t *wbuf, int status) { lcb_server_t *c; if (!lcb_sockrw_v1_cb_common(sockptr, wbuf, (void **)&c)) { return; } lcb_sockrw_v1_onwrite_common(sockptr, wbuf, &c->connection.output); c->inside_handler = 1; if (status) { event_complete_common(c, LCB_NETWORK_ERROR); } else { lcb_sockrw_set_want(&c->connection, LCB_READ_EVENT, 0); event_complete_common(c, LCB_SUCCESS); } }
void lcb_server_v0_event_handler(lcb_socket_t sock, short which, void *arg) { lcb_server_t *c = arg; lcb_connection_t conn = &c->connection; (void)sock; if (which & LCB_WRITE_EVENT) { lcb_sockrw_status_t status; status = lcb_sockrw_v0_write(conn, conn->output); if (status != LCB_SOCKRW_WROTE && status != LCB_SOCKRW_WOULDBLOCK) { event_complete_common(c, LCB_NETWORK_ERROR); return; } } if (which & LCB_READ_EVENT || conn->input->nbytes) { if (do_read_data(c, which & LCB_READ_EVENT) != 0) { /* TODO stash error message somewhere * "Failed to read from connection to \"%s:%s\"", c->hostname, c->port */ event_complete_common(c, LCB_NETWORK_ERROR); return; } } /** * Because of the operations-per-call limit, we might still need to read * a bit more once the event loop calls us again. We can't assume a * non-blocking read if we don't expect any data, but we can usually rely * on a non-blocking write. */ if (conn->output->nbytes || conn->input->nbytes) { which = LCB_RW_EVENT; } else { which = LCB_READ_EVENT; } lcb_sockrw_set_want(conn, which, 1); event_complete_common(c, LCB_SUCCESS); }
/** * Callback from libevent when we read from the REST socket * @param sock the readable socket * @param which what kind of events we may do * @param arg pointer to the libcouchbase instance */ static void config_v0_handler(lcb_socket_t sock, short which, void *arg) { lcb_t instance = arg; lcb_connection_t conn = &instance->bootstrap.via.http.connection; lcb_sockrw_status_t status; lcb_assert(sock != INVALID_SOCKET); if ((which & LCB_WRITE_EVENT) == LCB_WRITE_EVENT) { status = lcb_sockrw_v0_write(conn, conn->output); if (status != LCB_SOCKRW_WROTE && status != LCB_SOCKRW_WOULDBLOCK) { lcb_bootstrap_error(instance, LCB_NETWORK_ERROR, "Problem with sending data. " "Failed to send data to REST server", 0); return; } if (lcb_sockrw_flushed(conn)) { lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); } } if ((which & LCB_READ_EVENT) == 0) { return; } status = lcb_sockrw_v0_slurp(conn, conn->input); if (status != LCB_SOCKRW_READ && status != LCB_SOCKRW_WOULDBLOCK) { lcb_bootstrap_error(instance, LCB_NETWORK_ERROR, "Problem with reading data. " "Failed to send read data from REST server", 0); return; } handle_vbstream_read(instance); (void)sock; }
static void connect_done_handler(lcb_connection_t conn, lcb_error_t err) { lcb_t instance = conn->instance; if (err == LCB_SUCCESS) { /** * Print the URI to the ringbuffer */ ringbuffer_strcat(conn->output, instance->bootstrap.via.http.uri); lcb_assert(conn->output->nbytes > 0); lcb_sockrw_set_want(conn, LCB_RW_EVENT, 0); lcb_sockrw_apply_want(conn); lcb_connection_activate_timer(conn); return; } if (err == LCB_ETIMEDOUT) { timeout_handler(conn, err); } else { lcb_bootstrap_error(instance, err, "Couldn't connect", 0); } }
/** * Common function to handle parsing the event loop for both v0 and v1 io * implementations. */ static lcb_error_t handle_vbstream_read(lcb_t instance) { lcb_error_t err; int can_retry = 0; int old_gen = instance->config.generation; struct lcb_http_bootstrap_st *http = &instance->bootstrap.via.http; lcb_connection_t conn = &instance->bootstrap.via.http.connection; err = lcb_parse_vbucket_stream(instance); if (err == LCB_SUCCESS) { if (instance->type == LCB_TYPE_BUCKET) { lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); } if (old_gen != instance->config.generation || instance->type == LCB_TYPE_CLUSTER) { lcb_connection_cancel_timer(conn); conn->timeout.usec = 0; lcb_maybe_breakout(instance); } return LCB_SUCCESS; } else if (err != LCB_BUSY) { /** * XXX: We only want to retry on some errors. Things which signify an * obvious user error should be left out here; we only care about * actual "network" errors */ switch (err) { case LCB_ENOMEM: case LCB_AUTH_ERROR: case LCB_PROTOCOL_ERROR: case LCB_BUCKET_ENOENT: can_retry = 0; break; default: can_retry = 1; } if (http->bummer && (err == LCB_BUCKET_ENOENT || err == LCB_AUTH_ERROR)) { can_retry = 1; } if (can_retry) { const char *msg = "Failed to get configuration"; lcb_bootstrap_error(instance, err, msg, LCB_CONFERR_NO_BREAKOUT); return err; } else { lcb_maybe_breakout(instance); return lcb_error_handler(instance, err, ""); } } lcb_assert(err == LCB_BUSY); lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); if (old_gen != instance->config.generation) { lcb_connection_cancel_timer(conn); conn->timeout.usec = 0; lcb_maybe_breakout(instance); } return LCB_BUSY; }
static void sasl_list_mech_response_handler(lcb_server_t *server, struct lcb_command_data_st *command_data, protocol_binary_response_header *res) { const char *chosenmech; char *mechlist; const char *data; unsigned int ndata; protocol_binary_request_no_extras req; lcb_size_t bodysize; lcb_uint16_t ret = ntohs(res->response.status); lcb_connection_t conn = &server->connection; if (ret != PROTOCOL_BINARY_RESPONSE_SUCCESS) { lcb_error_handler(server->instance, LCB_AUTH_ERROR, "SASL authentication failed: " "bad SASL_LIST_MECH status code"); return; } bodysize = ntohl(res->response.bodylen); mechlist = calloc(bodysize + 1, sizeof(char)); if (mechlist == NULL) { lcb_error_handler(server->instance, LCB_CLIENT_ENOMEM, NULL); return; } memcpy(mechlist, (const char *)(res + 1), bodysize); if (server->instance->sasl_mech_force) { if (!strstr(mechlist, server->instance->sasl_mech_force)) { lcb_error_handler(server->instance, LCB_SASLMECH_UNAVAILABLE, mechlist); free(mechlist); return; } /** strstr already ensures we have enough space */ strcpy(mechlist, server->instance->sasl_mech_force); } if (cbsasl_client_start(server->sasl_conn, mechlist, NULL, &data, &ndata, &chosenmech) != SASL_OK) { free(mechlist); lcb_error_handler(server->instance, LCB_AUTH_ERROR, "Unable to start sasl client"); return; } free(mechlist); server->sasl_nmech = strlen(chosenmech); server->sasl_mech = strdup(chosenmech); if (server->sasl_mech == NULL) { lcb_error_handler(server->instance, LCB_CLIENT_ENOMEM, NULL); return; } memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SASL_AUTH; req.message.header.request.keylen = ntohs((lcb_uint16_t)server->sasl_nmech); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.bodylen = ntohl((lcb_uint32_t)(server->sasl_nmech + ndata)); lcb_server_buffer_start_packet(server, command_data->cookie, conn->output, &server->output_cookies, req.bytes, sizeof(req.bytes)); lcb_server_buffer_write_packet(server, conn->output, server->sasl_mech, server->sasl_nmech); lcb_server_buffer_write_packet(server, conn->output, data, ndata); lcb_server_buffer_end_packet(server, conn->output); lcb_sockrw_set_want(conn, LCB_WRITE_EVENT, 0); }
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); } }