mirror of https://github.com/grpc/grpc.git
commit
9177d84da3
2430 changed files with 2027 additions and 57650 deletions
@ -0,0 +1,286 @@ |
||||
# xDS (Load-Balancing) Interop Test Case Descriptions |
||||
|
||||
Client and server use [test.proto](../src/proto/grpc/testing/test.proto). |
||||
|
||||
## Server |
||||
|
||||
The code for the xDS test server can be found at: |
||||
[Java](https://github.com/grpc/grpc-java/blob/master/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java) (other language implementations are in progress). |
||||
|
||||
Server should accept these arguments: |
||||
|
||||
* --port=PORT |
||||
* The port the server will run on. |
||||
|
||||
## Client |
||||
|
||||
The base behavior of the xDS test client is to send a constant QPS of unary |
||||
messages and record the remote-peer distribution of the responses. Further, the |
||||
client must expose an implementation of the `LoadBalancerStatsService` gRPC |
||||
service to allow the test driver to validate the load balancing behavior for a |
||||
particular test case (see below for more details). |
||||
|
||||
The code for the xDS test client can be at: |
||||
[Java](https://github.com/grpc/grpc-java/blob/master/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestClient.java) (other language implementations are in progress). |
||||
|
||||
Clients should accept these arguments: |
||||
|
||||
* --num_channels=CHANNELS |
||||
* The number of channels to create to the server. |
||||
* --qps=QPS |
||||
* The QPS per channel. |
||||
* --server=HOSTNAME:PORT |
||||
* The server host to connect to. For example, "localhost:8080" |
||||
* --stats_port=PORT |
||||
* The port for to expose the client's `LoadBalancerStatsService` |
||||
implementation. |
||||
|
||||
## Test Driver |
||||
|
||||
Note that, unlike our other interop tests, neither the client nor the server has |
||||
any notion of which of the following test scenarios is under test. Instead, a |
||||
separate test driver is responsible for configuring the load balancer and the |
||||
server backends, running the client, and then querying the client's |
||||
`LoadBalancerStatsService` to validate load balancer behavior for each of the |
||||
tests described below. |
||||
|
||||
## LoadBalancerStatsService |
||||
|
||||
The service is defined as: |
||||
|
||||
``` |
||||
message LoadBalancerStatsRequest { |
||||
// Request stats for the next num_rpcs sent by client. |
||||
int32 num_rpcs = 1; |
||||
// If num_rpcs have not completed within timeout_sec, return partial results. |
||||
int32 timeout_sec = 2; |
||||
} |
||||
|
||||
message LoadBalancerStatsResponse { |
||||
// The number of completed RPCs for each peer. |
||||
map<string, int32> rpcs_by_peer = 1; |
||||
// The number of RPCs that failed to record a remote peer. |
||||
int32 num_failures = 2; |
||||
} |
||||
|
||||
service LoadBalancerStatsService { |
||||
// Gets the backend distribution for RPCs sent by a test client. |
||||
rpc GetClientStats(LoadBalancerStatsRequest) |
||||
returns (LoadBalancerStatsResponse) {} |
||||
} |
||||
``` |
||||
|
||||
Note that the `LoadBalancerStatsResponse` contains the remote peer distribution |
||||
of the next `num_rpcs` *sent* by the client after receiving the |
||||
`LoadBalancerStatsRequest`. It is important that the remote peer distribution be |
||||
recorded for a block of consecutive outgoing RPCs, to validate the intended |
||||
distribution from the load balancer, rather than just looking at the next |
||||
`num_rpcs` responses received from backends, as different backends may respond |
||||
at different rates. |
||||
|
||||
## Test Cases |
||||
|
||||
### ping_pong |
||||
|
||||
This test verifies that every backend receives traffic. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. 4 backends are created in a single managed instance group (MIG). |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC |
||||
|
||||
### round_robin |
||||
|
||||
This test verifies that RPCs are evenly routed according to an unweighted round |
||||
robin policy. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. 4 backends are created in a single MIG. |
||||
|
||||
Test driver asserts that: |
||||
|
||||
1. Once all backends receive at least one RPC, the following 100 RPCs are |
||||
evenly distributed across the 4 backends. |
||||
|
||||
### backends_restart |
||||
|
||||
This test verifies that the load balancer will resume sending traffic to a set |
||||
of backends that is stopped and then resumed. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. 4 backends are created in a single MIG. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC. |
||||
|
||||
The test driver records the peer distribution for a subsequent block of 100 RPCs |
||||
then stops the backends. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. No RPCs from the client are successful. |
||||
|
||||
The test driver resumes the backends. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. Once all backends receive at least one RPC, the distribution for a block of |
||||
100 RPCs is the same as the distribution recorded prior to restart. |
||||
|
||||
### secondary_locality_gets_requests_on_primary_failure |
||||
|
||||
This test verifies that backends in a secondary locality receive traffic when |
||||
all backends in the primary locality fail. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. The primary MIG with 2 backends in the same zone as the client |
||||
1. The secondary MIG with 2 backends in a different zone |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the primary locality receive at least 1 RPC. |
||||
1. No backends in the secondary locality receive RPCs. |
||||
|
||||
The test driver stops the backends in the primary locality. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the secondary locality receive at least 1 RPC. |
||||
|
||||
The test driver resumes the backends in the primary locality. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the primary locality receive at least 1 RPC. |
||||
1. No backends in the secondary locality receive RPCs. |
||||
|
||||
### secondary_locality_gets_no_requests_on_partial_primary_failure |
||||
|
||||
This test verifies that backends in a failover locality do not receive traffic |
||||
when at least one of the backends in the primary locality remain healthy. |
||||
|
||||
**Note:** Future TD features may change the expected behavior and require |
||||
changes to this test case. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. The primary MIG with 2 backends in the same zone as the client |
||||
1. The secondary MIG with 2 backends in a different zone |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the primary locality receive at least 1 RPC. |
||||
1. No backends in the secondary locality receive RPCs. |
||||
|
||||
The test driver stops one of the backends in the primary locality. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the primary locality receive at least 1 RPC. |
||||
1. No backends in the secondary locality receive RPCs. |
||||
|
||||
### new_instance_group_receives_traffic |
||||
|
||||
This test verifies that new instance groups added to a backend service in the |
||||
same zone receive traffic. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. One MIG with two backends, using rate balancing mode. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC. |
||||
|
||||
The test driver adds a new MIG with two backends in the same zone. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in each MIG receive at least one RPC. |
||||
|
||||
### remove_instance_group |
||||
|
||||
This test verifies that a remaining instance group can successfully serve RPCs |
||||
after removal of another instance group in the same zone. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. Two MIGs with two backends each, using rate balancing mode. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC. |
||||
|
||||
The test driver removes one MIG. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All RPCs are directed to the two remaining backends (no RPC failures). |
||||
|
||||
### change_backend_service |
||||
|
||||
This test verifies that the backend service can be replaced and traffic routed |
||||
to the new backends. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. One MIG with two backends |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC. |
||||
|
||||
The test driver creates a new backend service containing a MIG with two backends |
||||
and changes the TD URL map to point to this new backend service. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All RPCs are directed to the new backend service. |
||||
|
@ -0,0 +1,36 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2020 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_IMPL_CODEGEN_SYNC_ABSEIL_H |
||||
#define GRPC_IMPL_CODEGEN_SYNC_ABSEIL_H |
||||
|
||||
#include <grpc/impl/codegen/port_platform.h> |
||||
|
||||
#include <grpc/impl/codegen/sync_generic.h> |
||||
|
||||
#ifdef GPR_ABSEIL_SYNC |
||||
|
||||
typedef intptr_t gpr_mu; |
||||
typedef intptr_t gpr_cv; |
||||
typedef int32_t gpr_once; |
||||
|
||||
#define GPR_ONCE_INIT 0 |
||||
|
||||
#endif |
||||
|
||||
#endif /* GRPC_IMPL_CODEGEN_SYNC_ABSEIL_H */ |
@ -0,0 +1,26 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2020 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_SUPPORT_SYNC_ABSEIL_H |
||||
#define GRPC_SUPPORT_SYNC_ABSEIL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <grpc/impl/codegen/sync_abseil.h> |
||||
|
||||
#endif /* GRPC_SUPPORT_SYNC_ABSEIL_H */ |
@ -0,0 +1,114 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2020 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#if defined(GPR_ABSEIL_SYNC) && !defined(GPR_CUSTOM_SYNC) |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
|
||||
#include <errno.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/sync.h> |
||||
#include <grpc/support/time.h> |
||||
#include <time.h> |
||||
#include "src/core/lib/profiling/timers.h" |
||||
|
||||
#include "absl/base/call_once.h" |
||||
#include "absl/synchronization/mutex.h" |
||||
#include "absl/time/clock.h" |
||||
#include "absl/time/time.h" |
||||
|
||||
#ifdef GPR_LOW_LEVEL_COUNTERS |
||||
gpr_atm gpr_mu_locks = 0; |
||||
gpr_atm gpr_counter_atm_cas = 0; |
||||
gpr_atm gpr_counter_atm_add = 0; |
||||
#endif |
||||
|
||||
void gpr_mu_init(gpr_mu* mu) { |
||||
static_assert(sizeof(gpr_mu) == sizeof(absl::Mutex), |
||||
"gpr_mu and Mutex must be the same size"); |
||||
new (mu) absl::Mutex; |
||||
} |
||||
|
||||
void gpr_mu_destroy(gpr_mu* mu) { |
||||
reinterpret_cast<absl::Mutex*>(mu)->~Mutex(); |
||||
} |
||||
|
||||
void gpr_mu_lock(gpr_mu* mu) ABSL_NO_THREAD_SAFETY_ANALYSIS { |
||||
GPR_TIMER_SCOPE("gpr_mu_lock", 0); |
||||
reinterpret_cast<absl::Mutex*>(mu)->Lock(); |
||||
} |
||||
|
||||
void gpr_mu_unlock(gpr_mu* mu) ABSL_NO_THREAD_SAFETY_ANALYSIS { |
||||
GPR_TIMER_SCOPE("gpr_mu_unlock", 0); |
||||
reinterpret_cast<absl::Mutex*>(mu)->Unlock(); |
||||
} |
||||
|
||||
int gpr_mu_trylock(gpr_mu* mu) { |
||||
GPR_TIMER_SCOPE("gpr_mu_trylock", 0); |
||||
int ret = reinterpret_cast<absl::Mutex*>(mu)->TryLock() == true; |
||||
return ret; |
||||
} |
||||
|
||||
/*----------------------------------------*/ |
||||
|
||||
void gpr_cv_init(gpr_cv* cv) { |
||||
static_assert(sizeof(gpr_cv) == sizeof(absl::CondVar), |
||||
"gpr_cv and CondVar must be the same size"); |
||||
new (cv) absl::CondVar; |
||||
} |
||||
|
||||
void gpr_cv_destroy(gpr_cv* cv) { |
||||
reinterpret_cast<absl::CondVar*>(cv)->~CondVar(); |
||||
} |
||||
|
||||
int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) { |
||||
GPR_TIMER_SCOPE("gpr_cv_wait", 0); |
||||
if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == |
||||
0) { |
||||
reinterpret_cast<absl::CondVar*>(cv)->Wait( |
||||
reinterpret_cast<absl::Mutex*>(mu)); |
||||
return 0; |
||||
} |
||||
abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); |
||||
timespec ts = {static_cast<decltype(ts.tv_sec)>(abs_deadline.tv_sec), |
||||
static_cast<decltype(ts.tv_nsec)>(abs_deadline.tv_nsec)}; |
||||
int ret = reinterpret_cast<absl::CondVar*>(cv)->WaitWithDeadline( |
||||
reinterpret_cast<absl::Mutex*>(mu), |
||||
absl::TimeFromTimespec(ts)) == true; |
||||
return ret; |
||||
} |
||||
|
||||
void gpr_cv_signal(gpr_cv* cv) { |
||||
GPR_TIMER_MARK("gpr_cv_signal", 0); |
||||
reinterpret_cast<absl::CondVar*>(cv)->Signal(); |
||||
} |
||||
|
||||
void gpr_cv_broadcast(gpr_cv* cv) { |
||||
GPR_TIMER_MARK("gpr_cv_broadcast", 0); |
||||
reinterpret_cast<absl::CondVar*>(cv)->SignalAll(); |
||||
} |
||||
|
||||
/*----------------------------------------*/ |
||||
|
||||
void gpr_once_init(gpr_once* once, void (*init_function)(void)) { |
||||
absl::call_once(*reinterpret_cast<absl::once_flag*>(once), init_function); |
||||
} |
||||
|
||||
#endif /* defined(GPR_ABSEIL_SYNC) && !defined(GPR_CUSTOM_SYNC) */ |
@ -1,94 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <inttypes.h> |
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include "src/core/lib/json/json.h" |
||||
|
||||
grpc_json* grpc_json_create(grpc_json_type type) { |
||||
grpc_json* json = static_cast<grpc_json*>(gpr_zalloc(sizeof(*json))); |
||||
json->type = type; |
||||
|
||||
return json; |
||||
} |
||||
|
||||
void grpc_json_destroy(grpc_json* json) { |
||||
if (json == nullptr) return; |
||||
while (json->child) { |
||||
grpc_json_destroy(json->child); |
||||
} |
||||
if (json->next) { |
||||
json->next->prev = json->prev; |
||||
} |
||||
if (json->prev) { |
||||
json->prev->next = json->next; |
||||
} else if (json->parent) { |
||||
json->parent->child = json->next; |
||||
} |
||||
if (json->owns_value) { |
||||
gpr_free((void*)json->value); |
||||
} |
||||
gpr_free(json); |
||||
} |
||||
|
||||
grpc_json* grpc_json_link_child(grpc_json* parent, grpc_json* child, |
||||
grpc_json* sibling) { |
||||
// link child up to parent
|
||||
child->parent = parent; |
||||
// first child case.
|
||||
if (parent->child == nullptr) { |
||||
GPR_ASSERT(sibling == nullptr); |
||||
parent->child = child; |
||||
return child; |
||||
} |
||||
if (sibling == nullptr) { |
||||
sibling = parent->child; |
||||
} |
||||
// always find the right most sibling.
|
||||
while (sibling->next != nullptr) { |
||||
sibling = sibling->next; |
||||
} |
||||
sibling->next = child; |
||||
return child; |
||||
} |
||||
|
||||
grpc_json* grpc_json_create_child(grpc_json* sibling, grpc_json* parent, |
||||
const char* key, const char* value, |
||||
grpc_json_type type, bool owns_value) { |
||||
grpc_json* child = grpc_json_create(type); |
||||
grpc_json_link_child(parent, child, sibling); |
||||
child->owns_value = owns_value; |
||||
child->value = value; |
||||
child->key = key; |
||||
return child; |
||||
} |
||||
|
||||
grpc_json* grpc_json_add_number_string_child(grpc_json* parent, grpc_json* it, |
||||
const char* name, int64_t num) { |
||||
char* num_str; |
||||
gpr_asprintf(&num_str, "%" PRId64, num); |
||||
return grpc_json_create_child(it, parent, name, num_str, GRPC_JSON_STRING, |
||||
true); |
||||
} |
@ -1,825 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015-2016 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include "src/core/lib/json/json.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace { |
||||
|
||||
class JsonReader { |
||||
public: |
||||
static grpc_error* Parse(StringView input, Json* output); |
||||
|
||||
private: |
||||
enum class Status { |
||||
GRPC_JSON_DONE, /* The parser finished successfully. */ |
||||
GRPC_JSON_PARSE_ERROR, /* The parser found an error in the json stream. */ |
||||
GRPC_JSON_INTERNAL_ERROR /* The parser got an internal error. */ |
||||
}; |
||||
|
||||
enum class State { |
||||
GRPC_JSON_STATE_OBJECT_KEY_BEGIN, |
||||
GRPC_JSON_STATE_OBJECT_KEY_STRING, |
||||
GRPC_JSON_STATE_OBJECT_KEY_END, |
||||
GRPC_JSON_STATE_VALUE_BEGIN, |
||||
GRPC_JSON_STATE_VALUE_STRING, |
||||
GRPC_JSON_STATE_STRING_ESCAPE, |
||||
GRPC_JSON_STATE_STRING_ESCAPE_U1, |
||||
GRPC_JSON_STATE_STRING_ESCAPE_U2, |
||||
GRPC_JSON_STATE_STRING_ESCAPE_U3, |
||||
GRPC_JSON_STATE_STRING_ESCAPE_U4, |
||||
GRPC_JSON_STATE_VALUE_NUMBER, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_ZERO, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_DOT, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_E, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_EPM, |
||||
GRPC_JSON_STATE_VALUE_TRUE_R, |
||||
GRPC_JSON_STATE_VALUE_TRUE_U, |
||||
GRPC_JSON_STATE_VALUE_TRUE_E, |
||||
GRPC_JSON_STATE_VALUE_FALSE_A, |
||||
GRPC_JSON_STATE_VALUE_FALSE_L, |
||||
GRPC_JSON_STATE_VALUE_FALSE_S, |
||||
GRPC_JSON_STATE_VALUE_FALSE_E, |
||||
GRPC_JSON_STATE_VALUE_NULL_U, |
||||
GRPC_JSON_STATE_VALUE_NULL_L1, |
||||
GRPC_JSON_STATE_VALUE_NULL_L2, |
||||
GRPC_JSON_STATE_VALUE_END, |
||||
GRPC_JSON_STATE_END |
||||
}; |
||||
|
||||
/* The first non-unicode value is 0x110000. But let's pick
|
||||
* a value high enough to start our error codes from. These |
||||
* values are safe to return from the read_char function. |
||||
*/ |
||||
static constexpr uint32_t GRPC_JSON_READ_CHAR_EOF = 0x7ffffff0; |
||||
|
||||
explicit JsonReader(StringView input) |
||||
: original_input_(reinterpret_cast<const uint8_t*>(input.data())), |
||||
input_(original_input_), |
||||
remaining_input_(input.size()) {} |
||||
|
||||
Status Run(); |
||||
uint32_t ReadChar(); |
||||
bool IsComplete(); |
||||
|
||||
size_t CurrentIndex() const { return input_ - original_input_ - 1; } |
||||
|
||||
void StringAddChar(uint32_t c); |
||||
void StringAddUtf32(uint32_t c); |
||||
|
||||
Json* CreateAndLinkValue(); |
||||
void StartContainer(Json::Type type); |
||||
void EndContainer(); |
||||
void SetKey(); |
||||
void SetString(); |
||||
bool SetNumber(); |
||||
void SetTrue(); |
||||
void SetFalse(); |
||||
void SetNull(); |
||||
|
||||
const uint8_t* original_input_; |
||||
const uint8_t* input_; |
||||
size_t remaining_input_; |
||||
|
||||
State state_ = State::GRPC_JSON_STATE_VALUE_BEGIN; |
||||
bool escaped_string_was_key_ = false; |
||||
bool container_just_begun_ = false; |
||||
uint16_t unicode_char_ = 0; |
||||
uint16_t unicode_high_surrogate_ = 0; |
||||
std::vector<grpc_error*> errors_; |
||||
|
||||
Json root_value_; |
||||
std::vector<Json*> stack_; |
||||
|
||||
std::string key_; |
||||
std::string string_; |
||||
}; |
||||
|
||||
void JsonReader::StringAddChar(uint32_t c) { |
||||
string_.push_back(static_cast<uint8_t>(c)); |
||||
} |
||||
|
||||
void JsonReader::StringAddUtf32(uint32_t c) { |
||||
if (c <= 0x7f) { |
||||
StringAddChar(c); |
||||
} else if (c <= 0x7ff) { |
||||
uint32_t b1 = 0xc0 | ((c >> 6) & 0x1f); |
||||
uint32_t b2 = 0x80 | (c & 0x3f); |
||||
StringAddChar(b1); |
||||
StringAddChar(b2); |
||||
} else if (c <= 0xffff) { |
||||
uint32_t b1 = 0xe0 | ((c >> 12) & 0x0f); |
||||
uint32_t b2 = 0x80 | ((c >> 6) & 0x3f); |
||||
uint32_t b3 = 0x80 | (c & 0x3f); |
||||
StringAddChar(b1); |
||||
StringAddChar(b2); |
||||
StringAddChar(b3); |
||||
} else if (c <= 0x1fffff) { |
||||
uint32_t b1 = 0xf0 | ((c >> 18) & 0x07); |
||||
uint32_t b2 = 0x80 | ((c >> 12) & 0x3f); |
||||
uint32_t b3 = 0x80 | ((c >> 6) & 0x3f); |
||||
uint32_t b4 = 0x80 | (c & 0x3f); |
||||
StringAddChar(b1); |
||||
StringAddChar(b2); |
||||
StringAddChar(b3); |
||||
StringAddChar(b4); |
||||
} |
||||
} |
||||
|
||||
uint32_t JsonReader::ReadChar() { |
||||
if (remaining_input_ == 0) return GRPC_JSON_READ_CHAR_EOF; |
||||
const uint32_t r = *input_++; |
||||
--remaining_input_; |
||||
if (r == 0) { |
||||
remaining_input_ = 0; |
||||
return GRPC_JSON_READ_CHAR_EOF; |
||||
} |
||||
return r; |
||||
} |
||||
|
||||
Json* JsonReader::CreateAndLinkValue() { |
||||
Json* value; |
||||
if (stack_.empty()) { |
||||
value = &root_value_; |
||||
} else { |
||||
Json* parent = stack_.back(); |
||||
if (parent->type() == Json::Type::OBJECT) { |
||||
if (parent->object_value().find(key_) != parent->object_value().end()) { |
||||
char* msg; |
||||
gpr_asprintf(&msg, "duplicate key \"%s\" at index %" PRIuPTR, |
||||
key_.c_str(), CurrentIndex()); |
||||
errors_.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg)); |
||||
gpr_free(msg); |
||||
} |
||||
value = &(*parent->mutable_object())[std::move(key_)]; |
||||
} else { |
||||
GPR_ASSERT(parent->type() == Json::Type::ARRAY); |
||||
parent->mutable_array()->emplace_back(); |
||||
value = &parent->mutable_array()->back(); |
||||
} |
||||
} |
||||
return value; |
||||
} |
||||
|
||||
void JsonReader::StartContainer(Json::Type type) { |
||||
Json* value = CreateAndLinkValue(); |
||||
if (type == Json::Type::OBJECT) { |
||||
*value = Json::Object(); |
||||
} else { |
||||
GPR_ASSERT(type == Json::Type::ARRAY); |
||||
*value = Json::Array(); |
||||
} |
||||
stack_.push_back(value); |
||||
} |
||||
|
||||
void JsonReader::EndContainer() { |
||||
GPR_ASSERT(!stack_.empty()); |
||||
stack_.pop_back(); |
||||
} |
||||
|
||||
void JsonReader::SetKey() { |
||||
key_ = std::move(string_); |
||||
string_.clear(); |
||||
} |
||||
|
||||
void JsonReader::SetString() { |
||||
Json* value = CreateAndLinkValue(); |
||||
*value = std::move(string_); |
||||
string_.clear(); |
||||
} |
||||
|
||||
bool JsonReader::SetNumber() { |
||||
Json* value = CreateAndLinkValue(); |
||||
*value = Json(std::move(string_), /*is_number=*/true); |
||||
string_.clear(); |
||||
return true; |
||||
} |
||||
|
||||
void JsonReader::SetTrue() { |
||||
Json* value = CreateAndLinkValue(); |
||||
*value = true; |
||||
string_.clear(); |
||||
} |
||||
|
||||
void JsonReader::SetFalse() { |
||||
Json* value = CreateAndLinkValue(); |
||||
*value = false; |
||||
string_.clear(); |
||||
} |
||||
|
||||
void JsonReader::SetNull() { CreateAndLinkValue(); } |
||||
|
||||
bool JsonReader::IsComplete() { |
||||
return (stack_.empty() && (state_ == State::GRPC_JSON_STATE_END || |
||||
state_ == State::GRPC_JSON_STATE_VALUE_END)); |
||||
} |
||||
|
||||
/* Call this function to start parsing the input. It will return the following:
|
||||
* . GRPC_JSON_DONE if the input got eof, and the parsing finished |
||||
* successfully. |
||||
* . GRPC_JSON_PARSE_ERROR if the input was somehow invalid. |
||||
* . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid |
||||
* internal state. |
||||
*/ |
||||
JsonReader::Status JsonReader::Run() { |
||||
uint32_t c; |
||||
|
||||
/* This state-machine is a strict implementation of ECMA-404 */ |
||||
while (true) { |
||||
c = ReadChar(); |
||||
switch (c) { |
||||
/* Let's process the error case first. */ |
||||
case GRPC_JSON_READ_CHAR_EOF: |
||||
if (IsComplete()) { |
||||
return Status::GRPC_JSON_DONE; |
||||
} else { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
/* Processing whitespaces. */ |
||||
case ' ': |
||||
case '\t': |
||||
case '\n': |
||||
case '\r': |
||||
switch (state_) { |
||||
case State::GRPC_JSON_STATE_OBJECT_KEY_BEGIN: |
||||
case State::GRPC_JSON_STATE_OBJECT_KEY_END: |
||||
case State::GRPC_JSON_STATE_VALUE_BEGIN: |
||||
case State::GRPC_JSON_STATE_VALUE_END: |
||||
case State::GRPC_JSON_STATE_END: |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_OBJECT_KEY_STRING: |
||||
case State::GRPC_JSON_STATE_VALUE_STRING: |
||||
if (c != ' ') return Status::GRPC_JSON_PARSE_ERROR; |
||||
if (unicode_high_surrogate_ != 0) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
StringAddChar(c); |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER: |
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: |
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_ZERO: |
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_EPM: |
||||
if (!SetNumber()) return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_END; |
||||
break; |
||||
|
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
/* Value, object or array terminations. */ |
||||
case ',': |
||||
case '}': |
||||
case ']': |
||||
switch (state_) { |
||||
case State::GRPC_JSON_STATE_OBJECT_KEY_STRING: |
||||
case State::GRPC_JSON_STATE_VALUE_STRING: |
||||
if (unicode_high_surrogate_ != 0) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
StringAddChar(c); |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER: |
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: |
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_ZERO: |
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_EPM: |
||||
if (stack_.empty()) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} else if (c == '}' && |
||||
stack_.back()->type() != Json::Type::OBJECT) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} else if (c == ']' && stack_.back()->type() != Json::Type::ARRAY) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if (!SetNumber()) return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_END; |
||||
/* The missing break here is intentional. */ |
||||
/* fallthrough */ |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_END: |
||||
case State::GRPC_JSON_STATE_OBJECT_KEY_BEGIN: |
||||
case State::GRPC_JSON_STATE_VALUE_BEGIN: |
||||
if (c == ',') { |
||||
if (state_ != State::GRPC_JSON_STATE_VALUE_END) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if (!stack_.empty() && |
||||
stack_.back()->type() == Json::Type::OBJECT) { |
||||
state_ = State::GRPC_JSON_STATE_OBJECT_KEY_BEGIN; |
||||
} else if (!stack_.empty() && |
||||
stack_.back()->type() == Json::Type::ARRAY) { |
||||
state_ = State::GRPC_JSON_STATE_VALUE_BEGIN; |
||||
} else { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
} else { |
||||
if (stack_.empty()) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if (c == '}' && stack_.back()->type() != Json::Type::OBJECT) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if (c == '}' && |
||||
state_ == State::GRPC_JSON_STATE_OBJECT_KEY_BEGIN && |
||||
!container_just_begun_) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if (c == ']' && stack_.back()->type() != Json::Type::ARRAY) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if (c == ']' && state_ == State::GRPC_JSON_STATE_VALUE_BEGIN && |
||||
!container_just_begun_) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
state_ = State::GRPC_JSON_STATE_VALUE_END; |
||||
EndContainer(); |
||||
if (stack_.empty()) { |
||||
state_ = State::GRPC_JSON_STATE_END; |
||||
} |
||||
} |
||||
break; |
||||
|
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
/* In-string escaping. */ |
||||
case '\\': |
||||
switch (state_) { |
||||
case State::GRPC_JSON_STATE_OBJECT_KEY_STRING: |
||||
escaped_string_was_key_ = true; |
||||
state_ = State::GRPC_JSON_STATE_STRING_ESCAPE; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_STRING: |
||||
escaped_string_was_key_ = false; |
||||
state_ = State::GRPC_JSON_STATE_STRING_ESCAPE; |
||||
break; |
||||
|
||||
/* This is the \\ case. */ |
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE: |
||||
if (unicode_high_surrogate_ != 0) |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
StringAddChar('\\'); |
||||
if (escaped_string_was_key_) { |
||||
state_ = State::GRPC_JSON_STATE_OBJECT_KEY_STRING; |
||||
} else { |
||||
state_ = State::GRPC_JSON_STATE_VALUE_STRING; |
||||
} |
||||
break; |
||||
|
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
default: |
||||
container_just_begun_ = false; |
||||
switch (state_) { |
||||
case State::GRPC_JSON_STATE_OBJECT_KEY_BEGIN: |
||||
if (c != '"') return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_OBJECT_KEY_STRING; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_OBJECT_KEY_STRING: |
||||
if (unicode_high_surrogate_ != 0) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if (c == '"') { |
||||
state_ = State::GRPC_JSON_STATE_OBJECT_KEY_END; |
||||
SetKey(); |
||||
} else { |
||||
if (c < 32) return Status::GRPC_JSON_PARSE_ERROR; |
||||
StringAddChar(c); |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_STRING: |
||||
if (unicode_high_surrogate_ != 0) { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if (c == '"') { |
||||
state_ = State::GRPC_JSON_STATE_VALUE_END; |
||||
SetString(); |
||||
} else { |
||||
if (c < 32) return Status::GRPC_JSON_PARSE_ERROR; |
||||
StringAddChar(c); |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_OBJECT_KEY_END: |
||||
if (c != ':') return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_BEGIN; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_BEGIN: |
||||
switch (c) { |
||||
case 't': |
||||
state_ = State::GRPC_JSON_STATE_VALUE_TRUE_R; |
||||
break; |
||||
|
||||
case 'f': |
||||
state_ = State::GRPC_JSON_STATE_VALUE_FALSE_A; |
||||
break; |
||||
|
||||
case 'n': |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NULL_U; |
||||
break; |
||||
|
||||
case '"': |
||||
state_ = State::GRPC_JSON_STATE_VALUE_STRING; |
||||
break; |
||||
|
||||
case '0': |
||||
StringAddChar(c); |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER_ZERO; |
||||
break; |
||||
|
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
case '-': |
||||
StringAddChar(c); |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER; |
||||
break; |
||||
|
||||
case '{': |
||||
container_just_begun_ = true; |
||||
StartContainer(Json::Type::OBJECT); |
||||
state_ = State::GRPC_JSON_STATE_OBJECT_KEY_BEGIN; |
||||
break; |
||||
|
||||
case '[': |
||||
container_just_begun_ = true; |
||||
StartContainer(Json::Type::ARRAY); |
||||
break; |
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE: |
||||
if (escaped_string_was_key_) { |
||||
state_ = State::GRPC_JSON_STATE_OBJECT_KEY_STRING; |
||||
} else { |
||||
state_ = State::GRPC_JSON_STATE_VALUE_STRING; |
||||
} |
||||
if (unicode_high_surrogate_ && c != 'u') { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
switch (c) { |
||||
case '"': |
||||
case '/': |
||||
StringAddChar(c); |
||||
break; |
||||
case 'b': |
||||
StringAddChar('\b'); |
||||
break; |
||||
case 'f': |
||||
StringAddChar('\f'); |
||||
break; |
||||
case 'n': |
||||
StringAddChar('\n'); |
||||
break; |
||||
case 'r': |
||||
StringAddChar('\r'); |
||||
break; |
||||
case 't': |
||||
StringAddChar('\t'); |
||||
break; |
||||
case 'u': |
||||
state_ = State::GRPC_JSON_STATE_STRING_ESCAPE_U1; |
||||
unicode_char_ = 0; |
||||
break; |
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE_U1: |
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE_U2: |
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE_U3: |
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE_U4: |
||||
if ((c >= '0') && (c <= '9')) { |
||||
c -= '0'; |
||||
} else if ((c >= 'A') && (c <= 'F')) { |
||||
c -= 'A' - 10; |
||||
} else if ((c >= 'a') && (c <= 'f')) { |
||||
c -= 'a' - 10; |
||||
} else { |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
unicode_char_ = static_cast<uint16_t>(unicode_char_ << 4); |
||||
unicode_char_ = static_cast<uint16_t>(unicode_char_ | c); |
||||
|
||||
switch (state_) { |
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE_U1: |
||||
state_ = State::GRPC_JSON_STATE_STRING_ESCAPE_U2; |
||||
break; |
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE_U2: |
||||
state_ = State::GRPC_JSON_STATE_STRING_ESCAPE_U3; |
||||
break; |
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE_U3: |
||||
state_ = State::GRPC_JSON_STATE_STRING_ESCAPE_U4; |
||||
break; |
||||
case State::GRPC_JSON_STATE_STRING_ESCAPE_U4: |
||||
/* See grpc_json_writer_escape_string to have a description
|
||||
* of what's going on here. |
||||
*/ |
||||
if ((unicode_char_ & 0xfc00) == 0xd800) { |
||||
/* high surrogate utf-16 */ |
||||
if (unicode_high_surrogate_ != 0) |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
unicode_high_surrogate_ = unicode_char_; |
||||
} else if ((unicode_char_ & 0xfc00) == 0xdc00) { |
||||
/* low surrogate utf-16 */ |
||||
uint32_t utf32; |
||||
if (unicode_high_surrogate_ == 0) |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
utf32 = 0x10000; |
||||
utf32 += static_cast<uint32_t>( |
||||
(unicode_high_surrogate_ - 0xd800) * 0x400); |
||||
utf32 += static_cast<uint32_t>(unicode_char_ - 0xdc00); |
||||
StringAddUtf32(utf32); |
||||
unicode_high_surrogate_ = 0; |
||||
} else { |
||||
/* anything else */ |
||||
if (unicode_high_surrogate_ != 0) |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
StringAddUtf32(unicode_char_); |
||||
} |
||||
if (escaped_string_was_key_) { |
||||
state_ = State::GRPC_JSON_STATE_OBJECT_KEY_STRING; |
||||
} else { |
||||
state_ = State::GRPC_JSON_STATE_VALUE_STRING; |
||||
} |
||||
break; |
||||
default: |
||||
GPR_UNREACHABLE_CODE(return Status::GRPC_JSON_INTERNAL_ERROR); |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER: |
||||
StringAddChar(c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
break; |
||||
case 'e': |
||||
case 'E': |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER_E; |
||||
break; |
||||
case '.': |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER_DOT; |
||||
break; |
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: |
||||
StringAddChar(c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
break; |
||||
case 'e': |
||||
case 'E': |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER_E; |
||||
break; |
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_ZERO: |
||||
if (c != '.') return Status::GRPC_JSON_PARSE_ERROR; |
||||
StringAddChar(c); |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER_DOT; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_DOT: |
||||
StringAddChar(c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL; |
||||
break; |
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_E: |
||||
StringAddChar(c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
case '+': |
||||
case '-': |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER_EPM; |
||||
break; |
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NUMBER_EPM: |
||||
StringAddChar(c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
break; |
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_TRUE_R: |
||||
if (c != 'r') return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_TRUE_U; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_TRUE_U: |
||||
if (c != 'u') return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_TRUE_E; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_TRUE_E: |
||||
if (c != 'e') return Status::GRPC_JSON_PARSE_ERROR; |
||||
SetTrue(); |
||||
state_ = State::GRPC_JSON_STATE_VALUE_END; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_FALSE_A: |
||||
if (c != 'a') return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_FALSE_L; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_FALSE_L: |
||||
if (c != 'l') return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_FALSE_S; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_FALSE_S: |
||||
if (c != 's') return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_FALSE_E; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_FALSE_E: |
||||
if (c != 'e') return Status::GRPC_JSON_PARSE_ERROR; |
||||
SetFalse(); |
||||
state_ = State::GRPC_JSON_STATE_VALUE_END; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NULL_U: |
||||
if (c != 'u') return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NULL_L1; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NULL_L1: |
||||
if (c != 'l') return Status::GRPC_JSON_PARSE_ERROR; |
||||
state_ = State::GRPC_JSON_STATE_VALUE_NULL_L2; |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_VALUE_NULL_L2: |
||||
if (c != 'l') return Status::GRPC_JSON_PARSE_ERROR; |
||||
SetNull(); |
||||
state_ = State::GRPC_JSON_STATE_VALUE_END; |
||||
break; |
||||
|
||||
/* All of the VALUE_END cases are handled in the specialized case
|
||||
* above. */ |
||||
case State::GRPC_JSON_STATE_VALUE_END: |
||||
switch (c) { |
||||
case ',': |
||||
case '}': |
||||
case ']': |
||||
GPR_UNREACHABLE_CODE(return Status::GRPC_JSON_INTERNAL_ERROR); |
||||
break; |
||||
|
||||
default: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case State::GRPC_JSON_STATE_END: |
||||
return Status::GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
} |
||||
} |
||||
|
||||
GPR_UNREACHABLE_CODE(return Status::GRPC_JSON_INTERNAL_ERROR); |
||||
} |
||||
|
||||
grpc_error* JsonReader::Parse(StringView input, Json* output) { |
||||
JsonReader reader(input); |
||||
Status status = reader.Run(); |
||||
if (status == Status::GRPC_JSON_INTERNAL_ERROR) { |
||||
char* msg; |
||||
gpr_asprintf(&msg, "internal error in JSON parser at index %" PRIuPTR, |
||||
reader.CurrentIndex()); |
||||
reader.errors_.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg)); |
||||
gpr_free(msg); |
||||
} else if (status == Status::GRPC_JSON_PARSE_ERROR) { |
||||
char* msg; |
||||
gpr_asprintf(&msg, "JSON parse error at index %" PRIuPTR, |
||||
reader.CurrentIndex()); |
||||
reader.errors_.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg)); |
||||
gpr_free(msg); |
||||
} |
||||
if (!reader.errors_.empty()) { |
||||
return GRPC_ERROR_CREATE_FROM_VECTOR("JSON parsing failed", |
||||
&reader.errors_); |
||||
} |
||||
*output = std::move(reader.root_value_); |
||||
return GRPC_ERROR_NONE; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
Json Json::Parse(StringView json_str, grpc_error** error) { |
||||
Json value; |
||||
*error = JsonReader::Parse(json_str, &value); |
||||
return value; |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -1,336 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
#include "src/core/lib/json/json.h" |
||||
|
||||
#include "src/core/lib/gprpp/string_view.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace { |
||||
|
||||
/* The idea of the writer is basically symmetrical of the reader. While the
|
||||
* reader emits various calls to your code, the writer takes basically the |
||||
* same calls and emit json out of it. It doesn't try to make any check on |
||||
* the order of the calls you do on it. Meaning you can theorically force |
||||
* it to generate invalid json. |
||||
* |
||||
* Also, unlike the reader, the writer expects UTF-8 encoded input strings. |
||||
* These strings will be UTF-8 validated, and any invalid character will |
||||
* cut the conversion short, before any invalid UTF-8 sequence, thus forming |
||||
* a valid UTF-8 string overall. |
||||
*/ |
||||
class JsonWriter { |
||||
public: |
||||
static std::string Dump(const Json& value, int indent); |
||||
|
||||
private: |
||||
explicit JsonWriter(int indent) : indent_(indent) {} |
||||
|
||||
void OutputCheck(size_t needed); |
||||
void OutputChar(char c); |
||||
void OutputString(const StringView str); |
||||
void OutputIndent(); |
||||
void ValueEnd(); |
||||
void EscapeUtf16(uint16_t utf16); |
||||
void EscapeString(const std::string& string); |
||||
void ContainerBegins(Json::Type type); |
||||
void ContainerEnds(Json::Type type); |
||||
void ObjectKey(const std::string& string); |
||||
void ValueRaw(const std::string& string); |
||||
void ValueString(const std::string& string); |
||||
|
||||
void DumpObject(const Json::Object& object); |
||||
void DumpArray(const Json::Array& array); |
||||
void DumpValue(const Json& value); |
||||
|
||||
int indent_; |
||||
int depth_ = 0; |
||||
bool container_empty_ = true; |
||||
bool got_key_ = false; |
||||
std::string output_; |
||||
}; |
||||
|
||||
/* This function checks if there's enough space left in the output buffer,
|
||||
* and will enlarge it if necessary. We're only allocating chunks of 256 |
||||
* bytes at a time (or multiples thereof). |
||||
*/ |
||||
void JsonWriter::OutputCheck(size_t needed) { |
||||
size_t free_space = output_.capacity() - output_.size(); |
||||
if (free_space >= needed) return; |
||||
needed -= free_space; |
||||
/* Round up by 256 bytes. */ |
||||
needed = (needed + 0xff) & ~0xffU; |
||||
output_.reserve(output_.capacity() + needed); |
||||
} |
||||
|
||||
void JsonWriter::OutputChar(char c) { |
||||
OutputCheck(1); |
||||
output_.push_back(c); |
||||
} |
||||
|
||||
void JsonWriter::OutputString(const StringView str) { |
||||
OutputCheck(str.size()); |
||||
output_.append(str.data(), str.size()); |
||||
} |
||||
|
||||
void JsonWriter::OutputIndent() { |
||||
static const char spacesstr[] = |
||||
" " |
||||
" " |
||||
" " |
||||
" "; |
||||
unsigned spaces = static_cast<unsigned>(depth_ * indent_); |
||||
if (indent_ == 0) return; |
||||
if (got_key_) { |
||||
OutputChar(' '); |
||||
return; |
||||
} |
||||
while (spaces >= (sizeof(spacesstr) - 1)) { |
||||
OutputString(StringView(spacesstr, sizeof(spacesstr) - 1)); |
||||
spaces -= static_cast<unsigned>(sizeof(spacesstr) - 1); |
||||
} |
||||
if (spaces == 0) return; |
||||
OutputString(StringView(spacesstr + sizeof(spacesstr) - 1 - spaces, spaces)); |
||||
} |
||||
|
||||
void JsonWriter::ValueEnd() { |
||||
if (container_empty_) { |
||||
container_empty_ = false; |
||||
if (indent_ == 0 || depth_ == 0) return; |
||||
OutputChar('\n'); |
||||
} else { |
||||
OutputChar(','); |
||||
if (indent_ == 0) return; |
||||
OutputChar('\n'); |
||||
} |
||||
} |
||||
|
||||
void JsonWriter::EscapeUtf16(uint16_t utf16) { |
||||
static const char hex[] = "0123456789abcdef"; |
||||
OutputString(StringView("\\u", 2)); |
||||
OutputChar(hex[(utf16 >> 12) & 0x0f]); |
||||
OutputChar(hex[(utf16 >> 8) & 0x0f]); |
||||
OutputChar(hex[(utf16 >> 4) & 0x0f]); |
||||
OutputChar(hex[(utf16)&0x0f]); |
||||
} |
||||
|
||||
void JsonWriter::EscapeString(const std::string& string) { |
||||
OutputChar('"'); |
||||
for (size_t idx = 0; idx < string.size(); ++idx) { |
||||
uint8_t c = static_cast<uint8_t>(string[idx]); |
||||
if (c == 0) { |
||||
break; |
||||
} else if (c >= 32 && c <= 126) { |
||||
if (c == '\\' || c == '"') OutputChar('\\'); |
||||
OutputChar(static_cast<char>(c)); |
||||
} else if (c < 32 || c == 127) { |
||||
switch (c) { |
||||
case '\b': |
||||
OutputString(StringView("\\b", 2)); |
||||
break; |
||||
case '\f': |
||||
OutputString(StringView("\\f", 2)); |
||||
break; |
||||
case '\n': |
||||
OutputString(StringView("\\n", 2)); |
||||
break; |
||||
case '\r': |
||||
OutputString(StringView("\\r", 2)); |
||||
break; |
||||
case '\t': |
||||
OutputString(StringView("\\t", 2)); |
||||
break; |
||||
default: |
||||
EscapeUtf16(c); |
||||
break; |
||||
} |
||||
} else { |
||||
uint32_t utf32 = 0; |
||||
int extra = 0; |
||||
int i; |
||||
int valid = 1; |
||||
if ((c & 0xe0) == 0xc0) { |
||||
utf32 = c & 0x1f; |
||||
extra = 1; |
||||
} else if ((c & 0xf0) == 0xe0) { |
||||
utf32 = c & 0x0f; |
||||
extra = 2; |
||||
} else if ((c & 0xf8) == 0xf0) { |
||||
utf32 = c & 0x07; |
||||
extra = 3; |
||||
} else { |
||||
break; |
||||
} |
||||
for (i = 0; i < extra; i++) { |
||||
utf32 <<= 6; |
||||
++idx; |
||||
/* Breaks out and bail if we hit the end of the string. */ |
||||
if (idx == string.size()) { |
||||
valid = 0; |
||||
break; |
||||
} |
||||
c = static_cast<uint8_t>(string[idx]); |
||||
/* Breaks out and bail on any invalid UTF-8 sequence, including \0. */ |
||||
if ((c & 0xc0) != 0x80) { |
||||
valid = 0; |
||||
break; |
||||
} |
||||
utf32 |= c & 0x3f; |
||||
} |
||||
if (!valid) break; |
||||
/* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam.
|
||||
* Any other range is technically reserved for future usage, so if we |
||||
* don't want the software to break in the future, we have to allow |
||||
* anything else. The first non-unicode character is 0x110000. */ |
||||
if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) || (utf32 >= 0x110000)) |
||||
break; |
||||
if (utf32 >= 0x10000) { |
||||
/* If utf32 contains a character that is above 0xffff, it needs to be
|
||||
* broken down into a utf-16 surrogate pair. A surrogate pair is first |
||||
* a high surrogate, followed by a low surrogate. Each surrogate holds |
||||
* 10 bits of usable data, thus allowing a total of 20 bits of data. |
||||
* The high surrogate marker is 0xd800, while the low surrogate marker |
||||
* is 0xdc00. The low 10 bits of each will be the usable data. |
||||
* |
||||
* After re-combining the 20 bits of data, one has to add 0x10000 to |
||||
* the resulting value, in order to obtain the original character. |
||||
* This is obviously because the range 0x0000 - 0xffff can be written |
||||
* without any special trick. |
||||
* |
||||
* Since 0x10ffff is the highest allowed character, we're working in |
||||
* the range 0x00000 - 0xfffff after we decrement it by 0x10000. |
||||
* That range is exactly 20 bits. |
||||
*/ |
||||
utf32 -= 0x10000; |
||||
EscapeUtf16(static_cast<uint16_t>(0xd800 | (utf32 >> 10))); |
||||
EscapeUtf16(static_cast<uint16_t>(0xdc00 | (utf32 & 0x3ff))); |
||||
} else { |
||||
EscapeUtf16(static_cast<uint16_t>(utf32)); |
||||
} |
||||
} |
||||
} |
||||
OutputChar('"'); |
||||
} |
||||
|
||||
void JsonWriter::ContainerBegins(Json::Type type) { |
||||
if (!got_key_) ValueEnd(); |
||||
OutputIndent(); |
||||
OutputChar(type == Json::Type::OBJECT ? '{' : '['); |
||||
container_empty_ = true; |
||||
got_key_ = false; |
||||
depth_++; |
||||
} |
||||
|
||||
void JsonWriter::ContainerEnds(Json::Type type) { |
||||
if (indent_ && !container_empty_) OutputChar('\n'); |
||||
depth_--; |
||||
if (!container_empty_) OutputIndent(); |
||||
OutputChar(type == Json::Type::OBJECT ? '}' : ']'); |
||||
container_empty_ = false; |
||||
got_key_ = false; |
||||
} |
||||
|
||||
void JsonWriter::ObjectKey(const std::string& string) { |
||||
ValueEnd(); |
||||
OutputIndent(); |
||||
EscapeString(string); |
||||
OutputChar(':'); |
||||
got_key_ = true; |
||||
} |
||||
|
||||
void JsonWriter::ValueRaw(const std::string& string) { |
||||
if (!got_key_) ValueEnd(); |
||||
OutputIndent(); |
||||
OutputString(string); |
||||
got_key_ = false; |
||||
} |
||||
|
||||
void JsonWriter::ValueString(const std::string& string) { |
||||
if (!got_key_) ValueEnd(); |
||||
OutputIndent(); |
||||
EscapeString(string); |
||||
got_key_ = false; |
||||
} |
||||
|
||||
void JsonWriter::DumpObject(const Json::Object& object) { |
||||
ContainerBegins(Json::Type::OBJECT); |
||||
for (const auto& p : object) { |
||||
ObjectKey(p.first.data()); |
||||
DumpValue(p.second); |
||||
} |
||||
ContainerEnds(Json::Type::OBJECT); |
||||
} |
||||
|
||||
void JsonWriter::DumpArray(const Json::Array& array) { |
||||
ContainerBegins(Json::Type::ARRAY); |
||||
for (const auto& v : array) { |
||||
DumpValue(v); |
||||
} |
||||
ContainerEnds(Json::Type::ARRAY); |
||||
} |
||||
|
||||
void JsonWriter::DumpValue(const Json& value) { |
||||
switch (value.type()) { |
||||
case Json::Type::OBJECT: |
||||
DumpObject(value.object_value()); |
||||
break; |
||||
case Json::Type::ARRAY: |
||||
DumpArray(value.array_value()); |
||||
break; |
||||
case Json::Type::STRING: |
||||
ValueString(value.string_value()); |
||||
break; |
||||
case Json::Type::NUMBER: |
||||
ValueRaw(value.string_value()); |
||||
break; |
||||
case Json::Type::JSON_TRUE: |
||||
ValueRaw(std::string("true", 4)); |
||||
break; |
||||
case Json::Type::JSON_FALSE: |
||||
ValueRaw(std::string("false", 5)); |
||||
break; |
||||
case Json::Type::JSON_NULL: |
||||
ValueRaw(std::string("null", 4)); |
||||
break; |
||||
default: |
||||
GPR_UNREACHABLE_CODE(abort()); |
||||
} |
||||
} |
||||
|
||||
std::string JsonWriter::Dump(const Json& value, int indent) { |
||||
JsonWriter writer(indent); |
||||
writer.DumpValue(value); |
||||
return std::move(writer.output_); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
std::string Json::Dump(int indent) const { |
||||
return JsonWriter::Dump(*this, indent); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -1,106 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <functional> |
||||
|
||||
#include <grpcpp/generic/generic_stub.h> |
||||
#include <grpcpp/impl/rpc_method.h> |
||||
#include <grpcpp/support/client_callback.h> |
||||
|
||||
namespace grpc_impl { |
||||
|
||||
namespace { |
||||
std::unique_ptr<grpc::GenericClientAsyncReaderWriter> CallInternal( |
||||
grpc::ChannelInterface* channel, grpc::ClientContext* context, |
||||
const grpc::string& method, CompletionQueue* cq, bool start, void* tag) { |
||||
return std::unique_ptr<grpc::GenericClientAsyncReaderWriter>( |
||||
internal::ClientAsyncReaderWriterFactory<grpc::ByteBuffer, |
||||
grpc::ByteBuffer>:: |
||||
Create(channel, cq, |
||||
grpc::internal::RpcMethod( |
||||
method.c_str(), grpc::internal::RpcMethod::BIDI_STREAMING), |
||||
context, start, tag)); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
// begin a call to a named method
|
||||
std::unique_ptr<grpc::GenericClientAsyncReaderWriter> GenericStub::Call( |
||||
grpc::ClientContext* context, const grpc::string& method, |
||||
CompletionQueue* cq, void* tag) { |
||||
return CallInternal(channel_.get(), context, method, cq, true, tag); |
||||
} |
||||
|
||||
// setup a call to a named method
|
||||
std::unique_ptr<grpc::GenericClientAsyncReaderWriter> GenericStub::PrepareCall( |
||||
grpc::ClientContext* context, const grpc::string& method, |
||||
CompletionQueue* cq) { |
||||
return CallInternal(channel_.get(), context, method, cq, false, nullptr); |
||||
} |
||||
|
||||
// setup a unary call to a named method
|
||||
std::unique_ptr<grpc::GenericClientAsyncResponseReader> |
||||
GenericStub::PrepareUnaryCall(grpc::ClientContext* context, |
||||
const grpc::string& method, |
||||
const grpc::ByteBuffer& request, |
||||
CompletionQueue* cq) { |
||||
return std::unique_ptr<grpc::GenericClientAsyncResponseReader>( |
||||
internal::ClientAsyncResponseReaderFactory<grpc::ByteBuffer>::Create( |
||||
channel_.get(), cq, |
||||
grpc::internal::RpcMethod(method.c_str(), |
||||
grpc::internal::RpcMethod::NORMAL_RPC), |
||||
context, request, false)); |
||||
} |
||||
|
||||
void GenericStub::UnaryCallInternal( |
||||
grpc::ClientContext* context, const grpc::string& method, |
||||
const grpc::ByteBuffer* request, grpc::ByteBuffer* response, |
||||
std::function<void(grpc::Status)> on_completion) { |
||||
internal::CallbackUnaryCall( |
||||
channel_.get(), |
||||
grpc::internal::RpcMethod(method.c_str(), |
||||
grpc::internal::RpcMethod::NORMAL_RPC), |
||||
context, request, response, std::move(on_completion)); |
||||
} |
||||
|
||||
void GenericStub::PrepareBidiStreamingCallInternal( |
||||
grpc::ClientContext* context, const grpc::string& method, |
||||
ClientBidiReactor<grpc::ByteBuffer, grpc::ByteBuffer>* reactor) { |
||||
internal::ClientCallbackReaderWriterFactory< |
||||
grpc::ByteBuffer, |
||||
grpc::ByteBuffer>::Create(channel_.get(), |
||||
grpc::internal::RpcMethod( |
||||
method.c_str(), |
||||
grpc::internal::RpcMethod::BIDI_STREAMING), |
||||
context, reactor); |
||||
} |
||||
|
||||
void GenericStub::PrepareUnaryCallInternal(grpc::ClientContext* context, |
||||
const grpc::string& method, |
||||
const grpc::ByteBuffer* request, |
||||
grpc::ByteBuffer* response, |
||||
ClientUnaryReactor* reactor) { |
||||
internal::ClientCallbackUnaryFactory::Create<grpc::ByteBuffer, |
||||
grpc::ByteBuffer>( |
||||
channel_.get(), |
||||
grpc::internal::RpcMethod(method.c_str(), |
||||
grpc::internal::RpcMethod::NORMAL_RPC), |
||||
context, request, response, reactor); |
||||
} |
||||
|
||||
} // namespace grpc_impl
|
File diff suppressed because it is too large
Load Diff
@ -1,27 +0,0 @@ |
||||
# tracers |
||||
"api\x00" |
||||
"channel\x00" |
||||
"channel_stack_builder\x00" |
||||
"connectivity_state\x00" |
||||
"flowctl\x00" |
||||
"http\x00" |
||||
"http1\x00" |
||||
"round_robin\x00" |
||||
"secure_endpoint\x00" |
||||
"tcp\x00" |
||||
"transport_security\x00" |
||||
|
||||
# channel args |
||||
"\x00grpc.census\x00" |
||||
"\x00grpc.max_concurrent_streams\x00" |
||||
"\x00grpc.max_message_length\x00" |
||||
"\x00grpc.http2.initial_sequence_number\x00" |
||||
"\x00grpc.http2.lookahead_bytes\x00" |
||||
"\x00grpc.http2.hpack_table_size.decoder\x00" |
||||
"\x00grpc.http2.hpack_table_size.encoder\x00" |
||||
"\x01grpc.default_authority\x00" |
||||
"\x01grpc.primary_user_agent\x00" |
||||
"\x01grpc.secondary_user_agent\x00" |
||||
"\x00grpc.max_reconnect_backoff_ms\x00" |
||||
"\x01grpc.ssl_target_name_override\x00" |
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue