couchbase 3.8.1 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/ext/CMakeLists.txt +16 -1
- data/ext/cache/cpm/{CPM_0.42.0.cmake → CPM_0.43.1.cmake} +18 -2
- data/ext/cache/extconf_include.rb +6 -6
- data/ext/cache/mozilla-ca-bundle.crt +214 -155
- data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
- data/ext/couchbase/CMakeLists.txt +130 -21
- data/ext/couchbase/cmake/APKBUILD.in +25 -4
- data/ext/couchbase/cmake/Bundler.cmake +143 -34
- data/ext/couchbase/cmake/CPM.cmake +126 -12
- data/ext/couchbase/cmake/Cache.cmake +7 -1
- data/ext/couchbase/cmake/CompilerOptions.cmake +1 -1
- data/ext/couchbase/cmake/CompilerWarnings.cmake +17 -6
- data/ext/couchbase/cmake/GrpcProtobuf.cmake +481 -0
- data/ext/couchbase/cmake/OpenSSL.cmake +10 -3
- data/ext/couchbase/cmake/Packaging.cmake +519 -65
- data/ext/couchbase/cmake/Protostellar.cmake +250 -0
- data/ext/couchbase/cmake/Sanitizers.cmake +61 -50
- data/ext/couchbase/cmake/StaticAnalyzers.cmake +16 -5
- data/ext/couchbase/cmake/TestFramework.cmake +283 -0
- data/ext/couchbase/cmake/TestFrameworkAddTests.cmake +51 -0
- data/ext/couchbase/cmake/Testing.cmake +27 -9
- data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +141 -8
- data/ext/couchbase/cmake/VersionInfo.cmake +50 -0
- data/ext/couchbase/cmake/build_config.hxx.in +2 -1
- data/ext/couchbase/cmake/couchbase-cxx-client.spec.in +101 -23
- data/ext/couchbase/cmake/couchbase_cxx_client_static-config.cmake.in +7 -0
- data/ext/couchbase/cmake/couchbase_cxx_client_static.pc.in +2 -0
- data/ext/couchbase/cmake/gRPC_Generate.cmake +58 -0
- data/ext/couchbase/cmake/protobuf_Generate.cmake +69 -0
- data/ext/couchbase/cmake/require-git-checkout.cmake +25 -0
- data/ext/couchbase/cmake/tarball_glob.txt +49 -0
- data/ext/couchbase/core/agent_group.cxx +0 -8
- data/ext/couchbase/core/agent_group.hxx +0 -4
- data/ext/couchbase/core/analytics_stream.cxx +314 -0
- data/ext/couchbase/core/analytics_stream.hxx +129 -0
- data/ext/couchbase/core/analytics_stream_component.cxx +245 -0
- data/ext/couchbase/core/analytics_stream_component.hxx +67 -0
- data/ext/couchbase/core/app_telemetry_meter.cxx +25 -3
- data/ext/couchbase/core/app_telemetry_meter.hxx +77 -0
- data/ext/couchbase/core/app_telemetry_reporter.cxx +7 -6
- data/ext/couchbase/core/bucket.cxx +381 -87
- data/ext/couchbase/core/bucket.hxx +105 -25
- data/ext/couchbase/core/bucket_unit_test_api.hxx +57 -0
- data/ext/couchbase/core/capella.hxx +91 -0
- data/ext/couchbase/core/cluster.cxx +1554 -398
- data/ext/couchbase/core/cluster.hxx +39 -8
- data/ext/couchbase/core/cluster_agent.cxx +0 -8
- data/ext/couchbase/core/cluster_agent.hxx +0 -4
- data/ext/couchbase/core/cluster_options.hxx +26 -3
- data/ext/couchbase/core/collections_component.cxx +39 -15
- data/ext/couchbase/core/config_profiles.cxx +1 -1
- data/ext/couchbase/core/crud_component.cxx +2 -2
- data/ext/couchbase/core/crud_options.hxx +34 -0
- data/ext/couchbase/core/crypto/cbcrypto.cc +18 -13
- data/ext/couchbase/core/document_id_redaction.hxx +81 -0
- data/ext/couchbase/core/error_context/base_error_context.hxx +7 -6
- data/ext/couchbase/core/error_context/key_value.cxx +25 -2
- data/ext/couchbase/core/error_context/key_value.hxx +60 -33
- data/ext/couchbase/core/error_context/key_value_error_context.hxx +16 -15
- data/ext/couchbase/core/error_context/search.hxx +5 -0
- data/ext/couchbase/core/error_context/search_json.hxx +3 -0
- data/ext/couchbase/core/error_context/subdocument_error_context.hxx +19 -18
- data/ext/couchbase/core/error_context/view.hxx +5 -0
- data/ext/couchbase/core/free_form_http_request.cxx +237 -4
- data/ext/couchbase/core/free_form_http_request.hxx +41 -8
- data/ext/couchbase/core/http_component.cxx +300 -284
- data/ext/couchbase/core/http_component.hxx +0 -4
- data/ext/couchbase/core/impl/analytics.cxx +38 -5
- data/ext/couchbase/core/impl/analytics.hxx +34 -0
- data/ext/couchbase/core/impl/bootstrap_error.hxx +0 -3
- data/ext/couchbase/core/impl/collection.cxx +215 -202
- data/ext/couchbase/core/impl/collection_manager.cxx +1 -1
- data/ext/couchbase/core/impl/dns_srv_tracker.cxx +45 -18
- data/ext/couchbase/core/impl/error.cxx +22 -2
- data/ext/couchbase/core/impl/internal_analytics_stream_result.hxx +111 -0
- data/ext/couchbase/core/impl/internal_query_stream_result.hxx +110 -0
- data/ext/couchbase/core/impl/invoke_with_node_id.hxx +15 -7
- data/ext/couchbase/core/impl/key_value_error_category.cxx +6 -0
- data/ext/couchbase/core/impl/lookup_in_replica.cxx +10 -10
- data/ext/couchbase/core/impl/lookup_in_replica.hxx +4 -4
- data/ext/couchbase/core/impl/node_id.cxx +49 -13
- data/ext/couchbase/core/impl/observability_recorder.cxx +12 -2
- data/ext/couchbase/core/impl/observe_poll.cxx +63 -23
- data/ext/couchbase/core/impl/public_analytics_stream_result.cxx +372 -0
- data/ext/couchbase/core/impl/public_bucket.cxx +48 -2
- data/ext/couchbase/core/impl/public_cluster.cxx +325 -25
- data/ext/couchbase/core/impl/public_query_stream_result.cxx +380 -0
- data/ext/couchbase/core/impl/public_transaction_get_multi_replicas_from_preferred_server_group_result.cxx +42 -0
- data/ext/couchbase/core/impl/public_transaction_get_multi_result.cxx +42 -0
- data/ext/couchbase/core/impl/query.cxx +76 -7
- data/ext/couchbase/core/impl/query.hxx +36 -0
- data/ext/couchbase/core/impl/query_index_manager.cxx +54 -1
- data/ext/couchbase/core/impl/replica_utils.cxx +101 -0
- data/ext/couchbase/core/impl/replica_utils.hxx +64 -0
- data/ext/couchbase/core/impl/resolve_node_id.hxx +125 -0
- data/ext/couchbase/core/impl/scope.cxx +70 -0
- data/ext/couchbase/core/impl/search.cxx +31 -1
- data/ext/couchbase/core/impl/transactions.cxx +130 -0
- data/ext/couchbase/core/impl/wait_until_ready.cxx +439 -0
- data/ext/couchbase/core/impl/wait_until_ready.hxx +110 -0
- data/ext/couchbase/core/impl/with_bucket_config_or_timeout.hxx +180 -0
- data/ext/couchbase/core/impl/with_cancellation.hxx +11 -3
- data/ext/couchbase/core/io/configuration_belongs_to_session.hxx +66 -0
- data/ext/couchbase/core/io/dns_client.cxx +122 -45
- data/ext/couchbase/core/io/dns_codec.hxx +21 -21
- data/ext/couchbase/core/io/dns_config.cxx +21 -6
- data/ext/couchbase/core/io/http_command.hxx +149 -121
- data/ext/couchbase/core/io/http_error.hxx +107 -0
- data/ext/couchbase/core/io/http_parser.cxx +4 -0
- data/ext/couchbase/core/io/http_session.cxx +606 -154
- data/ext/couchbase/core/io/http_session.hxx +147 -30
- data/ext/couchbase/core/io/http_session_manager.hxx +329 -382
- data/ext/couchbase/core/io/http_streaming_parser.cxx +4 -0
- data/ext/couchbase/core/io/http_streaming_response.cxx +328 -43
- data/ext/couchbase/core/io/http_streaming_response.hxx +37 -3
- data/ext/couchbase/core/io/mcbp_buffer_pool.hxx +122 -0
- data/ext/couchbase/core/io/mcbp_command.hxx +150 -51
- data/ext/couchbase/core/io/mcbp_command_id.hxx +45 -0
- data/ext/couchbase/core/io/mcbp_message.cxx +9 -1
- data/ext/couchbase/core/io/mcbp_message.hxx +19 -0
- data/ext/couchbase/core/io/mcbp_output_queue.hxx +150 -0
- data/ext/couchbase/core/io/mcbp_parser.cxx +24 -7
- data/ext/couchbase/core/io/mcbp_session.cxx +316 -317
- data/ext/couchbase/core/io/mcbp_session.hxx +27 -18
- data/ext/couchbase/core/io/opaque_ring_table.hxx +145 -0
- data/ext/couchbase/core/io/retry_orchestrator.hxx +6 -5
- data/ext/couchbase/core/io/stream_deadline.hxx +50 -0
- data/ext/couchbase/core/io/streams.cxx +275 -14
- data/ext/couchbase/core/io/streams.hxx +53 -0
- data/ext/couchbase/core/logger/logger.cxx +36 -0
- data/ext/couchbase/core/logger/logger.hxx +46 -6
- data/ext/couchbase/core/logger/redaction.hxx +503 -0
- data/ext/couchbase/core/management/eventing_status.hxx +10 -0
- data/ext/couchbase/core/management/eventing_status_json.hxx +4 -0
- data/ext/couchbase/core/mcbp/big_endian.cxx +1 -1
- data/ext/couchbase/core/mcbp/codec.cxx +27 -18
- data/ext/couchbase/core/mcbp/operation_queue.cxx +2 -2
- data/ext/couchbase/core/mcbp/packet.hxx +2 -2
- data/ext/couchbase/core/mcbp/queue_request.cxx +23 -6
- data/ext/couchbase/core/mcbp/queue_request.hxx +21 -0
- data/ext/couchbase/core/meta/features.hxx +13 -0
- data/ext/couchbase/core/meta/version.cxx +6 -3
- data/ext/couchbase/core/metrics/logging_meter.cxx +5 -10
- data/ext/couchbase/core/metrics/meter_wrapper.cxx +63 -33
- data/ext/couchbase/core/metrics/meter_wrapper.hxx +55 -3
- data/ext/couchbase/core/operations/analytics_response_parsing.cxx +177 -0
- data/ext/couchbase/core/operations/analytics_response_parsing.hxx +55 -0
- data/ext/couchbase/core/operations/document_analytics.cxx +48 -130
- data/ext/couchbase/core/operations/document_analytics.hxx +14 -2
- data/ext/couchbase/core/operations/document_get_all_replicas.hxx +15 -7
- data/ext/couchbase/core/operations/document_get_any_replica.hxx +19 -8
- data/ext/couchbase/core/operations/document_get_projected.cxx +1 -1
- data/ext/couchbase/core/{impl/get_replica.cxx → operations/document_get_replica.cxx} +21 -3
- data/ext/couchbase/core/{impl/get_replica.hxx → operations/document_get_replica.hxx} +31 -4
- data/ext/couchbase/core/operations/document_lookup_in.cxx +10 -10
- data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +228 -198
- data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +215 -217
- data/ext/couchbase/core/operations/document_query.cxx +108 -221
- data/ext/couchbase/core/operations/document_query.hxx +18 -4
- data/ext/couchbase/core/operations/document_search.cxx +52 -8
- data/ext/couchbase/core/operations/document_search.hxx +3 -0
- data/ext/couchbase/core/operations/document_view.cxx +3 -3
- data/ext/couchbase/core/operations/management/analytics_dataset_create.cxx +15 -8
- data/ext/couchbase/core/operations/management/analytics_dataset_create.hxx +4 -0
- data/ext/couchbase/core/operations/management/analytics_dataset_drop.cxx +11 -6
- data/ext/couchbase/core/operations/management/analytics_dataset_get_all.cxx +3 -2
- data/ext/couchbase/core/operations/management/analytics_dataverse_create.cxx +10 -5
- data/ext/couchbase/core/operations/management/analytics_dataverse_drop.cxx +10 -5
- data/ext/couchbase/core/operations/management/analytics_get_pending_mutations.cxx +2 -1
- data/ext/couchbase/core/operations/management/analytics_index_create.cxx +33 -8
- data/ext/couchbase/core/operations/management/analytics_index_drop.cxx +12 -7
- data/ext/couchbase/core/operations/management/analytics_index_get_all.cxx +3 -2
- data/ext/couchbase/core/operations/management/analytics_link_connect.cxx +11 -6
- data/ext/couchbase/core/operations/management/analytics_link_create.cxx +3 -3
- data/ext/couchbase/core/operations/management/analytics_link_disconnect.cxx +10 -4
- data/ext/couchbase/core/operations/management/analytics_link_drop.cxx +6 -4
- data/ext/couchbase/core/operations/management/analytics_link_get_all.cxx +4 -3
- data/ext/couchbase/core/operations/management/analytics_link_replace.cxx +3 -3
- data/ext/couchbase/core/operations/management/analytics_link_utils.hxx +1 -1
- data/ext/couchbase/core/operations/management/bucket_create.cxx +37 -38
- data/ext/couchbase/core/operations/management/bucket_update.cxx +26 -28
- data/ext/couchbase/core/operations/management/change_password.cxx +2 -0
- data/ext/couchbase/core/operations/management/collection_drop.cxx +5 -1
- data/ext/couchbase/core/operations/management/error_utils.cxx +59 -5
- data/ext/couchbase/core/operations/management/error_utils.hxx +29 -1
- data/ext/couchbase/core/operations/management/eventing_deploy_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/eventing_drop_function.cxx +8 -3
- data/ext/couchbase/core/operations/management/eventing_get_all_functions.cxx +4 -2
- data/ext/couchbase/core/operations/management/eventing_get_function.cxx +5 -3
- data/ext/couchbase/core/operations/management/eventing_get_status.cxx +4 -2
- data/ext/couchbase/core/operations/management/eventing_pause_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/eventing_resume_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/eventing_undeploy_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/eventing_upsert_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/group_drop.cxx +3 -1
- data/ext/couchbase/core/operations/management/group_get.cxx +3 -1
- data/ext/couchbase/core/operations/management/group_upsert.cxx +11 -8
- data/ext/couchbase/core/operations/management/query_index_build.cxx +5 -2
- data/ext/couchbase/core/operations/management/query_index_create.cxx +9 -3
- data/ext/couchbase/core/operations/management/query_index_drop.cxx +11 -4
- data/ext/couchbase/core/operations/management/query_index_get_all.cxx +1 -1
- data/ext/couchbase/core/operations/management/query_index_get_all_deferred.cxx +1 -1
- data/ext/couchbase/core/operations/management/scope_drop.cxx +5 -1
- data/ext/couchbase/core/operations/management/scope_get_all.cxx +6 -1
- data/ext/couchbase/core/operations/management/search_get_stats.cxx +10 -0
- data/ext/couchbase/core/operations/management/search_index_analyze_document.cxx +3 -2
- data/ext/couchbase/core/operations/management/search_index_control_ingest.cxx +4 -3
- data/ext/couchbase/core/operations/management/search_index_control_plan_freeze.cxx +4 -3
- data/ext/couchbase/core/operations/management/search_index_control_query.cxx +4 -3
- data/ext/couchbase/core/operations/management/search_index_drop.cxx +2 -2
- data/ext/couchbase/core/operations/management/search_index_get.cxx +2 -2
- data/ext/couchbase/core/operations/management/search_index_get_documents_count.cxx +3 -2
- data/ext/couchbase/core/operations/management/search_index_get_stats.cxx +3 -1
- data/ext/couchbase/core/operations/management/search_index_upsert.cxx +2 -2
- data/ext/couchbase/core/operations/management/user_drop.cxx +3 -1
- data/ext/couchbase/core/operations/management/user_get.cxx +3 -1
- data/ext/couchbase/core/operations/management/user_upsert.cxx +12 -9
- data/ext/couchbase/core/operations/management/view_index_drop.cxx +1 -1
- data/ext/couchbase/core/operations/management/view_index_get.cxx +1 -1
- data/ext/couchbase/core/operations/management/view_index_upsert.cxx +1 -1
- data/ext/couchbase/core/operations/operation_traits.hxx +18 -0
- data/ext/couchbase/core/operations/query_response_parsing.cxx +211 -0
- data/ext/couchbase/core/operations/query_response_parsing.hxx +60 -0
- data/ext/couchbase/core/operations_fwd.hxx +3 -3
- data/ext/couchbase/core/origin.cxx +54 -26
- data/ext/couchbase/core/origin.hxx +12 -0
- data/ext/couchbase/core/orphan_reporter.cxx +2 -1
- data/ext/couchbase/core/platform/random.cc +7 -7
- data/ext/couchbase/core/protocol/client_request.cxx +2 -2
- data/ext/couchbase/core/protocol/client_request.hxx +38 -21
- data/ext/couchbase/core/protocol/client_response.cxx +11 -3
- data/ext/couchbase/core/protocol/client_response.hxx +27 -6
- data/ext/couchbase/core/protocol/cmd_append.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_cluster_map_change_notification.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_decrement.cxx +6 -6
- data/ext/couchbase/core/protocol/cmd_get.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_get_and_lock.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_get_and_touch.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_get_cluster_config.cxx +6 -4
- data/ext/couchbase/core/protocol/cmd_get_collection_id.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_get_error_map.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_get_meta.cxx +4 -4
- data/ext/couchbase/core/protocol/cmd_get_replica.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_hello.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_increment.cxx +6 -6
- data/ext/couchbase/core/protocol/cmd_insert.cxx +4 -4
- data/ext/couchbase/core/protocol/cmd_lookup_in.cxx +4 -3
- data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +4 -3
- data/ext/couchbase/core/protocol/cmd_mutate_in.cxx +10 -9
- data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +10 -10
- data/ext/couchbase/core/protocol/cmd_observe_seqno.cxx +7 -7
- data/ext/couchbase/core/protocol/cmd_prepend.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_remove.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_replace.cxx +4 -4
- data/ext/couchbase/core/protocol/cmd_touch.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_upsert.cxx +4 -4
- data/ext/couchbase/core/protocol/datatype.hxx +118 -1
- data/ext/couchbase/core/protocol/frame_info_utils.cxx +1 -1
- data/ext/couchbase/core/protocol/magic.hxx +4 -0
- data/ext/couchbase/core/protocol/magic_fmt.hxx +3 -0
- data/ext/couchbase/core/protocol/server_request.hxx +3 -1
- data/ext/couchbase/core/protocol/status.cxx +1 -1
- data/ext/couchbase/core/protostellar/analytics_converter.hxx +171 -0
- data/ext/couchbase/core/protostellar/bucket_admin_converter.hxx +301 -0
- data/ext/couchbase/core/protostellar/collection_admin_converter.hxx +92 -0
- data/ext/couchbase/core/protostellar/component.cxx +2445 -0
- data/ext/couchbase/core/protostellar/component.hxx +391 -0
- data/ext/couchbase/core/{columnar/bootstrap_notification_subscriber.hxx → protostellar/compression_settings.hxx} +11 -10
- data/ext/couchbase/core/protostellar/credentials.cxx +233 -0
- data/ext/couchbase/core/protostellar/credentials.hxx +88 -0
- data/ext/couchbase/core/protostellar/dispatcher.cxx +57 -0
- data/ext/couchbase/core/protostellar/dispatcher.hxx +482 -0
- data/ext/couchbase/core/protostellar/error_utils.cxx +293 -0
- data/ext/couchbase/core/protostellar/error_utils.hxx +97 -0
- data/ext/couchbase/core/protostellar/json_payload.hxx +49 -0
- data/ext/couchbase/core/protostellar/kv_converter.cxx +70 -0
- data/ext/couchbase/core/protostellar/kv_converter.hxx +600 -0
- data/ext/couchbase/core/protostellar/query_converter.hxx +265 -0
- data/ext/couchbase/core/protostellar/query_index_admin_converter.hxx +291 -0
- data/ext/couchbase/core/protostellar/query_proto.hxx +73 -0
- data/ext/couchbase/core/protostellar/search_converter.hxx +244 -0
- data/ext/couchbase/core/protostellar/search_index_admin_converter.hxx +156 -0
- data/ext/couchbase/core/protostellar/view_converter.hxx +158 -0
- data/ext/couchbase/core/query_stream.cxx +447 -0
- data/ext/couchbase/core/query_stream.hxx +141 -0
- data/ext/couchbase/core/query_stream_component.cxx +243 -0
- data/ext/couchbase/core/query_stream_component.hxx +68 -0
- data/ext/couchbase/core/range_scan_load_balancer.cxx +15 -8
- data/ext/couchbase/core/range_scan_orchestrator.cxx +15 -3
- data/ext/couchbase/core/response_handler.hxx +41 -4
- data/ext/couchbase/core/row_streamer.cxx +332 -46
- data/ext/couchbase/core/row_streamer.hxx +31 -1
- data/ext/couchbase/core/row_streamer_options.hxx +55 -0
- data/ext/couchbase/core/sasl/client.h +3 -0
- data/ext/couchbase/core/search_scoring.hxx +66 -0
- data/ext/couchbase/core/stream_error_details.hxx +69 -0
- data/ext/couchbase/core/timeout_defaults.hxx +0 -1
- data/ext/couchbase/core/topology/capabilities.hxx +6 -0
- data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
- data/ext/couchbase/core/topology/configuration.cxx +21 -6
- data/ext/couchbase/core/topology/configuration.hxx +29 -8
- data/ext/couchbase/core/topology/configuration_json.hxx +3 -0
- data/ext/couchbase/core/topology/error_map_json.hxx +3 -1
- data/ext/couchbase/core/tracing/threshold_logging_tracer.cxx +70 -4
- data/ext/couchbase/core/tracing/threshold_logging_tracer.hxx +10 -0
- data/ext/couchbase/core/transactions/active_transaction_record.cxx +0 -34
- data/ext/couchbase/core/transactions/attempt_context_impl.cxx +362 -241
- data/ext/couchbase/core/transactions/attempt_context_impl.hxx +24 -3
- data/ext/couchbase/core/transactions/error_list.hxx +2 -2
- data/ext/couchbase/core/transactions/get_multi_fetch.hxx +160 -0
- data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +326 -125
- data/ext/couchbase/core/transactions/get_multi_transaction_id.hxx +62 -0
- data/ext/couchbase/core/transactions/internal/atr_entry.hxx +22 -2
- data/ext/couchbase/core/transactions/internal/transaction_context.hxx +2 -2
- data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +23 -1
- data/ext/couchbase/core/transactions/internal/utils.hxx +50 -14
- data/ext/couchbase/core/transactions/staged_mutation.cxx +23 -18
- data/ext/couchbase/core/transactions/staged_mutation.hxx +15 -4
- data/ext/couchbase/core/transactions/transaction_context.cxx +55 -40
- data/ext/couchbase/core/transactions/transaction_get_result.hxx +16 -0
- data/ext/couchbase/core/transactions/transactions.cxx +3 -55
- data/ext/couchbase/core/transactions/transactions_cleanup.cxx +194 -66
- data/ext/couchbase/core/transactions/transactions_config.cxx +0 -11
- data/ext/couchbase/core/transactions/utils.cxx +10 -0
- data/ext/couchbase/core/transactions/waitable_op_list.hxx +15 -4
- data/ext/couchbase/core/transactions.hxx +9 -9
- data/ext/couchbase/core/utils/byteswap.hxx +166 -9
- data/ext/couchbase/core/utils/connection_string.cxx +50 -85
- data/ext/couchbase/core/utils/connection_string.hxx +15 -0
- data/ext/couchbase/core/{columnar/backoff_calculator.hxx → utils/deadline.hxx} +13 -13
- data/ext/couchbase/core/utils/duration_parser.cxx +1 -1
- data/ext/couchbase/core/utils/json_streaming_lexer.cxx +48 -5
- data/ext/couchbase/core/utils/json_streaming_lexer.hxx +4 -1
- data/ext/couchbase/core/utils/movable_function.hxx +301 -52
- data/ext/couchbase/core/utils/name_codec.hxx +154 -11
- data/ext/couchbase/core/{columnar/retry_info.hxx → utils/process_id.hxx} +18 -13
- data/ext/couchbase/core/utils/url_codec.cxx +12 -51
- data/ext/couchbase/core/websocket_codec.cxx +3 -29
- data/ext/couchbase/couchbase/analytics_options.hxx +21 -10
- data/ext/couchbase/couchbase/analytics_row.hxx +105 -0
- data/ext/couchbase/couchbase/analytics_stream_result.hxx +266 -0
- data/ext/couchbase/couchbase/boolean_query.hxx +3 -3
- data/ext/couchbase/couchbase/bucket.hxx +57 -0
- data/ext/couchbase/couchbase/cluster.hxx +216 -5
- data/ext/couchbase/{core/columnar/security_options.hxx → couchbase/cluster_state.hxx} +18 -14
- data/ext/couchbase/couchbase/codec/lenient_json_transcoder.hxx +69 -0
- data/ext/couchbase/couchbase/collection.hxx +234 -14
- data/ext/couchbase/couchbase/common_durability_options.hxx +18 -3
- data/ext/couchbase/couchbase/conjunction_query.hxx +1 -1
- data/ext/couchbase/couchbase/create_dataset_analytics_options.hxx +6 -1
- data/ext/couchbase/couchbase/date_range_query.hxx +2 -2
- data/ext/couchbase/couchbase/decrement_options.hxx +1 -1
- data/ext/couchbase/couchbase/disjunction_query.hxx +1 -1
- data/ext/couchbase/couchbase/error.hxx +32 -0
- data/ext/couchbase/couchbase/error_codes.hxx +32 -0
- data/ext/couchbase/couchbase/geo_bounding_box_query.hxx +1 -1
- data/ext/couchbase/couchbase/geo_distance_query.hxx +1 -1
- data/ext/couchbase/couchbase/geo_polygon_query.hxx +1 -1
- data/ext/couchbase/couchbase/get_replica_options.hxx +68 -0
- data/ext/couchbase/couchbase/get_replica_result.hxx +5 -3
- data/ext/couchbase/couchbase/get_replica_strategy.hxx +161 -0
- data/ext/couchbase/couchbase/get_result.hxx +1 -0
- data/ext/couchbase/couchbase/increment_options.hxx +1 -1
- data/ext/couchbase/couchbase/insert_options.hxx +1 -1
- data/ext/couchbase/couchbase/match_phrase_query.hxx +1 -1
- data/ext/couchbase/couchbase/match_query.hxx +1 -1
- data/ext/couchbase/couchbase/mutate_in_options.hxx +3 -3
- data/ext/couchbase/couchbase/network_options.hxx +1 -1
- data/ext/couchbase/couchbase/node_id.hxx +42 -14
- data/ext/couchbase/couchbase/node_id_for_options.hxx +7 -1
- data/ext/couchbase/couchbase/node_ids_options.hxx +8 -2
- data/ext/couchbase/couchbase/numeric_range_query.hxx +1 -1
- data/ext/couchbase/couchbase/phrase_query.hxx +1 -1
- data/ext/couchbase/couchbase/prefix_query.hxx +1 -1
- data/ext/couchbase/couchbase/query_options.hxx +23 -12
- data/ext/couchbase/couchbase/query_row.hxx +111 -0
- data/ext/couchbase/couchbase/query_stream_result.hxx +302 -0
- data/ext/couchbase/couchbase/query_string_query.hxx +3 -3
- data/ext/couchbase/couchbase/regexp_query.hxx +1 -1
- data/ext/couchbase/couchbase/replace_options.hxx +3 -3
- data/ext/couchbase/couchbase/result.hxx +14 -3
- data/ext/couchbase/couchbase/scan_result_item.hxx +1 -0
- data/ext/couchbase/couchbase/scope.hxx +85 -2
- data/ext/couchbase/couchbase/search_options.hxx +58 -8
- data/ext/couchbase/couchbase/search_scoring.hxx +94 -0
- data/ext/couchbase/couchbase/search_scoring_none.hxx +51 -0
- data/ext/couchbase/couchbase/search_scoring_reciprocal_rank_fusion.hxx +88 -0
- data/ext/couchbase/couchbase/search_scoring_relative_score_fusion.hxx +70 -0
- data/ext/couchbase/couchbase/term_range_query.hxx +1 -1
- data/ext/couchbase/couchbase/tracing/request_span.hxx +46 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +23 -21
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +24 -19
- data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +17 -3
- data/ext/couchbase/couchbase/transactions/transactions_config.hxx +4 -2
- data/ext/couchbase/couchbase/transactions.hxx +27 -18
- data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
- data/ext/couchbase/couchbase/vector_query.hxx +1 -1
- data/ext/couchbase/couchbase/wait_until_ready_options.hxx +111 -0
- data/ext/couchbase/couchbase/wildcard_query.hxx +1 -1
- data/ext/extconf.rb +15 -10
- data/ext/rcb_analytics.cxx +82 -102
- data/ext/rcb_backend.cxx +42 -36
- data/ext/rcb_backend.hxx +2 -0
- data/ext/rcb_buckets.cxx +47 -59
- data/ext/rcb_collections.cxx +42 -52
- data/ext/rcb_crud.cxx +345 -224
- data/ext/rcb_diagnostics.cxx +117 -27
- data/ext/rcb_exceptions.cxx +152 -99
- data/ext/rcb_exceptions.hxx +25 -0
- data/ext/rcb_extras.cxx +18 -19
- data/ext/rcb_hdr_histogram.cxx +19 -4
- data/ext/rcb_logger.cxx +202 -27
- data/ext/rcb_logger.hxx +12 -0
- data/ext/rcb_multi.cxx +40 -40
- data/ext/rcb_observability.cxx +7 -5
- data/ext/rcb_query.cxx +99 -142
- data/ext/rcb_range_scan.cxx +41 -35
- data/ext/rcb_search.cxx +98 -99
- data/ext/rcb_users.cxx +36 -56
- data/ext/rcb_utils.cxx +100 -37
- data/ext/rcb_utils.hxx +287 -33
- data/ext/rcb_version.cxx +1 -1
- data/ext/rcb_views.cxx +55 -51
- data/lib/active_support/cache/couchbase_store.rb +77 -12
- data/lib/couchbase/bucket.rb +20 -0
- data/lib/couchbase/cluster.rb +33 -14
- data/lib/couchbase/collection.rb +36 -1
- data/lib/couchbase/errors.rb +18 -0
- data/lib/couchbase/fork_hooks.rb +16 -7
- data/lib/couchbase/get_replica_strategy.rb +89 -0
- data/lib/couchbase/logger.rb +1 -1
- data/lib/couchbase/management/analytics_index_manager.rb +15 -15
- data/lib/couchbase/management/bucket_manager.rb +6 -6
- data/lib/couchbase/management/collection_manager.rb +6 -6
- data/lib/couchbase/management/query_index_manager.rb +7 -7
- data/lib/couchbase/management/search_index_manager.rb +13 -13
- data/lib/couchbase/management/user_manager.rb +10 -10
- data/lib/couchbase/management/view_index_manager.rb +5 -5
- data/lib/couchbase/options.rb +181 -44
- data/lib/couchbase/protostellar/bucket.rb +4 -0
- data/lib/couchbase/protostellar/cluster.rb +4 -0
- data/lib/couchbase/protostellar/collection.rb +4 -0
- data/lib/couchbase/protostellar/error_handling.rb +2 -1
- data/lib/couchbase/protostellar/request.rb +1 -0
- data/lib/couchbase/protostellar/request_generator/kv.rb +16 -2
- data/lib/couchbase/protostellar/request_generator/search.rb +17 -1
- data/lib/couchbase/protostellar/retry/orchestrator.rb +3 -2
- data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
- data/lib/couchbase/protostellar/retry/strategies.rb +1 -0
- data/lib/couchbase/search_options.rb +95 -6
- data/lib/couchbase/version.rb +1 -1
- metadata +92 -33
- data/ext/0001-fix-build-for-mingw-w64-ucrt-x86_64-toolchain.patch +0 -40
- data/ext/couchbase/core/columnar/agent.hxx +0 -71
- data/ext/couchbase/core/columnar/agent_config.hxx +0 -59
- data/ext/couchbase/core/columnar/background_bootstrap_listener.hxx +0 -42
- data/ext/couchbase/core/columnar/backoff_calculator.cxx +0 -62
- data/ext/couchbase/core/columnar/columnar_agent.cxx +0 -147
- data/ext/couchbase/core/columnar/columnar_agent_config.cxx +0 -48
- data/ext/couchbase/core/columnar/columnar_error.cxx +0 -51
- data/ext/couchbase/core/columnar/database_management_options.hxx +0 -56
- data/ext/couchbase/core/columnar/error.hxx +0 -51
- data/ext/couchbase/core/columnar/error_codes.cxx +0 -107
- data/ext/couchbase/core/columnar/error_codes.hxx +0 -75
- data/ext/couchbase/core/columnar/management_component.cxx +0 -300
- data/ext/couchbase/core/columnar/management_component.hxx +0 -56
- data/ext/couchbase/core/columnar/query_component.cxx +0 -523
- data/ext/couchbase/core/columnar/query_component.hxx +0 -57
- data/ext/couchbase/core/columnar/query_options.hxx +0 -59
- data/ext/couchbase/core/columnar/query_result.cxx +0 -130
- data/ext/couchbase/core/columnar/query_result.hxx +0 -78
- data/ext/couchbase/core/io/config_tracker.cxx +0 -687
- data/ext/couchbase/core/io/config_tracker.hxx +0 -123
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
#include "http_session.hxx"
|
|
19
19
|
|
|
20
20
|
#include "core/logger/logger.hxx"
|
|
21
|
+
#include "core/logger/redaction.hxx"
|
|
21
22
|
#include "core/meta/version.hxx"
|
|
22
23
|
#include "core/platform/base64.h"
|
|
23
24
|
#include "core/platform/uuid.h"
|
|
@@ -25,12 +26,21 @@
|
|
|
25
26
|
|
|
26
27
|
#include <couchbase/error_codes.hxx>
|
|
27
28
|
|
|
29
|
+
#include <asio/ssl/error.hpp>
|
|
30
|
+
#include <gsl/util>
|
|
28
31
|
#include <spdlog/fmt/bin_to_hex.h>
|
|
29
32
|
|
|
33
|
+
#include <exception>
|
|
30
34
|
#include <utility>
|
|
31
35
|
|
|
32
36
|
namespace couchbase::core::io
|
|
33
37
|
{
|
|
38
|
+
namespace
|
|
39
|
+
{
|
|
40
|
+
// Between connection attempts, and before a resend after a refused client certificate.
|
|
41
|
+
constexpr std::chrono::milliseconds reconnect_backoff{ 500 };
|
|
42
|
+
} // namespace
|
|
43
|
+
|
|
34
44
|
http_session_info::http_session_info(const std::string& client_id, const std::string& session_id)
|
|
35
45
|
: log_prefix_(fmt::format("[{}/{}]", client_id, session_id))
|
|
36
46
|
{
|
|
@@ -61,12 +71,11 @@ http_session_info::http_session_info(const std::string& client_id,
|
|
|
61
71
|
fmt::format("{}:{}", remote_endpoint_address_, remote_endpoint_.port());
|
|
62
72
|
}
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
remote_endpoint_.port());
|
|
74
|
+
const auto endpoints = fmt::format("{}:{}:{}",
|
|
75
|
+
local_endpoint_.port(),
|
|
76
|
+
remote_endpoint_.address().to_string(),
|
|
77
|
+
remote_endpoint_.port());
|
|
78
|
+
log_prefix_ = fmt::format("[{}/{}] <{}>", client_id, session_id, logger::system_data(endpoints));
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
auto
|
|
@@ -106,11 +115,13 @@ http_session::http_session(couchbase::core::service_type type,
|
|
|
106
115
|
origin& origin,
|
|
107
116
|
std::string hostname,
|
|
108
117
|
std::string service,
|
|
109
|
-
couchbase::core::http_context http_ctx
|
|
118
|
+
couchbase::core::http_context http_ctx,
|
|
119
|
+
std::uint64_t pool_generation)
|
|
110
120
|
: type_(type)
|
|
111
121
|
, client_id_(std::move(client_id))
|
|
112
122
|
, node_uuid_(std::move(node_uuid))
|
|
113
123
|
, id_(uuid::to_string(uuid::random()))
|
|
124
|
+
, pool_generation_(pool_generation)
|
|
114
125
|
, ctx_(ctx)
|
|
115
126
|
, resolver_(ctx_)
|
|
116
127
|
, stream_(std::make_unique<plain_stream_impl>(ctx_))
|
|
@@ -134,17 +145,19 @@ http_session::http_session(couchbase::core::service_type type,
|
|
|
134
145
|
origin& origin,
|
|
135
146
|
std::string hostname,
|
|
136
147
|
std::string service,
|
|
137
|
-
couchbase::core::http_context http_ctx
|
|
148
|
+
couchbase::core::http_context http_ctx,
|
|
149
|
+
std::uint64_t pool_generation)
|
|
138
150
|
: type_(type)
|
|
139
151
|
, client_id_(std::move(client_id))
|
|
140
152
|
, node_uuid_(std::move(node_uuid))
|
|
141
153
|
, id_(uuid::to_string(uuid::random()))
|
|
154
|
+
, pool_generation_(pool_generation)
|
|
142
155
|
, ctx_(ctx)
|
|
143
156
|
, resolver_(ctx_)
|
|
144
157
|
, stream_(std::make_unique<tls_stream_impl>(ctx_, tls))
|
|
145
|
-
, connect_deadline_timer_(
|
|
146
|
-
, idle_timer_(
|
|
147
|
-
, retry_backoff_(
|
|
158
|
+
, connect_deadline_timer_(stream_->get_executor())
|
|
159
|
+
, idle_timer_(stream_->get_executor())
|
|
160
|
+
, retry_backoff_(stream_->get_executor())
|
|
148
161
|
, origin_(origin)
|
|
149
162
|
, hostname_(std::move(hostname))
|
|
150
163
|
, service_(std::move(service))
|
|
@@ -185,6 +198,21 @@ http_session::local_address() -> std::string
|
|
|
185
198
|
return info_.local_address();
|
|
186
199
|
}
|
|
187
200
|
|
|
201
|
+
auto
|
|
202
|
+
http_session::dispatched_endpoints() -> std::pair<std::string, std::string>
|
|
203
|
+
{
|
|
204
|
+
const std::scoped_lock lock(info_mutex_);
|
|
205
|
+
return { dispatched_from_, dispatched_to_ };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
void
|
|
209
|
+
http_session::record_dispatch()
|
|
210
|
+
{
|
|
211
|
+
const std::scoped_lock lock(info_mutex_);
|
|
212
|
+
dispatched_from_ = info_.local_address();
|
|
213
|
+
dispatched_to_ = info_.remote_address();
|
|
214
|
+
}
|
|
215
|
+
|
|
188
216
|
auto
|
|
189
217
|
http_session::remote_endpoint() -> const asio::ip::tcp::endpoint&
|
|
190
218
|
{
|
|
@@ -195,15 +223,16 @@ http_session::remote_endpoint() -> const asio::ip::tcp::endpoint&
|
|
|
195
223
|
auto
|
|
196
224
|
http_session::diag_info() -> diag::endpoint_diag_info
|
|
197
225
|
{
|
|
226
|
+
const auto last_active = last_active_.load();
|
|
198
227
|
return { type_,
|
|
199
228
|
id_,
|
|
200
|
-
|
|
229
|
+
last_active.time_since_epoch().count() == 0
|
|
201
230
|
? std::nullopt
|
|
202
231
|
: std::make_optional(std::chrono::duration_cast<std::chrono::microseconds>(
|
|
203
|
-
std::chrono::steady_clock::now() -
|
|
232
|
+
std::chrono::steady_clock::now() - last_active)),
|
|
204
233
|
remote_address(),
|
|
205
234
|
local_address(),
|
|
206
|
-
state_ };
|
|
235
|
+
state_.load() };
|
|
207
236
|
}
|
|
208
237
|
|
|
209
238
|
auto
|
|
@@ -219,6 +248,12 @@ http_session::id() const -> const std::string&
|
|
|
219
248
|
return id_;
|
|
220
249
|
}
|
|
221
250
|
|
|
251
|
+
auto
|
|
252
|
+
http_session::pool_generation() const -> std::uint64_t
|
|
253
|
+
{
|
|
254
|
+
return pool_generation_;
|
|
255
|
+
}
|
|
256
|
+
|
|
222
257
|
auto
|
|
223
258
|
http_session::node_uuid() const -> const std::string&
|
|
224
259
|
{
|
|
@@ -276,26 +311,36 @@ void
|
|
|
276
311
|
http_session::initiate_connect()
|
|
277
312
|
{
|
|
278
313
|
if (stopped_) {
|
|
279
|
-
|
|
314
|
+
// stop() has begun, so nothing connects. Its teardown may already have taken an earlier
|
|
315
|
+
// callback, so the one just stored, which holds this session, is run here. Whichever of this
|
|
316
|
+
// call and the teardown moves it out under connect_callback_mutex_ runs it.
|
|
317
|
+
return invoke_connect_callback();
|
|
280
318
|
}
|
|
281
319
|
if (state_ != diag::endpoint_state::connecting) {
|
|
282
|
-
CB_LOG_DEBUG(
|
|
283
|
-
|
|
320
|
+
CB_LOG_DEBUG("{} {} attempt to establish HTTP connection",
|
|
321
|
+
info_.log_prefix(),
|
|
322
|
+
logger::system_data(fmt::format("{}:{}", hostname_, service_)));
|
|
284
323
|
state_ = diag::endpoint_state::connecting;
|
|
285
|
-
async_resolve(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
324
|
+
async_resolve(
|
|
325
|
+
http_ctx_.options.use_ip_protocol,
|
|
326
|
+
resolver_,
|
|
327
|
+
hostname_,
|
|
328
|
+
service_,
|
|
329
|
+
asio::bind_executor(get_executor(), [capture0 = shared_from_this()](auto&& PH1, auto&& PH2) {
|
|
330
|
+
capture0->on_resolve(std::forward<decltype(PH1)>(PH1), std::forward<decltype(PH2)>(PH2));
|
|
331
|
+
}));
|
|
293
332
|
} else {
|
|
294
333
|
// reset state in case the session is being reused
|
|
295
334
|
state_ = diag::endpoint_state::disconnected;
|
|
296
|
-
auto backoff =
|
|
335
|
+
auto backoff = reconnect_backoff;
|
|
297
336
|
CB_LOG_DEBUG(
|
|
298
337
|
"{} waiting for {}ms before trying to connect", info_.log_prefix(), backoff.count());
|
|
338
|
+
const std::scoped_lock lock(retry_backoff_mutex_);
|
|
339
|
+
// stop() can set stopped_ on another thread after the test at the top of this function.
|
|
340
|
+
// cppcheck-suppress knownConditionTrueFalse
|
|
341
|
+
if (stopped_) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
299
344
|
retry_backoff_.expires_after(backoff);
|
|
300
345
|
retry_backoff_.async_wait([self = shared_from_this()](std::error_code ec) mutable {
|
|
301
346
|
if (ec == asio::error::operation_aborted || self->stopped_) {
|
|
@@ -332,6 +377,132 @@ http_session::cancel_current_response(std::error_code ec)
|
|
|
332
377
|
}
|
|
333
378
|
}
|
|
334
379
|
|
|
380
|
+
void
|
|
381
|
+
http_session::remember_request(bool uses_certificate)
|
|
382
|
+
{
|
|
383
|
+
// Only a session with certificate credentials resends, so no other session keeps a copy.
|
|
384
|
+
const std::scoped_lock lock(output_buffer_mutex_);
|
|
385
|
+
if (fresh_connection_ && uses_certificate) {
|
|
386
|
+
unanswered_request_ = output_buffer_;
|
|
387
|
+
} else {
|
|
388
|
+
unanswered_request_.clear();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
namespace
|
|
393
|
+
{
|
|
394
|
+
// The alerts a server sends when it refuses the client certificate. OpenSSL and BoringSSL report a
|
|
395
|
+
// received alert as the reason SSL_AD_REASON_OFFSET plus the alert's code.
|
|
396
|
+
auto
|
|
397
|
+
is_refused_certificate(std::error_code ec) -> bool
|
|
398
|
+
{
|
|
399
|
+
if (ec.category() != asio::error::get_ssl_category()) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
// OpenSSL 1.1 defines ERR_GET_REASON() as a macro with a C-style cast; OpenSSL 3.0 and later,
|
|
403
|
+
// and BoringSSL, define it as an inline function.
|
|
404
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
405
|
+
#pragma GCC diagnostic push
|
|
406
|
+
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
|
407
|
+
#endif
|
|
408
|
+
const int alert = ERR_GET_REASON(static_cast<std::uint32_t>(ec.value())) - SSL_AD_REASON_OFFSET;
|
|
409
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
410
|
+
#pragma GCC diagnostic pop
|
|
411
|
+
#endif
|
|
412
|
+
switch (alert) {
|
|
413
|
+
case SSL_AD_BAD_CERTIFICATE:
|
|
414
|
+
case SSL_AD_UNSUPPORTED_CERTIFICATE:
|
|
415
|
+
case SSL_AD_CERTIFICATE_REVOKED:
|
|
416
|
+
case SSL_AD_CERTIFICATE_EXPIRED:
|
|
417
|
+
case SSL_AD_CERTIFICATE_UNKNOWN:
|
|
418
|
+
case SSL_AD_UNKNOWN_CA:
|
|
419
|
+
case SSL_AD_ACCESS_DENIED:
|
|
420
|
+
#if defined(SSL_AD_CERTIFICATE_REQUIRED)
|
|
421
|
+
case SSL_AD_CERTIFICATE_REQUIRED:
|
|
422
|
+
#endif
|
|
423
|
+
return true;
|
|
424
|
+
default:
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} // namespace
|
|
429
|
+
|
|
430
|
+
// Under TLS 1.3 the server checks the client certificate after the client's handshake has
|
|
431
|
+
// completed. A refusal therefore arrives as an alert when the response to the first request on a
|
|
432
|
+
// new connection is read. The server has processed nothing on that connection, so the request
|
|
433
|
+
// is written again on a new one, with the current TLS context. This repeats until the request is
|
|
434
|
+
// answered or its command's deadline stops this session. Only a session with certificate
|
|
435
|
+
// credentials resends: other credentials present no client certificate, and a rotation cannot
|
|
436
|
+
// change that.
|
|
437
|
+
auto
|
|
438
|
+
http_session::resend_after_rejected_handshake(std::error_code ec) -> bool
|
|
439
|
+
{
|
|
440
|
+
if (stopped_ || !fresh_connection_ || !is_refused_certificate(ec) ||
|
|
441
|
+
!credentials().uses_certificate()) {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
{
|
|
445
|
+
const std::scoped_lock lock(output_buffer_mutex_);
|
|
446
|
+
if (unanswered_request_.empty()) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (!refusal_logged_.exchange(true)) {
|
|
451
|
+
CB_LOG_WARNING("{} client certificate refused by the server ({}), reconnecting",
|
|
452
|
+
info_.log_prefix(),
|
|
453
|
+
ec.message());
|
|
454
|
+
} else {
|
|
455
|
+
CB_LOG_DEBUG("{} client certificate refused by the server ({}), reconnecting",
|
|
456
|
+
info_.log_prefix(),
|
|
457
|
+
ec.message());
|
|
458
|
+
}
|
|
459
|
+
connected_ = false;
|
|
460
|
+
reading_ = false;
|
|
461
|
+
{
|
|
462
|
+
const std::scoped_lock lock(writing_buffer_mutex_);
|
|
463
|
+
writing_buffer_.clear();
|
|
464
|
+
}
|
|
465
|
+
state_ = diag::endpoint_state::disconnected;
|
|
466
|
+
stream_->close([self = shared_from_this()](std::error_code) {
|
|
467
|
+
const std::scoped_lock lock(self->retry_backoff_mutex_);
|
|
468
|
+
if (self->stopped_) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
self->retry_backoff_.expires_after(reconnect_backoff);
|
|
472
|
+
self->retry_backoff_.async_wait([self](std::error_code wait_ec) {
|
|
473
|
+
if (wait_ec == asio::error::operation_aborted || self->stopped_) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
self->reconnect_to_resend();
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// initiate_connect() also runs the connect callback after a failed attempt and its backoff. The
|
|
483
|
+
// callback then connects again.
|
|
484
|
+
void
|
|
485
|
+
http_session::reconnect_to_resend()
|
|
486
|
+
{
|
|
487
|
+
{
|
|
488
|
+
const std::scoped_lock lock(connect_callback_mutex_);
|
|
489
|
+
connect_callback_ = [self = shared_from_this()]() {
|
|
490
|
+
if (self->stopped_) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
if (!self->connected_) {
|
|
494
|
+
return self->reconnect_to_resend();
|
|
495
|
+
}
|
|
496
|
+
{
|
|
497
|
+
const std::scoped_lock buffer_lock(self->output_buffer_mutex_);
|
|
498
|
+
self->output_buffer_ = self->unanswered_request_;
|
|
499
|
+
}
|
|
500
|
+
self->flush();
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
initiate_connect();
|
|
504
|
+
}
|
|
505
|
+
|
|
335
506
|
void
|
|
336
507
|
http_session::invoke_connect_callback()
|
|
337
508
|
{
|
|
@@ -348,24 +519,56 @@ http_session::invoke_connect_callback()
|
|
|
348
519
|
void
|
|
349
520
|
http_session::stop()
|
|
350
521
|
{
|
|
351
|
-
|
|
522
|
+
// One caller wins. A stop() from a response handler the teardown below runs returns here; a
|
|
523
|
+
// second teardown would lock current_response_mutex_ again inside cancel_current_response().
|
|
524
|
+
// An exchange rather than a load and a store: two threads stopping at once would both pass a
|
|
525
|
+
// load and run the teardown concurrently.
|
|
526
|
+
if (stopped_.exchange(true)) {
|
|
352
527
|
return;
|
|
353
528
|
}
|
|
354
|
-
|
|
529
|
+
// Every step runs although a callback throws, because a later stop() returns above and on_stop
|
|
530
|
+
// is what removes the session from the manager's pools. The first exception is rethrown once the
|
|
531
|
+
// teardown is complete; later ones are dropped. Not a scope guard: its destructor is noexcept.
|
|
532
|
+
std::exception_ptr failure;
|
|
533
|
+
const auto run_step = [&failure](auto&& step) {
|
|
534
|
+
try {
|
|
535
|
+
step();
|
|
536
|
+
} catch (...) {
|
|
537
|
+
if (!failure) {
|
|
538
|
+
failure = std::current_exception();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
};
|
|
355
542
|
state_ = diag::endpoint_state::disconnecting;
|
|
356
543
|
stream_->close([](std::error_code) {
|
|
357
544
|
});
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
545
|
+
run_step([this]() {
|
|
546
|
+
invoke_connect_callback();
|
|
547
|
+
});
|
|
548
|
+
{
|
|
549
|
+
const std::scoped_lock lock(connect_deadline_timer_mutex_);
|
|
550
|
+
connect_deadline_timer_.cancel();
|
|
551
|
+
}
|
|
552
|
+
{
|
|
553
|
+
const std::scoped_lock lock(idle_timer_mutex_);
|
|
554
|
+
idle_timer_.cancel();
|
|
555
|
+
}
|
|
556
|
+
{
|
|
557
|
+
const std::scoped_lock lock(retry_backoff_mutex_);
|
|
558
|
+
retry_backoff_.cancel();
|
|
559
|
+
}
|
|
362
560
|
|
|
363
|
-
|
|
561
|
+
run_step([this]() {
|
|
562
|
+
cancel_current_response(errc::common::request_canceled);
|
|
563
|
+
});
|
|
364
564
|
|
|
365
565
|
if (auto handler = std::move(on_stop_handler_); handler) {
|
|
366
|
-
handler
|
|
566
|
+
run_step(handler);
|
|
367
567
|
}
|
|
368
568
|
state_ = diag::endpoint_state::disconnected;
|
|
569
|
+
if (failure) {
|
|
570
|
+
std::rethrow_exception(failure);
|
|
571
|
+
}
|
|
369
572
|
}
|
|
370
573
|
|
|
371
574
|
auto
|
|
@@ -380,6 +583,18 @@ http_session::is_stopped() const -> bool
|
|
|
380
583
|
return stopped_;
|
|
381
584
|
}
|
|
382
585
|
|
|
586
|
+
void
|
|
587
|
+
http_session::mark_stopping()
|
|
588
|
+
{
|
|
589
|
+
stopping_ = true;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
auto
|
|
593
|
+
http_session::is_stopping() const -> bool
|
|
594
|
+
{
|
|
595
|
+
return stopping_;
|
|
596
|
+
}
|
|
597
|
+
|
|
383
598
|
void
|
|
384
599
|
http_session::write(const std::vector<std::uint8_t>& buf)
|
|
385
600
|
{
|
|
@@ -409,32 +624,35 @@ http_session::flush()
|
|
|
409
624
|
if (stopped_) {
|
|
410
625
|
return;
|
|
411
626
|
}
|
|
412
|
-
asio::post(
|
|
627
|
+
asio::post(get_executor(), [self = shared_from_this()]() {
|
|
413
628
|
self->do_write();
|
|
414
|
-
})
|
|
629
|
+
});
|
|
415
630
|
}
|
|
416
631
|
|
|
417
632
|
void
|
|
418
633
|
http_session::write_and_stream(
|
|
419
634
|
io::http_request& request,
|
|
420
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
421
|
-
utils::movable_function<void(couchbase::core::error_union, io::http_streaming_response)>
|
|
422
|
-
resp_handler,
|
|
423
|
-
#else
|
|
424
635
|
utils::movable_function<void(std::error_code, io::http_streaming_response)> resp_handler,
|
|
425
|
-
#endif
|
|
426
636
|
utils::movable_function<void()> stream_end_handler)
|
|
427
637
|
{
|
|
428
|
-
if (stopped_) {
|
|
429
|
-
resp_handler(errc::common::request_canceled, {});
|
|
430
|
-
stream_end_handler();
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
638
|
{
|
|
434
639
|
streaming_response_context ctx{ std::move(resp_handler), std::move(stream_end_handler) };
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
640
|
+
bool installed = false;
|
|
641
|
+
{
|
|
642
|
+
// As in write_and_subscribe: the stopped_ test shares the section that installs the
|
|
643
|
+
// context, so a stop is either seen here or its cancel_current_response() ends the stream.
|
|
644
|
+
const std::scoped_lock lock(current_response_mutex_);
|
|
645
|
+
if (!stopped_) {
|
|
646
|
+
std::swap(current_streaming_response_, ctx);
|
|
647
|
+
streaming_response_ = true;
|
|
648
|
+
installed = true;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (!installed) {
|
|
652
|
+
ctx.resp_handler(errc::common::request_canceled, {});
|
|
653
|
+
ctx.stream_end_handler();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
438
656
|
}
|
|
439
657
|
if (request.headers["connection"] == "keep-alive") {
|
|
440
658
|
keep_alive_ = true;
|
|
@@ -459,54 +677,120 @@ http_session::write_and_stream(
|
|
|
459
677
|
}
|
|
460
678
|
write("\r\n");
|
|
461
679
|
write(request.body);
|
|
680
|
+
remember_request(creds.uses_certificate());
|
|
462
681
|
flush();
|
|
463
682
|
}
|
|
464
683
|
|
|
465
684
|
void
|
|
466
685
|
http_session::set_idle(std::chrono::milliseconds timeout)
|
|
467
686
|
{
|
|
687
|
+
const std::scoped_lock lock(idle_timer_mutex_);
|
|
688
|
+
// check_in can call this off the strand after its is_stopped() test, while a teardown runs. The
|
|
689
|
+
// teardown cancels the idle timer under this mutex after stopped_ is set, so either that cancel
|
|
690
|
+
// finds the wait armed here, or this test sees the stop. Armed after the cancel, the wait would
|
|
691
|
+
// hold the stopped session until the idle timeout.
|
|
692
|
+
if (stopped_) {
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
468
695
|
idle_timer_.expires_after(timeout);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
696
|
+
// Bound to the session strand, so the idle-timeout stop() is serialised with the completion of
|
|
697
|
+
// the liveness read armed below.
|
|
698
|
+
idle_timer_.async_wait(
|
|
699
|
+
asio::bind_executor(get_executor(), [self = shared_from_this()](std::error_code ec) {
|
|
700
|
+
if (ec == asio::error::operation_aborted) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
CB_LOG_DEBUG("{} idle timeout expired, stopping session: \"{}\"",
|
|
704
|
+
self->info_.log_prefix(),
|
|
705
|
+
logger::system_data(fmt::format("{}:{}", self->hostname_, self->service_)));
|
|
706
|
+
self->stop();
|
|
707
|
+
}));
|
|
708
|
+
// Keep a read armed while the connection is idle so that a peer-initiated
|
|
709
|
+
// close (FIN/RST) is noticed promptly and the session removed from the pool,
|
|
710
|
+
// instead of being handed to the next request as a dead socket that then
|
|
711
|
+
// stalls until the service timeout. Other SDKs get this from their HTTP
|
|
712
|
+
// stacks (Netty channelInactive, Go net/http and Rust hyper liveness checks);
|
|
713
|
+
// here the read completion (EOF/error, or unexpected data) tears the session
|
|
714
|
+
// down -- see do_read(). Posted onto the session executor so it never races
|
|
715
|
+
// with an in-flight read.
|
|
716
|
+
idle_ = true;
|
|
717
|
+
asio::post(get_executor(), [self = shared_from_this()]() {
|
|
718
|
+
self->do_read();
|
|
478
719
|
});
|
|
479
720
|
}
|
|
480
721
|
|
|
481
722
|
auto
|
|
482
723
|
http_session::reset_idle() -> bool
|
|
483
724
|
{
|
|
725
|
+
// The session is leaving the pool to service a request, so the read armed by
|
|
726
|
+
// set_idle() is no longer an idle liveness probe -- it becomes the read that
|
|
727
|
+
// receives the response. Clear the flag first so do_read() treats any
|
|
728
|
+
// subsequent completion as normal request/response traffic, not a stale
|
|
729
|
+
// connection.
|
|
730
|
+
idle_ = false;
|
|
484
731
|
// Return true if cancel() is successful. Since the idle_timer_ has a single pending
|
|
485
732
|
// wait per session, we know the timer has already expired if cancel() returns 0.
|
|
486
|
-
|
|
733
|
+
// stop() sets stopped_ before it cancels the idle timer under this mutex, so a stop whose cancel
|
|
734
|
+
// has not run yet is seen here, and one whose cancel has run leaves nothing to cancel.
|
|
735
|
+
const bool reset = [this]() {
|
|
736
|
+
const std::scoped_lock lock(idle_timer_mutex_);
|
|
737
|
+
return idle_timer_.cancel() != 0 && !stopped_;
|
|
738
|
+
}();
|
|
739
|
+
if (!reset) {
|
|
740
|
+
// The idle timer already fired, and its (strand-bound) handler is about to stop() this
|
|
741
|
+
// session, or a stop() has begun: the session is being torn down rather than checked out.
|
|
742
|
+
// Restore idle_ so the still-armed liveness read stays on the idle path (tearing the
|
|
743
|
+
// connection down) instead of being fed to the response parser as if it were request traffic.
|
|
744
|
+
idle_ = true;
|
|
745
|
+
}
|
|
746
|
+
return reset;
|
|
487
747
|
}
|
|
488
748
|
|
|
489
749
|
void
|
|
490
750
|
http_session::read_some(
|
|
491
751
|
utils::movable_function<void(std::string, bool, std::error_code)>&& callback)
|
|
492
752
|
{
|
|
493
|
-
|
|
494
|
-
|
|
753
|
+
// Initiated on the strand: callers pull from arbitrary io threads, and the body's close() closes
|
|
754
|
+
// stream_ through a stop() it posts to the strand.
|
|
755
|
+
asio::dispatch(get_executor(),
|
|
756
|
+
[self = shared_from_this(), callback = std::move(callback)]() mutable {
|
|
757
|
+
self->do_read_some(std::move(callback));
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
void
|
|
762
|
+
http_session::do_read_some(read_callback&& callback)
|
|
763
|
+
{
|
|
764
|
+
if (read_some_in_flight_) {
|
|
765
|
+
queued_reads_.push_back(std::move(callback));
|
|
495
766
|
return;
|
|
496
767
|
}
|
|
497
|
-
|
|
768
|
+
read_some_in_flight_ = true;
|
|
769
|
+
if (stopped_ || !stream_->is_open()) {
|
|
770
|
+
// Completed by the drain in finish_read_some(), never inline: this runs from that function's
|
|
771
|
+
// first branch, which a noexcept scope guard reaches, so a callback throwing here would
|
|
772
|
+
// terminate. At the front, since it was issued before anything still queued.
|
|
773
|
+
queued_reads_.push_front(std::move(callback));
|
|
774
|
+
return finish_read_some(errc::common::request_canceled, false);
|
|
775
|
+
}
|
|
498
776
|
return stream_->async_read_some(
|
|
499
777
|
asio::buffer(input_buffer_),
|
|
500
|
-
[self = shared_from_this(),
|
|
501
|
-
|
|
778
|
+
[self = shared_from_this(),
|
|
779
|
+
callback = std::move(callback)](std::error_code ec, std::size_t bytes_transferred) mutable {
|
|
780
|
+
// Declared first, so it runs last: after the callback and the stream-end handler.
|
|
781
|
+
std::error_code queued_ec{};
|
|
782
|
+
bool response_ended = false;
|
|
783
|
+
const auto release = gsl::finally([&self, &queued_ec, &response_ended]() {
|
|
784
|
+
self->finish_read_some(queued_ec, response_ended);
|
|
785
|
+
});
|
|
502
786
|
if (ec == asio::error::operation_aborted || self->stopped_) {
|
|
503
787
|
CB_LOG_PROTOCOL("[HTTP, IN] type={}, host=\"{}\", rc={}, bytes_received={}",
|
|
504
788
|
self->type_,
|
|
505
789
|
self->info_.remote_address(),
|
|
506
790
|
ec ? ec.message() : "ok",
|
|
507
791
|
bytes_transferred);
|
|
508
|
-
|
|
509
|
-
callback({}, {},
|
|
792
|
+
queued_ec = errc::common::request_canceled;
|
|
793
|
+
callback({}, {}, queued_ec);
|
|
510
794
|
return;
|
|
511
795
|
}
|
|
512
796
|
CB_LOG_PROTOCOL("[HTTP, IN] type={}, host=\"{}\", rc={}, bytes_received={}{:a}",
|
|
@@ -522,46 +806,146 @@ http_session::read_some(
|
|
|
522
806
|
if (ec) {
|
|
523
807
|
CB_LOG_ERROR(
|
|
524
808
|
"{} IO error while reading from the socket: {}", self->info_.log_prefix(), ec.message());
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
809
|
+
// Stopped before the callback, as on a parse failure: a throwing callback must not leave
|
|
810
|
+
// the failed connection running and listed as busy.
|
|
811
|
+
self->stop();
|
|
812
|
+
queued_ec = ec;
|
|
813
|
+
return callback({}, {}, ec);
|
|
528
814
|
}
|
|
529
815
|
http_streaming_parser::feeding_result res{};
|
|
816
|
+
std::string data;
|
|
817
|
+
streaming_response_context ctx{};
|
|
818
|
+
bool stopped = false;
|
|
530
819
|
{
|
|
820
|
+
// One section, as in do_read(): stop() sets stopped_ before its cancel takes this mutex,
|
|
821
|
+
// so the section either sees the stop or takes the parts of the response before the cancel.
|
|
531
822
|
const std::scoped_lock lock(self->current_response_mutex_);
|
|
532
|
-
|
|
533
|
-
|
|
823
|
+
stopped = self->stopped_;
|
|
824
|
+
if (!stopped) {
|
|
825
|
+
res = self->current_streaming_response_.parser.feed(
|
|
826
|
+
reinterpret_cast<const char*>(self->input_buffer_.data()), bytes_transferred);
|
|
827
|
+
if (!res.failure) {
|
|
828
|
+
std::swap(data, self->current_streaming_response_.parser.body_chunk);
|
|
829
|
+
if (res.complete) {
|
|
830
|
+
std::swap(self->current_streaming_response_, ctx);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
if (stopped) {
|
|
836
|
+
queued_ec = errc::common::request_canceled;
|
|
837
|
+
return callback({}, {}, queued_ec);
|
|
534
838
|
}
|
|
535
839
|
if (res.failure) {
|
|
536
840
|
self->stop();
|
|
537
|
-
|
|
538
|
-
return callback({}, {},
|
|
841
|
+
queued_ec = errc::common::parsing_failure;
|
|
842
|
+
return callback({}, {}, queued_ec);
|
|
539
843
|
}
|
|
540
|
-
|
|
541
|
-
std::string data;
|
|
542
|
-
{
|
|
543
|
-
const std::scoped_lock lock(self->current_response_mutex_);
|
|
544
|
-
std::swap(data, self->current_streaming_response_.parser.body_chunk);
|
|
545
|
-
}
|
|
546
|
-
|
|
844
|
+
response_ended = res.complete;
|
|
547
845
|
if (res.complete) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
std::swap(self->current_streaming_response_, ctx);
|
|
552
|
-
}
|
|
553
|
-
if (ctx.stream_end_handler) {
|
|
554
|
-
ctx.stream_end_handler();
|
|
555
|
-
}
|
|
556
|
-
if (ctx.resp->must_close_connection()) {
|
|
846
|
+
// Cleared with the end of the response: check_in then posts the liveness read's do_read().
|
|
847
|
+
self->body_pulled_ = false;
|
|
848
|
+
if (ctx.must_close_connection) {
|
|
557
849
|
self->keep_alive_ = false;
|
|
558
850
|
}
|
|
559
851
|
}
|
|
560
|
-
|
|
852
|
+
// The stream-end handler checks this connection back into the keep-alive pool, and must not
|
|
853
|
+
// run until the body has observed the end of its response. Until it does,
|
|
854
|
+
// http_streaming_response_body_impl still holds this session with reading_complete_ false,
|
|
855
|
+
// and treats it as a response abandoned mid-body: a deadline expiry reaching close_impl in
|
|
856
|
+
// that state stops the session. Checking in first publishes the connection while that is
|
|
857
|
+
// still true, so the stop lands on whichever request took it out of the pool next.
|
|
858
|
+
//
|
|
859
|
+
// Check-in therefore waits for the whole body callback chain, which ends in consumer code: a
|
|
860
|
+
// request issued from inside that callback does not find this connection pooled and opens
|
|
861
|
+
// another. The guard runs the handler even if the callback throws, which would otherwise
|
|
862
|
+
// leave the session checked out and never returned to the pool.
|
|
863
|
+
const auto end_stream = gsl::finally([handler = std::move(ctx.stream_end_handler)]() mutable {
|
|
864
|
+
if (handler) {
|
|
865
|
+
handler();
|
|
866
|
+
}
|
|
867
|
+
});
|
|
561
868
|
callback(std::move(data), !res.complete, {});
|
|
562
869
|
});
|
|
563
870
|
}
|
|
564
871
|
|
|
872
|
+
void
|
|
873
|
+
http_session::finish_read_some(std::error_code ec, bool response_ended)
|
|
874
|
+
{
|
|
875
|
+
if (!ec && !response_ended && !queued_reads_.empty()) {
|
|
876
|
+
read_some_in_flight_ = false;
|
|
877
|
+
auto next = std::move(queued_reads_.front());
|
|
878
|
+
queued_reads_.pop_front();
|
|
879
|
+
return do_read_some(std::move(next));
|
|
880
|
+
}
|
|
881
|
+
// Nothing more belongs to this response. Each queued read completes without touching the socket.
|
|
882
|
+
if (queued_reads_.empty()) {
|
|
883
|
+
read_some_in_flight_ = false;
|
|
884
|
+
return notify_reads_drained();
|
|
885
|
+
}
|
|
886
|
+
if (!ec) {
|
|
887
|
+
// A clean end: the stream-end handler has already checked the connection in, and another
|
|
888
|
+
// request may have taken it out. Its reads must not queue behind these, which belong to the
|
|
889
|
+
// response that ended, so they are moved out and the flag cleared before they drain.
|
|
890
|
+
auto ended = std::make_shared<std::deque<read_callback>>();
|
|
891
|
+
std::swap(*ended, queued_reads_);
|
|
892
|
+
read_some_in_flight_ = false;
|
|
893
|
+
++ended_drains_;
|
|
894
|
+
return drain_ended_reads(std::move(ended));
|
|
895
|
+
}
|
|
896
|
+
// After a failure the session is being torn down. The flag stays set until the queue is empty, so
|
|
897
|
+
// a read_some() from one of these callbacks queues behind the rest and completes the same way.
|
|
898
|
+
// One callback per handler posted to the strand, never inline: this function runs from noexcept
|
|
899
|
+
// scope guards, where a throwing callback would terminate. The guard posts the next handler even
|
|
900
|
+
// if the callback throws, so the exception reaches io_context::run() and the rest still complete
|
|
901
|
+
// in order.
|
|
902
|
+
asio::post(get_executor(), [self = shared_from_this(), ec]() {
|
|
903
|
+
auto callback = std::move(self->queued_reads_.front());
|
|
904
|
+
self->queued_reads_.pop_front();
|
|
905
|
+
const auto next = gsl::finally([&self, ec]() {
|
|
906
|
+
self->finish_read_some(ec, true);
|
|
907
|
+
});
|
|
908
|
+
callback({}, false, ec);
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
void
|
|
913
|
+
http_session::on_reads_drained(utils::movable_function<void()>&& handler)
|
|
914
|
+
{
|
|
915
|
+
reads_drained_.emplace_back(std::move(handler));
|
|
916
|
+
notify_reads_drained();
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
void
|
|
920
|
+
http_session::notify_reads_drained()
|
|
921
|
+
{
|
|
922
|
+
if (read_some_in_flight_ || ended_drains_ != 0) {
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
// Posted, never inline: this runs from noexcept scope guards.
|
|
926
|
+
for (auto& handler : std::exchange(reads_drained_, {})) {
|
|
927
|
+
asio::post(get_executor(), std::move(handler));
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
void
|
|
932
|
+
http_session::drain_ended_reads(std::shared_ptr<std::deque<read_callback>> reads)
|
|
933
|
+
{
|
|
934
|
+
if (reads->empty()) {
|
|
935
|
+
--ended_drains_;
|
|
936
|
+
return notify_reads_drained();
|
|
937
|
+
}
|
|
938
|
+
// Posted one per handler for the reason finish_read_some() posts its drain.
|
|
939
|
+
asio::post(get_executor(), [self = shared_from_this(), reads]() {
|
|
940
|
+
auto callback = std::move(reads->front());
|
|
941
|
+
reads->pop_front();
|
|
942
|
+
const auto next = gsl::finally([&self, &reads]() {
|
|
943
|
+
self->drain_ended_reads(reads);
|
|
944
|
+
});
|
|
945
|
+
callback({}, false, {});
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
|
|
565
949
|
void
|
|
566
950
|
http_session::on_resolve(std::error_code ec, const asio::ip::tcp::resolver::results_type& endpoints)
|
|
567
951
|
{
|
|
@@ -569,16 +953,17 @@ http_session::on_resolve(std::error_code ec, const asio::ip::tcp::resolver::resu
|
|
|
569
953
|
return;
|
|
570
954
|
}
|
|
571
955
|
if (ec) {
|
|
572
|
-
CB_LOG_ERROR(
|
|
573
|
-
|
|
956
|
+
CB_LOG_ERROR("{} error on resolve \"{}\": {}",
|
|
957
|
+
info_.log_prefix(),
|
|
958
|
+
logger::system_data(fmt::format("{}:{}", hostname_, service_)),
|
|
959
|
+
ec.message());
|
|
574
960
|
return initiate_connect();
|
|
575
961
|
}
|
|
576
962
|
last_active_ = std::chrono::steady_clock::now();
|
|
577
963
|
endpoints_ = endpoints;
|
|
578
|
-
CB_LOG_TRACE("{} resolved \"{}
|
|
964
|
+
CB_LOG_TRACE("{} resolved \"{}\" to {} endpoint(s)",
|
|
579
965
|
info_.log_prefix(),
|
|
580
|
-
hostname_,
|
|
581
|
-
service_,
|
|
966
|
+
logger::system_data(fmt::format("{}:{}", hostname_, service_)),
|
|
582
967
|
endpoints_.size());
|
|
583
968
|
do_connect(endpoints_.begin());
|
|
584
969
|
}
|
|
@@ -590,43 +975,49 @@ http_session::do_connect(asio::ip::tcp::resolver::results_type::iterator it)
|
|
|
590
975
|
return;
|
|
591
976
|
}
|
|
592
977
|
if (it != endpoints_.end()) {
|
|
593
|
-
CB_LOG_DEBUG("{} connecting to {}
|
|
978
|
+
CB_LOG_DEBUG("{} connecting to {} (\"{}\"), timeout={}ms",
|
|
594
979
|
info_.log_prefix(),
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
hostname_,
|
|
598
|
-
service_,
|
|
980
|
+
logger::system_data(fmt::format(
|
|
981
|
+
"{}:{}", it->endpoint().address().to_string(), it->endpoint().port())),
|
|
982
|
+
logger::system_data(fmt::format("{}:{}", hostname_, service_)),
|
|
599
983
|
http_ctx_.options.connect_timeout.count());
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
984
|
+
{
|
|
985
|
+
const std::scoped_lock lock(connect_deadline_timer_mutex_);
|
|
986
|
+
// stop() can set stopped_ on another thread after the test at the top of this function.
|
|
987
|
+
// cppcheck-suppress knownConditionTrueFalse
|
|
988
|
+
if (stopped_) {
|
|
604
989
|
return;
|
|
605
990
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
991
|
+
connect_deadline_timer_.expires_after(http_ctx_.options.connect_timeout);
|
|
992
|
+
connect_deadline_timer_.async_wait(
|
|
993
|
+
[self = shared_from_this(), it](const auto timer_ec) mutable {
|
|
994
|
+
if (timer_ec == asio::error::operation_aborted || self->stopped_) {
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
CB_LOG_DEBUG("{} unable to connect to {} in time, reconnecting",
|
|
998
|
+
self->info_.log_prefix(),
|
|
999
|
+
logger::system_data(fmt::format("{}:{}", self->hostname_, self->service_)));
|
|
1000
|
+
return self->stream_->close([self, next_address = ++it](std::error_code ec) {
|
|
1001
|
+
if (ec) {
|
|
1002
|
+
CB_LOG_WARNING(
|
|
1003
|
+
"{} unable to close socket, but continue connecting attempt to {}: {}",
|
|
1004
|
+
self->info_.log_prefix(),
|
|
1005
|
+
logger::system_data(fmt::format("{}:{}",
|
|
1006
|
+
next_address->endpoint().address().to_string(),
|
|
1007
|
+
next_address->endpoint().port())),
|
|
1008
|
+
ec.value());
|
|
1009
|
+
}
|
|
1010
|
+
self->do_connect(next_address);
|
|
1011
|
+
});
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
stream_->async_connect(it->endpoint(), hostname_, [self = shared_from_this(), it](auto&& ec) {
|
|
1015
|
+
self->on_connect(std::forward<decltype(ec)>(ec), it);
|
|
624
1016
|
});
|
|
625
1017
|
} else {
|
|
626
|
-
CB_LOG_ERROR("{} no more endpoints left to connect, \"{}
|
|
1018
|
+
CB_LOG_ERROR("{} no more endpoints left to connect, \"{}\" is not reachable",
|
|
627
1019
|
info_.log_prefix(),
|
|
628
|
-
hostname_,
|
|
629
|
-
service_);
|
|
1020
|
+
logger::system_data(fmt::format("{}:{}", hostname_, service_)));
|
|
630
1021
|
return initiate_connect();
|
|
631
1022
|
}
|
|
632
1023
|
}
|
|
@@ -640,10 +1031,10 @@ http_session::on_connect(const std::error_code& ec,
|
|
|
640
1031
|
}
|
|
641
1032
|
last_active_ = std::chrono::steady_clock::now();
|
|
642
1033
|
if (!stream_->is_open() || ec) {
|
|
643
|
-
CB_LOG_WARNING("{} unable to connect to {}:
|
|
1034
|
+
CB_LOG_WARNING("{} unable to connect to {}: {}{}",
|
|
644
1035
|
info_.log_prefix(),
|
|
645
|
-
|
|
646
|
-
|
|
1036
|
+
logger::system_data(fmt::format(
|
|
1037
|
+
"{}:{}", it->endpoint().address().to_string(), it->endpoint().port())),
|
|
647
1038
|
ec.message(),
|
|
648
1039
|
(ec == asio::error::connection_refused)
|
|
649
1040
|
? ", check server ports and cluster encryption setting"
|
|
@@ -651,11 +1042,13 @@ http_session::on_connect(const std::error_code& ec,
|
|
|
651
1042
|
if (stream_->is_open()) {
|
|
652
1043
|
stream_->close([self = shared_from_this(), next_address = ++it](std::error_code ec) {
|
|
653
1044
|
if (ec) {
|
|
654
|
-
CB_LOG_WARNING(
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
1045
|
+
CB_LOG_WARNING(
|
|
1046
|
+
"{} unable to close socket, but continue connecting attempt to {}: {}",
|
|
1047
|
+
self->info_.log_prefix(),
|
|
1048
|
+
logger::system_data(fmt::format("{}:{}",
|
|
1049
|
+
next_address->endpoint().address().to_string(),
|
|
1050
|
+
next_address->endpoint().port())),
|
|
1051
|
+
ec.value());
|
|
659
1052
|
}
|
|
660
1053
|
self->do_connect(next_address);
|
|
661
1054
|
});
|
|
@@ -665,15 +1058,19 @@ http_session::on_connect(const std::error_code& ec,
|
|
|
665
1058
|
} else {
|
|
666
1059
|
state_ = diag::endpoint_state::connected;
|
|
667
1060
|
connected_ = true;
|
|
668
|
-
CB_LOG_DEBUG("{} connected to {}
|
|
1061
|
+
CB_LOG_DEBUG("{} connected to {}",
|
|
669
1062
|
info_.log_prefix(),
|
|
670
|
-
|
|
671
|
-
|
|
1063
|
+
logger::system_data(fmt::format(
|
|
1064
|
+
"{}:{}", it->endpoint().address().to_string(), it->endpoint().port())));
|
|
672
1065
|
{
|
|
673
1066
|
const std::scoped_lock lock(info_mutex_);
|
|
674
1067
|
info_ = http_session_info(client_id_, id_, stream_->local_endpoint(), it->endpoint());
|
|
675
1068
|
}
|
|
676
|
-
|
|
1069
|
+
{
|
|
1070
|
+
const std::scoped_lock lock(connect_deadline_timer_mutex_);
|
|
1071
|
+
connect_deadline_timer_.cancel();
|
|
1072
|
+
}
|
|
1073
|
+
fresh_connection_ = true;
|
|
677
1074
|
invoke_connect_callback();
|
|
678
1075
|
flush();
|
|
679
1076
|
}
|
|
@@ -682,7 +1079,10 @@ http_session::on_connect(const std::error_code& ec,
|
|
|
682
1079
|
void
|
|
683
1080
|
http_session::do_read()
|
|
684
1081
|
{
|
|
685
|
-
|
|
1082
|
+
// body_pulled_: past a streaming response's head the body belongs to read_some(), which reads the
|
|
1083
|
+
// same socket into the same buffer. A do_read() armed then, such as the one a write completion
|
|
1084
|
+
// starts after the head has already arrived, would take a part of that body and drop it.
|
|
1085
|
+
if (stopped_ || reading_ || body_pulled_ || !stream_->is_open()) {
|
|
686
1086
|
return;
|
|
687
1087
|
}
|
|
688
1088
|
reading_ = true;
|
|
@@ -708,10 +1108,35 @@ http_session::do_read()
|
|
|
708
1108
|
|
|
709
1109
|
self->last_active_ = std::chrono::steady_clock::now();
|
|
710
1110
|
if (ec) {
|
|
711
|
-
|
|
712
|
-
|
|
1111
|
+
if (self->idle_) {
|
|
1112
|
+
// Expected: the peer closed a pooled idle connection. Tear it down
|
|
1113
|
+
// quietly so it is removed from the pool rather than reused dead.
|
|
1114
|
+
CB_LOG_DEBUG("{} idle HTTP connection closed by peer ({}), stopping session",
|
|
1115
|
+
self->info_.log_prefix(),
|
|
1116
|
+
ec.message());
|
|
1117
|
+
} else {
|
|
1118
|
+
if (self->resend_after_rejected_handshake(ec)) {
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
CB_LOG_ERROR("{} IO error while reading from the socket: {}",
|
|
1122
|
+
self->info_.log_prefix(),
|
|
1123
|
+
ec.message());
|
|
1124
|
+
}
|
|
713
1125
|
return self->stop();
|
|
714
1126
|
}
|
|
1127
|
+
if (self->idle_) {
|
|
1128
|
+
// A pooled keep-alive connection must not receive data until it is
|
|
1129
|
+
// checked out for a request (which clears idle_ via reset_idle()).
|
|
1130
|
+
// Unsolicited bytes mean the connection is in an unexpected state, so
|
|
1131
|
+
// drop it rather than feed them into an empty response parser.
|
|
1132
|
+
CB_LOG_DEBUG("{} unexpected data on idle HTTP connection, stopping session",
|
|
1133
|
+
self->info_.log_prefix());
|
|
1134
|
+
return self->stop();
|
|
1135
|
+
}
|
|
1136
|
+
if (self->fresh_connection_.exchange(false)) {
|
|
1137
|
+
const std::scoped_lock lock(self->output_buffer_mutex_);
|
|
1138
|
+
self->unanswered_request_.clear();
|
|
1139
|
+
}
|
|
715
1140
|
|
|
716
1141
|
if (self->streaming_response_) {
|
|
717
1142
|
// If streaming the response, read at least the entire header and then call the
|
|
@@ -732,23 +1157,37 @@ http_session::do_read()
|
|
|
732
1157
|
std::swap(self->current_streaming_response_, ctx);
|
|
733
1158
|
}
|
|
734
1159
|
|
|
735
|
-
|
|
1160
|
+
http_streaming_response resp{ self->ctx_, ctx.parser, self };
|
|
1161
|
+
ctx.must_close_connection = resp.must_close_connection();
|
|
736
1162
|
ctx.parser.body_chunk = "";
|
|
737
1163
|
|
|
738
|
-
if (res.complete && ctx.
|
|
1164
|
+
if (res.complete && ctx.must_close_connection) {
|
|
739
1165
|
self->keep_alive_ = false;
|
|
740
1166
|
}
|
|
1167
|
+
self->body_pulled_ = !res.complete;
|
|
741
1168
|
self->reading_ = false;
|
|
742
|
-
if
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
1169
|
+
// Runs even if the response handler throws. Skipped, the context holding the parser
|
|
1170
|
+
// would be dropped, so the rest of the body is fed to an empty parser and the stream-end
|
|
1171
|
+
// handler never runs.
|
|
1172
|
+
const auto reinstall_or_end = gsl::finally([&self, &ctx, complete = res.complete]() {
|
|
1173
|
+
if (!complete) {
|
|
1174
|
+
// A stop() that ran while ctx was swapped out, from the response handler or from
|
|
1175
|
+
// another thread, ended nothing, and a context reinstalled into a stopped session is
|
|
1176
|
+
// never ended: its stream-end handler keeps this session alive. stop() sets
|
|
1177
|
+
// stopped_ before its teardown takes this mutex, so either the teardown finds the
|
|
1178
|
+
// reinstalled context or this test sees the stop.
|
|
1179
|
+
const std::scoped_lock lock(self->current_response_mutex_);
|
|
1180
|
+
if (!self->stopped_) {
|
|
1181
|
+
std::swap(self->current_streaming_response_, ctx);
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
749
1185
|
if (auto handler = std::move(ctx.stream_end_handler); handler) {
|
|
750
1186
|
handler();
|
|
751
1187
|
}
|
|
1188
|
+
});
|
|
1189
|
+
if (auto handler = std::move(ctx.resp_handler); handler) {
|
|
1190
|
+
handler({}, std::move(resp));
|
|
752
1191
|
}
|
|
753
1192
|
return;
|
|
754
1193
|
}
|
|
@@ -756,20 +1195,27 @@ http_session::do_read()
|
|
|
756
1195
|
return self->do_read();
|
|
757
1196
|
}
|
|
758
1197
|
http_parser::feeding_result res{};
|
|
1198
|
+
response_context ctx{};
|
|
759
1199
|
{
|
|
1200
|
+
// One section for the feed and the take. stop() sets stopped_ before its
|
|
1201
|
+
// cancel_current_response() takes this mutex, so either the stop is seen here and its
|
|
1202
|
+
// cancel completes the response, or the cancel runs after the take and finds no handler.
|
|
1203
|
+
// A cancel between a feed and a separate take would leave an empty context, whose handler
|
|
1204
|
+
// throws std::bad_function_call on the io thread.
|
|
760
1205
|
const std::scoped_lock lock(self->current_response_mutex_);
|
|
1206
|
+
if (self->stopped_) {
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
761
1209
|
res = self->current_response_.parser.feed(
|
|
762
1210
|
reinterpret_cast<const char*>(self->input_buffer_.data()), bytes_transferred);
|
|
1211
|
+
if (res.complete) {
|
|
1212
|
+
std::swap(self->current_response_, ctx);
|
|
1213
|
+
}
|
|
763
1214
|
}
|
|
764
1215
|
if (res.failure) {
|
|
765
1216
|
return self->stop();
|
|
766
1217
|
}
|
|
767
1218
|
if (res.complete) {
|
|
768
|
-
response_context ctx{};
|
|
769
|
-
{
|
|
770
|
-
const std::scoped_lock lock(self->current_response_mutex_);
|
|
771
|
-
std::swap(self->current_response_, ctx);
|
|
772
|
-
}
|
|
773
1219
|
if (ctx.parser.response.must_close_connection()) {
|
|
774
1220
|
self->keep_alive_ = false;
|
|
775
1221
|
}
|
|
@@ -793,6 +1239,9 @@ http_session::do_write()
|
|
|
793
1239
|
return;
|
|
794
1240
|
}
|
|
795
1241
|
std::swap(writing_buffer_, output_buffer_);
|
|
1242
|
+
// Recorded where the request is handed to the socket. A request a stop() keeps from this point
|
|
1243
|
+
// is not reported as written on this connection.
|
|
1244
|
+
record_dispatch();
|
|
796
1245
|
std::vector<asio::const_buffer> buffers;
|
|
797
1246
|
buffers.reserve(writing_buffer_.size());
|
|
798
1247
|
for (auto& buf : writing_buffer_) {
|
|
@@ -815,6 +1264,9 @@ http_session::do_write()
|
|
|
815
1264
|
}
|
|
816
1265
|
self->last_active_ = std::chrono::steady_clock::now();
|
|
817
1266
|
if (ec) {
|
|
1267
|
+
if (self->resend_after_rejected_handshake(ec)) {
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
818
1270
|
CB_LOG_ERROR(
|
|
819
1271
|
"{} IO error while writing to the socket: {}", self->info_.log_prefix(), ec.message());
|
|
820
1272
|
return self->stop();
|