fix memory leak bugs
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
add_library(common src/tfe_stat.cpp src/tfe_utils.cpp src/tfe_future.cpp src/tfe_http.cpp src/tfe_plugin.cpp)
|
||||
add_library(common src/tfe_stat.cpp src/tfe_utils.cpp src/tfe_future.cpp src/tfe_http.cpp src/tfe_plugin.cpp src/tfe_rpc.cpp)
|
||||
target_include_directories(common PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
target_link_libraries(common MESA_handle_logger)
|
||||
target_link_libraries(common MESA_handle_logger libevent-static libevent-static-openssl libevent-static-pthreads)
|
||||
|
||||
27
common/include/tfe_rpc.h
Normal file
27
common/include/tfe_rpc.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "tfe_future.h"
|
||||
#include "event2/event.h"
|
||||
|
||||
struct tfe_rpc_response_result{
|
||||
int status_code;
|
||||
const char* status_msg;
|
||||
char* data;
|
||||
int len;
|
||||
};
|
||||
|
||||
enum TFE_RPC_FLAG
|
||||
{
|
||||
CHUNK_CB = 0,
|
||||
DONE_CB,
|
||||
};
|
||||
|
||||
enum TFE_RPC_METHOD
|
||||
{
|
||||
GET = 0,
|
||||
POST,
|
||||
};
|
||||
|
||||
|
||||
struct tfe_rpc_response_result* tfe_rpc_release(void* result);
|
||||
|
||||
void tfe_rpc_async_ask(struct future* f, const char* url, enum TFE_RPC_METHOD method, enum TFE_RPC_FLAG flag, const char* data, int data_len, struct event_base * evbase);
|
||||
@@ -25,7 +25,7 @@ struct _future_promise_debug
|
||||
int fsid_latency;
|
||||
int fsid_failed;
|
||||
long long succ_times;
|
||||
struct timespec create_time;
|
||||
struct timespec create_time;
|
||||
};
|
||||
struct future
|
||||
{
|
||||
@@ -52,7 +52,7 @@ void future_promise_library_init(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int value=0;
|
||||
memset(&g_FP_instance,0,sizeof(g_FP_instance));
|
||||
MESA_htable_handle htable = MESA_htable_born();
|
||||
@@ -90,7 +90,7 @@ void future_promise_library_init(void)
|
||||
}
|
||||
|
||||
struct promise * future_to_promise(struct future * f)
|
||||
{
|
||||
{
|
||||
return (struct promise *) f;
|
||||
}
|
||||
struct field_get_set_args
|
||||
@@ -116,7 +116,7 @@ static long field_get_set_cb(void * data, const uchar * key, uint size, void * u
|
||||
field_id[1]=FS_register(args->fs_handle, FS_STYLE_FIELD, FS_CALC_SPEED,buff);
|
||||
args->fsid_failed=field_id[1];
|
||||
ret = MESA_htable_add(args->htable, key, size, (void*)field_id);
|
||||
assert(ret>=0);
|
||||
assert(ret>=0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -134,7 +134,7 @@ struct future * future_create(const char* symbol, future_success_cb * cb_success
|
||||
p->f.cb_success = cb_success;
|
||||
p->f.cb_failed = cb_failed;
|
||||
strncpy(p->f.symbol,symbol,sizeof(p->f.symbol));
|
||||
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC,&p->debug.create_time);
|
||||
long cb_ret=0;
|
||||
struct field_get_set_args args={.htable = g_FP_instance.name_table, .fs_handle = g_FP_instance.fs_handle};
|
||||
|
||||
242
common/src/tfe_rpc.cpp
Normal file
242
common/src/tfe_rpc.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
#include "tfe_rpc.h"
|
||||
#include "event2/http.h"
|
||||
#include "event2/http_struct.h"
|
||||
#include "event2/event.h"
|
||||
#include "event2/buffer.h"
|
||||
#include "event2/dns.h"
|
||||
#include "event2/thread.h"
|
||||
#include "tfe_utils.h"
|
||||
#include "MESA/MESA_handle_logger.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#define MODULE_NAME "tfe_rpc"
|
||||
|
||||
|
||||
struct tfe_rpc_ctx
|
||||
{
|
||||
struct event_base * evbase;
|
||||
enum TFE_RPC_FLAG flag;
|
||||
};
|
||||
|
||||
/*
|
||||
//will be called after receiving and parsing the full header. It allows analyzing the header and possibly closing the connection by returning a value < 0.
|
||||
int read_header_done_cb(struct evhttp_request* response, void* arg)
|
||||
{
|
||||
//struct promise* p = (struct promise*)arg;
|
||||
printf("call read_header_done_cb!\n");
|
||||
printf("< HTTP/1.1 %d %s\n", evhttp_request_get_response_code(response), evhttp_request_get_response_code_line(response));
|
||||
return 0;
|
||||
struct evkeyvalq* headers = evhttp_request_get_input_headers(response);
|
||||
struct evkeyval* header;
|
||||
TAILQ_FOREACH(header, headers, next){
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_INFO, MODULE_NAME, "< %s: %s\n", header->key, header->value);
|
||||
}
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_INFO, MODULE_NAME, "< \n");
|
||||
}
|
||||
*/
|
||||
|
||||
static void tfe_rpc_promise_free_ctx(void* ctx)
|
||||
{
|
||||
free(ctx);
|
||||
ctx = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
static void _wrapped_promise_success(struct promise* p, void* result)
|
||||
{
|
||||
struct tfe_rpc_ctx* ctx = (struct tfe_rpc_ctx*)promise_get_ctx(p);
|
||||
struct tfe_rpc_response_result* _result = (struct tfe_rpc_response_result*)result;
|
||||
if(ctx->flag == CHUNK_CB && _result->len > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(ctx->evbase)
|
||||
{
|
||||
event_base_loopexit(ctx->evbase, 0);
|
||||
}
|
||||
//promise_dettach_ctx(p);
|
||||
//tfe_rpc_promise_free_ctx(ctx);
|
||||
promise_success(p, result);
|
||||
return;
|
||||
}
|
||||
|
||||
static void _wrapped_promise_failed(struct promise * p, enum e_future_error error, const char * what)
|
||||
{
|
||||
struct tfe_rpc_ctx* ctx = (struct tfe_rpc_ctx*)promise_get_ctx(p);
|
||||
if(ctx->evbase)
|
||||
{
|
||||
event_base_loopexit(ctx->evbase, 0);
|
||||
}
|
||||
promise_failed(p, error, what);
|
||||
//promise_dettach_ctx(p);
|
||||
//ctx_destroy_cb(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//will be called after every read of data with the same argument as the completion callback. Will never be called on an empty response. May drain the input buffer; it will be drained automatically on return.
|
||||
//will drain automaticly
|
||||
void read_chunk_cb(struct evhttp_request* response, void* arg)
|
||||
{
|
||||
struct promise* p = (struct promise*)arg;
|
||||
//printf("call get_chunk_cb\n");
|
||||
if(response == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
struct evbuffer* evbuf = evhttp_request_get_input_buffer(response);
|
||||
if(evbuf == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int evbuf_len = evbuffer_get_length(evbuf);
|
||||
char* data = (char*)evbuffer_pullup(evbuf, evbuf_len);
|
||||
//printf("data is %s\n", data==NULL ? "NULL":"NOT NULL");
|
||||
struct tfe_rpc_response_result* result = ALLOC(struct tfe_rpc_response_result, 1);
|
||||
result->status_code = evhttp_request_get_response_code(response);
|
||||
result->status_msg = evhttp_request_get_response_code_line(response);
|
||||
result->data = data;
|
||||
result->len = evbuf_len;
|
||||
_wrapped_promise_success(p, result);
|
||||
free(result);
|
||||
}
|
||||
|
||||
//The callback is executed when the request completed or an error occurred
|
||||
void get_response_cb(struct evhttp_request* response, void* arg)
|
||||
{
|
||||
//printf("call get_response_cb\n");
|
||||
read_chunk_cb(response, arg);
|
||||
}
|
||||
|
||||
|
||||
//On error, both the error callback and the regular callback will be called, error callback is called before the regular callback.
|
||||
void request_error_cb(enum evhttp_request_error error, void* arg)
|
||||
{
|
||||
//printf("call request_error_cb\n");
|
||||
struct promise* p = (struct promise*)arg;
|
||||
switch(error)
|
||||
{
|
||||
case EVREQ_HTTP_TIMEOUT:
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_TIMEOUT, "EVREQ_HTTP_TIMEOUT");
|
||||
break;
|
||||
case EVREQ_HTTP_EOF:
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "EVREQ_HTTP_EOF");
|
||||
break;
|
||||
case EVREQ_HTTP_INVALID_HEADER:
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "EVREQ_HTTP_INVALID_HEADER");
|
||||
break;
|
||||
case EVREQ_HTTP_BUFFER_ERROR:
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "EVREQ_HTTP_BUFFER_ERROR");
|
||||
break;
|
||||
case EVREQ_HTTP_REQUEST_CANCEL:
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_CANCEL, "EVREQ_HTTP_REQUEST_CANCEL");
|
||||
break;
|
||||
case EVREQ_HTTP_DATA_TOO_LONG:
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "EVREQ_HTTP_DATA_TOO_LONG");
|
||||
break;
|
||||
default:
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "EVREQ_HTTP_UNKOWN_EXCEPTION");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//when to close a connection ???
|
||||
//Set a callback for connection close
|
||||
void connection_close_cb(struct evhttp_connection* connection, void* arg)
|
||||
{
|
||||
//printf("call connection_close_cb\n");
|
||||
}
|
||||
|
||||
//data is for POST. if method is GET, data should be NULL
|
||||
void tfe_rpc_async_ask(struct future* f, const char* url, enum TFE_RPC_METHOD method, enum TFE_RPC_FLAG flag, const char* data, int data_len, struct event_base * evbase)
|
||||
{
|
||||
struct promise* p = future_to_promise(f);
|
||||
struct tfe_rpc_ctx* ctx = ALLOC(struct tfe_rpc_ctx, 1);
|
||||
ctx->evbase = evbase;
|
||||
ctx->flag = flag;
|
||||
promise_set_ctx(p, (void*)ctx, tfe_rpc_promise_free_ctx);
|
||||
if(!evbase)
|
||||
{
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "event base is NULL");
|
||||
return;
|
||||
}
|
||||
struct evhttp_uri* uri = evhttp_uri_parse(url);
|
||||
if(NULL == uri)
|
||||
{
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "parse url failed!");
|
||||
return;
|
||||
}
|
||||
const char* host = evhttp_uri_get_host(uri);
|
||||
if(!host)
|
||||
{
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "parse host failed!");
|
||||
return;
|
||||
}
|
||||
int port = evhttp_uri_get_port(uri);
|
||||
if(port < 0)
|
||||
{
|
||||
port = 80;
|
||||
}
|
||||
const char* path = evhttp_uri_get_path(uri);
|
||||
const char* query = evhttp_uri_get_query(uri);
|
||||
char request_url[TFE_STRING_MAX] = "";
|
||||
if(path == NULL || strnlen(path, TFE_STRING_MAX) == 0)
|
||||
{
|
||||
snprintf(request_url, TFE_STRING_MAX, "/");
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(request_url, TFE_STRING_MAX, "%s", path);
|
||||
}
|
||||
if(query && strnlen(query, TFE_STRING_MAX))
|
||||
{
|
||||
strncat(request_url, "?", TFE_STRING_MAX);
|
||||
strncat(request_url, query, TFE_STRING_MAX);
|
||||
}
|
||||
//printf("url:%s host:%s port:%d path:%s query:%s request_url:%s\n", url, host, port, path, query, request_url);
|
||||
struct evdns_base* dnsbase = evdns_base_new(evbase, EVDNS_BASE_INITIALIZE_NAMESERVERS);
|
||||
if (!dnsbase)
|
||||
{
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "create dns base failed!");
|
||||
return;
|
||||
}
|
||||
struct evhttp_connection* connection = evhttp_connection_base_new(evbase, dnsbase, host, port);
|
||||
if (!connection)
|
||||
{
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "create connection failed!");
|
||||
return;
|
||||
}
|
||||
evhttp_connection_set_closecb(connection, connection_close_cb, evbase);
|
||||
struct evhttp_request* request = evhttp_request_new(get_response_cb, (void*)p);
|
||||
//evhttp_request_set_header_cb(request, read_header_done_cb);
|
||||
if(flag == CHUNK_CB)
|
||||
{
|
||||
evhttp_request_set_chunked_cb(request, read_chunk_cb);
|
||||
}
|
||||
evhttp_request_set_error_cb(request, request_error_cb);
|
||||
evhttp_add_header(evhttp_request_get_output_headers(request), "Host", host);
|
||||
switch(method)
|
||||
{
|
||||
case GET:
|
||||
evhttp_make_request(connection, request, EVHTTP_REQ_GET, request_url);
|
||||
break;
|
||||
case POST:
|
||||
evbuffer_add(request->output_buffer, data, data_len);
|
||||
evhttp_make_request(connection, request, EVHTTP_REQ_POST, request_url);
|
||||
break;
|
||||
default:
|
||||
_wrapped_promise_failed(p, FUTURE_ERROR_EXCEPTION, "method is invalid!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct tfe_rpc_response_result* tfe_rpc_release(void* result)
|
||||
{
|
||||
struct tfe_rpc_response_result* response = (struct tfe_rpc_response_result*)result;
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
add_executable(tfe src/key_keeper.cpp src/tfe_rpc.cpp src/kni_acceptor.cpp src/ssl_stream.cpp src/ssl_sess_cache.cpp src/ssl_utils.cc src/tcp_stream.cpp src/main.cpp src/proxy.cpp)
|
||||
add_executable(tfe src/key_keeper.cpp src/kni_acceptor.cpp src/ssl_stream.cpp src/ssl_sess_cache.cpp src/ssl_utils.cc src/tcp_stream.cpp src/main.cpp src/proxy.cpp)
|
||||
|
||||
target_include_directories(tfe PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/external)
|
||||
target_include_directories(tfe PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include/internal)
|
||||
@@ -12,6 +12,7 @@ target_link_libraries(tfe pthread dl
|
||||
libevent-static-pthreads
|
||||
MESA_handle_logger
|
||||
MESA_prof_load
|
||||
cjson
|
||||
MESA_htable wiredcfg
|
||||
MESA_field_stat)
|
||||
|
||||
@@ -20,7 +21,7 @@ target_link_libraries(tfe -Wl,--whole-archive http -Wl,--no-whole-archive)
|
||||
install(TARGETS tfe RUNTIME DESTINATION ./)
|
||||
|
||||
### test_key_keeper
|
||||
add_executable(test_key_keeper test/test_key_keeper.cpp src/key_keeper.cpp src/tfe_rpc.cpp src/ssl_stream.cpp src/ssl_sess_cache.cpp src/ssl_utils.cc src/tcp_stream.cpp)
|
||||
add_executable(test_key_keeper test/test_key_keeper.cpp src/key_keeper.cpp src/ssl_sess_cache.cpp src/ssl_utils.cc )
|
||||
|
||||
target_include_directories(test_key_keeper PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include/internal)
|
||||
|
||||
@@ -33,13 +34,14 @@ target_link_libraries(test_key_keeper pthread dl
|
||||
libevent-static-pthreads
|
||||
MESA_handle_logger
|
||||
MESA_prof_load
|
||||
cjson
|
||||
MESA_htable wiredcfg
|
||||
MESA_field_stat)
|
||||
|
||||
install(TARGETS test_key_keeper RUNTIME DESTINATION ./)
|
||||
|
||||
### test_tfe_rpc
|
||||
add_executable(test_tfe_rpc test/test_tfe_rpc.cpp src/key_keeper.cpp src/tfe_rpc.cpp src/ssl_stream.cpp src/ssl_sess_cache.cpp src/ssl_utils.cc src/tcp_stream.cpp)
|
||||
add_executable(test_tfe_rpc test/test_tfe_rpc.cpp src/key_keeper.cpp src/ssl_sess_cache.cpp src/ssl_utils.cc)
|
||||
|
||||
target_include_directories(test_tfe_rpc PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include/internal)
|
||||
|
||||
@@ -53,6 +55,7 @@ target_link_libraries(test_tfe_rpc pthread dl
|
||||
MESA_handle_logger
|
||||
MESA_prof_load
|
||||
MESA_htable wiredcfg
|
||||
cjson
|
||||
MESA_field_stat)
|
||||
|
||||
install(TARGETS test_tfe_rpc RUNTIME DESTINATION ./)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
#include "tfe_future.h"
|
||||
#include "event2/event.h"
|
||||
|
||||
struct tfe_rpc_response_result{
|
||||
int status_code;
|
||||
const char* status_msg;
|
||||
const char* data;
|
||||
int len;
|
||||
};
|
||||
|
||||
enum TFE_RPC_FLAG
|
||||
{
|
||||
CHUNK_CB = 0,
|
||||
DONE_CB,
|
||||
};
|
||||
|
||||
enum TFE_RPC_METHOD
|
||||
{
|
||||
GET = 0,
|
||||
POST,
|
||||
};
|
||||
|
||||
struct tfe_rpc
|
||||
{
|
||||
void* logger;
|
||||
};
|
||||
|
||||
struct tfe_rpc* tfe_rpc_init(const char * profile, const char* section, void* logger);
|
||||
|
||||
struct tfe_rpc* tfe_rpc_destroy(struct tfe_rpc *rpc);
|
||||
|
||||
struct tfe_rpc_response_result* tfe_rpc_release(void* result);
|
||||
|
||||
void tfe_rpc_async_ask(struct future* f, struct tfe_rpc* rpc, const char* url, int method, int flag, const char* data, int data_len, struct event_base * evbase);
|
||||
@@ -6,15 +6,20 @@
|
||||
#include "MESA/MESA_prof_load.h"
|
||||
#include "tfe_rpc.h"
|
||||
#include "event2/http.h"
|
||||
#include "cjson/cJSON.h"
|
||||
#define HTABLE_MAX_KEY_LEN 256
|
||||
#define KEYRING_EXSITED 0
|
||||
#define KEYRING_NOT_EXSITED -1
|
||||
|
||||
struct key_keeper
|
||||
{
|
||||
unsigned int mode;
|
||||
char mode[TFE_STRING_MAX];
|
||||
char ca_path[TFE_STRING_MAX];
|
||||
char untrusted_ca_path[TFE_STRING_MAX];
|
||||
char cert_store_host[TFE_STRING_MAX];
|
||||
unsigned int cert_store_port;
|
||||
unsigned int hash_slot_size;
|
||||
unsigned int hash_expire_seconds;
|
||||
MESA_htable_handle htable;
|
||||
void* logger;
|
||||
};
|
||||
@@ -35,11 +40,18 @@ struct key_keeper_promise_ctx
|
||||
{
|
||||
void* logger;
|
||||
MESA_htable_handle htable;
|
||||
const uchar* key;
|
||||
uchar* key;
|
||||
struct future* f_certstore_rpc;
|
||||
unsigned int key_len;
|
||||
};
|
||||
|
||||
static const uchar* get_key_by_cert(X509* cert, int keyring_id, unsigned int* len);
|
||||
static void key_keeper_promise_free_ctx(void* ctx)
|
||||
{
|
||||
struct key_keeper_promise_ctx* _ctx = (struct key_keeper_promise_ctx*)ctx;
|
||||
free(_ctx->key);
|
||||
_ctx->key = NULL;
|
||||
free(_ctx);
|
||||
}
|
||||
|
||||
static struct keyring_private* keyring_new(void)
|
||||
{
|
||||
@@ -60,7 +72,7 @@ static struct keyring_private* keyring_new(void)
|
||||
* Passed OpenSSL objects are owned by cert_t; refcount will not be
|
||||
* incremented, stack will not be duplicated.
|
||||
*/
|
||||
struct keyring* keyring_new3(EVP_PKEY *key, X509 *cert, STACK_OF(X509) *chain)
|
||||
static struct keyring* keyring_new3(EVP_PKEY *key, X509 *cert, STACK_OF(X509) *chain)
|
||||
{
|
||||
struct keyring_private* kyr=NULL;
|
||||
kyr=keyring_new();
|
||||
@@ -88,7 +100,7 @@ struct keyring* keyring_new3(EVP_PKEY *key, X509 *cert, STACK_OF(X509) *chain)
|
||||
}
|
||||
|
||||
// Increment reference count.
|
||||
void keyring_ref_inc(struct keyring_private* kyr)
|
||||
static void keyring_ref_inc(struct keyring_private* kyr)
|
||||
{
|
||||
pthread_mutex_lock(&kyr->mutex);
|
||||
kyr->references++;
|
||||
@@ -183,9 +195,116 @@ void key_keeper_free_keyring(struct keyring *kyr)
|
||||
free(_kyr);
|
||||
}
|
||||
|
||||
|
||||
static X509* transform_cert_to_x509(const char* str)
|
||||
{
|
||||
BIO *bio;
|
||||
X509 *cert;
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
BIO_write(bio, (const void*)str, strnlen(str, TFE_STRING_MAX));
|
||||
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
BIO_free_all(bio);
|
||||
return cert;
|
||||
}
|
||||
|
||||
static EVP_PKEY* transform_key_to_EVP(const char* str)
|
||||
{
|
||||
BIO *mem;
|
||||
mem = BIO_new_mem_buf(str, -1);
|
||||
EVP_PKEY* key = PEM_read_bio_PrivateKey(mem, NULL, NULL, 0);
|
||||
BIO_free(mem);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
static void err_out(X509* cert, EVP_PKEY* key, STACK_OF(X509)* chain)
|
||||
{
|
||||
if(cert)
|
||||
{
|
||||
X509_free(cert);
|
||||
}
|
||||
if(key)
|
||||
{
|
||||
EVP_PKEY_free(key);
|
||||
}
|
||||
if(chain)
|
||||
{
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static struct keyring* get_keyring_from_response(const char* data)
|
||||
{
|
||||
X509* cert = NULL;
|
||||
EVP_PKEY* key = NULL;
|
||||
STACK_OF(X509)* chain = NULL;
|
||||
if(data == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
cJSON* data_json = cJSON_Parse(data);
|
||||
if(data_json == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
cJSON* cert_json = NULL;
|
||||
cJSON* key_json = NULL;
|
||||
cJSON* chain_json = NULL;
|
||||
cert_json = cJSON_GetObjectItemCaseSensitive(data_json, "CERTIFICATE");
|
||||
key_json = cJSON_GetObjectItemCaseSensitive(data_json, "PRIVATE_KEY");
|
||||
chain_json = cJSON_GetObjectItemCaseSensitive(data_json, "CERTIFICATE_CHAIN");
|
||||
if (cert_json && cert_json->valuestring != NULL)
|
||||
{
|
||||
cert = transform_cert_to_x509(cert_json->valuestring);
|
||||
}
|
||||
if(cert == NULL)
|
||||
{
|
||||
err_out(cert, key, chain);
|
||||
return NULL;
|
||||
}
|
||||
if (key_json && key_json->valuestring != NULL)
|
||||
{
|
||||
key = transform_key_to_EVP(key_json->valuestring);
|
||||
}
|
||||
if(key == NULL)
|
||||
{
|
||||
err_out(cert, key, chain);
|
||||
return NULL;
|
||||
}
|
||||
if(chain_json == NULL)
|
||||
{
|
||||
err_out(cert, key, chain);
|
||||
return NULL;
|
||||
}
|
||||
cJSON* chain_cert_json = NULL;
|
||||
chain = sk_X509_new_null();
|
||||
cJSON_ArrayForEach(chain_cert_json, chain_json)
|
||||
{
|
||||
X509* chain_cert = NULL;
|
||||
if (chain_cert_json && chain_cert_json->valuestring != NULL)
|
||||
{
|
||||
chain_cert = transform_cert_to_x509(chain_cert_json->valuestring);
|
||||
}
|
||||
if(chain_cert == NULL)
|
||||
{
|
||||
err_out(cert, key, chain);
|
||||
return NULL;
|
||||
}
|
||||
sk_X509_push(chain, chain_cert);
|
||||
}
|
||||
struct keyring_private* _kyr= keyring_new();
|
||||
keyring_set_cert(_kyr, cert);
|
||||
keyring_set_key(_kyr, key);
|
||||
keyring_set_chain(_kyr, chain);
|
||||
X509_free(cert);
|
||||
EVP_PKEY_free(key);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return &(_kyr->head);
|
||||
}
|
||||
|
||||
static long keyring_local_cache_query_cb(void * data, const uchar * key, uint size, void * user_arg)
|
||||
{
|
||||
//data is (struct keyring*)
|
||||
struct keyring_private* kyr=(struct keyring_private*)data;
|
||||
if(kyr == NULL)
|
||||
{
|
||||
@@ -198,11 +317,8 @@ static long keyring_local_cache_query_cb(void * data, const uchar * key, uint si
|
||||
return KEYRING_EXSITED;
|
||||
}
|
||||
}
|
||||
static void certstore_rpc_on_fail(enum e_future_error err, const char * what, void * user)
|
||||
{
|
||||
struct promise * p = (struct promise *) user;
|
||||
promise_failed(p, err, what);
|
||||
}
|
||||
|
||||
|
||||
static struct keyring_private* generate_x509_keyring(X509* origin_cert, int keyring_id, const char* filename)
|
||||
{
|
||||
X509* ca = ssl_x509_load(filename);
|
||||
@@ -212,198 +328,64 @@ static struct keyring_private* generate_x509_keyring(X509* origin_cert, int keyr
|
||||
STACK_OF(X509)* chain = sk_X509_new_null();
|
||||
sk_X509_push(chain, ca);
|
||||
sk_X509_push(chain, forge_cert);
|
||||
struct keyring_private* ring= keyring_new();
|
||||
keyring_set_key(ring, forge_key);
|
||||
keyring_set_cert(ring, forge_cert);
|
||||
keyring_set_chain(ring, chain);
|
||||
return ring;
|
||||
}
|
||||
//how to free
|
||||
static X509* get_cert_from_response(const char* data, int len, void* logger)
|
||||
{
|
||||
BIO *bio;
|
||||
X509 *cert;
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
BIO_write(bio, (const void*)data, len);
|
||||
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
return cert;
|
||||
}
|
||||
|
||||
static EVP_PKEY* get_key_from_response(const char* data, int len, void* logger)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static STACK_OF(X509)* get_chain_from_response(const char* data, int len, void* logger)
|
||||
{
|
||||
return NULL;
|
||||
struct keyring_private* _kyr= keyring_new();
|
||||
keyring_set_key(_kyr, forge_key);
|
||||
keyring_set_cert(_kyr, forge_cert);
|
||||
keyring_set_chain(_kyr, chain);
|
||||
X509_free(ca);
|
||||
EVP_PKEY_free(cakey);
|
||||
X509_free(forge_cert);
|
||||
EVP_PKEY_free(forge_key);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return _kyr;
|
||||
}
|
||||
|
||||
static void certstore_rpc_on_succ(void* result, void* user)
|
||||
{
|
||||
//printf("call certstore_rpc_on_succ\n");
|
||||
struct promise * p = (struct promise *) user;
|
||||
struct key_keeper_promise_ctx* ctx = (struct key_keeper_promise_ctx*)promise_get_ctx(p);
|
||||
MESA_htable_handle htable= ctx->htable;
|
||||
const uchar* key = ctx->key;
|
||||
unsigned int key_len = ctx->key_len;
|
||||
//transform to x509
|
||||
struct tfe_rpc_response_result* response = tfe_rpc_release(result);
|
||||
int status_code = response->status_code;
|
||||
const char* status_msg = response->status_msg;
|
||||
const char* data = response->data;
|
||||
char* data = response->data;
|
||||
int len = response->len;
|
||||
if(status_code == HTTP_OK)
|
||||
{
|
||||
struct keyring_private* ring= keyring_new();
|
||||
X509* forge_cert = get_cert_from_response(data, len, NULL);
|
||||
EVP_PKEY* forge_key = get_key_from_response(data, len, NULL);
|
||||
STACK_OF(X509)* chain = get_chain_from_response(data, len, NULL);
|
||||
keyring_set_key(ring, forge_key);
|
||||
keyring_set_cert(ring, forge_cert);
|
||||
keyring_set_chain(ring, chain);
|
||||
promise_success(p, (void*)ring);
|
||||
|
||||
int ret = MESA_htable_add(htable, key, key_len, (void*)ring);
|
||||
*(data+len) = '\0';
|
||||
struct keyring* kyr= get_keyring_from_response(data);
|
||||
promise_success(p, (void*)kyr);
|
||||
int ret = MESA_htable_add(htable, key, key_len, (void*)kyr);
|
||||
if(ret<0)
|
||||
{
|
||||
key_keeper_free_keyring((struct keyring*)ring);
|
||||
key_keeper_free_keyring((struct keyring*)kyr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
promise_failed(p, FUTURE_ERROR_EXCEPTION, status_msg);
|
||||
}
|
||||
future_destroy(ctx->f_certstore_rpc);
|
||||
//promise_dettach_ctx(p);
|
||||
//ctx_destroy_cb((void*)ctx);
|
||||
}
|
||||
|
||||
static void certstore_rpc_on_fail(enum e_future_error err, const char * what, void * user)
|
||||
{
|
||||
struct promise * p = (struct promise *) user;
|
||||
promise_failed(p, err, what);
|
||||
struct key_keeper_promise_ctx* ctx= (struct key_keeper_promise_ctx*)promise_get_ctx(p);
|
||||
future_destroy(ctx->f_certstore_rpc);
|
||||
//promise_dettach_ctx(p);
|
||||
//ctx_destroy_cb((void*)ctx);
|
||||
}
|
||||
|
||||
static int __wrapper_MESA_htable_set_opt(MESA_htable_handle table, enum MESA_htable_opt opt_type, unsigned int value);
|
||||
|
||||
static MESA_htable_handle create_hash_table(unsigned int slot_size, unsigned int expire_seconds);
|
||||
|
||||
static STACK_OF(X509)* get_chain_from_response(const char* data, int len, void* logger);
|
||||
/*
|
||||
* Certificate, including private key and keyring chain.
|
||||
*/
|
||||
|
||||
|
||||
struct key_keeper* key_keeper_init(const char * profile, const char* section, void* logger)
|
||||
{
|
||||
//load conf
|
||||
//TODO free
|
||||
struct key_keeper* keeper = ALLOC(struct key_keeper, 1);
|
||||
keeper->logger = logger;
|
||||
MESA_load_profile_uint_def(profile, section, "mode", &(keeper->mode), 1);
|
||||
MESA_load_profile_string_def(profile, section, "cert_store_host", keeper->cert_store_host, sizeof(keeper->cert_store_host), "xxxxx");
|
||||
MESA_load_profile_uint_def(profile, section, "cert_store_port", &(keeper->cert_store_port), 80);
|
||||
//TODO: argument
|
||||
keeper->htable = create_hash_table(16,16);
|
||||
return keeper;
|
||||
}
|
||||
|
||||
void key_keeper_destroy(struct key_keeper *keeper)
|
||||
{
|
||||
free(keeper);
|
||||
keeper = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
struct keyring* key_keeper_release_keyring(future_result_t* result)
|
||||
{
|
||||
struct keyring_private* kyr=(struct keyring_private*)result;
|
||||
keyring_ref_inc(kyr);
|
||||
return &(kyr->head);
|
||||
}
|
||||
|
||||
static void ctx_destory_cb(struct promise* p)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//TODO: cert_not_valid
|
||||
void key_keeper_async_ask(struct future * f, struct key_keeper * keeper, const char* sni, int keyring_id, X509 * origin_cert, int is_cert_valid, struct event_base * evbase)
|
||||
{
|
||||
//get subject name from cert
|
||||
|
||||
//current promise, belong to key_keeper
|
||||
struct promise* p = future_to_promise(f);
|
||||
struct key_keeper_promise_ctx* ctx = ALLOC(struct key_keeper_promise_ctx, 1);
|
||||
//TODO free ctx!!
|
||||
unsigned int len = 0;
|
||||
const uchar* key = get_key_by_cert(origin_cert, keyring_id, &len);
|
||||
ctx->logger = keeper->logger;
|
||||
ctx->htable = keeper->htable;
|
||||
ctx->key = key;
|
||||
ctx->key_len = len;
|
||||
promise_set_ctx(p, (void*)ctx, ctx_destory_cb);
|
||||
long int cb_rtn = 0;
|
||||
MESA_htable_search_cb(ctx->htable, (const unsigned char*)(ctx->key), ctx->key_len, keyring_local_cache_query_cb, p, &cb_rtn);
|
||||
printf(cb_rtn == KEYRING_EXSITED ? "KEYRING_EXSITED\n": "KEYRING_NOT_EXSITED\n");
|
||||
if(cb_rtn == KEYRING_EXSITED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int mode = keeper->mode;
|
||||
printf("mode is %s", mode == NORMAL ? "normal\n":"debug\n");
|
||||
switch(mode){
|
||||
case NORMAL:
|
||||
{
|
||||
struct future* f_tfe_rpc = future_create("tfe_rpc", certstore_rpc_on_succ, certstore_rpc_on_fail, p);
|
||||
//TODO: init in main()? store in ctx
|
||||
struct tfe_rpc* rpc = tfe_rpc_init(NULL, NULL, keeper->logger);
|
||||
char url[TFE_STRING_MAX];
|
||||
const char host[] = "www.baidu.com";
|
||||
snprintf(url, TFE_STRING_MAX, "%s:%d?host=%s&flag=1&valid=1&kering_id=%d", keeper->cert_store_host, keeper->cert_store_port, host, keyring_id);
|
||||
tfe_rpc_async_ask(f_tfe_rpc, rpc, url, GET, DONE_CB, NULL, 0, evbase);
|
||||
break;
|
||||
}
|
||||
case DEBUG:
|
||||
{
|
||||
//TOOD: generate X509 cert
|
||||
const char* filename = "./conf/mesalab-ca.pem";
|
||||
struct keyring_private* ring = generate_x509_keyring(origin_cert, keyring_id, filename);
|
||||
if(ring)
|
||||
{
|
||||
promise_success(p, (void*)ring);
|
||||
int ret = MESA_htable_add(ctx->htable, ctx->key, ctx->key_len, (void*)ring);
|
||||
if(ret<0)
|
||||
{
|
||||
key_keeper_free_keyring((struct keyring*)ring);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("key %s is added to hash table\n", ctx->key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
promise_failed(p, FUTURE_ERROR_EXCEPTION, "generate X509 cert failed");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static const uchar*
|
||||
get_key_by_cert(X509* cert, int keyring_id, unsigned int* len)
|
||||
{
|
||||
char* cert_fgr = NULL;
|
||||
cert_fgr = ssl_x509_fingerprint(cert, 0);
|
||||
char* key = (char*)malloc(HTABLE_MAX_KEY_LEN);
|
||||
*key = '\0';
|
||||
if(cert == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
snprintf(key, HTABLE_MAX_KEY_LEN, "%d:", keyring_id);
|
||||
strncat(key, cert_fgr, HTABLE_MAX_KEY_LEN);
|
||||
*len = strnlen(key, HTABLE_MAX_KEY_LEN);
|
||||
return (const uchar*)key;
|
||||
}
|
||||
|
||||
|
||||
static int __wrapper_MESA_htable_set_opt(MESA_htable_handle table, enum MESA_htable_opt opt_type, unsigned int value)
|
||||
{
|
||||
int ret = MESA_htable_set_opt(table, opt_type, &value, (int)(sizeof(value)));
|
||||
@@ -411,8 +393,7 @@ static int __wrapper_MESA_htable_set_opt(MESA_htable_handle table, enum MESA_hta
|
||||
return ret;
|
||||
}
|
||||
|
||||
static MESA_htable_handle
|
||||
create_hash_table(unsigned int slot_size, unsigned int expire_seconds)
|
||||
static MESA_htable_handle create_hash_table(unsigned int slot_size, unsigned int expire_seconds)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned max_num = slot_size * 4;
|
||||
@@ -435,5 +416,122 @@ create_hash_table(unsigned int slot_size, unsigned int expire_seconds)
|
||||
return htable;
|
||||
}
|
||||
|
||||
struct key_keeper* key_keeper_init(const char * profile, const char* section, void* logger)
|
||||
{
|
||||
struct key_keeper* keeper = ALLOC(struct key_keeper, 1);
|
||||
keeper->logger = logger;
|
||||
MESA_load_profile_string_def(profile, section, "mode", keeper->mode, sizeof(keeper->mode), "debug");
|
||||
MESA_load_profile_string_def(profile, section, "ca_path", keeper->ca_path, sizeof(keeper->ca_path), "./conf/mesalab-ca.pem");
|
||||
MESA_load_profile_string_def(profile, section, "untrusted_ca_path", keeper->untrusted_ca_path, sizeof(keeper->untrusted_ca_path), "./conf/mesalab-ca.pem");
|
||||
MESA_load_profile_string_def(profile, section, "cert_store_host", keeper->cert_store_host, sizeof(keeper->cert_store_host), "xxxxx");
|
||||
MESA_load_profile_uint_def(profile, section, "cert_store_port", &(keeper->cert_store_port), 80);
|
||||
MESA_load_profile_uint_def(profile, section, "hash_slot_size", &(keeper->hash_slot_size), 16);
|
||||
MESA_load_profile_uint_def(profile, section, "hash_expire_seconds", &(keeper->hash_expire_seconds), 16);
|
||||
keeper->htable = create_hash_table(keeper->hash_slot_size, keeper->hash_expire_seconds);
|
||||
return keeper;
|
||||
}
|
||||
|
||||
void key_keeper_destroy(struct key_keeper *keeper)
|
||||
{
|
||||
MESA_htable_destroy(keeper->htable, NULL);
|
||||
free(keeper);
|
||||
keeper = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
struct keyring* key_keeper_release_keyring(future_result_t* result)
|
||||
{
|
||||
struct keyring_private* kyr=(struct keyring_private*)result;
|
||||
keyring_ref_inc(kyr);
|
||||
return &(kyr->head);
|
||||
}
|
||||
|
||||
|
||||
static uchar* get_key_by_cert(X509* cert, int keyring_id, unsigned int* len)
|
||||
{
|
||||
char* cert_fgr = NULL;
|
||||
cert_fgr = ssl_x509_fingerprint(cert, 0);
|
||||
char* key = (char*)malloc(HTABLE_MAX_KEY_LEN);
|
||||
*key = '\0';
|
||||
if(cert == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
snprintf(key, HTABLE_MAX_KEY_LEN, "%d:", keyring_id);
|
||||
strncat(key, cert_fgr, HTABLE_MAX_KEY_LEN);
|
||||
*len = strnlen(key, HTABLE_MAX_KEY_LEN);
|
||||
free(cert_fgr);
|
||||
return (uchar*)key;
|
||||
}
|
||||
|
||||
|
||||
void key_keeper_async_ask(struct future * f, struct key_keeper * keeper, const char* sni, int keyring_id, X509 * origin_cert, int is_cert_valid, struct event_base * evbase)
|
||||
{
|
||||
struct promise* p = future_to_promise(f);
|
||||
struct key_keeper_promise_ctx* ctx = ALLOC(struct key_keeper_promise_ctx, 1);
|
||||
unsigned int len = 0;
|
||||
uchar* key = get_key_by_cert(origin_cert, keyring_id, &len);
|
||||
ctx->logger = keeper->logger;
|
||||
ctx->htable = keeper->htable;
|
||||
ctx->key = key;
|
||||
ctx->key_len = len;
|
||||
promise_set_ctx(p, (void*)ctx, key_keeper_promise_free_ctx);
|
||||
long int cb_rtn = 0;
|
||||
MESA_htable_search_cb(ctx->htable, (const unsigned char*)(ctx->key), ctx->key_len, keyring_local_cache_query_cb, p, &cb_rtn);
|
||||
if(cb_rtn == KEYRING_EXSITED)
|
||||
{
|
||||
printf("KEYRING_EXSITED\n");
|
||||
return;
|
||||
}
|
||||
int mode = 0;
|
||||
if(strncmp(keeper->mode, "debug", TFE_STRING_MAX) == 0)
|
||||
{
|
||||
mode = 1;
|
||||
}
|
||||
switch(mode){
|
||||
case NORMAL:
|
||||
{
|
||||
struct future* f_certstore_rpc = future_create("tfe_rpc", certstore_rpc_on_succ, certstore_rpc_on_fail, p);
|
||||
ctx->f_certstore_rpc = f_certstore_rpc;
|
||||
char url[TFE_STRING_MAX];
|
||||
char _sni[TFE_STRING_MAX] = "www.baidu.com";
|
||||
if(sni)
|
||||
{
|
||||
strncpy(_sni, sni, TFE_STRING_MAX);
|
||||
}
|
||||
snprintf(url, TFE_STRING_MAX, "http://%s:%d/ca?host=%s&flag=1&valid=1&keyring_id=%d", keeper->cert_store_host, keeper->cert_store_port, _sni, keyring_id);
|
||||
printf("url is %s\n", url);
|
||||
tfe_rpc_async_ask(f_certstore_rpc, url, GET, DONE_CB, NULL, 0, evbase);
|
||||
break;
|
||||
}
|
||||
case DEBUG:
|
||||
{
|
||||
//TOOD: generate X509 cert
|
||||
char* filename = NULL;
|
||||
if(is_cert_valid == 1)
|
||||
{
|
||||
filename = keeper->ca_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = keeper->untrusted_ca_path;
|
||||
}
|
||||
struct keyring_private* ring = generate_x509_keyring(origin_cert, keyring_id, filename);
|
||||
if(ring)
|
||||
{
|
||||
promise_success(p, (void*)ring);
|
||||
int ret = MESA_htable_add(ctx->htable, ctx->key, ctx->key_len, (void*)ring);
|
||||
if(ret<0)
|
||||
{
|
||||
key_keeper_free_keyring((struct keyring*)ring);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
promise_failed(p, FUTURE_ERROR_EXCEPTION, "generate X509 cert failed");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
#include "tfe_rpc.h"
|
||||
#include "event2/http.h"
|
||||
#include "event2/http_struct.h"
|
||||
#include "event2/event.h"
|
||||
#include "event2/buffer.h"
|
||||
#include "event2/dns.h"
|
||||
#include "event2/thread.h"
|
||||
#include "tfe_utils.h"
|
||||
#include "MESA/MESA_handle_logger.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#define MODULE_NAME "tfe_rpc"
|
||||
|
||||
void promise_success_cb(const char* data, int len);
|
||||
|
||||
struct req_ctx
|
||||
{
|
||||
struct promise* promise;
|
||||
void* logger;
|
||||
};
|
||||
|
||||
//The callback is executed when the request completed or an error occurred
|
||||
void
|
||||
get_response_cb(struct evhttp_request* response, void* arg)
|
||||
{
|
||||
struct req_ctx* ctx = (struct req_ctx*)arg;
|
||||
void* logger = ctx->logger;
|
||||
struct promise* p = ctx->promise;
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_INFO, MODULE_NAME, "call get_response_cb!");
|
||||
struct evbuffer* evbuf = evhttp_request_get_input_buffer(response);
|
||||
int evbuf_len = evbuffer_get_length(evbuf);
|
||||
char* data = (char*)evbuffer_pullup(evbuf, evbuf_len);
|
||||
printf("data is %s\n", data==NULL ? "NULL":"NOT NULL");
|
||||
//promise_success_cb(data, evbuf_len);
|
||||
//TODO: free
|
||||
struct tfe_rpc_response_result* result = ALLOC(struct tfe_rpc_response_result, 1);
|
||||
result->status_code = evhttp_request_get_response_code(response);
|
||||
result->status_msg = evhttp_request_get_response_code_line(response);
|
||||
result->data = data;
|
||||
result->len = evbuf_len;
|
||||
promise_success(p, result);
|
||||
}
|
||||
|
||||
//will be called after receiving and parsing the full header. It allows analyzing the header and possibly closing the connection by returning a value < 0.
|
||||
int
|
||||
read_header_done_cb(struct evhttp_request* response, void* arg)
|
||||
{
|
||||
struct req_ctx* ctx = (struct req_ctx*)arg;
|
||||
void* logger = ctx->logger;
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_INFO, MODULE_NAME, "call read_header_done_cb!");
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_INFO, MODULE_NAME, "< HTTP/1.1 %d %s\n", evhttp_request_get_response_code(response), evhttp_request_get_response_code_line(response));
|
||||
return 0;
|
||||
/*
|
||||
struct evkeyvalq* headers = evhttp_request_get_input_headers(response);
|
||||
struct evkeyval* header;
|
||||
TAILQ_FOREACH(header, headers, next){
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_INFO, MODULE_NAME, "< %s: %s\n", header->key, header->value);
|
||||
}
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_INFO, MODULE_NAME, "< \n");
|
||||
*/
|
||||
}
|
||||
|
||||
//will be called after every read of data with the same argument as the completion callback. Will never be called on an empty response. May drain the input buffer; it will be drained automatically on return.
|
||||
//will drain automaticly
|
||||
void
|
||||
read_chunk_cb(struct evhttp_request* response, void* arg)
|
||||
{
|
||||
struct req_ctx* ctx = (struct req_ctx*)arg;
|
||||
void* logger = ctx->logger;
|
||||
struct promise* p = ctx->promise;
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_INFO, MODULE_NAME, "call read_chunk_cb!");
|
||||
struct evbuffer* evbuf = evhttp_request_get_input_buffer(response);
|
||||
int evbuf_len = evbuffer_get_length(evbuf);
|
||||
char* data = (char*)evbuffer_pullup(evbuf, evbuf_len);
|
||||
struct tfe_rpc_response_result* result = ALLOC(struct tfe_rpc_response_result, 1);
|
||||
result->status_code = evhttp_request_get_response_code(response);
|
||||
result->status_msg = evhttp_request_get_response_code_line(response);
|
||||
result->data = data;
|
||||
result->len = evbuf_len;
|
||||
promise_success(p, result);
|
||||
}
|
||||
|
||||
void
|
||||
promise_success_cb(const char* data, int len)
|
||||
{
|
||||
printf("len is %d, data is: \n", len);
|
||||
char* data1 = (char*)malloc(len+1);
|
||||
strncpy(data1, data, len);
|
||||
data1[len] = '\0';
|
||||
printf("%s", data1);
|
||||
free(data1);
|
||||
}
|
||||
|
||||
|
||||
//On error, both the error callback and the regular callback will be called, error callback is called before the regular callback.
|
||||
void
|
||||
request_error_cb(enum evhttp_request_error error, void* arg)
|
||||
{
|
||||
struct req_ctx* ctx = (struct req_ctx*)arg;
|
||||
void* logger = ctx->logger;
|
||||
struct promise* p = ctx->promise;
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_FATAL, MODULE_NAME, "call request_error_cb, error code is %d!", error);
|
||||
enum e_future_error error_map_table[6];
|
||||
error_map_table[EVREQ_HTTP_TIMEOUT] = FUTURE_ERROR_TIMEOUT;
|
||||
error_map_table[EVREQ_HTTP_EOF] = FUTURE_ERROR_EXCEPTION;
|
||||
error_map_table[EVREQ_HTTP_INVALID_HEADER] = FUTURE_ERROR_EXCEPTION;
|
||||
error_map_table[EVREQ_HTTP_BUFFER_ERROR] = FUTURE_ERROR_EXCEPTION;
|
||||
error_map_table[EVREQ_HTTP_REQUEST_CANCEL] = FUTURE_ERROR_CANCEL;
|
||||
error_map_table[EVREQ_HTTP_DATA_TOO_LONG] = FUTURE_ERROR_EXCEPTION;
|
||||
promise_failed(p, error_map_table[error], NULL);
|
||||
//promise_fail_cb();
|
||||
}
|
||||
|
||||
//when to close a connection ???
|
||||
//Set a callback for connection close
|
||||
void
|
||||
connection_close_cb(struct evhttp_connection* connection, void* arg)
|
||||
{
|
||||
struct req_ctx* ctx = (struct req_ctx*)arg;
|
||||
void* logger = ctx->logger;
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_FATAL, MODULE_NAME, "call connection_close_cb!");
|
||||
}
|
||||
|
||||
struct
|
||||
tfe_rpc * tfe_rpc_init(const char * profile, const char* section, void* logger)
|
||||
{
|
||||
struct tfe_rpc* rpc = ALLOC(struct tfe_rpc, 1);
|
||||
rpc->logger = logger;
|
||||
return rpc;
|
||||
}
|
||||
|
||||
|
||||
//TODO: deep destory
|
||||
struct tfe_rpc *
|
||||
tfe_rpc_destroy(struct tfe_rpc *rpc)
|
||||
{
|
||||
free(rpc);
|
||||
rpc = NULL;
|
||||
return rpc;
|
||||
}
|
||||
|
||||
//data is for POST. if method is GET, data should be NULL
|
||||
void
|
||||
tfe_rpc_async_ask(struct future* f, struct tfe_rpc* rpc, const char* url, int method, int flag, const char* data, int data_len, struct event_base * evbase)
|
||||
{
|
||||
struct promise* p = future_to_promise(f);
|
||||
void* logger = rpc->logger;
|
||||
struct req_ctx* ctx = ALLOC(struct req_ctx, 1);
|
||||
ctx->promise = p;
|
||||
ctx->logger = logger;
|
||||
assert(logger);
|
||||
if(!evbase)
|
||||
{
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_FATAL, MODULE_NAME, "event base is NULL");
|
||||
promise_failed(p, FUTURE_ERROR_EXCEPTION, "event base is NULL");
|
||||
return;
|
||||
}
|
||||
struct evhttp_uri* uri = evhttp_uri_parse(url);
|
||||
if(NULL == uri)
|
||||
{
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_FATAL, MODULE_NAME, "parse url failed!");
|
||||
promise_failed(p, FUTURE_ERROR_EXCEPTION, "parse url failed!");
|
||||
return;
|
||||
}
|
||||
const char* host = evhttp_uri_get_host(uri);
|
||||
if(!host)
|
||||
{
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_FATAL, MODULE_NAME, "parse host failed!");
|
||||
promise_failed(p, FUTURE_ERROR_EXCEPTION, "parse host failed!");
|
||||
return;
|
||||
}
|
||||
int port = evhttp_uri_get_port(uri);
|
||||
if(port < 0)
|
||||
{
|
||||
port = 80;
|
||||
}
|
||||
const char* path = evhttp_uri_get_path(uri);
|
||||
const char* request_url = path;
|
||||
if(path == NULL || strlen(path) == 0)
|
||||
{
|
||||
request_url = "/";
|
||||
}
|
||||
printf("url:%s host:%s port:%d path:%s request_url:%s\n", url, host, port, path, request_url);
|
||||
struct evdns_base* dnsbase = evdns_base_new(evbase, EVDNS_BASE_INITIALIZE_NAMESERVERS);
|
||||
if (!dnsbase)
|
||||
{
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_FATAL, MODULE_NAME, "create dns base failed!");
|
||||
promise_failed(p, FUTURE_ERROR_EXCEPTION, "create dns base failed!");
|
||||
return;
|
||||
}
|
||||
struct evhttp_connection* connection = evhttp_connection_base_new(evbase, dnsbase, host, port);
|
||||
if (!connection)
|
||||
{
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_FATAL, MODULE_NAME, "create connection failed!");
|
||||
promise_failed(p, FUTURE_ERROR_EXCEPTION, "create connection failed!");
|
||||
return;
|
||||
}
|
||||
evhttp_connection_set_closecb(connection, connection_close_cb, evbase);
|
||||
//TODO: free
|
||||
struct evhttp_request* request = evhttp_request_new(get_response_cb, ctx);
|
||||
evhttp_request_set_header_cb(request, read_header_done_cb);
|
||||
if(flag == CHUNK_CB)
|
||||
{
|
||||
evhttp_request_set_chunked_cb(request, read_chunk_cb);
|
||||
}
|
||||
evhttp_request_set_error_cb(request, request_error_cb);
|
||||
evhttp_add_header(evhttp_request_get_output_headers(request), "Host", host);
|
||||
switch(method)
|
||||
{
|
||||
case GET:
|
||||
evhttp_make_request(connection, request, EVHTTP_REQ_GET, request_url);
|
||||
break;
|
||||
case POST:
|
||||
evbuffer_add(request->output_buffer, data, data_len);
|
||||
evhttp_make_request(connection, request, EVHTTP_REQ_POST, request_url);
|
||||
break;
|
||||
default:
|
||||
promise_failed(p, FUTURE_ERROR_EXCEPTION, "method is invalid!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct
|
||||
tfe_rpc_response_result* tfe_rpc_release(void* result)
|
||||
{
|
||||
struct tfe_rpc_response_result* response = (struct tfe_rpc_response_result*)result;
|
||||
return response;
|
||||
}
|
||||
|
||||
/*
|
||||
int main(int argc, char** argv){
|
||||
char* url = "http://localhost:80/download/test1.txt";
|
||||
void* logger = MESA_create_runtime_log_handle_new("tfe_rpc_logger");
|
||||
if(!logger){
|
||||
printf("create runtime log handle failed!\n");
|
||||
return -1;
|
||||
}
|
||||
int rtn = MESA_read_runtime_log_handle_conf(logger, "./conf/MESA_handle_logger.conf");
|
||||
if(rtn == -1){
|
||||
printf("read runtime log handle conf failed!\n");
|
||||
return -1;
|
||||
}
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_INFO, MODULE_NAME, "test log!\n");
|
||||
struct event_base* base = event_base_new();
|
||||
if(!base){
|
||||
MESA_handle_runtime_log(logger, RLOG_LV_FATAL, MODULE_NAME, "create event base failed!\n");
|
||||
return -1;
|
||||
}
|
||||
tfe_rpc(url, GET, CHUNK_CB, base, logger, NULL);
|
||||
event_base_dispatch(base);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,37 +1,58 @@
|
||||
#include "key_keeper.h"
|
||||
#include "tfe_future.h"
|
||||
#include "ssl_utils.h"
|
||||
#include "event2/event.h"
|
||||
#include "tfe_rpc.h"
|
||||
|
||||
|
||||
void ask_key_keeper_on_succ(void* result, void* user);
|
||||
|
||||
void ask_key_keeper_on_fail(enum e_future_error error, const char * what, void * user);
|
||||
|
||||
int
|
||||
main()
|
||||
int main()
|
||||
{
|
||||
void* logger = NULL;
|
||||
future_promise_library_init();
|
||||
struct event_base* evbase = event_base_new();
|
||||
struct key_keeper * keeper = key_keeper_init("./conf/tfe.conf", "key_keeper", logger);
|
||||
struct promise* user = NULL;
|
||||
struct future* f = future_create("key_keeper", ask_key_keeper_on_succ, ask_key_keeper_on_fail, user);
|
||||
struct event_base* evbase = NULL;
|
||||
X509* origin_cert = ssl_x509_load("./conf/origin_cert.pem");
|
||||
printf("-------------------------------\n");
|
||||
int i = 0;
|
||||
for(i = 0;i<10;i++){
|
||||
printf("-------------------------------\n");
|
||||
printf("call key_keeper_async_ask, i = %d\n", i);
|
||||
key_keeper_async_ask(f, keeper, NULL, 0, origin_cert, 0, evbase);
|
||||
}
|
||||
printf("call key_keeper_async_ask, i = %d\n", i);
|
||||
key_keeper_async_ask(f, keeper, NULL, 0, origin_cert, 0, evbase);
|
||||
event_base_dispatch(evbase);
|
||||
|
||||
|
||||
struct event_base* evbase1 = event_base_new();
|
||||
struct future* f1 = future_create("key_keeper", ask_key_keeper_on_succ, ask_key_keeper_on_fail, user);
|
||||
key_keeper_async_ask(f1, keeper, NULL, 0, origin_cert, 0, evbase1);
|
||||
event_base_dispatch(evbase1);
|
||||
}
|
||||
|
||||
void
|
||||
ask_key_keeper_on_succ(void* result, void* user)
|
||||
void ask_key_keeper_on_succ(void* result, void* user)
|
||||
{
|
||||
printf("call ask_key_keeper_on_succ\n");
|
||||
struct keyring* kyr = key_keeper_release_keyring(result);
|
||||
X509* cert = kyr->cert;
|
||||
EVP_PKEY* key = kyr->key;
|
||||
STACK_OF(X509) * chain = kyr->chain;
|
||||
if(cert)
|
||||
{
|
||||
printf("cert is not null\n");
|
||||
}
|
||||
if(key)
|
||||
{
|
||||
printf("key is not null\n");
|
||||
}
|
||||
if(chain)
|
||||
{
|
||||
printf("chain is not null\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
ask_key_keeper_on_fail(enum e_future_error error, const char * what, void * user)
|
||||
void ask_key_keeper_on_fail(enum e_future_error error, const char * what, void * user)
|
||||
{
|
||||
printf("call ask_key_keeper_on_fail\n");
|
||||
}
|
||||
|
||||
@@ -3,9 +3,207 @@
|
||||
#include "MESA/MESA_prof_load.h"
|
||||
#include "MESA/MESA_handle_logger.h"
|
||||
#include "string.h"
|
||||
#include "cjson/cJSON.h"
|
||||
#include "ssl_utils.h"
|
||||
#include "key_keeper.h"
|
||||
|
||||
static void
|
||||
tfe_rpc_on_succ(void* result, void* user)
|
||||
struct keyring_private
|
||||
{
|
||||
struct keyring head;
|
||||
pthread_mutex_t mutex;
|
||||
size_t references;
|
||||
};
|
||||
|
||||
static struct keyring_private* keyring_new(void)
|
||||
{
|
||||
struct keyring_private *kyr;
|
||||
if (!(kyr = (struct keyring_private *)ALLOC(struct keyring_private, 1)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (pthread_mutex_init(&kyr->mutex, NULL)) {
|
||||
free(kyr);
|
||||
return NULL;
|
||||
}
|
||||
kyr->references = 1;
|
||||
return kyr;
|
||||
}
|
||||
|
||||
|
||||
// Increment reference count.
|
||||
static void keyring_ref_inc(struct keyring_private* kyr)
|
||||
{
|
||||
pthread_mutex_lock(&kyr->mutex);
|
||||
kyr->references++;
|
||||
pthread_mutex_unlock(&kyr->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Thread-safe setter functions; they copy the value (refcounts are inc'd).
|
||||
*/
|
||||
static void keyring_set_key(struct keyring_private* kyr, EVP_PKEY *key)
|
||||
{
|
||||
pthread_mutex_lock(&kyr->mutex);
|
||||
if ((kyr->head).key)
|
||||
{
|
||||
EVP_PKEY_free((kyr->head).key);
|
||||
}
|
||||
(kyr->head).key = key;
|
||||
if (key)
|
||||
{
|
||||
ssl_key_refcount_inc((kyr->head).key);
|
||||
}
|
||||
pthread_mutex_unlock(&kyr->mutex);
|
||||
}
|
||||
|
||||
static void keyring_set_cert(struct keyring_private* kry, X509 *cert)
|
||||
{
|
||||
pthread_mutex_lock(&kry->mutex);
|
||||
if ((kry->head).cert)
|
||||
{
|
||||
X509_free((kry->head).cert);
|
||||
}
|
||||
(kry->head).cert = cert;
|
||||
if (cert)
|
||||
{
|
||||
ssl_x509_refcount_inc((kry->head).cert);
|
||||
}
|
||||
pthread_mutex_unlock(&kry->mutex);
|
||||
}
|
||||
|
||||
static void keyring_set_chain(struct keyring_private* kyr, STACK_OF(X509) *chain)
|
||||
{
|
||||
pthread_mutex_lock(&kyr->mutex);
|
||||
if ((kyr->head).chain)
|
||||
{
|
||||
sk_X509_pop_free((kyr->head).chain, X509_free);
|
||||
}
|
||||
if (chain)
|
||||
{
|
||||
(kyr->head).chain = sk_X509_dup(chain);
|
||||
int i = 0;
|
||||
for (i = 0; i < sk_X509_num((kyr->head).chain); i++)
|
||||
{
|
||||
ssl_x509_refcount_inc(sk_X509_value((kyr->head).chain, i));
|
||||
}
|
||||
} else
|
||||
{
|
||||
(kyr->head).chain = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&kyr->mutex);
|
||||
}
|
||||
|
||||
static X509* transform_cert_to_x509(const char* str)
|
||||
{
|
||||
BIO *bio;
|
||||
X509 *cert;
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
BIO_write(bio, (const void*)str, strnlen(str, TFE_STRING_MAX));
|
||||
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
return cert;
|
||||
}
|
||||
|
||||
static EVP_PKEY* transform_key_to_EVP(const char* str)
|
||||
{
|
||||
BIO *mem;
|
||||
mem = BIO_new_mem_buf(str, -1);
|
||||
EVP_PKEY* key = PEM_read_bio_PrivateKey(mem, NULL, NULL, 0);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
static void err_out(X509* cert, EVP_PKEY* key, STACK_OF(X509)* chain)
|
||||
{
|
||||
if(cert)
|
||||
{
|
||||
X509_free(cert);
|
||||
}
|
||||
if(key)
|
||||
{
|
||||
EVP_PKEY_free(key);
|
||||
}
|
||||
if(chain)
|
||||
{
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static struct keyring* get_keyring_from_response(const char* data)
|
||||
{
|
||||
X509* cert = NULL;
|
||||
EVP_PKEY* key = NULL;
|
||||
STACK_OF(X509)* chain = NULL;
|
||||
if(data == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
cJSON* data_json = cJSON_Parse(data);
|
||||
if(data_json == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
cJSON* cert_json = NULL;
|
||||
cJSON* key_json = NULL;
|
||||
cJSON* chain_json = NULL;
|
||||
cert_json = cJSON_GetObjectItemCaseSensitive(data_json, "CERTIFICATE");
|
||||
key_json = cJSON_GetObjectItemCaseSensitive(data_json, "PRIVATE_KEY");
|
||||
chain_json = cJSON_GetObjectItemCaseSensitive(data_json, "CERTIFICATE_CHAIN");
|
||||
if (cert_json && cert_json->valuestring != NULL)
|
||||
{
|
||||
cert = transform_cert_to_x509(cert_json->valuestring);
|
||||
}
|
||||
if(cert == NULL)
|
||||
{
|
||||
err_out(cert, key, chain);
|
||||
return NULL;
|
||||
}
|
||||
if (key_json && key_json->valuestring != NULL)
|
||||
{
|
||||
key = transform_key_to_EVP(key_json->valuestring);
|
||||
}
|
||||
if(key == NULL)
|
||||
{
|
||||
err_out(cert, key, chain);
|
||||
return NULL;
|
||||
}
|
||||
if(chain_json == NULL)
|
||||
{
|
||||
err_out(cert, key, chain);
|
||||
return NULL;
|
||||
}
|
||||
cJSON* chain_cert_json = NULL;
|
||||
chain = sk_X509_new_null();
|
||||
cJSON_ArrayForEach(chain_cert_json, chain_json)
|
||||
{
|
||||
X509* chain_cert = NULL;
|
||||
if (chain_cert_json && chain_cert_json->valuestring != NULL)
|
||||
{
|
||||
chain_cert = transform_cert_to_x509(chain_cert_json->valuestring);
|
||||
}
|
||||
if(chain_cert == NULL)
|
||||
{
|
||||
err_out(cert, key, chain);
|
||||
return NULL;
|
||||
}
|
||||
if(chain_cert)
|
||||
printf("push to chain\n");
|
||||
sk_X509_push(chain, chain_cert);
|
||||
}
|
||||
struct keyring_private* _kyr= keyring_new();
|
||||
printf("cert is %s", cert == NULL ? "null" : "not null\n");
|
||||
printf("key is %s", key == NULL ? "null" : "not null\n");
|
||||
printf("chain is %s", chain == NULL ? "null" : "not null\n");
|
||||
keyring_set_cert(_kyr, cert);
|
||||
keyring_set_key(_kyr, key);
|
||||
keyring_set_chain(_kyr, chain);
|
||||
X509_free(cert);
|
||||
EVP_PKEY_free(key);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return &(_kyr->head);
|
||||
}
|
||||
|
||||
static void tfe_rpc_on_succ(void* result, void* user)
|
||||
{
|
||||
struct tfe_rpc_response_result* response = tfe_rpc_release(result);
|
||||
int status_code = response->status_code;
|
||||
@@ -15,24 +213,28 @@ tfe_rpc_on_succ(void* result, void* user)
|
||||
*(data+len) = '\0';
|
||||
printf("status_code is %d\n", status_code);
|
||||
printf("status_msg is %s\n", status_msg);
|
||||
printf("data is %s\n", data);
|
||||
//printf("data is %s\n", data);
|
||||
printf("len is %d\n", len);
|
||||
struct keyring* kyr = get_keyring_from_response(data);
|
||||
//add to hash table
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
tfe_rpc_on_fail(enum e_future_error err, const char * what, void * user){
|
||||
static void tfe_rpc_on_fail(enum e_future_error err, const char * what, void * user){
|
||||
printf("err is %d\n", err);
|
||||
printf("what is %s\n", what);
|
||||
printf("user is %s\n", user);
|
||||
printf("user is %s\n", (char*)user);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
char cert_store_host[TFE_STRING_MAX];
|
||||
unsigned int cert_store_port;
|
||||
const char* file_path = "./log/test_tfe_rpc.log",*host="localhost";
|
||||
void * logger = MESA_create_runtime_log_handle(file_path, RLOG_LV_INFO);
|
||||
future_promise_library_init();
|
||||
//const char* file_path = "./log/test_tfe_rpc.log",*host="localhost";
|
||||
//void * logger = MESA_create_runtime_log_handle(file_path, RLOG_LV_INFO);
|
||||
const char* profile = "./conf/tfe.conf";
|
||||
const char* section = "key_keeper";
|
||||
int keyring_id = 0;
|
||||
@@ -40,12 +242,11 @@ int main()
|
||||
MESA_load_profile_uint_def(profile, section, "cert_store_port", &cert_store_port, 80);
|
||||
struct event_base* evbase = event_base_new();
|
||||
struct future* f_tfe_rpc = future_create("tfe_rpc", tfe_rpc_on_succ, tfe_rpc_on_fail, NULL);
|
||||
struct tfe_rpc* rpc = tfe_rpc_init(NULL, NULL, logger);
|
||||
char url[TFE_STRING_MAX];
|
||||
strncpy(cert_store_host, "www.baidu.com", TFE_STRING_MAX);
|
||||
snprintf(url, TFE_STRING_MAX, "http://%s:%d/ca?host=%s&flag=1&valid=1&kering_id=%d", cert_store_host, cert_store_port, host, keyring_id);
|
||||
char sni[TFE_STRING_MAX] = "www.baidu.com";
|
||||
snprintf(url, TFE_STRING_MAX, "http://%s:%d/ca?host=%s&flag=1&valid=1&kering_id=%d", cert_store_host, cert_store_port, sni, keyring_id);
|
||||
printf("url is %s\n", url);
|
||||
tfe_rpc_async_ask(f_tfe_rpc, rpc, url, GET, DONE_CB, NULL, 0, evbase);
|
||||
event_base_dispatch(evbase);
|
||||
tfe_rpc_async_ask(f_tfe_rpc, url, GET, DONE_CB, NULL, 0, evbase);
|
||||
event_base_dispatch(evbase);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user