Add http cancel api (#28354)

Add an API to cancel HTTP1 requests
pull/28682/head
apolcyn 3 years ago committed by GitHub
parent cf81e41162
commit 6bf8e22484
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 49
      BUILD
  2. 169
      CMakeLists.txt
  3. 25
      Makefile
  4. 1
      bazel/grpc_build_system.bzl
  5. 94
      build_autogenerated.yaml
  6. 2
      gRPC-C++.podspec
  7. 2
      gRPC-Core.podspec
  8. 1
      grpc.gemspec
  9. 12
      grpc.gyp
  10. 1
      package.xml
  11. 21
      src/core/ext/filters/client_channel/http_connect_handshaker.cc
  12. 4
      src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
  13. 36
      src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc
  14. 50
      src/core/lib/http/format_request.cc
  15. 13
      src/core/lib/http/format_request.h
  16. 520
      src/core/lib/http/httpcli.cc
  17. 254
      src/core/lib/http/httpcli.h
  18. 110
      src/core/lib/http/httpcli_security_connector.cc
  19. 37
      src/core/lib/http/httpcli_ssl_credentials.h
  20. 2
      src/core/lib/http/parser.h
  21. 2
      src/core/lib/security/credentials/credentials.cc
  22. 27
      src/core/lib/security/credentials/credentials.h
  23. 73
      src/core/lib/security/credentials/external/aws_external_account_credentials.cc
  24. 1
      src/core/lib/security/credentials/external/aws_external_account_credentials.h
  25. 76
      src/core/lib/security/credentials/external/external_account_credentials.cc
  26. 1
      src/core/lib/security/credentials/external/external_account_credentials.h
  27. 45
      src/core/lib/security/credentials/external/url_external_account_credentials.cc
  28. 1
      src/core/lib/security/credentials/external/url_external_account_credentials.h
  29. 20
      src/core/lib/security/credentials/google_default/google_default_credentials.cc
  30. 77
      src/core/lib/security/credentials/jwt/jwt_verifier.cc
  31. 100
      src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
  32. 24
      src/core/lib/security/credentials/oauth2/oauth2_credentials.h
  33. 1
      src/core/lib/surface/init.cc
  34. 2
      src/cpp/client/secure_credentials.h
  35. 22
      test/core/http/BUILD
  36. 52
      test/core/http/format_request_test.cc
  37. 558
      test/core/http/httpcli_test.cc
  38. 105
      test/core/http/httpcli_test_util.cc
  39. 38
      test/core/http/httpcli_test_util.h
  40. 391
      test/core/http/httpscli_test.cc
  41. 582
      test/core/security/credentials_test.cc
  42. 108
      test/core/security/jwt_verifier_test.cc
  43. 1
      test/core/util/evaluate_args_test_util.h
  44. 50
      test/core/util/fake_udp_and_tcp_server.cc
  45. 80
      test/core/util/port_server_client.cc
  46. 1
      test/cpp/ext/filters/census/BUILD
  47. 1
      tools/doxygen/Doxyfile.c++.internal
  48. 1
      tools/doxygen/Doxyfile.core.internal
  49. 88
      tools/run_tests/generated/tests.json

49
BUILD

@ -1797,9 +1797,6 @@ grpc_cc_library(
"src/core/lib/debug/stats_data.cc",
"src/core/lib/event_engine/channel_args_endpoint_config.cc",
"src/core/lib/event_engine/sockaddr.cc",
"src/core/lib/http/format_request.cc",
"src/core/lib/http/httpcli.cc",
"src/core/lib/http/parser.cc",
"src/core/lib/iomgr/buffer_list.cc",
"src/core/lib/iomgr/call_combiner.cc",
"src/core/lib/iomgr/cfstream_handle.cc",
@ -1950,9 +1947,6 @@ grpc_cc_library(
"src/core/lib/debug/stats_data.h",
"src/core/lib/event_engine/channel_args_endpoint_config.h",
"src/core/lib/event_engine/sockaddr.h",
"src/core/lib/http/format_request.h",
"src/core/lib/http/httpcli.h",
"src/core/lib/http/parser.h",
"src/core/lib/iomgr/block_annotate.h",
"src/core/lib/iomgr/buffer_list.h",
"src/core/lib/iomgr/call_combiner.h",
@ -2364,6 +2358,7 @@ grpc_cc_library(
"grpc_service_config",
"grpc_trace",
"handshaker_registry",
"httpcli",
"json",
"json_util",
"orphanable",
@ -3474,14 +3469,48 @@ grpc_cc_library(
"grpc_client_channel",
"grpc_resolver",
"grpc_xds_client",
"httpcli",
],
)
grpc_cc_library(
name = "httpcli",
srcs = [
"src/core/lib/http/format_request.cc",
"src/core/lib/http/httpcli.cc",
"src/core/lib/http/parser.cc",
],
hdrs = [
"src/core/lib/http/format_request.h",
"src/core/lib/http/httpcli.h",
"src/core/lib/http/parser.h",
],
external_deps = [
"absl/functional:bind_front",
"absl/strings",
"absl/strings:str_format",
],
language = "c++",
visibility = ["@grpc:httpcli"],
deps = [
"config",
"gpr_base",
"grpc_base",
"grpc_security_base",
"ref_counted_ptr",
"sockaddr_utils",
"useful",
],
)
grpc_cc_library(
name = "grpc_httpcli_security_connector",
name = "httpcli_ssl_credentials",
srcs = [
"src/core/lib/http/httpcli_security_connector.cc",
],
hdrs = [
"src/core/lib/http/httpcli_ssl_credentials.h",
],
external_deps = [
"absl/strings",
],
@ -3563,7 +3592,6 @@ grpc_cc_library(
"grpc_client_channel",
"grpc_codegen",
"grpc_credentials_util",
"grpc_httpcli_security_connector",
"grpc_jwt_credentials",
"grpc_lb_xds_channel_args",
"grpc_security_base",
@ -3572,6 +3600,8 @@ grpc_cc_library(
"grpc_tls_credentials",
"grpc_trace",
"grpc_transport_chttp2_alpn",
"httpcli",
"httpcli_ssl_credentials",
"json",
"ref_counted",
"ref_counted_ptr",
@ -3811,6 +3841,8 @@ grpc_cc_library(
"grpc_base",
"grpc_credentials_util",
"grpc_security_base",
"httpcli",
"httpcli_ssl_credentials",
"json",
"ref_counted",
"ref_counted_ptr",
@ -4136,6 +4168,7 @@ grpc_cc_library(
"grpc_transport_chttp2_alpn",
"hpack_constants",
"hpack_encoder_table",
"httpcli",
"memory_quota",
"resource_quota_trace",
"slice",

169
CMakeLists.txt generated

@ -690,12 +690,6 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_c histogram_test)
add_dependencies(buildtests_c host_port_test)
add_dependencies(buildtests_c hpack_encoder_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_c httpcli_test)
endif()
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_c httpscli_test)
endif()
add_dependencies(buildtests_c inproc_callback_test)
add_dependencies(buildtests_c invalid_call_argument_test)
add_dependencies(buildtests_c json_token_test)
@ -895,6 +889,12 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx hpack_parser_table_test)
add_dependencies(buildtests_cxx hpack_parser_test)
add_dependencies(buildtests_cxx http2_client)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx httpcli_test)
endif()
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx httpscli_test)
endif()
add_dependencies(buildtests_cxx hybrid_end2end_test)
add_dependencies(buildtests_cxx idle_filter_state_test)
add_dependencies(buildtests_cxx if_test)
@ -2664,6 +2664,16 @@ add_library(grpc_unsecure
src/core/lib/resource_quota/thread_quota.cc
src/core/lib/resource_quota/trace.cc
src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc
src/core/lib/security/context/security_context.cc
src/core/lib/security/credentials/composite/composite_credentials.cc
src/core/lib/security/credentials/credentials.cc
src/core/lib/security/credentials/plugin/plugin_credentials.cc
src/core/lib/security/security_connector/security_connector.cc
src/core/lib/security/transport/client_auth_filter.cc
src/core/lib/security/transport/secure_endpoint.cc
src/core/lib/security/transport/security_handshaker.cc
src/core/lib/security/transport/server_auth_filter.cc
src/core/lib/security/transport/tsi_error.cc
src/core/lib/service_config/service_config.cc
src/core/lib/service_config/service_config_parser.cc
src/core/lib/slice/b64.cc
@ -2707,6 +2717,8 @@ add_library(grpc_unsecure
src/core/lib/transport/transport_op_string.cc
src/core/lib/uri/uri_parser.cc
src/core/plugin_registry/grpc_unsecure_plugin_registry.cc
src/core/tsi/transport_security.cc
src/core/tsi/transport_security_grpc.cc
)
set_target_properties(grpc_unsecure PROPERTIES
@ -2773,6 +2785,7 @@ foreach(_hdr
include/grpc/fork.h
include/grpc/grpc.h
include/grpc/grpc_posix.h
include/grpc/grpc_security.h
include/grpc/grpc_security_constants.h
include/grpc/load_reporting.h
include/grpc/slice.h
@ -5794,72 +5807,6 @@ target_link_libraries(hpack_encoder_test
)
endif()
if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_executable(httpcli_test
test/core/end2end/data/client_certs.cc
test/core/end2end/data/server1_cert.cc
test/core/end2end/data/server1_key.cc
test/core/end2end/data/test_root_cert.cc
test/core/http/httpcli_test.cc
)
target_include_directories(httpcli_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
)
target_link_libraries(httpcli_test
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
endif()
if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_executable(httpscli_test
test/core/end2end/data/client_certs.cc
test/core/end2end/data/server1_cert.cc
test/core/end2end/data/server1_key.cc
test/core/end2end/data/test_root_cert.cc
test/core/http/httpscli_test.cc
)
target_include_directories(httpscli_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
)
target_link_libraries(httpscli_test
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
endif()
if(gRPC_BUILD_TESTS)
@ -11886,6 +11833,84 @@ target_link_libraries(http2_client
)
endif()
if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_executable(httpcli_test
test/core/http/httpcli_test.cc
test/core/http/httpcli_test_util.cc
test/core/util/fake_udp_and_tcp_server.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(httpcli_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(httpcli_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc++_test_util
)
endif()
endif()
if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_executable(httpscli_test
test/core/http/httpcli_test_util.cc
test/core/http/httpscli_test.cc
test/core/util/fake_udp_and_tcp_server.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(httpscli_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(httpscli_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc++_test_util
)
endif()
endif()
if(gRPC_BUILD_TESTS)

25
Makefile generated

@ -1991,6 +1991,16 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/resource_quota/thread_quota.cc \
src/core/lib/resource_quota/trace.cc \
src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc \
src/core/lib/security/context/security_context.cc \
src/core/lib/security/credentials/composite/composite_credentials.cc \
src/core/lib/security/credentials/credentials.cc \
src/core/lib/security/credentials/plugin/plugin_credentials.cc \
src/core/lib/security/security_connector/security_connector.cc \
src/core/lib/security/transport/client_auth_filter.cc \
src/core/lib/security/transport/secure_endpoint.cc \
src/core/lib/security/transport/security_handshaker.cc \
src/core/lib/security/transport/server_auth_filter.cc \
src/core/lib/security/transport/tsi_error.cc \
src/core/lib/service_config/service_config.cc \
src/core/lib/service_config/service_config_parser.cc \
src/core/lib/slice/b64.cc \
@ -2034,6 +2044,8 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/transport/transport_op_string.cc \
src/core/lib/uri/uri_parser.cc \
src/core/plugin_registry/grpc_unsecure_plugin_registry.cc \
src/core/tsi/transport_security.cc \
src/core/tsi/transport_security_grpc.cc \
PUBLIC_HEADERS_C += \
include/grpc/byte_buffer.h \
@ -2049,6 +2061,7 @@ PUBLIC_HEADERS_C += \
include/grpc/fork.h \
include/grpc/grpc.h \
include/grpc/grpc_posix.h \
include/grpc/grpc_security.h \
include/grpc/grpc_security_constants.h \
include/grpc/load_reporting.h \
include/grpc/slice.h \
@ -2984,7 +2997,6 @@ src/core/lib/security/authorization/grpc_authorization_engine.cc: $(OPENSSL_DEP)
src/core/lib/security/authorization/matchers.cc: $(OPENSSL_DEP)
src/core/lib/security/authorization/rbac_policy.cc: $(OPENSSL_DEP)
src/core/lib/security/authorization/sdk_server_authz_filter.cc: $(OPENSSL_DEP)
src/core/lib/security/context/security_context.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/alts/alts_credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/alts/check_gcp_environment.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc: $(OPENSSL_DEP)
@ -2993,8 +3005,6 @@ src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc: $(OPENS
src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/composite/composite_credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/external/aws_external_account_credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/external/aws_request_signer.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/external/external_account_credentials.cc: $(OPENSSL_DEP)
@ -3010,7 +3020,6 @@ src/core/lib/security/credentials/jwt/jwt_credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/jwt/jwt_verifier.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/local/local_credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/oauth2/oauth2_credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/plugin/plugin_credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/ssl/ssl_credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.cc: $(OPENSSL_DEP)
@ -3025,16 +3034,10 @@ src/core/lib/security/security_connector/insecure/insecure_security_connector.cc
src/core/lib/security/security_connector/load_system_roots_fallback.cc: $(OPENSSL_DEP)
src/core/lib/security/security_connector/load_system_roots_linux.cc: $(OPENSSL_DEP)
src/core/lib/security/security_connector/local/local_security_connector.cc: $(OPENSSL_DEP)
src/core/lib/security/security_connector/security_connector.cc: $(OPENSSL_DEP)
src/core/lib/security/security_connector/ssl/ssl_security_connector.cc: $(OPENSSL_DEP)
src/core/lib/security/security_connector/ssl_utils.cc: $(OPENSSL_DEP)
src/core/lib/security/security_connector/ssl_utils_config.cc: $(OPENSSL_DEP)
src/core/lib/security/security_connector/tls/tls_security_connector.cc: $(OPENSSL_DEP)
src/core/lib/security/transport/client_auth_filter.cc: $(OPENSSL_DEP)
src/core/lib/security/transport/secure_endpoint.cc: $(OPENSSL_DEP)
src/core/lib/security/transport/security_handshaker.cc: $(OPENSSL_DEP)
src/core/lib/security/transport/server_auth_filter.cc: $(OPENSSL_DEP)
src/core/lib/security/transport/tsi_error.cc: $(OPENSSL_DEP)
src/core/lib/security/util/json_util.cc: $(OPENSSL_DEP)
src/core/lib/surface/init_secure.cc: $(OPENSSL_DEP)
src/core/plugin_registry/grpc_plugin_registry.cc: $(OPENSSL_DEP)
@ -3064,8 +3067,6 @@ src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc: $(OPENSSL_DEP)
src/core/tsi/ssl/session_cache/ssl_session_cache.cc: $(OPENSSL_DEP)
src/core/tsi/ssl/session_cache/ssl_session_openssl.cc: $(OPENSSL_DEP)
src/core/tsi/ssl_transport_security.cc: $(OPENSSL_DEP)
src/core/tsi/transport_security.cc: $(OPENSSL_DEP)
src/core/tsi/transport_security_grpc.cc: $(OPENSSL_DEP)
endif
.PHONY: all strip tools dep_error openssl_dep_error openssl_dep_message git_update stop buildtests buildtests_c buildtests_cxx test test_c test_cxx install install_c install_cxx install_csharp install-static install-certs strip strip-shared strip-static strip_c strip-shared_c strip-static_c strip_cxx strip-shared_cxx strip-static_cxx dep_c dep_cxx bins_dep_c bins_dep_cxx clean

@ -90,6 +90,7 @@ def _update_visibility(visibility):
"grpc_opencensus_plugin": PUBLIC,
"grpc_resolver_fake": PRIVATE,
"grpc++_test": PRIVATE,
"httpcli": PRIVATE,
"public": PUBLIC,
"ref_counted_ptr": PRIVATE,
"trace": PRIVATE,

@ -764,6 +764,7 @@ libs:
- src/core/lib/gprpp/table.h
- src/core/lib/http/format_request.h
- src/core/lib/http/httpcli.h
- src/core/lib/http/httpcli_ssl_credentials.h
- src/core/lib/http/parser.h
- src/core/lib/iomgr/block_annotate.h
- src/core/lib/iomgr/buffer_list.h
@ -1747,6 +1748,7 @@ libs:
- include/grpc/fork.h
- include/grpc/grpc.h
- include/grpc/grpc_posix.h
- include/grpc/grpc_security.h
- include/grpc/grpc_security_constants.h
- include/grpc/load_reporting.h
- include/grpc/slice.h
@ -1984,6 +1986,15 @@ libs:
- src/core/lib/resource_quota/resource_quota.h
- src/core/lib/resource_quota/thread_quota.h
- src/core/lib/resource_quota/trace.h
- src/core/lib/security/context/security_context.h
- src/core/lib/security/credentials/composite/composite_credentials.h
- src/core/lib/security/credentials/credentials.h
- src/core/lib/security/credentials/plugin/plugin_credentials.h
- src/core/lib/security/security_connector/security_connector.h
- src/core/lib/security/transport/auth_filters.h
- src/core/lib/security/transport/secure_endpoint.h
- src/core/lib/security/transport/security_handshaker.h
- src/core/lib/security/transport/tsi_error.h
- src/core/lib/service_config/service_config.h
- src/core/lib/service_config/service_config_call_data.h
- src/core/lib/service_config/service_config_parser.h
@ -2022,6 +2033,9 @@ libs:
- src/core/lib/transport/transport.h
- src/core/lib/transport/transport_impl.h
- src/core/lib/uri/uri_parser.h
- src/core/tsi/transport_security.h
- src/core/tsi/transport_security_grpc.h
- src/core/tsi/transport_security_interface.h
- third_party/xxhash/xxhash.h
src:
- src/core/ext/filters/census/grpc_context.cc
@ -2264,6 +2278,16 @@ libs:
- src/core/lib/resource_quota/thread_quota.cc
- src/core/lib/resource_quota/trace.cc
- src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc
- src/core/lib/security/context/security_context.cc
- src/core/lib/security/credentials/composite/composite_credentials.cc
- src/core/lib/security/credentials/credentials.cc
- src/core/lib/security/credentials/plugin/plugin_credentials.cc
- src/core/lib/security/security_connector/security_connector.cc
- src/core/lib/security/transport/client_auth_filter.cc
- src/core/lib/security/transport/secure_endpoint.cc
- src/core/lib/security/transport/security_handshaker.cc
- src/core/lib/security/transport/server_auth_filter.cc
- src/core/lib/security/transport/tsi_error.cc
- src/core/lib/service_config/service_config.cc
- src/core/lib/service_config/service_config_parser.cc
- src/core/lib/slice/b64.cc
@ -2307,6 +2331,8 @@ libs:
- src/core/lib/transport/transport_op_string.cc
- src/core/lib/uri/uri_parser.cc
- src/core/plugin_registry/grpc_unsecure_plugin_registry.cc
- src/core/tsi/transport_security.cc
- src/core/tsi/transport_security_grpc.cc
deps:
- absl/container:flat_hash_map
- absl/container:inlined_vector
@ -3636,40 +3662,6 @@ targets:
deps:
- grpc_test_util
uses_polling: false
- name: httpcli_test
build: test
language: c
headers:
- test/core/end2end/data/ssl_test_data.h
src:
- test/core/end2end/data/client_certs.cc
- test/core/end2end/data/server1_cert.cc
- test/core/end2end/data/server1_key.cc
- test/core/end2end/data/test_root_cert.cc
- test/core/http/httpcli_test.cc
deps:
- grpc_test_util
platforms:
- linux
- posix
- mac
- name: httpscli_test
build: test
language: c
headers:
- test/core/end2end/data/ssl_test_data.h
src:
- test/core/end2end/data/client_certs.cc
- test/core/end2end/data/server1_cert.cc
- test/core/end2end/data/server1_key.cc
- test/core/end2end/data/test_root_cert.cc
- test/core/http/httpscli_test.cc
deps:
- grpc_test_util
platforms:
- linux
- posix
- mac
- name: inproc_callback_test
build: test
language: c
@ -6226,6 +6218,40 @@ targets:
deps:
- grpc++_test_config
- grpc++_test_util
- name: httpcli_test
gtest: true
build: test
language: c++
headers:
- test/core/http/httpcli_test_util.h
- test/core/util/fake_udp_and_tcp_server.h
src:
- test/core/http/httpcli_test.cc
- test/core/http/httpcli_test_util.cc
- test/core/util/fake_udp_and_tcp_server.cc
deps:
- grpc++_test_util
platforms:
- linux
- posix
- mac
- name: httpscli_test
gtest: true
build: test
language: c++
headers:
- test/core/http/httpcli_test_util.h
- test/core/util/fake_udp_and_tcp_server.h
src:
- test/core/http/httpcli_test_util.cc
- test/core/http/httpscli_test.cc
- test/core/util/fake_udp_and_tcp_server.cc
deps:
- grpc++_test_util
platforms:
- linux
- posix
- mac
- name: hybrid_end2end_test
gtest: true
build: test

2
gRPC-C++.podspec generated

@ -636,6 +636,7 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/time_util.h',
'src/core/lib/http/format_request.h',
'src/core/lib/http/httpcli.h',
'src/core/lib/http/httpcli_ssl_credentials.h',
'src/core/lib/http/parser.h',
'src/core/lib/iomgr/block_annotate.h',
'src/core/lib/iomgr/buffer_list.h',
@ -1372,6 +1373,7 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/time_util.h',
'src/core/lib/http/format_request.h',
'src/core/lib/http/httpcli.h',
'src/core/lib/http/httpcli_ssl_credentials.h',
'src/core/lib/http/parser.h',
'src/core/lib/iomgr/block_annotate.h',
'src/core/lib/iomgr/buffer_list.h',

2
gRPC-Core.podspec generated

@ -987,6 +987,7 @@ Pod::Spec.new do |s|
'src/core/lib/http/httpcli.cc',
'src/core/lib/http/httpcli.h',
'src/core/lib/http/httpcli_security_connector.cc',
'src/core/lib/http/httpcli_ssl_credentials.h',
'src/core/lib/http/parser.cc',
'src/core/lib/http/parser.h',
'src/core/lib/iomgr/block_annotate.h',
@ -1914,6 +1915,7 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/time_util.h',
'src/core/lib/http/format_request.h',
'src/core/lib/http/httpcli.h',
'src/core/lib/http/httpcli_ssl_credentials.h',
'src/core/lib/http/parser.h',
'src/core/lib/iomgr/block_annotate.h',
'src/core/lib/iomgr/buffer_list.h',

1
grpc.gemspec generated

@ -906,6 +906,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/http/httpcli.cc )
s.files += %w( src/core/lib/http/httpcli.h )
s.files += %w( src/core/lib/http/httpcli_security_connector.cc )
s.files += %w( src/core/lib/http/httpcli_ssl_credentials.h )
s.files += %w( src/core/lib/http/parser.cc )
s.files += %w( src/core/lib/http/parser.h )
s.files += %w( src/core/lib/iomgr/block_annotate.h )

12
grpc.gyp generated

@ -1411,6 +1411,16 @@
'src/core/lib/resource_quota/thread_quota.cc',
'src/core/lib/resource_quota/trace.cc',
'src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc',
'src/core/lib/security/context/security_context.cc',
'src/core/lib/security/credentials/composite/composite_credentials.cc',
'src/core/lib/security/credentials/credentials.cc',
'src/core/lib/security/credentials/plugin/plugin_credentials.cc',
'src/core/lib/security/security_connector/security_connector.cc',
'src/core/lib/security/transport/client_auth_filter.cc',
'src/core/lib/security/transport/secure_endpoint.cc',
'src/core/lib/security/transport/security_handshaker.cc',
'src/core/lib/security/transport/server_auth_filter.cc',
'src/core/lib/security/transport/tsi_error.cc',
'src/core/lib/service_config/service_config.cc',
'src/core/lib/service_config/service_config_parser.cc',
'src/core/lib/slice/b64.cc',
@ -1454,6 +1464,8 @@
'src/core/lib/transport/transport_op_string.cc',
'src/core/lib/uri/uri_parser.cc',
'src/core/plugin_registry/grpc_unsecure_plugin_registry.cc',
'src/core/tsi/transport_security.cc',
'src/core/tsi/transport_security_grpc.cc',
],
},
{

1
package.xml generated

@ -886,6 +886,7 @@
<file baseinstalldir="/" name="src/core/lib/http/httpcli.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/http/httpcli.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/http/httpcli_security_connector.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/http/httpcli_ssl_credentials.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/http/parser.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/http/parser.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/block_annotate.h" role="src" />

@ -331,18 +331,15 @@ void HttpConnectHandshaker::DoHandshake(grpc_tcp_server_acceptor* /*acceptor*/,
gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s", server_name,
proxy_name.c_str());
// Construct HTTP CONNECT request.
grpc_httpcli_request request;
request.host = server_name;
request.ssl_host_override = nullptr;
request.http.method = const_cast<char*>("CONNECT");
request.http.path = server_name;
request.http.version = GRPC_HTTP_HTTP10; // Set by OnReadDone
request.http.hdrs = headers;
request.http.hdr_count = num_headers;
request.http.body_length = 0;
request.http.body = nullptr;
request.handshaker = &grpc_httpcli_plaintext;
grpc_slice request_slice = grpc_httpcli_format_connect_request(&request);
grpc_http_request request;
request.method = const_cast<char*>("CONNECT");
request.version = GRPC_HTTP_HTTP10; // Set by OnReadDone
request.hdrs = headers;
request.hdr_count = num_headers;
request.body_length = 0;
request.body = nullptr;
grpc_slice request_slice =
grpc_httpcli_format_connect_request(&request, server_name, server_name);
grpc_slice_buffer_add(&write_buffer_, request_slice);
// Clean up.
gpr_free(headers);

@ -522,7 +522,9 @@ class AresDNSResolver : public DNSResolver {
absl::MutexLock lock(&mu_);
GRPC_CARES_TRACE_LOG("AresRequest:%p Orphan ares_request_:%p", this,
ares_request_.get());
grpc_cancel_ares_request(ares_request_.get());
if (ares_request_ != nullptr) {
grpc_cancel_ares_request(ares_request_.get());
}
}
Unref();
}

@ -24,6 +24,7 @@
#include "src/core/lib/http/httpcli.h"
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/resolver/resolver_registry.h"
#include "src/core/lib/resource_quota/api.h"
#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
namespace grpc_core {
@ -61,7 +62,8 @@ class GoogleCloud2ProdResolver : public Resolver {
grpc_error_handle error) = 0;
RefCountedPtr<GoogleCloud2ProdResolver> resolver_;
grpc_httpcli_response response_;
OrphanablePtr<HttpRequest> http_request_;
grpc_http_response response_;
grpc_closure on_done_;
std::atomic<bool> on_done_called_{false};
};
@ -94,6 +96,7 @@ class GoogleCloud2ProdResolver : public Resolver {
void IPv6QueryDone(bool ipv6_supported);
void StartXdsResolver();
ResourceQuotaRefPtr resource_quota_;
std::shared_ptr<WorkSerializer> work_serializer_;
grpc_polling_entity pollent_;
bool using_dns_ = false;
@ -117,18 +120,26 @@ GoogleCloud2ProdResolver::MetadataQuery::MetadataQuery(
// Start HTTP request.
GRPC_CLOSURE_INIT(&on_done_, OnHttpRequestDone, this, nullptr);
Ref().release(); // Ref held by callback.
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
const_cast<char*>("Google")};
request.host = const_cast<char*>("metadata.google.internal");
request.http.path = const_cast<char*>(path);
request.http.hdr_count = 1;
request.http.hdrs = &header;
// TODO(ctiller): share the quota from whomever instantiates this!
grpc_httpcli_get(pollent, ResourceQuota::Default(), &request,
ExecCtx::Get()->Now() + 10000, // 10s timeout
&on_done_, &response_);
request.hdr_count = 1;
request.hdrs = &header;
auto uri = URI::Create("http", "metadata.google.internal.", path,
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok()); // params are hardcoded
grpc_arg resource_quota_arg = grpc_channel_arg_pointer_create(
const_cast<char*>(GRPC_ARG_RESOURCE_QUOTA),
resolver_->resource_quota_.get(), grpc_resource_quota_arg_vtable());
grpc_channel_args args = {1, &resource_quota_arg};
http_request_ =
HttpRequest::Get(std::move(*uri), &args, pollent, &request,
ExecCtx::Get()->Now() + 10000, // 10s timeout
&on_done_, &response_,
RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
http_request_->Start();
}
GoogleCloud2ProdResolver::MetadataQuery::~MetadataQuery() {
@ -227,7 +238,8 @@ void GoogleCloud2ProdResolver::IPv6Query::OnDone(
//
GoogleCloud2ProdResolver::GoogleCloud2ProdResolver(ResolverArgs args)
: work_serializer_(std::move(args.work_serializer)),
: resource_quota_(ResourceQuotaFromChannelArgs(args.args)),
work_serializer_(std::move(args.work_serializer)),
pollent_(grpc_polling_entity_create_from_pollset_set(args.pollset_set)) {
absl::string_view name_to_resolve = absl::StripPrefix(args.uri.path(), "/");
// If we're not running on GCP, we can't use DirectPath, so delegate

@ -35,46 +35,47 @@
#include "src/core/lib/gpr/string.h"
static void fill_common_header(const grpc_httpcli_request* request,
static void fill_common_header(const grpc_http_request* request,
const char* host, const char* path,
bool connection_close,
std::vector<std::string>* buf) {
buf->push_back(request->http.path);
buf->push_back(path);
buf->push_back(" HTTP/1.0\r\n");
/* just in case some crazy server really expects HTTP/1.1 */
buf->push_back("Host: ");
buf->push_back(request->host);
buf->push_back(host);
buf->push_back("\r\n");
if (connection_close) buf->push_back("Connection: close\r\n");
buf->push_back("User-Agent: " GRPC_HTTPCLI_USER_AGENT "\r\n");
/* user supplied headers */
for (size_t i = 0; i < request->http.hdr_count; i++) {
buf->push_back(request->http.hdrs[i].key);
for (size_t i = 0; i < request->hdr_count; i++) {
buf->push_back(request->hdrs[i].key);
buf->push_back(": ");
buf->push_back(request->http.hdrs[i].value);
buf->push_back(request->hdrs[i].value);
buf->push_back("\r\n");
}
}
grpc_slice grpc_httpcli_format_get_request(
const grpc_httpcli_request* request) {
grpc_slice grpc_httpcli_format_get_request(const grpc_http_request* request,
const char* host, const char* path) {
std::vector<std::string> out;
out.push_back("GET ");
fill_common_header(request, true, &out);
fill_common_header(request, host, path, true, &out);
out.push_back("\r\n");
std::string req = absl::StrJoin(out, "");
return grpc_slice_from_copied_buffer(req.data(), req.size());
}
grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request* request,
const char* body_bytes,
size_t body_size) {
grpc_slice grpc_httpcli_format_post_request(const grpc_http_request* request,
const char* host,
const char* path) {
std::vector<std::string> out;
out.push_back("POST ");
fill_common_header(request, true, &out);
if (body_bytes != nullptr) {
fill_common_header(request, host, path, true, &out);
if (request->body != nullptr) {
bool has_content_type = false;
for (size_t i = 0; i < request->http.hdr_count; i++) {
if (strcmp(request->http.hdrs[i].key, "Content-Type") == 0) {
for (size_t i = 0; i < request->hdr_count; i++) {
if (strcmp(request->hdrs[i].key, "Content-Type") == 0) {
has_content_type = true;
break;
}
@ -82,22 +83,25 @@ grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request* request,
if (!has_content_type) {
out.push_back("Content-Type: text/plain\r\n");
}
out.push_back(absl::StrFormat("Content-Length: %lu\r\n",
static_cast<unsigned long>(body_size)));
out.push_back(
absl::StrFormat("Content-Length: %lu\r\n",
static_cast<unsigned long>(request->body_length)));
}
out.push_back("\r\n");
std::string req = absl::StrJoin(out, "");
if (body_bytes != nullptr) {
absl::StrAppend(&req, absl::string_view(body_bytes, body_size));
if (request->body != nullptr) {
absl::StrAppend(&req,
absl::string_view(request->body, request->body_length));
}
return grpc_slice_from_copied_buffer(req.data(), req.size());
}
grpc_slice grpc_httpcli_format_connect_request(
const grpc_httpcli_request* request) {
grpc_slice grpc_httpcli_format_connect_request(const grpc_http_request* request,
const char* host,
const char* path) {
std::vector<std::string> out;
out.push_back("CONNECT ");
fill_common_header(request, false, &out);
fill_common_header(request, host, path, false, &out);
out.push_back("\r\n");
std::string req = absl::StrJoin(out, "");
return grpc_slice_from_copied_buffer(req.data(), req.size());

@ -25,11 +25,12 @@
#include "src/core/lib/http/httpcli.h"
grpc_slice grpc_httpcli_format_get_request(const grpc_httpcli_request* request);
grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request* request,
const char* body_bytes,
size_t body_size);
grpc_slice grpc_httpcli_format_connect_request(
const grpc_httpcli_request* request);
grpc_slice grpc_httpcli_format_get_request(const grpc_http_request* request,
const char* host, const char* path);
grpc_slice grpc_httpcli_format_post_request(const grpc_http_request* request,
const char* host, const char* path);
grpc_slice grpc_httpcli_format_connect_request(const grpc_http_request* request,
const char* host,
const char* path);
#endif /* GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H */

@ -48,272 +48,332 @@
#include "src/core/lib/transport/error_utils.h"
namespace grpc_core {
namespace {
class InternalRequest {
public:
InternalRequest(const grpc_slice& request_text,
grpc_httpcli_response* response,
ResourceQuotaRefPtr resource_quota, absl::string_view host,
absl::string_view ssl_host_override, grpc_millis deadline,
const grpc_httpcli_handshaker* handshaker,
grpc_closure* on_done, grpc_polling_entity* pollent,
const char* name)
: request_text_(request_text),
resource_quota_(std::move(resource_quota)),
host_(host),
ssl_host_override_(ssl_host_override),
deadline_(deadline),
handshaker_(handshaker),
on_done_(on_done),
pollent_(pollent),
pollset_set_(grpc_pollset_set_create()) {
grpc_http_parser_init(&parser_, GRPC_HTTP_RESPONSE, response);
grpc_slice_buffer_init(&incoming_);
grpc_slice_buffer_init(&outgoing_);
grpc_iomgr_register_object(&iomgr_obj_, name);
grpc_httpcli_get_override g_get_override;
grpc_httpcli_post_override g_post_override;
GRPC_CLOSURE_INIT(&on_read_, OnRead, this, grpc_schedule_on_exec_ctx);
GRPC_CLOSURE_INIT(&done_write_, DoneWrite, this, grpc_schedule_on_exec_ctx);
GPR_ASSERT(pollent);
grpc_polling_entity_add_to_pollset_set(pollent, pollset_set_);
dns_request_ = GetDNSResolver()->ResolveName(
host_.c_str(), handshaker_->default_port, pollset_set_,
absl::bind_front(&InternalRequest::OnResolved, this));
dns_request_->Start();
}
} // namespace
~InternalRequest() {
grpc_http_parser_destroy(&parser_);
if (ep_ != nullptr) {
grpc_endpoint_destroy(ep_);
}
grpc_slice_unref_internal(request_text_);
grpc_iomgr_unregister_object(&iomgr_obj_);
grpc_slice_buffer_destroy_internal(&incoming_);
grpc_slice_buffer_destroy_internal(&outgoing_);
GRPC_ERROR_UNREF(overall_error_);
grpc_pollset_set_destroy(pollset_set_);
OrphanablePtr<HttpRequest> HttpRequest::Get(
URI uri, const grpc_channel_args* channel_args,
grpc_polling_entity* pollent, const grpc_http_request* request,
grpc_millis deadline, grpc_closure* on_done, grpc_http_response* response,
RefCountedPtr<grpc_channel_credentials> channel_creds) {
absl::optional<std::function<void()>> test_only_generate_response;
if (g_get_override != nullptr) {
test_only_generate_response = [request, uri, deadline, on_done,
response]() {
// Note that capturing request here assumes it will remain alive
// until after Start is called. This avoids making a copy as this
// code path is only used for test mocks.
g_get_override(request, uri.authority().c_str(), uri.path().c_str(),
deadline, on_done, response);
};
}
std::string name =
absl::StrFormat("HTTP:GET:%s:%s", uri.authority(), uri.path());
const grpc_slice request_text = grpc_httpcli_format_get_request(
request, uri.authority().c_str(), uri.path().c_str());
return MakeOrphanable<HttpRequest>(
std::move(uri), request_text, response, deadline, channel_args, on_done,
pollent, name.c_str(), std::move(test_only_generate_response),
std::move(channel_creds));
}
private:
void Finish(grpc_error_handle error) {
grpc_polling_entity_del_from_pollset_set(pollent_, pollset_set_);
ExecCtx::Run(DEBUG_LOCATION, on_done_, error);
delete this;
OrphanablePtr<HttpRequest> HttpRequest::Post(
URI uri, const grpc_channel_args* channel_args,
grpc_polling_entity* pollent, const grpc_http_request* request,
grpc_millis deadline, grpc_closure* on_done, grpc_http_response* response,
RefCountedPtr<grpc_channel_credentials> channel_creds) {
absl::optional<std::function<void()>> test_only_generate_response;
if (g_post_override != nullptr) {
test_only_generate_response = [request, uri, deadline, on_done,
response]() {
g_post_override(request, uri.authority().c_str(), uri.path().c_str(),
request->body, request->body_length, deadline, on_done,
response);
};
}
std::string name =
absl::StrFormat("HTTP:POST:%s:%s", uri.authority(), uri.path());
const grpc_slice request_text = grpc_httpcli_format_post_request(
request, uri.authority().c_str(), uri.path().c_str());
return MakeOrphanable<HttpRequest>(
std::move(uri), request_text, response, deadline, channel_args, on_done,
pollent, name.c_str(), std::move(test_only_generate_response),
std::move(channel_creds));
}
void AppendError(grpc_error_handle error) {
if (overall_error_ == GRPC_ERROR_NONE) {
overall_error_ =
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed HTTP/1 client request");
}
const grpc_resolved_address* addr = &addresses_[next_address_ - 1];
std::string addr_text = grpc_sockaddr_to_uri(addr);
overall_error_ = grpc_error_add_child(
overall_error_,
grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, addr_text));
}
void HttpRequest::SetOverride(grpc_httpcli_get_override get,
grpc_httpcli_post_override post) {
g_get_override = get;
g_post_override = post;
}
void DoRead() {
grpc_endpoint_read(ep_, &incoming_, &on_read_, /*urgent=*/true);
}
HttpRequest::HttpRequest(
URI uri, const grpc_slice& request_text, grpc_http_response* response,
grpc_millis deadline, const grpc_channel_args* channel_args,
grpc_closure* on_done, grpc_polling_entity* pollent, const char* name,
absl::optional<std::function<void()>> test_only_generate_response,
RefCountedPtr<grpc_channel_credentials> channel_creds)
: uri_(std::move(uri)),
request_text_(request_text),
deadline_(deadline),
channel_args_(CoreConfiguration::Get()
.channel_args_preconditioning()
.PreconditionChannelArgs(channel_args)),
channel_creds_(std::move(channel_creds)),
on_done_(on_done),
resource_quota_(ResourceQuotaFromChannelArgs(channel_args_)),
pollent_(pollent),
pollset_set_(grpc_pollset_set_create()),
test_only_generate_response_(std::move(test_only_generate_response)) {
grpc_http_parser_init(&parser_, GRPC_HTTP_RESPONSE, response);
grpc_slice_buffer_init(&incoming_);
grpc_slice_buffer_init(&outgoing_);
grpc_iomgr_register_object(&iomgr_obj_, name);
GRPC_CLOSURE_INIT(&on_read_, OnRead, this, grpc_schedule_on_exec_ctx);
GRPC_CLOSURE_INIT(&continue_on_read_after_schedule_on_exec_ctx_,
ContinueOnReadAfterScheduleOnExecCtx, this,
grpc_schedule_on_exec_ctx);
GRPC_CLOSURE_INIT(&done_write_, DoneWrite, this, grpc_schedule_on_exec_ctx);
GRPC_CLOSURE_INIT(&continue_done_write_after_schedule_on_exec_ctx_,
ContinueDoneWriteAfterScheduleOnExecCtx, this,
grpc_schedule_on_exec_ctx);
GPR_ASSERT(pollent);
grpc_polling_entity_add_to_pollset_set(pollent, pollset_set_);
// Create the DNS resolver. We'll start resolving when Start is called.
dns_request_ = GetDNSResolver()->ResolveName(
uri_.authority(), uri_.scheme(), pollset_set_,
absl::bind_front(&HttpRequest::OnResolved, this));
}
static void OnRead(void* user_data, grpc_error_handle error) {
InternalRequest* req = static_cast<InternalRequest*>(user_data);
req->OnReadInternal(error);
HttpRequest::~HttpRequest() {
grpc_channel_args_destroy(channel_args_);
grpc_http_parser_destroy(&parser_);
if (own_endpoint_ && ep_ != nullptr) {
grpc_endpoint_destroy(ep_);
}
grpc_slice_unref_internal(request_text_);
grpc_iomgr_unregister_object(&iomgr_obj_);
grpc_slice_buffer_destroy_internal(&incoming_);
grpc_slice_buffer_destroy_internal(&outgoing_);
GRPC_ERROR_UNREF(overall_error_);
grpc_pollset_set_destroy(pollset_set_);
}
void OnReadInternal(grpc_error_handle error) {
size_t i;
void HttpRequest::Start() {
MutexLock lock(&mu_);
if (test_only_generate_response_.has_value()) {
test_only_generate_response_.value()();
return;
}
Ref().release(); // ref held by pending DNS resolution
dns_request_->Start();
}
for (i = 0; i < incoming_.count; i++) {
if (GRPC_SLICE_LENGTH(incoming_.slices[i])) {
have_read_byte_ = 1;
grpc_error_handle err =
grpc_http_parser_parse(&parser_, incoming_.slices[i], nullptr);
if (err != GRPC_ERROR_NONE) {
Finish(err);
return;
}
}
void HttpRequest::Orphan() {
{
MutexLock lock(&mu_);
GPR_ASSERT(!cancelled_);
cancelled_ = true;
dns_request_.reset(); // cancel potentially pending DNS resolution
if (connecting_) {
// gRPC's TCP connection establishment API doesn't currently have
// a mechanism for cancellation. So invoke the user callback now. The TCP
// connection will eventually complete (at least within its deadline), and
// we'll simply unref ourselves at that point.
// TODO(apolcyn): fix this to cancel the TCP connection attempt when
// an API to do so exists.
Finish(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
"HTTP request cancelled during TCP connection establishment",
&overall_error_, 1));
}
if (error == GRPC_ERROR_NONE) {
DoRead();
} else if (!have_read_byte_) {
NextAddress(GRPC_ERROR_REF(error));
} else {
Finish(grpc_http_parser_eof(&parser_));
if (handshake_mgr_ != nullptr) {
handshake_mgr_->Shutdown(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"HTTP request cancelled during security handshake"));
}
}
void OnWritten() { DoRead(); }
static void DoneWrite(void* arg, grpc_error_handle error) {
InternalRequest* req = static_cast<InternalRequest*>(arg);
if (error == GRPC_ERROR_NONE) {
req->OnWritten();
} else {
req->NextAddress(GRPC_ERROR_REF(error));
if (own_endpoint_ && ep_ != nullptr) {
grpc_endpoint_shutdown(
ep_, GRPC_ERROR_CREATE_FROM_STATIC_STRING("HTTP request cancelled"));
}
}
Unref();
}
void StartWrite() {
grpc_slice_ref_internal(request_text_);
grpc_slice_buffer_add(&outgoing_, request_text_);
grpc_endpoint_write(ep_, &outgoing_, &done_write_, nullptr);
}
static void OnHandshakeDone(void* arg, grpc_endpoint* ep) {
InternalRequest* req = static_cast<InternalRequest*>(arg);
if (!ep) {
req->NextAddress(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Unexplained handshake failure"));
return;
}
req->ep_ = ep;
req->StartWrite();
void HttpRequest::AppendError(grpc_error_handle error) {
if (overall_error_ == GRPC_ERROR_NONE) {
overall_error_ =
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed HTTP/1 client request");
}
const grpc_resolved_address* addr = &addresses_[next_address_ - 1];
std::string addr_text = grpc_sockaddr_to_uri(addr);
overall_error_ = grpc_error_add_child(
overall_error_,
grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, addr_text));
}
static void OnConnected(void* arg, grpc_error_handle error) {
InternalRequest* req = static_cast<InternalRequest*>(arg);
if (!req->ep_) {
req->NextAddress(GRPC_ERROR_REF(error));
return;
void HttpRequest::OnReadInternal(grpc_error_handle error) {
for (size_t i = 0; i < incoming_.count; i++) {
if (GRPC_SLICE_LENGTH(incoming_.slices[i])) {
have_read_byte_ = 1;
grpc_error_handle err =
grpc_http_parser_parse(&parser_, incoming_.slices[i], nullptr);
if (err != GRPC_ERROR_NONE) {
Finish(err);
return;
}
}
req->handshaker_->handshake(req, req->ep_,
req->ssl_host_override_.empty()
? req->host_.c_str()
: req->ssl_host_override_.c_str(),
req->deadline_, OnHandshakeDone);
}
void NextAddress(grpc_error_handle error) {
if (error != GRPC_ERROR_NONE) {
AppendError(error);
}
if (next_address_ == addresses_.size()) {
Finish(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
"Failed HTTP requests to all targets", &overall_error_, 1));
return;
}
const grpc_resolved_address* addr = &addresses_[next_address_++];
GRPC_CLOSURE_INIT(&connected_, OnConnected, this,
grpc_schedule_on_exec_ctx);
grpc_arg rq_arg = grpc_channel_arg_pointer_create(
const_cast<char*>(GRPC_ARG_RESOURCE_QUOTA), resource_quota_->c_ptr(),
grpc_resource_quota_arg_vtable());
grpc_channel_args channel_args{1, &rq_arg};
auto* args = CoreConfiguration::Get()
.channel_args_preconditioning()
.PreconditionChannelArgs(&channel_args);
grpc_tcp_client_connect(&connected_, &ep_, pollset_set_, args, addr,
deadline_);
grpc_channel_args_destroy(args);
if (cancelled_) {
Finish(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
"HTTP1 request cancelled during read", &overall_error_, 1));
} else if (error == GRPC_ERROR_NONE) {
DoRead();
} else if (!have_read_byte_) {
NextAddress(GRPC_ERROR_REF(error));
} else {
Finish(grpc_http_parser_eof(&parser_));
}
}
void OnResolved(
absl::StatusOr<std::vector<grpc_resolved_address>> addresses_or) {
dns_request_.reset();
if (!addresses_or.ok()) {
Finish(absl_status_to_grpc_error(addresses_or.status()));
return;
}
addresses_ = std::move(*addresses_or);
next_address_ = 0;
NextAddress(GRPC_ERROR_NONE);
void HttpRequest::ContinueDoneWriteAfterScheduleOnExecCtx(
void* arg, grpc_error_handle error) {
RefCountedPtr<HttpRequest> req(static_cast<HttpRequest*>(arg));
MutexLock lock(&req->mu_);
if (error == GRPC_ERROR_NONE && !req->cancelled_) {
req->OnWritten();
} else {
req->NextAddress(GRPC_ERROR_REF(error));
}
grpc_slice request_text_;
grpc_http_parser parser_;
std::vector<grpc_resolved_address> addresses_;
size_t next_address_ = 0;
grpc_endpoint* ep_ = nullptr;
ResourceQuotaRefPtr resource_quota_;
std::string host_;
std::string ssl_host_override_;
grpc_millis deadline_;
int have_read_byte_ = 0;
const grpc_httpcli_handshaker* handshaker_;
grpc_closure* on_done_;
grpc_polling_entity* pollent_;
grpc_pollset_set* pollset_set_;
grpc_iomgr_object iomgr_obj_;
grpc_slice_buffer incoming_;
grpc_slice_buffer outgoing_;
grpc_closure on_read_;
grpc_closure done_write_;
grpc_closure connected_;
grpc_error_handle overall_error_ = GRPC_ERROR_NONE;
OrphanablePtr<DNSResolver::Request> dns_request_;
};
} // namespace
} // namespace grpc_core
static grpc_httpcli_get_override g_get_override = nullptr;
static grpc_httpcli_post_override g_post_override = nullptr;
static void plaintext_handshake(void* arg, grpc_endpoint* endpoint,
const char* /*host*/, grpc_millis /*deadline*/,
void (*on_done)(void* arg,
grpc_endpoint* endpoint)) {
on_done(arg, endpoint);
}
const grpc_httpcli_handshaker grpc_httpcli_plaintext = {"http",
plaintext_handshake};
void HttpRequest::StartWrite() {
grpc_slice_ref_internal(request_text_);
grpc_slice_buffer_add(&outgoing_, request_text_);
Ref().release(); // ref held by pending write
grpc_endpoint_write(ep_, &outgoing_, &done_write_, nullptr);
}
static void internal_request_begin(
grpc_polling_entity* pollent, grpc_core::ResourceQuotaRefPtr resource_quota,
const grpc_httpcli_request* request, grpc_millis deadline,
grpc_closure* on_done, grpc_httpcli_response* response, const char* name,
const grpc_slice& request_text) {
new grpc_core::InternalRequest(
request_text, response, std::move(resource_quota), request->host,
request->ssl_host_override, deadline,
request->handshaker ? request->handshaker : &grpc_httpcli_plaintext,
on_done, pollent, name);
void HttpRequest::OnHandshakeDone(void* arg, grpc_error_handle error) {
auto* args = static_cast<HandshakerArgs*>(arg);
RefCountedPtr<HttpRequest> req(static_cast<HttpRequest*>(args->user_data));
MutexLock lock(&req->mu_);
req->own_endpoint_ = true;
if (error != GRPC_ERROR_NONE || req->cancelled_) {
gpr_log(GPR_ERROR, "Secure transport setup failed: %s",
grpc_error_std_string(error).c_str());
req->NextAddress(GRPC_ERROR_REF(error));
return;
}
grpc_channel_args_destroy(args->args);
grpc_slice_buffer_destroy_internal(args->read_buffer);
gpr_free(args->read_buffer);
req->ep_ = args->endpoint;
req->StartWrite();
}
void grpc_httpcli_get(grpc_polling_entity* pollent,
grpc_core::ResourceQuotaRefPtr resource_quota,
const grpc_httpcli_request* request, grpc_millis deadline,
grpc_closure* on_done, grpc_httpcli_response* response) {
if (g_get_override && g_get_override(request, deadline, on_done, response)) {
void HttpRequest::OnConnected(void* arg, grpc_error_handle error) {
RefCountedPtr<HttpRequest> req(static_cast<HttpRequest*>(arg));
MutexLock lock(&req->mu_);
req->connecting_ = false;
req->own_endpoint_ = true;
if (req->cancelled_) {
// since we were cancelled while connecting, Finish has already
// been called.
return;
}
std::string name =
absl::StrFormat("HTTP:GET:%s:%s", request->host, request->http.path);
internal_request_begin(pollent, std::move(resource_quota), request, deadline,
on_done, response, name.c_str(),
grpc_httpcli_format_get_request(request));
if (!req->ep_) {
req->NextAddress(GRPC_ERROR_REF(error));
return;
}
// TODO(yihuaz): treating nullptr channel_creds_ as insecure is
// a hack used to support the port server client (a test utility) in
// unsecure builds (when no definition of grpc_insecure_credentials_create
// exists). We can remove this hack and unconditionally assume a valid
// channel_creds_ object after unsecure builds are deleted, in
// https://github.com/grpc/grpc/pull/25586.
if (req->channel_creds_ == nullptr) {
gpr_log(GPR_DEBUG,
"HTTP request skipping handshake because creds are null");
req->StartWrite();
return;
}
// Create the security connector using the credentials and target name.
grpc_channel_args* new_args_from_connector = nullptr;
RefCountedPtr<grpc_channel_security_connector> sc =
req->channel_creds_->create_security_connector(
nullptr /*call_creds*/, req->uri_.authority().c_str(),
req->channel_args_, &new_args_from_connector);
if (sc == nullptr) {
req->Finish(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
"failed to create security connector", &req->overall_error_, 1));
return;
}
grpc_arg security_connector_arg = grpc_security_connector_to_arg(sc.get());
grpc_channel_args* new_args = grpc_channel_args_copy_and_add(
new_args_from_connector != nullptr ? new_args_from_connector
: req->channel_args_,
&security_connector_arg, 1);
grpc_channel_args_destroy(new_args_from_connector);
// Start the handshake
req->handshake_mgr_ = MakeRefCounted<HandshakeManager>();
CoreConfiguration::Get().handshaker_registry().AddHandshakers(
HANDSHAKER_CLIENT, new_args, req->pollset_set_,
req->handshake_mgr_.get());
req->Ref().release(); // ref held by pending handshake
grpc_endpoint* ep = req->ep_;
req->ep_ = nullptr;
req->own_endpoint_ = false;
req->handshake_mgr_->DoHandshake(ep, new_args, req->deadline_,
/*acceptor=*/nullptr, OnHandshakeDone,
/*user_data=*/req.get());
sc.reset(DEBUG_LOCATION, "httpcli");
grpc_channel_args_destroy(new_args);
}
void grpc_httpcli_post(grpc_polling_entity* pollent,
grpc_core::ResourceQuotaRefPtr resource_quota,
const grpc_httpcli_request* request,
const char* body_bytes, size_t body_size,
grpc_millis deadline, grpc_closure* on_done,
grpc_httpcli_response* response) {
if (g_post_override && g_post_override(request, body_bytes, body_size,
deadline, on_done, response)) {
void HttpRequest::NextAddress(grpc_error_handle error) {
if (error != GRPC_ERROR_NONE) {
AppendError(error);
}
if (cancelled_) {
Finish(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
"HTTP request was cancelled", &overall_error_, 1));
return;
}
std::string name =
absl::StrFormat("HTTP:POST:%s:%s", request->host, request->http.path);
internal_request_begin(
pollent, std::move(resource_quota), request, deadline, on_done, response,
name.c_str(),
grpc_httpcli_format_post_request(request, body_bytes, body_size));
if (next_address_ == addresses_.size()) {
Finish(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
"Failed HTTP requests to all targets", &overall_error_, 1));
return;
}
const grpc_resolved_address* addr = &addresses_[next_address_++];
GRPC_CLOSURE_INIT(&connected_, OnConnected, this, grpc_schedule_on_exec_ctx);
connecting_ = true;
own_endpoint_ = false;
Ref().release(); // ref held by pending connect
grpc_tcp_client_connect(&connected_, &ep_, pollset_set_, channel_args_, addr,
deadline_);
}
void grpc_httpcli_set_override(grpc_httpcli_get_override get,
grpc_httpcli_post_override post) {
g_get_override = get;
g_post_override = post;
void HttpRequest::OnResolved(
absl::StatusOr<std::vector<grpc_resolved_address>> addresses_or) {
RefCountedPtr<HttpRequest> unreffer(this);
MutexLock lock(&mu_);
dns_request_.reset();
if (!addresses_or.ok()) {
Finish(absl_status_to_grpc_error(addresses_or.status()));
return;
}
if (cancelled_) {
Finish(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"cancelled during DNS resolution"));
return;
}
addresses_ = std::move(*addresses_or);
next_address_ = 0;
NextAddress(GRPC_ERROR_NONE);
}
} // namespace grpc_core

@ -25,94 +25,196 @@
#include <grpc/support/time.h>
#include "src/core/lib/channel/handshaker.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/http/parser.h"
#include "src/core/lib/iomgr/endpoint.h"
#include "src/core/lib/iomgr/iomgr_internal.h"
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/iomgr/resolve_address.h"
#include "src/core/lib/resource_quota/resource_quota.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/uri/uri_parser.h"
/* User agent this library reports */
#define GRPC_HTTPCLI_USER_AGENT "grpc-httpcli/0.0"
/* TODO(ctiller): allow caching and capturing multiple requests for the
same content and combining them */
struct grpc_httpcli_handshaker {
const char* default_port;
void (*handshake)(void* arg, grpc_endpoint* endpoint, const char* host,
grpc_millis deadline,
void (*on_done)(void* arg, grpc_endpoint* endpoint));
};
extern const grpc_httpcli_handshaker grpc_httpcli_plaintext;
extern const grpc_httpcli_handshaker grpc_httpcli_ssl;
/* A request */
typedef struct grpc_httpcli_request {
/* The host name to connect to */
char* host;
/* The host to verify in the SSL handshake (or NULL) */
char* ssl_host_override;
/* The main part of the request
The following headers are supplied automatically and MUST NOT be set here:
Host, Connection, User-Agent */
grpc_http_request http;
/* handshaker to use ssl for the request */
const grpc_httpcli_handshaker* handshaker;
} grpc_httpcli_request;
/* Expose the parser response type as a httpcli response too */
typedef struct grpc_http_response grpc_httpcli_response;
/* Asynchronously perform a HTTP GET.
'pollent' indicates a grpc_polling_entity that is interested in the result
of the get - work on this entity may be used to progress the get
operation
'resource_quota' allows the caller to specify the quota against which to
allocate
'request' contains request parameters - these are caller owned and
can be destroyed once the call returns 'deadline' contains a deadline for the
request (or gpr_inf_future)
'on_response' is a callback to report results to */
void grpc_httpcli_get(grpc_polling_entity* pollent,
grpc_core::ResourceQuotaRefPtr resource_quota,
const grpc_httpcli_request* request, grpc_millis deadline,
grpc_closure* on_done, grpc_httpcli_response* response);
/* Asynchronously perform a HTTP POST.
'pollent' indicates a grpc_polling_entity that is interested in the result
of the post - work on this entity may be used to progress the post
operation
'resource_quota' allows the caller to specify the quota against which to
allocate
'request' contains request parameters - these are caller owned and can be
destroyed once the call returns
'body_bytes' and 'body_size' specify the payload for the post.
When there is no body, pass in NULL as body_bytes.
'deadline' contains a deadline for the request (or gpr_inf_future)
'em' points to a caller owned event manager that must be alive for the
lifetime of the request
'on_response' is a callback to report results to
Does not support ?var1=val1&var2=val2 in the path. */
void grpc_httpcli_post(grpc_polling_entity* pollent,
grpc_core::ResourceQuotaRefPtr resource_quota,
const grpc_httpcli_request* request,
const char* body_bytes, size_t body_size,
grpc_millis deadline, grpc_closure* on_done,
grpc_httpcli_response* response);
/* override functions return 1 if they handled the request, 0 otherwise */
typedef int (*grpc_httpcli_get_override)(const grpc_httpcli_request* request,
typedef int (*grpc_httpcli_get_override)(const grpc_http_request* request,
const char* host, const char* path,
grpc_millis deadline,
grpc_closure* on_complete,
grpc_httpcli_response* response);
typedef int (*grpc_httpcli_post_override)(const grpc_httpcli_request* request,
const char* body_bytes,
size_t body_size,
grpc_millis deadline,
grpc_closure* on_complete,
grpc_httpcli_response* response);
void grpc_httpcli_set_override(grpc_httpcli_get_override get,
grpc_httpcli_post_override post);
grpc_http_response* response);
typedef int (*grpc_httpcli_post_override)(
const grpc_http_request* request, const char* host, const char* path,
const char* body_bytes, size_t body_size, grpc_millis deadline,
grpc_closure* on_complete, grpc_http_response* response);
namespace grpc_core {
// Tracks an in-progress GET or POST request. Calling \a Start()
// begins async work and calling \a Orphan() arranges for async work
// to be completed as sooon as possible (possibly aborting the request
// if it's in flight).
// TODO(ctiller): allow caching and capturing multiple requests for the
// same content and combining them
class HttpRequest : public InternallyRefCounted<HttpRequest> {
public:
// Asynchronously perform a HTTP GET.
// 'uri' is the target to make the request to. The scheme field is used to
// determine the port number. The authority field is the target host. The
// path field determines the path of the request. No other fields are used.
// 'args' are optional channel args for the request.
// 'pollent' indicates a grpc_polling_entity that is interested in the result
// of the get - work on this entity may be used to progress the get
// operation
// 'request' contains request parameters - these are caller owned and
// can be destroyed once the call returns
// 'deadline' contains a deadline for the request (or gpr_inf_future)
// 'on_done' is a callback to report results to
// 'channel_creds' are used to configurably secure the connection.
// For insecure requests, use grpc_insecure_credentials_create.
// For secure requests, use CreateHttpRequestSSLCredentials().
// nullptr is treated as insecure credentials.
// TODO(yihuaz): disallow nullptr as a value after unsecure builds
// are removed.
static OrphanablePtr<HttpRequest> Get(
URI uri, const grpc_channel_args* args, grpc_polling_entity* pollent,
const grpc_http_request* request, grpc_millis deadline,
grpc_closure* on_done, grpc_http_response* response,
RefCountedPtr<grpc_channel_credentials> channel_creds)
GRPC_MUST_USE_RESULT;
// Asynchronously perform a HTTP POST.
// 'uri' is the target to make the request to. The scheme field is used to
// determine the port number. The authority field is the target host. The
// path field determines the path of the request. No other fields are used.
// 'args' are optional channel args for the request.
// 'pollent' indicates a grpc_polling_entity that is interested in the result
// of the post - work on this entity may be used to progress the post
// operation
// 'request' contains request parameters - these are caller owned and can be
// destroyed once the call returns
// 'deadline' contains a deadline for the request (or gpr_inf_future)
// 'on_done' is a callback to report results to
// 'channel_creds' are used to configurably secure the connection.
// For insecure requests, use grpc_insecure_credentials_create.
// For secure requests, use CreateHttpRequestSSLCredentials().
// nullptr is treated as insecure credentials.
// TODO(apolcyn): disallow nullptr as a value after unsecure builds
// are removed.
// Does not support ?var1=val1&var2=val2 in the path.
static OrphanablePtr<HttpRequest> Post(
URI uri, const grpc_channel_args* args, grpc_polling_entity* pollent,
const grpc_http_request* request, grpc_millis deadline,
grpc_closure* on_done, grpc_http_response* response,
RefCountedPtr<grpc_channel_credentials> channel_creds)
GRPC_MUST_USE_RESULT;
HttpRequest(URI uri, const grpc_slice& request_text,
grpc_http_response* response, grpc_millis deadline,
const grpc_channel_args* channel_args, grpc_closure* on_done,
grpc_polling_entity* pollent, const char* name,
absl::optional<std::function<void()>> test_only_generate_response,
RefCountedPtr<grpc_channel_credentials> channel_creds);
~HttpRequest() override;
void Start();
void Orphan() override;
static void SetOverride(grpc_httpcli_get_override get,
grpc_httpcli_post_override post);
private:
void Finish(grpc_error_handle error) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
grpc_polling_entity_del_from_pollset_set(pollent_, pollset_set_);
ExecCtx::Run(DEBUG_LOCATION, on_done_, error);
}
void AppendError(grpc_error_handle error) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
void DoRead() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
Ref().release(); // ref held by pending read
grpc_endpoint_read(ep_, &incoming_, &on_read_, /*urgent=*/true);
}
static void OnRead(void* user_data, grpc_error_handle error) {
HttpRequest* req = static_cast<HttpRequest*>(user_data);
ExecCtx::Run(DEBUG_LOCATION,
&req->continue_on_read_after_schedule_on_exec_ctx_,
GRPC_ERROR_REF(error));
}
// Needed since OnRead may be called inline from grpc_endpoint_read
static void ContinueOnReadAfterScheduleOnExecCtx(void* user_data,
grpc_error_handle error) {
RefCountedPtr<HttpRequest> req(static_cast<HttpRequest*>(user_data));
MutexLock lock(&req->mu_);
req->OnReadInternal(error);
}
void OnReadInternal(grpc_error_handle error)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
void OnWritten() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) { DoRead(); }
static void DoneWrite(void* arg, grpc_error_handle error) {
HttpRequest* req = static_cast<HttpRequest*>(arg);
ExecCtx::Run(DEBUG_LOCATION,
&req->continue_done_write_after_schedule_on_exec_ctx_,
GRPC_ERROR_REF(error));
}
// Needed since DoneWrite may be called inline from grpc_endpoint_write
static void ContinueDoneWriteAfterScheduleOnExecCtx(void* arg,
grpc_error_handle error);
void StartWrite() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
static void OnHandshakeDone(void* arg, grpc_error_handle error);
static void OnConnected(void* arg, grpc_error_handle error);
void NextAddress(grpc_error_handle error) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
void OnResolved(
absl::StatusOr<std::vector<grpc_resolved_address>> addresses_or);
const URI uri_;
const grpc_slice request_text_;
const grpc_millis deadline_;
const grpc_channel_args* channel_args_;
RefCountedPtr<grpc_channel_credentials> channel_creds_;
grpc_closure on_read_;
grpc_closure continue_on_read_after_schedule_on_exec_ctx_;
grpc_closure done_write_;
grpc_closure continue_done_write_after_schedule_on_exec_ctx_;
grpc_closure connected_;
grpc_endpoint* ep_ = nullptr;
grpc_closure* on_done_;
ResourceQuotaRefPtr resource_quota_;
grpc_polling_entity* pollent_;
grpc_pollset_set* pollset_set_;
const absl::optional<std::function<void()>> test_only_generate_response_;
Mutex mu_;
RefCountedPtr<HandshakeManager> handshake_mgr_ ABSL_GUARDED_BY(mu_);
bool own_endpoint_ ABSL_GUARDED_BY(mu_) = true;
bool cancelled_ ABSL_GUARDED_BY(mu_) = false;
bool connecting_ ABSL_GUARDED_BY(mu_) = false;
grpc_http_parser parser_ ABSL_GUARDED_BY(mu_);
std::vector<grpc_resolved_address> addresses_ ABSL_GUARDED_BY(mu_);
size_t next_address_ ABSL_GUARDED_BY(mu_) = 0;
int have_read_byte_ ABSL_GUARDED_BY(mu_) = 0;
grpc_iomgr_object iomgr_obj_ ABSL_GUARDED_BY(mu_);
grpc_slice_buffer incoming_ ABSL_GUARDED_BY(mu_);
grpc_slice_buffer outgoing_ ABSL_GUARDED_BY(mu_);
grpc_error_handle overall_error_ ABSL_GUARDED_BY(mu_) = GRPC_ERROR_NONE;
OrphanablePtr<DNSResolver::Request> dns_request_ ABSL_GUARDED_BY(mu_);
};
} // namespace grpc_core
#endif /* GRPC_CORE_LIB_HTTP_HTTPCLI_H */

@ -31,7 +31,6 @@
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/http/httpcli.h"
#include "src/core/lib/iomgr/pollset.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/security/security_connector/ssl_utils.h"
@ -39,6 +38,10 @@
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/tsi/ssl_transport_security.h"
namespace grpc_core {
namespace {
class grpc_httpcli_ssl_channel_security_connector final
: public grpc_channel_security_connector {
public:
@ -69,7 +72,7 @@ class grpc_httpcli_ssl_channel_security_connector final
void add_handshakers(const grpc_channel_args* args,
grpc_pollset_set* /*interested_parties*/,
grpc_core::HandshakeManager* handshake_mgr) override {
HandshakeManager* handshake_mgr) override {
tsi_handshaker* handshaker = nullptr;
if (handshaker_factory_ != nullptr) {
tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker(
@ -79,8 +82,7 @@ class grpc_httpcli_ssl_channel_security_connector final
tsi_result_to_string(result));
}
}
handshake_mgr->Add(
grpc_core::SecurityHandshakerCreate(handshaker, this, args));
handshake_mgr->Add(SecurityHandshakerCreate(handshaker, this, args));
}
tsi_ssl_client_handshaker_factory* handshaker_factory() const {
@ -88,7 +90,7 @@ class grpc_httpcli_ssl_channel_security_connector final
}
void check_peer(tsi_peer peer, grpc_endpoint* /*ep*/,
grpc_core::RefCountedPtr<grpc_auth_context>* /*auth_context*/,
RefCountedPtr<grpc_auth_context>* /*auth_context*/,
grpc_closure* on_peer_checked) override {
grpc_error_handle error = GRPC_ERROR_NONE;
@ -98,7 +100,7 @@ class grpc_httpcli_ssl_channel_security_connector final
error = GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat(
"Peer name ", secure_peer_name_, " is not in peer certificate"));
}
grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error);
ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error);
tsi_peer_destruct(&peer);
}
@ -134,17 +136,17 @@ class grpc_httpcli_ssl_channel_security_connector final
char* secure_peer_name_;
};
static grpc_core::RefCountedPtr<grpc_channel_security_connector>
RefCountedPtr<grpc_channel_security_connector>
httpcli_ssl_channel_security_connector_create(
const char* pem_root_certs, const tsi_ssl_root_certs_store* root_store,
const char* secure_peer_name, grpc_channel_args* /*channel_args*/) {
const char* secure_peer_name) {
if (secure_peer_name != nullptr && pem_root_certs == nullptr) {
gpr_log(GPR_ERROR,
"Cannot assert a secure peer name without a trust root.");
return nullptr;
}
grpc_core::RefCountedPtr<grpc_httpcli_ssl_channel_security_connector> c =
grpc_core::MakeRefCounted<grpc_httpcli_ssl_channel_security_connector>(
RefCountedPtr<grpc_httpcli_ssl_channel_security_connector> c =
MakeRefCounted<grpc_httpcli_ssl_channel_security_connector>(
secure_peer_name == nullptr ? nullptr : gpr_strdup(secure_peer_name));
tsi_result result = c->InitHandshakerFactory(pem_root_certs, root_store);
if (result != TSI_OK) {
@ -155,61 +157,45 @@ httpcli_ssl_channel_security_connector_create(
return c;
}
/* handshaker */
class HttpRequestSSLCredentials : public grpc_channel_credentials {
public:
HttpRequestSSLCredentials() : grpc_channel_credentials("HttpRequestSSL") {}
~HttpRequestSSLCredentials() override {}
RefCountedPtr<grpc_channel_security_connector> create_security_connector(
RefCountedPtr<grpc_call_credentials> /*call_creds*/, const char* target,
const grpc_channel_args* args,
grpc_channel_args** /*new_args*/) override {
const char* pem_root_certs = DefaultSslRootStore::GetPemRootCerts();
const tsi_ssl_root_certs_store* root_store =
DefaultSslRootStore::GetRootStore();
if (root_store == nullptr) {
gpr_log(GPR_ERROR, "Could not get default pem root certs.");
return nullptr;
}
const char* ssl_host_override =
grpc_channel_args_find_string(args, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG);
if (ssl_host_override != nullptr) {
target = ssl_host_override;
}
return httpcli_ssl_channel_security_connector_create(pem_root_certs,
root_store, target);
}
struct on_done_closure {
void (*func)(void* arg, grpc_endpoint* endpoint);
void* arg;
grpc_core::RefCountedPtr<grpc_core::HandshakeManager> handshake_mgr;
};
static void on_handshake_done(void* arg, grpc_error_handle error) {
auto* args = static_cast<grpc_core::HandshakerArgs*>(arg);
on_done_closure* c = static_cast<on_done_closure*>(args->user_data);
if (error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR, "Secure transport setup failed: %s",
grpc_error_std_string(error).c_str());
c->func(c->arg, nullptr);
} else {
grpc_channel_args_destroy(args->args);
grpc_slice_buffer_destroy_internal(args->read_buffer);
gpr_free(args->read_buffer);
c->func(c->arg, args->endpoint);
RefCountedPtr<grpc_channel_credentials> duplicate_without_call_credentials()
override {
return Ref();
}
delete c;
}
static void ssl_handshake(void* arg, grpc_endpoint* tcp, const char* host,
grpc_millis deadline,
void (*on_done)(void* arg, grpc_endpoint* endpoint)) {
auto* c = new on_done_closure();
const char* pem_root_certs =
grpc_core::DefaultSslRootStore::GetPemRootCerts();
const tsi_ssl_root_certs_store* root_store =
grpc_core::DefaultSslRootStore::GetRootStore();
if (root_store == nullptr) {
gpr_log(GPR_ERROR, "Could not get default pem root certs.");
on_done(arg, nullptr);
gpr_free(c);
return;
grpc_channel_args* update_arguments(grpc_channel_args* args) override {
return args;
}
c->func = on_done;
c->arg = arg;
grpc_core::RefCountedPtr<grpc_channel_security_connector> sc =
httpcli_ssl_channel_security_connector_create(
pem_root_certs, root_store, host,
static_cast<grpc_core::HandshakerArgs*>(arg)->args);
GPR_ASSERT(sc != nullptr);
grpc_arg channel_arg = grpc_security_connector_to_arg(sc.get());
grpc_channel_args args = {1, &channel_arg};
c->handshake_mgr = grpc_core::MakeRefCounted<grpc_core::HandshakeManager>();
grpc_core::CoreConfiguration::Get().handshaker_registry().AddHandshakers(
grpc_core::HANDSHAKER_CLIENT, &args,
/*interested_parties=*/nullptr, c->handshake_mgr.get());
c->handshake_mgr->DoHandshake(tcp, /*channel_args=*/nullptr, deadline,
/*acceptor=*/nullptr, on_handshake_done,
/*user_data=*/c);
sc.reset(DEBUG_LOCATION, "httpcli");
};
} // namespace
RefCountedPtr<grpc_channel_credentials> CreateHttpRequestSSLCredentials() {
return MakeRefCounted<HttpRequestSSLCredentials>();
}
const grpc_httpcli_handshaker grpc_httpcli_ssl = {"https", ssl_handshake};
} // namespace grpc_core

@ -0,0 +1,37 @@
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_CORE_LIB_HTTP_HTTPCLI_SSL_CREDENTIALS_H
#define GRPC_CORE_LIB_HTTP_HTTPCLI_SSL_CREDENTIALS_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/security/credentials/credentials.h"
namespace grpc_core {
// Creates a channel credentials suitable for use with the
// HttpRequest::Get and HttpRequest::Post APIs. Notably, this allows
// HTTP1 requests to use secure connections without ALPN (as the
// typical gRPC SSL credentials do).
//
// These credentials are NOT INTENDED FOR USE with gRPC channels, and
// MUST ONLY BE USED with the HttpRequest::Get and Post APIs.
RefCountedPtr<grpc_channel_credentials> CreateHttpRequestSSLCredentials();
} // namespace grpc_core
#endif // GRPC_CORE_LIB_HTTP_HTTPCLI_SSL_CREDENTIALS_H

@ -56,7 +56,7 @@ typedef enum {
typedef struct grpc_http_request {
/* Method of the request (e.g. GET, POST) */
char* method;
/* The path of the resource to fetch */
/* The path of the resource to fetch (only used for parsed requests) */
char* path;
/* HTTP version to use */
grpc_http_version version;

@ -31,8 +31,6 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/http/httpcli.h"
#include "src/core/lib/http/parser.h"
#include "src/core/lib/iomgr/executor.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/surface/api_trace.h"

@ -30,8 +30,6 @@
#include <grpc/support/sync.h>
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/http/httpcli.h"
#include "src/core/lib/http/parser.h"
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/security/security_connector/security_connector.h"
#include "src/core/lib/transport/metadata_batch.h"
@ -253,29 +251,4 @@ grpc_server_credentials* grpc_server_credentials_from_arg(const grpc_arg* arg);
grpc_server_credentials* grpc_find_server_credentials_in_args(
const grpc_channel_args* args);
/* -- Credentials Metadata Request. -- */
struct grpc_credentials_metadata_request {
explicit grpc_credentials_metadata_request(
grpc_core::RefCountedPtr<grpc_call_credentials> creds)
: creds(std::move(creds)) {}
~grpc_credentials_metadata_request() {
grpc_http_response_destroy(&response);
}
grpc_core::RefCountedPtr<grpc_call_credentials> creds;
grpc_http_response response;
};
inline grpc_credentials_metadata_request*
grpc_credentials_metadata_request_create(
grpc_core::RefCountedPtr<grpc_call_credentials> creds) {
return new grpc_credentials_metadata_request(std::move(creds));
}
inline void grpc_credentials_metadata_request_destroy(
grpc_credentials_metadata_request* r) {
delete r;
}
#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H */

@ -22,6 +22,7 @@
#include "absl/strings/str_replace.h"
#include "src/core/lib/gpr/env.h"
#include "src/core/lib/http/httpcli_ssl_credentials.h"
namespace grpc_core {
@ -160,18 +161,24 @@ void AwsExternalAccountCredentials::RetrieveRegion() {
"Invalid region url. %s", uri.status().ToString())));
return;
}
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(uri->authority().c_str());
request.http.path = gpr_strdup(uri->path().c_str());
request.handshaker =
uri->scheme() == "https" ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext;
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
grpc_http_response_destroy(&ctx_->response);
ctx_->response = {};
GRPC_CLOSURE_INIT(&ctx_->closure, OnRetrieveRegion, this, nullptr);
grpc_httpcli_get(ctx_->pollent, ResourceQuota::Default(), &request,
ctx_->deadline, &ctx_->closure, &ctx_->response);
grpc_http_request_destroy(&request.http);
RefCountedPtr<grpc_channel_credentials> http_request_creds;
if (uri->scheme() == "http") {
http_request_creds = RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create());
} else {
http_request_creds = CreateHttpRequestSSLCredentials();
}
http_request_ =
HttpRequest::Get(std::move(*uri), nullptr /* channel args */,
ctx_->pollent, &request, ctx_->deadline, &ctx_->closure,
&ctx_->response, std::move(http_request_creds));
http_request_->Start();
grpc_http_request_destroy(&request);
}
void AwsExternalAccountCredentials::OnRetrieveRegion(void* arg,
@ -206,19 +213,25 @@ void AwsExternalAccountCredentials::RetrieveRoleName() {
absl::StrFormat("Invalid url: %s.", uri.status().ToString())));
return;
}
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(uri->authority().c_str());
request.http.path = gpr_strdup(uri->path().c_str());
request.handshaker =
uri->scheme() == "https" ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext;
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
grpc_http_response_destroy(&ctx_->response);
ctx_->response = {};
GRPC_CLOSURE_INIT(&ctx_->closure, OnRetrieveRoleName, this, nullptr);
// TODO(ctiller): use the caller's resource quota.
grpc_httpcli_get(ctx_->pollent, ResourceQuota::Default(), &request,
ctx_->deadline, &ctx_->closure, &ctx_->response);
grpc_http_request_destroy(&request.http);
RefCountedPtr<grpc_channel_credentials> http_request_creds;
if (uri->scheme() == "http") {
http_request_creds = RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create());
} else {
http_request_creds = CreateHttpRequestSSLCredentials();
}
http_request_ =
HttpRequest::Get(std::move(*uri), nullptr /* channel args */,
ctx_->pollent, &request, ctx_->deadline, &ctx_->closure,
&ctx_->response, std::move(http_request_creds));
http_request_->Start();
grpc_http_request_destroy(&request);
}
void AwsExternalAccountCredentials::OnRetrieveRoleName(
@ -265,19 +278,25 @@ void AwsExternalAccountCredentials::RetrieveSigningKeys() {
"Invalid url with role name: %s.", uri.status().ToString())));
return;
}
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(uri->authority().c_str());
request.http.path = gpr_strdup(uri->path().c_str());
request.handshaker =
uri->scheme() == "https" ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext;
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
grpc_http_response_destroy(&ctx_->response);
ctx_->response = {};
GRPC_CLOSURE_INIT(&ctx_->closure, OnRetrieveSigningKeys, this, nullptr);
// TODO(ctiller): use the caller's resource quota.
grpc_httpcli_get(ctx_->pollent, ResourceQuota::Default(), &request,
ctx_->deadline, &ctx_->closure, &ctx_->response);
grpc_http_request_destroy(&request.http);
RefCountedPtr<grpc_channel_credentials> http_request_creds;
if (uri->scheme() == "http") {
http_request_creds = RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create());
} else {
http_request_creds = CreateHttpRequestSSLCredentials();
}
http_request_ =
HttpRequest::Get(std::move(*uri), nullptr /* channel args */,
ctx_->pollent, &request, ctx_->deadline, &ctx_->closure,
&ctx_->response, std::move(http_request_creds));
http_request_->Start();
grpc_http_request_destroy(&request);
}
void AwsExternalAccountCredentials::OnRetrieveSigningKeys(

@ -56,6 +56,7 @@ class AwsExternalAccountCredentials final : public ExternalAccountCredentials {
grpc_error_handle error);
std::string audience_;
OrphanablePtr<HttpRequest> http_request_;
// Fields of credential source
std::string region_url_;

@ -25,6 +25,7 @@
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "src/core/lib/http/httpcli_ssl_credentials.h"
#include "src/core/lib/http/parser.h"
#include "src/core/lib/security/credentials/external/aws_external_account_credentials.h"
#include "src/core/lib/security/credentials/external/file_external_account_credentials.h"
@ -267,15 +268,13 @@ void ExternalAccountCredentials::ExchangeToken(
uri.status().ToString())));
return;
}
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(uri->authority().c_str());
request.http.path = gpr_strdup(uri->path().c_str());
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
grpc_http_header* headers = nullptr;
if (!options_.client_id.empty() && !options_.client_secret.empty()) {
request.http.hdr_count = 2;
request.hdr_count = 2;
headers = static_cast<grpc_http_header*>(
gpr_malloc(sizeof(grpc_http_header) * request.http.hdr_count));
gpr_malloc(sizeof(grpc_http_header) * request.hdr_count));
headers[0].key = gpr_strdup("Content-Type");
headers[0].value = gpr_strdup("application/x-www-form-urlencoded");
std::string raw_cred =
@ -287,15 +286,13 @@ void ExternalAccountCredentials::ExchangeToken(
headers[1].value = gpr_strdup(str.c_str());
gpr_free(encoded_cred);
} else {
request.http.hdr_count = 1;
request.hdr_count = 1;
headers = static_cast<grpc_http_header*>(
gpr_malloc(sizeof(grpc_http_header) * request.http.hdr_count));
gpr_malloc(sizeof(grpc_http_header) * request.hdr_count));
headers[0].key = gpr_strdup("Content-Type");
headers[0].value = gpr_strdup("application/x-www-form-urlencoded");
}
request.http.hdrs = headers;
request.handshaker =
uri->scheme() == "https" ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext;
request.hdrs = headers;
std::vector<std::string> body_parts;
body_parts.push_back(
absl::StrFormat("audience=%s", UrlEncode(options_.audience).c_str()));
@ -323,13 +320,26 @@ void ExternalAccountCredentials::ExchangeToken(
body_parts.push_back(absl::StrFormat(
"options=%s", UrlEncode(addtional_options_json.Dump()).c_str()));
std::string body = absl::StrJoin(body_parts, "&");
request.body = const_cast<char*>(body.c_str());
request.body_length = body.size();
grpc_http_response_destroy(&ctx_->response);
ctx_->response = {};
GRPC_CLOSURE_INIT(&ctx_->closure, OnExchangeToken, this, nullptr);
grpc_httpcli_post(ctx_->pollent, ResourceQuota::Default(), &request,
body.c_str(), body.size(), ctx_->deadline, &ctx_->closure,
&ctx_->response);
grpc_http_request_destroy(&request.http);
GPR_ASSERT(http_request_ == nullptr);
RefCountedPtr<grpc_channel_credentials> http_request_creds;
if (uri->scheme() == "http") {
http_request_creds = RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create());
} else {
http_request_creds = CreateHttpRequestSSLCredentials();
}
http_request_ =
HttpRequest::Post(std::move(*uri), nullptr /* channel args */,
ctx_->pollent, &request, ctx_->deadline, &ctx_->closure,
&ctx_->response, std::move(http_request_creds));
http_request_->Start();
request.body = nullptr;
grpc_http_request_destroy(&request);
}
void ExternalAccountCredentials::OnExchangeToken(void* arg,
@ -341,6 +351,7 @@ void ExternalAccountCredentials::OnExchangeToken(void* arg,
void ExternalAccountCredentials::OnExchangeTokenInternal(
grpc_error_handle error) {
http_request_.reset();
if (error != GRPC_ERROR_NONE) {
FinishTokenFetch(error);
} else {
@ -390,31 +401,39 @@ void ExternalAccountCredentials::ImpersenateServiceAccount() {
options_.service_account_impersonation_url, uri.status().ToString())));
return;
}
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(uri->authority().c_str());
request.http.path = gpr_strdup(uri->path().c_str());
request.http.hdr_count = 2;
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
request.hdr_count = 2;
grpc_http_header* headers = static_cast<grpc_http_header*>(
gpr_malloc(sizeof(grpc_http_header) * request.http.hdr_count));
gpr_malloc(sizeof(grpc_http_header) * request.hdr_count));
headers[0].key = gpr_strdup("Content-Type");
headers[0].value = gpr_strdup("application/x-www-form-urlencoded");
std::string str = absl::StrFormat("Bearer %s", access_token);
headers[1].key = gpr_strdup("Authorization");
headers[1].value = gpr_strdup(str.c_str());
request.http.hdrs = headers;
request.handshaker =
uri->scheme() == "https" ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext;
request.hdrs = headers;
std::string scope = absl::StrJoin(scopes_, " ");
std::string body = absl::StrFormat("scope=%s", scope);
request.body = const_cast<char*>(body.c_str());
request.body_length = body.size();
grpc_http_response_destroy(&ctx_->response);
ctx_->response = {};
GRPC_CLOSURE_INIT(&ctx_->closure, OnImpersenateServiceAccount, this, nullptr);
// TODO(ctiller): Use the callers resource quota.
grpc_httpcli_post(ctx_->pollent, ResourceQuota::Default(), &request,
body.c_str(), body.size(), ctx_->deadline, &ctx_->closure,
&ctx_->response);
grpc_http_request_destroy(&request.http);
GPR_ASSERT(http_request_ == nullptr);
RefCountedPtr<grpc_channel_credentials> http_request_creds;
if (uri->scheme() == "http") {
http_request_creds = RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create());
} else {
http_request_creds = CreateHttpRequestSSLCredentials();
}
http_request_ = HttpRequest::Post(
std::move(*uri), nullptr, ctx_->pollent, &request, ctx_->deadline,
&ctx_->closure, &ctx_->response, std::move(http_request_creds));
http_request_->Start();
request.body = nullptr;
grpc_http_request_destroy(&request);
}
void ExternalAccountCredentials::OnImpersenateServiceAccount(
@ -426,6 +445,7 @@ void ExternalAccountCredentials::OnImpersenateServiceAccount(
void ExternalAccountCredentials::OnImpersenateServiceAccountInternal(
grpc_error_handle error) {
http_request_.reset();
if (error != GRPC_ERROR_NONE) {
FinishTokenFetch(error);
return;

@ -107,6 +107,7 @@ class ExternalAccountCredentials
Options options_;
std::vector<std::string> scopes_;
OrphanablePtr<HttpRequest> http_request_;
HTTPRequestContext* ctx_ = nullptr;
grpc_credentials_metadata_request* metadata_req_ = nullptr;
grpc_iomgr_cb_func response_cb_ = nullptr;

@ -21,6 +21,9 @@
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "src/core/lib/http/httpcli_ssl_credentials.h"
#include "src/core/lib/transport/error_utils.h"
namespace grpc_core {
RefCountedPtr<UrlExternalAccountCredentials>
@ -120,31 +123,48 @@ void UrlExternalAccountCredentials::RetrieveSubjectToken(
"Missing HTTPRequestContext to start subject token retrieval."));
return;
}
auto url_for_request =
URI::Create(url_.scheme(), url_.authority(), url_full_path_,
{} /* query params */, "" /* fragment */);
if (!url_for_request.ok()) {
FinishRetrieveSubjectToken(
"", absl_status_to_grpc_error(url_for_request.status()));
return;
}
ctx_ = ctx;
cb_ = cb;
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(url_.authority().c_str());
request.http.path = gpr_strdup(url_full_path_.c_str());
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
request.path = gpr_strdup(url_full_path_.c_str());
grpc_http_header* headers = nullptr;
request.http.hdr_count = headers_.size();
request.hdr_count = headers_.size();
headers = static_cast<grpc_http_header*>(
gpr_malloc(sizeof(grpc_http_header) * request.http.hdr_count));
gpr_malloc(sizeof(grpc_http_header) * request.hdr_count));
int i = 0;
for (auto const& header : headers_) {
headers[i].key = gpr_strdup(header.first.c_str());
headers[i].value = gpr_strdup(header.second.c_str());
++i;
}
request.http.hdrs = headers;
request.handshaker =
url_.scheme() == "https" ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext;
request.hdrs = headers;
grpc_http_response_destroy(&ctx_->response);
ctx_->response = {};
GRPC_CLOSURE_INIT(&ctx_->closure, OnRetrieveSubjectToken, this, nullptr);
grpc_httpcli_get(ctx_->pollent, ResourceQuota::Default(), &request,
ctx_->deadline, &ctx_->closure, &ctx_->response);
grpc_http_request_destroy(&request.http);
GPR_ASSERT(http_request_ == nullptr);
RefCountedPtr<grpc_channel_credentials> http_request_creds;
if (url_.scheme() == "http") {
http_request_creds = RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create());
} else {
http_request_creds = RefCountedPtr<grpc_channel_credentials>(
CreateHttpRequestSSLCredentials());
}
http_request_ =
HttpRequest::Get(std::move(*url_for_request), nullptr /* channel args */,
ctx_->pollent, &request, ctx_->deadline, &ctx_->closure,
&ctx_->response, std::move(http_request_creds));
http_request_->Start();
grpc_http_request_destroy(&request);
}
void UrlExternalAccountCredentials::OnRetrieveSubjectToken(
@ -156,6 +176,7 @@ void UrlExternalAccountCredentials::OnRetrieveSubjectToken(
void UrlExternalAccountCredentials::OnRetrieveSubjectTokenInternal(
grpc_error_handle error) {
http_request_.reset();
if (error != GRPC_ERROR_NONE) {
FinishRetrieveSubjectToken("", error);
return;

@ -51,6 +51,7 @@ class UrlExternalAccountCredentials final : public ExternalAccountCredentials {
std::string format_type_;
std::string format_subject_token_field_name_;
OrphanablePtr<HttpRequest> http_request_;
HTTPRequestContext* ctx_ = nullptr;
std::function<void(std::string, grpc_error_handle)> cb_ = nullptr;
};

@ -171,7 +171,7 @@ static void destroy_pollset(void* p, grpc_error_handle /*e*/) {
static int is_metadata_server_reachable() {
metadata_server_detector detector;
grpc_httpcli_request request;
grpc_http_request request;
grpc_closure destroy_closure;
/* The http call is local. If it takes more than one sec, it is for sure not
on compute engine. */
@ -182,15 +182,20 @@ static int is_metadata_server_reachable() {
detector.pollent = grpc_polling_entity_create_from_pollset(pollset);
detector.is_done = 0;
detector.success = 0;
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(GRPC_COMPUTE_ENGINE_DETECTION_HOST);
request.http.path = const_cast<char*>("/");
grpc_httpcli_get(
&detector.pollent, grpc_core::ResourceQuota::Default(), &request,
memset(&request, 0, sizeof(grpc_http_request));
auto uri =
grpc_core::URI::Create("http", GRPC_COMPUTE_ENGINE_DETECTION_HOST, "/",
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok()); // params are hardcoded
auto http_request = grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, &detector.pollent, &request,
grpc_core::ExecCtx::Get()->Now() + max_detection_delay,
GRPC_CLOSURE_CREATE(on_metadata_server_detection_http_response, &detector,
grpc_schedule_on_exec_ctx),
&detector.response);
&detector.response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
http_request->Start();
grpc_core::ExecCtx::Get()->Flush();
/* Block until we get the response. This is not ideal but this should only be
called once for the lifetime of the process by the default credentials. */
@ -206,6 +211,7 @@ static int is_metadata_server_reachable() {
}
}
gpr_mu_unlock(g_polling_mu);
http_request.reset();
GRPC_CLOSURE_INIT(&destroy_closure, destroy_pollset,
grpc_polling_entity_pollset(&detector.pollent),
grpc_schedule_on_exec_ctx);

@ -35,6 +35,7 @@
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/manual_constructor.h"
#include "src/core/lib/http/httpcli.h"
#include "src/core/lib/http/httpcli_ssl_credentials.h"
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/slice/b64.h"
#include "src/core/lib/slice/slice_internal.h"
@ -344,6 +345,7 @@ struct verifier_cb_ctx {
void* user_data;
grpc_jwt_verification_done_cb user_cb;
grpc_http_response responses[HTTP_RESPONSE_COUNT];
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request;
};
/* Takes ownership of the header, claims and signature. */
static verifier_cb_ctx* verifier_cb_ctx_create(
@ -397,7 +399,7 @@ struct grpc_jwt_verifier {
size_t allocated_mappings;
};
static Json json_from_http(const grpc_httpcli_response* response) {
static Json json_from_http(const grpc_http_response* response) {
if (response == nullptr) {
gpr_log(GPR_ERROR, "HTTP response is NULL.");
return Json(); // JSON null
@ -666,9 +668,13 @@ static void on_openid_config_retrieved(void* user_data,
verifier_cb_ctx* ctx = static_cast<verifier_cb_ctx*>(user_data);
const grpc_http_response* response = &ctx->responses[HTTP_RESPONSE_OPENID];
Json json = json_from_http(response);
grpc_httpcli_request req;
grpc_http_request req;
memset(&req, 0, sizeof(grpc_http_request));
const char* jwks_uri;
const Json* cur;
absl::StatusOr<grpc_core::URI> uri;
char* host;
char* path;
/* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time. */
if (json.type() == Json::Type::JSON_NULL) goto error;
@ -684,24 +690,30 @@ static void on_openid_config_retrieved(void* user_data,
goto error;
}
jwks_uri += 8;
req.handshaker = &grpc_httpcli_ssl;
req.host = gpr_strdup(jwks_uri);
req.http.path = const_cast<char*>(strchr(jwks_uri, '/'));
if (req.http.path == nullptr) {
req.http.path = const_cast<char*>("");
host = gpr_strdup(jwks_uri);
path = const_cast<char*>(strchr(jwks_uri, '/'));
if (path == nullptr) {
path = const_cast<char*>("");
} else {
*(req.host + (req.http.path - jwks_uri)) = '\0';
*(host + (path - jwks_uri)) = '\0';
}
/* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
grpc_httpcli_get(
&ctx->pollent, grpc_core::ResourceQuota::Default(), &req,
uri = grpc_core::URI::Create("https", host, path, {} /* query params /*/,
"" /* fragment */);
if (!uri.ok()) {
goto error;
}
ctx->http_request = grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, &ctx->pollent, &req,
grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay,
GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx),
&ctx->responses[HTTP_RESPONSE_KEYS]);
gpr_free(req.host);
&ctx->responses[HTTP_RESPONSE_KEYS],
grpc_core::CreateHttpRequestSSLCredentials());
ctx->http_request->Start();
gpr_free(host);
return;
error:
@ -759,10 +771,12 @@ static void retrieve_key_and_verify(verifier_cb_ctx* ctx) {
grpc_closure* http_cb;
char* path_prefix = nullptr;
const char* iss;
grpc_httpcli_request req;
memset(&req, 0, sizeof(grpc_httpcli_request));
req.handshaker = &grpc_httpcli_ssl;
grpc_http_request req;
memset(&req, 0, sizeof(grpc_http_request));
http_response_index rsp_idx;
char* host;
char* path;
absl::StatusOr<grpc_core::URI> uri;
GPR_ASSERT(ctx != nullptr && ctx->header != nullptr &&
ctx->claims != nullptr);
@ -790,26 +804,25 @@ static void retrieve_key_and_verify(verifier_cb_ctx* ctx) {
gpr_log(GPR_ERROR, "Missing mapping for issuer email.");
goto error;
}
req.host = gpr_strdup(mapping->key_url_prefix);
path_prefix = strchr(req.host, '/');
host = gpr_strdup(mapping->key_url_prefix);
path_prefix = strchr(host, '/');
if (path_prefix == nullptr) {
gpr_asprintf(&req.http.path, "/%s", iss);
gpr_asprintf(&path, "/%s", iss);
} else {
*(path_prefix++) = '\0';
gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss);
gpr_asprintf(&path, "/%s/%s", path_prefix, iss);
}
http_cb =
GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx);
rsp_idx = HTTP_RESPONSE_KEYS;
} else {
req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
path_prefix = strchr(req.host, '/');
host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
path_prefix = strchr(host, '/');
if (path_prefix == nullptr) {
req.http.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX);
path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX);
} else {
*(path_prefix++) = 0;
gpr_asprintf(&req.http.path, "/%s%s", path_prefix,
GRPC_OPENID_CONFIG_URL_SUFFIX);
gpr_asprintf(&path, "/%s%s", path_prefix, GRPC_OPENID_CONFIG_URL_SUFFIX);
}
http_cb = GRPC_CLOSURE_CREATE(on_openid_config_retrieved, ctx,
grpc_schedule_on_exec_ctx);
@ -819,12 +832,18 @@ static void retrieve_key_and_verify(verifier_cb_ctx* ctx) {
/* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
grpc_httpcli_get(
&ctx->pollent, grpc_core::ResourceQuota::Default(), &req,
uri = grpc_core::URI::Create("https", host, path, {} /* query params */,
"" /* fragment */);
if (!uri.ok()) {
goto error;
}
ctx->http_request = grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, &ctx->pollent, &req,
grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay, http_cb,
&ctx->responses[rsp_idx]);
gpr_free(req.host);
gpr_free(req.http.path);
&ctx->responses[rsp_idx], grpc_core::CreateHttpRequestSSLCredentials());
ctx->http_request->Start();
gpr_free(host);
gpr_free(path);
return;
error:

@ -36,6 +36,7 @@
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/http/httpcli_ssl_credentials.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/json/json.h"
@ -269,7 +270,7 @@ void grpc_oauth2_token_fetcher_credentials::on_http_response(
gpr_free(prev);
}
Unref();
grpc_credentials_metadata_request_destroy(r);
delete r;
}
bool grpc_oauth2_token_fetcher_credentials::get_request_metadata(
@ -315,8 +316,8 @@ bool grpc_oauth2_token_fetcher_credentials::get_request_metadata(
gpr_mu_unlock(&mu_);
if (start_fetch) {
Ref().release();
fetch_oauth2(grpc_credentials_metadata_request_create(this->Ref()),
&pollent_, on_oauth2_token_fetcher_http_response,
fetch_oauth2(new grpc_credentials_metadata_request(this->Ref()), &pollent_,
on_oauth2_token_fetcher_http_response,
grpc_core::ExecCtx::Get()->Now() + refresh_threshold);
}
return false;
@ -380,21 +381,26 @@ class grpc_compute_engine_token_fetcher_credentials
grpc_millis deadline) override {
grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
const_cast<char*>("Google")};
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(GRPC_COMPUTE_ENGINE_METADATA_HOST);
request.http.path =
const_cast<char*>(GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH);
request.http.hdr_count = 1;
request.http.hdrs = &header;
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
request.hdr_count = 1;
request.hdrs = &header;
/* TODO(ctiller): Carry the memory quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
grpc_httpcli_get(pollent, grpc_core::ResourceQuota::Default(), &request,
deadline,
GRPC_CLOSURE_INIT(&http_get_cb_closure_, response_cb,
metadata_req, grpc_schedule_on_exec_ctx),
&metadata_req->response);
auto uri = grpc_core::URI::Create("http", GRPC_COMPUTE_ENGINE_METADATA_HOST,
GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH,
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok()); // params are hardcoded
http_request_ = grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, pollent, &request,
deadline,
GRPC_CLOSURE_INIT(&http_get_cb_closure_, response_cb, metadata_req,
grpc_schedule_on_exec_ctx),
&metadata_req->response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
http_request_->Start();
}
std::string debug_string() override {
@ -405,6 +411,7 @@ class grpc_compute_engine_token_fetcher_credentials
private:
grpc_closure http_get_cb_closure_;
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request_;
};
} // namespace
@ -435,24 +442,28 @@ void grpc_google_refresh_token_credentials::fetch_oauth2(
grpc_http_header header = {
const_cast<char*>("Content-Type"),
const_cast<char*>("application/x-www-form-urlencoded")};
grpc_httpcli_request request;
grpc_http_request request;
std::string body = absl::StrFormat(
GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING, refresh_token_.client_id,
refresh_token_.client_secret, refresh_token_.refresh_token);
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(GRPC_GOOGLE_OAUTH2_SERVICE_HOST);
request.http.path = const_cast<char*>(GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH);
request.http.hdr_count = 1;
request.http.hdrs = &header;
request.handshaker = &grpc_httpcli_ssl;
memset(&request, 0, sizeof(grpc_http_request));
request.hdr_count = 1;
request.hdrs = &header;
request.body = const_cast<char*>(body.c_str());
request.body_length = body.size();
/* TODO(ctiller): Carry the memory quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
grpc_httpcli_post(pollent, grpc_core::ResourceQuota::Default(), &request,
body.c_str(), body.size(), deadline,
GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb,
metadata_req, grpc_schedule_on_exec_ctx),
&metadata_req->response);
auto uri = grpc_core::URI::Create("https", GRPC_GOOGLE_OAUTH2_SERVICE_HOST,
GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH,
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok()); // params are hardcoded
http_request_ = grpc_core::HttpRequest::Post(
std::move(*uri), nullptr /* channel args */, pollent, &request, deadline,
GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req,
grpc_schedule_on_exec_ctx),
&metadata_req->response, grpc_core::CreateHttpRequestSSLCredentials());
http_request_->Start();
}
grpc_google_refresh_token_credentials::grpc_google_refresh_token_credentials(
@ -553,9 +564,9 @@ class StsTokenFetcherCredentials
grpc_polling_entity* pollent,
grpc_iomgr_cb_func response_cb,
grpc_millis deadline) override {
char* body = nullptr;
size_t body_length = 0;
grpc_error_handle err = FillBody(&body, &body_length);
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
grpc_error_handle err = FillBody(&request.body, &request.body_length);
if (err != GRPC_ERROR_NONE) {
response_cb(metadata_req, err);
GRPC_ERROR_UNREF(err);
@ -564,25 +575,25 @@ class StsTokenFetcherCredentials
grpc_http_header header = {
const_cast<char*>("Content-Type"),
const_cast<char*>("application/x-www-form-urlencoded")};
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = const_cast<char*>(sts_url_.authority().c_str());
request.http.path = const_cast<char*>(sts_url_.path().c_str());
request.http.hdr_count = 1;
request.http.hdrs = &header;
request.handshaker = (sts_url_.scheme() == "https")
? &grpc_httpcli_ssl
: &grpc_httpcli_plaintext;
request.hdr_count = 1;
request.hdrs = &header;
/* TODO(ctiller): Carry the memory quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
grpc_httpcli_post(
pollent, ResourceQuota::Default(), &request, body, body_length,
deadline,
RefCountedPtr<grpc_channel_credentials> http_request_creds;
if (sts_url_.scheme() == "http") {
http_request_creds = RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create());
} else {
http_request_creds = CreateHttpRequestSSLCredentials();
}
http_request_ = HttpRequest::Post(
sts_url_, nullptr /* channel args */, pollent, &request, deadline,
GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req,
grpc_schedule_on_exec_ctx),
&metadata_req->response);
gpr_free(body);
&metadata_req->response, std::move(http_request_creds));
http_request_->Start();
gpr_free(request.body);
}
grpc_error_handle FillBody(char** body, size_t* body_length) {
@ -637,6 +648,7 @@ class StsTokenFetcherCredentials
UniquePtr<char> subject_token_type_;
UniquePtr<char> actor_token_path_;
UniquePtr<char> actor_token_type_;
OrphanablePtr<HttpRequest> http_request_;
};
} // namespace

@ -25,6 +25,7 @@
#include <grpc/grpc_security.h>
#include "src/core/lib/http/httpcli.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/uri/uri_parser.h"
@ -58,10 +59,19 @@ grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
/// Destructs the object.
void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token* refresh_token);
// -- Oauth2 Token Fetcher credentials --
//
// This object is a base for credentials that need to acquire an oauth2 token
// from an http service.
// -- Credentials Metadata Request. --
struct grpc_credentials_metadata_request {
explicit grpc_credentials_metadata_request(
grpc_core::RefCountedPtr<grpc_call_credentials> creds)
: creds(std::move(creds)) {}
~grpc_credentials_metadata_request() {
grpc_http_response_destroy(&response);
}
grpc_core::RefCountedPtr<grpc_call_credentials> creds;
grpc_http_response response;
};
struct grpc_oauth2_pending_get_request_metadata {
grpc_core::CredentialsMetadataArray* md_array;
@ -70,6 +80,11 @@ struct grpc_oauth2_pending_get_request_metadata {
struct grpc_oauth2_pending_get_request_metadata* next;
};
// -- Oauth2 Token Fetcher credentials --
//
// This object is a base for credentials that need to acquire an oauth2 token
// from an http service.
class grpc_oauth2_token_fetcher_credentials : public grpc_call_credentials {
public:
grpc_oauth2_token_fetcher_credentials();
@ -125,6 +140,7 @@ class grpc_google_refresh_token_credentials final
private:
grpc_auth_refresh_token refresh_token_;
grpc_closure http_post_cb_closure_;
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request_;
};
// Access token credentials.

@ -36,7 +36,6 @@
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/fork.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/http/parser.h"
#include "src/core/lib/iomgr/call_combiner.h"
#include "src/core/lib/iomgr/combiner.h"
#include "src/core/lib/iomgr/exec_ctx.h"

@ -37,6 +37,7 @@ class SecureChannelCredentials final : public ChannelCredentials {
public:
explicit SecureChannelCredentials(grpc_channel_credentials* c_creds);
~SecureChannelCredentials() override {
grpc_core::ExecCtx exec_ctx;
if (c_creds_ != nullptr) c_creds_->Unref();
}
grpc_channel_credentials* GetRawCreds() { return c_creds_; }
@ -59,6 +60,7 @@ class SecureCallCredentials final : public CallCredentials {
public:
explicit SecureCallCredentials(grpc_call_credentials* c_creds);
~SecureCallCredentials() override {
grpc_core::ExecCtx exec_ctx;
if (c_creds_ != nullptr) c_creds_->Unref();
}
grpc_call_credentials* GetRawCreds() { return c_creds_; }

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package")
load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package")
load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer")
grpc_package(name = "test/core/http")
@ -61,6 +61,18 @@ grpc_fuzzer(
licenses(["notice"])
grpc_cc_library(
name = "httpcli_test_util",
testonly = True,
srcs = ["httpcli_test_util.cc"],
hdrs = ["httpcli_test_util.h"],
deps = [
"//:gpr",
"//test/core/end2end:ssl_test_data",
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "httpcli_test",
srcs = ["httpcli_test.cc"],
@ -70,13 +82,17 @@ grpc_cc_test(
"//src/core/tsi/test_creds:server1.key",
"//src/core/tsi/test_creds:server1.pem",
],
external_deps = ["gtest"],
language = "C++",
tags = ["no_windows"],
deps = [
":httpcli_test_util",
"//:gpr",
"//:grpc",
"//test/core/end2end:ssl_test_data",
"//test/core/util:fake_udp_and_tcp_server",
"//test/core/util:grpc_test_util",
"//test/cpp/util:test_util",
],
)
@ -90,13 +106,17 @@ grpc_cc_test(
"//src/core/tsi/test_creds:server1.key",
"//src/core/tsi/test_creds:server1.pem",
],
external_deps = ["gtest"],
language = "C++",
tags = ["no_windows"],
deps = [
":httpcli_test_util",
"//:gpr",
"//:grpc",
"//test/core/end2end:ssl_test_data",
"//test/core/util:fake_udp_and_tcp_server",
"//test/core/util:grpc_test_util",
"//test/cpp/util:test_util",
],
)

@ -27,16 +27,15 @@
static void test_format_get_request(void) {
grpc_http_header hdr = {const_cast<char*>("x-yz"), const_cast<char*>("abc")};
grpc_httpcli_request req;
grpc_http_request req;
grpc_slice slice;
const char* host = "example.com";
memset(&req, 0, sizeof(req));
req.host = const_cast<char*>("example.com");
req.http.path = const_cast<char*>("/index.html");
req.http.hdr_count = 1;
req.http.hdrs = &hdr;
req.hdr_count = 1;
req.hdrs = &hdr;
slice = grpc_httpcli_format_get_request(&req);
slice = grpc_httpcli_format_get_request(&req, host, "/index.html");
GPR_ASSERT(0 == grpc_slice_str_cmp(slice,
"GET /index.html HTTP/1.0\r\n"
@ -52,18 +51,17 @@ static void test_format_get_request(void) {
static void test_format_post_request(void) {
grpc_http_header hdr = {const_cast<char*>("x-yz"), const_cast<char*>("abc")};
grpc_httpcli_request req;
grpc_http_request req;
grpc_slice slice;
char body_bytes[] = "fake body";
size_t body_len = 9;
const char* host = "example.com";
memset(&req, 0, sizeof(req));
req.host = const_cast<char*>("example.com");
req.http.path = const_cast<char*>("/index.html");
req.http.hdr_count = 1;
req.http.hdrs = &hdr;
req.hdr_count = 1;
req.hdrs = &hdr;
req.body = const_cast<char*>("fake body");
req.body_length = 9;
slice = grpc_httpcli_format_post_request(&req, body_bytes, body_len);
slice = grpc_httpcli_format_post_request(&req, host, "/index.html");
GPR_ASSERT(0 == grpc_slice_str_cmp(slice,
"POST /index.html HTTP/1.0\r\n"
@ -82,16 +80,15 @@ static void test_format_post_request(void) {
static void test_format_post_request_no_body(void) {
grpc_http_header hdr = {const_cast<char*>("x-yz"), const_cast<char*>("abc")};
grpc_httpcli_request req;
grpc_http_request req;
grpc_slice slice;
const char* host = "example.com";
memset(&req, 0, sizeof(req));
req.host = const_cast<char*>("example.com");
req.http.path = const_cast<char*>("/index.html");
req.http.hdr_count = 1;
req.http.hdrs = &hdr;
req.hdr_count = 1;
req.hdrs = &hdr;
slice = grpc_httpcli_format_post_request(&req, nullptr, 0);
slice = grpc_httpcli_format_post_request(&req, host, "/index.html");
GPR_ASSERT(0 == grpc_slice_str_cmp(slice,
"POST /index.html HTTP/1.0\r\n"
@ -107,22 +104,21 @@ static void test_format_post_request_no_body(void) {
static void test_format_post_request_content_type_override(void) {
grpc_http_header hdrs[2];
grpc_httpcli_request req;
grpc_http_request req;
grpc_slice slice;
char body_bytes[] = "fake%20body";
size_t body_len = 11;
const char* host = "example.com";
hdrs[0].key = const_cast<char*>("x-yz");
hdrs[0].value = const_cast<char*>("abc");
hdrs[1].key = const_cast<char*>("Content-Type");
hdrs[1].value = const_cast<char*>("application/x-www-form-urlencoded");
memset(&req, 0, sizeof(req));
req.host = const_cast<char*>("example.com");
req.http.path = const_cast<char*>("/index.html");
req.http.hdr_count = 2;
req.http.hdrs = hdrs;
req.hdr_count = 2;
req.hdrs = hdrs;
req.body = const_cast<char*>("fake%20body");
req.body_length = 11;
slice = grpc_httpcli_format_post_request(&req, body_bytes, body_len);
slice = grpc_httpcli_format_post_request(&req, host, "/index.html");
GPR_ASSERT(0 == grpc_slice_str_cmp(
slice,

@ -20,194 +20,458 @@
#include <string.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
#include "src/core/lib/iomgr/iomgr.h"
#include "test/core/http/httpcli_test_util.h"
#include "test/core/util/fake_udp_and_tcp_server.h"
#include "test/core/util/port.h"
#include "test/core/util/subprocess.h"
#include "test/core/util/test_config.h"
static int g_done = 0;
static gpr_mu* g_mu;
static grpc_polling_entity g_pops;
namespace {
static grpc_millis n_seconds_time(int seconds) {
grpc_millis NSecondsTime(int seconds) {
return grpc_timespec_to_millis_round_up(
grpc_timeout_seconds_to_deadline(seconds));
}
static void on_finish(void* arg, grpc_error_handle error) {
const char* expect =
"<html><head><title>Hello world!</title></head>"
"<body><p>This is a test</p></body></html>";
grpc_http_response* response = static_cast<grpc_http_response*>(arg);
GPR_ASSERT(response);
gpr_log(GPR_INFO, "response status=%d error=%s", response->status,
grpc_error_std_string(error).c_str());
GPR_ASSERT(response->status == 200);
GPR_ASSERT(response->body_length == strlen(expect));
GPR_ASSERT(0 == memcmp(expect, response->body, response->body_length));
gpr_mu_lock(g_mu);
g_done = 1;
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"pollset_kick",
grpc_pollset_kick(grpc_polling_entity_pollset(&g_pops), nullptr)));
gpr_mu_unlock(g_mu);
absl::Time AbslDeadlineSeconds(int s) {
return grpc_core::ToAbslTime(grpc_timeout_seconds_to_deadline(s));
}
static void test_get(int port) {
grpc_httpcli_request req;
char* host;
grpc_core::ExecCtx exec_ctx;
g_done = 0;
gpr_log(GPR_INFO, "test_get");
int g_argc;
char** g_argv;
int g_server_port;
gpr_subprocess* g_server;
gpr_asprintf(&host, "localhost:%d", port);
gpr_log(GPR_INFO, "requesting from %s", host);
class HttpRequestTest : public ::testing::Test {
public:
HttpRequestTest() {
grpc_init();
grpc_core::ExecCtx exec_ctx;
grpc_pollset* pollset =
static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
grpc_pollset_init(pollset, &mu_);
pops_ = grpc_polling_entity_create_from_pollset(pollset);
}
~HttpRequestTest() override {
{
grpc_core::ExecCtx exec_ctx;
grpc_pollset_shutdown(
grpc_polling_entity_pollset(&pops_),
GRPC_CLOSURE_CREATE(DestroyPops, &pops_, grpc_schedule_on_exec_ctx));
}
grpc_shutdown();
}
memset(&req, 0, sizeof(req));
req.host = host;
req.http.path = const_cast<char*>("/get");
req.handshaker = &grpc_httpcli_plaintext;
grpc_http_response response;
response = {};
grpc_httpcli_get(
&g_pops, grpc_core::ResourceQuota::Default(), &req, n_seconds_time(15),
GRPC_CLOSURE_CREATE(on_finish, &response, grpc_schedule_on_exec_ctx),
&response);
gpr_mu_lock(g_mu);
while (!g_done) {
grpc_pollset_worker* worker = nullptr;
void RunAndKick(const std::function<void()>& f) {
grpc_core::MutexLockForGprMu lock(mu_);
f();
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"pollset_work", grpc_pollset_work(grpc_polling_entity_pollset(&g_pops),
&worker, n_seconds_time(1))));
gpr_mu_unlock(g_mu);
"pollset_kick",
grpc_pollset_kick(grpc_polling_entity_pollset(&pops_), nullptr)));
}
gpr_mu_lock(g_mu);
void PollUntil(const std::function<bool()>& predicate, absl::Time deadline) {
gpr_mu_lock(mu_);
while (!predicate()) {
GPR_ASSERT(absl::Now() < deadline);
grpc_pollset_worker* worker = nullptr;
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"pollset_work", grpc_pollset_work(grpc_polling_entity_pollset(&pops_),
&worker, NSecondsTime(1))));
gpr_mu_unlock(mu_);
gpr_mu_lock(mu_);
}
gpr_mu_unlock(mu_);
}
gpr_mu_unlock(g_mu);
gpr_free(host);
grpc_http_response_destroy(&response);
}
static void test_post(int port) {
grpc_httpcli_request req;
char* host;
grpc_core::ExecCtx exec_ctx;
grpc_polling_entity* pops() { return &pops_; }
g_done = 0;
gpr_log(GPR_INFO, "test_post");
protected:
static void SetUpTestSuite() {
auto test_server = grpc_core::testing::StartHttpRequestTestServer(
g_argc, g_argv, false /* use_ssl */);
g_server = test_server.server;
g_server_port = test_server.port;
}
gpr_asprintf(&host, "localhost:%d", port);
gpr_log(GPR_INFO, "posting to %s", host);
static void TearDownTestSuite() { gpr_subprocess_destroy(g_server); }
memset(&req, 0, sizeof(req));
req.host = host;
req.http.path = const_cast<char*>("/post");
req.handshaker = &grpc_httpcli_plaintext;
grpc_http_response response;
response = {};
grpc_httpcli_post(
&g_pops, grpc_core::ResourceQuota::Default(), &req, "hello", 5,
n_seconds_time(15),
GRPC_CLOSURE_CREATE(on_finish, &response, grpc_schedule_on_exec_ctx),
&response);
gpr_mu_lock(g_mu);
while (!g_done) {
grpc_pollset_worker* worker = nullptr;
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"pollset_work", grpc_pollset_work(grpc_polling_entity_pollset(&g_pops),
&worker, n_seconds_time(1))));
gpr_mu_unlock(g_mu);
private:
static void DestroyPops(void* p, grpc_error_handle /*error*/) {
grpc_polling_entity* pops = static_cast<grpc_polling_entity*>(p);
grpc_pollset_destroy(grpc_polling_entity_pollset(pops));
gpr_free(grpc_polling_entity_pollset(pops));
}
gpr_mu* mu_;
grpc_polling_entity pops_;
};
struct RequestState {
explicit RequestState(HttpRequestTest* test) : test(test) {}
gpr_mu_lock(g_mu);
~RequestState() {
grpc_core::ExecCtx exec_ctx;
grpc_http_response_destroy(&response);
}
HttpRequestTest* test;
bool done = false;
grpc_http_response response = {};
grpc_pollset_set* pollset_set_to_destroy_eagerly = nullptr;
};
void OnFinish(void* arg, grpc_error_handle error) {
RequestState* request_state = static_cast<RequestState*>(arg);
if (request_state->pollset_set_to_destroy_eagerly != nullptr) {
// Destroy the request's polling entity param. The goal is to try to catch a
// bug where we might still be referencing the polling entity by
// a pending TCP connect.
grpc_pollset_set_destroy(request_state->pollset_set_to_destroy_eagerly);
}
gpr_mu_unlock(g_mu);
gpr_free(host);
grpc_http_response_destroy(&response);
const char* expect =
"<html><head><title>Hello world!</title></head>"
"<body><p>This is a test</p></body></html>";
GPR_ASSERT(error == GRPC_ERROR_NONE);
grpc_http_response response = request_state->response;
gpr_log(GPR_INFO, "response status=%d error=%s", response.status,
grpc_error_std_string(error).c_str());
GPR_ASSERT(response.status == 200);
GPR_ASSERT(response.body_length == strlen(expect));
GPR_ASSERT(0 == memcmp(expect, response.body, response.body_length));
request_state->test->RunAndKick(
[request_state]() { request_state->done = true; });
}
static void destroy_pops(void* p, grpc_error_handle /*error*/) {
grpc_pollset_destroy(
grpc_polling_entity_pollset(static_cast<grpc_polling_entity*>(p)));
void OnFinishExpectFailure(void* arg, grpc_error_handle error) {
RequestState* request_state = static_cast<RequestState*>(arg);
if (request_state->pollset_set_to_destroy_eagerly != nullptr) {
// Destroy the request's polling entity param. The goal is to try to catch a
// bug where we might still be referencing the polling entity by
// a pending TCP connect.
grpc_pollset_set_destroy(request_state->pollset_set_to_destroy_eagerly);
}
grpc_http_response response = request_state->response;
gpr_log(GPR_INFO, "response status=%d error=%s", response.status,
grpc_error_std_string(error).c_str());
GPR_ASSERT(error != GRPC_ERROR_NONE);
request_state->test->RunAndKick(
[request_state]() { request_state->done = true; });
}
int main(int argc, char** argv) {
gpr_subprocess* server;
grpc::testing::TestEnvironment env(argc, argv);
grpc_init();
{
grpc_closure destroyed;
grpc_core::ExecCtx exec_ctx;
char* me = argv[0];
char* lslash = strrchr(me, '/');
char* args[4];
int port = grpc_pick_unused_port_or_die();
int arg_shift = 0;
/* figure out where we are */
char* root;
if (lslash != nullptr) {
/* Hack for bazel target */
if (static_cast<unsigned>(lslash - me) >= (sizeof("http") - 1) &&
strncmp(me + (lslash - me) - sizeof("http") + 1, "http",
sizeof("http") - 1) == 0) {
lslash = me + (lslash - me) - sizeof("http");
}
root = static_cast<char*>(
gpr_malloc(static_cast<size_t>(lslash - me + sizeof("/../.."))));
memcpy(root, me, static_cast<size_t>(lslash - me));
memcpy(root + (lslash - me), "/../..", sizeof("/../.."));
} else {
root = gpr_strdup(".");
}
TEST_F(HttpRequestTest, Get) {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
std::string host = absl::StrFormat("localhost:%d", g_server_port);
gpr_log(GPR_INFO, "requesting from %s", host.c_str());
memset(&req, 0, sizeof(req));
auto uri = grpc_core::URI::Create("http", host, "/get", {} /* query params */,
"" /* fragment */);
GPR_ASSERT(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, pops(), &req,
NSecondsTime(15),
GRPC_CLOSURE_CREATE(OnFinish, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
http_request->Start();
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
}
GPR_ASSERT(argc <= 2);
if (argc == 2) {
args[0] = gpr_strdup(argv[1]);
} else {
arg_shift = 1;
gpr_asprintf(&args[0], "%s/test/core/http/python_wrapper.sh", root);
gpr_asprintf(&args[1], "%s/test/core/http/test_server.py", root);
}
TEST_F(HttpRequestTest, Post) {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
std::string host = absl::StrFormat("localhost:%d", g_server_port);
gpr_log(GPR_INFO, "posting to %s", host.c_str());
memset(&req, 0, sizeof(req));
req.body = const_cast<char*>("hello");
req.body_length = 5;
auto uri = grpc_core::URI::Create("http", host, "/post",
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Post(
std::move(*uri), nullptr /* channel args */, pops(), &req,
NSecondsTime(15),
GRPC_CLOSURE_CREATE(OnFinish, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
http_request->Start();
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
}
/* start the server */
args[1 + arg_shift] = const_cast<char*>("--port");
gpr_asprintf(&args[2 + arg_shift], "%d", port);
server =
gpr_subprocess_create(3 + arg_shift, const_cast<const char**>(args));
GPR_ASSERT(server);
gpr_free(args[0]);
if (arg_shift) gpr_free(args[1]);
gpr_free(args[2 + arg_shift]);
gpr_free(root);
int g_fake_non_responsive_dns_server_port;
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_seconds(5, GPR_TIMESPAN)));
void InjectNonResponsiveDNSServer(ares_channel channel) {
gpr_log(GPR_DEBUG,
"Injecting broken nameserver list. Bad server address:|[::1]:%d|.",
g_fake_non_responsive_dns_server_port);
// Configure a non-responsive DNS server at the front of c-ares's nameserver
// list.
struct ares_addr_port_node dns_server_addrs[1];
dns_server_addrs[0].family = AF_INET6;
(reinterpret_cast<char*>(&dns_server_addrs[0].addr.addr6))[15] = 0x1;
dns_server_addrs[0].tcp_port = g_fake_non_responsive_dns_server_port;
dns_server_addrs[0].udp_port = g_fake_non_responsive_dns_server_port;
dns_server_addrs[0].next = nullptr;
GPR_ASSERT(ares_set_servers_ports(channel, dns_server_addrs) == ARES_SUCCESS);
}
grpc_pollset* pollset =
static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
grpc_pollset_init(pollset, &g_mu);
g_pops = grpc_polling_entity_create_from_pollset(pollset);
TEST_F(HttpRequestTest, CancelGetDuringDNSResolution) {
// Inject an unresponsive DNS server into the resolver's DNS server config
grpc_core::testing::FakeUdpAndTcpServer fake_dns_server(
grpc_core::testing::FakeUdpAndTcpServer::AcceptMode::
kWaitForClientToSendFirstBytes,
grpc_core::testing::FakeUdpAndTcpServer::CloseSocketUponCloseFromPeer);
g_fake_non_responsive_dns_server_port = fake_dns_server.port();
void (*prev_test_only_inject_config)(ares_channel channel) =
grpc_ares_test_only_inject_config;
grpc_ares_test_only_inject_config = InjectNonResponsiveDNSServer;
// Run the same test on several threads in parallel to try to trigger races
// etc.
int kNumThreads = 100;
std::vector<std::thread> threads;
threads.reserve(kNumThreads);
for (int i = 0; i < kNumThreads; i++) {
threads.push_back(std::thread([this]() {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
memset(&req, 0, sizeof(grpc_http_request));
auto uri = grpc_core::URI::Create(
"http", "dont-care-since-wont-be-resolved.test.com:443", "/get",
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, pops(), &req,
NSecondsTime(120),
GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
http_request->Start();
std::thread cancel_thread([&http_request]() {
gpr_sleep_until(grpc_timeout_seconds_to_deadline(1));
grpc_core::ExecCtx exec_ctx;
http_request.reset();
});
// Poll with a deadline explicitly lower than the request timeout, so
// that we know that the request timeout isn't just kicking in.
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
cancel_thread.join();
}));
}
for (auto& t : threads) {
t.join();
}
grpc_ares_test_only_inject_config = prev_test_only_inject_config;
}
test_get(port);
test_post(port);
TEST_F(HttpRequestTest, CancelGetWhileReadingResponse) {
// Start up a fake HTTP server which just accepts connections
// and then hangs, i.e. does not send back any bytes to the client.
// The goal here is to get the client to connect to this fake server
// and send a request, and then sit waiting for a response. Then, a
// separate thread will cancel the HTTP request, and that should let it
// complete.
grpc_core::testing::FakeUdpAndTcpServer fake_http_server(
grpc_core::testing::FakeUdpAndTcpServer::AcceptMode::
kWaitForClientToSendFirstBytes,
grpc_core::testing::FakeUdpAndTcpServer::CloseSocketUponCloseFromPeer);
// Run the same test on several threads in parallel to try to trigger races
// etc.
int kNumThreads = 100;
std::vector<std::thread> threads;
threads.reserve(kNumThreads);
for (int i = 0; i < kNumThreads; i++) {
grpc_core::testing::FakeUdpAndTcpServer* fake_http_server_ptr =
&fake_http_server;
threads.push_back(std::thread([this, fake_http_server_ptr]() {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
memset(&req, 0, sizeof(req));
auto uri = grpc_core::URI::Create("http", fake_http_server_ptr->address(),
"/get", {} /* query params */,
"" /* fragment */);
GPR_ASSERT(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, pops(), &req,
NSecondsTime(120),
GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
http_request->Start();
exec_ctx.Flush();
std::thread cancel_thread([&http_request]() {
gpr_sleep_until(grpc_timeout_seconds_to_deadline(1));
grpc_core::ExecCtx exec_ctx;
http_request.reset();
});
// Poll with a deadline explicitly lower than the request timeout, so
// that we know that the request timeout isn't just kicking in.
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
cancel_thread.join();
}));
}
for (auto& t : threads) {
t.join();
}
}
GRPC_CLOSURE_INIT(&destroyed, destroy_pops, &g_pops,
grpc_schedule_on_exec_ctx);
grpc_pollset_shutdown(grpc_polling_entity_pollset(&g_pops), &destroyed);
// The main point of this test is just to exercise the machinery around
// cancellation during TCP connection establishment, to make sure there are no
// crashes/races etc. This test doesn't actually verify that cancellation during
// TCP setup is happening, though. For that, we would need to induce packet loss
// in the test.
TEST_F(HttpRequestTest, CancelGetRacesWithConnectionFailure) {
// Grab an unoccupied port but don't listen on it. The goal
// here is just to have a server address that will reject
// TCP connection setups.
// Note that because the server is rejecting TCP connections, we
// don't really need to cancel the HTTP requests in this test case
// in order for them proceeed i.e. in order for them to pass. The test
// is still beneficial though because it can exercise the same code paths
// that would get taken if the HTTP request was cancelled while the TCP
// connect attempt was actually hanging.
int fake_server_port = grpc_pick_unused_port_or_die();
std::string fake_server_address =
absl::StrCat("[::1]:", std::to_string(fake_server_port));
// Run the same test on several threads in parallel to try to trigger races
// etc.
int kNumThreads = 100;
std::vector<std::thread> threads;
threads.reserve(kNumThreads);
for (int i = 0; i < kNumThreads; i++) {
threads.push_back(std::thread([this, fake_server_address]() {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
memset(&req, 0, sizeof(req));
auto uri =
grpc_core::URI::Create("http", fake_server_address, "/get",
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, pops(), &req,
NSecondsTime(120),
GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
// Start the HTTP request. We will ~immediately begin a TCP connect
// attempt because there's no name to resolve.
http_request->Start();
exec_ctx.Flush();
// Spawn a separate thread which ~immediately cancels the HTTP request.
// Note that even though the server is rejecting TCP connections, it can
// still take some time for the client to receive that rejection. So
// cancelling the request now can trigger the code paths that would get
// taken if the TCP connection was truly hanging e.g. from packet loss.
// The goal is just to make sure there are no crashes, races, etc.
std::thread cancel_thread([&http_request]() {
grpc_core::ExecCtx exec_ctx;
http_request.reset();
});
// Poll with a deadline explicitly lower than the request timeout, so
// that we know that the request timeout isn't just kicking in.
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
cancel_thread.join();
}));
}
grpc_shutdown();
for (auto& t : threads) {
t.join();
}
}
gpr_free(grpc_polling_entity_pollset(&g_pops));
// The pollent parameter passed to HttpRequest::Get or Post is owned by
// the caller and must not be referenced by the HttpRequest after the
// requests's on_done callback is invoked. This test verifies that this
// isn't happening by destroying the request's pollset set within the
// on_done callback.
TEST_F(HttpRequestTest, CallerPollentsAreNotReferencedAfterCallbackIsRan) {
// Grab an unoccupied port but don't listen on it. The goal
// here is just to have a server address that will reject
// TCP connection setups.
// Note that we could have used a different server for this test case, e.g.
// one which accepts TCP connections. All we need here is something for the
// client to connect to, since it will be cancelled roughly during the
// connection attempt anyways.
int fake_server_port = grpc_pick_unused_port_or_die();
std::string fake_server_address =
absl::StrCat("[::1]:", std::to_string(fake_server_port));
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
memset(&req, 0, sizeof(req));
req.path = const_cast<char*>("/get");
request_state.pollset_set_to_destroy_eagerly = grpc_pollset_set_create();
grpc_polling_entity_add_to_pollset_set(
pops(), request_state.pollset_set_to_destroy_eagerly);
grpc_polling_entity wrapped_pollset_set_to_destroy_eagerly =
grpc_polling_entity_create_from_pollset_set(
request_state.pollset_set_to_destroy_eagerly);
auto uri = grpc_core::URI::Create("http", fake_server_address, "/get",
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */,
&wrapped_pollset_set_to_destroy_eagerly, &req, NSecondsTime(15),
GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
// Start the HTTP request. We'll start the TCP connect attempt right away.
http_request->Start();
exec_ctx.Flush();
http_request.reset(); // cancel the request
// Since the request was cancelled, the on_done callback should be flushed
// out on the ExecCtx flush below. When the on_done callback is ran, it will
// eagerly destroy 'request_state.pollset_set_to_destroy_eagerly'. Thus, we
// can't poll on that pollset here.
exec_ctx.Flush();
}
gpr_subprocess_destroy(server);
} // namespace
return 0;
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(argc, argv);
// launch the test server later, so that --gtest_list_tests works
g_argc = argc;
g_argv = argv;
// run tests
return RUN_ALL_TESTS();
}

@ -0,0 +1,105 @@
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <grpc/support/port_platform.h>
#include "test/core/http/httpcli_test_util.h"
#include <string.h>
#include <tuple>
#include <vector>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
#include "src/core/lib/security/security_connector/ssl_utils_config.h"
#include "test/core/util/port.h"
#include "test/core/util/subprocess.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
namespace testing {
HttpRequestTestServer StartHttpRequestTestServer(int argc, char** argv,
bool use_ssl) {
char* me = argv[0];
char* lslash = strrchr(me, '/');
std::vector<char*> args;
int server_port = grpc_pick_unused_port_or_die();
// figure out where we are
char* root;
if (lslash != nullptr) {
// Hack for bazel target
if (static_cast<unsigned>(lslash - me) >= (sizeof("http") - 1) &&
strncmp(me + (lslash - me) - sizeof("http") + 1, "http",
sizeof("http") - 1) == 0) {
lslash = me + (lslash - me) - sizeof("http");
}
root = static_cast<char*>(
gpr_malloc(static_cast<size_t>(lslash - me + sizeof("/../.."))));
memcpy(root, me, static_cast<size_t>(lslash - me));
memcpy(root + (lslash - me), "/../..", sizeof("/../.."));
} else {
root = gpr_strdup(".");
}
GPR_ASSERT(argc <= 2);
if (argc == 2) {
args.push_back(gpr_strdup(argv[1]));
} else {
char* python_wrapper_arg;
char* test_server_arg;
gpr_asprintf(&python_wrapper_arg, "%s/test/core/http/python_wrapper.sh",
root);
gpr_asprintf(&test_server_arg, "%s/test/core/http/test_server.py", root);
args.push_back(python_wrapper_arg);
args.push_back(test_server_arg);
}
// start the server
args.push_back(gpr_strdup("--port"));
char* server_port_str;
gpr_asprintf(&server_port_str, "%d", server_port);
args.push_back(server_port_str);
if (use_ssl) {
args.push_back(gpr_strdup("--ssl"));
// Set the environment variable for the SSL certificate file
char* pem_file;
gpr_asprintf(&pem_file, "%s/src/core/tsi/test_creds/ca.pem", root);
GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, pem_file);
gpr_free(pem_file);
}
gpr_log(GPR_INFO, "starting HttpRequest test server subprocess:");
for (size_t i = 0; i < args.size(); i++) {
gpr_log(GPR_INFO, " HttpRequest test server subprocess argv[%ld]: %s", i,
args[i]);
}
gpr_subprocess* server =
gpr_subprocess_create(args.size(), const_cast<const char**>(args.data()));
GPR_ASSERT(server);
for (size_t i = 0; i < args.size(); i++) {
gpr_free(args[i]);
}
gpr_free(root);
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_seconds(5, GPR_TIMESPAN)));
return {server, server_port};
}
} // namespace testing
} // namespace grpc_core

@ -0,0 +1,38 @@
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_TEST_CORE_HTTP_HTTPCLI_TEST_UTIL_H
#define GRPC_TEST_CORE_HTTP_HTTPCLI_TEST_UTIL_H
#include <grpc/support/port_platform.h>
#include "test/core/util/subprocess.h"
namespace grpc_core {
namespace testing {
struct HttpRequestTestServer {
gpr_subprocess* server;
int port;
};
HttpRequestTestServer StartHttpRequestTestServer(int argc, char** argv,
bool use_ssl);
} // namespace testing
} // namespace grpc_core
#endif // GRPC_TEST_CORE_HTTP_HTTPCLI_TEST_UTIL_H

@ -18,206 +18,271 @@
#include <string.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security_constants.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
#include "src/core/lib/gpr/env.h"
#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
#include "src/core/lib/http/httpcli.h"
#include "src/core/lib/http/httpcli_ssl_credentials.h"
#include "src/core/lib/iomgr/iomgr.h"
#include "src/core/lib/security/security_connector/ssl_utils_config.h"
#include "test/core/http/httpcli_test_util.h"
#include "test/core/util/fake_udp_and_tcp_server.h"
#include "test/core/util/port.h"
#include "test/core/util/subprocess.h"
#include "test/core/util/test_config.h"
static int g_done = 0;
static gpr_mu* g_mu;
static grpc_polling_entity g_pops;
namespace {
static grpc_millis n_seconds_time(int seconds) {
grpc_millis NSecondsTime(int seconds) {
return grpc_timespec_to_millis_round_up(
grpc_timeout_seconds_to_deadline(seconds));
}
static void on_finish(void* arg, grpc_error_handle error) {
const char* expect =
"<html><head><title>Hello world!</title></head>"
"<body><p>This is a test</p></body></html>";
grpc_http_response* response = static_cast<grpc_http_response*>(arg);
GPR_ASSERT(response);
gpr_log(GPR_INFO, "response status=%d error=%s", response->status,
grpc_error_std_string(error).c_str());
GPR_ASSERT(response->status == 200);
GPR_ASSERT(response->body_length == strlen(expect));
GPR_ASSERT(0 == memcmp(expect, response->body, response->body_length));
gpr_mu_lock(g_mu);
g_done = 1;
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"pollset_kick",
grpc_pollset_kick(grpc_polling_entity_pollset(&g_pops), nullptr)));
gpr_mu_unlock(g_mu);
absl::Time AbslDeadlineSeconds(int s) {
return grpc_core::ToAbslTime(grpc_timeout_seconds_to_deadline(s));
}
static void test_get(int port) {
grpc_httpcli_request req;
char* host;
grpc_core::ExecCtx exec_ctx;
g_done = 0;
gpr_log(GPR_INFO, "test_get");
int g_argc;
char** g_argv;
int g_server_port;
gpr_subprocess* g_server;
gpr_asprintf(&host, "localhost:%d", port);
gpr_log(GPR_INFO, "requesting from %s", host);
class HttpsCliTest : public ::testing::Test {
public:
HttpsCliTest() {
grpc_init();
grpc_core::ExecCtx exec_ctx;
grpc_pollset* pollset =
static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
grpc_pollset_init(pollset, &mu_);
pops_ = grpc_polling_entity_create_from_pollset(pollset);
}
~HttpsCliTest() override {
{
grpc_core::ExecCtx exec_ctx;
grpc_pollset_shutdown(
grpc_polling_entity_pollset(&pops_),
GRPC_CLOSURE_CREATE(DestroyPops, &pops_, grpc_schedule_on_exec_ctx));
}
grpc_shutdown();
}
memset(&req, 0, sizeof(req));
req.host = host;
req.ssl_host_override = const_cast<char*>("foo.test.google.fr");
req.http.path = const_cast<char*>("/get");
req.handshaker = &grpc_httpcli_ssl;
grpc_http_response response;
response = {};
grpc_httpcli_get(
&g_pops, grpc_core::ResourceQuota::Default(), &req, n_seconds_time(15),
GRPC_CLOSURE_CREATE(on_finish, &response, grpc_schedule_on_exec_ctx),
&response);
gpr_mu_lock(g_mu);
while (!g_done) {
grpc_pollset_worker* worker = nullptr;
void RunAndKick(const std::function<void()>& f) {
grpc_core::MutexLockForGprMu lock(mu_);
f();
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"pollset_work", grpc_pollset_work(grpc_polling_entity_pollset(&g_pops),
&worker, n_seconds_time(1))));
gpr_mu_unlock(g_mu);
grpc_core::ExecCtx::Get()->Flush();
gpr_mu_lock(g_mu);
"pollset_kick",
grpc_pollset_kick(grpc_polling_entity_pollset(&pops_), nullptr)));
}
gpr_mu_unlock(g_mu);
gpr_free(host);
grpc_http_response_destroy(&response);
}
static void test_post(int port) {
grpc_httpcli_request req;
char* host;
grpc_core::ExecCtx exec_ctx;
g_done = 0;
gpr_log(GPR_INFO, "test_post");
void PollUntil(const std::function<bool()>& predicate, absl::Time deadline) {
gpr_mu_lock(mu_);
while (!predicate()) {
GPR_ASSERT(absl::Now() < deadline);
grpc_pollset_worker* worker = nullptr;
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"pollset_work", grpc_pollset_work(grpc_polling_entity_pollset(&pops_),
&worker, NSecondsTime(1))));
gpr_mu_unlock(mu_);
gpr_mu_lock(mu_);
}
gpr_mu_unlock(mu_);
}
gpr_asprintf(&host, "localhost:%d", port);
gpr_log(GPR_INFO, "posting to %s", host);
grpc_polling_entity* pops() { return &pops_; }
memset(&req, 0, sizeof(req));
req.host = host;
req.ssl_host_override = const_cast<char*>("foo.test.google.fr");
req.http.path = const_cast<char*>("/post");
req.handshaker = &grpc_httpcli_ssl;
grpc_http_response response;
response = {};
grpc_httpcli_post(
&g_pops, grpc_core::ResourceQuota::Default(), &req, "hello", 5,
n_seconds_time(15),
GRPC_CLOSURE_CREATE(on_finish, &response, grpc_schedule_on_exec_ctx),
&response);
gpr_mu_lock(g_mu);
while (!g_done) {
grpc_pollset_worker* worker = nullptr;
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"pollset_work", grpc_pollset_work(grpc_polling_entity_pollset(&g_pops),
&worker, n_seconds_time(1))));
gpr_mu_unlock(g_mu);
grpc_core::ExecCtx::Get()->Flush();
gpr_mu_lock(g_mu);
protected:
static void SetUpTestSuite() {
auto test_server = grpc_core::testing::StartHttpRequestTestServer(
g_argc, g_argv, true /* use_ssl */);
g_server = test_server.server;
g_server_port = test_server.port;
}
gpr_mu_unlock(g_mu);
gpr_free(host);
grpc_http_response_destroy(&response);
}
static void destroy_pops(void* p, grpc_error_handle /*error*/) {
grpc_pollset_destroy(
grpc_polling_entity_pollset(static_cast<grpc_polling_entity*>(p)));
}
static void TearDownTestSuite() { gpr_subprocess_destroy(g_server); }
int main(int argc, char** argv) {
grpc_closure destroyed;
gpr_subprocess* server;
char* me = argv[0];
char* lslash = strrchr(me, '/');
char* args[5];
int port = grpc_pick_unused_port_or_die();
int arg_shift = 0;
/* figure out where we are */
char* root;
if (lslash != nullptr) {
/* Hack for bazel target */
if (static_cast<unsigned>(lslash - me) >= (sizeof("http") - 1) &&
strncmp(me + (lslash - me) - sizeof("http") + 1, "http",
sizeof("http") - 1) == 0) {
lslash = me + (lslash - me) - sizeof("http");
}
root = static_cast<char*>(
gpr_malloc(static_cast<size_t>(lslash - me + sizeof("/../.."))));
memcpy(root, me, static_cast<size_t>(lslash - me));
memcpy(root + (lslash - me), "/../..", sizeof("/../.."));
} else {
root = gpr_strdup(".");
private:
static void DestroyPops(void* p, grpc_error_handle /*error*/) {
grpc_polling_entity* pops = static_cast<grpc_polling_entity*>(p);
grpc_pollset_destroy(grpc_polling_entity_pollset(pops));
gpr_free(grpc_polling_entity_pollset(pops));
}
GPR_ASSERT(argc <= 2);
if (argc == 2) {
args[0] = gpr_strdup(argv[1]);
} else {
arg_shift = 1;
gpr_asprintf(&args[0], "%s/test/core/http/python_wrapper.sh", root);
gpr_asprintf(&args[1], "%s/test/core/http/test_server.py", root);
gpr_mu* mu_;
grpc_polling_entity pops_;
};
struct RequestState {
explicit RequestState(HttpsCliTest* test) : test(test) {}
~RequestState() {
grpc_core::ExecCtx exec_ctx;
grpc_http_response_destroy(&response);
}
/* Set the environment variable for the SSL certificate file */
char* pem_file;
gpr_asprintf(&pem_file, "%s/src/core/tsi/test_creds/ca.pem", root);
GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, pem_file);
gpr_free(pem_file);
/* start the server */
args[1 + arg_shift] = const_cast<char*>("--port");
gpr_asprintf(&args[2 + arg_shift], "%d", port);
args[3 + arg_shift] = const_cast<char*>("--ssl");
server = gpr_subprocess_create(4 + arg_shift, const_cast<const char**>(args));
GPR_ASSERT(server);
gpr_free(args[0]);
if (arg_shift) gpr_free(args[1]);
gpr_free(args[2 + arg_shift]);
gpr_free(root);
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_seconds(5, GPR_TIMESPAN)));
HttpsCliTest* test;
bool done = false;
grpc_http_response response = {};
};
grpc::testing::TestEnvironment env(argc, argv);
grpc_init();
grpc_pollset* pollset =
static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
grpc_pollset_init(pollset, &g_mu);
g_pops = grpc_polling_entity_create_from_pollset(pollset);
void OnFinish(void* arg, grpc_error_handle error) {
RequestState* request_state = static_cast<RequestState*>(arg);
const char* expect =
"<html><head><title>Hello world!</title></head>"
"<body><p>This is a test</p></body></html>";
GPR_ASSERT(error == GRPC_ERROR_NONE);
grpc_http_response response = request_state->response;
gpr_log(GPR_INFO, "response status=%d error=%s", response.status,
grpc_error_std_string(error).c_str());
GPR_ASSERT(response.status == 200);
GPR_ASSERT(response.body_length == strlen(expect));
GPR_ASSERT(0 == memcmp(expect, response.body, response.body_length));
request_state->test->RunAndKick(
[request_state]() { request_state->done = true; });
}
test_get(port);
test_post(port);
void OnFinishExpectFailure(void* arg, grpc_error_handle error) {
RequestState* request_state = static_cast<RequestState*>(arg);
grpc_http_response response = request_state->response;
gpr_log(GPR_INFO, "response status=%d error=%s", response.status,
grpc_error_std_string(error).c_str());
GPR_ASSERT(error != GRPC_ERROR_NONE);
request_state->test->RunAndKick(
[request_state]() { request_state->done = true; });
}
{
grpc_core::ExecCtx exec_ctx;
GRPC_CLOSURE_INIT(&destroyed, destroy_pops, &g_pops,
grpc_schedule_on_exec_ctx);
grpc_pollset_shutdown(grpc_polling_entity_pollset(&g_pops), &destroyed);
}
grpc_shutdown();
TEST_F(HttpsCliTest, Get) {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
std::string host = absl::StrFormat("localhost:%d", g_server_port);
gpr_log(GPR_INFO, "requesting from %s", host.c_str());
memset(&req, 0, sizeof(req));
grpc_arg ssl_override_arg = grpc_channel_arg_string_create(
const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
const_cast<char*>("foo.test.google.fr"));
grpc_channel_args args = {1, &ssl_override_arg};
auto uri = grpc_core::URI::Create("https", host, "/get",
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
std::move(*uri), &args, pops(), &req, NSecondsTime(15),
GRPC_CLOSURE_CREATE(OnFinish, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::CreateHttpRequestSSLCredentials());
http_request->Start();
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
}
TEST_F(HttpsCliTest, Post) {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
std::string host = absl::StrFormat("localhost:%d", g_server_port);
gpr_log(GPR_INFO, "posting to %s", host.c_str());
memset(&req, 0, sizeof(req));
req.body = const_cast<char*>("hello");
req.body_length = 5;
grpc_arg ssl_override_arg = grpc_channel_arg_string_create(
const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
const_cast<char*>("foo.test.google.fr"));
grpc_channel_args args = {1, &ssl_override_arg};
auto uri = grpc_core::URI::Create("https", host, "/post",
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Post(
std::move(*uri), &args /* channel args */, pops(), &req,
NSecondsTime(15),
GRPC_CLOSURE_CREATE(OnFinish, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::CreateHttpRequestSSLCredentials());
http_request->Start();
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
}
gpr_free(grpc_polling_entity_pollset(&g_pops));
// The goal of this test is to make sure that we can cancel HTTP requests
// while they're waiting for a response from the server to finish their
// SSL handshakes. Note that the main focus of this test is to just exercise
// the relevant code paths and make sure there aren't any crashes etc., rather
// than to make sure that cancellation happens in a timely manner.
TEST_F(HttpsCliTest, CancelGetDuringSSLHandshake) {
// Start up a fake TCP server which accepts connections and then hangs,
// i.e. it won't send any bytes back to the client.
grpc_core::testing::FakeUdpAndTcpServer fake_http_server(
grpc_core::testing::FakeUdpAndTcpServer::AcceptMode::
kWaitForClientToSendFirstBytes,
grpc_core::testing::FakeUdpAndTcpServer::CloseSocketUponCloseFromPeer);
// Use multiple threads to try to trigger races etc.
int kNumThreads = 100;
std::vector<std::thread> threads;
threads.reserve(kNumThreads);
for (int i = 0; i < kNumThreads; i++) {
grpc_core::testing::FakeUdpAndTcpServer* fake_http_server_ptr =
&fake_http_server;
threads.push_back(std::thread([this, fake_http_server_ptr]() {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
memset(&req, 0, sizeof(req));
grpc_arg ssl_override_arg = grpc_channel_arg_string_create(
const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
const_cast<char*>("foo.test.google.fr"));
grpc_channel_args args = {1, &ssl_override_arg};
auto uri = grpc_core::URI::Create(
"https", fake_http_server_ptr->address(), "/get",
{} /* query params */, "" /* fragment */);
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
std::move(*uri), &args, pops(), &req, NSecondsTime(120),
GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::CreateHttpRequestSSLCredentials());
// Start a request. It will establish a TCP connection to the
// server and then begin an SSL handshake. The server won't send
// anything back though, so it will be stuck in its SSL handshake,
// waiting for the firt response from the server.
http_request->Start();
exec_ctx.Flush();
std::thread cancel_thread([&http_request]() {
// Give one second to let the client get into the middle of its
// SSL handshake, and then cancel the request.
gpr_sleep_until(grpc_timeout_seconds_to_deadline(1));
grpc_core::ExecCtx exec_ctx;
http_request.reset();
});
// Poll with a deadline explicitly lower than the request timeout, so
// that we know that the request timeout isn't just kicking in.
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
cancel_thread.join();
}));
}
for (auto& t : threads) {
t.join();
}
}
gpr_subprocess_destroy(server);
} // namespace
return 0;
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(argc, argv);
// launch the test server later, so that --gtest_list_tests works
g_argc = argc;
g_argv = argv;
// run tests
return RUN_ALL_TESTS();
}

File diff suppressed because it is too large Load Diff

@ -330,8 +330,8 @@ static char* good_google_email_keys(void) {
return result;
}
static grpc_httpcli_response http_response(int status, char* body) {
grpc_httpcli_response response;
static grpc_http_response http_response(int status, char* body) {
grpc_http_response response;
response = {};
response.status = status;
response.body = body;
@ -340,20 +340,21 @@ static grpc_httpcli_response http_response(int status, char* body) {
}
static int httpcli_post_should_not_be_called(
const grpc_httpcli_request* /*request*/, const char* /*body_bytes*/,
size_t /*body_size*/, grpc_millis /*deadline*/, grpc_closure* /*on_done*/,
grpc_httpcli_response* /*response*/) {
const grpc_http_request* /*request*/, const char* /*host*/,
const char* /*path*/, const char* /*body_bytes*/, size_t /*body_size*/,
grpc_millis /*deadline*/, grpc_closure* /*on_done*/,
grpc_http_response* /*response*/) {
GPR_ASSERT("HTTP POST should not be called" == nullptr);
return 1;
}
static int httpcli_get_google_keys_for_email(
const grpc_httpcli_request* request, grpc_millis /*deadline*/,
grpc_closure* on_done, grpc_httpcli_response* response) {
const grpc_http_request* /*request*/, const char* host, const char* path,
grpc_millis /*deadline*/, grpc_closure* on_done,
grpc_http_response* response) {
*response = http_response(200, good_google_email_keys());
GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
GPR_ASSERT(strcmp(request->host, "www.googleapis.com") == 0);
GPR_ASSERT(strcmp(request->http.path,
GPR_ASSERT(strcmp(host, "www.googleapis.com") == 0);
GPR_ASSERT(strcmp(path,
"/robot/v1/metadata/x509/"
"777-abaslkan11hlb6nmim3bpspl31ud@developer."
"gserviceaccount.com") == 0);
@ -379,8 +380,8 @@ static void test_jwt_verifier_google_email_issuer_success(void) {
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_google_keys_for_email,
httpcli_post_should_not_be_called);
grpc_core::HttpRequest::SetOverride(httpcli_get_google_keys_for_email,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
grpc_auth_json_key_destruct(&key);
@ -391,16 +392,16 @@ static void test_jwt_verifier_google_email_issuer_success(void) {
grpc_jwt_verifier_destroy(verifier);
grpc_core::ExecCtx::Get()->Flush();
gpr_free(jwt);
grpc_httpcli_set_override(nullptr, nullptr);
grpc_core::HttpRequest::SetOverride(nullptr, nullptr);
}
static int httpcli_get_custom_keys_for_email(
const grpc_httpcli_request* request, grpc_millis /*deadline*/,
grpc_closure* on_done, grpc_httpcli_response* response) {
const grpc_http_request* /*request*/, const char* host, const char* path,
grpc_millis /*deadline*/, grpc_closure* on_done,
grpc_http_response* response) {
*response = http_response(200, gpr_strdup(good_jwk_set));
GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
GPR_ASSERT(strcmp(request->host, "keys.bar.com") == 0);
GPR_ASSERT(strcmp(request->http.path, "/jwk/foo@bar.com") == 0);
GPR_ASSERT(strcmp(host, "keys.bar.com") == 0);
GPR_ASSERT(strcmp(path, "/jwk/foo@bar.com") == 0);
grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_done, GRPC_ERROR_NONE);
return 1;
}
@ -413,8 +414,8 @@ static void test_jwt_verifier_custom_email_issuer_success(void) {
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_custom_keys_for_email,
httpcli_post_should_not_be_called);
grpc_core::HttpRequest::SetOverride(httpcli_get_custom_keys_for_email,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
grpc_auth_json_key_destruct(&key);
@ -425,30 +426,30 @@ static void test_jwt_verifier_custom_email_issuer_success(void) {
grpc_jwt_verifier_destroy(verifier);
grpc_core::ExecCtx::Get()->Flush();
gpr_free(jwt);
grpc_httpcli_set_override(nullptr, nullptr);
grpc_core::HttpRequest::SetOverride(nullptr, nullptr);
}
static int httpcli_get_jwk_set(const grpc_httpcli_request* request,
static int httpcli_get_jwk_set(const grpc_http_request* /*request*/,
const char* host, const char* path,
grpc_millis /*deadline*/, grpc_closure* on_done,
grpc_httpcli_response* response) {
grpc_http_response* response) {
*response = http_response(200, gpr_strdup(good_jwk_set));
GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
GPR_ASSERT(strcmp(request->host, "www.googleapis.com") == 0);
GPR_ASSERT(strcmp(request->http.path, "/oauth2/v3/certs") == 0);
GPR_ASSERT(strcmp(host, "www.googleapis.com") == 0);
GPR_ASSERT(strcmp(path, "/oauth2/v3/certs") == 0);
grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_done, GRPC_ERROR_NONE);
return 1;
}
static int httpcli_get_openid_config(const grpc_httpcli_request* request,
static int httpcli_get_openid_config(const grpc_http_request* /*request*/,
const char* host, const char* path,
grpc_millis /*deadline*/,
grpc_closure* on_done,
grpc_httpcli_response* response) {
grpc_http_response* response) {
*response = http_response(200, gpr_strdup(good_openid_config));
GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
GPR_ASSERT(strcmp(request->host, "accounts.google.com") == 0);
GPR_ASSERT(strcmp(request->http.path, GRPC_OPENID_CONFIG_URL_SUFFIX) == 0);
grpc_httpcli_set_override(httpcli_get_jwk_set,
httpcli_post_should_not_be_called);
GPR_ASSERT(strcmp(host, "accounts.google.com") == 0);
GPR_ASSERT(strcmp(path, GRPC_OPENID_CONFIG_URL_SUFFIX) == 0);
grpc_core::HttpRequest::SetOverride(httpcli_get_jwk_set,
httpcli_post_should_not_be_called);
grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_done, GRPC_ERROR_NONE);
return 1;
}
@ -461,8 +462,8 @@ static void test_jwt_verifier_url_issuer_success(void) {
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_openid_config,
httpcli_post_should_not_be_called);
grpc_core::HttpRequest::SetOverride(httpcli_get_openid_config,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
grpc_auth_json_key_destruct(&key);
@ -473,7 +474,7 @@ static void test_jwt_verifier_url_issuer_success(void) {
grpc_jwt_verifier_destroy(verifier);
grpc_core::ExecCtx::Get()->Flush();
gpr_free(jwt);
grpc_httpcli_set_override(nullptr, nullptr);
grpc_core::HttpRequest::SetOverride(nullptr, nullptr);
}
static void on_verification_key_retrieval_error(void* user_data,
@ -484,11 +485,11 @@ static void on_verification_key_retrieval_error(void* user_data,
GPR_ASSERT(user_data == (void*)expected_user_data);
}
static int httpcli_get_bad_json(const grpc_httpcli_request* request,
static int httpcli_get_bad_json(const grpc_http_request* /* request */,
const char* /*host*/, const char* /*path*/,
grpc_millis /*deadline*/, grpc_closure* on_done,
grpc_httpcli_response* response) {
grpc_http_response* response) {
*response = http_response(200, gpr_strdup("{\"bad\": \"stuff\"}"));
GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_done, GRPC_ERROR_NONE);
return 1;
}
@ -501,8 +502,8 @@ static void test_jwt_verifier_url_issuer_bad_config(void) {
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_bad_json,
httpcli_post_should_not_be_called);
grpc_core::HttpRequest::SetOverride(httpcli_get_bad_json,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
grpc_auth_json_key_destruct(&key);
@ -513,7 +514,7 @@ static void test_jwt_verifier_url_issuer_bad_config(void) {
grpc_jwt_verifier_destroy(verifier);
grpc_core::ExecCtx::Get()->Flush();
gpr_free(jwt);
grpc_httpcli_set_override(nullptr, nullptr);
grpc_core::HttpRequest::SetOverride(nullptr, nullptr);
}
static void test_jwt_verifier_bad_json_key(void) {
@ -524,8 +525,8 @@ static void test_jwt_verifier_bad_json_key(void) {
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_bad_json,
httpcli_post_should_not_be_called);
grpc_core::HttpRequest::SetOverride(httpcli_get_bad_json,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
grpc_auth_json_key_destruct(&key);
@ -536,7 +537,7 @@ static void test_jwt_verifier_bad_json_key(void) {
grpc_jwt_verifier_destroy(verifier);
grpc_core::ExecCtx::Get()->Flush();
gpr_free(jwt);
grpc_httpcli_set_override(nullptr, nullptr);
grpc_core::HttpRequest::SetOverride(nullptr, nullptr);
}
static void corrupt_jwt_sig(char* jwt) {
@ -575,8 +576,8 @@ static void test_jwt_verifier_bad_signature(void) {
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_openid_config,
httpcli_post_should_not_be_called);
grpc_core::HttpRequest::SetOverride(httpcli_get_openid_config,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
grpc_auth_json_key_destruct(&key);
@ -588,12 +589,13 @@ static void test_jwt_verifier_bad_signature(void) {
gpr_free(jwt);
grpc_jwt_verifier_destroy(verifier);
grpc_core::ExecCtx::Get()->Flush();
grpc_httpcli_set_override(nullptr, nullptr);
grpc_core::HttpRequest::SetOverride(nullptr, nullptr);
}
static int httpcli_get_should_not_be_called(
const grpc_httpcli_request* /*request*/, grpc_millis /*deadline*/,
grpc_closure* /*on_done*/, grpc_httpcli_response* /*response*/) {
const grpc_http_request* /*request*/, const char* /*host*/,
const char* /*path*/, grpc_millis /*deadline*/, grpc_closure* /*on_done*/,
grpc_http_response* /*response*/) {
GPR_ASSERT(0);
return 1;
}
@ -609,14 +611,14 @@ static void on_verification_bad_format(void* user_data,
static void test_jwt_verifier_bad_format(void) {
grpc_core::ExecCtx exec_ctx;
grpc_jwt_verifier* verifier = grpc_jwt_verifier_create(nullptr, 0);
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
httpcli_post_should_not_be_called);
grpc_core::HttpRequest::SetOverride(httpcli_get_should_not_be_called,
httpcli_post_should_not_be_called);
grpc_jwt_verifier_verify(verifier, nullptr, "bad jwt", expected_audience,
on_verification_bad_format,
const_cast<char*>(expected_user_data));
grpc_jwt_verifier_destroy(verifier);
grpc_core::ExecCtx::Get()->Flush();
grpc_httpcli_set_override(nullptr, nullptr);
grpc_core::HttpRequest::SetOverride(nullptr, nullptr);
}
/* find verification key: bad jks, cannot find key in jks */

@ -21,6 +21,7 @@
#include <gtest/gtest.h>
#include "src/core/lib/resource_quota/resource_quota.h"
#include "src/core/lib/security/authorization/evaluate_args.h"
#include "test/core/util/mock_authorization_endpoint.h"

@ -30,17 +30,31 @@
#include "src/core/lib/iomgr/tcp_windows.h"
#define BAD_SOCKET_RETURN_VAL INVALID_SOCKET
#define CLOSE_SOCKET closesocket
#define ERRNO WSAGetLastError()
#else
#include <fcntl.h>
#include "src/core/lib/iomgr/sockaddr_posix.h"
#define BAD_SOCKET_RETURN_VAL (-1)
#define CLOSE_SOCKET close
#define ERRNO errno
#endif
namespace grpc_core {
namespace testing {
namespace {
bool ErrorIsRetryable(int error) {
#ifdef GPR_WINDOWS
return error == WSAEWOULDBLOCK || error == WSAEINPROGRESS;
#else
return error == EWOULDBLOCK || error == EAGAIN;
#endif
}
} // namespace
FakeUdpAndTcpServer::FakeUdpAndTcpServer(
AcceptMode accept_mode,
std::function<FakeUdpAndTcpServer::ProcessReadResult(int, int, int)>
@ -49,13 +63,13 @@ FakeUdpAndTcpServer::FakeUdpAndTcpServer(
port_ = grpc_pick_unused_port_or_die();
udp_socket_ = socket(AF_INET6, SOCK_DGRAM, 0);
if (udp_socket_ == BAD_SOCKET_RETURN_VAL) {
gpr_log(GPR_DEBUG, "Failed to create UDP ipv6 socket: %d", errno);
gpr_log(GPR_DEBUG, "Failed to create UDP ipv6 socket: %d", ERRNO);
GPR_ASSERT(0);
}
accept_socket_ = socket(AF_INET6, SOCK_STREAM, 0);
address_ = absl::StrCat("[::]:", port_);
address_ = absl::StrCat("[::1]:", port_);
if (accept_socket_ == BAD_SOCKET_RETURN_VAL) {
gpr_log(GPR_ERROR, "Failed to create TCP IPv6 socket: %d", errno);
gpr_log(GPR_ERROR, "Failed to create TCP IPv6 socket: %d", ERRNO);
GPR_ASSERT(0);
}
#ifdef GPR_WINDOWS
@ -65,7 +79,7 @@ FakeUdpAndTcpServer::FakeUdpAndTcpServer(
gpr_log(GPR_DEBUG,
"Failed to set SO_REUSEADDR on TCP ipv6 socket to [::1]:%d, "
"errno: %d",
port_, errno);
port_, ERRNO);
GPR_ASSERT(0);
}
grpc_error_handle set_non_block_error;
@ -89,11 +103,11 @@ FakeUdpAndTcpServer::FakeUdpAndTcpServer(
GPR_ASSERT(0);
}
if (fcntl(udp_socket_, F_SETFL, O_NONBLOCK) != 0) {
gpr_log(GPR_ERROR, "Failed to set O_NONBLOCK on socket: %d", errno);
gpr_log(GPR_ERROR, "Failed to set O_NONBLOCK on socket: %d", ERRNO);
GPR_ASSERT(0);
}
if (fcntl(accept_socket_, F_SETFL, O_NONBLOCK) != 0) {
gpr_log(GPR_ERROR, "Failed to set O_NONBLOCK on socket: %d", errno);
gpr_log(GPR_ERROR, "Failed to set O_NONBLOCK on socket: %d", ERRNO);
GPR_ASSERT(0);
}
#endif
@ -110,12 +124,12 @@ FakeUdpAndTcpServer::FakeUdpAndTcpServer(
if (bind(accept_socket_, reinterpret_cast<const sockaddr*>(&addr),
sizeof(addr)) != 0) {
gpr_log(GPR_ERROR, "Failed to bind TCP socket to [::1]:%d : %d", port_,
errno);
ERRNO);
GPR_ASSERT(0);
}
if (listen(accept_socket_, 100)) {
gpr_log(GPR_ERROR, "Failed to listen on socket bound to [::1]:%d : %d",
port_, errno);
port_, ERRNO);
GPR_ASSERT(0);
}
gpr_event_init(&stop_ev_);
@ -139,10 +153,9 @@ FakeUdpAndTcpServer::~FakeUdpAndTcpServer() {
FakeUdpAndTcpServer::ProcessReadResult
FakeUdpAndTcpServer::CloseSocketUponReceivingBytesFromPeer(
int bytes_received_size, int read_error, int s) {
if (bytes_received_size < 0 && read_error != EAGAIN &&
read_error != EWOULDBLOCK) {
if (bytes_received_size < 0 && !ErrorIsRetryable(read_error)) {
gpr_log(GPR_ERROR, "Failed to receive from peer socket: %d. errno: %d", s,
errno);
read_error);
GPR_ASSERT(0);
}
if (bytes_received_size >= 0) {
@ -159,10 +172,9 @@ FakeUdpAndTcpServer::CloseSocketUponReceivingBytesFromPeer(
FakeUdpAndTcpServer::ProcessReadResult
FakeUdpAndTcpServer::CloseSocketUponCloseFromPeer(int bytes_received_size,
int read_error, int s) {
if (bytes_received_size < 0 && read_error != EAGAIN &&
read_error != EWOULDBLOCK) {
if (bytes_received_size < 0 && !ErrorIsRetryable(read_error)) {
gpr_log(GPR_ERROR, "Failed to receive from peer socket: %d. errno: %d", s,
errno);
read_error);
GPR_ASSERT(0);
}
if (bytes_received_size == 0) {
@ -198,11 +210,11 @@ void FakeUdpAndTcpServer::FakeUdpAndTcpServerPeer::
int bytes_sent =
send(fd_, kEmptyHttp2SettingsFrame.data() + total_bytes_sent_,
bytes_to_send, 0);
if (bytes_sent < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
if (bytes_sent < 0 && !ErrorIsRetryable(ERRNO)) {
gpr_log(GPR_ERROR,
"Fake TCP server encountered unexpected error:%d |%s| "
"Fake TCP server encountered unexpected error:%d "
"sending %d bytes on fd:%d",
errno, strerror(errno), bytes_to_send, fd_);
ERRNO, bytes_to_send, fd_);
GPR_ASSERT(0);
} else if (bytes_sent > 0) {
total_bytes_sent_ += bytes_sent;
@ -234,7 +246,7 @@ void FakeUdpAndTcpServer::RunServerLoop() {
#else
if (fcntl(p, F_SETFL, O_NONBLOCK) != 0) {
gpr_log(GPR_ERROR, "Failed to configure non-blocking socket, errno: %d",
errno);
ERRNO);
GPR_ASSERT(0);
}
#endif
@ -249,7 +261,7 @@ void FakeUdpAndTcpServer::RunServerLoop() {
char buf[100];
int bytes_received_size = recv(peer->fd(), buf, 100, 0);
FakeUdpAndTcpServer::ProcessReadResult r =
process_read_cb_(bytes_received_size, errno, peer->fd());
process_read_cb_(bytes_received_size, ERRNO, peer->fd());
if (r == FakeUdpAndTcpServer::ProcessReadResult::kCloseSocket) {
it = peers.erase(it);
} else {

@ -24,6 +24,8 @@
#include <math.h>
#include <string.h>
#include "absl/strings/str_format.h"
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
@ -58,10 +60,9 @@ static void freed_port_from_server(void* arg, grpc_error_handle /*error*/) {
}
void grpc_free_port_using_server(int port) {
grpc_httpcli_request req;
grpc_httpcli_response rsp;
grpc_http_request req;
grpc_http_response rsp;
freereq pr;
char* path;
grpc_closure* shutdown_closure;
grpc_init();
@ -79,15 +80,19 @@ void grpc_free_port_using_server(int port) {
shutdown_closure = GRPC_CLOSURE_CREATE(destroy_pops_and_shutdown, &pr.pops,
grpc_schedule_on_exec_ctx);
req.host = const_cast<char*>(GRPC_PORT_SERVER_ADDRESS);
gpr_asprintf(&path, "/drop/%d", port);
req.http.path = path;
grpc_httpcli_get(&pr.pops, grpc_core::ResourceQuota::Default(), &req,
grpc_core::ExecCtx::Get()->Now() + 30 * GPR_MS_PER_SEC,
GRPC_CLOSURE_CREATE(freed_port_from_server, &pr,
grpc_schedule_on_exec_ctx),
&rsp);
std::string path = absl::StrFormat("/drop/%d", port);
auto uri = grpc_core::URI::Create("https", GRPC_PORT_SERVER_ADDRESS, path,
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok());
auto http_request = grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, &pr.pops, &req,
grpc_core::ExecCtx::Get()->Now() + 30 * GPR_MS_PER_SEC,
GRPC_CLOSURE_CREATE(freed_port_from_server, &pr,
grpc_schedule_on_exec_ctx),
&rsp,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
nullptr /* insecure credentials */));
http_request->Start();
grpc_core::ExecCtx::Get()->Flush();
gpr_mu_lock(pr.mu);
while (!pr.done) {
@ -105,7 +110,6 @@ void grpc_free_port_using_server(int port) {
grpc_pollset_shutdown(grpc_polling_entity_pollset(&pr.pops),
shutdown_closure);
gpr_free(path);
grpc_http_response_destroy(&rsp);
}
grpc_shutdown();
@ -117,15 +121,17 @@ typedef struct portreq {
int port = 0;
int retries = 0;
char* server = nullptr;
grpc_httpcli_response response = {};
grpc_http_response response = {};
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request;
} portreq;
static void got_port_from_server(void* arg, grpc_error_handle error) {
size_t i;
int port = 0;
portreq* pr = static_cast<portreq*>(arg);
pr->http_request.reset();
int failed = 0;
grpc_httpcli_response* response = &pr->response;
grpc_http_response* response = &pr->response;
if (error != GRPC_ERROR_NONE) {
failed = 1;
@ -138,7 +144,7 @@ static void got_port_from_server(void* arg, grpc_error_handle error) {
}
if (failed) {
grpc_httpcli_request req;
grpc_http_request req;
memset(&req, 0, sizeof(req));
if (pr->retries >= 5) {
gpr_mu_lock(pr->mu);
@ -157,15 +163,20 @@ static void got_port_from_server(void* arg, grpc_error_handle error) {
1000.0 * (1 + pow(1.3, pr->retries) * rand() / RAND_MAX)),
GPR_TIMESPAN)));
pr->retries++;
req.host = pr->server;
req.http.path = const_cast<char*>("/get");
grpc_http_response_destroy(&pr->response);
pr->response = {};
grpc_httpcli_get(&pr->pops, grpc_core::ResourceQuota::Default(), &req,
grpc_core::ExecCtx::Get()->Now() + 30 * GPR_MS_PER_SEC,
GRPC_CLOSURE_CREATE(got_port_from_server, pr,
grpc_schedule_on_exec_ctx),
&pr->response);
auto uri = grpc_core::URI::Create("http", pr->server, "/get",
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok());
pr->http_request = grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, &pr->pops, &req,
grpc_core::ExecCtx::Get()->Now() + 30 * GPR_MS_PER_SEC,
GRPC_CLOSURE_CREATE(got_port_from_server, pr,
grpc_schedule_on_exec_ctx),
&pr->response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
nullptr /* insecure credentials */));
pr->http_request->Start();
return;
}
GPR_ASSERT(response);
@ -184,7 +195,7 @@ static void got_port_from_server(void* arg, grpc_error_handle error) {
}
int grpc_pick_port_using_server(void) {
grpc_httpcli_request req;
grpc_http_request req;
portreq pr;
grpc_closure* shutdown_closure;
@ -201,15 +212,18 @@ int grpc_pick_port_using_server(void) {
grpc_schedule_on_exec_ctx);
pr.port = -1;
pr.server = const_cast<char*>(GRPC_PORT_SERVER_ADDRESS);
req.host = const_cast<char*>(GRPC_PORT_SERVER_ADDRESS);
req.http.path = const_cast<char*>("/get");
grpc_httpcli_get(&pr.pops, grpc_core::ResourceQuota::Default(), &req,
grpc_core::ExecCtx::Get()->Now() + 30 * GPR_MS_PER_SEC,
GRPC_CLOSURE_CREATE(got_port_from_server, &pr,
grpc_schedule_on_exec_ctx),
&pr.response);
auto uri = grpc_core::URI::Create("http", GRPC_PORT_SERVER_ADDRESS, "/get",
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok());
auto http_request = grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, &pr.pops, &req,
grpc_core::ExecCtx::Get()->Now() + 30 * GPR_MS_PER_SEC,
GRPC_CLOSURE_CREATE(got_port_from_server, &pr,
grpc_schedule_on_exec_ctx),
&pr.response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
nullptr /*insecure credentials*/));
http_request->Start();
grpc_core::ExecCtx::Get()->Flush();
gpr_mu_lock(pr.mu);
while (pr.port == -1) {

@ -30,6 +30,7 @@ grpc_cc_test(
"opencensus-with-tag-map",
],
language = "C++",
linkstatic = True,
tags = ["no_windows"], # TODO(jtattermusch): fix test on windows
deps = [
"//:grpc++",

@ -1885,6 +1885,7 @@ src/core/lib/http/format_request.h \
src/core/lib/http/httpcli.cc \
src/core/lib/http/httpcli.h \
src/core/lib/http/httpcli_security_connector.cc \
src/core/lib/http/httpcli_ssl_credentials.h \
src/core/lib/http/parser.cc \
src/core/lib/http/parser.h \
src/core/lib/iomgr/block_annotate.h \

@ -1683,6 +1683,7 @@ src/core/lib/http/format_request.h \
src/core/lib/http/httpcli.cc \
src/core/lib/http/httpcli.h \
src/core/lib/http/httpcli_security_connector.cc \
src/core/lib/http/httpcli_ssl_credentials.h \
src/core/lib/http/parser.cc \
src/core/lib/http/parser.h \
src/core/lib/iomgr/README.md \

@ -1421,50 +1421,6 @@
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": false,
"language": "c",
"name": "httpcli_test",
"platforms": [
"linux",
"mac",
"posix"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": false,
"language": "c",
"name": "httpscli_test",
"platforms": [
"linux",
"mac",
"posix"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
@ -4831,6 +4787,50 @@
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "httpcli_test",
"platforms": [
"linux",
"mac",
"posix"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "httpscli_test",
"platforms": [
"linux",
"mac",
"posix"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save