mirror of https://github.com/grpc/grpc.git
commit
30da294e3c
340 changed files with 5763 additions and 4793 deletions
@ -1,513 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include <grpc/grpc_zookeeper.h> |
||||
#include <zookeeper/zookeeper.h> |
||||
|
||||
#include "src/core/ext/client_config/lb_policy_registry.h" |
||||
#include "src/core/ext/client_config/resolver_registry.h" |
||||
#include "src/core/lib/iomgr/resolve_address.h" |
||||
#include "src/core/lib/json/json.h" |
||||
#include "src/core/lib/support/string.h" |
||||
#include "src/core/lib/surface/api_trace.h" |
||||
|
||||
/** Zookeeper session expiration time in milliseconds */ |
||||
#define GRPC_ZOOKEEPER_SESSION_TIMEOUT 15000 |
||||
|
||||
typedef struct { |
||||
/** base class: must be first */ |
||||
grpc_resolver base; |
||||
/** refcount */ |
||||
gpr_refcount refs; |
||||
/** name to resolve */ |
||||
char *name; |
||||
/** subchannel factory */ |
||||
grpc_client_channel_factory *client_channel_factory; |
||||
/** load balancing policy name */ |
||||
char *lb_policy_name; |
||||
|
||||
/** mutex guarding the rest of the state */ |
||||
gpr_mu mu; |
||||
/** are we currently resolving? */ |
||||
int resolving; |
||||
/** which version of resolved_config have we published? */ |
||||
int published_version; |
||||
/** which version of resolved_config is current? */ |
||||
int resolved_version; |
||||
/** pending next completion, or NULL */ |
||||
grpc_closure *next_completion; |
||||
/** target config address for next completion */ |
||||
grpc_client_config **target_config; |
||||
/** current (fully resolved) config */ |
||||
grpc_client_config *resolved_config; |
||||
|
||||
/** zookeeper handle */ |
||||
zhandle_t *zookeeper_handle; |
||||
/** zookeeper resolved addresses */ |
||||
grpc_resolved_addresses *resolved_addrs; |
||||
/** total number of addresses to be resolved */ |
||||
int resolved_total; |
||||
/** number of addresses resolved */ |
||||
int resolved_num; |
||||
} zookeeper_resolver; |
||||
|
||||
static void zookeeper_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); |
||||
|
||||
static void zookeeper_start_resolving_locked(zookeeper_resolver *r); |
||||
static void zookeeper_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, |
||||
zookeeper_resolver *r); |
||||
|
||||
static void zookeeper_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); |
||||
static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx, |
||||
grpc_resolver *r); |
||||
static void zookeeper_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, |
||||
grpc_client_config **target_config, |
||||
grpc_closure *on_complete); |
||||
|
||||
static const grpc_resolver_vtable zookeeper_resolver_vtable = { |
||||
zookeeper_destroy, zookeeper_shutdown, zookeeper_channel_saw_error, |
||||
zookeeper_next}; |
||||
|
||||
static void zookeeper_shutdown(grpc_exec_ctx *exec_ctx, |
||||
grpc_resolver *resolver) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)resolver; |
||||
grpc_closure *call = NULL; |
||||
gpr_mu_lock(&r->mu); |
||||
if (r->next_completion != NULL) { |
||||
*r->target_config = NULL; |
||||
call = r->next_completion; |
||||
r->next_completion = NULL; |
||||
} |
||||
zookeeper_close(r->zookeeper_handle); |
||||
gpr_mu_unlock(&r->mu); |
||||
if (call != NULL) { |
||||
call->cb(exec_ctx, call->cb_arg, 1); |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx, |
||||
grpc_resolver *resolver) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)resolver; |
||||
gpr_mu_lock(&r->mu); |
||||
if (r->resolving == 0) { |
||||
zookeeper_start_resolving_locked(r); |
||||
} |
||||
gpr_mu_unlock(&r->mu); |
||||
} |
||||
|
||||
static void zookeeper_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, |
||||
grpc_client_config **target_config, |
||||
grpc_closure *on_complete) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)resolver; |
||||
gpr_mu_lock(&r->mu); |
||||
GPR_ASSERT(r->next_completion == NULL); |
||||
r->next_completion = on_complete; |
||||
r->target_config = target_config; |
||||
if (r->resolved_version == 0 && r->resolving == 0) { |
||||
zookeeper_start_resolving_locked(r); |
||||
} else { |
||||
zookeeper_maybe_finish_next_locked(exec_ctx, r); |
||||
} |
||||
gpr_mu_unlock(&r->mu); |
||||
} |
||||
|
||||
/** Zookeeper global watcher for connection management
|
||||
TODO: better connection management besides logs */ |
||||
static void zookeeper_global_watcher(zhandle_t *zookeeper_handle, int type, |
||||
int state, const char *path, |
||||
void *watcher_ctx) { |
||||
if (type == ZOO_SESSION_EVENT) { |
||||
if (state == ZOO_EXPIRED_SESSION_STATE) { |
||||
gpr_log(GPR_ERROR, "Zookeeper session expired"); |
||||
} else if (state == ZOO_AUTH_FAILED_STATE) { |
||||
gpr_log(GPR_ERROR, "Zookeeper authentication failed"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** Zookeeper watcher triggered by changes to watched nodes
|
||||
Once triggered, it tries to resolve again to get updated addresses */ |
||||
static void zookeeper_watcher(zhandle_t *zookeeper_handle, int type, int state, |
||||
const char *path, void *watcher_ctx) { |
||||
if (watcher_ctx != NULL) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)watcher_ctx; |
||||
if (state == ZOO_CONNECTED_STATE) { |
||||
gpr_mu_lock(&r->mu); |
||||
if (r->resolving == 0) { |
||||
zookeeper_start_resolving_locked(r); |
||||
} |
||||
gpr_mu_unlock(&r->mu); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** Callback function after getting all resolved addresses
|
||||
Creates a subchannel for each address */ |
||||
static void zookeeper_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, |
||||
grpc_resolved_addresses *addresses) { |
||||
zookeeper_resolver *r = arg; |
||||
grpc_client_config *config = NULL; |
||||
grpc_lb_policy *lb_policy; |
||||
|
||||
if (addresses != NULL) { |
||||
grpc_lb_policy_args lb_policy_args; |
||||
config = grpc_client_config_create(); |
||||
lb_policy_args.addresses = addresses; |
||||
lb_policy_args.client_channel_factory = r->client_channel_factory; |
||||
lb_policy = |
||||
grpc_lb_policy_create(exec_ctx, r->lb_policy_name, &lb_policy_args); |
||||
|
||||
if (lb_policy != NULL) { |
||||
grpc_client_config_set_lb_policy(config, lb_policy); |
||||
GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "construction"); |
||||
} |
||||
grpc_resolved_addresses_destroy(addresses); |
||||
} |
||||
gpr_mu_lock(&r->mu); |
||||
GPR_ASSERT(r->resolving == 1); |
||||
r->resolving = 0; |
||||
if (r->resolved_config != NULL) { |
||||
grpc_client_config_unref(exec_ctx, r->resolved_config); |
||||
} |
||||
r->resolved_config = config; |
||||
r->resolved_version++; |
||||
zookeeper_maybe_finish_next_locked(exec_ctx, r); |
||||
gpr_mu_unlock(&r->mu); |
||||
|
||||
GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "zookeeper-resolving"); |
||||
} |
||||
|
||||
/** Callback function for each DNS resolved address */ |
||||
static void zookeeper_dns_resolved(grpc_exec_ctx *exec_ctx, void *arg, |
||||
grpc_resolved_addresses *addresses) { |
||||
size_t i; |
||||
zookeeper_resolver *r = arg; |
||||
int resolve_done = 0; |
||||
|
||||
gpr_mu_lock(&r->mu); |
||||
r->resolved_num++; |
||||
r->resolved_addrs->addrs = |
||||
gpr_realloc(r->resolved_addrs->addrs, |
||||
sizeof(grpc_resolved_address) * |
||||
(r->resolved_addrs->naddrs + addresses->naddrs)); |
||||
for (i = 0; i < addresses->naddrs; i++) { |
||||
memcpy(r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].addr, |
||||
addresses->addrs[i].addr, addresses->addrs[i].len); |
||||
r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].len = |
||||
addresses->addrs[i].len; |
||||
} |
||||
|
||||
r->resolved_addrs->naddrs += addresses->naddrs; |
||||
grpc_resolved_addresses_destroy(addresses); |
||||
|
||||
/** Wait for all addresses to be resolved */ |
||||
resolve_done = (r->resolved_num == r->resolved_total); |
||||
gpr_mu_unlock(&r->mu); |
||||
if (resolve_done) { |
||||
zookeeper_on_resolved(exec_ctx, r, r->resolved_addrs); |
||||
} |
||||
} |
||||
|
||||
/** Parses JSON format address of a zookeeper node */ |
||||
static char *zookeeper_parse_address(const char *value, size_t value_len) { |
||||
grpc_json *json; |
||||
grpc_json *cur; |
||||
const char *host; |
||||
const char *port; |
||||
char *buffer; |
||||
char *address = NULL; |
||||
|
||||
buffer = gpr_malloc(value_len); |
||||
memcpy(buffer, value, value_len); |
||||
json = grpc_json_parse_string_with_len(buffer, value_len); |
||||
if (json != NULL) { |
||||
host = NULL; |
||||
port = NULL; |
||||
for (cur = json->child; cur != NULL; cur = cur->next) { |
||||
if (!strcmp(cur->key, "host")) { |
||||
host = cur->value; |
||||
if (port != NULL) { |
||||
break; |
||||
} |
||||
} else if (!strcmp(cur->key, "port")) { |
||||
port = cur->value; |
||||
if (host != NULL) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
if (host != NULL && port != NULL) { |
||||
gpr_asprintf(&address, "%s:%s", host, port); |
||||
} |
||||
grpc_json_destroy(json); |
||||
} |
||||
gpr_free(buffer); |
||||
|
||||
return address; |
||||
} |
||||
|
||||
static void zookeeper_get_children_node_completion(int rc, const char *value, |
||||
int value_len, |
||||
const struct Stat *stat, |
||||
const void *arg) { |
||||
char *address = NULL; |
||||
zookeeper_resolver *r = (zookeeper_resolver *)arg; |
||||
int resolve_done = 0; |
||||
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; |
||||
|
||||
if (rc != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting a child node of %s", r->name); |
||||
grpc_exec_ctx_finish(&exec_ctx); |
||||
return; |
||||
} |
||||
|
||||
address = zookeeper_parse_address(value, (size_t)value_len); |
||||
if (address != NULL) { |
||||
/** Further resolves address by DNS */ |
||||
grpc_resolve_address(&exec_ctx, address, NULL, zookeeper_dns_resolved, r); |
||||
gpr_free(address); |
||||
} else { |
||||
gpr_log(GPR_ERROR, "Error in resolving a child node of %s", r->name); |
||||
gpr_mu_lock(&r->mu); |
||||
r->resolved_total--; |
||||
resolve_done = (r->resolved_num == r->resolved_total); |
||||
gpr_mu_unlock(&r->mu); |
||||
if (resolve_done) { |
||||
zookeeper_on_resolved(&exec_ctx, r, r->resolved_addrs); |
||||
} |
||||
} |
||||
|
||||
grpc_exec_ctx_finish(&exec_ctx); |
||||
} |
||||
|
||||
static void zookeeper_get_children_completion( |
||||
int rc, const struct String_vector *children, const void *arg) { |
||||
char *path; |
||||
int status; |
||||
int i; |
||||
zookeeper_resolver *r = (zookeeper_resolver *)arg; |
||||
|
||||
if (rc != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name); |
||||
return; |
||||
} |
||||
|
||||
if (children->count == 0) { |
||||
gpr_log(GPR_ERROR, "Error in resolving zookeeper address %s", r->name); |
||||
return; |
||||
} |
||||
|
||||
r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); |
||||
r->resolved_addrs->addrs = NULL; |
||||
r->resolved_addrs->naddrs = 0; |
||||
r->resolved_total = children->count; |
||||
|
||||
/** TODO: Replace expensive heap allocation with stack
|
||||
if we can get maximum length of zookeeper path */ |
||||
for (i = 0; i < children->count; i++) { |
||||
gpr_asprintf(&path, "%s/%s", r->name, children->data[i]); |
||||
status = zoo_awget(r->zookeeper_handle, path, zookeeper_watcher, r, |
||||
zookeeper_get_children_node_completion, r); |
||||
gpr_free(path); |
||||
if (status != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", path); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_get_node_completion(int rc, const char *value, |
||||
int value_len, |
||||
const struct Stat *stat, |
||||
const void *arg) { |
||||
int status; |
||||
char *address = NULL; |
||||
zookeeper_resolver *r = (zookeeper_resolver *)arg; |
||||
r->resolved_addrs = NULL; |
||||
r->resolved_total = 0; |
||||
r->resolved_num = 0; |
||||
|
||||
if (rc != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name); |
||||
return; |
||||
} |
||||
|
||||
/** If zookeeper node of path r->name does not have address
|
||||
(i.e. service node), get its children */ |
||||
address = zookeeper_parse_address(value, (size_t)value_len); |
||||
if (address != NULL) { |
||||
r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); |
||||
r->resolved_addrs->addrs = NULL; |
||||
r->resolved_addrs->naddrs = 0; |
||||
r->resolved_total = 1; |
||||
/** Further resolves address by DNS */ |
||||
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; |
||||
grpc_resolve_address(&exec_ctx, address, NULL, zookeeper_dns_resolved, r); |
||||
gpr_free(address); |
||||
grpc_exec_ctx_finish(&exec_ctx); |
||||
return; |
||||
} |
||||
|
||||
status = zoo_awget_children(r->zookeeper_handle, r->name, zookeeper_watcher, |
||||
r, zookeeper_get_children_completion, r); |
||||
if (status != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name); |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_resolve_address(zookeeper_resolver *r) { |
||||
int status; |
||||
status = zoo_awget(r->zookeeper_handle, r->name, zookeeper_watcher, r, |
||||
zookeeper_get_node_completion, r); |
||||
if (status != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name); |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_start_resolving_locked(zookeeper_resolver *r) { |
||||
GRPC_RESOLVER_REF(&r->base, "zookeeper-resolving"); |
||||
GPR_ASSERT(r->resolving == 0); |
||||
r->resolving = 1; |
||||
zookeeper_resolve_address(r); |
||||
} |
||||
|
||||
static void zookeeper_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, |
||||
zookeeper_resolver *r) { |
||||
if (r->next_completion != NULL && |
||||
r->resolved_version != r->published_version) { |
||||
*r->target_config = r->resolved_config; |
||||
if (r->resolved_config != NULL) { |
||||
grpc_client_config_ref(r->resolved_config); |
||||
} |
||||
grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL); |
||||
r->next_completion = NULL; |
||||
r->published_version = r->resolved_version; |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)gr; |
||||
gpr_mu_destroy(&r->mu); |
||||
if (r->resolved_config != NULL) { |
||||
grpc_client_config_unref(exec_ctx, r->resolved_config); |
||||
} |
||||
grpc_client_channel_factory_unref(exec_ctx, r->client_channel_factory); |
||||
gpr_free(r->name); |
||||
gpr_free(r->lb_policy_name); |
||||
gpr_free(r); |
||||
} |
||||
|
||||
static grpc_resolver *zookeeper_create(grpc_resolver_args *args, |
||||
const char *lb_policy_name) { |
||||
zookeeper_resolver *r; |
||||
size_t length; |
||||
char *path = args->uri->path; |
||||
|
||||
if (0 == strcmp(args->uri->authority, "")) { |
||||
gpr_log(GPR_ERROR, "No authority specified in zookeeper uri"); |
||||
return NULL; |
||||
} |
||||
|
||||
/** Removes the trailing slash if exists */ |
||||
length = strlen(path); |
||||
if (length > 1 && path[length - 1] == '/') { |
||||
path[length - 1] = 0; |
||||
} |
||||
|
||||
r = gpr_malloc(sizeof(zookeeper_resolver)); |
||||
memset(r, 0, sizeof(*r)); |
||||
gpr_ref_init(&r->refs, 1); |
||||
gpr_mu_init(&r->mu); |
||||
grpc_resolver_init(&r->base, &zookeeper_resolver_vtable); |
||||
r->name = gpr_strdup(path); |
||||
|
||||
r->client_channel_factory = args->client_channel_factory; |
||||
grpc_client_channel_factory_ref(r->client_channel_factory); |
||||
|
||||
r->lb_policy_name = gpr_strdup(lb_policy_name); |
||||
|
||||
/** Initializes zookeeper client */ |
||||
zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); |
||||
r->zookeeper_handle = |
||||
zookeeper_init(args->uri->authority, zookeeper_global_watcher, |
||||
GRPC_ZOOKEEPER_SESSION_TIMEOUT, 0, 0, 0); |
||||
if (r->zookeeper_handle == NULL) { |
||||
gpr_log(GPR_ERROR, "Unable to connect to zookeeper server"); |
||||
return NULL; |
||||
} |
||||
|
||||
return &r->base; |
||||
} |
||||
|
||||
/*
|
||||
* FACTORY |
||||
*/ |
||||
|
||||
static void zookeeper_factory_ref(grpc_resolver_factory *factory) {} |
||||
|
||||
static void zookeeper_factory_unref(grpc_resolver_factory *factory) {} |
||||
|
||||
static char *zookeeper_factory_get_default_hostname( |
||||
grpc_resolver_factory *factory, grpc_uri *uri) { |
||||
return NULL; |
||||
} |
||||
|
||||
static grpc_resolver *zookeeper_factory_create_resolver( |
||||
grpc_resolver_factory *factory, grpc_resolver_args *args) { |
||||
return zookeeper_create(args, "pick_first"); |
||||
} |
||||
|
||||
static const grpc_resolver_factory_vtable zookeeper_factory_vtable = { |
||||
zookeeper_factory_ref, zookeeper_factory_unref, |
||||
zookeeper_factory_create_resolver, zookeeper_factory_get_default_hostname, |
||||
"zookeeper"}; |
||||
|
||||
static grpc_resolver_factory zookeeper_resolver_factory = { |
||||
&zookeeper_factory_vtable}; |
||||
|
||||
static grpc_resolver_factory *zookeeper_resolver_factory_create() { |
||||
return &zookeeper_resolver_factory; |
||||
} |
||||
|
||||
static void zookeeper_plugin_init() { |
||||
grpc_register_resolver_type(zookeeper_resolver_factory_create()); |
||||
} |
||||
|
||||
void grpc_zookeeper_register() { |
||||
GRPC_API_TRACE("grpc_zookeeper_register(void)", 0, ()); |
||||
grpc_register_plugin(zookeeper_plugin_init, NULL); |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,357 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include "src/core/lib/http/parser.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/useful.h> |
||||
|
||||
int grpc_http1_trace = 0; |
||||
|
||||
static char *buf2str(void *buffer, size_t length) { |
||||
char *out = gpr_malloc(length + 1); |
||||
memcpy(out, buffer, length); |
||||
out[length] = 0; |
||||
return out; |
||||
} |
||||
|
||||
static grpc_error *handle_response_line(grpc_http_parser *parser) { |
||||
uint8_t *beg = parser->cur_line; |
||||
uint8_t *cur = beg; |
||||
uint8_t *end = beg + parser->cur_line_length; |
||||
|
||||
if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'"); |
||||
if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); |
||||
if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); |
||||
if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'"); |
||||
if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'"); |
||||
if (cur == end || *cur++ != '1') return GRPC_ERROR_CREATE("Expected '1'"); |
||||
if (cur == end || *cur++ != '.') return GRPC_ERROR_CREATE("Expected '.'"); |
||||
if (cur == end || *cur < '0' || *cur++ > '1') { |
||||
return GRPC_ERROR_CREATE("Expected HTTP/1.0 or HTTP/1.1"); |
||||
} |
||||
if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '"); |
||||
if (cur == end || *cur < '1' || *cur++ > '9') |
||||
return GRPC_ERROR_CREATE("Expected status code"); |
||||
if (cur == end || *cur < '0' || *cur++ > '9') |
||||
return GRPC_ERROR_CREATE("Expected status code"); |
||||
if (cur == end || *cur < '0' || *cur++ > '9') |
||||
return GRPC_ERROR_CREATE("Expected status code"); |
||||
parser->http.response->status = |
||||
(cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0'); |
||||
if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '"); |
||||
|
||||
/* we don't really care about the status code message */ |
||||
|
||||
return GRPC_ERROR_NONE; |
||||
} |
||||
|
||||
static grpc_error *handle_request_line(grpc_http_parser *parser) { |
||||
uint8_t *beg = parser->cur_line; |
||||
uint8_t *cur = beg; |
||||
uint8_t *end = beg + parser->cur_line_length; |
||||
uint8_t vers_major = 0; |
||||
uint8_t vers_minor = 0; |
||||
|
||||
while (cur != end && *cur++ != ' ') |
||||
; |
||||
if (cur == end) return GRPC_ERROR_CREATE("No method on HTTP request line"); |
||||
parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1)); |
||||
|
||||
beg = cur; |
||||
while (cur != end && *cur++ != ' ') |
||||
; |
||||
if (cur == end) return GRPC_ERROR_CREATE("No path on HTTP request line"); |
||||
parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1)); |
||||
|
||||
if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'"); |
||||
if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); |
||||
if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); |
||||
if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'"); |
||||
if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'"); |
||||
vers_major = (uint8_t)(*cur++ - '1' + 1); |
||||
++cur; |
||||
if (cur == end) |
||||
return GRPC_ERROR_CREATE("End of line in HTTP version string"); |
||||
vers_minor = (uint8_t)(*cur++ - '1' + 1); |
||||
|
||||
if (vers_major == 1) { |
||||
if (vers_minor == 0) { |
||||
parser->http.request->version = GRPC_HTTP_HTTP10; |
||||
} else if (vers_minor == 1) { |
||||
parser->http.request->version = GRPC_HTTP_HTTP11; |
||||
} else { |
||||
return GRPC_ERROR_CREATE( |
||||
"Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); |
||||
} |
||||
} else if (vers_major == 2) { |
||||
if (vers_minor == 0) { |
||||
parser->http.request->version = GRPC_HTTP_HTTP20; |
||||
} else { |
||||
return GRPC_ERROR_CREATE( |
||||
"Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); |
||||
} |
||||
} else { |
||||
return GRPC_ERROR_CREATE("Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); |
||||
} |
||||
|
||||
return GRPC_ERROR_NONE; |
||||
} |
||||
|
||||
static grpc_error *handle_first_line(grpc_http_parser *parser) { |
||||
switch (parser->type) { |
||||
case GRPC_HTTP_REQUEST: |
||||
return handle_request_line(parser); |
||||
case GRPC_HTTP_RESPONSE: |
||||
return handle_response_line(parser); |
||||
} |
||||
GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); |
||||
} |
||||
|
||||
static grpc_error *add_header(grpc_http_parser *parser) { |
||||
uint8_t *beg = parser->cur_line; |
||||
uint8_t *cur = beg; |
||||
uint8_t *end = beg + parser->cur_line_length; |
||||
size_t *hdr_count = NULL; |
||||
grpc_http_header **hdrs = NULL; |
||||
grpc_http_header hdr = {NULL, NULL}; |
||||
grpc_error *error = GRPC_ERROR_NONE; |
||||
|
||||
GPR_ASSERT(cur != end); |
||||
|
||||
if (*cur == ' ' || *cur == '\t') { |
||||
error = GRPC_ERROR_CREATE("Continued header lines not supported yet"); |
||||
goto done; |
||||
} |
||||
|
||||
while (cur != end && *cur != ':') { |
||||
cur++; |
||||
} |
||||
if (cur == end) { |
||||
<<<<<<< HEAD |
||||
error = GRPC_ERROR_CREATE("Didn't find ':' in header string"); |
||||
goto done; |
||||
======= |
||||
if (grpc_http1_trace) { |
||||
gpr_log(GPR_ERROR, "Didn't find ':' in header string"); |
||||
} |
||||
goto error; |
||||
>>>>>>> a709afe241d8b264a1c326315f757b4a8d330207 |
||||
} |
||||
GPR_ASSERT(cur >= beg); |
||||
hdr.key = buf2str(beg, (size_t)(cur - beg)); |
||||
cur++; /* skip : */ |
||||
|
||||
while (cur != end && (*cur == ' ' || *cur == '\t')) { |
||||
cur++; |
||||
} |
||||
GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length); |
||||
hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length); |
||||
|
||||
switch (parser->type) { |
||||
case GRPC_HTTP_RESPONSE: |
||||
hdr_count = &parser->http.response->hdr_count; |
||||
hdrs = &parser->http.response->hdrs; |
||||
break; |
||||
case GRPC_HTTP_REQUEST: |
||||
hdr_count = &parser->http.request->hdr_count; |
||||
hdrs = &parser->http.request->hdrs; |
||||
break; |
||||
} |
||||
|
||||
if (*hdr_count == parser->hdr_capacity) { |
||||
parser->hdr_capacity = |
||||
GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2); |
||||
*hdrs = gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs)); |
||||
} |
||||
(*hdrs)[(*hdr_count)++] = hdr; |
||||
|
||||
done: |
||||
if (error != GRPC_ERROR_NONE) { |
||||
gpr_free(hdr.key); |
||||
gpr_free(hdr.value); |
||||
} |
||||
return error; |
||||
} |
||||
|
||||
static grpc_error *finish_line(grpc_http_parser *parser) { |
||||
grpc_error *err; |
||||
switch (parser->state) { |
||||
case GRPC_HTTP_FIRST_LINE: |
||||
err = handle_first_line(parser); |
||||
if (err != GRPC_ERROR_NONE) return err; |
||||
parser->state = GRPC_HTTP_HEADERS; |
||||
break; |
||||
case GRPC_HTTP_HEADERS: |
||||
if (parser->cur_line_length == parser->cur_line_end_length) { |
||||
parser->state = GRPC_HTTP_BODY; |
||||
break; |
||||
} |
||||
err = add_header(parser); |
||||
if (err != GRPC_ERROR_NONE) { |
||||
return err; |
||||
} |
||||
break; |
||||
case GRPC_HTTP_BODY: |
||||
GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); |
||||
} |
||||
|
||||
parser->cur_line_length = 0; |
||||
return GRPC_ERROR_NONE; |
||||
} |
||||
|
||||
static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) { |
||||
size_t *body_length = NULL; |
||||
char **body = NULL; |
||||
|
||||
if (parser->type == GRPC_HTTP_RESPONSE) { |
||||
body_length = &parser->http.response->body_length; |
||||
body = &parser->http.response->body; |
||||
} else if (parser->type == GRPC_HTTP_REQUEST) { |
||||
body_length = &parser->http.request->body_length; |
||||
body = &parser->http.request->body; |
||||
} else { |
||||
GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); |
||||
} |
||||
|
||||
if (*body_length == parser->body_capacity) { |
||||
parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2); |
||||
*body = gpr_realloc((void *)*body, parser->body_capacity); |
||||
} |
||||
(*body)[*body_length] = (char)byte; |
||||
(*body_length)++; |
||||
|
||||
return GRPC_ERROR_NONE; |
||||
} |
||||
|
||||
static bool check_line(grpc_http_parser *parser) { |
||||
if (parser->cur_line_length >= 2 && |
||||
parser->cur_line[parser->cur_line_length - 2] == '\r' && |
||||
parser->cur_line[parser->cur_line_length - 1] == '\n') { |
||||
return true; |
||||
} |
||||
|
||||
// HTTP request with \n\r line termiantors.
|
||||
else if (parser->cur_line_length >= 2 && |
||||
parser->cur_line[parser->cur_line_length - 2] == '\n' && |
||||
parser->cur_line[parser->cur_line_length - 1] == '\r') { |
||||
return true; |
||||
} |
||||
|
||||
// HTTP request with only \n line terminators.
|
||||
else if (parser->cur_line_length >= 1 && |
||||
parser->cur_line[parser->cur_line_length - 1] == '\n') { |
||||
parser->cur_line_end_length = 1; |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) { |
||||
switch (parser->state) { |
||||
case GRPC_HTTP_FIRST_LINE: |
||||
case GRPC_HTTP_HEADERS: |
||||
if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) { |
||||
if (grpc_http1_trace) |
||||
gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded", |
||||
GRPC_HTTP_PARSER_MAX_HEADER_LENGTH); |
||||
return 0; |
||||
} |
||||
parser->cur_line[parser->cur_line_length] = byte; |
||||
parser->cur_line_length++; |
||||
if (check_line(parser)) { |
||||
return finish_line(parser); |
||||
} else { |
||||
return GRPC_ERROR_NONE; |
||||
} |
||||
GPR_UNREACHABLE_CODE(return 0); |
||||
case GRPC_HTTP_BODY: |
||||
return addbyte_body(parser, byte); |
||||
} |
||||
GPR_UNREACHABLE_CODE(return 0); |
||||
} |
||||
|
||||
void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type, |
||||
void *request_or_response) { |
||||
memset(parser, 0, sizeof(*parser)); |
||||
parser->state = GRPC_HTTP_FIRST_LINE; |
||||
parser->type = type; |
||||
parser->http.request_or_response = request_or_response; |
||||
parser->cur_line_end_length = 2; |
||||
} |
||||
|
||||
void grpc_http_parser_destroy(grpc_http_parser *parser) {} |
||||
|
||||
void grpc_http_request_destroy(grpc_http_request *request) { |
||||
size_t i; |
||||
gpr_free(request->body); |
||||
for (i = 0; i < request->hdr_count; i++) { |
||||
gpr_free(request->hdrs[i].key); |
||||
gpr_free(request->hdrs[i].value); |
||||
} |
||||
gpr_free(request->hdrs); |
||||
gpr_free(request->method); |
||||
gpr_free(request->path); |
||||
} |
||||
|
||||
void grpc_http_response_destroy(grpc_http_response *response) { |
||||
size_t i; |
||||
gpr_free(response->body); |
||||
for (i = 0; i < response->hdr_count; i++) { |
||||
gpr_free(response->hdrs[i].key); |
||||
gpr_free(response->hdrs[i].value); |
||||
} |
||||
gpr_free(response->hdrs); |
||||
} |
||||
|
||||
grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) { |
||||
size_t i; |
||||
|
||||
for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) { |
||||
grpc_error *err = addbyte(parser, GPR_SLICE_START_PTR(slice)[i]); |
||||
if (err != GRPC_ERROR_NONE) return err; |
||||
} |
||||
|
||||
return GRPC_ERROR_NONE; |
||||
} |
||||
|
||||
grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) { |
||||
if (parser->state != GRPC_HTTP_BODY) { |
||||
return GRPC_ERROR_CREATE("Did not finish headers"); |
||||
} |
||||
return GRPC_ERROR_NONE; |
||||
} |
@ -0,0 +1,536 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include "src/core/lib/iomgr/error.h" |
||||
|
||||
#include <stdbool.h> |
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/avl.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
#include <grpc/support/useful.h> |
||||
|
||||
#ifdef GPR_WIN32 |
||||
#include <grpc/support/log_win32.h> |
||||
#endif |
||||
|
||||
static void destroy_integer(void *key) {} |
||||
|
||||
static void *copy_integer(void *key) { return key; } |
||||
|
||||
static long compare_integers(void *key1, void *key2) { |
||||
return GPR_ICMP((uintptr_t)key1, (uintptr_t)key2); |
||||
} |
||||
|
||||
static void destroy_string(void *str) { gpr_free(str); } |
||||
|
||||
static void *copy_string(void *str) { return gpr_strdup(str); } |
||||
|
||||
static void destroy_err(void *err) { GRPC_ERROR_UNREF(err); } |
||||
|
||||
static void *copy_err(void *err) { return GRPC_ERROR_REF(err); } |
||||
|
||||
static void destroy_time(void *tm) { gpr_free(tm); } |
||||
|
||||
static gpr_timespec *box_time(gpr_timespec tm) { |
||||
gpr_timespec *out = gpr_malloc(sizeof(*out)); |
||||
*out = tm; |
||||
return out; |
||||
} |
||||
|
||||
static void *copy_time(void *tm) { return box_time(*(gpr_timespec *)tm); } |
||||
|
||||
static const gpr_avl_vtable avl_vtable_ints = {destroy_integer, copy_integer, |
||||
compare_integers, |
||||
destroy_integer, copy_integer}; |
||||
|
||||
static const gpr_avl_vtable avl_vtable_strs = {destroy_integer, copy_integer, |
||||
compare_integers, destroy_string, |
||||
copy_string}; |
||||
|
||||
static const gpr_avl_vtable avl_vtable_times = { |
||||
destroy_integer, copy_integer, compare_integers, destroy_time, copy_time}; |
||||
|
||||
static const gpr_avl_vtable avl_vtable_errs = { |
||||
destroy_integer, copy_integer, compare_integers, destroy_err, copy_err}; |
||||
|
||||
static const char *error_int_name(grpc_error_ints key) { |
||||
switch (key) { |
||||
case GRPC_ERROR_INT_STATUS_CODE: |
||||
return "status_code"; |
||||
case GRPC_ERROR_INT_ERRNO: |
||||
return "errno"; |
||||
case GRPC_ERROR_INT_FILE_LINE: |
||||
return "file_line"; |
||||
case GRPC_ERROR_INT_WARNING: |
||||
return "warning"; |
||||
case GRPC_ERROR_INT_STREAM_ID: |
||||
return "stream_id"; |
||||
case GRPC_ERROR_INT_GRPC_STATUS: |
||||
return "grpc_status"; |
||||
case GRPC_ERROR_INT_OFFSET: |
||||
return "offset"; |
||||
case GRPC_ERROR_INT_INDEX: |
||||
return "index"; |
||||
case GRPC_ERROR_INT_SIZE: |
||||
return "size"; |
||||
case GRPC_ERROR_INT_HTTP2_ERROR: |
||||
return "http2_error"; |
||||
case GRPC_ERROR_INT_TSI_CODE: |
||||
return "tsi_code"; |
||||
case GRPC_ERROR_INT_SECURITY_STATUS: |
||||
return "security_status"; |
||||
case GRPC_ERROR_INT_FD: |
||||
return "fd"; |
||||
case GRPC_ERROR_INT_WSA_ERROR: |
||||
return "wsa_error"; |
||||
} |
||||
GPR_UNREACHABLE_CODE(return "unknown"); |
||||
} |
||||
|
||||
static const char *error_str_name(grpc_error_strs key) { |
||||
switch (key) { |
||||
case GRPC_ERROR_STR_DESCRIPTION: |
||||
return "description"; |
||||
case GRPC_ERROR_STR_OS_ERROR: |
||||
return "os_error"; |
||||
case GRPC_ERROR_STR_TARGET_ADDRESS: |
||||
return "target_address"; |
||||
case GRPC_ERROR_STR_SYSCALL: |
||||
return "syscall"; |
||||
case GRPC_ERROR_STR_FILE: |
||||
return "file"; |
||||
case GRPC_ERROR_STR_GRPC_MESSAGE: |
||||
return "grpc_message"; |
||||
case GRPC_ERROR_STR_RAW_BYTES: |
||||
return "raw_bytes"; |
||||
case GRPC_ERROR_STR_TSI_ERROR: |
||||
return "tsi_error"; |
||||
case GRPC_ERROR_STR_FILENAME: |
||||
return "filename"; |
||||
} |
||||
GPR_UNREACHABLE_CODE(return "unknown"); |
||||
} |
||||
|
||||
static const char *error_time_name(grpc_error_times key) { |
||||
switch (key) { |
||||
case GRPC_ERROR_TIME_CREATED: |
||||
return "created"; |
||||
} |
||||
GPR_UNREACHABLE_CODE(return "unknown"); |
||||
} |
||||
|
||||
struct grpc_error { |
||||
gpr_refcount refs; |
||||
gpr_avl ints; |
||||
gpr_avl strs; |
||||
gpr_avl times; |
||||
gpr_avl errs; |
||||
uintptr_t next_err; |
||||
}; |
||||
|
||||
static bool is_special(grpc_error *err) { |
||||
return err == GRPC_ERROR_NONE || err == GRPC_ERROR_OOM || |
||||
err == GRPC_ERROR_CANCELLED; |
||||
} |
||||
|
||||
#ifdef GRPC_ERROR_REFCOUNT_DEBUG |
||||
grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line, |
||||
const char *func) { |
||||
if (is_special(err)) return err; |
||||
gpr_log(GPR_DEBUG, "%p: %d -> %d [%s:%d %s]", err, err->refs.count, |
||||
err->refs.count + 1, file, line, func); |
||||
gpr_ref(&err->refs); |
||||
return err; |
||||
} |
||||
#else |
||||
grpc_error *grpc_error_ref(grpc_error *err) { |
||||
if (is_special(err)) return err; |
||||
gpr_ref(&err->refs); |
||||
return err; |
||||
} |
||||
#endif |
||||
|
||||
static void error_destroy(grpc_error *err) { |
||||
GPR_ASSERT(!is_special(err)); |
||||
gpr_avl_unref(err->ints); |
||||
gpr_avl_unref(err->strs); |
||||
gpr_avl_unref(err->errs); |
||||
gpr_avl_unref(err->times); |
||||
gpr_free(err); |
||||
} |
||||
|
||||
#ifdef GRPC_ERROR_REFCOUNT_DEBUG |
||||
void grpc_error_unref(grpc_error *err, const char *file, int line, |
||||
const char *func) { |
||||
if (is_special(err)) return; |
||||
gpr_log(GPR_DEBUG, "%p: %d -> %d [%s:%d %s]", err, err->refs.count, |
||||
err->refs.count - 1, file, line, func); |
||||
if (gpr_unref(&err->refs)) { |
||||
error_destroy(err); |
||||
} |
||||
} |
||||
#else |
||||
void grpc_error_unref(grpc_error *err) { |
||||
if (is_special(err)) return; |
||||
if (gpr_unref(&err->refs)) { |
||||
error_destroy(err); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
grpc_error *grpc_error_create(const char *file, int line, const char *desc, |
||||
grpc_error **referencing, |
||||
size_t num_referencing) { |
||||
grpc_error *err = gpr_malloc(sizeof(*err)); |
||||
if (err == NULL) { // TODO(ctiller): make gpr_malloc return NULL
|
||||
return GRPC_ERROR_OOM; |
||||
} |
||||
#ifdef GRPC_ERROR_REFCOUNT_DEBUG |
||||
gpr_log(GPR_DEBUG, "%p create [%s:%d]", err, file, line); |
||||
#endif |
||||
err->ints = gpr_avl_add(gpr_avl_create(&avl_vtable_ints), |
||||
(void *)(uintptr_t)GRPC_ERROR_INT_FILE_LINE, |
||||
(void *)(uintptr_t)line); |
||||
err->strs = gpr_avl_add( |
||||
gpr_avl_add(gpr_avl_create(&avl_vtable_strs), |
||||
(void *)(uintptr_t)GRPC_ERROR_STR_FILE, gpr_strdup(file)), |
||||
(void *)(uintptr_t)GRPC_ERROR_STR_DESCRIPTION, gpr_strdup(desc)); |
||||
err->errs = gpr_avl_create(&avl_vtable_errs); |
||||
err->next_err = 0; |
||||
for (size_t i = 0; i < num_referencing; i++) { |
||||
if (referencing[i] == GRPC_ERROR_NONE) continue; |
||||
err->errs = gpr_avl_add(err->errs, (void *)(err->next_err++), |
||||
GRPC_ERROR_REF(referencing[i])); |
||||
} |
||||
err->times = gpr_avl_add(gpr_avl_create(&avl_vtable_times), |
||||
(void *)(uintptr_t)GRPC_ERROR_TIME_CREATED, |
||||
box_time(gpr_now(GPR_CLOCK_REALTIME))); |
||||
gpr_ref_init(&err->refs, 1); |
||||
return err; |
||||
} |
||||
|
||||
static grpc_error *copy_error_and_unref(grpc_error *in) { |
||||
if (is_special(in)) { |
||||
if (in == GRPC_ERROR_NONE) return GRPC_ERROR_CREATE("no error"); |
||||
if (in == GRPC_ERROR_OOM) return GRPC_ERROR_CREATE("oom"); |
||||
if (in == GRPC_ERROR_CANCELLED) return GRPC_ERROR_CREATE("cancelled"); |
||||
return GRPC_ERROR_CREATE("unknown"); |
||||
} |
||||
grpc_error *out = gpr_malloc(sizeof(*out)); |
||||
#ifdef GRPC_ERROR_REFCOUNT_DEBUG |
||||
gpr_log(GPR_DEBUG, "%p create copying", out); |
||||
#endif |
||||
out->ints = gpr_avl_ref(in->ints); |
||||
out->strs = gpr_avl_ref(in->strs); |
||||
out->errs = gpr_avl_ref(in->errs); |
||||
out->times = gpr_avl_ref(in->times); |
||||
out->next_err = in->next_err; |
||||
gpr_ref_init(&out->refs, 1); |
||||
GRPC_ERROR_UNREF(in); |
||||
return out; |
||||
} |
||||
|
||||
grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which, |
||||
intptr_t value) { |
||||
grpc_error *new = copy_error_and_unref(src); |
||||
new->ints = gpr_avl_add(new->ints, (void *)(uintptr_t)which, (void *)value); |
||||
return new; |
||||
} |
||||
|
||||
bool grpc_error_get_int(grpc_error *err, grpc_error_ints which, intptr_t *p) { |
||||
void *pp; |
||||
if (gpr_avl_maybe_get(err->ints, (void *)(uintptr_t)which, &pp)) { |
||||
if (p != NULL) *p = (intptr_t)pp; |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which, |
||||
const char *value) { |
||||
grpc_error *new = copy_error_and_unref(src); |
||||
new->strs = |
||||
gpr_avl_add(new->strs, (void *)(uintptr_t)which, gpr_strdup(value)); |
||||
return new; |
||||
} |
||||
|
||||
grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child) { |
||||
grpc_error *new = copy_error_and_unref(src); |
||||
new->errs = gpr_avl_add(new->errs, (void *)(new->next_err++), child); |
||||
return new; |
||||
} |
||||
|
||||
static const char *no_error_string = "null"; |
||||
static const char *oom_error_string = "\"Out of memory\""; |
||||
static const char *cancelled_error_string = "\"Cancelled\""; |
||||
|
||||
typedef struct { |
||||
char *key; |
||||
char *value; |
||||
} kv_pair; |
||||
|
||||
typedef struct { |
||||
kv_pair *kvs; |
||||
size_t num_kvs; |
||||
size_t cap_kvs; |
||||
} kv_pairs; |
||||
|
||||
static void append_kv(kv_pairs *kvs, char *key, char *value) { |
||||
if (kvs->num_kvs == kvs->cap_kvs) { |
||||
kvs->cap_kvs = GPR_MAX(3 * kvs->cap_kvs / 2, 4); |
||||
kvs->kvs = gpr_realloc(kvs->kvs, sizeof(*kvs->kvs) * kvs->cap_kvs); |
||||
} |
||||
kvs->kvs[kvs->num_kvs].key = key; |
||||
kvs->kvs[kvs->num_kvs].value = value; |
||||
kvs->num_kvs++; |
||||
} |
||||
|
||||
static void collect_kvs(gpr_avl_node *node, char *key(void *k), |
||||
char *fmt(void *v), kv_pairs *kvs) { |
||||
if (node == NULL) return; |
||||
append_kv(kvs, key(node->key), fmt(node->value)); |
||||
collect_kvs(node->left, key, fmt, kvs); |
||||
collect_kvs(node->right, key, fmt, kvs); |
||||
} |
||||
|
||||
static char *key_int(void *p) { |
||||
return gpr_strdup(error_int_name((grpc_error_ints)(uintptr_t)p)); |
||||
} |
||||
|
||||
static char *key_str(void *p) { |
||||
return gpr_strdup(error_str_name((grpc_error_strs)(uintptr_t)p)); |
||||
} |
||||
|
||||
static char *key_time(void *p) { |
||||
return gpr_strdup(error_time_name((grpc_error_times)(uintptr_t)p)); |
||||
} |
||||
|
||||
static char *fmt_int(void *p) { |
||||
char *s; |
||||
gpr_asprintf(&s, "%lld", (intptr_t)p); |
||||
return s; |
||||
} |
||||
|
||||
static void append_chr(char c, char **s, size_t *sz, size_t *cap) { |
||||
if (*sz == *cap) { |
||||
*cap = GPR_MAX(8, 3 * *cap / 2); |
||||
*s = gpr_realloc(*s, *cap); |
||||
} |
||||
(*s)[(*sz)++] = c; |
||||
} |
||||
|
||||
static void append_str(const char *str, char **s, size_t *sz, size_t *cap) { |
||||
for (const char *c = str; *c; c++) { |
||||
append_chr(*c, s, sz, cap); |
||||
} |
||||
} |
||||
|
||||
static void append_esc_str(const char *str, char **s, size_t *sz, size_t *cap) { |
||||
static const char *hex = "0123456789abcdef"; |
||||
append_chr('"', s, sz, cap); |
||||
for (const uint8_t *c = (const uint8_t *)str; *c; c++) { |
||||
if (*c < 32 || *c >= 127) { |
||||
append_chr('\\', s, sz, cap); |
||||
switch (*c) { |
||||
case '\b': |
||||
append_chr('b', s, sz, cap); |
||||
break; |
||||
case '\f': |
||||
append_chr('f', s, sz, cap); |
||||
break; |
||||
case '\n': |
||||
append_chr('n', s, sz, cap); |
||||
break; |
||||
case '\r': |
||||
append_chr('r', s, sz, cap); |
||||
break; |
||||
case '\t': |
||||
append_chr('t', s, sz, cap); |
||||
break; |
||||
default: |
||||
append_chr('u', s, sz, cap); |
||||
append_chr('0', s, sz, cap); |
||||
append_chr('0', s, sz, cap); |
||||
append_chr(hex[*c >> 4], s, sz, cap); |
||||
append_chr(hex[*c & 0x0f], s, sz, cap); |
||||
break; |
||||
} |
||||
} else { |
||||
append_chr((char)*c, s, sz, cap); |
||||
} |
||||
} |
||||
append_chr('"', s, sz, cap); |
||||
} |
||||
|
||||
static char *fmt_str(void *p) { |
||||
char *s = NULL; |
||||
size_t sz = 0; |
||||
size_t cap = 0; |
||||
append_esc_str(p, &s, &sz, &cap); |
||||
append_chr(0, &s, &sz, &cap); |
||||
return s; |
||||
} |
||||
|
||||
static char *fmt_time(void *p) { |
||||
gpr_timespec tm = *(gpr_timespec *)p; |
||||
char *out; |
||||
char *pfx = "!!"; |
||||
switch (tm.clock_type) { |
||||
case GPR_CLOCK_MONOTONIC: |
||||
pfx = "@monotonic:"; |
||||
break; |
||||
case GPR_CLOCK_REALTIME: |
||||
pfx = "@"; |
||||
break; |
||||
case GPR_CLOCK_PRECISE: |
||||
pfx = "@precise:"; |
||||
break; |
||||
case GPR_TIMESPAN: |
||||
pfx = ""; |
||||
break; |
||||
} |
||||
gpr_asprintf(&out, "\"%s%d.%09d\"", pfx, tm.tv_sec, tm.tv_nsec); |
||||
return out; |
||||
} |
||||
|
||||
static void add_errs(gpr_avl_node *n, char **s, size_t *sz, size_t *cap) { |
||||
if (n == NULL) return; |
||||
add_errs(n->left, s, sz, cap); |
||||
const char *e = grpc_error_string(n->value); |
||||
append_str(e, s, sz, cap); |
||||
grpc_error_free_string(e); |
||||
add_errs(n->right, s, sz, cap); |
||||
} |
||||
|
||||
static char *errs_string(grpc_error *err) { |
||||
char *s = NULL; |
||||
size_t sz = 0; |
||||
size_t cap = 0; |
||||
append_chr('[', &s, &sz, &cap); |
||||
add_errs(err->errs.root, &s, &sz, &cap); |
||||
append_chr(']', &s, &sz, &cap); |
||||
append_chr(0, &s, &sz, &cap); |
||||
return s; |
||||
} |
||||
|
||||
static int cmp_kvs(const void *a, const void *b) { |
||||
const kv_pair *ka = a; |
||||
const kv_pair *kb = b; |
||||
return strcmp(ka->key, kb->key); |
||||
} |
||||
|
||||
static const char *finish_kvs(kv_pairs *kvs) { |
||||
char *s = NULL; |
||||
size_t sz = 0; |
||||
size_t cap = 0; |
||||
|
||||
append_chr('{', &s, &sz, &cap); |
||||
for (size_t i = 0; i < kvs->num_kvs; i++) { |
||||
if (i != 0) append_chr(',', &s, &sz, &cap); |
||||
append_esc_str(kvs->kvs[i].key, &s, &sz, &cap); |
||||
gpr_free(kvs->kvs[i].key); |
||||
append_chr(':', &s, &sz, &cap); |
||||
append_str(kvs->kvs[i].value, &s, &sz, &cap); |
||||
gpr_free(kvs->kvs[i].value); |
||||
} |
||||
append_chr('}', &s, &sz, &cap); |
||||
append_chr(0, &s, &sz, &cap); |
||||
|
||||
gpr_free(kvs->kvs); |
||||
return s; |
||||
} |
||||
|
||||
void grpc_error_free_string(const char *str) { |
||||
if (str == no_error_string) return; |
||||
if (str == oom_error_string) return; |
||||
if (str == cancelled_error_string) return; |
||||
gpr_free((char *)str); |
||||
} |
||||
|
||||
const char *grpc_error_string(grpc_error *err) { |
||||
if (err == GRPC_ERROR_NONE) return no_error_string; |
||||
if (err == GRPC_ERROR_OOM) return oom_error_string; |
||||
if (err == GRPC_ERROR_CANCELLED) return cancelled_error_string; |
||||
|
||||
kv_pairs kvs; |
||||
memset(&kvs, 0, sizeof(kvs)); |
||||
|
||||
collect_kvs(err->ints.root, key_int, fmt_int, &kvs); |
||||
collect_kvs(err->strs.root, key_str, fmt_str, &kvs); |
||||
collect_kvs(err->times.root, key_time, fmt_time, &kvs); |
||||
if (!gpr_avl_is_empty(err->errs)) { |
||||
append_kv(&kvs, gpr_strdup("referenced_errors"), errs_string(err)); |
||||
} |
||||
|
||||
qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs); |
||||
|
||||
return finish_kvs(&kvs); |
||||
} |
||||
|
||||
grpc_error *grpc_os_error(const char *file, int line, int err, |
||||
const char *call_name) { |
||||
return grpc_error_set_str( |
||||
grpc_error_set_str( |
||||
grpc_error_set_int(grpc_error_create(file, line, "OS Error", NULL, 0), |
||||
GRPC_ERROR_INT_ERRNO, err), |
||||
GRPC_ERROR_STR_OS_ERROR, strerror(err)), |
||||
GRPC_ERROR_STR_SYSCALL, call_name); |
||||
} |
||||
|
||||
#ifdef GPR_WIN32 |
||||
grpc_error *grpc_wsa_error(const char *file, int line, int err, |
||||
const char *call_name) { |
||||
char *utf8_message = gpr_format_message(err); |
||||
grpc_error *error = grpc_error_set_str( |
||||
grpc_error_set_str( |
||||
grpc_error_set_int(grpc_error_create(file, line, "OS Error", NULL, 0), |
||||
GRPC_ERROR_INT_WSA_ERROR, err), |
||||
GRPC_ERROR_STR_OS_ERROR, utf8_message), |
||||
GRPC_ERROR_STR_SYSCALL, call_name); |
||||
gpr_free(utf8_message); |
||||
return error; |
||||
} |
||||
#endif |
||||
|
||||
bool grpc_log_if_error(const char *what, grpc_error *error, const char *file, |
||||
int line) { |
||||
if (error == GRPC_ERROR_NONE) return true; |
||||
const char *msg = grpc_error_string(error); |
||||
gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, "%s: %s", what, msg); |
||||
grpc_error_free_string(msg); |
||||
GRPC_ERROR_UNREF(error); |
||||
return false; |
||||
} |
@ -0,0 +1,143 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_IOMGR_ERROR_H |
||||
#define GRPC_CORE_LIB_IOMGR_ERROR_H |
||||
|
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
|
||||
#include <grpc/support/time.h> |
||||
|
||||
// Opaque representation of an error.
|
||||
// Errors are refcounted objects that represent the result of an operation.
|
||||
// Ownership laws:
|
||||
// if a grpc_error is returned by a function, the caller owns a ref to that
|
||||
// instance
|
||||
// if a grpc_error is passed to a grpc_closure callback function (functions
|
||||
// with the signature:
|
||||
// void (*f)(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error))
|
||||
// then those functions do not automatically own a ref to error
|
||||
// if a grpc_error is passed to *ANY OTHER FUNCTION* then that function takes
|
||||
// ownership of the error
|
||||
typedef struct grpc_error grpc_error; |
||||
|
||||
typedef enum { |
||||
GRPC_ERROR_INT_ERRNO, |
||||
GRPC_ERROR_INT_FILE_LINE, |
||||
GRPC_ERROR_INT_STATUS_CODE, |
||||
GRPC_ERROR_INT_WARNING, |
||||
GRPC_ERROR_INT_STREAM_ID, |
||||
GRPC_ERROR_INT_GRPC_STATUS, |
||||
GRPC_ERROR_INT_OFFSET, |
||||
GRPC_ERROR_INT_INDEX, |
||||
GRPC_ERROR_INT_SIZE, |
||||
GRPC_ERROR_INT_HTTP2_ERROR, |
||||
GRPC_ERROR_INT_TSI_CODE, |
||||
GRPC_ERROR_INT_SECURITY_STATUS, |
||||
GRPC_ERROR_INT_WSA_ERROR, |
||||
GRPC_ERROR_INT_FD, |
||||
} grpc_error_ints; |
||||
|
||||
typedef enum { |
||||
GRPC_ERROR_STR_DESCRIPTION, |
||||
GRPC_ERROR_STR_FILE, |
||||
GRPC_ERROR_STR_OS_ERROR, |
||||
GRPC_ERROR_STR_SYSCALL, |
||||
GRPC_ERROR_STR_TARGET_ADDRESS, |
||||
GRPC_ERROR_STR_GRPC_MESSAGE, |
||||
GRPC_ERROR_STR_RAW_BYTES, |
||||
GRPC_ERROR_STR_TSI_ERROR, |
||||
GRPC_ERROR_STR_FILENAME, |
||||
} grpc_error_strs; |
||||
|
||||
typedef enum { |
||||
GRPC_ERROR_TIME_CREATED, |
||||
} grpc_error_times; |
||||
|
||||
#define GRPC_ERROR_NONE ((grpc_error *)NULL) |
||||
#define GRPC_ERROR_OOM ((grpc_error *)1) |
||||
#define GRPC_ERROR_CANCELLED ((grpc_error *)2) |
||||
|
||||
const char *grpc_error_string(grpc_error *error); |
||||
void grpc_error_free_string(const char *str); |
||||
|
||||
grpc_error *grpc_error_create(const char *file, int line, const char *desc, |
||||
grpc_error **referencing, size_t num_referencing); |
||||
#define GRPC_ERROR_CREATE(desc) \ |
||||
grpc_error_create(__FILE__, __LINE__, desc, NULL, 0) |
||||
|
||||
// Create an error that references some other errors. This function adds a
|
||||
// reference to each error in errs - it does not consume an existing reference
|
||||
#define GRPC_ERROR_CREATE_REFERENCING(desc, errs, count) \ |
||||
grpc_error_create(__FILE__, __LINE__, desc, errs, count) |
||||
|
||||
//#define GRPC_ERROR_REFCOUNT_DEBUG
|
||||
#ifdef GRPC_ERROR_REFCOUNT_DEBUG |
||||
grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line, |
||||
const char *func); |
||||
void grpc_error_unref(grpc_error *err, const char *file, int line, |
||||
const char *func); |
||||
#define GRPC_ERROR_REF(err) grpc_error_ref(err, __FILE__, __LINE__, __func__) |
||||
#define GRPC_ERROR_UNREF(err) \ |
||||
grpc_error_unref(err, __FILE__, __LINE__, __func__) |
||||
#else |
||||
grpc_error *grpc_error_ref(grpc_error *err); |
||||
void grpc_error_unref(grpc_error *err); |
||||
#define GRPC_ERROR_REF(err) grpc_error_ref(err) |
||||
#define GRPC_ERROR_UNREF(err) grpc_error_unref(err) |
||||
#endif |
||||
|
||||
grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which, |
||||
intptr_t value); |
||||
bool grpc_error_get_int(grpc_error *error, grpc_error_ints which, intptr_t *p); |
||||
grpc_error *grpc_error_set_time(grpc_error *src, grpc_error_times which, |
||||
gpr_timespec value); |
||||
grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which, |
||||
const char *value); |
||||
grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child); |
||||
grpc_error *grpc_os_error(const char *file, int line, int err, |
||||
const char *call_name); |
||||
#define GRPC_OS_ERROR(err, call_name) \ |
||||
grpc_os_error(__FILE__, __LINE__, err, call_name) |
||||
grpc_error *grpc_wsa_error(const char *file, int line, int err, |
||||
const char *call_name); |
||||
#define GRPC_WSA_ERROR(err, call_name) \ |
||||
grpc_wsa_error(__FILE__, __LINE__, err, call_name) |
||||
|
||||
bool grpc_log_if_error(const char *what, grpc_error *error, const char *file, |
||||
int line); |
||||
#define GRPC_LOG_IF_ERROR(what, error) \ |
||||
grpc_log_if_error((what), (error), __FILE__, __LINE__) |
||||
|
||||
#endif /* GRPC_CORE_LIB_IOMGR_ERROR_H */ |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue