Compare commits

...

52 Commits

Author SHA1 Message Date
AJ Heller 1d85b01fc1
[backport][iomgr][EventEngine] Improve server handling of file descri… (#36489)
…ptor exhaustion (#33656)
2024-04-30 14:29:59 -07:00
Xuan Wang 17d9ce26e4
[PSM interop] Don't fail target if sub-target already failed (#33222) (v1.46.x backport) (#33293)
Backport of #33222 to v1.46.x.
---
We configured TestGrid to file bug separately for each failed
sub-target, if we still fail the main target, TestGrid will fail
duplicate bugs.




<!--

If you know who should review your pull request, please assign it to
that
person, otherwise the pull request would get assigned randomly.

If your pull request is for a specific language, please add the
appropriate
lang label.

-->
2023-06-01 09:23:31 -07:00
Esun Kim 02384e3918
Version bump to v1.46.7 (#32387)
<!--

If you know who should review your pull request, please assign it to
that
person, otherwise the pull request would get assigned randomly.

If your pull request is for a specific language, please add the
appropriate
lang label.

-->
2023-02-14 16:24:10 -08:00
Esun Kim bcc62b00b9
[Backport: v1.46.x] Update boringssl to grpc-202302 (#32378) 2023-02-14 13:00:30 -08:00
Sergii Tkachenko bbeafc7ee8
xds interop: Fix buildscripts not continuing on a failed test suite (#32093) (#32122)
Apparently there's a difference between bash 3 and bash 4. OSX comes with bash 3 out-of-box, so for whoever wrote this logic it "worked on my machine".

The `((` construct returns a 0 exit code if the value is non-zero. Since the value starts at 0 and we do a post-increment, it will always fail the first time. Changing it to a pre-increment should fix it.
2023-01-17 15:46:30 -08:00
Sergii Tkachenko 18dda3c586
xDS interop: resume circuit_breaking test (#32038) (#32055)
This is a test running on legacy (GCE) framework.
It was disabled in https://github.com/grpc/grpc/pull/27355,
however it has never been ported to the new framework.
Unless there were reasons I'm not aware of, disabling it is a mistake.
Other languages still run it:

- Java: cc5378453f/buildscripts/kokoro/xds_v3.sh (L21)
- Go: f2fbb0e07e/test/kokoro/xds.sh (L30)
- Node: 81083bd229/packages/grpc-js-xds/scripts/xds.sh (L57)
2023-01-10 10:35:38 -08:00
Jan Tattermusch cdc97245c7
pin version of rubygems-update (#32026) 2023-01-09 07:44:28 -08:00
Jan Tattermusch dc2d5f447c
add back grpc_artifact_android_ndk image to unbreak C# artifact build (#32025) 2023-01-09 07:43:32 -08:00
Jan Tattermusch 1d85d9f7b9
fix PHP distribtest on monterey (#31963) 2023-01-03 13:21:56 -08:00
Jan Tattermusch 3a1aaa4279
run microbenchmarks_diff under docker (#31962) 2023-01-03 13:21:48 -08:00
Jan Tattermusch 8147f9e647
Backport #31504 to v1.46.x (#31961)
* run grpc_android in a docker container

* add TODO

* fixup android_ndk dockerfile
2023-01-03 13:21:41 -08:00
Jan Tattermusch 36bfe355da
Backport #31126 to v1.46.x (#31960)
* fix python build on kokoro macos monterey

* avoid using realpath

* fix grpc_distribtests_python on macos monterey

* try upgrade setuptools

* remove python2.7 from grpc_build_artifacts.sh as well

* python3.11 not needed yet for the backport

* stop building python3.6 wheel on macos
2023-01-03 13:21:24 -08:00
Jan Tattermusch 420ed4bd5a
Backport #31125 to v1.46.x (#31910)
* cocoapods is not a ruby dependency

* fix rvm update on macos monterey images
2022-12-20 12:54:29 -08:00
AJ Heller af855eb64e
Bump v1.46.x to v1.46.6 (#31849) 2022-12-15 20:47:53 -08:00
Jan Tattermusch 218146b83c
Work around UnaryCall hang with corrupt status detail string #29854 (#31853)
* workaround #29854

* Update BatchContextSafeHandle.cs
2022-12-13 08:59:10 -08:00
AJ Heller 3bfe761a6e
Update zlib (#31356) (#31357) (#31845)
To mitigate CVE-2022-37434

Co-authored-by: Mikhail Lappo <miklelappo@users.noreply.github.com>
2022-12-08 20:34:27 -08:00
Jan Tattermusch 90b0ea387e
Preserve a frozen snapshot for C# quickstart and basics tutorial in the v1.46.x branch (alongside with C# examples) (#31491)
* add frozen version of C# quickstart and basics

* freeze a copy quickstart and basics tutorial at v1.46.x branch
2022-10-31 09:36:35 -07:00
James Newton-King dc4e17f9cc
[C#] Move insecure compose validation into Channel (#31376)
* Move insecure compose validation into Channel

* try fix compilation problem

* Fix tests?

* Revert "Fix tests?"

This reverts commit 7852e996b9.

* cleanup in Channel constructor if credential creation fails

* fix throw

Co-authored-by: James Newton-King <james@newtonking.com>

* review feedback

Co-authored-by: James Newton-King <james@newtonking.com>

Co-authored-by: Jan Tattermusch <jtattermusch@google.com>
2022-10-24 11:25:23 -07:00
tony fc302e37f2
Handle server Shutdown when no Start called (#31232)
See https://github.com/grpc/grpc/issues/23390
2022-10-10 10:44:00 -07:00
Sergii Tkachenko 2c03f37c47
[CPP] xDS interop GCE framework: pin grpcio-tools to use protobuf 3.x (#31214) (#31220) 2022-10-03 12:22:42 -07:00
Sergii Tkachenko 2e9625d4da
xDS interop GCE framework: pin grpcio-tools to use protobuf 3.x (#31191) (#31200) 2022-10-03 10:34:30 -07:00
Richard Belleville 3f072f44cb
Bump v1.46.x to v1.46.5 (#31050)
* bump version to 1.46.5

* regenerate projects

* Automated change: Fix sanity tests

Co-authored-by: gnossen <gnossen@users.noreply.github.com>
2022-09-19 19:42:04 -07:00
Richard Belleville ae50337551
Update protobuf to v3.19.5 on v1.46.x (#31032)
* Update third_party/protobuf

* run tools/distrib/python/make_grpcio_tools.py

* update build_handwritten.yaml

* regenerate projects
2022-09-19 13:40:41 -07:00
Sergii Tkachenko fd1889564f
xDS interop: buildscripts: fix run_test return status (#30768) (#30878)
To capture the return status of the test in run_test the last command must be the call to the test itself.
This removes `set +x`, which makes the run_test always return success, and not propagate the test status.

I can't find it, but this exact error bit us before. Looks like it leaked to other scripts.
The good thing is if the test was executed, it's failure would still be picked up from the result xml.

However, if the test framework didn't start in the first place, the result will be false positive.
Example: https://source.cloud.google.com/results/invocations/98d3e679-ec8a-40bd-9f36-88179747b0d6/targets

```
/home/kbuilder/.pyenv/versions/k8s_xds_test_runner/bin/python3: Error while finding module specification for 'tests.authz_test' (ModuleNotFoundError: No module named 'tests')
+ set +x
Failed test suites: 0

[ID: 3548168] Command finished after 625 secs, exit value: 0
```
2022-09-08 13:56:26 -07:00
tony aff8712136
Fix client resource leaks-issues 8451 and 28153 (#30290)
* Fix client resource leaks-issues 8451 and 28153

* Updated from review comments

* added further tests
2022-09-07 08:39:57 -07:00
Sergii Tkachenko 30ec4bc93e
xDS interop: enable pod log collection in the buildscripts (#30735) (#30859)
- Enables pod log collection in all PSM interop jobs implemented in https://github.com/grpc/grpc/pull/30594.
- Associate test suite runs with their own log file, so it's displayed on "Target Log" tab

Cherry-pick conflicts resolved:
- Removed authz test
2022-09-06 17:23:35 -07:00
tony d6f7a8f942
Test to verify fix for #17761 (#30523)
* Test to verify fix for 17761

* Updated test comments to clarify expected behaviour
2022-09-06 08:42:38 -07:00
Sergii Tkachenko 0553241986
xDS interop: Python LB tests build and use the python server (#30637) (#30657)
Undoes https://github.com/grpc/grpc/pull/27096.
While we lost context why py tests were used pinned cpp server,
we think this is due to lack of support of the set_not_serving RPC
in the python server, see https://github.com/grpc/grpc/issues/30635.

This RPC is only used in two tests, and for them we added a
temporary override of the test server to the reference Java server,
see https://github.com/grpc/grpc/pull/30636.

All other LB tests should work with the python server just fine.
2022-08-19 14:22:10 -07:00
Sergii Tkachenko c34b22eda3
xDS interop: add missing image tagging to the buildscripts (#30520) (#30531) 2022-08-08 13:44:28 -07:00
Sergii Tkachenko 2e696fa051
xds interop: choose correct cluster in grpc_xds_k8s_lb_python.sh (1.48.x backport) (#30331) 2022-07-19 13:56:15 -07:00
Yash Tibrewal 2d9bdfc048
priority and ring_hash LBs: fix interactions when using ring_hash under priority (#29332) (#30253)
* refactor connection delay injection from client_lb_end2end_test

* fix build

* fix build on older compilers

* clang-format

* buildifier

* a bit of code cleanup

* start failover time whenever the child reports CONNECTING, and don't cancel when deactivating

* clang-format

* rewrite test

* simplify logic in priority policy

* clang-format

* switch to using a bit to indicate child healthiness

* fix reversed comment

* more changes in priority and ring_hash.

priority:
- go back to starting failover timer upon CONNECTING, but only if seen
  READY or IDLE more recently than TRANSIENT_FAILURE

ring_hash:
- don't flap back and forth between IDLE and CONNECTING; once we go
  CONNECTING, we stay there until either TF or READY
- after the first subchannel goes TF, we proactively start another
  subchannel connecting, just like we do after a second subchannel
  reports TF, to ensure that we don't stay in CONNECTING indefinitely if
  we aren't getting any new picks
- always return ring hash's picker, regardless of connectivity state
- update the subchannel connectivity state seen by the picker upon
  subchannel list creation
- start proactive subchannel connection attempt upon subchannel list
  creation if needed

* ring_hash: fix connectivity state seen by aggregation and picker

* fix obiwan error

* swap the order of ring_hash aggregation rules 3 and 4

* restore original test

* refactor connection injector QueuedAttempt code

* add test showing that ring_hash will continue connecting without picks

* clang-format

* don't actually need seen_failure_since_ready_ anymore

* fix TSAN problem

* address code review comments

Co-authored-by: Mark D. Roth <roth@google.com>
2022-07-11 09:37:02 -07:00
Yash Tibrewal b9f099b98e
ring hash: fix picker propagation bug in xds_cluster_manager policy (#29959) (#30254)
* ring hash: fix picker propagation bug in xds_cluster_manager policy

* fix build

* fix build

Co-authored-by: Mark D. Roth <roth@google.com>
2022-07-08 17:36:12 -07:00
Esun Kim a672e22bd1
Bump release version to v1.46.4 (#30179) 2022-07-06 11:24:03 -07:00
hongnod ae5ee43667
tell MSVC to build grpc with utf-8 (#30003) (#30152)
* tell MSVC to build grpc with utf-8

fix #29979

* generate_projects

Co-authored-by: AJ Heller <hork@google.com>

Co-authored-by: AJ Heller <hork@google.com>
2022-07-05 11:55:26 -07:00
Esun Kim 5c53533a1d
Using string rep for status-time (#30123) (#30146) 2022-06-30 10:49:11 -07:00
Sergii Tkachenko 3618772809
xds-k8s jobs: standardize TESTING_VERSION (#30027) (#30049)
This Addresses the issue with skips not working in golang tests, ref b/235688697. 

1. Unifies `TESTING_VERSION` detection  in grpc_xds_k8s_install_test_driver.sh - new approach applicable to all languages.
2. Use `TESTING_VERSION` in all build files in `--testing_version` and when tagging docker images. This will be backported to all active test branches. Build Scripts in all other languages will be updated as well.
2022-06-17 15:33:22 -07:00
Lidi Zheng 53d69cc581
Bump release version to v1.46.3 (#29736) 2022-05-19 14:15:41 -07:00
Mark D. Roth 93692265b0
xds: use federation env var to guard new-style resource name parsing (#29725) (#29727) 2022-05-19 07:05:32 -07:00
Jan Tattermusch 0172cd96f6
use internal release_open_source.py script to commit changes and create PR (#29698) 2022-05-16 19:00:23 +02:00
apolcyn c828f70902
Bump version 1.46.2 (#29661)
* Bump version to 1.46.2

* Regenerate projects
2022-05-11 22:36:06 -07:00
apolcyn 2d25d83cf2
[Ruby]fix a rake compile error since rake-compiler ~> 1.1 (#28666) (#29656)
* fix a rake compile error since rake-compiler ~> 1.1

* change File.directory? check

Co-authored-by: Hannah Shi <hannahshisfb@gmail.com>
2022-05-11 17:24:50 -07:00
apolcyn c697d4bfe9
Upgrade ruby rake-compiler-dock images (#29304) (#29657)
* Upgrade rake-compiler-dock images
2022-05-11 15:26:35 -07:00
Jan Tattermusch d0f397841a
Fix #24421: UnobservedTaskException when server Unavailable (C# on v1.46.x branch) (#29582)
* fix #24421

* add test for #24421
2022-05-10 09:13:09 +02:00
Lidi Zheng 22d4401efc
Bump version to v1.46.1 (#29606)
* Bump version to v1.46.1

* Bump version in BUILD as well
2022-05-06 14:34:58 -07:00
Lidi Zheng 80a429899d
Downgrade io_bazel_rules_go to v0.27.0 restore Bazel 3.x support (#29596) (#29602) 2022-05-06 11:17:07 -07:00
Lidi Zheng 0da56bcc0e
Bump release version to v1.46.0 (#29565)
* bump version to 1.46.0

* regenerate projects
2022-05-03 11:28:38 -07:00
Lidi Zheng 4870127fb2
Revert "Enable authz_test for C++ and Python (#29147)" (#29556)
This reverts commit 02bddf2017.
2022-05-02 17:12:26 -07:00
Nicolas Noble 31a829787b
Upgrading zlib. (#29533)
CVE-2018-25032
2022-05-02 09:24:22 -07:00
Lidi Zheng 730599bd5b
Bump release version to v1.46.0-pre2 (#29471)
* bump version to 1.46.0-pre2

* regenerate projects
2022-04-21 12:21:44 -07:00
Craig Tiller 99c5319a76
[1.46][fault-injection] Backport fix for fault injection filter #29467
Backports #29288
2022-04-21 10:37:14 -07:00
Yash Tibrewal 4d118154a9
PSM Security: Increase test timeout to 240 for C++ and Python (#29420) (#29421) 2022-04-15 16:09:08 -07:00
Lidi Zheng a4c35cb5ff
Bump release version to v1.46.0-pre1 (#29415)
* bump version to 1.46.0-pre1

* regenerate projects
2022-04-15 14:27:13 -07:00
142 changed files with 3543 additions and 1363 deletions

3
BUILD
View File

@ -155,7 +155,7 @@ g_stands_for = "golazo" # @unused
core_version = "24.0.0" # @unused
version = "1.46.0-dev" # @unused
version = "1.46.7" # @unused
GPR_PUBLIC_HDRS = [
"include/grpc/support/alloc.h",
@ -2688,6 +2688,7 @@ grpc_cc_library(
external_deps = ["absl/strings"],
language = "c++",
deps = [
"capture",
"gpr_base",
"grpc_base",
"grpc_service_config",

11
CMakeLists.txt generated
View File

@ -25,12 +25,12 @@
cmake_minimum_required(VERSION 3.5.1)
set(PACKAGE_NAME "grpc")
set(PACKAGE_VERSION "1.46.0-dev")
set(PACKAGE_VERSION "1.46.7")
set(gRPC_CORE_VERSION "24.0.0")
set(gRPC_CORE_SOVERSION "24")
set(gRPC_CPP_VERSION "1.46.0-dev")
set(gRPC_CPP_VERSION "1.46.7")
set(gRPC_CPP_SOVERSION "1.46")
set(gRPC_CSHARP_VERSION "2.46.0-dev")
set(gRPC_CSHARP_VERSION "2.46.7")
set(gRPC_CSHARP_SOVERSION "2.46")
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME "${PACKAGE_NAME}-${PACKAGE_VERSION}")
@ -261,6 +261,8 @@ if(MSVC)
set(_gRPC_C_CXX_FLAGS "${_gRPC_C_CXX_FLAGS} /wd4987 /wd4774 /wd4819 /wd4996 /wd4619")
# Silences thousands of trucation warnings
set(_gRPC_C_CXX_FLAGS "${_gRPC_C_CXX_FLAGS} /wd4503")
# Tell MSVC to build grpc using utf-8
set(_gRPC_C_CXX_FLAGS "${_gRPC_C_CXX_FLAGS} /utf-8")
endif()
if (MINGW)
add_definitions(-D_WIN32_WINNT=0x600)
@ -292,7 +294,7 @@ endif()
if (gRPC_XDS_USER_AGENT_IS_CSHARP)
# The value of the defines needs to contain quotes.
# See https://github.com/grpc/grpc/blob/fbf32836a418cc84f58786700273b65cb9174e1d/src/core/ext/xds/xds_api.cc#L854
add_definitions("-DGRPC_XDS_USER_AGENT_NAME_SUFFIX=\"csharp\"" "-DGRPC_XDS_USER_AGENT_VERSION_SUFFIX=\"2.46.0-dev\"")
add_definitions("-DGRPC_XDS_USER_AGENT_NAME_SUFFIX=\"csharp\"" "-DGRPC_XDS_USER_AGENT_VERSION_SUFFIX=\"2.46.7\"")
endif()
if(UNIX)
@ -17218,6 +17220,7 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.grpc.pb.h
src/cpp/server/csds/csds.cc
test/cpp/end2end/connection_delay_injector.cc
test/cpp/end2end/rls_server.cc
test/cpp/end2end/test_service_impl.cc
test/cpp/end2end/xds/xds_end2end_test.cc

4
Makefile generated
View File

@ -447,8 +447,8 @@ Q = @
endif
CORE_VERSION = 24.0.0
CPP_VERSION = 1.46.0-dev
CSHARP_VERSION = 2.46.0-dev
CPP_VERSION = 1.46.7
CSHARP_VERSION = 2.46.7
CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)

View File

@ -34,11 +34,11 @@ Rake::ExtensionTask.new('grpc_c', spec) do |ext|
'universal-darwin'
]
ext.cross_compiling do |spec|
spec.files = %w( etc/roots.pem grpc_c.32.ruby grpc_c.64.ruby )
spec.files += Dir.glob('src/ruby/bin/**/*')
spec.files += Dir.glob('src/ruby/ext/**/*')
spec.files += Dir.glob('src/ruby/lib/**/*')
spec.files += Dir.glob('src/ruby/pb/**/*')
spec.files = spec.files.select {
|file| file.start_with?(
"src/ruby/bin/", "src/ruby/ext/", "src/ruby/lib/", "src/ruby/pb/")
}
spec.files += %w( etc/roots.pem grpc_c.32.ruby grpc_c.64.ruby )
end
end

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/_metadata.py.template`!!!
__version__ = """1.46.0.dev0"""
__version__ = """1.46.7"""

View File

@ -195,11 +195,11 @@ def grpc_deps():
name = "boringssl",
# Use github mirror instead of https://boringssl.googlesource.com/boringssl
# to obtain a boringssl archive with consistent sha256
sha256 = "534fa658bd845fd974b50b10f444d392dfd0d93768c4a51b61263fd37d851c40",
strip_prefix = "boringssl-b9232f9e27e5668bc0414879dcdedb2a59ea75f2",
sha256 = "ad0b806b6c5cbd6cae121c608945d5fed468748e330632e8d53315089ad52c67",
strip_prefix = "boringssl-6195bf8242156c9a2fa75702eee058f91b86a88b",
urls = [
"https://storage.googleapis.com/grpc-bazel-mirror/github.com/google/boringssl/archive/b9232f9e27e5668bc0414879dcdedb2a59ea75f2.tar.gz",
"https://github.com/google/boringssl/archive/b9232f9e27e5668bc0414879dcdedb2a59ea75f2.tar.gz",
"https://storage.googleapis.com/grpc-bazel-mirror/github.com/google/boringssl/archive/6195bf8242156c9a2fa75702eee058f91b86a88b.tar.gz",
"https://github.com/google/boringssl/archive/6195bf8242156c9a2fa75702eee058f91b86a88b.tar.gz",
],
)
@ -207,22 +207,22 @@ def grpc_deps():
http_archive(
name = "zlib",
build_file = "@com_github_grpc_grpc//third_party:zlib.BUILD",
sha256 = "6d4d6640ca3121620995ee255945161821218752b551a1a180f4215f7d124d45",
strip_prefix = "zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f",
sha256 = "90f43a9c998740e8a0db24b0af0147033db2aaaa99423129abbd76640757cac9",
strip_prefix = "zlib-04f42ceca40f73e2978b50e93806c2a18c1281fc",
urls = [
"https://storage.googleapis.com/grpc-bazel-mirror/github.com/madler/zlib/archive/cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz",
"https://github.com/madler/zlib/archive/cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz",
"https://storage.googleapis.com/grpc-bazel-mirror/github.com/madler/zlib/archive/04f42ceca40f73e2978b50e93806c2a18c1281fc.tar.gz",
"https://github.com/madler/zlib/archive/04f42ceca40f73e2978b50e93806c2a18c1281fc.tar.gz",
],
)
if "com_google_protobuf" not in native.existing_rules():
http_archive(
name = "com_google_protobuf",
sha256 = "bab1685f92cc4ea5b6420026eef6c7973ae96fc21f4f1a3ee626dc6ca6d77c12",
strip_prefix = "protobuf-22d0e265de7d2b3d2e9a00d071313502e7d4cccf",
sha256 = "245da8acf76b24317c885b8157b7ddcbe5e4919bd8ff60def824c8ed3ee49a8e",
strip_prefix = "protobuf-b464cfbee18c71c40e761a5273ad369f3547294b",
urls = [
"https://storage.googleapis.com/grpc-bazel-mirror/github.com/protocolbuffers/protobuf/archive/22d0e265de7d2b3d2e9a00d071313502e7d4cccf.tar.gz",
"https://github.com/protocolbuffers/protobuf/archive/22d0e265de7d2b3d2e9a00d071313502e7d4cccf.tar.gz",
"https://storage.googleapis.com/grpc-bazel-mirror/github.com/protocolbuffers/protobuf/archive/b464cfbee18c71c40e761a5273ad369f3547294b.tar.gz",
"https://github.com/protocolbuffers/protobuf/archive/b464cfbee18c71c40e761a5273ad369f3547294b.tar.gz",
],
patches = ["@com_github_grpc_grpc//third_party:protobuf.patch"],
patch_args = ["-p1"],
@ -366,10 +366,10 @@ def grpc_deps():
if "io_bazel_rules_go" not in native.existing_rules():
http_archive(
name = "io_bazel_rules_go",
sha256 = "f2dcd210c7095febe54b804bb1cd3a58fe8435a909db2ec04e31542631cf715c",
sha256 = "69de5c704a05ff37862f7e0f5534d4f479418afc21806c887db544a316f3cb6b",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.31.0/rules_go-v0.31.0.zip",
"https://github.com/bazelbuild/rules_go/releases/download/v0.31.0/rules_go-v0.31.0.zip",
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.27.0/rules_go-v0.27.0.tar.gz",
"https://github.com/bazelbuild/rules_go/releases/download/v0.27.0/rules_go-v0.27.0.tar.gz",
],
)

View File

@ -8408,6 +8408,7 @@ targets:
language: c++
headers:
- src/cpp/server/csds/csds.h
- test/cpp/end2end/connection_delay_injector.h
- test/cpp/end2end/counted_service.h
- test/cpp/end2end/rls_server.h
- test/cpp/end2end/test_service_impl.h
@ -8457,6 +8458,7 @@ targets:
- src/proto/grpc/testing/xds/v3/string.proto
- src/proto/grpc/testing/xds/v3/tls.proto
- src/cpp/server/csds/csds.cc
- test/cpp/end2end/connection_delay_injector.cc
- test/cpp/end2end/rls_server.cc
- test/cpp/end2end/test_service_impl.cc
- test/cpp/end2end/xds/xds_end2end_test.cc

View File

@ -15,8 +15,8 @@ settings:
core_version: 24.0.0
csharp_major_version: 2
g_stands_for: golazo
protobuf_version: 3.19.4
version: 1.46.0-dev
protobuf_version: 3.19.5
version: 1.46.7
targets:
- name: gen_hpack_tables
build: tool

2
config.m4 generated
View File

@ -1163,7 +1163,7 @@ if test "$PHP_GRPC" != "no"; then
-D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0 \
-DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1 \
-DGRPC_XDS_USER_AGENT_NAME_SUFFIX='"\"PHP\""' \
-DGRPC_XDS_USER_AGENT_VERSION_SUFFIX='"\"1.46.0dev\""')
-DGRPC_XDS_USER_AGENT_VERSION_SUFFIX='"\"1.46.7\""')
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/census)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/channel_idle)

View File

@ -45,4 +45,4 @@
- 1.43 'g' stands for ['green'](https://github.com/grpc/grpc/tree/v1.43.x)
- 1.44 'g' stands for ['great'](https://github.com/grpc/grpc/tree/v1.44.x)
- 1.45 'g' stands for ['gravity'](https://github.com/grpc/grpc/tree/v1.45.x)
- 1.46 'g' stands for ['golazo'](https://github.com/grpc/grpc/tree/master)
- 1.46 'g' stands for ['golazo'](https://github.com/grpc/grpc/tree/v1.46.x)

View File

@ -10,7 +10,7 @@ and the client, and run them.
PREREQUISITES
-------------
- The [.NET Core SDK 2.1+](https://www.microsoft.com/net/core)
- The [.NET Core SDK 3.1+](https://www.microsoft.com/net/core)
You can also build the solution `Greeter.sln` using Visual Studio 2017,
but it's not a requirement.
@ -36,4 +36,4 @@ Tutorial
You can find a more detailed tutorial about Grpc in [gRPC Basics: C#][]
[helloworld.proto]:../../protos/helloworld.proto
[gRPC Basics: C#]:https://grpc.io/docs/languages/csharp/basics
[gRPC Basics: C#]:../RouteGuide/basics.md

View File

@ -0,0 +1,221 @@
### Prerequisites
Whether you're using Windows, OS X, or Linux, you can follow this
example by using either an IDE and its build tools,
or by using the the .NET Core SDK command line tools.
First, make sure you have installed the
[gRPC C# prerequisites](https://github.com/grpc/grpc/blob/v1.46.x/src/csharp/README.md#prerequisites).
You will also need Git to download the sample code.
### Download the example
You'll need a local copy of the example code to work through this quick start.
Download the example code from our GitHub repository (the following command
clones the entire repository, but you just need the examples for this quick start
and other tutorials):
```sh
# Clone the repository to get the example code:
$ git clone -b v1.46.x --depth 1 --shallow-submodules https://github.com/grpc/grpc
$ cd grpc
```
This document will walk you through the "Hello World" example.
The projects and source files can be found in the `examples/csharp/Helloworld` directory.
The example in this walkthrough already adds the necessary
dependencies for you (`Grpc`, `Grpc.Tools` and `Google.Protobuf` NuGet packages).
### Build the example
#### Using Visual Studio (or Visual Studio for Mac)
* Open the solution `Greeter.sln` with Visual Studio
* Build the solution
#### Using .NET Core SDK from the command line
From the `examples/csharp/Helloworld` directory:
```sh
> dotnet build Greeter.sln
```
### Run a gRPC application
From the `examples/csharp/Helloworld` directory:
* Run the server:
```sh
> cd GreeterServer
> dotnet run
```
* From another terminal, run the client:
```sh
> cd GreeterClient
> dotnet run
```
Congratulations! You've just run a client-server application with gRPC.
### Update the gRPC service
Now let's look at how to update the application with an extra method on the
server for the client to call. Our gRPC service is defined using protocol
buffers; you can find out lots more about how to define a service in a `.proto`
file in [Basics tutorial](../RouteGuide/basics.md). For now all you need to know is that both the
server and the client "stub" have a `SayHello` RPC method that takes a
`HelloRequest` parameter from the client and returns a `HelloResponse` from the
server, and that this method is defined like this:
```proto
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
```
Let's update this so that the `Greeter` service has two methods. Edit
`examples/protos/helloworld.proto` and update it with a new `SayHelloAgain`
method, with the same request and response types:
```proto
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
// Sends another greeting
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
```
Remember to save the file!
### Generate gRPC code
Next we need to update the gRPC code used by our application to use the new service definition.
The `Grpc.Tools` NuGet package contains the protoc and protobuf C# plugin binaries needed
to generate the code. Starting from version 1.17 the package also integrates with
MSBuild to provide [automatic C# code generation](https://github.com/grpc/grpc/blob/v1.46.x/src/csharp/BUILD-INTEGRATION.md)
from `.proto` files.
This example project already depends on the `Grpc.Tools` NuGet package so just re-building the solution
is enough to regenerate the code from our modified `.proto` file.
You can rebuild just like we first built the original
example by running `dotnet build Greeter.sln` or by clicking "Build" in Visual Studio.
The build regenerates the following files
under the `Greeter/obj/Debug/TARGET_FRAMEWORK` directory:
* `Helloworld.cs` contains all the protocol buffer code to populate,
serialize, and retrieve our request and response message types
* `HelloworldGrpc.cs` provides generated client and server classes,
including:
* an abstract class `Greeter.GreeterBase` to inherit from when defining
Greeter service implementations
* a class `Greeter.GreeterClient` that can be used to access remote Greeter
instances
### Update and run the application
We now have new generated server and client code, but we still need to implement
and call the new method in the human-written parts of our example application.
#### Update the server
With the `Greeter.sln` open in your IDE, open `GreeterServer/Program.cs`.
Implement the new method by editing the GreeterImpl class like this:
```C#
class GreeterImpl : Greeter.GreeterBase
{
// Server side handler of the SayHello RPC
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
}
// Server side handler for the SayHelloAgain RPC
public override Task<HelloReply> SayHelloAgain(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply { Message = "Hello again " + request.Name });
}
}
```
#### Update the client
With the same `Greeter.sln` open in your IDE, open `GreeterClient/Program.cs`.
Call the new method like this:
```C#
public static void Main(string[] args)
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
var client = new Greeter.GreeterClient(channel);
String user = "you";
var reply = client.SayHello(new HelloRequest { Name = user });
Console.WriteLine("Greeting: " + reply.Message);
var secondReply = client.SayHelloAgain(new HelloRequest { Name = user });
Console.WriteLine("Greeting: " + secondReply.Message);
channel.ShutdownAsync().Wait();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
```
#### Rebuild the modified example
Rebuild the newly modified example just like we first built the original
example by running `dotnet build Greeter.sln` or by clicking "Build" in Visual Studio.
#### Run!
Just like we did before, from the `examples/csharp/Helloworld` directory:
1. Run the server:
```sh
> cd GreeterServer
> dotnet run
```
2. From another terminal, run the client:
```sh
> cd GreeterClient
> dotnet run
```

View File

@ -3,4 +3,4 @@
The files in this folder are the samples used in [gRPC Basics: C#][],
a detailed tutorial for using gRPC in C#.
[gRPC Basics: C#]:https://grpc.io/docs/languages/csharp/basics
[gRPC Basics: C#]:basics.md

View File

@ -0,0 +1,480 @@
This tutorial provides a basic C# programmer's introduction to working with gRPC.
By walking through this example you'll learn how to:
- Define a service in a .proto file.
- Generate server and client code using the protocol buffer compiler.
- Use the C# gRPC API to write a simple client and server for your service.
It assumes that you have read the [Introduction to gRPC](/docs/what-is-grpc/introduction/) and are familiar
with [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). Note that the
example in this tutorial uses the proto3 version of the protocol buffers
language: you can find out more in the
[proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) and
[C# generated code reference](https://developers.google.com/protocol-buffers/docs/reference/csharp-generated).
### Why use gRPC?
Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients.
With gRPC we can define our service once in a `.proto` file and generate clients and servers in any of gRPCs supported languages, which in turn can be run in environments ranging from servers inside a large data center to your own tablet — all the complexity of communication between different languages and environments is handled for you by gRPC. We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating.
### Example code and setup
The example code for our tutorial is in
[grpc/grpc/examples/csharp/RouteGuide](https://github.com/grpc/grpc/tree/v1.46.x/examples/csharp/RouteGuide). To
download the example, clone the `grpc` repository by running the following
command:
```sh
$ git clone -b v1.46.x --depth 1 --shallow-submodules https://github.com/grpc/grpc
$ cd grpc
```
All the files for this tutorial are in the directory
`examples/csharp/RouteGuide`. Open the solution
`examples/csharp/RouteGuide/RouteGuide.sln` from Visual Studio (Windows or Mac) or Visual Studio Code.
For additional installation details, see the [How to use
instructions](https://github.com/grpc/grpc/tree/v1.46.x/src/csharp#how-to-use).
### Defining the service
Our first step (as you'll know from the [Introduction to gRPC](/docs/what-is-grpc/introduction/)) is to
define the gRPC *service* and the method *request* and *response* types using
[protocol buffers](https://developers.google.com/protocol-buffers/docs/overview).
You can see the complete .proto file in
[`examples/protos/route_guide.proto`](https://github.com/grpc/grpc/blob/v1.46.x/examples/protos/route_guide.proto).
To define a service, you specify a named `service` in your .proto file:
```protobuf
service RouteGuide {
...
}
```
Then you define `rpc` methods inside your service definition, specifying their
request and response types. gRPC lets you define four kinds of service method,
all of which are used in the `RouteGuide` service:
- A *simple RPC* where the client sends a request to the server using the client
object and waits for a response to come back, just like a normal function
call.
```protobuf
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
```
- A *server-side streaming RPC* where the client sends a request to the server
and gets a stream to read a sequence of messages back. The client reads from
the returned stream until there are no more messages. As you can see in our
example, you specify a server-side streaming method by placing the `stream`
keyword before the *response* type.
```protobuf
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
```
- A *client-side streaming RPC* where the client writes a sequence of messages
and sends them to the server, again using a provided stream. Once the client
has finished writing the messages, it waits for the server to read them all
and return its response. You specify a client-side streaming method by placing
the `stream` keyword before the *request* type.
```protobuf
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}
```
- A *bidirectional streaming RPC* where both sides send a sequence of messages
using a read-write stream. The two streams operate independently, so clients
and servers can read and write in whatever order they like: for example, the
server could wait to receive all the client messages before writing its
responses, or it could alternately read a message then write a message, or
some other combination of reads and writes. The order of messages in each
stream is preserved. You specify this type of method by placing the `stream`
keyword before both the request and the response.
```protobuf
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
```
Our `.proto` file also contains protocol buffer message type definitions for all
the request and response types used in our service methods - for example, here's
the `Point` message type:
```protobuf
// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
```
### Generating client and server code
Next we need to generate the gRPC client and server interfaces from our .proto
service definition. This can be done by invoking the protocol buffer compiler `protoc` with
a special gRPC C# plugin from the command line, but starting from version
1.17 the `Grpc.Tools` NuGet package integrates with MSBuild to provide [automatic C# code generation](https://github.com/grpc/grpc/blob/v1.46.x/src/csharp/BUILD-INTEGRATION.md)
from `.proto` files, which gives much better developer experience by running
the right commands for you as part of the build.
This example already has a dependency on `Grpc.Tools` NuGet package and the
`route_guide.proto` has already been added to the project, so the only thing
needed to generate the client and server code is to build the solution.
That can be done by running `dotnet build RouteGuide.sln` or building directly
in Visual Studio.
The build regenerates the following files
under the `RouteGuide/obj/Debug/TARGET_FRAMEWORK` directory:
- `RouteGuide.cs` contains all the protocol buffer code to populate,
serialize, and retrieve our request and response message types
- `RouteGuideGrpc.cs` provides generated client and server classes,
including:
- an abstract class `RouteGuide.RouteGuideBase` to inherit from when defining
RouteGuide service implementations
- a class `RouteGuide.RouteGuideClient` that can be used to access remote
RouteGuide instances
### Creating the server {#server}
First let's look at how we create a `RouteGuide` server. If you're only
interested in creating gRPC clients, you can skip this section and go straight
to [Creating the client](#client) (though you might find it interesting
anyway!).
There are two parts to making our `RouteGuide` service do its job:
- Implementing the service functionality by inheriting from the base class
generated from our service definition: doing the actual "work" of our service.
- Running a gRPC server to listen for requests from clients and return the
service responses.
You can find our example `RouteGuide` server in
[examples/csharp/RouteGuide/RouteGuideServer/RouteGuideImpl.cs](https://github.com/grpc/grpc/blob/v1.46.x/examples/csharp/RouteGuide/RouteGuideServer/RouteGuideImpl.cs).
Let's take a closer look at how it works.
#### Implementing RouteGuide
As you can see, our server has a `RouteGuideImpl` class that inherits from the
generated `RouteGuide.RouteGuideBase`:
```csharp
// RouteGuideImpl provides an implementation of the RouteGuide service.
public class RouteGuideImpl : RouteGuide.RouteGuideBase
```
##### Simple RPC
`RouteGuideImpl` implements all our service methods. Let's look at the simplest
type first, `GetFeature`, which just gets a `Point` from the client and returns
the corresponding feature information from its database in a `Feature`.
```csharp
public override Task<Feature> GetFeature(Point request, Grpc.Core.ServerCallContext context)
{
return Task.FromResult(CheckFeature(request));
}
```
The method is passed a context for the RPC (which is empty in the alpha
release), the client's `Point` protocol buffer request, and returns a `Feature`
protocol buffer. In the method we create the `Feature` with the appropriate
information, and then return it. To allow asynchronous implementation, the
method returns `Task<Feature>` rather than just `Feature`. You are free to
perform your computations synchronously and return the result once you've
finished, just as we do in the example.
##### Server-side streaming RPC
Now let's look at something a bit more complicated - a streaming RPC.
`ListFeatures` is a server-side streaming RPC, so we need to send back multiple
`Feature` protocol buffers to our client.
```csharp
// in RouteGuideImpl
public override async Task ListFeatures(Rectangle request,
Grpc.Core.IServerStreamWriter<Feature> responseStream,
Grpc.Core.ServerCallContext context)
{
var responses = features.FindAll( (feature) => feature.Exists() && request.Contains(feature.Location) );
foreach (var response in responses)
{
await responseStream.WriteAsync(response);
}
}
```
As you can see, here the request object is a `Rectangle` in which our client
wants to find `Feature`s, but instead of returning a simple response we need to
write responses to an asynchronous stream `IServerStreamWriter` using async
method `WriteAsync`.
##### Client-side streaming RPC
Similarly, the client-side streaming method `RecordRoute` uses an
[IAsyncEnumerator](https://github.com/Reactive-Extensions/Rx.NET/blob/master/Ix.NET/Source/System.Interactive.Async/IAsyncEnumerator.cs),
to read the stream of requests using the async method `MoveNext` and the
`Current` property.
```csharp
public override async Task<RouteSummary> RecordRoute(Grpc.Core.IAsyncStreamReader<Point> requestStream,
Grpc.Core.ServerCallContext context)
{
int pointCount = 0;
int featureCount = 0;
int distance = 0;
Point previous = null;
var stopwatch = new Stopwatch();
stopwatch.Start();
while (await requestStream.MoveNext())
{
var point = requestStream.Current;
pointCount++;
if (CheckFeature(point).Exists())
{
featureCount++;
}
if (previous != null)
{
distance += (int) previous.GetDistance(point);
}
previous = point;
}
stopwatch.Stop();
return new RouteSummary
{
PointCount = pointCount,
FeatureCount = featureCount,
Distance = distance,
ElapsedTime = (int)(stopwatch.ElapsedMilliseconds / 1000)
};
}
```
##### Bidirectional streaming RPC
Finally, let's look at our bidirectional streaming RPC `RouteChat`.
```csharp
public override async Task RouteChat(Grpc.Core.IAsyncStreamReader<RouteNote> requestStream,
Grpc.Core.IServerStreamWriter<RouteNote> responseStream,
Grpc.Core.ServerCallContext context)
{
while (await requestStream.MoveNext())
{
var note = requestStream.Current;
List<RouteNote> prevNotes = AddNoteForLocation(note.Location, note);
foreach (var prevNote in prevNotes)
{
await responseStream.WriteAsync(prevNote);
}
}
}
```
Here the method receives both `requestStream` and `responseStream` arguments.
Reading the requests is done the same way as in the client-side streaming method
`RecordRoute`. Writing the responses is done the same way as in the server-side
streaming method `ListFeatures`.
#### Starting the server
Once we've implemented all our methods, we also need to start up a gRPC server
so that clients can actually use our service. The following snippet shows how we
do this for our `RouteGuide` service:
```csharp
var features = RouteGuideUtil.LoadFeatures();
Server server = new Server
{
Services = { RouteGuide.BindService(new RouteGuideImpl(features)) },
Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
};
server.Start();
Console.WriteLine("RouteGuide server listening on port " + port);
Console.WriteLine("Press any key to stop the server...");
Console.ReadKey();
server.ShutdownAsync().Wait();
```
As you can see, we build and start our server using `Grpc.Core.Server` class. To
do this, we:
1. Create an instance of `Grpc.Core.Server`.
1. Create an instance of our service implementation class `RouteGuideImpl`.
1. Register our service implementation by adding its service definition to the
`Services` collection (We obtain the service definition from the generated
`RouteGuide.BindService` method).
1. Specify the address and port we want to use to listen for client requests.
This is done by adding `ServerPort` to the `Ports` collection.
1. Call `Start` on the server instance to start an RPC server for our service.
### Creating the client {#client}
In this section, we'll look at creating a C# client for our `RouteGuide`
service. You can see our complete example client code in
[examples/csharp/RouteGuide/RouteGuideClient/Program.cs](https://github.com/grpc/grpc/blob/v1.46.x/examples/csharp/RouteGuide/RouteGuideClient/Program.cs).
#### Creating a client object
To call service methods, we first need to create a client object (also referred
to as *stub* for other gRPC languages).
First, we need to create a gRPC client channel that will connect to gRPC server.
Then, we create an instance of the `RouteGuide.RouteGuideClient` class generated
from our .proto, passing the channel as an argument.
```csharp
Channel channel = new Channel("127.0.0.1:50052", ChannelCredentials.Insecure);
var client = new RouteGuide.RouteGuideClient(channel);
// YOUR CODE GOES HERE
channel.ShutdownAsync().Wait();
```
#### Calling service methods
Now let's look at how we call our service methods. gRPC C# provides asynchronous
versions of each of the supported method types. For convenience, gRPC C# also
provides a synchronous method stub, but only for simple (single request/single
response) RPCs.
##### Simple RPC
Calling the simple RPC `GetFeature` in a synchronous way is nearly as
straightforward as calling a local method.
```csharp
Point request = new Point { Latitude = 409146138, Longitude = -746188906 };
Feature feature = client.GetFeature(request);
```
As you can see, we create and populate a request protocol buffer object (in our
case `Point`), and call the desired method on the client object, passing it the
request. If the RPC finishes with success, the response protocol buffer (in our
case `Feature`) is returned. Otherwise, an exception of type `RpcException` is
thrown, indicating the status code of the problem.
Alternatively, if you are in an async context, you can call an asynchronous
version of the method and use the `await` keyword to await the result:
```csharp
Point request = new Point { Latitude = 409146138, Longitude = -746188906 };
Feature feature = await client.GetFeatureAsync(request);
```
##### Streaming RPCs
Now let's look at our streaming methods. If you've already read [Creating the
server](#server) some of this may look very familiar - streaming RPCs are
implemented in a similar way on both sides. The difference with respect to
simple call is that the client methods return an instance of a call object. This
provides access to request/response streams and/or the asynchronous result,
depending on the streaming type you are using.
Here's where we call the server-side streaming method `ListFeatures`, which has
the property `ReponseStream` of type `IAsyncEnumerator<Feature>`
```csharp
using (var call = client.ListFeatures(request))
{
while (await call.ResponseStream.MoveNext())
{
Feature feature = call.ResponseStream.Current;
Console.WriteLine("Received " + feature.ToString());
}
}
```
The client-side streaming method `RecordRoute` is similar, except we use the
property `RequestStream` to write the requests one by one using `WriteAsync`,
and eventually signal that no more requests will be sent using `CompleteAsync`.
The method result can be obtained through the property `ResponseAsync`.
```csharp
using (var call = client.RecordRoute())
{
foreach (var point in points)
{
await call.RequestStream.WriteAsync(point);
}
await call.RequestStream.CompleteAsync();
RouteSummary summary = await call.ResponseAsync;
}
```
Finally, let's look at our bidirectional streaming RPC `RouteChat`. In this
case, we write the request to `RequestStream` and receive the responses from
`ResponseStream`. As you can see from the example, the streams are independent
of each other.
```csharp
using (var call = client.RouteChat())
{
var responseReaderTask = Task.Run(async () =>
{
while (await call.ResponseStream.MoveNext())
{
var note = call.ResponseStream.Current;
Console.WriteLine("Received " + note);
}
});
foreach (RouteNote request in requests)
{
await call.RequestStream.WriteAsync(request);
}
await call.RequestStream.CompleteAsync();
await responseReaderTask;
}
```
### Try it out!
Build the client and server:
Using Visual Studio (or Visual Studio For Mac)
: Open the solution `examples/csharp/RouteGuide/RouteGuide.sln` and select **Build**.
Using `dotnet` command line tool
: Run `dotnet build RouteGuide.sln` from the `examples/csharp/RouteGuide`
directory. For additional instructions on building the gRPC example with the
`dotnet` command line tool, see [Quick start](../Greeter/quickstart.md).
Run the server:
```sh
> cd RouteGuideServer
> dotnet run
```
From a different terminal, run the client:
```sh
> cd RouteGuideClient
> dotnet run
```
You can also run the server and client directly from Visual Studio.

2
gRPC-C++.podspec generated
View File

@ -22,7 +22,7 @@
Pod::Spec.new do |s|
s.name = 'gRPC-C++'
# TODO (mxyan): use version that match gRPC version when pod is stabilized
version = '1.46.0-dev'
version = '1.46.7'
s.version = version
s.summary = 'gRPC C++ library'
s.homepage = 'https://grpc.io'

4
gRPC-Core.podspec generated
View File

@ -21,7 +21,7 @@
Pod::Spec.new do |s|
s.name = 'gRPC-Core'
version = '1.46.0-dev'
version = '1.46.7'
s.version = version
s.summary = 'Core cross-platform gRPC library, written in C'
s.homepage = 'https://grpc.io'
@ -169,7 +169,7 @@ Pod::Spec.new do |s|
ss.header_mappings_dir = '.'
ss.libraries = 'z'
ss.dependency "#{s.name}/Interface", version
ss.dependency 'BoringSSL-GRPC', '0.0.24'
ss.dependency 'BoringSSL-GRPC', '0.0.25'
ss.dependency 'Libuv-gRPC', '0.0.10'
ss.dependency 'abseil/base/base', abseil_version
ss.dependency 'abseil/base/core_headers', abseil_version

2
gRPC-ProtoRPC.podspec generated
View File

@ -21,7 +21,7 @@
Pod::Spec.new do |s|
s.name = 'gRPC-ProtoRPC'
version = '1.46.0-dev'
version = '1.46.7'
s.version = version
s.summary = 'RPC library for Protocol Buffers, based on gRPC'
s.homepage = 'https://grpc.io'

View File

@ -21,7 +21,7 @@
Pod::Spec.new do |s|
s.name = 'gRPC-RxLibrary'
version = '1.46.0-dev'
version = '1.46.7'
s.version = version
s.summary = 'Reactive Extensions library for iOS/OSX.'
s.homepage = 'https://grpc.io'

2
gRPC.podspec generated
View File

@ -20,7 +20,7 @@
Pod::Spec.new do |s|
s.name = 'gRPC'
version = '1.46.0-dev'
version = '1.46.7'
s.version = version
s.summary = 'gRPC client library for iOS/OSX'
s.homepage = 'https://grpc.io'

6
grpc.gemspec generated
View File

@ -19,15 +19,13 @@ Gem::Specification.new do |s|
s.files += %w( etc/roots.pem )
s.files += Dir.glob('src/ruby/bin/**/*')
s.files += Dir.glob('src/ruby/ext/**/*')
s.files += Dir.glob('src/ruby/lib/**/*').reject do |f|
# Binaries are included by rake-compiler and would lead to circular dependencies here
File.fnmatch("**/?.?/grpc_c.so", f)
end
s.files += Dir.glob('src/ruby/lib/**/*')
s.files += Dir.glob('src/ruby/pb/**/*').reject do |f|
f.match(%r{^src/ruby/pb/test})
end
s.files += Dir.glob('include/grpc/**/*')
s.test_files = Dir.glob('src/ruby/spec/**/*')
s.test_files += Dir.glob('src/ruby/pb/test/**/*')
s.bindir = 'src/ruby/bin'
s.require_paths = %w( src/ruby/lib src/ruby/bin src/ruby/pb )
s.platform = Gem::Platform::RUBY

10
package.xml generated
View File

@ -13,16 +13,16 @@
<date>2019-09-24</date>
<time>16:06:07</time>
<version>
<release>1.46.0dev</release>
<api>1.46.0dev</api>
<release>1.46.7</release>
<api>1.46.7</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
<release>stable</release>
<api>stable</api>
</stability>
<license>Apache 2.0</license>
<notes>
- gRPC Core 1.46.0 update
- gRPC Core 1.46.7 update
</notes>
<contents>
<dir baseinstalldir="/" name="/">

View File

@ -1,4 +1,4 @@
// generated by generate_boringssl_prefix_header.sh on BoringSSL commit: b9232f9e27e5668bc0414879dcdedb2a59ea75f2
// generated by generate_boringssl_prefix_header.sh on BoringSSL commit: 6195bf8242156c9a2fa75702eee058f91b86a88b
// Copyright (c) 2018, Google Inc.
//

View File

@ -104,7 +104,7 @@ class PriorityLb : public LoadBalancingPolicy {
bool ignore_reresolution_requests);
void ExitIdleLocked();
void ResetBackoffLocked();
void DeactivateLocked();
void MaybeDeactivateLocked();
void MaybeReactivateLocked();
void Orphan() override;
@ -217,6 +217,8 @@ class PriorityLb : public LoadBalancingPolicy {
absl::Status connectivity_status_;
RefCountedPtr<RefCountedPicker> picker_wrapper_;
bool seen_ready_or_idle_since_transient_failure_ = true;
OrphanablePtr<DeactivationTimer> deactivation_timer_;
OrphanablePtr<FailoverTimer> failover_timer_;
};
@ -225,14 +227,37 @@ class PriorityLb : public LoadBalancingPolicy {
void ShutdownLocked() override;
// Returns UINT32_MAX if child is not in current priority list.
// Returns the priority of the specified child name, or UINT32_MAX if
// the child is not in the current priority list.
uint32_t GetChildPriorityLocked(const std::string& child_name) const;
// Called when a child's connectivity state has changed.
// May propagate the update to the channel or trigger choosing a new
// priority.
void HandleChildConnectivityStateChangeLocked(ChildPriority* child);
// Deletes a child. Called when the child's deactivation timer fires.
void DeleteChild(ChildPriority* child);
void TryNextPriorityLocked(bool report_connecting);
void SelectPriorityLocked(uint32_t priority);
// Iterates through the list of priorities to choose one:
// - If the child for a priority doesn't exist, creates it.
// - If a child's failover timer is pending, returns without selecting
// a priority while we wait for the child to attempt to connect. In
// this case, if report_connecting is true, reports CONNECTING state to
// the channel.
// - If the child is connected, it will be used as the current priority.
// - Otherwise, continues on to the next child.
// Reports TRANSIENT_FAILURE to the channel if all children are not
// connected.
//
// This method is idempotent; it should yield the same result every
// time as a function of the state of the children.
void ChoosePriorityLocked(bool report_connecting);
// Sets the specified priority as the current priority.
// Deactivates any children at lower priorities.
// Returns the child's picker to the channel.
void SetCurrentPriorityLocked(uint32_t priority);
const Duration child_failover_timeout_;
@ -246,6 +271,8 @@ class PriorityLb : public LoadBalancingPolicy {
bool update_in_progress_ = false;
// All children that currently exist.
// Some of these children may be in deactivated state.
std::map<std::string, OrphanablePtr<ChildPriority>> children_;
// The priority that is being used.
uint32_t current_priority_ = UINT32_MAX;
@ -312,7 +339,7 @@ void PriorityLb::UpdateLocked(UpdateArgs args) {
current_child_from_before_update_ = children_[child_name].get();
// Unset current_priority_, since it was an index into the old
// config's priority list and may no longer be valid. It will be
// reset later by TryNextPriorityLocked(), but we unset it here in
// reset later by ChoosePriorityLocked(), but we unset it here in
// case updating any of our children triggers a state update.
current_priority_ = UINT32_MAX;
}
@ -332,7 +359,7 @@ void PriorityLb::UpdateLocked(UpdateArgs args) {
auto config_it = config_->children().find(child_name);
if (config_it == config_->children().end()) {
// Existing child not found in new config. Deactivate it.
child->DeactivateLocked();
child->MaybeDeactivateLocked();
} else {
// Existing child found in new config. Update it.
child->UpdateLocked(config_it->second.config,
@ -341,7 +368,7 @@ void PriorityLb::UpdateLocked(UpdateArgs args) {
}
update_in_progress_ = false;
// Try to get connected.
TryNextPriorityLocked(/*report_connecting=*/children_.empty());
ChoosePriorityLocked(/*report_connecting=*/children_.empty());
}
uint32_t PriorityLb::GetChildPriorityLocked(
@ -380,11 +407,11 @@ void PriorityLb::HandleChildConnectivityStateChangeLocked(
} else {
// If it's no longer READY or IDLE, we should stop using it.
// We already started trying other priorities as a result of the
// update, but calling TryNextPriorityLocked() ensures that we will
// update, but calling ChoosePriorityLocked() ensures that we will
// properly select between CONNECTING and TRANSIENT_FAILURE as the
// new state to report to our parent.
current_child_from_before_update_ = nullptr;
TryNextPriorityLocked(/*report_connecting=*/true);
ChoosePriorityLocked(/*report_connecting=*/true);
}
return;
}
@ -396,52 +423,26 @@ void PriorityLb::HandleChildConnectivityStateChangeLocked(
"priority %u",
this, child_priority, child->name().c_str(), current_priority_);
}
// Ignore priorities not in the current config.
if (child_priority == UINT32_MAX) return;
// Ignore lower-than-current priorities.
if (child_priority > current_priority_) return;
// If a child reports TRANSIENT_FAILURE, start trying the next priority.
// Note that even if this is for a higher-than-current priority, we
// may still need to create some children between this priority and
// the current one (e.g., if we got an update that inserted new
// priorities ahead of the current one).
if (child->connectivity_state() == GRPC_CHANNEL_TRANSIENT_FAILURE) {
TryNextPriorityLocked(
/*report_connecting=*/child_priority == current_priority_);
return;
}
// The update is for a higher-than-current priority (or for any
// priority if we don't have any current priority).
if (child_priority < current_priority_) {
// If the child reports READY or IDLE, switch to that priority.
// Otherwise, ignore the update.
if (child->connectivity_state() == GRPC_CHANNEL_READY ||
child->connectivity_state() == GRPC_CHANNEL_IDLE) {
SelectPriorityLocked(child_priority);
}
return;
}
// The current priority has returned a new picker, so pass it up to
// our parent.
channel_control_helper()->UpdateState(child->connectivity_state(),
child->connectivity_status(),
child->GetPicker());
// Unconditionally call ChoosePriorityLocked(). It should do the
// right thing based on the state of all children.
ChoosePriorityLocked(
/*report_connecting=*/child_priority == current_priority_);
}
void PriorityLb::DeleteChild(ChildPriority* child) {
// If this was the current child from before the most recent update,
// stop using it. We already started trying other priorities as a
// result of the update, but calling TryNextPriorityLocked() ensures that
// result of the update, but calling ChoosePriorityLocked() ensures that
// we will properly select between CONNECTING and TRANSIENT_FAILURE as the
// new state to report to our parent.
if (current_child_from_before_update_ == child) {
current_child_from_before_update_ = nullptr;
TryNextPriorityLocked(/*report_connecting=*/true);
ChoosePriorityLocked(/*report_connecting=*/true);
}
children_.erase(child->name());
}
void PriorityLb::TryNextPriorityLocked(bool report_connecting) {
void PriorityLb::ChoosePriorityLocked(bool report_connecting) {
current_priority_ = UINT32_MAX;
for (uint32_t priority = 0; priority < config_->priorities().size();
++priority) {
@ -471,7 +472,7 @@ void PriorityLb::TryNextPriorityLocked(bool report_connecting) {
// If the child is in state READY or IDLE, switch to it.
if (child->connectivity_state() == GRPC_CHANNEL_READY ||
child->connectivity_state() == GRPC_CHANNEL_IDLE) {
SelectPriorityLocked(priority);
SetCurrentPriorityLocked(priority);
return;
}
// Child is not READY or IDLE.
@ -491,6 +492,13 @@ void PriorityLb::TryNextPriorityLocked(bool report_connecting) {
return;
}
// Child has been failing for a while. Move on to the next priority.
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_priority_trace)) {
gpr_log(GPR_INFO,
"[priority_lb %p] skipping priority %u, child %s: state=%s, "
"failover timer not pending",
this, priority, child_name.c_str(),
ConnectivityStateName(child->connectivity_state()));
}
}
// If there are no more priorities to try, report TRANSIENT_FAILURE.
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_priority_trace)) {
@ -506,7 +514,7 @@ void PriorityLb::TryNextPriorityLocked(bool report_connecting) {
absl::make_unique<TransientFailurePicker>(status));
}
void PriorityLb::SelectPriorityLocked(uint32_t priority) {
void PriorityLb::SetCurrentPriorityLocked(uint32_t priority) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_priority_trace)) {
gpr_log(GPR_INFO, "[priority_lb %p] selected priority %u, child %s", this,
priority, config_->priorities()[priority].c_str());
@ -517,7 +525,7 @@ void PriorityLb::SelectPriorityLocked(uint32_t priority) {
for (uint32_t p = priority + 1; p < config_->priorities().size(); ++p) {
const std::string& child_name = config_->priorities()[p];
auto it = children_.find(child_name);
if (it != children_.end()) it->second->DeactivateLocked();
if (it != children_.end()) it->second->MaybeDeactivateLocked();
}
// Update picker.
auto& child = children_[config_->priorities()[priority]];
@ -742,9 +750,6 @@ PriorityLb::ChildPriority::CreateChildPolicyLocked(
}
void PriorityLb::ChildPriority::ExitIdleLocked() {
if (connectivity_state_ == GRPC_CHANNEL_IDLE && failover_timer_ == nullptr) {
failover_timer_ = MakeOrphanable<FailoverTimer>(Ref());
}
child_policy_->ExitIdleLocked();
}
@ -766,20 +771,31 @@ void PriorityLb::ChildPriority::OnConnectivityStateUpdateLocked(
connectivity_state_ = state;
connectivity_status_ = status;
picker_wrapper_ = MakeRefCounted<RefCountedPicker>(std::move(picker));
// If READY or IDLE or TRANSIENT_FAILURE, cancel failover timer.
if (state == GRPC_CHANNEL_READY || state == GRPC_CHANNEL_IDLE ||
state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
// If we transition to state CONNECTING and we've not seen
// TRANSIENT_FAILURE more recently than READY or IDLE, start failover
// timer if not already pending.
// In any other state, update seen_ready_or_idle_since_transient_failure_
// and cancel failover timer.
if (state == GRPC_CHANNEL_CONNECTING) {
if (seen_ready_or_idle_since_transient_failure_ &&
failover_timer_ == nullptr) {
failover_timer_ = MakeOrphanable<FailoverTimer>(Ref());
}
} else if (state == GRPC_CHANNEL_READY || state == GRPC_CHANNEL_IDLE) {
seen_ready_or_idle_since_transient_failure_ = true;
failover_timer_.reset();
} else if (state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
seen_ready_or_idle_since_transient_failure_ = false;
failover_timer_.reset();
}
// Notify the parent policy.
priority_policy_->HandleChildConnectivityStateChangeLocked(this);
}
void PriorityLb::ChildPriority::DeactivateLocked() {
// If already deactivated, don't do it again.
if (deactivation_timer_ != nullptr) return;
failover_timer_.reset();
deactivation_timer_ = MakeOrphanable<DeactivationTimer>(Ref());
void PriorityLb::ChildPriority::MaybeDeactivateLocked() {
if (deactivation_timer_ == nullptr) {
deactivation_timer_ = MakeOrphanable<DeactivationTimer>(Ref());
}
}
void PriorityLb::ChildPriority::MaybeReactivateLocked() {

View File

@ -143,8 +143,6 @@ class RingHash : public LoadBalancingPolicy {
const ServerAddress& address() const { return address_; }
bool seen_failure_since_ready() const { return seen_failure_since_ready_; }
// Performs connectivity state updates that need to be done both when we
// first start watching and when a watcher notification is received.
void UpdateConnectivityStateLocked(
@ -157,10 +155,18 @@ class RingHash : public LoadBalancingPolicy {
grpc_connectivity_state connectivity_state) override;
ServerAddress address_;
// Last logical connectivity state seen.
// Note that this may differ from the state actually reported by the
// subchannel in some cases; for example, once this is set to
// TRANSIENT_FAILURE, we do not change it again until we get READY,
// so we skip any interim stops in CONNECTING.
grpc_connectivity_state last_connectivity_state_ = GRPC_CHANNEL_SHUTDOWN;
// Connectivity state seen by picker.
// Uses an atomic so that it can be accessed outside of the WorkSerializer.
std::atomic<grpc_connectivity_state> connectivity_state_for_picker_{
GRPC_CHANNEL_IDLE};
bool seen_failure_since_ready_ = false;
};
// A list of subchannels.
@ -193,9 +199,10 @@ class RingHash : public LoadBalancingPolicy {
// Updates the RH policy's connectivity state based on the
// subchannel list's state counters, creating new picker and new ring.
// Furthermore, return a bool indicating whether the aggregated state is
// Transient Failure.
bool UpdateRingHashConnectivityStateLocked();
// The index parameter indicates the index into the list of the subchannel
// whose status report triggered the call to
// UpdateRingHashConnectivityStateLocked().
void UpdateRingHashConnectivityStateLocked(size_t index);
// Create a new ring from this subchannel list.
RefCountedPtr<Ring> MakeRing();
@ -508,7 +515,7 @@ RingHash::PickResult RingHash::Picker::Pick(PickArgs args) {
//
void RingHash::RingHashSubchannelList::StartWatchingLocked() {
if (num_subchannels() == 0) return;
GPR_ASSERT(num_subchannels() != 0);
// Check current state of each subchannel synchronously.
for (size_t i = 0; i < num_subchannels(); ++i) {
grpc_connectivity_state state =
@ -522,7 +529,10 @@ void RingHash::RingHashSubchannelList::StartWatchingLocked() {
}
}
// Send updated state to parent based on reported subchannel states.
UpdateRingHashConnectivityStateLocked();
// Pretend we're getting this update from the last subchannel, so that
// if we need to proactively start connecting, we'll start from the
// first subchannel.
UpdateRingHashConnectivityStateLocked(num_subchannels() - 1);
}
void RingHash::RingHashSubchannelList::UpdateStateCountersLocked(
@ -552,47 +562,72 @@ void RingHash::RingHashSubchannelList::UpdateStateCountersLocked(
}
}
// Sets the RH policy's connectivity state and generates a new picker based
// on the current subchannel list or requests an re-attempt by returning true..
bool RingHash::RingHashSubchannelList::UpdateRingHashConnectivityStateLocked() {
void RingHash::RingHashSubchannelList::UpdateRingHashConnectivityStateLocked(
size_t index) {
RingHash* p = static_cast<RingHash*>(policy());
// Only set connectivity state if this is the current subchannel list.
if (p->subchannel_list_.get() != this) return false;
if (p->subchannel_list_.get() != this) return;
// The overall aggregation rules here are:
// 1. If there is at least one subchannel in READY state, report READY.
// 2. If there are 2 or more subchannels in TRANSIENT_FAILURE state, report
// TRANSIENT_FAILURE.
// TRANSIENT_FAILURE.
// 3. If there is at least one subchannel in CONNECTING state, report
// CONNECTING.
// 4. If there is at least one subchannel in IDLE state, report IDLE.
// 5. Otherwise, report TRANSIENT_FAILURE.
// CONNECTING.
// 4. If there is one subchannel in TRANSIENT_FAILURE state and there is
// more than one subchannel, report CONNECTING.
// 5. If there is at least one subchannel in IDLE state, report IDLE.
// 6. Otherwise, report TRANSIENT_FAILURE.
//
// We set start_connection_attempt to true if we match rules 2, 3, or 6.
grpc_connectivity_state state;
absl::Status status;
bool start_connection_attempt = false;
if (num_ready_ > 0) {
/* READY */
p->channel_control_helper()->UpdateState(
GRPC_CHANNEL_READY, absl::Status(),
absl::make_unique<Picker>(p->Ref(DEBUG_LOCATION, "RingHashPicker"),
p->ring_));
return false;
state = GRPC_CHANNEL_READY;
} else if (num_transient_failure_ >= 2) {
state = GRPC_CHANNEL_TRANSIENT_FAILURE;
status = absl::UnavailableError("connections to backends failing");
start_connection_attempt = true;
} else if (num_connecting_ > 0) {
state = GRPC_CHANNEL_CONNECTING;
} else if (num_transient_failure_ == 1 && num_subchannels() > 1) {
state = GRPC_CHANNEL_CONNECTING;
start_connection_attempt = true;
} else if (num_idle_ > 0) {
state = GRPC_CHANNEL_IDLE;
} else {
state = GRPC_CHANNEL_TRANSIENT_FAILURE;
status = absl::UnavailableError("connections to backends failing");
start_connection_attempt = true;
}
if (num_connecting_ > 0 && num_transient_failure_ < 2) {
p->channel_control_helper()->UpdateState(
GRPC_CHANNEL_CONNECTING, absl::Status(),
absl::make_unique<QueuePicker>(p->Ref(DEBUG_LOCATION, "QueuePicker")));
return false;
}
if (num_idle_ > 0 && num_transient_failure_ < 2) {
p->channel_control_helper()->UpdateState(
GRPC_CHANNEL_IDLE, absl::Status(),
absl::make_unique<Picker>(p->Ref(DEBUG_LOCATION, "RingHashPicker"),
p->ring_));
return false;
}
absl::Status status =
absl::UnavailableError("connections to backend failing or idle");
// Generate new picker and return it to the channel.
// Note that we use our own picker regardless of connectivity state.
p->channel_control_helper()->UpdateState(
GRPC_CHANNEL_TRANSIENT_FAILURE, status,
absl::make_unique<TransientFailurePicker>(status));
return true;
state, status,
absl::make_unique<Picker>(p->Ref(DEBUG_LOCATION, "RingHashPicker"),
p->ring_));
// While the ring_hash policy is reporting TRANSIENT_FAILURE, it will
// not be getting any pick requests from the priority policy.
// However, because the ring_hash policy does not attempt to
// reconnect to subchannels unless it is getting pick requests,
// it will need special handling to ensure that it will eventually
// recover from TRANSIENT_FAILURE state once the problem is resolved.
// Specifically, it will make sure that it is attempting to connect to
// at least one subchannel at any given time. After a given subchannel
// fails a connection attempt, it will move on to the next subchannel
// in the ring. It will keep doing this until one of the subchannels
// successfully connects, at which point it will report READY and stop
// proactively trying to connect. The policy will remain in
// TRANSIENT_FAILURE until at least one subchannel becomes connected,
// even if subchannels are in state CONNECTING during that time.
//
// Note that we do the same thing when the policy is in state
// CONNECTING, just to ensure that we don't remain in CONNECTING state
// indefinitely if there are no new picks coming in.
if (start_connection_attempt) {
size_t next_index = (index + 1) % num_subchannels();
subchannel(next_index)->subchannel()->AttemptToConnect();
}
}
RefCountedPtr<RingHash::Ring> RingHash::RingHashSubchannelList::MakeRing() {
@ -617,25 +652,34 @@ void RingHash::RingHashSubchannelData::UpdateConnectivityStateLocked(
ConnectivityStateName(last_connectivity_state_),
ConnectivityStateName(connectivity_state));
}
// Decide what state to report for aggregation purposes.
// Decide what state to report for the purposes of aggregation and
// picker behavior.
// If we haven't seen a failure since the last time we were in state
// READY, then we report the state change as-is. However, once we do see
// a failure, we report TRANSIENT_FAILURE and do not report any subsequent
// state changes until we go back into state READY.
if (!seen_failure_since_ready_) {
if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
seen_failure_since_ready_ = true;
}
subchannel_list()->UpdateStateCountersLocked(last_connectivity_state_,
connectivity_state);
} else {
if (connectivity_state == GRPC_CHANNEL_READY) {
seen_failure_since_ready_ = false;
subchannel_list()->UpdateStateCountersLocked(
GRPC_CHANNEL_TRANSIENT_FAILURE, connectivity_state);
if (last_connectivity_state_ == GRPC_CHANNEL_TRANSIENT_FAILURE) {
// If not transitioning to READY, ignore the update, since we want
// to continue to consider ourselves in TRANSIENT_FAILURE.
if (connectivity_state != GRPC_CHANNEL_READY) return;
} else if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
// If we go from READY to TF, treat it as IDLE.
// This transition can be caused by a "normal" connection failure, such
// as the server closing the connection due to a max-age setting. In
// this case, we want to have RPCs that hash to this subchannel wait for
// the reconnection attempt rather than assuming that the subchannel is
// bad and moving on to a subsequent subchannel in the ring.
if (last_connectivity_state_ == GRPC_CHANNEL_READY) {
connectivity_state = GRPC_CHANNEL_IDLE;
}
}
// Record last seen connectivity state.
// Update state counters used for aggregation.
subchannel_list()->UpdateStateCountersLocked(last_connectivity_state_,
connectivity_state);
// Update state seen by picker.
connectivity_state_for_picker_.store(connectivity_state,
std::memory_order_relaxed);
// Update last seen connectivity state.
last_connectivity_state_ = connectivity_state;
}
@ -643,15 +687,11 @@ void RingHash::RingHashSubchannelData::ProcessConnectivityChangeLocked(
grpc_connectivity_state connectivity_state) {
RingHash* p = static_cast<RingHash*>(subchannel_list()->policy());
GPR_ASSERT(subchannel() != nullptr);
// Update connectivity state used by picker.
connectivity_state_for_picker_.store(connectivity_state,
std::memory_order_relaxed);
// If the new state is TRANSIENT_FAILURE, re-resolve.
// Only do this if we've started watching, not at startup time.
// Otherwise, if the subchannel was already in state TRANSIENT_FAILURE
// when the subchannel list was created, we'd wind up in a constant
// loop of re-resolution.
// Also attempt to reconnect.
if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_ring_hash_trace)) {
gpr_log(GPR_INFO,
@ -665,28 +705,7 @@ void RingHash::RingHashSubchannelData::ProcessConnectivityChangeLocked(
UpdateConnectivityStateLocked(connectivity_state);
// Update the RH policy's connectivity state, creating new picker and new
// ring.
bool transient_failure =
subchannel_list()->UpdateRingHashConnectivityStateLocked();
// While the ring_hash policy is reporting TRANSIENT_FAILURE, it will
// not be getting any pick requests from the priority policy.
// However, because the ring_hash policy does not attempt to
// reconnect to subchannels unless it is getting pick requests,
// it will need special handling to ensure that it will eventually
// recover from TRANSIENT_FAILURE state once the problem is resolved.
// Specifically, it will make sure that it is attempting to connect to
// at least one subchannel at any given time. After a given subchannel
// fails a connection attempt, it will move on to the next subchannel
// in the ring. It will keep doing this until one of the subchannels
// successfully connects, at which point it will report READY and stop
// proactively trying to connect. The policy will remain in
// TRANSIENT_FAILURE until at least one subchannel becomes connected,
// even if subchannels are in state CONNECTING during that time.
if (transient_failure &&
connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
size_t next_index = (Index() + 1) % subchannel_list()->num_subchannels();
RingHashSubchannelData* next_sd = subchannel_list()->subchannel(next_index);
next_sd->subchannel()->AttemptToConnect();
}
subchannel_list()->UpdateRingHashConnectivityStateLocked(Index());
}
//

View File

@ -194,7 +194,6 @@ class WeightedTargetLb : public LoadBalancingPolicy {
RefCountedPtr<ChildPickerWrapper> picker_wrapper_;
grpc_connectivity_state connectivity_state_ = GRPC_CHANNEL_CONNECTING;
bool seen_failure_since_ready_ = false;
OrphanablePtr<DelayedRemovalTimer> delayed_removal_timer_;
};
@ -585,19 +584,12 @@ void WeightedTargetLb::WeightedChild::OnConnectivityStateUpdateLocked(
// If the child reports IDLE, immediately tell it to exit idle.
if (state == GRPC_CHANNEL_IDLE) child_policy_->ExitIdleLocked();
// Decide what state to report for aggregation purposes.
// If we haven't seen a failure since the last time we were in state
// READY, then we report the state change as-is. However, once we do see
// a failure, we report TRANSIENT_FAILURE and ignore any subsequent state
// changes until we go back into state READY.
if (!seen_failure_since_ready_) {
if (state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
seen_failure_since_ready_ = true;
}
} else {
if (state != GRPC_CHANNEL_READY) return;
seen_failure_since_ready_ = false;
// If the last recorded state was TRANSIENT_FAILURE and the new state
// is something other than READY, don't change the state.
if (connectivity_state_ != GRPC_CHANNEL_TRANSIENT_FAILURE ||
state == GRPC_CHANNEL_READY) {
connectivity_state_ = state;
}
connectivity_state_ = state;
// Notify the LB policy.
weighted_target_policy_->UpdateStateLocked();
}

View File

@ -504,8 +504,7 @@ void XdsClusterImplLb::MaybeUpdatePickerLocked() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
gpr_log(GPR_INFO,
"[xds_cluster_impl_lb %p] updating connectivity (drop all): "
"state=READY "
"picker=%p",
"state=READY picker=%p",
this, drop_picker.get());
}
channel_control_helper()->UpdateState(GRPC_CHANNEL_READY, absl::Status(),
@ -518,8 +517,7 @@ void XdsClusterImplLb::MaybeUpdatePickerLocked() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
gpr_log(GPR_INFO,
"[xds_cluster_impl_lb %p] updating connectivity: state=%s "
"status=(%s) "
"picker=%p",
"status=(%s) picker=%p",
this, ConnectivityStateName(state_), status_.ToString().c_str(),
drop_picker.get());
}

View File

@ -176,7 +176,6 @@ class XdsClusterManagerLb : public LoadBalancingPolicy {
RefCountedPtr<ChildPickerWrapper> picker_wrapper_;
grpc_connectivity_state connectivity_state_ = GRPC_CHANNEL_IDLE;
bool seen_failure_since_ready_ = false;
// States for delayed removal.
grpc_timer delayed_removal_timer_;
@ -554,19 +553,13 @@ void XdsClusterManagerLb::ClusterChild::Helper::UpdateState(
MakeRefCounted<ChildPickerWrapper>(xds_cluster_manager_child_->name_,
std::move(picker));
// Decide what state to report for aggregation purposes.
// If we haven't seen a failure since the last time we were in state
// READY, then we report the state change as-is. However, once we do see
// a failure, we report TRANSIENT_FAILURE and ignore any subsequent state
// changes until we go back into state READY.
if (!xds_cluster_manager_child_->seen_failure_since_ready_) {
if (state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
xds_cluster_manager_child_->seen_failure_since_ready_ = true;
}
} else {
if (state != GRPC_CHANNEL_READY) return;
xds_cluster_manager_child_->seen_failure_since_ready_ = false;
// If the last recorded state was TRANSIENT_FAILURE and the new state
// is something other than READY, don't change the state.
if (xds_cluster_manager_child_->connectivity_state_ !=
GRPC_CHANNEL_TRANSIENT_FAILURE ||
state == GRPC_CHANNEL_READY) {
xds_cluster_manager_child_->connectivity_state_ = state;
}
xds_cluster_manager_child_->connectivity_state_ = state;
// Notify the LB policy.
xds_cluster_manager_child_->xds_cluster_manager_policy_->UpdateStateLocked();
}

View File

@ -29,6 +29,7 @@
#include "src/core/ext/filters/fault_injection/service_config_parser.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/status_util.h"
#include "src/core/lib/gprpp/capture.h"
#include "src/core/lib/promise/sleep.h"
#include "src/core/lib/promise/try_seq.h"
#include "src/core/lib/service_config/service_config_call_data.h"
@ -62,6 +63,33 @@ inline bool UnderFraction(const uint32_t numerator,
return random_number < numerator;
}
// Tracks an active faults lifetime.
// Increments g_active_faults when created, and decrements it when destroyed.
class FaultHandle {
public:
explicit FaultHandle(bool active) : active_(active) {
if (active) {
g_active_faults.fetch_add(1, std::memory_order_relaxed);
}
}
~FaultHandle() {
if (active_) {
g_active_faults.fetch_sub(1, std::memory_order_relaxed);
}
}
FaultHandle(const FaultHandle&) = delete;
FaultHandle& operator=(const FaultHandle&) = delete;
FaultHandle(FaultHandle&& other) noexcept
: active_(absl::exchange(other.active_, false)) {}
FaultHandle& operator=(FaultHandle&& other) noexcept {
std::swap(active_, other.active_);
return *this;
}
private:
bool active_;
};
} // namespace
class FaultInjectionFilter::InjectionDecision {
@ -73,15 +101,16 @@ class FaultInjectionFilter::InjectionDecision {
abort_request_(abort_request) {}
std::string ToString() const;
Timestamp DelayUntil() const;
Timestamp DelayUntil();
absl::Status MaybeAbort() const;
private:
bool HaveActiveFaultsQuota(bool increment) const;
bool HaveActiveFaultsQuota() const;
uint32_t max_faults_;
Duration delay_time_;
absl::optional<absl::Status> abort_request_;
FaultHandle active_fault_{false};
};
absl::StatusOr<FaultInjectionFilter> FaultInjectionFilter::Create(
@ -104,9 +133,12 @@ ArenaPromise<ServerMetadataHandle> FaultInjectionFilter::MakeCallPromise(
gpr_log(GPR_INFO, "chand=%p: Fault injection triggered %s", this,
decision.ToString().c_str());
}
auto delay = decision.DelayUntil();
return TrySeq(
Sleep(decision.DelayUntil()),
[decision]() { return decision.MaybeAbort(); },
Sleep(delay),
Capture(
[](InjectionDecision* decision) { return decision->MaybeAbort(); },
std::move(decision)),
next_promise_factory(std::move(call_args)));
}
@ -190,17 +222,13 @@ FaultInjectionFilter::MakeInjectionDecision(
: absl::nullopt);
}
bool FaultInjectionFilter::InjectionDecision::HaveActiveFaultsQuota(
bool increment) const {
if (g_active_faults.load(std::memory_order_acquire) >= max_faults_) {
return false;
}
if (increment) g_active_faults.fetch_add(1, std::memory_order_relaxed);
return true;
bool FaultInjectionFilter::InjectionDecision::HaveActiveFaultsQuota() const {
return g_active_faults.load(std::memory_order_acquire) < max_faults_;
}
Timestamp FaultInjectionFilter::InjectionDecision::DelayUntil() const {
if (delay_time_ != Duration::Zero() && HaveActiveFaultsQuota(true)) {
Timestamp FaultInjectionFilter::InjectionDecision::DelayUntil() {
if (delay_time_ != Duration::Zero() && HaveActiveFaultsQuota()) {
active_fault_ = FaultHandle{true};
return ExecCtx::Get()->Now() + delay_time_;
}
return Timestamp::InfPast();
@ -208,7 +236,7 @@ Timestamp FaultInjectionFilter::InjectionDecision::DelayUntil() const {
absl::Status FaultInjectionFilter::InjectionDecision::MaybeAbort() const {
if (abort_request_.has_value() &&
(delay_time_ != Duration::Zero() || HaveActiveFaultsQuota(false))) {
(delay_time_ != Duration::Zero() || HaveActiveFaultsQuota())) {
return abort_request_.value();
}
return absl::OkStatus();

View File

@ -44,8 +44,6 @@
namespace grpc_core {
namespace {
// TODO(donnadionne): check to see if federation is enabled, this will be
// removed once federation is fully integrated and enabled by default.
bool XdsFederationEnabled() {
@ -56,6 +54,8 @@ bool XdsFederationEnabled() {
return parse_succeeded && parsed_value;
}
namespace {
grpc_error_handle ParseChannelCreds(const Json::Object& json, size_t idx,
XdsBootstrap::XdsServer* server) {
std::vector<grpc_error_handle> error_list;

View File

@ -37,6 +37,8 @@
namespace grpc_core {
bool XdsFederationEnabled();
class XdsClient;
class XdsBootstrap {

View File

@ -788,7 +788,7 @@ void XdsClient::ChannelState::AdsCallState::AdsResponseParser::ParseResource(
}
// Check the resource name.
auto resource_name =
XdsClient::ParseXdsResourceName(result->name, result_.type);
xds_client()->ParseXdsResourceName(result->name, result_.type);
if (!resource_name.ok()) {
result_.errors.emplace_back(absl::StrCat(
"resource index ", idx, ": Cannot parse xDS resource name \"",
@ -1788,6 +1788,7 @@ XdsClient::XdsClient(std::unique_ptr<XdsBootstrap> bootstrap,
bootstrap_(std::move(bootstrap)),
args_(ModifyChannelArgs(args)),
request_timeout_(GetRequestTimeout(args)),
xds_federation_enabled_(XdsFederationEnabled()),
interested_parties_(grpc_pollset_set_create()),
certificate_provider_store_(MakeOrphanable<CertificateProviderStore>(
bootstrap_->certificate_providers())),
@ -1930,6 +1931,7 @@ void XdsClient::CancelResourceWatch(const XdsResourceType* type,
}
if (shutting_down_) return;
// Find authority.
if (!resource_name.ok()) return;
auto authority_it = authority_state_map_.find(resource_name->authority);
if (authority_it == authority_state_map_.end()) return;
AuthorityState& authority_state = authority_it->second;
@ -1982,7 +1984,7 @@ absl::StatusOr<XdsClient::XdsResourceName> XdsClient::ParseXdsResourceName(
absl::string_view name, const XdsResourceType* type) {
// Old-style names use the empty string for authority.
// authority is prefixed with "old:" to indicate that it's an old-style name.
if (!absl::StartsWith(name, "xdstp:")) {
if (!xds_federation_enabled_ || !absl::StartsWith(name, "xdstp:")) {
return XdsResourceName{"old:", {std::string(name), {}}};
}
// New style name. Parse URI.

View File

@ -286,7 +286,7 @@ class XdsClient : public DualRefCounted<XdsClient> {
const XdsResourceType* GetResourceTypeLocked(absl::string_view resource_type)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
static absl::StatusOr<XdsResourceName> ParseXdsResourceName(
absl::StatusOr<XdsResourceName> ParseXdsResourceName(
absl::string_view name, const XdsResourceType* type);
static std::string ConstructFullXdsResourceName(
absl::string_view authority, absl::string_view resource_type,
@ -302,6 +302,7 @@ class XdsClient : public DualRefCounted<XdsClient> {
std::unique_ptr<XdsBootstrap> bootstrap_;
grpc_channel_args* args_;
const Duration request_timeout_;
const bool xds_federation_enabled_;
grpc_pollset_set* interested_parties_;
OrphanablePtr<CertificateProviderStore> certificate_provider_store_;
XdsApi api_;

View File

@ -20,7 +20,10 @@
#include "src/core/lib/gprpp/status_helper.h"
#include <type_traits>
#include <string.h>
#include <algorithm>
#include <utility>
#include "absl/strings/cord.h"
#include "absl/strings/escaping.h"
@ -220,23 +223,10 @@ absl::optional<std::string> StatusGetStr(const absl::Status& status,
void StatusSetTime(absl::Status* status, StatusTimeProperty key,
absl::Time time) {
#if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1910
// Abseil has a workaround for MSVC 2015 which prevents absl::Time
// from being is_trivially_copyable but it's still safe to be
// memcopied.
#elif defined(__GNUG__) && __GNUC__ < 5
// GCC versions < 5 do not support std::is_trivially_copyable
#else
static_assert(std::is_trivially_copyable<absl::Time>::value,
"absl::Time needs to be able to be memcopied");
#endif
// This is required not to get uninitialized padding of absl::Time.
alignas(absl::Time) char buf[sizeof(time)] = {
0,
};
new (buf) absl::Time(time);
std::string time_str =
absl::FormatTime(absl::RFC3339_full, time, absl::UTCTimeZone());
status->SetPayload(GetStatusTimePropertyUrl(key),
absl::Cord(absl::string_view(buf, sizeof(time))));
absl::Cord(std::move(time_str)));
}
absl::optional<absl::Time> StatusGetTime(const absl::Status& status,
@ -245,14 +235,16 @@ absl::optional<absl::Time> StatusGetTime(const absl::Status& status,
status.GetPayload(GetStatusTimePropertyUrl(key));
if (p.has_value()) {
absl::optional<absl::string_view> sv = p->TryFlat();
absl::Time time;
if (sv.has_value()) {
// copy the content before casting to avoid misaligned address access
alignas(absl::Time) char buf[sizeof(const absl::Time)];
memcpy(buf, sv->data(), sizeof(const absl::Time));
return *reinterpret_cast<const absl::Time*>(buf);
if (absl::ParseTime(absl::RFC3339_full, sv.value(), &time, nullptr)) {
return time;
}
} else {
std::string s = std::string(*p);
return *reinterpret_cast<const absl::Time*>(s.c_str());
if (absl::ParseTime(absl::RFC3339_full, s, &time, nullptr)) {
return time;
}
}
}
return {};
@ -320,9 +312,14 @@ std::string StatusToString(const absl::Status& status) {
absl::CHexEscape(payload_view), "\""));
} else if (absl::StartsWith(type_url, kTypeTimeTag)) {
type_url.remove_prefix(kTypeTimeTag.size());
absl::Time t =
*reinterpret_cast<const absl::Time*>(payload_view.data());
kvs.push_back(absl::StrCat(type_url, ":\"", absl::FormatTime(t), "\""));
absl::Time t;
if (absl::ParseTime(absl::RFC3339_full, payload_view, &t, nullptr)) {
kvs.push_back(
absl::StrCat(type_url, ":\"", absl::FormatTime(t), "\""));
} else {
kvs.push_back(absl::StrCat(type_url, ":\"",
absl::CHexEscape(payload_view), "\""));
}
} else {
kvs.push_back(absl::StrCat(type_url, ":\"",
absl::CHexEscape(payload_view), "\""));

View File

@ -29,6 +29,18 @@
#include "src/core/lib/gpr/time_precise.h"
#include "src/core/lib/gpr/useful.h"
#define GRPC_LOG_EVERY_N_SEC(n, severity, format, ...) \
do { \
static std::atomic<uint64_t> prev{0}; \
uint64_t now = grpc_core::Timestamp::FromTimespecRoundDown( \
gpr_now(GPR_CLOCK_MONOTONIC)) \
.milliseconds_after_process_epoch(); \
if (prev == 0 || now - prev > (n)*1000) { \
prev = now; \
gpr_log(severity, format, __VA_ARGS__); \
} \
} while (0)
namespace grpc_core {
namespace time_detail {

View File

@ -16,13 +16,17 @@
*
*/
/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */
#include <grpc/support/port_platform.h>
#include <utility>
#include <grpc/support/atm.h>
// FIXME: "posix" files shouldn't be depending on _GNU_SOURCE
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <grpc/support/port_platform.h>
#include "src/core/lib/iomgr/port.h"
#ifdef GRPC_POSIX_SOCKET_TCP_SERVER
@ -43,6 +47,9 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include <grpc/byte_buffer.h>
#include <grpc/event_engine/endpoint_config.h>
#include <grpc/event_engine/event_engine.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
@ -61,8 +68,11 @@
#include "src/core/lib/iomgr/tcp_server_utils_posix.h"
#include "src/core/lib/iomgr/unix_sockets_posix.h"
#include "src/core/lib/resource_quota/api.h"
#include "src/core/lib/transport/error_utils.h"
static std::atomic<int64_t> num_dropped_connections{0};
static constexpr grpc_core::Duration kRetryAcceptWaitTime{
grpc_core::Duration::Seconds(1)};
static grpc_error_handle tcp_server_create(grpc_closure* shutdown_complete,
const grpc_channel_args* args,
@ -206,21 +216,37 @@ static void on_read(void* arg, grpc_error_handle err) {
if (fd < 0) {
if (errno == EINTR) {
continue;
} else if (errno == EAGAIN || errno == ECONNABORTED ||
errno == EWOULDBLOCK) {
}
// When the process runs out of fds, accept4() returns EMFILE. When this
// happens, the connection is left in the accept queue until either a
// read event triggers the on_read callback, or time has passed and the
// accept should be re-tried regardless. This callback is not cancelled,
// so a spurious wakeup may occur even when there's nothing to accept.
// This is not a performant code path, but if an fd limit has been
// reached, the system is likely in an unhappy state regardless.
if (errno == EMFILE) {
GRPC_LOG_EVERY_N_SEC(1, GPR_ERROR, "%s",
"File descriptor limit reached. Retrying.");
grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
if (gpr_atm_full_xchg(&sp->retry_timer_armed, true)) return;
grpc_timer_init(&sp->retry_timer,
grpc_core::ExecCtx::Get()->Now() + kRetryAcceptWaitTime,
&sp->retry_closure);
return;
}
if (errno == EAGAIN || errno == ECONNABORTED || errno == EWOULDBLOCK) {
grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
return;
} else {
gpr_mu_lock(&sp->server->mu);
if (!sp->server->shutdown_listeners) {
gpr_log(GPR_ERROR, "Failed accept4: %s", strerror(errno));
} else {
/* if we have shutdown listeners, accept4 could fail, and we
needn't notify users */
}
gpr_mu_unlock(&sp->server->mu);
goto error;
}
gpr_mu_lock(&sp->server->mu);
if (!sp->server->shutdown_listeners) {
gpr_log(GPR_ERROR, "Failed accept4: %s", strerror(errno));
} else {
// if we have shutdown listeners, accept4 could fail, and we
// needn't notify users
}
gpr_mu_unlock(&sp->server->mu);
goto error;
}
if (sp->server->memory_quota->IsMemoryPressureHigh()) {
@ -405,6 +431,7 @@ static grpc_error_handle clone_port(grpc_tcp_listener* listener,
sp->port_index = listener->port_index;
sp->fd_index = listener->fd_index + count - i;
GPR_ASSERT(sp->emfd);
grpc_tcp_server_listener_initialize_retry_timer(sp);
while (listener->server->tail->next != nullptr) {
listener->server->tail = listener->server->tail->next;
}
@ -577,6 +604,7 @@ static void tcp_server_shutdown_listeners(grpc_tcp_server* s) {
if (s->active_ports) {
grpc_tcp_listener* sp;
for (sp = s->head; sp; sp = sp->next) {
grpc_timer_cancel(&sp->retry_timer);
grpc_fd_shutdown(sp->emfd,
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"));
}

View File

@ -25,6 +25,7 @@
#include "src/core/lib/iomgr/resolve_address.h"
#include "src/core/lib/iomgr/socket_utils_posix.h"
#include "src/core/lib/iomgr/tcp_server.h"
#include "src/core/lib/iomgr/timer.h"
#include "src/core/lib/resource_quota/memory_quota.h"
/* one listening port */
@ -47,6 +48,11 @@ typedef struct grpc_tcp_listener {
identified while iterating through 'next'. */
struct grpc_tcp_listener* sibling;
int is_sibling;
// If an accept4() call fails, a timer is started to drain the accept queue in
// case no further connection attempts reach the gRPC server.
grpc_closure retry_closure;
grpc_timer retry_timer;
gpr_atm retry_timer_armed;
} grpc_tcp_listener;
/* the overall server */
@ -126,4 +132,10 @@ grpc_error_handle grpc_tcp_server_prepare_socket(
/* Ruturn true if the platform supports ifaddrs */
bool grpc_tcp_server_have_ifaddrs(void);
#endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H */
/* Initialize (but don't start) the timer and callback to retry accept4() on a
listening socket after file descriptors have been exhausted. This must be
called when creating a new listener. */
void grpc_tcp_server_listener_initialize_retry_timer(
grpc_tcp_listener* listener);
#endif /* GRPC_SRC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H */

View File

@ -18,6 +18,8 @@
#include <grpc/support/port_platform.h>
#include <grpc/support/atm.h>
#include "src/core/lib/iomgr/port.h"
#ifdef GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON
@ -80,44 +82,59 @@ static int get_max_accept_queue_size(void) {
return s_max_accept_queue_size;
}
static void listener_retry_timer_cb(void* arg, grpc_error_handle err) {
// Do nothing if cancelled.
if (err != GRPC_ERROR_NONE) return;
grpc_tcp_listener* listener = static_cast<grpc_tcp_listener*>(arg);
gpr_atm_no_barrier_store(&listener->retry_timer_armed, false);
if (!grpc_fd_is_shutdown(listener->emfd)) {
grpc_fd_set_readable(listener->emfd);
}
}
void grpc_tcp_server_listener_initialize_retry_timer(
grpc_tcp_listener* listener) {
gpr_atm_no_barrier_store(&listener->retry_timer_armed, false);
grpc_timer_init_unset(&listener->retry_timer);
GRPC_CLOSURE_INIT(&listener->retry_closure, listener_retry_timer_cb, listener,
grpc_schedule_on_exec_ctx);
}
static grpc_error_handle add_socket_to_server(grpc_tcp_server* s, int fd,
const grpc_resolved_address* addr,
unsigned port_index,
unsigned fd_index,
grpc_tcp_listener** listener) {
grpc_tcp_listener* sp = nullptr;
int port = -1;
grpc_error_handle err =
grpc_tcp_server_prepare_socket(s, fd, addr, s->so_reuseport, &port);
if (err == GRPC_ERROR_NONE) {
GPR_ASSERT(port > 0);
std::string addr_str = grpc_sockaddr_to_string(addr, true);
std::string name = absl::StrCat("tcp-server-listener:", addr_str);
gpr_mu_lock(&s->mu);
s->nports++;
GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
sp = static_cast<grpc_tcp_listener*>(gpr_malloc(sizeof(grpc_tcp_listener)));
sp->next = nullptr;
if (s->head == nullptr) {
s->head = sp;
} else {
s->tail->next = sp;
}
s->tail = sp;
sp->server = s;
sp->fd = fd;
sp->emfd = grpc_fd_create(fd, name.c_str(), true);
memcpy(&sp->addr, addr, sizeof(grpc_resolved_address));
sp->port = port;
sp->port_index = port_index;
sp->fd_index = fd_index;
sp->is_sibling = 0;
sp->sibling = nullptr;
GPR_ASSERT(sp->emfd);
gpr_mu_unlock(&s->mu);
if (err != GRPC_ERROR_NONE) return err;
GPR_ASSERT(port > 0);
std::string addr_str = grpc_sockaddr_to_string(addr, true);
std::string name = absl::StrCat("tcp-server-listener:", addr_str);
gpr_mu_lock(&s->mu);
s->nports++;
grpc_tcp_listener* sp =
static_cast<grpc_tcp_listener*>(gpr_malloc(sizeof(grpc_tcp_listener)));
sp->next = nullptr;
if (s->head == nullptr) {
s->head = sp;
} else {
s->tail->next = sp;
}
s->tail = sp;
sp->server = s;
sp->fd = fd;
sp->emfd = grpc_fd_create(fd, name.c_str(), true);
grpc_tcp_server_listener_initialize_retry_timer(sp);
memcpy(&sp->addr, addr, sizeof(grpc_resolved_address));
sp->port = port;
sp->port_index = port_index;
sp->fd_index = fd_index;
sp->is_sibling = 0;
sp->sibling = nullptr;
GPR_ASSERT(sp->emfd);
gpr_mu_unlock(&s->mu);
*listener = sp;
return err;
}

View File

@ -22,5 +22,5 @@
#include <grpcpp/grpcpp.h>
namespace grpc {
std::string Version() { return "1.46.0-dev"; }
std::string Version() { return "1.46.7"; }
} // namespace grpc

View File

@ -89,6 +89,11 @@ namespace Grpc.Core
/// <summary>
/// Returns <c>true</c> if this credential type allows being composed by <c>CompositeCredentials</c>.
/// </summary>
/// <remark>
/// Note: No longer used. Decision on whether composition is allowed now happens in
/// <see cref="ChannelCredentialsConfiguratorBase.SetCompositeCredentials(object, ChannelCredentials, CallCredentials)"/>.
/// Internal property left for safety because Grpc.Core has internal access to Grpc.Core.Api.
/// </remark>
internal virtual bool IsComposable => false;
private sealed class InsecureCredentials : ChannelCredentials
@ -118,11 +123,6 @@ namespace Grpc.Core
{
this.channelCredentials = GrpcPreconditions.CheckNotNull(channelCredentials);
this.callCredentials = GrpcPreconditions.CheckNotNull(callCredentials);
if (!channelCredentials.IsComposable)
{
throw new ArgumentException(string.Format("CallCredentials can't be composed with {0}. CallCredentials must be used with secure channel credentials like SslCredentials.", channelCredentials.GetType().Name));
}
}
public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)

View File

@ -33,11 +33,11 @@ namespace Grpc.Core
/// <summary>
/// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies
/// </summary>
public const string CurrentAssemblyFileVersion = "2.46.0.0";
public const string CurrentAssemblyFileVersion = "2.46.7.0";
/// <summary>
/// Current version of gRPC C#
/// </summary>
public const string CurrentVersion = "2.46.0-dev";
public const string CurrentVersion = "2.46.7";
}
}

View File

@ -179,6 +179,220 @@ namespace Grpc.Core.Tests
}
}
[Test]
public async Task ServerStreamingCall_DisposeCallBeforeAllRead()
{
// Test to reproduce https://github.com/grpc/grpc/issues/8451
// "Client channel socket leaks unless read stream drained explicitly"
// This version of the test disposes the call before the response stream is
// fully read.
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) => {
try
{
// send responses to the client with a short delay between each response
foreach (string resp in request.Split(new[] { ' ' }))
{
await responseStream.WriteAsync(resp);
await Task.Delay(50);
}
}
catch (RpcException)
{
// don't care about server side exceptions in this test
}
});
// The 'using' statement makes sure that Dispose() is called at the end
// which is the point of the test - to make sure resources aren't leaked
// when Dispose() is called.
using (var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "A B C D E"))
{
Assert.AreEqual(1, channel.GetCallReferenceCount());
try
{
// Just call MoveNext once rather than consuming the entire stream
// to check that resources are released when the entire stream hasn't
// been read
await call.ResponseStream.MoveNext();
Assert.AreEqual("A", call.ResponseStream.Current);
}
catch (RpcException ex)
{
Assert.Fail("Unexpected exception thrown {}", ex);
}
} // Dispose happens here
// short delay to allow callbacks to fire
await Task.Delay(500);
// Resources for the call should have been cleaned up and thus no reference
// to the call in the channel. This would fail with a count of 1 before the
// fix for #8451.
Assert.AreEqual(0, channel.GetCallReferenceCount());
}
[Test]
public async Task ServerStreamingCall_DisposeCallBeforeAnyRead()
{
// Test to reproduce https://github.com/grpc/grpc/issues/8451
// "Client channel socket leaks unless read stream drained explicitly"
// This version of the test disposes the call before any of the response stream is
// read.
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) => {
try
{
// send responses to the client with a short delay between each response
foreach (string resp in request.Split(new[] { ' ' }))
{
await responseStream.WriteAsync(resp);
await Task.Delay(50);
}
}
catch (RpcException)
{
// don't care about server side exceptions in this test
}
});
// The 'using' statement makes sure that Dispose() is called at the end
// which is the point of the test - to make sure resources aren't leaked
// when Dispose() is called.
using (var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "A B C D E"))
{
Assert.AreEqual(1, channel.GetCallReferenceCount());
// don't read the response steam
} // Dispose happens here
// short delay to allow callbacks to fire
await Task.Delay(500);
// Resources for the call should have been cleaned up and thus no reference
// to the call in the channel. This would fail with a count of 1 before the
// fix for #8451.
Assert.AreEqual(0, channel.GetCallReferenceCount());
}
[Test]
public async Task ServerStreamingCall_CancelCallBeforeAllRead()
{
// Test to reproduce https://github.com/grpc/grpc/issues/8451
// "Client channel socket leaks unless read stream drained explicitly"
// This version of the test cancels the call before the response stream is
// fully read.
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) => {
try
{
// send responses to the client with a short delay between each response
foreach (string resp in request.Split(new[] { ' ' }))
{
await responseStream.WriteAsync(resp);
await Task.Delay(50);
}
}
catch (RpcException)
{
// don't care about server side exceptions in this test
}
});
var tokenSource = new CancellationTokenSource();
var call = Calls.AsyncServerStreamingCall(
helper.CreateServerStreamingCall(new CallOptions(cancellationToken: tokenSource.Token)),
"A B C D E");
Assert.AreEqual(1, channel.GetCallReferenceCount());
try
{
await call.ResponseStream.MoveNext();
Assert.AreEqual("A", call.ResponseStream.Current);
// To reproduce issue #8451 - cancel the call and don't
// read the response stream to the end
tokenSource.Cancel();
}
catch (RpcException ex)
{
Assert.Fail("Unexpected exception thrown {}", ex);
}
// short delay to allow callbacks to fire
await Task.Delay(500);
// Note: this version of the test does not explicitly Dispose the call. It
// is assuming the cancel of the call triggers the cleanup.
// Resources for the call should have been cleaned up and thus no reference
// to the call in the channel. This would fail with a count of 1 before the
// fix for #8451.
Assert.AreEqual(0, channel.GetCallReferenceCount());
}
[Test]
public async Task ServerStreamingCall_CancelCallBeforeAllReadAndDispose()
{
// Test to reproduce https://github.com/grpc/grpc/issues/8451
// "Client channel socket leaks unless read stream drained explicitly"
// This version of the test cancels the call before the response stream is
// fully read. It also calls Dispose on the call after the Cancel to check that
// the cleanup doesn't happen twice
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) => {
try
{
// send responses to the client with a short delay between each response
foreach (string resp in request.Split(new[] { ' ' }))
{
await responseStream.WriteAsync(resp);
await Task.Delay(50);
}
}
catch (RpcException)
{
// don't care about server side exceptions in this test
}
});
var tokenSource = new CancellationTokenSource();
// 'using' makes sure Dispose happens
using (var call = Calls.AsyncServerStreamingCall(
helper.CreateServerStreamingCall(new CallOptions(cancellationToken: tokenSource.Token)),
"A B C D E"))
{
Assert.AreEqual(1, channel.GetCallReferenceCount());
try
{
await call.ResponseStream.MoveNext();
Assert.AreEqual("A", call.ResponseStream.Current);
// To reproduce issue #8451 - cancel the call and don't read
// the response stream to the end
tokenSource.Cancel();
}
catch (RpcException ex)
{
Assert.Fail("Unexpected exception thrown {}", ex);
}
} // Dispose happens here
// short delay to allow callbacks to fire
await Task.Delay(500);
// Note: this version of the test does not explicitly Dispose the call. It
// is assuming the cancel of the call triggers the cleanup.
// Resources for the call should have been cleaned up and thus no reference
// to the call in the channel. This would fail with a count of 1 before the
// fix for #8451.
Assert.AreEqual(0, channel.GetCallReferenceCount());
}
[Test]
public void CanDisposeDefaultCancellationRegistration()
{
@ -189,5 +403,43 @@ namespace Grpc.Core.Tests
using (obj) {}
}
[Test]
public async Task ServerStreamingCall_CancelAfterServerHasFinished()
{
// Test to verify the fix for https://github.com/grpc/grpc/issues/17761.
// Fix appears to have happened in c-core between tags v1.30.0 (broken) and v1.31.0-pre1 (fixed).
//
// Scenario: the client cancels the request after the server has already successfully
// returned the results but before the client starts reading.
//
// Expected behaviour is that the client sees a Cancelled status and no messages received.
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) => {
await responseStream.WriteAsync("abc");
});
var cts = new CancellationTokenSource();
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(new CallOptions(cancellationToken: cts.Token)), "request1");
// make sure the response and status sent by the server is received on the client side
await Task.Delay(2000);
// cancel the call before actually reading the response
cts.Cancel();
var moveNextTask = call.ResponseStream.MoveNext();
try
{
// cannot use Assert.ThrowsAsync because it uses Task.Wait and would deadlock.
await moveNextTask;
Assert.Fail();
}
catch (RpcException ex)
{
// expect a Cancelled status
Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
}
}
}
}

View File

@ -24,18 +24,6 @@ namespace Grpc.Core.Tests
{
public class ChannelCredentialsTest
{
[Test]
public void InsecureCredentials_IsNonComposable()
{
Assert.IsFalse(ChannelCredentials.Insecure.IsComposable);
}
[Test]
public void SecureCredentials_IsComposable()
{
Assert.IsTrue(ChannelCredentials.SecureSsl.IsComposable);
}
[Test]
public void ChannelCredentials_CreateComposite()
{
@ -44,10 +32,6 @@ namespace Grpc.Core.Tests
Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(null, new FakeCallCredentials()));
Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(new FakeChannelCredentials(true), null));
// forbid composing non-composable
var ex = Assert.Throws(typeof(ArgumentException), () => ChannelCredentials.Create(new FakeChannelCredentials(false), new FakeCallCredentials()));
Assert.AreEqual("CallCredentials can't be composed with FakeChannelCredentials. CallCredentials must be used with secure channel credentials like SslCredentials.", ex.Message);
}
[Test]
@ -63,5 +47,16 @@ namespace Grpc.Core.Tests
var nativeCreds4 = ChannelCredentials.SecureSsl.ToNativeCredentials();
Assert.AreSame(nativeCreds3, nativeCreds4);
}
[Test]
public void ChannelCredentials_MalformedSslCredentialsCanStillCreateNativeCredentials()
{
// pass malformed root pem certs, but creation of native credentials still passes,
// since the credentials are parsed lazily by the C core.
using (var nativeCreds = new SslCredentials("MALFORMED_ROOT_CERTS_THAT_WILL_THROW_WHEN_CREATING_NATIVE_CREDENTIALS").ToNativeCredentials())
{
Assert.IsFalse(nativeCreds.IsInvalid);
}
}
}
}

View File

@ -123,5 +123,16 @@ namespace Grpc.Core.Tests
// check that Channel.ShutdownAsync has run
Assert.AreEqual(ChannelState.Shutdown, channel.State);
}
[Test]
public void CompositeCredentialsWithInsecureThrow()
{
var compositeCredentials = ChannelCredentials.Create(
ChannelCredentials.Insecure,
CallCredentials.FromInterceptor((context, metadata) => TaskUtils.CompletedTask));
var ex = Assert.Throws(typeof(InvalidOperationException), () => new Channel("localhost", compositeCredentials));
Assert.AreEqual("CallCredentials can't be composed with InsecureCredentials. " +
"CallCredentials must be used with secure channel credentials like SslCredentials.", ex.Message);
}
}
}

View File

@ -472,6 +472,275 @@ namespace Grpc.Core.Internal.Tests
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask3);
}
[Test]
public void ServerStreaming_CancelCall_Issue8451_1()
{
// Test client cancelling the call before the reading the server stream to the end.
// Test for resource leak https://github.com/grpc/grpc/issues/8451
// The callbacks OnReceivedResponseHeaders, OnReceivedStatusOnClient and OnReceivedMessage may
// occur in any order together with calls to MoveNext. We need to test that the resources
// are released correctly no matter the order of the calls, and that when the call is cancelled
// or there is an error (such as server unavailable) that MoveNext throws the correct exception.
// This test is testing the order:
// OnReceivedResponseHeaders
// MoveNext
// OnReceivedMessage
// Cancel (which could be because of a Dispose or explicit cancel)
// OnReceivedStatusOnClient
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
// Check that the client has a reference to exactly one call
Assert.AreEqual(1, channel.GetCallReferenceCount());
// Check that the call is not yet disposed
Assert.IsFalse(fakeCall.IsDisposed);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
// read some data
var readTask1 = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload());
Assert.IsTrue(readTask1.Result);
Assert.AreEqual("response1", responseStream.Current);
// Cancel the call. This will trigger ReceivedStatusOnClientCallback to be called with a Cancelled status.
asyncCall.Cancel();
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultCancelled, new Metadata()));
// Check that the resources have been released and that the client no longer references the call
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void ServerStreaming_CancelCall_Issue8451_2()
{
// Test client cancelling the call before the reading the server stream to the end.
// Test for resource leak https://github.com/grpc/grpc/issues/8451
// The callbacks OnReceivedResponseHeaders, OnReceivedStatusOnClient and OnReceivedMessage may
// occur in any order together with calls to MoveNext. We need to test that the resources
// are released correctly no matter the order of the calls, and that when the call is cancelled
// or there is an error (such as server unavailable) that MoveNext throws the correct exception.
// This test is testing the order:
// Cancel (which could be because of a Dispose or explicit cancel)
// OnReceivedStatusOnClient
// OnReceivedResponseHeaders
// MoveNext - fails with Exception
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
// Check that the client has a reference to exactly one call
Assert.AreEqual(1, channel.GetCallReferenceCount());
// Check that the call is not yet disposed
Assert.IsFalse(fakeCall.IsDisposed);
// Cancel the call. This will trigger ReceivedStatusOnClientCallback to be called with a Cancelled status.
asyncCall.Cancel();
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultCancelled, new Metadata()));
// OnReceivedResponseHeaders with have success == false as call is cancelled
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(false, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
// read some data - this should fail as the call has been cancelled
var ex = Assert.ThrowsAsync<RpcException>(async () => await responseStream.MoveNext());
Assert.AreEqual(Status.DefaultCancelled, ex.Status);
// Check that the resources have been released and that the client no longer references the call
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void ServerStreaming_CancelCall_Issue8451_3()
{
// Test client cancelling the call before the reading the server stream to the end.
// Test for resource leak https://github.com/grpc/grpc/issues/8451
// The callbacks OnReceivedResponseHeaders, OnReceivedStatusOnClient and OnReceivedMessage may
// occur in any order together with calls to MoveNext. We need to test that the resources
// are released correctly no matter the order of the calls, and that when the call is cancelled
// or there is an error (such as server unavailable) that MoveNext throws the correct exception.
// This test is testing the order:
// OnReceivedResponseHeaders
// Cancel (which could be because of a Dispose or explicit cancel)
// MoveNext - OK as cancel status not yet picked up in callback
// OnReceivedMessage
// OnReceivedStatusOnClient - delayed callback from the Cancel
// MoveNext - fails with Exception
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
// Check that the client has a reference to exactly one call
Assert.AreEqual(1, channel.GetCallReferenceCount());
// Check that the call is not yet disposed
Assert.IsFalse(fakeCall.IsDisposed);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
// cancel the call, but delay the callback with the cancelled status until later
asyncCall.Cancel();
// Not yet disposed
Assert.IsFalse(fakeCall.IsDisposed);
// read some data
var readTask1 = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload());
Assert.IsTrue(readTask1.Result);
Assert.AreEqual("response1", responseStream.Current);
// Trigger ReceivedStatusOnClientCallback to be called with a Cancelled status.
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultCancelled, new Metadata()));
// read some more data - this should fail as the call has been cancelled
var ex = Assert.ThrowsAsync<RpcException>(async () => await responseStream.MoveNext());
Assert.AreEqual(Status.DefaultCancelled, ex.Status);
// Check that the resources have been released and that the client no longer references the call
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void ServerStreaming_CancelCall_Issue8451_4()
{
// Test client cancelling the call before the reading the server stream to the end.
// Test for resource leak https://github.com/grpc/grpc/issues/8451
// The callbacks OnReceivedResponseHeaders, OnReceivedStatusOnClient and OnReceivedMessage may
// occur in any order together with calls to MoveNext. We need to test that the resources
// are released correctly no matter the order of the calls, and that when the call is cancelled
// or there is an error (such as server unavailable) that MoveNext throws the correct exception.
// This case tests the case when OnReceivedStatusOnClient occurs before the OnReceivedMessage
// for data already inflight.
// This test is testing the order:
// OnReceivedResponseHeaders
// MoveNext task started
// Cancel
// OnReceivedStatusOnClient
// OnReceivedMessage
// MoveNext task completes OK
// another MoveNext - fails with Exception
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
// Check that the client has a reference to exactly one call
Assert.AreEqual(1, channel.GetCallReferenceCount());
// Check that the call is not yet disposed
Assert.IsFalse(fakeCall.IsDisposed);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
// Prime for a read - this sets the OnReceivedMessage callback handler in fakeCall
// indicating that we are waiting to receive some data
var readTask1 = responseStream.MoveNext();
// Cancel the call
asyncCall.Cancel();
// Not yet disposed
Assert.IsFalse(fakeCall.IsDisposed);
// Trigger ReceivedStatusOnClientCallback to be called with a Cancelled status.
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultCancelled, new Metadata()));
// Check not yet disposed the call as there is data inflight
Assert.IsFalse(fakeCall.IsDisposed);
// Read some data. Although we've already received this cancel status this will succeed as the
// the data was already inflight. Note: called with "success = true"
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload());
Assert.IsTrue(readTask1.Result);
Assert.AreEqual("response1", responseStream.Current);
// check has disposed the call as there is no more data inflight and we previously received an error status
Assert.IsTrue(fakeCall.IsDisposed);
// Read some more data - this should fail as the call has been cancelled
var ex = Assert.ThrowsAsync<RpcException>(async () => await responseStream.MoveNext());
Assert.AreEqual(Status.DefaultCancelled, ex.Status);
// Check that the resources have been released and that the client no longer references the call
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void ServerStreaming_CancelCall_Issue8451_5()
{
// Test client cancelling the call before the reading the server stream to the end.
// Test for resource leak https://github.com/grpc/grpc/issues/8451
// The callbacks OnReceivedResponseHeaders, OnReceivedStatusOnClient and OnReceivedMessage may
// occur in any order together with calls to MoveNext. We need to test that the resources
// are released correctly no matter the order of the calls, and that when the call is cancelled
// or there is an error (such as server unavailable) that MoveNext throws the correct exception.
// This case tests the case when OnReceivedStatusOnClient occurs before the OnReceivedMessage
// for data already inflight.
// Same as ServerStreaming_CancelCall_Issue8451_4() but with "success = false" in the
// OnReceivedMessage callback
// This test is testing the order:
// OnReceivedResponseHeaders
// MoveNext task started
// Cancel
// OnReceivedStatusOnClient
// OnReceivedMessage (with success = false)
// MoveNext task completes with error
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
// Check that the client has a reference to exactly one call
Assert.AreEqual(1, channel.GetCallReferenceCount());
// Check that the call is not yet disposed
Assert.IsFalse(fakeCall.IsDisposed);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
// Prime for a read - this sets the OnReceivedMessage callback handler in fakeCall
// indicating that we are waiting to receive some data
var readTask1 = responseStream.MoveNext();
// Cancel the call
asyncCall.Cancel();
// Not yet disposed
Assert.IsFalse(fakeCall.IsDisposed);
// Trigger ReceivedStatusOnClientCallback to be called with a Cancelled status.
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultCancelled, new Metadata()));
// Check not yet disposed the call as there is data inflight
Assert.IsFalse(fakeCall.IsDisposed);
// Read some data.
// Note: being called with "success = false" - this may occur if the native code
// completes the callback indicating that there has already been an error
fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, CreateResponsePayload());
var ex = Assert.ThrowsAsync<RpcException>(async () => await readTask1);
Assert.AreEqual(Status.DefaultCancelled, ex.Status);
// Check that the resources have been released and that the client no longer references the call
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void ServerStreaming_RequestSerializationExceptionDoesntLeakResources()
{
@ -493,6 +762,56 @@ namespace Grpc.Core.Internal.Tests
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void ServerStreaming_ServerUnavailableDoesntLeakResources1()
{
// Test for https://github.com/grpc/grpc/issues/28153
// Memory leak if resources not released when server unavailable
asyncCall.StartServerStreamingCall("request1");
// error reading response headers - possibly because server unavailable
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(false, new Metadata());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(new Status(StatusCode.Unavailable, ""), new Metadata()));
Assert.IsTrue(fakeCall.IsDisposed);
Assert.AreEqual(0, channel.GetCallReferenceCount());
}
[Test]
public void ServerStreaming_ServerUnavailableDoesntLeakResources2()
{
// Test for https://github.com/grpc/grpc/issues/28153
// Memory leak if resources not released when server unavailable
asyncCall.StartServerStreamingCall("request1");
// Test OnReceivedStatusOnClient before OnReceivedResponseHeaders
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(new Status(StatusCode.Unavailable, ""), new Metadata()));
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(false, new Metadata());
Assert.IsTrue(fakeCall.IsDisposed);
Assert.AreEqual(0, channel.GetCallReferenceCount());
}
[Test]
public void ServerStreaming_ServerUnavailableDoesntLeakResources3()
{
// Test for https://github.com/grpc/grpc/issues/28153
// Memory leak if resources not released when server unavailable
asyncCall.StartServerStreamingCall("request1");
// error reading response headers - possibly because server unavailable
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(false, new Metadata());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(new Status(StatusCode.Unavailable, ""), new Metadata()));
Assert.IsTrue(fakeCall.IsDisposed);
// Test a cancel/dispose is OK even though the call has already been disposed
asyncCall.Cancel();
Assert.IsTrue(fakeCall.IsDisposed);
Assert.AreEqual(0, channel.GetCallReferenceCount());
}
[Test]
public void DuplexStreaming_NoRequestNoResponse_Success1()
{

View File

@ -109,5 +109,55 @@ namespace Grpc.Core.Tests
// just create a server, don't start it, and make sure it doesn't prevent shutdown.
var server = new Server();
}
[Test]
public void BindPortsNoStartAndShutdownServer()
{
// Test binding ports but no Start - that the bound ports are
// released on Shutdown.
// See https://github.com/grpc/grpc/issues/23390
int boundPort = 0;
try
{
// Create server, bind ports then shutdown (without start)
Server server = new Server {
Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) }
};
// find the port chosen so we can use it later
boundPort = server.Ports.Single().BoundPort;
Assert.Greater(boundPort, 0);
server.ShutdownAsync().Wait();
}
catch (Exception e)
{
Assert.Fail($"Failed to create and shutdown first server: {e}");
}
try
{
// Create new server, bind to same port as before, start and shutdown
// Note: there is a small possibility that the port may have been reused by a different
// test being run in parallel in between the previous Shutdown and using it here
// causing this test to be flaky.
Server server2 = new Server {
Ports = { new ServerPort("localhost", boundPort, ServerCredentials.Insecure) }
};
server2.Start();
server2.ShutdownAsync().Wait();
}
catch (Exception e)
{
Assert.Fail($"Failed to create and shutdown second server: {e}");
}
}
}
}

View File

@ -69,20 +69,29 @@ namespace Grpc.Core
EnsureUserAgentChannelOption(this.options);
this.environment = GrpcEnvironment.AddRef();
this.completionQueue = this.environment.PickCompletionQueue();
using (var nativeChannelArgs = ChannelOptions.CreateChannelArgs(this.options.Values))
try
{
var nativeCredentials = credentials.ToNativeCredentials();
if (nativeCredentials != null)
this.completionQueue = this.environment.PickCompletionQueue();
using (var nativeChannelArgs = ChannelOptions.CreateChannelArgs(this.options.Values))
{
this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs);
}
else
{
this.handle = ChannelSafeHandle.CreateInsecure(target, nativeChannelArgs);
var nativeCredentials = credentials.ToNativeCredentials();
if (nativeCredentials != null)
{
this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs);
}
else
{
this.handle = ChannelSafeHandle.CreateInsecure(target, nativeChannelArgs);
}
}
GrpcEnvironment.RegisterChannel(this);
}
catch (Exception)
{
// Constructor can't be async.
_ = Task.Run(async () => await GrpcEnvironment.ReleaseAsync().ConfigureAwait(false));
throw;
}
GrpcEnvironment.RegisterChannel(this);
}
/// <summary>

View File

@ -433,6 +433,11 @@ namespace Grpc.Core.Internal
return new RpcException(finishedStatus.Value.Status, finishedStatus.Value.Trailers);
}
protected override bool IsFinishedWithNonOkStatusClientOnly
{
get { return finishedStatus.HasValue && finishedStatus.Value.Status.StatusCode != StatusCode.OK; }
}
protected override Task CheckSendAllowedOrEarlyResult()
{
var earlyResult = CheckSendPreconditionsClientSide();
@ -648,14 +653,20 @@ namespace Grpc.Core.Internal
if (status.StatusCode != StatusCode.OK)
{
streamingResponseCallFinishedTcs.SetException(new RpcException(status, receivedStatus.Trailers));
if (status.StatusCode == StatusCode.Cancelled || origCancelRequested)
{
// Make sure the exception set to the Task is observed,
// otherwise this can trigger "Unobserved exception" when the response stream
// is not read until its end and the task created by the TCS is garbage collected.
// See https://github.com/grpc/grpc/issues/17458
var _ = streamingResponseCallFinishedTcs.Task.Exception;
}
// Make sure the exception set to the Task is observed,
// otherwise this can trigger "Unobserved exception" when the response stream
// is not read until its end (by invoking MoveNext() until the point where it either returns false or throws an exception)
// and the task created by the TCS is garbage collected.
// Note that the streamingResponseCallFinishedTcs.Task itself is never exposed to the user
// and its result only ever "observed" by MoveNext() implementation:
// https://github.com/grpc/grpc/blob/1cd6e69347cbf62a012477fe184ee6fa8f25d32c/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs#L59.
// Even though generally the response stream should always be read until the end (since that's how you get streaming call's responses and status code)
// there are cases where this shouldn't be strictly required and thus the user
// shouldn't need to worry about observing result of an internal task that's
// only exposed to them indirectly.
// See https://github.com/grpc/grpc/issues/17458
// See https://github.com/grpc/grpc/issues/24421
var _ = streamingResponseCallFinishedTcs.Task.Exception;
return;
}

View File

@ -154,6 +154,22 @@ namespace Grpc.Core.Internal
return streamingReadTcs.Task;
}
if (IsFinishedWithNonOkStatusClientOnly)
{
// The gRPC call has already failed with an error.
if (streamingReadTcs == null)
{
// Return an empty result rather than starting a new read.
// MoveNext will interpret this as end of stream and error handling will
// happen as expected.
streamingReadTcs = new TaskCompletionSource<TRead>();
streamingReadTcs.SetResult(default(TRead));
}
return streamingReadTcs.Task;
}
GrpcPreconditions.CheckState(streamingReadTcs == null, "Only one read can be pending at a time");
GrpcPreconditions.CheckState(!disposed);
@ -166,13 +182,21 @@ namespace Grpc.Core.Internal
/// <summary>
/// If there are no more pending actions and no new actions can be started, releases
/// the underlying native resources.
/// Only call when myLock is held.
/// </summary>
protected bool ReleaseResourcesIfPossible()
{
if (!disposed && call != null)
{
// Have sends completed?
bool noMoreSendCompletions = streamingWriteTcs == null && (halfcloseRequested || cancelRequested || finished);
if (noMoreSendCompletions && readingDone && finished && !receiveResponseHeadersPending)
// Have reads completed (normally or with error)?
// Not sure if the check for "streamingReadTcs == null" is necessary (indicating no pending reads) if an error status
// has already been received since I don't think a new read would be started.
bool readingFinished = readingDone || (IsFinishedWithNonOkStatusClientOnly && streamingReadTcs == null);
if (noMoreSendCompletions && readingFinished && finished && !receiveResponseHeadersPending)
{
ReleaseResources();
return true;
@ -192,6 +216,17 @@ namespace Grpc.Core.Internal
/// </summary>
protected abstract Exception GetRpcExceptionClientOnly();
/// <summary>
/// Client - returns true if the client has received an error status in the
/// OnReceivedStatusOnClient callback.
/// Server - always returns false as this is a client side method.
/// Only access when myLock is held.
/// </summary>
protected abstract bool IsFinishedWithNonOkStatusClientOnly
{
get;
}
protected void ReleaseResources()
{
if (call != null)

View File

@ -186,6 +186,10 @@ namespace Grpc.Core.Internal
{
throw new InvalidOperationException("Call be only called for client calls");
}
protected override bool IsFinishedWithNonOkStatusClientOnly
{
get { return false; }
}
protected override void OnAfterReleaseResourcesLocked()
{

View File

@ -17,6 +17,7 @@
#endregion
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Grpc.Core;
@ -91,7 +92,7 @@ namespace Grpc.Core.Internal
{
UIntPtr detailsLength;
IntPtr detailsPtr = Native.grpcsharp_batch_context_recv_status_on_client_details(this, out detailsLength);
string details = MarshalUtils.PtrToStringUTF8(detailsPtr, (int)detailsLength.ToUInt32());
string details = StatusDetailsPtrToStringUTF8(detailsPtr, (int)detailsLength.ToUInt32());
string debugErrorString = Marshal.PtrToStringAnsi(Native.grpcsharp_batch_context_recv_status_on_client_error_string(this));
var status = new Status(Native.grpcsharp_batch_context_recv_status_on_client_status(this), details, debugErrorString != null ? new CoreErrorDetailException(debugErrorString) : null);
@ -136,6 +137,27 @@ namespace Grpc.Core.Internal
return true;
}
// This method decodes the status details string "safely" and returns an empty
// string in case it seems corrupt.
// This is to stop UnaryCall from hanging if there is an issue with decoding
// the UTF8 status details string (likely due to an unexplained corruption).
// See https://github.com/grpc/grpc/issues/29854
// TODO(jtattermusch): remove this workaround once #29854 is fully explained
// and fixed.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private string StatusDetailsPtrToStringUTF8(IntPtr ptr, int len)
{
try
{
return MarshalUtils.PtrToStringUTF8(ptr, len);
}
catch (Exception e) {
Logger.Error(e, "Failed to decode status details string for a call (corrupt UTF8 string?). An empty status details string will be returned by the client.");
// return an empty status (the status code will be preserved)
return "";
}
}
void IOpCompletionCallback.OnComplete(bool success)
{
try

View File

@ -82,7 +82,15 @@ namespace Grpc.Core.Internal
{
using (var callCreds = callCredentials.ToNativeCredentials())
{
var nativeComposite = ChannelCredentialsSafeHandle.CreateComposite(channelCredentials.ToNativeCredentials(), callCreds);
var nativeChannelCredentials = channelCredentials.ToNativeCredentials();
// nativeChannelCredentials == null means insecure channel credentials were used.
if (nativeChannelCredentials == null)
{
throw new InvalidOperationException($"CallCredentials can't be composed with {channelCredentials.GetType().Name}. " +
$"CallCredentials must be used with secure channel credentials like SslCredentials.");
}
var nativeComposite = ChannelCredentialsSafeHandle.CreateComposite(nativeChannelCredentials, callCreds);
if (nativeComposite.IsInvalid)
{
throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");

View File

@ -220,8 +220,26 @@ namespace Grpc.Core
lock (myLock)
{
GrpcPreconditions.CheckState(!shutdownRequested);
if (!startRequested)
{
Logger.Warning("Shutdown called without Start being called.");
if (serverPortList.Count > 0)
{
// Ports have been added. They will already be bound and listening.
// Need to call Start so that the grpc core native library is in the
// correct state when shutdown is called. Otherwise the listening ports
// will not be released. See https://github.com/grpc/grpc/issues/23390
// lock is re-entrant so OK to call Start() while holding the lock.
Start();
}
}
shutdownRequested = true;
}
GrpcEnvironment.UnregisterServer(this);
var cq = environment.CompletionQueues.First(); // any cq will do
@ -381,9 +399,9 @@ namespace Grpc.Core
// Start asynchronous handler for the call.
// Don't await, the continuations will run on gRPC thread pool once triggered
// by cq.Next().
#pragma warning disable 4014
#pragma warning disable 4014
HandleCallAsync(newRpc, cq, (server, state) => server.AllowOneRpc(state));
#pragma warning restore 4014
#pragma warning restore 4014
}
}

View File

@ -96,5 +96,41 @@ namespace Grpc.IntegrationTesting
Assert.AreEqual(0, unobservedTaskExceptionCounter.Count);
}
[Test]
public async Task NoUnobservedTaskExceptionForUnavailableServers()
{
// Verify that https://github.com/grpc/grpc/issues/24421 has been fixed.
var unobservedTaskExceptionCounter = new AtomicCounter();
TaskScheduler.UnobservedTaskException += (sender, e) => {
unobservedTaskExceptionCounter.Increment();
Console.WriteLine("Detected unobserved task exception: " + e.Exception);
};
var channelToNonexistentBackend = new Channel("nonexistentbackend:100", ChannelCredentials.Insecure);
var client = new TestService.TestServiceClient(channelToNonexistentBackend);
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Starting iteration {i}");
try
{
var call = client.FullDuplexCall();
using (call)
{
var req = call.RequestStream;
// try to write a request (which will throw RpcException) and intentionally
// skip calling call.ReponseStream.MoveNext()
await call.RequestStream.WriteAsync(new StreamingOutputCallRequest());
}
}
catch (RpcException)
{
// eat exception, we expect a call to non-existent backend to fail.
}
// Make it more likely to trigger the "Unobserved task exception" warning
GC.Collect();
}
await channelToNonexistentBackend.ShutdownAsync();
Assert.AreEqual(0, unobservedTaskExceptionCounter.Count);
}
}
}

View File

@ -1,7 +1,7 @@
<!-- This file is generated -->
<Project>
<PropertyGroup>
<GrpcCsharpVersion>2.46.0-dev</GrpcCsharpVersion>
<GoogleProtobufVersion>3.19.4</GoogleProtobufVersion>
<GrpcCsharpVersion>2.46.7</GrpcCsharpVersion>
<GoogleProtobufVersion>3.19.5</GoogleProtobufVersion>
</PropertyGroup>
</Project>

View File

@ -42,7 +42,7 @@ Pod::Spec.new do |s|
# exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
# before them.
s.name = '!ProtoCompiler-gRPCCppPlugin'
v = '1.46.0-dev'
v = '1.46.7'
s.version = v
s.summary = 'The gRPC ProtoC plugin generates C++ files from .proto services.'
s.description = <<-DESC
@ -100,7 +100,7 @@ Pod::Spec.new do |s|
s.preserve_paths = plugin
# Restrict the protoc version to the one supported by this plugin.
s.dependency '!ProtoCompiler', '3.19.4'
s.dependency '!ProtoCompiler', '3.19.5'
# For the Protobuf dependency not to complain:
s.ios.deployment_target = '9.0'
s.osx.deployment_target = '10.10'

View File

@ -42,7 +42,7 @@ Pod::Spec.new do |s|
# exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
# before them.
s.name = '!ProtoCompiler-gRPCPlugin'
v = '1.46.0-dev'
v = '1.46.7'
s.version = v
s.summary = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.'
s.description = <<-DESC
@ -102,7 +102,7 @@ Pod::Spec.new do |s|
s.preserve_paths = plugin
# Restrict the protoc version to the one supported by this plugin.
s.dependency '!ProtoCompiler', '3.19.4'
s.dependency '!ProtoCompiler', '3.19.5'
# For the Protobuf dependency not to complain:
s.ios.deployment_target = '9.0'
s.osx.deployment_target = '10.10'

View File

@ -36,7 +36,7 @@ Pod::Spec.new do |s|
# exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
# before them.
s.name = '!ProtoCompiler'
v = '3.19.4'
v = '3.19.5'
s.version = v
s.summary = 'The Protobuf Compiler (protoc) generates Objective-C files from .proto files'
s.description = <<-DESC

File diff suppressed because it is too large Load Diff

View File

@ -22,4 +22,4 @@
// instead. This file can be regenerated from the template by running
// `tools/buildgen/generate_projects.sh`.
#define GRPC_OBJC_VERSION_STRING @"1.46.0-dev"
#define GRPC_OBJC_VERSION_STRING @"1.46.7"

View File

@ -22,5 +22,5 @@
// instead. This file can be regenerated from the template by running
// `tools/buildgen/generate_projects.sh`.
#define GRPC_OBJC_VERSION_STRING @"1.46.0-dev"
#define GRPC_OBJC_VERSION_STRING @"1.46.7"
#define GRPC_C_VERSION_STRING @"24.0.0"

View File

@ -2,7 +2,7 @@
"name": "grpc/grpc-dev",
"description": "gRPC library for PHP - for Development use only",
"license": "Apache-2.0",
"version": "1.46.0",
"version": "1.46.7",
"require": {
"php": ">=7.0.0",
"google/protobuf": "^v3.3.0"

View File

@ -20,6 +20,6 @@
#ifndef VERSION_H
#define VERSION_H
#define PHP_GRPC_VERSION "1.46.0dev"
#define PHP_GRPC_VERSION "1.46.7"
#endif /* VERSION_H */

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc/_grpcio_metadata.py.template`!!!
__version__ = """1.46.0.dev0"""
__version__ = """1.46.7"""

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!!
VERSION = '1.46.0.dev0'
VERSION = '1.46.7'

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_admin/grpc_version.py.template`!!!
VERSION = '1.46.0.dev0'
VERSION = '1.46.7'

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_channelz/grpc_version.py.template`!!!
VERSION = '1.46.0.dev0'
VERSION = '1.46.7'

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_csds/grpc_version.py.template`!!!
VERSION = '1.46.0.dev0'
VERSION = '1.46.7'

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!!
VERSION = '1.46.0.dev0'
VERSION = '1.46.7'

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_reflection/grpc_version.py.template`!!!
VERSION = '1.46.0.dev0'
VERSION = '1.46.7'

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_status/grpc_version.py.template`!!!
VERSION = '1.46.0.dev0'
VERSION = '1.46.7'

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_testing/grpc_version.py.template`!!!
VERSION = '1.46.0.dev0'
VERSION = '1.46.7'

View File

@ -14,4 +14,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!!
VERSION = '1.46.0.dev0'
VERSION = '1.46.7'

View File

@ -60,7 +60,7 @@ end
ENV['CPPFLAGS'] = '-DGPR_BACKWARDS_COMPATIBILITY_MODE'
ENV['CPPFLAGS'] += ' -DGRPC_XDS_USER_AGENT_NAME_SUFFIX="\"RUBY\"" '
ENV['CPPFLAGS'] += ' -DGRPC_XDS_USER_AGENT_VERSION_SUFFIX="\"1.46.0.dev\"" '
ENV['CPPFLAGS'] += ' -DGRPC_XDS_USER_AGENT_VERSION_SUFFIX="\"1.46.7\"" '
output_dir = File.expand_path(RbConfig::CONFIG['topdir'])
grpc_lib_dir = File.join(output_dir, 'libs', grpc_config)

View File

@ -16,7 +16,7 @@ begin
ruby_version_dirname = /(\d+\.\d+)/.match(RUBY_VERSION).to_s
distrib_lib_dir = File.expand_path(ruby_version_dirname,
File.dirname(__FILE__))
if File.directory?(distrib_lib_dir)
if !Dir.glob("#{distrib_lib_dir}/grpc_c*").empty?
require "#{distrib_lib_dir}/grpc_c"
else
require 'grpc/grpc_c'

View File

@ -14,5 +14,5 @@
# GRPC contains the General RPC module.
module GRPC
VERSION = '1.46.0.dev'
VERSION = '1.46.7'
end

View File

@ -14,6 +14,6 @@
module GRPC
module Tools
VERSION = '1.46.0.dev'
VERSION = '1.46.7'
end
end

View File

@ -299,6 +299,8 @@
set(_gRPC_C_CXX_FLAGS "<%text>${_gRPC_C_CXX_FLAGS}</%text> /wd4987 /wd4774 /wd4819 /wd4996 /wd4619")
# Silences thousands of trucation warnings
set(_gRPC_C_CXX_FLAGS "<%text>${_gRPC_C_CXX_FLAGS}</%text> /wd4503")
# Tell MSVC to build grpc using utf-8
set(_gRPC_C_CXX_FLAGS "<%text>${_gRPC_C_CXX_FLAGS}</%text> /utf-8")
endif()
if (MINGW)
add_definitions(-D_WIN32_WINNT=0x600)

View File

@ -195,7 +195,7 @@
ss.header_mappings_dir = '.'
ss.libraries = 'z'
ss.dependency "#{s.name}/Interface", version
ss.dependency 'BoringSSL-GRPC', '0.0.24'
ss.dependency 'BoringSSL-GRPC', '0.0.25'
ss.dependency 'Libuv-gRPC', '0.0.10'
% for abseil_spec in grpc_abseil_specs:
ss.dependency '${abseil_spec}', abseil_version

View File

@ -21,15 +21,13 @@
s.files += %w( etc/roots.pem )
s.files += Dir.glob('src/ruby/bin/**/*')
s.files += Dir.glob('src/ruby/ext/**/*')
s.files += Dir.glob('src/ruby/lib/**/*').reject do |f|
# Binaries are included by rake-compiler and would lead to circular dependencies here
File.fnmatch("**/?.?/grpc_c.so", f)
end
s.files += Dir.glob('src/ruby/lib/**/*')
s.files += Dir.glob('src/ruby/pb/**/*').reject do |f|
f.match(%r{^src/ruby/pb/test})
end
s.files += Dir.glob('include/grpc/**/*')
s.test_files = Dir.glob('src/ruby/spec/**/*')
s.test_files += Dir.glob('src/ruby/pb/test/**/*')
s.bindir = 'src/ruby/bin'
s.require_paths = %w( src/ruby/lib src/ruby/bin src/ruby/pb )
s.platform = Gem::Platform::RUBY

View File

@ -69,7 +69,7 @@
Pod::Spec.new do |s|
s.name = 'BoringSSL-GRPC'
version = '0.0.24'
version = '0.0.25'
s.version = version
s.summary = 'BoringSSL is a fork of OpenSSL that is designed to meet Google\'s needs.'
# Adapted from the homepage:

View File

@ -0,0 +1,56 @@
%YAML 1.2
--- |
# Copyright 2021 The 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.
FROM debian:stretch
<%include file="../../apt_get_basic.include"/>
<%include file="../../run_tests_python_deps.include"/>
<%include file="../../cmake.include"/>
<%include file="../../run_tests_addons.include"/>
# Java required by Android SDK
RUN apt-get update && apt-get -y install openjdk-8-jdk && apt-get clean
# Install Android SDK
ENV ANDROID_SDK_VERSION 4333796
RUN mkdir -p /opt/android-sdk && cd /opt/android-sdk && ${'\\'}
wget -q https://dl.google.com/android/repository/sdk-tools-linux-$ANDROID_SDK_VERSION.zip && ${'\\'}
unzip -q sdk-tools-linux-$ANDROID_SDK_VERSION.zip && ${'\\'}
rm sdk-tools-linux-$ANDROID_SDK_VERSION.zip
ENV ANDROID_SDK_PATH /opt/android-sdk
ENV ANDROID_HOME /opt/android-sdk
RUN yes | $ANDROID_SDK_PATH/tools/bin/sdkmanager --licenses # accept all licenses
# Install ndk-bundle and cmake using sdkmanager
RUN mkdir -p ~/.android && touch ~/.android/repositories.cfg
RUN $ANDROID_SDK_PATH/tools/bin/sdkmanager ndk-bundle 'cmake;3.6.4111459'
# Install Android NDK
RUN curl -L -o android-ndk.zip http://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip && ${'\\'}
unzip -q android-ndk.zip -d /opt && ${'\\'}
rm -f android-ndk.zip
ENV ANDROID_NDK_PATH /opt/android-ndk-r16b
ENV ANDROID_NDK_HOME /opt/android-ndk-r16b
# Install gcloud
RUN curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-407.0.0-linux-x86_64.tar.gz && ${'\\'}
tar -xf google-cloud-cli-407.0.0-linux-x86_64.tar.gz && ${'\\'}
./google-cloud-sdk/install.sh --bash-completion=false --path-update=true && ${'\\'}
rm -rf google-cloud-cli-407.0.0-linux-x86_64.tar.gz
RUN ln -s /google-cloud-sdk/bin/gcloud /usr/local/bin/gcloud
# Define the default command.
CMD ["bash"]

View File

@ -605,8 +605,7 @@ TEST_F(ClientLbEnd2endTest, PickFirstBackOffMinReconnect) {
response_generator.SetNextResolution(ports);
// Make connection delay a 10% longer than it's willing to in order to make
// sure we are hitting the codepath that waits for the min reconnect backoff.
ConnectionDelayInjector delay_injector;
auto injected_delay = delay_injector.SetDelay(
ConnectionDelayInjector delay_injector(
grpc_core::Duration::Milliseconds(kMinReconnectBackOffMs * 1.10));
const gpr_timespec t0 = gpr_now(GPR_CLOCK_MONOTONIC);
channel->WaitForConnected(
@ -2128,6 +2127,7 @@ TEST_F(ClientLbAddressTest, Basic) {
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(&argc, argv);
grpc::testing::ConnectionAttemptInjector::Init();
const auto result = RUN_ALL_TESTS();
return result;
}

View File

@ -20,96 +20,98 @@
#include "absl/memory/memory.h"
#include "absl/utility/utility.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/iomgr/tcp_client.h"
#include "src/core/lib/iomgr/timer.h"
// defined in tcp_client.cc
extern grpc_tcp_client_vtable* grpc_tcp_client_impl;
namespace grpc {
namespace testing {
//
// ConnectionAttemptInjector
//
namespace {
grpc_tcp_client_vtable* g_original_vtable = nullptr;
std::atomic<grpc_core::Duration> g_delay;
class InjectedDelay {
public:
InjectedDelay(grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline)
: closure_(closure),
endpoint_(ep),
interested_parties_(interested_parties),
channel_args_(grpc_channel_args_copy(channel_args)),
deadline_(deadline) {
memcpy(&address_, addr, sizeof(grpc_resolved_address));
GRPC_CLOSURE_INIT(&timer_callback_, TimerCallback, this, nullptr);
grpc_core::Duration duration = g_delay.load();
grpc_core::Timestamp now = grpc_core::ExecCtx::Get()->Now();
duration = std::min(duration, deadline_ - now);
grpc_timer_init(&timer_, now + duration, &timer_callback_);
}
~InjectedDelay() { grpc_channel_args_destroy(channel_args_); }
private:
static void TimerCallback(void* arg, grpc_error_handle /*error*/) {
auto* self = static_cast<InjectedDelay*>(arg);
g_original_vtable->connect(self->closure_, self->endpoint_,
self->interested_parties_, self->channel_args_,
&self->address_, self->deadline_);
delete self;
}
grpc_timer timer_;
grpc_closure timer_callback_;
// Original args.
grpc_closure* closure_;
grpc_endpoint** endpoint_;
grpc_pollset_set* interested_parties_;
const grpc_channel_args* channel_args_;
grpc_resolved_address address_;
grpc_core::Timestamp deadline_;
};
std::atomic<ConnectionAttemptInjector*> g_injector{nullptr};
void TcpConnectWithDelay(grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline) {
new InjectedDelay(closure, ep, interested_parties, channel_args, addr,
deadline);
ConnectionAttemptInjector* injector = g_injector.load();
if (injector == nullptr) {
g_original_vtable->connect(closure, ep, interested_parties, channel_args,
addr, deadline);
return;
}
injector->HandleConnection(closure, ep, interested_parties, channel_args,
addr, deadline);
}
grpc_tcp_client_vtable kDelayedConnectVTable = {TcpConnectWithDelay};
} // namespace
ConnectionDelayInjector::InjectedDelay::~InjectedDelay() {
g_delay.store(grpc_core::Duration());
void ConnectionAttemptInjector::Init() {
g_original_vtable = grpc_tcp_client_impl;
grpc_tcp_client_impl = &kDelayedConnectVTable;
}
ConnectionDelayInjector::ConnectionDelayInjector() {
g_original_vtable =
absl::exchange(grpc_tcp_client_impl, &kDelayedConnectVTable);
ConnectionAttemptInjector::ConnectionAttemptInjector() {
GPR_ASSERT(g_injector.exchange(this) == nullptr);
}
ConnectionDelayInjector::~ConnectionDelayInjector() {
grpc_tcp_client_impl = g_original_vtable;
ConnectionAttemptInjector::~ConnectionAttemptInjector() {
g_injector.store(nullptr);
}
std::unique_ptr<ConnectionDelayInjector::InjectedDelay>
ConnectionDelayInjector::SetDelay(grpc_core::Duration duration) {
GPR_ASSERT(g_delay.exchange(duration) == grpc_core::Duration());
return absl::make_unique<ConnectionDelayInjector::InjectedDelay>();
void ConnectionAttemptInjector::AttemptConnection(
grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args,
const grpc_resolved_address* addr, grpc_core::Timestamp deadline) {
g_original_vtable->connect(closure, ep, interested_parties, channel_args,
addr, deadline);
}
//
// ConnectionDelayInjector
//
class ConnectionDelayInjector::InjectedDelay {
public:
InjectedDelay(grpc_core::Duration duration, grpc_closure* closure,
grpc_endpoint** ep, grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline)
: attempt_(closure, ep, interested_parties, channel_args, addr,
deadline) {
GRPC_CLOSURE_INIT(&timer_callback_, TimerCallback, this, nullptr);
grpc_core::Timestamp now = grpc_core::ExecCtx::Get()->Now();
duration = std::min(duration, deadline - now);
grpc_timer_init(&timer_, now + duration, &timer_callback_);
}
private:
static void TimerCallback(void* arg, grpc_error_handle /*error*/) {
auto* self = static_cast<InjectedDelay*>(arg);
self->attempt_.Resume();
delete self;
}
QueuedAttempt attempt_;
grpc_timer timer_;
grpc_closure timer_callback_;
};
void ConnectionDelayInjector::HandleConnection(
grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args,
const grpc_resolved_address* addr, grpc_core::Timestamp deadline) {
new InjectedDelay(duration_, closure, ep, interested_parties, channel_args,
addr, deadline);
}
} // namespace testing

View File

@ -17,7 +17,10 @@
#include <memory>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/iomgr/tcp_client.h"
#include "src/core/lib/iomgr/timer.h"
namespace grpc {
namespace testing {
@ -25,31 +28,104 @@ namespace testing {
// Allows injecting connection-establishment delays into C-core.
// Typical usage:
//
// ConnectionDelayInjector delay_injector;
// auto scoped_delay =
// delay_injector.SetDelay(grpc_core::Duration::Seconds(10));
// // At grpc_init() time.
// ConnectionAttemptInjector::Init();
//
// When ConnectionDelayInjector is instantiated, it replaces the iomgr
// TCP client vtable, and it sets it back to the original value when it
// is destroyed.
// // When an injection is desired.
// ConnectionDelayInjector delay_injector(grpc_core::Duration::Seconds(10));
//
// When SetDelay() is called, it sets the global delay, which will
// automatically be unset when the result goes out of scope.
//
// The injection is global, so there must be only one ConnectionDelayInjector
// object at any one time, and there must be only one scoped delay in effect
// at any one time.
class ConnectionDelayInjector {
// The injection is global, so there must be only one ConnectionAttemptInjector
// object at any one time.
class ConnectionAttemptInjector {
public:
class InjectedDelay {
// Global initializer. Replaces the iomgr TCP client vtable.
// Must be called exactly once before any TCP connections are established.
static void Init();
ConnectionAttemptInjector();
virtual ~ConnectionAttemptInjector();
// Invoked for every TCP connection attempt.
// Implementations must eventually either invoke the closure
// themselves or delegate to the iomgr implementation by calling
// AttemptConnection(). QueuedAttempt may be used to queue an attempt
// for asynchronous processing.
virtual void HandleConnection(grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline) = 0;
protected:
// Represents a queued attempt.
// The caller must invoke either Resume() or Fail() before destroying.
class QueuedAttempt {
public:
~InjectedDelay();
QueuedAttempt(grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline)
: closure_(closure),
endpoint_(ep),
interested_parties_(interested_parties),
channel_args_(grpc_channel_args_copy(channel_args)),
deadline_(deadline) {
memcpy(&address_, addr, sizeof(address_));
}
~QueuedAttempt() {
GPR_ASSERT(closure_ == nullptr);
grpc_channel_args_destroy(channel_args_);
}
// Caller must invoke this from a thread with an ExecCtx.
void Resume() {
GPR_ASSERT(closure_ != nullptr);
AttemptConnection(closure_, endpoint_, interested_parties_, channel_args_,
&address_, deadline_);
closure_ = nullptr;
}
// Caller must invoke this from a thread with an ExecCtx.
void Fail(grpc_error_handle error) {
GPR_ASSERT(closure_ != nullptr);
grpc_core::ExecCtx::Run(DEBUG_LOCATION, closure_, error);
closure_ = nullptr;
}
private:
grpc_closure* closure_;
grpc_endpoint** endpoint_;
grpc_pollset_set* interested_parties_;
const grpc_channel_args* channel_args_;
grpc_resolved_address address_;
grpc_core::Timestamp deadline_;
};
ConnectionDelayInjector();
~ConnectionDelayInjector();
static void AttemptConnection(grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline);
};
std::unique_ptr<InjectedDelay> SetDelay(grpc_core::Duration duration);
// A concrete implementation that injects a fixed delay.
class ConnectionDelayInjector : public ConnectionAttemptInjector {
public:
explicit ConnectionDelayInjector(grpc_core::Duration duration)
: duration_(duration) {}
void HandleConnection(grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline) override;
private:
class InjectedDelay;
grpc_core::Duration duration_;
};
} // namespace testing

View File

@ -133,6 +133,7 @@ grpc_cc_test(
"//src/proto/grpc/testing/xds/v3:router_proto",
"//src/proto/grpc/testing/xds/v3:tls_proto",
"//test/core/util:grpc_test_util",
"//test/cpp/end2end:connection_delay_injector",
"//test/cpp/end2end:rls_server",
"//test/cpp/util:test_config",
"//test/cpp/util:test_util",

View File

@ -65,6 +65,7 @@
#include "src/core/ext/xds/xds_client.h"
#include "src/core/ext/xds/xds_listener.h"
#include "src/core/lib/address_utils/parse_address.h"
#include "src/core/lib/address_utils/sockaddr_utils.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/env.h"
#include "src/core/lib/gpr/string.h"
@ -104,6 +105,7 @@
#include "src/proto/grpc/testing/xds/v3/tls.grpc.pb.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
#include "test/cpp/end2end/connection_delay_injector.h"
#include "test/cpp/end2end/rls_server.h"
#include "test/cpp/end2end/xds/xds_end2end_test_lib.h"
#include "test/cpp/util/test_config.h"
@ -1239,6 +1241,42 @@ TEST_P(XdsFederationTest, FederationServer) {
WaitForAllBackends();
}
using XdsFederationDisabledTest = XdsEnd2endTest;
TEST_P(XdsFederationDisabledTest, FederationDisabledWithNewStyleNames) {
const char* kNewRouteConfigName =
"xdstp://xds.example.com/envoy.config.route.v3.RouteConfiguration/"
"new_route_config_name";
const char* kNewClusterName =
"xdstp://xds.example.com/envoy.config.cluster.v3.Cluster/"
"cluster_name";
const char* kNewEdsResourceName =
"xdstp://xds.example.com/envoy.config.endpoint.v3.ClusterLoadAssignment/"
"edsservice_name";
InitClient();
CreateAndStartBackends(1);
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args, kNewEdsResourceName));
// New cluster
Cluster new_cluster = default_cluster_;
new_cluster.set_name(kNewClusterName);
new_cluster.mutable_eds_cluster_config()->set_service_name(
kNewEdsResourceName);
balancer_->ads_service()->SetCdsResource(new_cluster);
// New RouteConfig
RouteConfiguration new_route_config = default_route_config_;
new_route_config.set_name(kNewRouteConfigName);
new_route_config.mutable_virtual_hosts(0)
->mutable_routes(0)
->mutable_route()
->set_cluster(kNewClusterName);
SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
new_route_config);
// Channel should work.
CheckRpcSendOk();
}
using XdsFederationLoadReportingTest = XdsFederationTest;
// Channel is created with URI "xds://xds.example.com/server.example.com".
@ -4814,6 +4852,209 @@ TEST_P(CdsTest, AggregateClusterFallBackFromRingHashAtStartup) {
EXPECT_TRUE(found);
}
TEST_P(CdsTest, AggregateClusterFallBackFromRingHashToLogicalDnsAtStartup) {
ScopedExperimentalEnvVar env_var(
"GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
CreateAndStartBackends(1);
const char* kEdsClusterName = "eds_cluster";
const char* kLogicalDNSClusterName = "logical_dns_cluster";
// Populate EDS resource.
EdsResourceArgs args({
{"locality0",
{MakeNonExistantEndpoint(), MakeNonExistantEndpoint()},
kDefaultLocalityWeight,
0},
{"locality1",
{MakeNonExistantEndpoint(), MakeNonExistantEndpoint()},
kDefaultLocalityWeight,
1},
});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
// Populate new CDS resources.
Cluster eds_cluster = default_cluster_;
eds_cluster.set_name(kEdsClusterName);
balancer_->ads_service()->SetCdsResource(eds_cluster);
// Populate LOGICAL_DNS cluster.
auto logical_dns_cluster = default_cluster_;
logical_dns_cluster.set_name(kLogicalDNSClusterName);
logical_dns_cluster.set_type(Cluster::LOGICAL_DNS);
auto* address = logical_dns_cluster.mutable_load_assignment()
->add_endpoints()
->add_lb_endpoints()
->mutable_endpoint()
->mutable_address()
->mutable_socket_address();
address->set_address(kServerName);
address->set_port_value(443);
balancer_->ads_service()->SetCdsResource(logical_dns_cluster);
// Create Aggregate Cluster
auto cluster = default_cluster_;
cluster.set_lb_policy(Cluster::RING_HASH);
CustomClusterType* custom_cluster = cluster.mutable_cluster_type();
custom_cluster->set_name("envoy.clusters.aggregate");
ClusterConfig cluster_config;
cluster_config.add_clusters(kEdsClusterName);
cluster_config.add_clusters(kLogicalDNSClusterName);
custom_cluster->mutable_typed_config()->PackFrom(cluster_config);
balancer_->ads_service()->SetCdsResource(cluster);
// Set up route with channel id hashing
auto new_route_config = default_route_config_;
auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* hash_policy = route->mutable_route()->add_hash_policy();
hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
new_route_config);
// Set Logical DNS result
{
grpc_core::ExecCtx exec_ctx;
grpc_core::Resolver::Result result;
result.addresses = CreateAddressListFromPortList(GetBackendPorts());
logical_dns_cluster_resolver_response_generator_->SetResponse(
std::move(result));
}
// Inject connection delay to make this act more realistically.
ConnectionDelayInjector delay_injector(
grpc_core::Duration::Milliseconds(500) * grpc_test_slowdown_factor());
// Send RPC. Need the timeout to be long enough to account for the
// subchannel connection delays.
CheckRpcSendOk(1, RpcOptions().set_timeout_ms(3500));
}
// This test covers a bug found in the following scenario:
// 1. P0 reports TRANSIENT_FAILURE, so we start connecting to P1.
// 2. While P1 is still in CONNECTING, P0 goes back to READY, so we
// switch back to P0, deactivating P1.
// 3. P0 then goes back to TRANSIENT_FAILURE, and we reactivate P1.
// The bug caused us to fail to choose P1 even though it is in state
// CONNECTING (because the failover timer was not running), so we
// incorrectly failed the RPCs.
TEST_P(CdsTest, AggregateClusterFallBackWithConnectivityChurn) {
ScopedExperimentalEnvVar env_var(
"GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
CreateAndStartBackends(2);
const char* kClusterName1 = "cluster1";
const char* kClusterName2 = "cluster2";
const char* kEdsServiceName2 = "eds_service_name2";
// Populate EDS resources.
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends(0, 1)}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
args = EdsResourceArgs({{"locality1", CreateEndpointsForBackends(1, 2)}});
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args, kEdsServiceName2));
// Populate new CDS resources.
Cluster cluster1 = default_cluster_;
cluster1.set_name(kClusterName1);
balancer_->ads_service()->SetCdsResource(cluster1);
Cluster cluster2 = default_cluster_;
cluster2.set_name(kClusterName2);
cluster2.mutable_eds_cluster_config()->set_service_name(kEdsServiceName2);
balancer_->ads_service()->SetCdsResource(cluster2);
// Create Aggregate Cluster
auto cluster = default_cluster_;
CustomClusterType* custom_cluster = cluster.mutable_cluster_type();
custom_cluster->set_name("envoy.clusters.aggregate");
ClusterConfig cluster_config;
cluster_config.add_clusters(kClusterName1);
cluster_config.add_clusters(kClusterName2);
custom_cluster->mutable_typed_config()->PackFrom(cluster_config);
balancer_->ads_service()->SetCdsResource(cluster);
// This class injects itself into all TCP connection attempts made
// against iomgr. It intercepts the attempts for the P0 and P1
// backends and allows them to proceed as desired to simulate the case
// being tested.
class ConnectionInjector : public ConnectionAttemptInjector {
public:
ConnectionInjector(int p0_port, int p1_port)
: p0_port_(p0_port), p1_port_(p1_port) {}
void HandleConnection(grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline) override {
{
grpc_core::MutexLock lock(&mu_);
const int port = grpc_sockaddr_get_port(addr);
gpr_log(GPR_INFO, "==> HandleConnection(): state_=%d, port=%d", state_,
port);
switch (state_) {
case kInit:
// Make P0 report TF, which should trigger us to try to connect to
// P1.
if (port == p0_port_) {
gpr_log(GPR_INFO, "*** INJECTING FAILURE FOR P0 ENDPOINT");
grpc_core::ExecCtx::Run(DEBUG_LOCATION, closure,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"injected connection failure"));
state_ = kP0Failed;
return;
}
break;
case kP0Failed:
// Hold connection attempt to P1 so that it stays in CONNECTING.
if (port == p1_port_) {
gpr_log(GPR_INFO,
"*** DELAYING CONNECTION ATTEMPT FOR P1 ENDPOINT");
queued_p1_attempt_ = absl::make_unique<QueuedAttempt>(
closure, ep, interested_parties, channel_args, addr,
deadline);
state_ = kDone;
return;
}
break;
case kDone:
// P0 should attempt reconnection. Log it to make the test
// easier to debug, but allow it to complete, so that the
// priority policy deactivates P1.
if (port == p0_port_) {
gpr_log(GPR_INFO,
"*** INTERCEPTING CONNECTION ATTEMPT FOR P0 ENDPOINT");
}
break;
}
}
AttemptConnection(closure, ep, interested_parties, channel_args, addr,
deadline);
}
// Invoked by the test when the RPC to the P0 backend has succeeded
// and it's ready to allow the P1 connection attempt to proceed.
void CompletePriority1Connection() {
grpc_core::ExecCtx exec_ctx;
std::unique_ptr<QueuedAttempt> attempt;
{
grpc_core::MutexLock lock(&mu_);
GPR_ASSERT(state_ == kDone);
attempt = std::move(queued_p1_attempt_);
}
attempt->Resume();
}
private:
const int p0_port_;
const int p1_port_;
grpc_core::Mutex mu_;
enum {
kInit,
kP0Failed,
kDone,
} state_ ABSL_GUARDED_BY(mu_) = kInit;
std::unique_ptr<QueuedAttempt> queued_p1_attempt_ ABSL_GUARDED_BY(mu_);
};
ConnectionInjector connection_attempt_injector(backends_[0]->port(),
backends_[1]->port());
// Wait for P0 backend.
// Increase timeout to account for subchannel connection delays.
WaitForBackend(0, WaitForBackendOptions(), RpcOptions().set_timeout_ms(2000));
// Bring down the P0 backend.
ShutdownBackend(0);
// Allow the connection attempt to the P1 backend to resume.
connection_attempt_injector.CompletePriority1Connection();
// Wait for P1 backend to start getting traffic.
WaitForBackend(1);
}
TEST_P(CdsTest, AggregateClusterEdsToLogicalDns) {
ScopedExperimentalEnvVar env_var(
"GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
@ -5696,6 +5937,88 @@ TEST_P(CdsTest, RingHashIdleToReady) {
EXPECT_EQ(GRPC_CHANNEL_READY, channel_->GetState(false));
}
// Test that the channel will transition to READY once it starts
// connecting even if there are no RPCs being sent to the picker.
TEST_P(CdsTest, RingHashContinuesConnectingWithoutPicks) {
// Create EDS resource.
CreateAndStartBackends(1);
auto non_existant_endpoint = MakeNonExistantEndpoint();
EdsResourceArgs args(
{{"locality0", {non_existant_endpoint, CreateEndpoint(0)}}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
// Change CDS resource to use RING_HASH.
auto cluster = default_cluster_;
cluster.set_lb_policy(Cluster::RING_HASH);
balancer_->ads_service()->SetCdsResource(cluster);
// Add hash policy to RDS resource.
auto new_route_config = default_route_config_;
auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* hash_policy = route->mutable_route()->add_hash_policy();
hash_policy->mutable_header()->set_header_name("address_hash");
SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
new_route_config);
// A connection injector that cancels the RPC after seeing the
// connection attempt for the non-existant endpoint.
class ConnectionInjector : public ConnectionAttemptInjector {
public:
explicit ConnectionInjector(int port) : port_(port) {}
void HandleConnection(grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline) override {
{
grpc_core::MutexLock lock(&mu_);
const int port = grpc_sockaddr_get_port(addr);
gpr_log(GPR_INFO, "==> HandleConnection(): seen_port_=%d, port=%d",
seen_port_, port);
// Initial attempt should be for port0_, which should fail.
// Cancel the RPC at this point, so that it's no longer
// queued when the LB policy updates the picker.
if (!seen_port_ && port == port_) {
gpr_log(GPR_INFO, "*** SEEN P0 CONNECTION ATTEMPT");
seen_port_ = true;
cond_.Signal();
}
}
AttemptConnection(closure, ep, interested_parties, channel_args, addr,
deadline);
}
void WaitForP0ConnectionAttempt() {
grpc_core::MutexLock lock(&mu_);
while (!seen_port_) {
cond_.Wait(&mu_);
}
}
private:
const int port_;
grpc_core::Mutex mu_;
grpc_core::CondVar cond_;
bool seen_port_ ABSL_GUARDED_BY(mu_) = false;
};
ConnectionInjector connection_injector(non_existant_endpoint.port);
// A long-running RPC, just used to send the RPC in another thread.
LongRunningRpc rpc;
std::vector<std::pair<std::string, std::string>> metadata = {
{"address_hash",
CreateMetadataValueThatHashesToBackendPort(non_existant_endpoint.port)}};
rpc.StartRpc(stub_.get(), RpcOptions().set_timeout_ms(0).set_metadata(
std::move(metadata)));
// Wait for the RPC to trigger the P0 connection attempt, then cancel it.
connection_injector.WaitForP0ConnectionAttempt();
rpc.CancelRpc();
// Wait for channel to become connected without any pending RPC.
EXPECT_TRUE(channel_->WaitForConnected(grpc_timeout_seconds_to_deadline(5)));
// RPC should have been cancelled.
EXPECT_EQ(StatusCode::CANCELLED, rpc.GetStatus().error_code());
// Make sure the backend did not get any requests.
EXPECT_EQ(0UL, backends_[0]->backend_service()->request_count());
}
// Test that when the first pick is down leading to a transient failure, we
// will move on to the next ring hash entry.
TEST_P(CdsTest, RingHashTransientFailureCheckNextOne) {
@ -5840,6 +6163,43 @@ TEST_P(CdsTest, RingHashTransientFailureSkipToAvailableReady) {
WaitForBackend(1, WaitForBackendOptions(), rpc_options);
}
// This tests a bug seen in the wild where ring_hash started with no
// endpoints and reported TRANSIENT_FAILURE, then got an update with
// endpoints and reported IDLE, but the picker update was squelched, so
// it failed to ever get reconnected.
TEST_P(CdsTest, RingHashReattemptWhenGoingFromTransientFailureToIdle) {
CreateAndStartBackends(1);
const uint32_t kConnectionTimeoutMilliseconds = 5000;
auto cluster = default_cluster_;
cluster.set_lb_policy(Cluster::RING_HASH);
balancer_->ads_service()->SetCdsResource(cluster);
auto new_route_config = default_route_config_;
SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
new_route_config);
// Send empty EDS update.
EdsResourceArgs args(
{{"locality0", std::vector<EdsResourceArgs::Endpoint>()}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
EXPECT_EQ(GRPC_CHANNEL_IDLE, channel_->GetState(false));
// Channel should fail RPCs and go into TRANSIENT_FAILURE.
CheckRpcSendFailure(
DEBUG_LOCATION, StatusCode::UNAVAILABLE,
// TODO(roth): As part of https://github.com/grpc/grpc/issues/22883,
// figure out how to get a useful resolution note plumbed down to
// improve this message.
"no ready priority",
RpcOptions().set_timeout_ms(kConnectionTimeoutMilliseconds));
EXPECT_EQ(GRPC_CHANNEL_TRANSIENT_FAILURE, channel_->GetState(false));
// Send EDS update with 1 backend.
args = EdsResourceArgs({{"locality0", CreateEndpointsForBackends()}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
// A wait_for_ready RPC should succeed, and the channel should report READY.
CheckRpcSendOk(1, RpcOptions()
.set_timeout_ms(kConnectionTimeoutMilliseconds)
.set_wait_for_ready(true));
EXPECT_EQ(GRPC_CHANNEL_READY, channel_->GetState(false));
}
// Test unspported hash policy types are all ignored before a supported
// policy.
TEST_P(CdsTest, RingHashUnsupportedHashPolicyUntilChannelIdHashing) {
@ -12482,6 +12842,11 @@ INSTANTIATE_TEST_SUITE_P(
.set_enable_rds_testing()),
&XdsTestType::Name);
INSTANTIATE_TEST_SUITE_P(
XdsTest, XdsFederationDisabledTest,
::testing::Values(XdsTestType().set_enable_rds_testing()),
&XdsTestType::Name);
INSTANTIATE_TEST_SUITE_P(
XdsTest, XdsFederationLoadReportingTest,
::testing::Values(
@ -12587,6 +12952,7 @@ int main(int argc, char** argv) {
absl::make_unique<grpc::testing::FakeCertificateProviderFactory>(
"fake2", &grpc::testing::g_fake2_cert_data_map));
grpc_init();
grpc::testing::ConnectionAttemptInjector::Init();
grpc_core::XdsHttpFilterRegistry::RegisterFilter(
absl::make_unique<grpc::testing::NoOpHttpFilter>(
"grpc.testing.client_only_http_filter",

View File

@ -825,6 +825,19 @@ void XdsEnd2endTest::CheckRpcSendOk(const size_t times,
}
}
void XdsEnd2endTest::CheckRpcSendFailure(
const grpc_core::DebugLocation& debug_location, StatusCode expected_status,
absl::string_view expected_message_regex, const RpcOptions& rpc_options) {
const Status status = SendRpc(rpc_options);
EXPECT_FALSE(status.ok())
<< debug_location.file() << ":" << debug_location.line();
EXPECT_EQ(expected_status, status.error_code())
<< debug_location.file() << ":" << debug_location.line();
EXPECT_THAT(status.error_message(),
::testing::ContainsRegex(expected_message_regex))
<< debug_location.file() << ":" << debug_location.line();
}
void XdsEnd2endTest::CheckRpcSendFailure(
const CheckRpcSendFailureOptions& options) {
for (size_t i = 0; options.continue_predicate(i); ++i) {

View File

@ -798,7 +798,16 @@ class XdsEnd2endTest : public ::testing::TestWithParam<XdsTestType> {
void CheckRpcSendOk(const size_t times = 1,
const RpcOptions& rpc_options = RpcOptions());
// Options to use with CheckRpcSendFailure().
// Sends one RPC, which must fail with the specified status code and
// a message matching the specified regex.
void CheckRpcSendFailure(const grpc_core::DebugLocation& debug_location,
StatusCode expected_status,
absl::string_view expected_message_regex,
const RpcOptions& rpc_options = RpcOptions());
// DEPRECATED -- USE THE ABOVE VARIANT INSTEAD.
// TODO(roth): Change all existing callers to use the above variant
// instead and then remove this.
struct CheckRpcSendFailureOptions {
std::function<bool(size_t)> continue_predicate = [](size_t i) {
return i < 1;
@ -829,8 +838,6 @@ class XdsEnd2endTest : public ::testing::TestWithParam<XdsTestType> {
return *this;
}
};
// Sends RPCs and expects them to fail.
void CheckRpcSendFailure(
const CheckRpcSendFailureOptions& options = CheckRpcSendFailureOptions());

View File

@ -19,7 +19,10 @@ cd "$(dirname "$0")"
cp -r "$EXTERNAL_GIT_ROOT"/input_artifacts/grpc-*.tgz .
find . -regex ".*/grpc-[0-9].*.tgz" | cut -b3- | \
MAKEFLAGS=-j xargs pecl install
# get name of the PHP package archive to test (we don't know
# the exact version string in advance)
GRPC_PEAR_PACKAGE_NAME=$(find . -regex '.*/grpc-[0-9].*.tgz' | sed 's|./||')
MAKEFLAGS=-j pecl install "${GRPC_PEAR_PACKAGE_NAME}"
php -d extension=grpc.so -d max_execution_time=300 distribtest.php

View File

@ -19,7 +19,12 @@ cd "$(dirname "$0")"
cp -r "$EXTERNAL_GIT_ROOT"/input_artifacts/grpc-*.tgz .
find . -regex ".*/grpc-[0-9].*.tgz" | cut -b3- | \
xargs sudo MAKEFLAGS=-j pecl install
# get name of the PHP package archive to test (we don't know
# the exact version string in advance)
GRPC_PEAR_PACKAGE_NAME=$(find . -regex '.*/grpc-[0-9].*.tgz' | sed 's|./||')
# Use -j4 since higher parallelism can lead to "resource unavailable"
# errors during the build. See b/257261061#comment4
sudo MAKEFLAGS=-j4 pecl install "${GRPC_PEAR_PACKAGE_NAME}"
php -d extension=grpc.so -d max_execution_time=300 distribtest.php

@ -1 +1 @@
Subproject commit b9232f9e27e5668bc0414879dcdedb2a59ea75f2
Subproject commit 6195bf8242156c9a2fa75702eee058f91b86a88b

@ -1 +1 @@
Subproject commit 22d0e265de7d2b3d2e9a00d071313502e7d4cccf
Subproject commit b464cfbee18c71c40e761a5273ad369f3547294b

View File

@ -5,7 +5,7 @@ index 97ac28028..8b7585d9d 100644
@@ -31,3 +31,9 @@
# Copyright 2007 Google Inc. All Rights Reserved.
__version__ = '3.19.4'
__version__ = '3.19.5'
+
+if __name__ != '__main__':
+ try:

View File

@ -1 +1 @@
FROM larskanis/rake-compiler-dock-mri-arm64-darwin:1.1.0
FROM larskanis/rake-compiler-dock-mri-arm64-darwin:1.2.1

View File

@ -1,4 +1,4 @@
FROM larskanis/rake-compiler-dock-mri-x64-mingw32:1.1.0
FROM larskanis/rake-compiler-dock-mri-x64-mingw32:1.2.1
RUN find / -name win32.h | while read f ; do sed -i 's/gettimeofday/rb_gettimeofday/' $f ; done

View File

@ -1,4 +1,4 @@
FROM larskanis/rake-compiler-dock-mri-x86-linux:1.1.0
FROM larskanis/rake-compiler-dock-mri-x86-linux:1.2.1
#=================
# Install ccache

View File

@ -1,4 +1,4 @@
FROM larskanis/rake-compiler-dock-mri-x86-mingw32:1.1.0
FROM larskanis/rake-compiler-dock-mri-x86-mingw32:1.2.1
RUN find / -name win32.h | while read f ; do sed -i 's/gettimeofday/rb_gettimeofday/' $f ; done

View File

@ -1 +1 @@
FROM larskanis/rake-compiler-dock-mri-x86_64-darwin:1.1.0
FROM larskanis/rake-compiler-dock-mri-x86_64-darwin:1.2.1

View File

@ -1,4 +1,4 @@
FROM larskanis/rake-compiler-dock-mri-x86_64-linux:1.1.0
FROM larskanis/rake-compiler-dock-mri-x86_64-linux:1.2.1
#=================
# Install ccache

2
third_party/zlib vendored

@ -1 +1 @@
Subproject commit cacf7f1d4e3d44d871b605da3b647f07d718623f
Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc

Some files were not shown because too many files have changed in this diff Show More