1 /* 2 * Copyright 2022 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #ifndef FCP_CLIENT_HTTP_PROTOCOL_REQUEST_HELPER_H_ 17 #define FCP_CLIENT_HTTP_PROTOCOL_REQUEST_HELPER_H_ 18 19 #include "absl/container/flat_hash_set.h" 20 #include "absl/status/status.h" 21 #include "absl/status/statusor.h" 22 #include "fcp/base/clock.h" 23 #include "fcp/base/wall_clock_stopwatch.h" 24 #include "fcp/client/http/http_client.h" 25 #include "fcp/client/http/in_memory_request_response.h" 26 #include "fcp/protos/federatedcompute/common.pb.h" 27 // #include "google/longrunning/operations.pb.h" 28 29 namespace fcp { 30 namespace client { 31 namespace http { 32 33 // Note the uri query parameters should be percent encoded. 34 using QueryParams = absl::flat_hash_map<std::string, std::string>; 35 36 // A helper for creating HTTP request with base uri, request headers and 37 // compression setting. 38 class ProtocolRequestCreator { 39 public: 40 ProtocolRequestCreator(absl::string_view request_base_uri, 41 absl::string_view api_key, HeaderList request_headers, 42 bool use_compression); 43 44 // Creates a `ProtocolRequestCreator` based on the forwarding info. 45 // Validates and extracts the base URI and headers to use for the subsequent 46 // request(s). 47 static absl::StatusOr<std::unique_ptr<ProtocolRequestCreator>> Create( 48 absl::string_view api_key, 49 const ::google::internal::federatedcompute::v1::ForwardingInfo& 50 forwarding_info, 51 bool use_compression); 52 53 // Creates an `HttpRequest` with base uri, request headers and compression 54 // setting. The `uri_path_suffix` argument must always either be empty or 55 // start with a leading '/'. The method will return `InvalidArgumentError` if 56 // this isn't the case. The `uri_path_suffix` should not contain any query 57 // parameters, instead, query parameters should be specified in `params`. 58 // 59 // The URI to which the protocol request will be sent will be constructed by 60 // joining `next_request_base_uri_` with `uri_path_suffix` (see 61 // `JoinBaseUriWithSuffix` for details), and any query parameters if `params` 62 // is not empty. 63 // 64 // When `is_protobuf_encoded` is true, `%24alt=proto` will be added to the uri 65 // as a query parameter to indicate that the proto encoded payload is 66 // expected. When the `request_body` is not empty, a `Content-Type` header 67 // will also be added to the request 68 absl::StatusOr<std::unique_ptr<HttpRequest>> CreateProtocolRequest( 69 absl::string_view uri_path_suffix, QueryParams params, 70 HttpRequest::Method method, std::string request_body, 71 bool is_protobuf_encoded) const; 72 73 // Creates an `HttpRequest` for getting the result of a 74 // `google.longrunning.operation`. Note that the request body is empty, 75 // because its only field (`name`) is included in the URI instead. Also note 76 // that the `next_request_headers_` will be attached to this request. 77 absl::StatusOr<std::unique_ptr<HttpRequest>> CreateGetOperationRequest( 78 absl::string_view operation_name) const; 79 80 // Creates an `HttpRequest` for canceling a `google.longrunning.operation`. 81 // Note that the request body is empty, because its only field (`name`) is 82 // included in the URI instead. Also note that the `next_request_headers_` 83 // will be attached to this request. 84 absl::StatusOr<std::unique_ptr<HttpRequest>> CreateCancelOperationRequest( 85 absl::string_view operation_name) const; 86 87 private: 88 absl::StatusOr<std::unique_ptr<HttpRequest>> CreateHttpRequest( 89 absl::string_view uri_path_suffix, QueryParams params, 90 HttpRequest::Method method, std::string request_body, 91 bool is_protobuf_encoded, bool use_compression) const; 92 // The URI to use for the next protocol request. See `ForwardingInfo`. 93 std::string next_request_base_uri_; 94 // The API key used for requests. 95 const std::string api_key_; 96 // The set of headers to attach to the next protocol request. See 97 // `ForwardingInfo`. 98 HeaderList next_request_headers_; 99 const bool use_compression_; 100 }; 101 102 // A helper for issuing protocol requests. 103 class ProtocolRequestHelper { 104 public: 105 ProtocolRequestHelper(HttpClient* http_client, int64_t* bytes_downloaded, 106 int64_t* bytes_uploaded, 107 WallClockStopwatch* network_stopwatch, Clock* clock); 108 109 // Performs the given request (handling any interruptions that may occur) and 110 // updates the network stats. 111 absl::StatusOr<InMemoryHttpResponse> PerformProtocolRequest( 112 std::unique_ptr<HttpRequest> request, InterruptibleRunner& runner); 113 114 // Performs the vector of requests (handling any interruptions that may occur) 115 // concurrently and updates the network stats. 116 // The returned vector of responses has the same order of the issued requests. 117 absl::StatusOr<std::vector<absl::StatusOr<InMemoryHttpResponse>>> 118 PerformMultipleProtocolRequests( 119 std::vector<std::unique_ptr<HttpRequest>> requests, 120 InterruptibleRunner& runner); 121 122 // Helper function for handling an HTTP response that contains an `Operation` 123 // proto. 124 // 125 // Takes an HTTP response (which must have been produced by a call to 126 // `PerformRequestInMemory`), parses the proto, and returns it if its 127 // `Operation.done` field is true. If the field is false then this method 128 // keeps polling the Operation via performing requests created by 129 // `CreateGetOperationRequest` until it a response is received where the field 130 // is true, at which point that most recent response is returned. If at any 131 // point an HTTP or response parsing error is encountered, then that error is 132 // returned instead. 133 // absl::StatusOr<::google::longrunning::Operation> 134 // PollOperationResponseUntilDone( 135 // const ::google::longrunning::Operation& initial_operation, 136 // const ProtocolRequestCreator& request_creator, 137 // InterruptibleRunner& runner); 138 139 // // Helper function for cancelling an operation. 140 // absl::StatusOr<InMemoryHttpResponse> CancelOperation( 141 // absl::string_view operation_name, 142 // const ProtocolRequestCreator& request_creator, 143 // InterruptibleRunner& runner); 144 145 private: 146 HttpClient& http_client_; 147 int64_t& bytes_downloaded_; 148 int64_t& bytes_uploaded_; 149 WallClockStopwatch& network_stopwatch_; 150 Clock& clock_; 151 }; 152 153 // Parse a google::longrunning::Operation out of a InMemoryHttpResponse. 154 // If the initial http_response is not OK, this method will immediately return 155 // with the error status. 156 // absl::StatusOr<google::longrunning::Operation> 157 // ParseOperationProtoFromHttpResponse( 158 // absl::StatusOr<InMemoryHttpResponse> http_response); 159 160 // // Extract the operation name from Operation proto. 161 // // If the operation name is not started with "operations/", invalid argument 162 // // error will be returned. 163 // absl::StatusOr<std::string> ExtractOperationName( 164 // const google::longrunning::Operation& operation); 165 166 } // namespace http 167 } // namespace client 168 } // namespace fcp 169 170 #endif // FCP_CLIENT_HTTP_PROTOCOL_REQUEST_HELPER_H_ 171