xref: /aosp_15_r20/external/federated-compute/fcp/client/http/java/java_http_client.cc (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 #include "fcp/client/http/java/java_http_client.h"
17 
18 #include <jni.h>
19 
20 #include <cstdint>
21 #include <functional>
22 #include <memory>
23 #include <optional>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 // #include "google/rpc/status.pb.h"
29 #include "absl/cleanup/cleanup.h"
30 #include "absl/status/status.h"
31 #include "absl/status/statusor.h"
32 #include "absl/strings/numbers.h"
33 #include "absl/strings/str_cat.h"
34 #include "absl/strings/string_view.h"
35 #include "absl/synchronization/mutex.h"
36 #include "fcp/base/monitoring.h"
37 #include "fcp/client/http/http_client.h"
38 #include "fcp/client/http/http_client_util.h"
39 #include "fcp/client/http/java/jni.pb.h"
40 #include "fcp/jni/jni_util.h"
41 
42 namespace fcp {
43 namespace client {
44 namespace http {
45 namespace java {
46 
47 using fcp::client::http::HttpRequestCallback;
48 using fcp::client::http::HttpRequestHandle;
49 using fcp::jni::JavaFieldSig;
50 using fcp::jni::JavaMethodSig;
51 using fcp::jni::LocalRefDeleter;
52 using fcp::jni::ParseProtoFromJByteArray;
53 using fcp::jni::ScopedJniEnv;
54 using fcp::jni::SerializeProtoToJByteArray;
55 
56 namespace {
57 
58 // The Java method signatures for the Java class corresponding to the C++
59 // `JavaHttpClient` class.
60 struct JavaHttpClientClassDesc {
61   static constexpr JavaMethodSig kEnqueueRequest = {
62       "enqueueRequest",
63       "([B)Lcom/google/fcp/client/http/HttpClientForNative$HttpRequestHandle;"};
64   static constexpr JavaMethodSig kPerformRequests = {"performRequests",
65                                                      "([Ljava/lang/Object;)[B"};
66   static constexpr JavaMethodSig kClose = {"close", "()V"};
67 };
68 
69 // The Java method and field signatures for the Java class corresponding to the
70 // C++ `JavaHttpRequestHandle` class.
71 struct JavaHttpRequestHandleClassDesc {
72   static constexpr JavaMethodSig kGetTotalSentReceivedBytes = {
73       "getTotalSentReceivedBytes", "()[B"};
74   static constexpr JavaMethodSig kClose = {"close", "()V"};
75   static constexpr JavaFieldSig kNativeHandle = {"nativeHandle", "J"};
76 };
77 
ConvertHttpClientMethodToProtoMethod(fcp::client::http::HttpRequest::Method method)78 JniHttpMethod ConvertHttpClientMethodToProtoMethod(
79     fcp::client::http::HttpRequest::Method method) {
80   switch (method) {
81     case fcp::client::http::HttpRequest::Method::kHead:
82       return JniHttpMethod::HTTP_METHOD_HEAD;
83     case fcp::client::http::HttpRequest::Method::kGet:
84       return JniHttpMethod::HTTP_METHOD_GET;
85     case fcp::client::http::HttpRequest::Method::kPost:
86       return JniHttpMethod::HTTP_METHOD_POST;
87     case fcp::client::http::HttpRequest::Method::kPut:
88       return JniHttpMethod::HTTP_METHOD_PUT;
89     case fcp::client::http::HttpRequest::Method::kPatch:
90       return JniHttpMethod::HTTP_METHOD_PATCH;
91     case fcp::client::http::HttpRequest::Method::kDelete:
92       return JniHttpMethod::HTTP_METHOD_DELETE;
93     default:
94       return JniHttpMethod::HTTP_METHOD_UNKNOWN;
95   }
96 }
97 
98 // Calls JNIEnv::GetMethodID and ensures that its return value is valid.
GetMethodIdOrAbort(JNIEnv & env,jclass clazz,JavaMethodSig method)99 jmethodID GetMethodIdOrAbort(JNIEnv& env, jclass clazz, JavaMethodSig method) {
100   jmethodID id = env.GetMethodID(clazz, method.name, method.signature);
101   FCP_CHECK(id != nullptr);
102   return id;
103 }
104 }  // namespace
105 
JavaHttpClient(JavaVM * jvm,jobject java_http_client)106 JavaHttpClient::JavaHttpClient(JavaVM* jvm, jobject java_http_client)
107     : jvm_(jvm) {
108   ScopedJniEnv scoped_env(jvm_);
109   JNIEnv* env = scoped_env.env();
110   jthis_ = env->NewGlobalRef(java_http_client);
111   FCP_CHECK(jthis_ != nullptr);
112   // We get the class from the jobject here instead of looking it up by name in
113   // the classloader because we may be using this class from a non java thread
114   // that has been attached to the jvm, and thus has a classloader with only
115   // "system" classes.
116   jclass java_http_client_class = env->GetObjectClass(java_http_client);
117   FCP_CHECK(java_http_client_class != nullptr);
118   LocalRefDeleter java_http_client_class_deleter(env, java_http_client_class);
119 
120   // Look up the method IDs for the Java methods we'll call later on.
121   enqueue_request_id_ = GetMethodIdOrAbort(
122       *env, java_http_client_class, JavaHttpClientClassDesc::kEnqueueRequest);
123   perform_requests_id_ = GetMethodIdOrAbort(
124       *env, java_http_client_class, JavaHttpClientClassDesc::kPerformRequests);
125   close_id_ = GetMethodIdOrAbort(*env, java_http_client_class,
126                                  JavaHttpClientClassDesc::kClose);
127 }
128 
~JavaHttpClient()129 JavaHttpClient::~JavaHttpClient() {
130   ScopedJniEnv scoped_env(jvm_);
131   JNIEnv* env = scoped_env.env();
132 
133   // We call the Java close() method when the destructor is invoked. This gives
134   // the Java code a chance to clean things up on its side, if it needs to.
135   env->CallVoidMethod(jthis_, close_id_);
136   FCP_CHECK(!env->ExceptionCheck());
137 
138   // Delete the global reference to the Java object.
139   env->DeleteGlobalRef(jthis_);
140 }
141 
EnqueueRequest(std::unique_ptr<fcp::client::http::HttpRequest> request)142 std::unique_ptr<HttpRequestHandle> JavaHttpClient::EnqueueRequest(
143     std::unique_ptr<fcp::client::http::HttpRequest> request) {
144   // Convert the `HttpRequest`'s info into a proto we can serialize and pass
145   // over the JNI boundary.
146   JniHttpRequest request_proto;
147   request_proto.set_uri(std::string(request->uri()));
148   request_proto.set_method(
149       ConvertHttpClientMethodToProtoMethod(request->method()));
150   for (auto request_header : request->extra_headers()) {
151     JniHttpHeader* header = request_proto.add_extra_headers();
152     header->set_name(request_header.first);
153     header->set_value(request_header.second);
154   }
155   request_proto.set_has_body(request->HasBody());
156 
157   // Call into Java to create the Java request handle object that will cooperate
158   // with our C++ `JavaHttpRequestHandle` object.
159   ScopedJniEnv scoped_env(jvm_);
160   JNIEnv* env = scoped_env.env();
161   jbyteArray serialized_request_proto =
162       SerializeProtoToJByteArray(env, request_proto);
163   LocalRefDeleter serialized_request_proto_deleter(env,
164                                                    serialized_request_proto);
165   jobject java_http_request_handle = env->CallObjectMethod(
166       jthis_, enqueue_request_id_, serialized_request_proto);
167   FCP_CHECK(java_http_request_handle != nullptr);
168   FCP_CHECK(!env->ExceptionCheck());
169 
170   // Create the C++ `JavaHttpRequestHandle` object (which will 'attach' itself
171   // to the Java object by writing its address to the `nativeHandle` Java
172   // field).
173   auto result = std::make_unique<JavaHttpRequestHandle>(
174       jvm_, java_http_request_handle, std::move(request));
175   return result;
176 }
177 
PerformRequests(std::vector<std::pair<HttpRequestHandle *,HttpRequestCallback * >> generic_requests)178 absl::Status JavaHttpClient::PerformRequests(
179     std::vector<std::pair<HttpRequestHandle*, HttpRequestCallback*>>
180         generic_requests) {
181   // We're about to kick off a group of requests. Each request has a matching
182   // callback, as well as a corresponding Java object. To prepare for the
183   // `performRequests` call into Java:
184   // 1. Create an Object[] array, consisting of each request's corresponding
185   //    Java object.
186   // 2. For each request, register the callback that should be used for it.
187   ScopedJniEnv scoped_env(jvm_);
188   JNIEnv* env = scoped_env.env();
189 
190   // The object array we'll create will just be of type Object[]. The Java
191   // code/JNI runtime will be in charge of downcasting the individual elements
192   // back to its concrete Java HttpRequestHandle implementation class. This
193   // avoids us having to try and look up the Java class (which can be difficult,
194   // since we may be running on a thread with a ClassLoader containing only Java
195   // system classes).
196   jclass object_class = env->FindClass("java/lang/Object");
197   FCP_CHECK(object_class != nullptr);
198   FCP_CHECK(!env->ExceptionCheck());
199   LocalRefDeleter object_class_deleter(env, object_class);
200 
201   // Create the Object[] array.
202   jobjectArray request_handle_array = env->NewObjectArray(
203       static_cast<jsize>(generic_requests.size()), object_class, nullptr);
204   FCP_CHECK(request_handle_array != nullptr);
205   FCP_CHECK(!env->ExceptionCheck());
206   LocalRefDeleter request_handle_array_deleter(env, request_handle_array);
207 
208   // Populate the Object[] array with the Java objects corresponding to each
209   // request, and register each callback with the `JavaHttpRequestHandle`.
210   int i = 0;
211   for (const auto& [generic_handle, callback] : generic_requests) {
212     auto request_handle = static_cast<JavaHttpRequestHandle*>(generic_handle);
213     env->SetObjectArrayElement(request_handle_array, i++,
214                                request_handle->GetJobject());
215     FCP_CHECK(!env->ExceptionCheck());
216     FCP_RETURN_IF_ERROR(request_handle->SetCallback(callback));
217   }
218 
219   // Call the Java `performRequests` method over JNI, passing it the Object[]
220   // array.
221   jbyteArray perform_requests_result =
222       static_cast<jbyteArray>(env->CallObjectMethod(
223           jthis_, perform_requests_id_, request_handle_array));
224   FCP_CHECK(!env->ExceptionCheck());
225   FCP_CHECK(perform_requests_result != nullptr);
226   LocalRefDeleter perform_requests_result_deleter(env, perform_requests_result);
227 
228   // Convert the return value from Java to an absl::Status.
229   return ConvertRpcStatusToAbslStatus(
230       ParseProtoFromJByteArray<
231           ::google::internal::federatedcompute::v1::Status>(
232           env, perform_requests_result));
233 }
234 
FromJlong(jlong ptr)235 JavaHttpRequestHandle* JavaHttpRequestHandle::FromJlong(jlong ptr) {
236   // If the Java code erroneously calls a JNI callback with a handle that has
237   // already been destroyed, then `ptr` will be 0. We want to catch such bugs
238   // early.
239   FCP_CHECK(ptr != 0)
240       << "cannot call JNI callback before enqueueRequest has been called";
241   return reinterpret_cast<JavaHttpRequestHandle*>(ptr);
242 }
243 
JavaHttpRequestHandle(JavaVM * jvm,jobject java_http_request_handle,std::unique_ptr<fcp::client::http::HttpRequest> request)244 JavaHttpRequestHandle::JavaHttpRequestHandle(
245     JavaVM* jvm, jobject java_http_request_handle,
246     std::unique_ptr<fcp::client::http::HttpRequest> request)
247     : jvm_(jvm), request_(std::move(request)) {
248   ScopedJniEnv scoped_env(jvm_);
249   JNIEnv* env = scoped_env.env();
250   jthis_ = env->NewGlobalRef(java_http_request_handle);
251   FCP_CHECK(jthis_ != nullptr);
252 
253   // We get the class from the jobject here instead of looking up by name in
254   // the classloader because we may be using this class from a non java thread
255   // that has been attached to the jvm, and thus has a classloader with only
256   // "system" classes.
257   jclass java_http_request_handle_class =
258       env->GetObjectClass(java_http_request_handle);
259   LocalRefDeleter java_http_request_handle_class_deleter(
260       env, java_http_request_handle_class);
261 
262   get_total_sent_received_bytes_id_ = GetMethodIdOrAbort(
263       *env, java_http_request_handle_class,
264       JavaHttpRequestHandleClassDesc::kGetTotalSentReceivedBytes);
265 
266   close_id_ = GetMethodIdOrAbort(*env, java_http_request_handle_class,
267                                  JavaHttpRequestHandleClassDesc::kClose);
268 
269   native_handle_id_ =
270       env->GetFieldID(java_http_request_handle_class,
271                       JavaHttpRequestHandleClassDesc::kNativeHandle.name,
272                       JavaHttpRequestHandleClassDesc::kNativeHandle.signature);
273 
274   // Register this object's address inside the `nativeHandle` field, so we can
275   // look this object up during later calls back into native.
276   env->SetLongField(jthis_, native_handle_id_, reinterpret_cast<jlong>(this));
277   FCP_CHECK(!env->ExceptionCheck());
278 }
279 
~JavaHttpRequestHandle()280 JavaHttpRequestHandle::~JavaHttpRequestHandle() {
281   ScopedJniEnv scoped_env(jvm_);
282   JNIEnv* env = scoped_env.env();
283 
284   absl::MutexLock locked(&lock_);
285   // We call the Java close() method when the destructor is invoked, to let the
286   // Java code know the request's resources (if any) can be released. The
287   // close() method may not have been invoked yet if the JavaHttpRequestHandle
288   // never ended up being passed to `performRequests`.
289   env->CallVoidMethod(jthis_, close_id_);
290   FCP_CHECK(!env->ExceptionCheck());
291 
292   // Unset the native handle (this is an additional safety check, so that if the
293   // Java object erroneously calls back into the native layer again, we will be
294   // able to detect it, rather than us accidentally accessing a destructed
295   // object).
296   env->SetLongField(jthis_, native_handle_id_, 0);
297 
298   // Delete the reference to the Java object.
299   env->DeleteGlobalRef(jthis_);
300 }
301 
Cancel()302 void JavaHttpRequestHandle::Cancel() {
303   {
304     absl::MutexLock locked(&lock_);
305     // We mark the request 'performed'. This way if the handle is subsequently
306     // still erroneously passed to `PerformRequests`, we can detect the error.
307     performed_ = true;
308   }
309   // Note that we release the lock before calling into Java, to ensure that if
310   // the Java call itself calls back into the native layer (e.g. by calling on
311   // of the request handle callbacks), we don't accidentally try to acquire
312   // the same mutex twice.
313 
314   ScopedJniEnv scoped_env(jvm_);
315   JNIEnv* env = scoped_env.env();
316 
317   // We call the Java close() method to indicate that the request should be
318   // cancelled. If `PerformRequests` wasn't called yet, then this will be a
319   // no-op.
320   env->CallVoidMethod(jthis_, close_id_);
321   FCP_CHECK(!env->ExceptionCheck());
322 }
323 
324 HttpRequestHandle::SentReceivedBytes
TotalSentReceivedBytes() const325 JavaHttpRequestHandle::TotalSentReceivedBytes() const {
326   ScopedJniEnv scoped_env(jvm_);
327   JNIEnv* env = scoped_env.env();
328 
329   jbyteArray sent_received_bytes_result = static_cast<jbyteArray>(
330       env->CallObjectMethod(jthis_, get_total_sent_received_bytes_id_));
331   FCP_CHECK(!env->ExceptionCheck());
332   FCP_CHECK(sent_received_bytes_result != nullptr);
333   LocalRefDeleter sent_received_bytes_result_deleter(
334       env, sent_received_bytes_result);
335 
336   // Convert the return value from a Java byte[] to the expected proto.
337   auto sent_received_bytes = ParseProtoFromJByteArray<JniHttpSentReceivedBytes>(
338       env, sent_received_bytes_result);
339   return {.sent_bytes = sent_received_bytes.sent_bytes(),
340           .received_bytes = sent_received_bytes.received_bytes()};
341 }
342 
callback() const343 fcp::client::http::HttpRequestCallback* JavaHttpRequestHandle::callback()
344     const {
345   // This method acquires and immediately releases the lock to ensure that all
346   // JNI callback invocations observe the effects of any prior JNI callback
347   // invocation, prior to invoking another `HttpRequestCallback` method.
348   //
349   // We don't hold the lock while invoking the actual `HttpRequestCallback`
350   // method though, to ensure that if the `HttpRequestCallback` invocation
351   // ultimately causes another JNI callback to be invoked, we don't attempt to
352   // acquire the lock twice on the same thread.
353   absl::MutexLock _(&lock_);
354   return callback_;
355 }
356 
response() const357 const JavaHttpResponse& JavaHttpRequestHandle::response() const {
358   // We synchronize for the same purpose as in callback().
359   absl::MutexLock _(&lock_);
360   return response_;
361 }
362 
SetCallback(fcp::client::http::HttpRequestCallback * callback)363 absl::Status JavaHttpRequestHandle::SetCallback(
364     fcp::client::http::HttpRequestCallback* callback) {
365   absl::MutexLock locked(&lock_);
366   // If the request was already 'performed' then we should detect that error.
367   if (performed_) {
368     return absl::InvalidArgumentError(
369         "can't perform a request twice, or perform an already-cancelled "
370         "request");
371   }
372   performed_ = true;
373   callback_ = callback;
374   return absl::OkStatus();
375 }
376 
ReadRequestBody(JNIEnv * env,jbyteArray buffer,jlong requested_bytes,jintArray actual_bytes_read)377 jboolean JavaHttpRequestHandle::ReadRequestBody(JNIEnv* env, jbyteArray buffer,
378                                                 jlong requested_bytes,
379                                                 jintArray actual_bytes_read) {
380   // Get a pointer to the output buffer's raw data. Note that this may make a
381   // copy of the Java data, but depending on JVM implementation it may also
382   // return a direct pointer to it, avoiding the copy (on Android, ART will
383   // generally avoid copying if the array is large enough).
384   jbyte* raw_buffer = env->GetByteArrayElements(buffer, nullptr);
385   FCP_CHECK(raw_buffer != nullptr);
386   FCP_CHECK(!env->ExceptionCheck());
387   // Ask the `HttpRequest` to write the request body data into the buffer.
388   absl::StatusOr<int64_t> read_body_result =
389       request_->ReadBody(reinterpret_cast<char*>(raw_buffer), requested_bytes);
390   // Release the raw buffer pointer (we must always do this, even if we hit an
391   // error and didn't write anything to the buffer).
392   //
393   // This ensures that the data in raw_buffer is now visible via the Java buffer
394   // (as noted above, this may result in copying the data into the Java heap,
395   // but if a direct pointer was returned earlier on then this will be a no-op).
396   env->ReleaseByteArrayElements(buffer, raw_buffer, 0);
397   FCP_CHECK(!env->ExceptionCheck());
398 
399   // Out of range is expected, and marks the end of the body. Any other error is
400   // unrecoverable, and should result in the OnResponseError being called.
401   if (!read_body_result.ok() &&
402       read_body_result.status().code() != absl::StatusCode::kOutOfRange) {
403     // If we receive an error during the reading of the request body, we
404     // immediately forward that error to the HttpRequestCallback (i.e. the Java
405     // layer will not need to call this callback method anymore). This ensures
406     // that we can forward the original error back to the callback (without
407     // having to convert it to a Java representation and back to a Status
408     // again).
409     callback()->OnResponseError(
410         *request_,
411         absl::Status(read_body_result.status().code(),
412                      absl::StrCat("failed to read request body",
413                                   read_body_result.status().message())));
414     return JNI_FALSE;
415   }
416 
417   // Otherwise, if everything went successfully, then we still need to write the
418   // actual amount of data we read (or -1 if we hit the end of the data) to the
419   // `actual_bytes_read` output array (the output array provides a convenient
420   // way to return something in addition to the return value, while still using
421   // only primitive Java types to keep the JNI boilerplate to a minimum).
422   //
423   // Note: we know that casting from int64_t to jint (aka a 32 bit int) should
424   // be safe, since `requested_bytes` is a jint, and the actual bytes read can
425   // never be larger than that number.
426   jint actual_bytes_read_result[] = {
427       static_cast<jint>(read_body_result.value_or(-1))};
428   env->SetIntArrayRegion(actual_bytes_read, 0, 1, actual_bytes_read_result);
429   return JNI_TRUE;
430 }
431 
OnResponseStarted(JNIEnv * env,jbyteArray response_proto)432 jboolean JavaHttpRequestHandle::OnResponseStarted(JNIEnv* env,
433                                                   jbyteArray response_proto) {
434   // Populate the response_ field based on the serialized response proto. This
435   // will allow us to access it in subsequent callbacks as well.
436   {
437     absl::MutexLock _(&lock_);
438     response_.PopulateFromProto(
439         ParseProtoFromJByteArray<JniHttpResponse>(env, response_proto));
440   }
441   return callback()->OnResponseStarted(*request_, response()).ok() ? JNI_TRUE
442                                                                    : JNI_FALSE;
443 }
444 
OnResponseError(JNIEnv * env,jbyteArray status_proto)445 void JavaHttpRequestHandle::OnResponseError(JNIEnv* env,
446                                             jbyteArray status_proto) {
447   absl::Status status = ConvertRpcStatusToAbslStatus(
448       ParseProtoFromJByteArray<
449           ::google::internal::federatedcompute::v1::Status>(env, status_proto));
450   callback()->OnResponseError(*request_, status);
451 }
452 
OnResponseBody(JNIEnv * env,jbyteArray buffer,jint bytes_available)453 jboolean JavaHttpRequestHandle::OnResponseBody(JNIEnv* env, jbyteArray buffer,
454                                                jint bytes_available) {
455   // Get a pointer to the input buffer's raw data. Note that this may make a
456   // copy of the Java data, but depending on JVM implementation it may also
457   // return a direct pointer to it, avoiding the copy (on Android, ART will
458   // generally avoid copying if the array is large enough).
459   jbyte* raw_buffer = env->GetByteArrayElements(buffer, nullptr);
460   FCP_CHECK(raw_buffer != nullptr);
461   FCP_CHECK(!env->ExceptionCheck());
462   absl::string_view buffer_view(reinterpret_cast<char*>(raw_buffer),
463                                 bytes_available);
464   // Pass the response body data to the HttpRequestCallback.
465   auto result = callback()->OnResponseBody(*request_, response(), buffer_view);
466 
467   // JNI_ABORT ensures that we don't copy the bytes in the raw buffer back to
468   // the main buffer (since we know they weren't modified).
469   env->ReleaseByteArrayElements(buffer, raw_buffer, JNI_ABORT);
470 
471   return result.ok() ? JNI_TRUE : JNI_FALSE;
472 }
473 
OnResponseBodyError(JNIEnv * env,jbyteArray status_proto)474 void JavaHttpRequestHandle::OnResponseBodyError(JNIEnv* env,
475                                                 jbyteArray status_proto) {
476   absl::Status status = ConvertRpcStatusToAbslStatus(
477       ParseProtoFromJByteArray<
478           ::google::internal::federatedcompute::v1::Status>(env, status_proto));
479   callback()->OnResponseBodyError(*request_, response(), status);
480 }
481 
OnResponseCompleted()482 void JavaHttpRequestHandle::OnResponseCompleted() {
483   callback()->OnResponseCompleted(*request_, response());
484 }
485 
486 // JNI functions. These are called from Java. We just forward them to the
487 // appropriate JavaHttpRequestHandle instance's member function.
488 #define JFUN(METHOD_NAME) \
489   Java_com_google_fcp_client_http_HttpClientForNative_##METHOD_NAME  // NOLINT
490 
JFUN(readRequestBody)491 extern "C" JNIEXPORT jboolean JNICALL JFUN(readRequestBody)(
492     JNIEnv* env, jclass, jlong request_handle_ptr, jbyteArray buffer,
493     jlong requested_bytes, jintArray actual_bytes_read) {
494   return JavaHttpRequestHandle::FromJlong(request_handle_ptr)
495       ->ReadRequestBody(env, buffer, requested_bytes, actual_bytes_read);
496 }
497 
JFUN(onResponseStarted)498 extern "C" JNIEXPORT jboolean JNICALL JFUN(onResponseStarted)(
499     JNIEnv* env, jclass, jlong request_handle_ptr, jbyteArray response_proto) {
500   return JavaHttpRequestHandle::FromJlong(request_handle_ptr)
501       ->OnResponseStarted(env, response_proto);
502 }
503 
JFUN(onResponseError)504 extern "C" JNIEXPORT void JNICALL JFUN(onResponseError)(
505     JNIEnv* env, jclass, jlong request_handle_ptr, jbyteArray status_proto) {
506   return JavaHttpRequestHandle::FromJlong(request_handle_ptr)
507       ->OnResponseError(env, status_proto);
508 }
509 
510 extern "C" JNIEXPORT jboolean JNICALL
JFUN(onResponseBody)511 JFUN(onResponseBody)(JNIEnv* env, jclass, jlong request_handle_ptr,
512                      jbyteArray buffer, jint bytes_available) {
513   return JavaHttpRequestHandle::FromJlong(request_handle_ptr)
514       ->OnResponseBody(env, buffer, bytes_available);
515 }
516 
JFUN(onResponseBodyError)517 extern "C" JNIEXPORT void JNICALL JFUN(onResponseBodyError)(
518     JNIEnv* env, jclass, jlong request_handle_ptr, jbyteArray status_proto) {
519   JavaHttpRequestHandle::FromJlong(request_handle_ptr)
520       ->OnResponseBodyError(env, status_proto);
521 }
522 
523 extern "C" JNIEXPORT void JNICALL
JFUN(onResponseCompleted)524 JFUN(onResponseCompleted)(JNIEnv* env, jclass, jlong request_handle_ptr) {
525   JavaHttpRequestHandle::FromJlong(request_handle_ptr)->OnResponseCompleted();
526 }
527 
528 }  // namespace java
529 }  // namespace http
530 }  // namespace client
531 }  // namespace fcp
532