// // 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 "src/core/ext/filters/http/client/http_client_filter.h" #include #include #include #include #include #include #include #include #include #include #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/promise/context.h" #include "src/core/lib/promise/latch.h" #include "src/core/lib/promise/map.h" #include "src/core/lib/promise/pipe.h" #include "src/core/lib/promise/race.h" #include "src/core/lib/resource_quota/arena.h" #include "src/core/lib/slice/percent_encoding.h" #include "src/core/lib/transport/status_conversion.h" #include "src/core/lib/transport/transport.h" #include "src/core/util/latent_see.h" namespace grpc_core { const NoInterceptor HttpClientFilter::Call::OnServerToClientMessage; const NoInterceptor HttpClientFilter::Call::OnClientToServerMessage; const NoInterceptor HttpClientFilter::Call::OnClientToServerHalfClose; const NoInterceptor HttpClientFilter::Call::OnFinalize; const grpc_channel_filter HttpClientFilter::kFilter = MakePromiseBasedFilter(); namespace { absl::Status CheckServerMetadata(ServerMetadata* b) { if (auto* status = b->get_pointer(HttpStatusMetadata())) { // If both gRPC status and HTTP status are provided in the response, we // should prefer the gRPC status code, as mentioned in // https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md. // const grpc_status_code* grpc_status = b->get_pointer(GrpcStatusMetadata()); if (grpc_status != nullptr || *status == 200) { b->Remove(HttpStatusMetadata()); } else { return absl::Status( static_cast( grpc_http2_status_to_grpc_status(*status)), absl::StrCat("Received http2 header with status: ", *status)); } } if (Slice* grpc_message = b->get_pointer(GrpcMessageMetadata())) { *grpc_message = PermissivePercentDecodeSlice(std::move(*grpc_message)); } b->Remove(ContentTypeMetadata()); return absl::OkStatus(); } HttpSchemeMetadata::ValueType SchemeFromArgs(const ChannelArgs& args) { HttpSchemeMetadata::ValueType scheme = HttpSchemeMetadata::Parse( args.GetString(GRPC_ARG_HTTP2_SCHEME).value_or(""), [](absl::string_view, const Slice&) {}); if (scheme == HttpSchemeMetadata::kInvalid) return HttpSchemeMetadata::kHttp; return scheme; } Slice UserAgentFromArgs(const ChannelArgs& args, absl::string_view transport_name) { std::vector fields; auto add = [&fields](absl::string_view x) { if (!x.empty()) fields.push_back(std::string(x)); }; add(args.GetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING).value_or("")); add(absl::StrFormat("grpc-c/%s (%s; %s)", grpc_version_string(), GPR_PLATFORM_STRING, transport_name)); add(args.GetString(GRPC_ARG_SECONDARY_USER_AGENT_STRING).value_or("")); return Slice::FromCopiedString(absl::StrJoin(fields, " ")); } } // namespace void HttpClientFilter::Call::OnClientInitialMetadata(ClientMetadata& md, HttpClientFilter* filter) { GRPC_LATENT_SEE_INNER_SCOPE( "HttpClientFilter::Call::OnClientInitialMetadata"); if (filter->test_only_use_put_requests_) { md.Set(HttpMethodMetadata(), HttpMethodMetadata::kPut); } else { md.Set(HttpMethodMetadata(), HttpMethodMetadata::kPost); } md.Set(HttpSchemeMetadata(), filter->scheme_); md.Set(TeMetadata(), TeMetadata::kTrailers); md.Set(ContentTypeMetadata(), ContentTypeMetadata::kApplicationGrpc); md.Set(UserAgentMetadata(), filter->user_agent_.Ref()); } absl::Status HttpClientFilter::Call::OnServerInitialMetadata( ServerMetadata& md) { GRPC_LATENT_SEE_INNER_SCOPE( "HttpClientFilter::Call::OnServerInitialMetadata"); return CheckServerMetadata(&md); } absl::Status HttpClientFilter::Call::OnServerTrailingMetadata( ServerMetadata& md) { GRPC_LATENT_SEE_INNER_SCOPE( "HttpClientFilter::Call::OnServerTrailingMetadata"); return CheckServerMetadata(&md); } HttpClientFilter::HttpClientFilter(HttpSchemeMetadata::ValueType scheme, Slice user_agent, bool test_only_use_put_requests) : scheme_(scheme), test_only_use_put_requests_(test_only_use_put_requests), user_agent_(std::move(user_agent)) {} absl::StatusOr> HttpClientFilter::Create( const ChannelArgs& args, ChannelFilter::Args) { auto* transport = args.GetObject(); if (transport == nullptr) { return absl::InvalidArgumentError("HttpClientFilter needs a transport"); } return std::make_unique( SchemeFromArgs(args), UserAgentFromArgs(args, transport->GetTransportName()), args.GetInt(GRPC_ARG_TEST_ONLY_USE_PUT_REQUESTS).value_or(false)); } } // namespace grpc_core