xref: /aosp_15_r20/external/federated-compute/fcp/client/http/java/java_http_client.h (revision 14675a029014e728ec732f129a32e299b2da0601)
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_JAVA_JAVA_HTTP_CLIENT_H_
17 #define FCP_CLIENT_HTTP_JAVA_JAVA_HTTP_CLIENT_H_
18 
19 #include <jni.h>
20 
21 #include <cstdint>
22 #include <memory>
23 #include <optional>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 #include "absl/base/attributes.h"
29 #include "absl/status/status.h"
30 #include "absl/status/statusor.h"
31 #include "absl/strings/string_view.h"
32 #include "absl/synchronization/mutex.h"
33 #include "fcp/client/http/http_client.h"
34 #include "fcp/client/http/java/jni.pb.h"
35 
36 namespace fcp {
37 namespace client {
38 namespace http {
39 namespace java {
40 
41 // An `HttpClient` implementation that performs HTTP requests by calling back
42 // into Java over JNI, and lets the Java code issue the actual requests.
43 class JavaHttpClient : public HttpClient {
44  public:
45   JavaHttpClient(JavaVM* jvm, jobject java_http_client);
46   ~JavaHttpClient() override;
47 
48   ABSL_MUST_USE_RESULT
49   std::unique_ptr<fcp::client::http::HttpRequestHandle> EnqueueRequest(
50       std::unique_ptr<fcp::client::http::HttpRequest> request) override;
51 
52   absl::Status PerformRequests(
53       std::vector<std::pair<fcp::client::http::HttpRequestHandle*,
54                             fcp::client::http::HttpRequestCallback*>>
55           requests) override;
56 
57  private:
58   JavaVM* const jvm_;
59   jobject jthis_;
60   jmethodID enqueue_request_id_;
61   jmethodID perform_requests_id_;
62   jmethodID close_id_;
63 };
64 
65 // An HttpResponse implementation that is based on data provided via the JNI
66 // callbacks.
67 class JavaHttpResponse : public fcp::client::http::HttpResponse {
68  public:
JavaHttpResponse()69   JavaHttpResponse() {}
70 
71   // Populates the response data based on the given JNI proto.
PopulateFromProto(const JniHttpResponse & response_proto)72   void PopulateFromProto(const JniHttpResponse& response_proto) {
73     code_ = response_proto.code();
74     headers_.clear();
75     for (const JniHttpHeader& header : response_proto.headers()) {
76       headers_.push_back(Header(header.name(), header.value()));
77     }
78   }
79 
code()80   int code() const override { return code_; }
headers()81   const HeaderList& headers() const override { return headers_; }
82 
83  private:
84   int code_ = -1;
85   HeaderList headers_;
86 };
87 
88 // An `HttpRequestHandle` implementation that performs HTTP requests by calling
89 // back into Java over JNI, and lets the Java code issue the actual requests.
90 class JavaHttpRequestHandle : public fcp::client::http::HttpRequestHandle {
91  public:
92   // Utility for extracting a JavaHttpRequestHandle pointer from a Java jlong.
93   // When a JavaHttpRequestHandle object is constructed, it will 'attach' itself
94   // to the corresponding Java object by writing its address to its
95   // `nativeHandle` field. When Java then calls back into our C++ code, it
96   // passes that address back to us, and this function can then be used to turn
97   // the address into a proper pointer.
98   static JavaHttpRequestHandle* FromJlong(jlong ptr);
99 
100   JavaHttpRequestHandle(
101       JavaVM* jvm, jobject java_http_request_handle,
102       std::unique_ptr<fcp::client::http::HttpRequest> request);
103 
104   ~JavaHttpRequestHandle() override ABSL_LOCKS_EXCLUDED(lock_);
105 
106   // --- HttpRequestHandle methods
107   fcp::client::http::HttpRequestHandle::SentReceivedBytes
108   TotalSentReceivedBytes() const override;
109   void Cancel() override ABSL_LOCKS_EXCLUDED(lock_);
110 
111   // --- JNI handling methods
112   jboolean ReadRequestBody(JNIEnv* env, jbyteArray buffer,
113                            jlong requested_bytes, jintArray actual_bytes_read)
114       ABSL_LOCKS_EXCLUDED(lock_);
115 
116   jboolean OnResponseStarted(JNIEnv* env, jbyteArray response_proto)
117       ABSL_LOCKS_EXCLUDED(lock_);
118   void OnResponseError(JNIEnv* env, jbyteArray status_proto)
119       ABSL_LOCKS_EXCLUDED(lock_);
120 
121   jboolean OnResponseBody(JNIEnv* env, jbyteArray buffer, jint bytes_available)
122       ABSL_LOCKS_EXCLUDED(lock_);
123   void OnResponseBodyError(JNIEnv* env, jbyteArray status_proto)
124       ABSL_LOCKS_EXCLUDED(lock_);
125   void OnResponseCompleted() ABSL_LOCKS_EXCLUDED(lock_);
126 
127   // --- Internal methods.
128   // Returns the (possibly empty/default-constructed) response object for this
129   // handle. This object is populated with actual data after `OnResponseStarted`
130   // is called.
131   const JavaHttpResponse& response() const ABSL_LOCKS_EXCLUDED(lock_);
132 
133   // Returns the `HttpRequestCallback` associated with this handle, or a nullptr
134   // if no callback is associated with it yet (i.e. no `PerformRequests` call
135   // was made yet with this handle).
136   fcp::client::http::HttpRequestCallback* callback() const
137       ABSL_LOCKS_EXCLUDED(lock_);
138 
139   // Associates an `HttpRequestCallback` with this handle, or returns an
140   // `INVALID_ARGUMENT` error if one was already associated with it (indicating
141   // the handle is being used with more than one `PerformRequests` call, which
142   // is not allowed).
143   absl::Status SetCallback(fcp::client::http::HttpRequestCallback* callback)
144       ABSL_LOCKS_EXCLUDED(lock_);
145 
146   // Returns the JNI reference for the Java handle object that corresponds to
147   // this C++ handle object.
GetJobject()148   jobject GetJobject() { return jthis_; }
149 
150  private:
151   JavaVM* const jvm_;
152   jobject jthis_;
153   jmethodID get_total_sent_received_bytes_id_;
154   jmethodID close_id_;
155   jfieldID native_handle_id_;
156 
157   const std::unique_ptr<fcp::client::http::HttpRequest> request_;
158 
159   // A note on synchronization: most of JavaHttpRequestHandle's methods may be
160   // called from any thread (incl. the JNI callback methods, which are
161   // guaranteed to never be called concurrently from more than one thread, but
162   // which for which subsequent calls may occur on different threads). This
163   // object also has mutable state (e.g. `response_` which only gets populated
164   // with data once the `OnResponseStarted` callback is invoked).
165   //
166   // We use this mutex to ensure that each JNI callback invocation observes the
167   // effects of every prior callback invocation. This means we don't have to
168   // rely on the Java side of the implementation for thread safety (even though
169   // the Java side likely also implements its own synchronization as well).
170   mutable absl::Mutex lock_;
171   JavaHttpResponse response_ ABSL_GUARDED_BY(lock_);
172   fcp::client::http::HttpRequestCallback* callback_ ABSL_GUARDED_BY(lock_) =
173       nullptr;
174   bool performed_ ABSL_GUARDED_BY(lock_) = false;
175 };
176 
177 }  // namespace java
178 }  // namespace http
179 }  // namespace client
180 }  // namespace fcp
181 #endif  // FCP_CLIENT_HTTP_JAVA_JAVA_HTTP_CLIENT_H_
182