void cetcd_client_destroy(cetcd_client *cli) { cetcd_addresses_release(cli->addresses); cetcd_array_release(cli->addresses); curl_easy_cleanup(cli->curl); curl_global_cleanup(); cetcd_array_destroy(&cli->watchers); }
/* * cetcd_cluster_request tries to request the whole cluster. It round-robin to next server if the request failed * */ void *cetcd_cluster_request(cetcd_client *cli, cetcd_request *req) { size_t i, count; cetcd_string url; cetcd_error *err = NULL; cetcd_response *resp = NULL; cetcd_array *addrs = NULL; void *res = NULL; count = cetcd_array_size(cli->addresses); for(i = 0; i < count; ++i) { url = sdscatprintf(sdsempty(), "http://%s/%s", (cetcd_string)cetcd_array_get(cli->addresses, cli->picked), req->uri); req->url = url; req->cli = cli; res = cetcd_send_request(cli->curl, req); sdsfree(url); /*api_type == syncCluster, got address, return*/ if (req->api_type == ETCD_MEMBERS ){ if ((addrs = res)) { if ( cetcd_array_size(addrs)) { return addrs; } else { cetcd_array_destroy(addrs); } } } else { if((resp=res) && resp->err && resp->err->ecode == error_send_request_failed) { if (i != count-1) { cetcd_response_release(resp); resp = NULL; } } else { /*got response, return*/ return resp; } } /*try next*/ if (i != count-1) { cli->picked = (cli->picked + 1) % count; } } /*the whole cluster failed*/ if (req->api_type == ETCD_MEMBERS) return NULL; if (resp) { if(resp->err) { err = resp->err; /*remember last error*/ } resp->err = calloc(1, sizeof(cetcd_error)); resp->err->ecode = error_cluster_failed; resp->err->message = sdsnew("etcd_do_request: all cluster servers failed."); if (err) { resp->err->message = sdscatprintf(resp->err->message, " last error: %s", err->message); cetcd_error_release(err); } resp->err->cause = sdsdup(req->uri); } return resp; }
int main(int argc, char *argv[]) { cetcd_client cli; cetcd_response *resp; cetcd_array addrs; cetcd_array_init(&addrs, 3); cetcd_array_append(&addrs, "http://127.0.0.1:2379"); cetcd_client_init(&cli, &addrs); resp = cetcd_get(&cli, "/radar/service"); if(resp->err) { printf("error :%d, %s (%s)\n", resp->err->ecode, resp->err->message, resp->err->cause); } cetcd_response_print(resp); cetcd_response_release(resp); cetcd_array_destroy(&addrs); cetcd_client_destroy(&cli); return 0; }
size_t cetcd_parse_response(char *ptr, size_t size, size_t nmemb, void *userdata) { int len, i; char *key, *val; cetcd_response_parser *parser; yajl_status status; cetcd_response *resp = NULL; cetcd_array *addrs = NULL; enum resp_parser_st { request_line_start_st, request_line_end_st, request_line_http_status_start_st, request_line_http_status_st, request_line_http_status_end_st, header_key_start_st, header_key_st, header_key_end_st, header_val_start_st, header_val_st, header_val_end_st, blank_line_st, json_start_st, json_end_st, response_discard_st }; /* Headers we are interested in: * X-Etcd-Index: 14695 * X-Raft-Index: 672930 * X-Raft-Term: 12 * */ parser = userdata; if (parser->api_type == ETCD_MEMBERS) { addrs = parser->resp; } else { resp = parser->resp; } len = size * nmemb; for (i = 0; i < len; ++i) { if (parser->st == request_line_start_st) { if (ptr[i] == ' ') { parser->st = request_line_http_status_start_st; } continue; } if (parser->st == request_line_end_st) { if (ptr[i] == '\n') { parser->st = header_key_start_st; } continue; } if (parser->st == request_line_http_status_start_st) { parser->buf = sdscatlen(parser->buf, ptr+i, 1); parser->st = request_line_http_status_st; continue; } if (parser->st == request_line_http_status_st) { if (ptr[i] == ' ') { parser->st = request_line_http_status_end_st; } else { parser->buf = sdscatlen(parser->buf, ptr+i, 1); continue; } } if (parser->st == request_line_http_status_end_st) { val = parser->buf; parser->http_status = atoi(val); sdsclear(parser->buf); parser->st = request_line_end_st; if (parser->api_type == ETCD_MEMBERS && parser->http_status != 200) { parser->st = response_discard_st; } continue; } if (parser->st == header_key_start_st) { if (ptr[i] == '\r') { ++i; } if (ptr[i] == '\n') { parser->st = blank_line_st; if (parser->http_status >= 300 && parser->http_status < 400) { /*this is a redirection, restart the state machine*/ parser->st = request_line_start_st; break; } continue; } parser->st = header_key_st; } if (parser->st == header_key_st) { parser->buf = sdscatlen(parser->buf, ptr+i, 1); if (ptr[i] == ':') { parser->st = header_key_end_st; } else { continue; } } if (parser->st == header_key_end_st) { parser->st = header_val_start_st; continue; } if (parser->st == header_val_start_st) { if (ptr[i] == ' ') { continue; } parser->st = header_val_st; } if (parser->st == header_val_st) { if (ptr[i] == '\r') { ++i; } if (ptr[i] == '\n') { parser->st = header_val_end_st; } else { parser->buf = sdscatlen(parser->buf, ptr+i, 1); continue; } } if (parser->st == header_val_end_st) { parser->st = header_key_start_st; if (parser->api_type == ETCD_MEMBERS) { continue; } int count = 0; sds *kvs = sdssplitlen(parser->buf, sdslen(parser->buf), ":", 1, &count); sdsclear(parser->buf); if (count < 2) { sdsfreesplitres(kvs, count); continue; } key = kvs[0]; val = kvs[1]; if (strncmp(key, "X-Etcd-Index", sizeof("X-Etcd-Index")-1) == 0) { resp->etcd_index = atoi(val); } else if (strncmp(key, "X-Raft-Index", sizeof("X-Raft-Index")-1) == 0) { resp->raft_index = atoi(val); } else if (strncmp(key, "X-Raft-Term", sizeof("X-Raft-Term")-1) == 0) { resp->raft_term = atoi(val); } sdsfreesplitres(kvs, count); continue; } if (parser->st == blank_line_st) { if (ptr[i] != '{') { /*not a json response, discard*/ parser->st = response_discard_st; if (resp->err == NULL && parser->api_type == ETCD_KEYS) { resp->err = calloc(1, sizeof(cetcd_error)); resp->err->ecode = error_response_parsed_failed; resp->err->message = sdsnew("not a json response"); resp->err->cause = sdsnewlen(ptr, len); } continue; } parser->st = json_start_st; cetcd_array_init(&parser->ctx.keystack, 10); cetcd_array_init(&parser->ctx.nodestack, 10); if (parser->api_type == ETCD_MEMBERS) { parser->ctx.userdata = addrs; parser->json = yajl_alloc(&sync_callbacks, 0, &parser->ctx); } else { if (parser->http_status != 200 && parser->http_status != 201) { resp->err = calloc(1, sizeof(cetcd_error)); parser->ctx.userdata = resp->err; parser->json = yajl_alloc(&error_callbacks, 0, &parser->ctx); } else { parser->ctx.userdata = resp; parser->json = yajl_alloc(&callbacks, 0, &parser->ctx); } } } if (parser->st == json_start_st) { if (yajl_status_ok != yajl_parse(parser->json, (const unsigned char *)ptr + i, len - i)) { parser->st = json_end_st; } else { parser->st = response_discard_st; yajl_free(parser->json); cetcd_array_destroy(&parser->ctx.keystack); cetcd_array_destroy(&parser->ctx.nodestack); } } if (parser->st == json_end_st) { status = yajl_complete_parse(parser->json); yajl_free(parser->json); cetcd_array_destroy(&parser->ctx.keystack); cetcd_array_destroy(&parser->ctx.nodestack); /*parse failed, TODO set error message*/ if (status != yajl_status_ok) { if ( parser->api_type == ETCD_KEYS && resp->err == NULL) { resp->err = calloc(1, sizeof(cetcd_error)); resp->err->ecode = error_response_parsed_failed; resp->err->message = sdsnew("http response is invalid json format"); resp->err->cause = sdsnewlen(ptr, len); } return 0; } break; } if (parser->st == response_discard_st) { return len; } } return len; }